about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock254
-rw-r--r--compiler/rustc_ast/src/ast.rs32
-rw-r--r--compiler/rustc_ast/src/attr/mod.rs15
-rw-r--r--compiler/rustc_ast/src/mut_visit.rs8
-rw-r--r--compiler/rustc_ast/src/token.rs43
-rw-r--r--compiler/rustc_ast/src/tokenstream.rs35
-rw-r--r--compiler/rustc_ast/src/util/comments.rs8
-rw-r--r--compiler/rustc_ast/src/util/parser.rs4
-rw-r--r--compiler/rustc_ast/src/visit.rs49
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs40
-rw-r--r--compiler/rustc_ast_lowering/src/index.rs19
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs369
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs1253
-rw-r--r--compiler/rustc_ast_lowering/src/path.rs126
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs71
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs1
-rw-r--r--compiler/rustc_ast_passes/src/node_count.rs2
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state.rs60
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/expr.rs14
-rw-r--r--compiler/rustc_borrowck/src/constraints/graph.rs10
-rw-r--r--compiler/rustc_borrowck/src/constraints/mod.rs7
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs75
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mod.rs29
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs2
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_errors.rs156
-rw-r--r--compiler/rustc_borrowck/src/region_infer/dump_mir.rs8
-rw-r--r--compiler/rustc_borrowck/src/region_infer/mod.rs7
-rw-r--r--compiler/rustc_borrowck/src/type_check/constraint_conversion.rs6
-rw-r--r--compiler/rustc_borrowck/src/type_check/free_region_relations.rs1
-rw-r--r--compiler/rustc_borrowck/src/type_check/input_output.rs1
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs55
-rw-r--r--compiler/rustc_borrowck/src/type_check/relate_tys.rs1
-rw-r--r--compiler/rustc_builtin_macros/src/asm.rs20
-rw-r--r--compiler/rustc_builtin_macros/src/derive.rs1
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/default.rs2
-rw-r--r--compiler/rustc_codegen_cranelift/Cargo.lock40
-rw-r--r--compiler/rustc_codegen_cranelift/Cargo.toml12
-rw-r--r--compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock9
-rw-r--r--compiler/rustc_codegen_cranelift/example/float-minmax-pass.rs53
-rw-r--r--compiler/rustc_codegen_cranelift/example/mini_core.rs16
-rw-r--r--compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs4
-rw-r--r--compiler/rustc_codegen_cranelift/example/std_example.rs6
-rw-r--r--compiler/rustc_codegen_cranelift/patches/0001-portable-simd-Disable-unsupported-tests.patch36
-rw-r--r--compiler/rustc_codegen_cranelift/rust-toolchain2
-rwxr-xr-xcompiler/rustc_codegen_cranelift/scripts/filter_profile.rs2
-rw-r--r--compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh2
-rwxr-xr-xcompiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh19
-rwxr-xr-xcompiler/rustc_codegen_cranelift/scripts/tests.sh4
-rw-r--r--compiler/rustc_codegen_cranelift/src/base.rs3
-rw-r--r--compiler/rustc_codegen_cranelift/src/cast.rs29
-rw-r--r--compiler/rustc_codegen_cranelift/src/config.rs9
-rw-r--r--compiler/rustc_codegen_cranelift/src/discriminant.rs12
-rw-r--r--compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs24
-rw-r--r--compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs29
-rw-r--r--compiler/rustc_codegen_cranelift/src/lib.rs2
-rw-r--r--compiler/rustc_codegen_cranelift/src/num.rs18
-rw-r--r--compiler/rustc_codegen_gcc/src/asm.rs3
-rw-r--r--compiler/rustc_codegen_llvm/src/asm.rs16
-rw-r--r--compiler/rustc_codegen_llvm/src/back/lto.rs8
-rw-r--r--compiler/rustc_codegen_llvm/src/back/write.rs31
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs22
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs25
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/mod.rs11
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm/ffi.rs15
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm_util.rs5
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs104
-rw-r--r--compiler/rustc_codegen_ssa/src/back/linker.rs72
-rw-r--r--compiler/rustc_codegen_ssa/src/back/metadata.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/back/symbol_export.rs206
-rw-r--r--compiler/rustc_codegen_ssa/src/back/write.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/base.rs7
-rw-r--r--compiler/rustc_codegen_ssa/src/common.rs19
-rw-r--r--compiler/rustc_codegen_ssa/src/lib.rs3
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/block.rs24
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/debuginfo.rs12
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/mod.rs1
-rw-r--r--compiler/rustc_const_eval/src/const_eval/eval_queries.rs38
-rw-r--r--compiler/rustc_const_eval/src/const_eval/mod.rs138
-rw-r--r--compiler/rustc_const_eval/src/const_eval/valtrees.rs459
-rw-r--r--compiler/rustc_const_eval/src/interpret/eval_context.rs4
-rw-r--r--compiler/rustc_const_eval/src/interpret/machine.rs28
-rw-r--r--compiler/rustc_const_eval/src/interpret/memory.rs111
-rw-r--r--compiler/rustc_const_eval/src/interpret/mod.rs2
-rw-r--r--compiler/rustc_const_eval/src/interpret/operand.rs1
-rw-r--r--compiler/rustc_const_eval/src/interpret/place.rs19
-rw-r--r--compiler/rustc_const_eval/src/interpret/validity.rs2
-rw-r--r--compiler/rustc_const_eval/src/lib.rs4
-rw-r--r--compiler/rustc_const_eval/src/transform/promote_consts.rs4
-rw-r--r--compiler/rustc_const_eval/src/transform/validate.rs3
-rw-r--r--compiler/rustc_data_structures/src/stable_hasher.rs15
-rw-r--r--compiler/rustc_error_messages/locales/en-US/parser.ftl4
-rw-r--r--compiler/rustc_error_messages/locales/en-US/typeck.ftl8
-rw-r--r--compiler/rustc_error_messages/src/lib.rs10
-rw-r--r--compiler/rustc_errors/src/diagnostic.rs28
-rw-r--r--compiler/rustc_errors/src/diagnostic_builder.rs28
-rw-r--r--compiler/rustc_errors/src/emitter.rs7
-rw-r--r--compiler/rustc_errors/src/json.rs3
-rw-r--r--compiler/rustc_errors/src/lib.rs25
-rw-r--r--compiler/rustc_expand/src/base.rs4
-rw-r--r--compiler/rustc_expand/src/build.rs1
-rw-r--r--compiler/rustc_expand/src/config.rs6
-rw-r--r--compiler/rustc_expand/src/expand.rs4
-rw-r--r--compiler/rustc_expand/src/mbe.rs8
-rw-r--r--compiler/rustc_expand/src/mbe/macro_check.rs8
-rw-r--r--compiler/rustc_expand/src/mbe/macro_rules.rs15
-rw-r--r--compiler/rustc_expand/src/mbe/metavar_expr.rs4
-rw-r--r--compiler/rustc_expand/src/mbe/quoted.rs12
-rw-r--r--compiler/rustc_expand/src/mbe/transcribe.rs34
-rw-r--r--compiler/rustc_expand/src/parse/tests.rs14
-rw-r--r--compiler/rustc_expand/src/placeholders.rs1
-rw-r--r--compiler/rustc_expand/src/proc_macro.rs15
-rw-r--r--compiler/rustc_expand/src/proc_macro_server.rs30
-rw-r--r--compiler/rustc_feature/src/active.rs2
-rw-r--r--compiler/rustc_hir/src/arena.rs2
-rw-r--r--compiler/rustc_hir/src/hir.rs328
-rw-r--r--compiler/rustc_hir/src/hir_id.rs3
-rw-r--r--compiler/rustc_hir/src/intravisit.rs95
-rw-r--r--compiler/rustc_hir/src/itemlikevisit.rs10
-rw-r--r--compiler/rustc_hir/src/lang_items.rs1
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs160
-rw-r--r--compiler/rustc_incremental/src/assert_dep_graph.rs53
-rw-r--r--compiler/rustc_index/src/bit_set.rs2
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs126
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs41
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs148
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs25
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs3
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs138
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/note.rs4
-rw-r--r--compiler/rustc_interface/src/tests.rs2
-rw-r--r--compiler/rustc_lint/src/array_into_iter.rs2
-rw-r--r--compiler/rustc_lint/src/builtin.rs291
-rw-r--r--compiler/rustc_lint/src/context.rs2
-rw-r--r--compiler/rustc_lint/src/non_fmt_panic.rs10
-rw-r--r--compiler/rustc_lint/src/nonstandard_style.rs2
-rw-r--r--compiler/rustc_lint_defs/src/lib.rs12
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp97
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp6
-rw-r--r--compiler/rustc_macros/src/diagnostics/diagnostic.rs586
-rw-r--r--compiler/rustc_macros/src/diagnostics/error.rs132
-rw-r--r--compiler/rustc_macros/src/diagnostics/mod.rs114
-rw-r--r--compiler/rustc_macros/src/diagnostics/subdiagnostic.rs437
-rw-r--r--compiler/rustc_macros/src/diagnostics/utils.rs267
-rw-r--r--compiler/rustc_macros/src/lib.rs23
-rw-r--r--compiler/rustc_macros/src/session_diagnostic.rs956
-rw-r--r--compiler/rustc_metadata/src/creader.rs19
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder.rs8
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs16
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs24
-rw-r--r--compiler/rustc_metadata/src/rmeta/mod.rs5
-rw-r--r--compiler/rustc_metadata/src/rmeta/table.rs14
-rw-r--r--compiler/rustc_middle/src/hir/map/mod.rs12
-rw-r--r--compiler/rustc_middle/src/middle/exported_symbols.rs17
-rw-r--r--compiler/rustc_middle/src/mir/interpret/allocation.rs44
-rw-r--r--compiler/rustc_middle/src/mir/interpret/pointer.rs17
-rw-r--r--compiler/rustc_middle/src/mir/mod.rs20
-rw-r--r--compiler/rustc_middle/src/mir/patch.rs35
-rw-r--r--compiler/rustc_middle/src/mir/pretty.rs1
-rw-r--r--compiler/rustc_middle/src/mir/traversal.rs90
-rw-r--r--compiler/rustc_middle/src/query/mod.rs9
-rw-r--r--compiler/rustc_middle/src/thir.rs24
-rw-r--r--compiler/rustc_middle/src/ty/codec.rs3
-rw-r--r--compiler/rustc_middle/src/ty/consts.rs2
-rw-r--r--compiler/rustc_middle/src/ty/consts/int.rs92
-rw-r--r--compiler/rustc_middle/src/ty/consts/valtree.rs19
-rw-r--r--compiler/rustc_middle/src/ty/diagnostics.rs398
-rw-r--r--compiler/rustc_middle/src/ty/error.rs59
-rw-r--r--compiler/rustc_middle/src/ty/layout.rs53
-rw-r--r--compiler/rustc_middle/src/ty/list.rs4
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs20
-rw-r--r--compiler/rustc_middle/src/ty/query.rs2
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs2
-rw-r--r--compiler/rustc_middle/src/ty/util.rs14
-rw-r--r--compiler/rustc_mir_build/src/build/expr/into.rs13
-rw-r--r--compiler/rustc_mir_build/src/build/mod.rs5
-rw-r--r--compiler/rustc_mir_build/src/check_unsafety.rs74
-rw-r--r--compiler/rustc_mir_transform/src/add_retag.rs15
-rw-r--r--compiler/rustc_mir_transform/src/check_unsafety.rs4
-rw-r--r--compiler/rustc_mir_transform/src/const_prop.rs17
-rw-r--r--compiler/rustc_mir_transform/src/const_prop_lint.rs23
-rw-r--r--compiler/rustc_mir_transform/src/deref_separator.rs134
-rw-r--r--compiler/rustc_mir_transform/src/early_otherwise_branch.rs4
-rw-r--r--compiler/rustc_mir_transform/src/lib.rs2
-rw-r--r--compiler/rustc_mir_transform/src/pass_manager.rs21
-rw-r--r--compiler/rustc_monomorphize/src/collector.rs10
-rw-r--r--compiler/rustc_monomorphize/src/partitioning/default.rs4
-rw-r--r--compiler/rustc_parse/Cargo.toml1
-rw-r--r--compiler/rustc_parse/src/lexer/mod.rs22
-rw-r--r--compiler/rustc_parse/src/lexer/tokentrees.rs12
-rw-r--r--compiler/rustc_parse/src/lexer/unicode_chars.rs18
-rw-r--r--compiler/rustc_parse/src/parser/attr.rs8
-rw-r--r--compiler/rustc_parse/src/parser/attr_wrapper.rs64
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs149
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs118
-rw-r--r--compiler/rustc_parse/src/parser/generics.rs13
-rw-r--r--compiler/rustc_parse/src/parser/item.rs95
-rw-r--r--compiler/rustc_parse/src/parser/mod.rs256
-rw-r--r--compiler/rustc_parse/src/parser/nonterminal.rs18
-rw-r--r--compiler/rustc_parse/src/parser/pat.rs32
-rw-r--r--compiler/rustc_parse/src/parser/path.rs11
-rw-r--r--compiler/rustc_parse/src/parser/stmt.rs50
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs28
-rw-r--r--compiler/rustc_passes/src/dead.rs61
-rw-r--r--compiler/rustc_passes/src/hir_stats.rs3
-rw-r--r--compiler/rustc_passes/src/reachable.rs5
-rw-r--r--compiler/rustc_passes/src/stability.rs2
-rw-r--r--compiler/rustc_privacy/src/lib.rs76
-rw-r--r--compiler/rustc_query_impl/src/keys.rs11
-rw-r--r--compiler/rustc_resolve/src/build_reduced_graph.rs21
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs168
-rw-r--r--compiler/rustc_resolve/src/ident.rs139
-rw-r--r--compiler/rustc_resolve/src/imports.rs24
-rw-r--r--compiler/rustc_resolve/src/late.rs389
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs93
-rw-r--r--compiler/rustc_resolve/src/late/lifetimes.rs109
-rw-r--r--compiler/rustc_resolve/src/lib.rs103
-rw-r--r--compiler/rustc_resolve/src/macros.rs9
-rw-r--r--compiler/rustc_save_analysis/src/dump_visitor.rs58
-rw-r--r--compiler/rustc_save_analysis/src/lib.rs10
-rw-r--r--compiler/rustc_save_analysis/src/sig.rs25
-rw-r--r--compiler/rustc_serialize/src/serialize.rs1
-rw-r--r--compiler/rustc_session/src/config.rs89
-rw-r--r--compiler/rustc_session/src/options.rs21
-rw-r--r--compiler/rustc_session/src/parse.rs22
-rw-r--r--compiler/rustc_session/src/search_paths.rs4
-rw-r--r--compiler/rustc_session/src/session.rs8
-rw-r--r--compiler/rustc_session/src/utils.rs10
-rw-r--r--compiler/rustc_span/src/hygiene.rs2
-rw-r--r--compiler/rustc_span/src/lib.rs5
-rw-r--r--compiler/rustc_span/src/profiling.rs35
-rw-r--r--compiler/rustc_span/src/source_map.rs14
-rw-r--r--compiler/rustc_span/src/source_map/tests.rs80
-rw-r--r--compiler/rustc_span/src/symbol.rs5
-rw-r--r--compiler/rustc_target/src/abi/call/mod.rs8
-rw-r--r--compiler/rustc_target/src/abi/call/nvptx64.rs47
-rw-r--r--compiler/rustc_target/src/abi/mod.rs32
-rw-r--r--compiler/rustc_target/src/asm/mips.rs3
-rw-r--r--compiler/rustc_target/src/asm/mod.rs6
-rw-r--r--compiler/rustc_target/src/asm/x86.rs11
-rw-r--r--compiler/rustc_target/src/spec/mod.rs4
-rw-r--r--compiler/rustc_target/src/spec/wasm64_unknown_unknown.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs34
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs42
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs31
-rw-r--r--compiler/rustc_trait_selection/src/traits/object_safety.rs5
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs28
-rw-r--r--compiler/rustc_typeck/src/astconv/generics.rs2
-rw-r--r--compiler/rustc_typeck/src/astconv/mod.rs112
-rw-r--r--compiler/rustc_typeck/src/check/check.rs6
-rw-r--r--compiler/rustc_typeck/src/check/compare_method.rs19
-rw-r--r--compiler/rustc_typeck/src/check/expr.rs117
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs2
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/checks.rs6
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs39
-rw-r--r--compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs1
-rw-r--r--compiler/rustc_typeck/src/check/method/suggest.rs640
-rw-r--r--compiler/rustc_typeck/src/check/mod.rs49
-rw-r--r--compiler/rustc_typeck/src/check/op.rs137
-rw-r--r--compiler/rustc_typeck/src/check/pat.rs2
-rw-r--r--compiler/rustc_typeck/src/check/wfcheck.rs17
-rw-r--r--compiler/rustc_typeck/src/check_unused.rs7
-rw-r--r--compiler/rustc_typeck/src/coherence/orphan.rs10
-rw-r--r--compiler/rustc_typeck/src/collect.rs302
-rw-r--r--compiler/rustc_typeck/src/collect/type_of.rs4
-rw-r--r--compiler/rustc_typeck/src/errors.rs42
-rw-r--r--compiler/rustc_typeck/src/lib.rs4
-rw-r--r--config.toml.example24
-rw-r--r--library/alloc/benches/vec_deque.rs24
-rw-r--r--library/alloc/src/borrow.rs6
-rw-r--r--library/alloc/src/collections/binary_heap.rs3
-rw-r--r--library/alloc/src/collections/binary_heap/tests.rs (renamed from library/alloc/tests/binary_heap.rs)4
-rw-r--r--library/alloc/src/collections/linked_list.rs10
-rw-r--r--library/alloc/src/collections/linked_list/tests.rs695
-rw-r--r--library/alloc/src/collections/vec_deque/mod.rs43
-rw-r--r--library/alloc/src/collections/vec_deque/spec_extend.rs73
-rw-r--r--library/alloc/src/collections/vec_deque/tests.rs294
-rw-r--r--library/alloc/src/rc.rs19
-rw-r--r--library/alloc/src/sync.rs19
-rw-r--r--library/alloc/src/vec/into_iter.rs5
-rw-r--r--library/alloc/src/vec/is_zero.rs9
-rw-r--r--library/alloc/src/vec/mod.rs42
-rw-r--r--library/alloc/src/vec/spec_extend.rs2
-rw-r--r--library/alloc/tests/c_str.rs3
-rw-r--r--library/alloc/tests/lib.rs1
-rw-r--r--library/alloc/tests/linked_list.rs683
-rw-r--r--library/core/src/internal_macros.rs24
-rw-r--r--library/core/src/intrinsics.rs8
-rw-r--r--library/core/src/iter/sources/repeat_with.rs2
-rw-r--r--library/core/src/iter/traits/collect.rs35
-rw-r--r--library/core/src/macros/mod.rs2
-rw-r--r--library/core/src/num/f32.rs167
-rw-r--r--library/core/src/num/f64.rs149
-rw-r--r--library/core/src/ops/mod.rs3
-rw-r--r--library/core/src/ops/try_trait.rs22
-rw-r--r--library/core/src/option.rs8
-rw-r--r--library/core/src/panic/location.rs4
-rw-r--r--library/core/src/panic/unwind_safe.rs7
-rw-r--r--library/core/src/result.rs8
-rw-r--r--library/core/src/slice/mod.rs18
-rw-r--r--library/core/tests/num/ops.rs12
-rw-r--r--library/proc_macro/Cargo.toml4
-rw-r--r--library/proc_macro/src/lib.rs4
-rw-r--r--library/proc_macro/src/quote.rs1
-rw-r--r--library/std/src/collections/hash/map.rs2
-rw-r--r--library/std/src/ffi/mod.rs9
-rw-r--r--library/std/src/lib.rs2
-rw-r--r--library/std/src/macros.rs16
-rw-r--r--library/std/src/net/parser.rs39
-rw-r--r--library/std/src/os/unix/process.rs24
-rw-r--r--library/std/src/os/windows/io/handle.rs64
-rw-r--r--library/std/src/path.rs42
-rw-r--r--library/std/src/path/tests.rs12
-rw-r--r--library/std/src/process/tests.rs21
-rw-r--r--library/std/src/sys/unix/fd.rs19
-rw-r--r--library/std/src/sys/unix/fs.rs5
-rw-r--r--library/std/src/sys/unix/futex.rs47
-rw-r--r--library/std/src/sys/unix/locks/mod.rs1
-rw-r--r--library/std/src/sys/unix/mod.rs1
-rw-r--r--library/std/src/sys/unix/os.rs22
-rw-r--r--library/std/src/sys/unix/process/process_common.rs3
-rw-r--r--library/std/src/sys/unix/thread_parker.rs265
-rw-r--r--library/std/src/sys/unix/time.rs4
-rw-r--r--library/std/src/sys/unix/weak.rs32
-rw-r--r--library/std/src/sys/wasm/atomics/condvar.rs102
-rw-r--r--library/std/src/sys/wasm/atomics/futex.rs22
-rw-r--r--library/std/src/sys/wasm/atomics/mutex.rs64
-rw-r--r--library/std/src/sys/wasm/atomics/rwlock.rs145
-rw-r--r--library/std/src/sys/wasm/atomics/thread.rs34
-rw-r--r--library/std/src/sys/wasm/mod.rs15
-rw-r--r--library/std/src/sys/windows/args.rs159
-rw-r--r--library/std/src/sys/windows/c.rs6
-rw-r--r--library/std/src/sys/windows/handle.rs4
-rw-r--r--library/std/src/sys/windows/mod.rs12
-rw-r--r--library/std/src/sys/windows/path.rs120
-rw-r--r--library/std/src/sys/windows/path/tests.rs20
-rw-r--r--library/std/src/sys/windows/pipe.rs90
-rw-r--r--library/std/src/sys/windows/process.rs154
-rw-r--r--library/std/src/sys/windows/process/tests.rs7
-rw-r--r--library/std/src/sys/windows/thread_parker.rs20
-rw-r--r--library/std/src/sys_common/thread_parker/futex.rs18
-rw-r--r--library/std/src/sys_common/thread_parker/generic.rs24
-rw-r--r--library/std/src/sys_common/thread_parker/mod.rs2
-rw-r--r--library/std/src/sys_common/wtf8.rs5
-rw-r--r--library/std/src/thread/local.rs2
-rw-r--r--library/std/src/thread/mod.rs33
-rw-r--r--src/bootstrap/Cargo.toml2
-rw-r--r--src/bootstrap/bin/rustdoc.rs11
-rw-r--r--src/bootstrap/bootstrap.py149
-rw-r--r--src/bootstrap/builder.rs116
-rw-r--r--src/bootstrap/builder/tests.rs226
-rw-r--r--src/bootstrap/check.rs4
-rw-r--r--src/bootstrap/compile.rs6
-rw-r--r--src/bootstrap/config.rs122
-rw-r--r--src/bootstrap/dist.rs8
-rw-r--r--src/bootstrap/doc.rs7
-rw-r--r--src/bootstrap/flags.rs146
-rw-r--r--src/bootstrap/lib.rs10
-rw-r--r--src/bootstrap/native.rs292
-rw-r--r--src/bootstrap/tarball.rs4
-rw-r--r--src/bootstrap/test.rs15
-rwxr-xr-xsrc/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh7
-rwxr-xr-xsrc/ci/docker/host-x86_64/dist-x86_64-linux/build-gcc.sh2
m---------src/doc/book0
m---------src/doc/embedded-book0
m---------src/doc/rust-by-example0
m---------src/doc/rustc-dev-guide0
-rw-r--r--src/doc/unstable-book/src/compiler-flags/cf-protection.md2
-rw-r--r--src/doc/unstable-book/src/language-features/yeet-expr.md26
-rw-r--r--src/etc/check_missing_items.py2
-rw-r--r--src/librustdoc/Cargo.toml1
-rw-r--r--src/librustdoc/clean/mod.rs121
-rw-r--r--src/librustdoc/clean/render_macro_matchers.rs12
-rw-r--r--src/librustdoc/clean/types.rs157
-rw-r--r--src/librustdoc/clean/types/tests.rs (renamed from src/librustdoc/passes/unindent_comments/tests.rs)2
-rw-r--r--src/librustdoc/config.rs6
-rw-r--r--src/librustdoc/core.rs5
-rw-r--r--src/librustdoc/doctest.rs8
-rw-r--r--src/librustdoc/html/format.rs134
-rw-r--r--src/librustdoc/html/markdown.rs116
-rw-r--r--src/librustdoc/html/render/span_map.rs13
-rw-r--r--src/librustdoc/html/render/write_shared.rs27
-rw-r--r--src/librustdoc/html/static/COPYRIGHT.txt10
-rw-r--r--src/librustdoc/html/static/css/rustdoc.css27
-rw-r--r--src/librustdoc/html/static/fonts/FiraSans-Medium.woffbin186824 -> 0 bytes
-rw-r--r--src/librustdoc/html/static/fonts/FiraSans-Regular.woffbin183268 -> 0 bytes
-rw-r--r--src/librustdoc/html/static/fonts/NanumBarunGothic.ttf.woffbin677868 -> 0 bytes
-rw-r--r--src/librustdoc/html/static/fonts/SourceCodePro-It.ttf.woffbin58444 -> 0 bytes
-rw-r--r--src/librustdoc/html/static/fonts/SourceCodePro-Regular.ttf.woffbin68152 -> 0 bytes
-rw-r--r--src/librustdoc/html/static/fonts/SourceCodePro-Semibold.ttf.woffbin68080 -> 0 bytes
-rw-r--r--src/librustdoc/html/static/fonts/SourceSerif4-Bold.ttf.woffbin110552 -> 0 bytes
-rw-r--r--src/librustdoc/html/static/fonts/SourceSerif4-It.ttf.woffbin78108 -> 0 bytes
-rw-r--r--src/librustdoc/html/static/fonts/SourceSerif4-Regular.ttf.woffbin103604 -> 0 bytes
-rw-r--r--src/librustdoc/html/static/js/externs.js68
-rw-r--r--src/librustdoc/html/static/js/main.js269
-rw-r--r--src/librustdoc/html/static/js/scrape-examples.js45
-rw-r--r--src/librustdoc/html/static/js/search.js1665
-rw-r--r--src/librustdoc/html/static/js/settings.js15
-rw-r--r--src/librustdoc/html/static/js/source-script.js89
-rw-r--r--src/librustdoc/html/static/js/storage.js47
-rw-r--r--src/librustdoc/html/static_files.rs51
-rw-r--r--src/librustdoc/lib.rs1
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs115
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links/early.rs200
-rw-r--r--src/librustdoc/passes/collect_trait_impls.rs101
-rw-r--r--src/librustdoc/passes/mod.rs5
-rw-r--r--src/librustdoc/passes/unindent_comments.rs116
m---------src/llvm-project0
-rw-r--r--src/test/assembly/nvptx-kernel-abi/nvptx-kernel-args-abi-v7.rs254
-rw-r--r--src/test/codegen/asm-clobber_abi.rs8
-rw-r--r--src/test/codegen/panic-in-drop-abort.rs12
-rw-r--r--src/test/codegen/remap_path_prefix/main.rs2
-rw-r--r--src/test/codegen/set-discriminant-invalid.rs2
-rw-r--r--src/test/debuginfo/thread.rs2
-rw-r--r--src/test/debuginfo/unit-type.rs71
-rw-r--r--src/test/incremental/hashes/enum_defs.rs8
-rw-r--r--src/test/incremental/hashes/inherent_impls.rs6
-rw-r--r--src/test/incremental/hashes/statics.rs4
-rw-r--r--src/test/incremental/hashes/struct_defs.rs22
-rw-r--r--src/test/incremental/hashes/trait_defs.rs4
-rw-r--r--src/test/mir-opt/combine_clone_of_primitives.rs2
-rw-r--r--src/test/mir-opt/combine_clone_of_primitives.{impl#0}-clone.InstCombine.diff24
-rw-r--r--src/test/mir-opt/derefer_complex_case.main.Derefer.diff111
-rw-r--r--src/test/mir-opt/derefer_complex_case.rs5
-rw-r--r--src/test/mir-opt/derefer_terminator_test.main.Derefer.diff103
-rw-r--r--src/test/mir-opt/derefer_terminator_test.rs14
-rw-r--r--src/test/mir-opt/derefer_test.main.Derefer.diff20
-rw-r--r--src/test/mir-opt/derefer_test_multiple.main.Derefer.diff44
-rw-r--r--src/test/mir-opt/early_otherwise_branch.opt2.EarlyOtherwiseBranch.diff8
-rw-r--r--src/test/mir-opt/early_otherwise_branch.rs2
-rw-r--r--src/test/mir-opt/early_otherwise_branch_3_element_tuple.rs2
-rw-r--r--src/test/mir-opt/early_otherwise_branch_68867.try_sum.EarlyOtherwiseBranch.before-SimplifyConstCondition-final.after.diff297
-rw-r--r--src/test/mir-opt/early_otherwise_branch_68867.try_sum.EarlyOtherwiseBranch.diff159
-rw-r--r--src/test/mir-opt/early_otherwise_branch_noopt.noopt1.EarlyOtherwiseBranch.diff26
-rw-r--r--src/test/mir-opt/early_otherwise_branch_noopt.rs2
-rw-r--r--src/test/mir-opt/early_otherwise_branch_soundness.no_deref_ptr.EarlyOtherwiseBranch.diff10
-rw-r--r--src/test/mir-opt/early_otherwise_branch_soundness.no_downcast.EarlyOtherwiseBranch.diff14
-rw-r--r--src/test/mir-opt/early_otherwise_branch_soundness.rs2
-rw-r--r--src/test/mir-opt/if-condition-int.rs2
-rw-r--r--src/test/mir-opt/inline/inline_closure_captures.foo.Inline.after.mir12
-rw-r--r--src/test/mir-opt/inline/inline_diverging.h.Inline.diff49
-rw-r--r--src/test/mir-opt/issue_72181_1.main.mir_map.0.mir2
-rw-r--r--src/test/mir-opt/lower_array_len.array_bound.NormalizeArrayLen.diff4
-rw-r--r--src/test/mir-opt/lower_array_len.array_bound_mut.NormalizeArrayLen.diff4
-rw-r--r--src/test/mir-opt/lower_array_len.array_len.NormalizeArrayLen.diff4
-rw-r--r--src/test/mir-opt/lower_array_len.array_len_by_value.NormalizeArrayLen.diff4
-rw-r--r--src/test/mir-opt/lower_slice_len.bound.LowerSliceLenCalls.diff4
-rw-r--r--src/test/mir-opt/lower_slice_len.rs2
-rw-r--r--src/test/mir-opt/nll/named_lifetimes_basic.use_x.nll.0.mir16
-rw-r--r--src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir4
-rw-r--r--src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir4
-rw-r--r--src/test/mir-opt/nrvo-simple.rs2
-rw-r--r--src/test/mir-opt/storage_ranges.main.nll.0.mir2
-rw-r--r--src/test/mir-opt/uninhabited_fallthrough_elimination.eliminate_fallthrough.UninhabitedEnumBranching.diff8
-rw-r--r--src/test/mir-opt/uninhabited_fallthrough_elimination.keep_fallthrough.UninhabitedEnumBranching.diff4
-rw-r--r--src/test/mir-opt/unreachable.main.UnreachablePropagation.diff4
-rw-r--r--src/test/mir-opt/unreachable_diverging.main.UnreachablePropagation.diff4
-rw-r--r--src/test/pretty/dollar-crate.pp2
-rw-r--r--src/test/pretty/hir-pretty-loop.pp2
-rw-r--r--src/test/pretty/issue-4264.pp18
-rw-r--r--src/test/pretty/yeet-expr.rs12
-rw-r--r--src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.closure.txt8
-rw-r--r--src/test/run-make-fulldeps/reproducible-build/linker.rs6
-rw-r--r--src/test/run-make-fulldeps/symbol-visibility/Makefile5
-rw-r--r--src/test/run-make/const_fn_mir/dump.mir4
-rw-r--r--src/test/run-make/emit-shared-files/Makefile6
-rw-r--r--src/test/run-make/issue-47384/Makefile12
-rw-r--r--src/test/run-make/issue-47384/lib.rs12
-rw-r--r--src/test/run-make/issue-47384/linker.ld7
-rw-r--r--src/test/run-make/issue-47384/main.rs1
-rw-r--r--src/test/run-make/issue-96498/Makefile8
-rw-r--r--src/test/run-make/issue-96498/foo.rs4
-rw-r--r--src/test/rustdoc-js-std/filter-crate.js2
-rw-r--r--src/test/rustdoc-js-std/parser-errors.js385
-rw-r--r--src/test/rustdoc-js-std/parser-filter.js43
-rw-r--r--src/test/rustdoc-js-std/parser-generics.js62
-rw-r--r--src/test/rustdoc-js-std/parser-ident.js93
-rw-r--r--src/test/rustdoc-js-std/parser-literal.js27
-rw-r--r--src/test/rustdoc-js-std/parser-paths.js90
-rw-r--r--src/test/rustdoc-js-std/parser-quote.js87
-rw-r--r--src/test/rustdoc-js-std/parser-returned.js99
-rw-r--r--src/test/rustdoc-js-std/parser-separators.js206
-rw-r--r--src/test/rustdoc-js-std/parser-weird-queries.js123
-rw-r--r--src/test/rustdoc-js-std/quoted.js10
-rw-r--r--src/test/rustdoc-js-std/struct-vec.js4
-rw-r--r--src/test/rustdoc-js-std/typed-query.js5
-rw-r--r--src/test/rustdoc-js-std/vec-new.js2
-rw-r--r--src/test/rustdoc-js/doc-alias-filter.js2
-rw-r--r--src/test/rustdoc-js/doc-alias.js20
-rw-r--r--src/test/rustdoc-js/generics.js23
-rw-r--r--src/test/rustdoc-js/generics.rs2
-rw-r--r--src/test/rustdoc-ui/intra-doc/email-address-localhost.rs2
-rw-r--r--src/test/rustdoc-ui/intra-doc/email-address-localhost.stderr16
-rw-r--r--src/test/rustdoc-ui/intra-doc/macro-rules-error.rs27
-rw-r--r--src/test/rustdoc-ui/intra-doc/macro-rules-error.stderr22
-rw-r--r--src/test/rustdoc-ui/intra-doc/macro-rules.rs15
-rw-r--r--src/test/rustdoc-ui/issue-91713.stdout2
-rw-r--r--src/test/rustdoc/deref-slice-core.rs22
-rw-r--r--src/test/rustdoc/early-unindent.rs26
-rw-r--r--src/test/rustdoc/issue-95873.rs2
-rw-r--r--src/test/rustdoc/issue-96381.rs16
-rw-r--r--src/test/rustdoc/where.SWhere_Simd_item-decl.html1
-rw-r--r--src/test/rustdoc/where.SWhere_TraitWhere_item-decl.html3
-rw-r--r--src/test/rustdoc/where.rs13
-rw-r--r--src/test/ui-fulldeps/issue-15149.rs27
-rw-r--r--src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs (renamed from src/test/ui-fulldeps/session-derive-errors.rs)82
-rw-r--r--src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr (renamed from src/test/ui-fulldeps/session-derive-errors.stderr)192
-rw-r--r--src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs501
-rw-r--r--src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr389
-rw-r--r--src/test/ui/array-slice-vec/repeat_empty_ok.stderr12
-rw-r--r--src/test/ui/asm/x86_64/bad-reg.rs4
-rw-r--r--src/test/ui/asm/x86_64/bad-reg.stderr16
-rw-r--r--src/test/ui/associated-types/impl-trait-return-missing-constraint.rs1
-rw-r--r--src/test/ui/associated-types/impl-trait-return-missing-constraint.stderr25
-rw-r--r--src/test/ui/associated-types/issue-25339.rs (renamed from src/test/ui/issues/issue-25339.rs)0
-rw-r--r--src/test/ui/associated-types/issue-55846.rs (renamed from src/test/ui/issues/issue-55846.rs)0
-rw-r--r--src/test/ui/async-await/async-unsafe-fn-call-in-safe.mir.stderr6
-rw-r--r--src/test/ui/async-await/async-unsafe-fn-call-in-safe.rs8
-rw-r--r--src/test/ui/async-await/async-unsafe-fn-call-in-safe.thir.stderr6
-rw-r--r--src/test/ui/async-await/issue-86507.stderr6
-rw-r--r--src/test/ui/binop/issue-3820.rs (renamed from src/test/ui/issues/issue-3820.rs)0
-rw-r--r--src/test/ui/binop/issue-3820.stderr (renamed from src/test/ui/issues/issue-3820.stderr)0
-rw-r--r--src/test/ui/binop/issue-93927.rs20
-rw-r--r--src/test/ui/binop/issue-93927.stderr16
-rw-r--r--src/test/ui/builtin-superkinds/builtin-superkinds-self-type.stderr9
-rw-r--r--src/test/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.thir.stderr2
-rw-r--r--src/test/ui/coercion/coerce-issue-49593-box-never-windows.nofallback.stderr39
-rw-r--r--src/test/ui/coercion/coerce-issue-49593-box-never-windows.rs58
-rw-r--r--src/test/ui/coercion/coerce-issue-49593-box-never.nofallback.stderr4
-rw-r--r--src/test/ui/coercion/coerce-issue-49593-box-never.rs1
-rw-r--r--src/test/ui/coherence/coherence-cow.re_a.stderr2
-rw-r--r--src/test/ui/coherence/coherence-cow.re_b.stderr2
-rw-r--r--src/test/ui/coherence/coherence-cow.re_c.stderr2
-rw-r--r--src/test/ui/coherence/coherence-impls-copy.stderr2
-rw-r--r--src/test/ui/coherence/coherence-orphan.stderr4
-rw-r--r--src/test/ui/coherence/coherence-overlapping-pairs.stderr2
-rw-r--r--src/test/ui/coherence/coherence-pair-covered-uncovered-1.stderr2
-rw-r--r--src/test/ui/coherence/coherence-pair-covered-uncovered.stderr2
-rw-r--r--src/test/ui/coherence/coherence-vec-local-2.stderr2
-rw-r--r--src/test/ui/coherence/coherence-vec-local.stderr2
-rw-r--r--src/test/ui/coherence/coherence_local_err_struct.stderr2
-rw-r--r--src/test/ui/coherence/impl-foreign-for-foreign.stderr2
-rw-r--r--src/test/ui/coherence/impl-foreign-for-foreign[foreign].stderr6
-rw-r--r--src/test/ui/coherence/impl-foreign-for-fundamental[foreign].stderr4
-rw-r--r--src/test/ui/coherence/impl-foreign[foreign]-for-foreign.stderr2
-rw-r--r--src/test/ui/coherence/impl-foreign[fundemental[foreign]]-for-foreign.stderr6
-rw-r--r--src/test/ui/coherence/impl[t]-foreign-for-foreign[t].stderr4
-rw-r--r--src/test/ui/conservative_impl_trait.rs1
-rw-r--r--src/test/ui/conservative_impl_trait.stderr14
-rw-r--r--src/test/ui/const-generics/const-arg-in-const-arg.min.stderr275
-rw-r--r--src/test/ui/const-generics/const-arg-in-const-arg.rs17
-rw-r--r--src/test/ui/const-generics/defaults/rp_impl_trait_fail.rs3
-rw-r--r--src/test/ui/const-generics/defaults/rp_impl_trait_fail.stderr49
-rw-r--r--src/test/ui/const-generics/issues/issue-56445-1.min.stderr11
-rw-r--r--src/test/ui/const-generics/issues/issue-56445-1.rs1
-rw-r--r--src/test/ui/const-generics/issues/issue-61336-2.stderr6
-rw-r--r--src/test/ui/const-generics/issues/issue-61336.stderr6
-rw-r--r--src/test/ui/consts/const-blocks/fn-call-in-non-const.rs2
-rw-r--r--src/test/ui/consts/const-blocks/fn-call-in-non-const.stderr14
-rw-r--r--src/test/ui/consts/const-blocks/migrate-fail.rs4
-rw-r--r--src/test/ui/consts/const-blocks/migrate-fail.stderr28
-rw-r--r--src/test/ui/consts/const-blocks/nll-fail.rs4
-rw-r--r--src/test/ui/consts/const-blocks/nll-fail.stderr28
-rw-r--r--src/test/ui/consts/const-blocks/trait-error.rs2
-rw-r--r--src/test/ui/consts/const-blocks/trait-error.stderr17
-rw-r--r--src/test/ui/consts/const-eval/issue-50814-2.stderr6
-rw-r--r--src/test/ui/consts/const-eval/issue-50814.stderr6
-rw-r--r--src/test/ui/consts/const-eval/ub-enum.32bit.stderr22
-rw-r--r--src/test/ui/consts/const-eval/ub-enum.64bit.stderr22
-rw-r--r--src/test/ui/consts/const-eval/ub-enum.rs4
-rw-r--r--src/test/ui/consts/const-eval/validate_uninhabited_zsts.32bit.stderr30
-rw-r--r--src/test/ui/consts/const-eval/validate_uninhabited_zsts.64bit.stderr30
-rw-r--r--src/test/ui/consts/const-eval/validate_uninhabited_zsts.rs17
-rw-r--r--src/test/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.mir.stderr2
-rw-r--r--src/test/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.rs3
-rw-r--r--src/test/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.thir.stderr2
-rw-r--r--src/test/ui/consts/const-float-bits-conv.rs30
-rw-r--r--src/test/ui/consts/const-float-bits-reject-conv.rs62
-rw-r--r--src/test/ui/consts/const-float-bits-reject-conv.stderr119
-rw-r--r--src/test/ui/consts/const-fn-error.stderr4
-rw-r--r--src/test/ui/consts/const-fn-in-vec.stderr6
-rw-r--r--src/test/ui/consts/const-for.stderr4
-rw-r--r--src/test/ui/consts/issue-19244.rs (renamed from src/test/ui/issues/issue-19244.rs)0
-rw-r--r--src/test/ui/did_you_mean/replace-impl-infer-ty-from-trait.fixed15
-rw-r--r--src/test/ui/did_you_mean/replace-impl-infer-ty-from-trait.rs15
-rw-r--r--src/test/ui/did_you_mean/replace-impl-infer-ty-from-trait.stderr18
-rw-r--r--src/test/ui/error-codes/E0117.stderr2
-rw-r--r--src/test/ui/error-codes/E0133.thir.stderr2
-rw-r--r--src/test/ui/error-codes/E0283.stderr5
-rw-r--r--src/test/ui/error-codes/E0771.stderr16
-rw-r--r--src/test/ui/extern-flag/no-nounused.rs6
-rw-r--r--src/test/ui/extern-flag/no-nounused.stderr10
-rw-r--r--src/test/ui/extern-flag/nounused.rs7
-rw-r--r--src/test/ui/extern/extern-type-diag-not-similar.rs22
-rw-r--r--src/test/ui/extern/extern-type-diag-not-similar.stderr16
-rw-r--r--src/test/ui/feature-gates/feature-gate-infer_static_outlives_requirements.stderr6
-rw-r--r--src/test/ui/feature-gates/feature-gate-yeet_expr-in-cfg.rs19
-rw-r--r--src/test/ui/feature-gates/feature-gate-yeet_expr-in-cfg.stderr21
-rw-r--r--src/test/ui/feature-gates/feature-gate-yeet_expr.rs9
-rw-r--r--src/test/ui/feature-gates/feature-gate-yeet_expr.stderr21
-rw-r--r--src/test/ui/foreign-unsafe-fn-called.rs3
-rw-r--r--src/test/ui/foreign-unsafe-fn-called.thir.stderr2
-rw-r--r--src/test/ui/functions-closures/fn-help-with-err.rs6
-rw-r--r--src/test/ui/functions-closures/fn-help-with-err.stderr6
-rw-r--r--src/test/ui/generator/issue-88653.rs3
-rw-r--r--src/test/ui/generator/issue-88653.stderr18
-rw-r--r--src/test/ui/generator/issue-93161.rs3
-rw-r--r--src/test/ui/generator/type-mismatch-signature-deduction.rs1
-rw-r--r--src/test/ui/generator/type-mismatch-signature-deduction.stderr24
-rw-r--r--src/test/ui/generic-associated-types/issue-70304.rs1
-rw-r--r--src/test/ui/generic-associated-types/issue-70304.stderr14
-rw-r--r--src/test/ui/generic-associated-types/issue-86483.stderr16
-rw-r--r--src/test/ui/generic-associated-types/issue-91139.migrate.stderr7
-rw-r--r--src/test/ui/generic-associated-types/issue-92096.migrate.stderr18
-rw-r--r--src/test/ui/generic-associated-types/issue-95305.rs17
-rw-r--r--src/test/ui/generic-associated-types/issue-95305.stderr25
-rw-r--r--src/test/ui/generic-associated-types/missing-bounds.fixed46
-rw-r--r--src/test/ui/generic-associated-types/missing-bounds.rs2
-rw-r--r--src/test/ui/generic-associated-types/missing-bounds.stderr26
-rw-r--r--src/test/ui/generic-associated-types/unsatified-item-lifetime-bound.stderr2
-rw-r--r--src/test/ui/generics/issue-80512-param-reordering-with-defaults.rs (renamed from src/test/ui/issues/issue-80512-param-reordering-with-defaults.rs)0
-rw-r--r--src/test/ui/generics/issue-80512-param-reordering-with-defaults.stderr (renamed from src/test/ui/issues/issue-80512-param-reordering-with-defaults.stderr)0
-rw-r--r--src/test/ui/generics/post_monomorphization_error_backtrace.rs33
-rw-r--r--src/test/ui/generics/post_monomorphization_error_backtrace.stderr31
-rw-r--r--src/test/ui/hrtb/issue-30786.migrate.stderr8
-rw-r--r--src/test/ui/hrtb/issue-30786.nll.stderr8
-rw-r--r--src/test/ui/impl-trait/bound-normalization-fail.rs2
-rw-r--r--src/test/ui/impl-trait/bound-normalization-fail.stderr53
-rw-r--r--src/test/ui/impl-trait/cross-return-site-inference.rs6
-rw-r--r--src/test/ui/impl-trait/cross-return-site-inference.stderr30
-rw-r--r--src/test/ui/impl-trait/equal-hidden-lifetimes.stderr2
-rw-r--r--src/test/ui/impl-trait/fallback_inference.rs7
-rw-r--r--src/test/ui/impl-trait/fallback_inference.stderr9
-rw-r--r--src/test/ui/impl-trait/issue-72911.rs2
-rw-r--r--src/test/ui/impl-trait/issue-72911.stderr10
-rw-r--r--src/test/ui/impl-trait/issues/issue-67830.stderr10
-rw-r--r--src/test/ui/impl-trait/issues/issue-86800.rs6
-rw-r--r--src/test/ui/impl-trait/issues/issue-88236-2.nll.stderr6
-rw-r--r--src/test/ui/impl-trait/issues/issue-88236-2.stderr11
-rw-r--r--src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr45
-rw-r--r--src/test/ui/impl-trait/must_outlive_least_region_or_bound.rs1
-rw-r--r--src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr24
-rw-r--r--src/test/ui/impl-trait/nested-return-type2-tait.rs1
-rw-r--r--src/test/ui/impl-trait/nested-return-type2-tait.stderr22
-rw-r--r--src/test/ui/impl-trait/type_parameters_captured.nll.stderr9
-rw-r--r--src/test/ui/impl-trait/type_parameters_captured.rs1
-rw-r--r--src/test/ui/impl-trait/type_parameters_captured.stderr22
-rw-r--r--src/test/ui/impl-trait/where-allowed-2.rs3
-rw-r--r--src/test/ui/impl-trait/where-allowed-2.stderr9
-rw-r--r--src/test/ui/inference/question-mark-type-infer.stderr14
-rw-r--r--src/test/ui/intrinsics/unchecked_math_unsafe.thir.stderr6
-rw-r--r--src/test/ui/issues-71798.rs1
-rw-r--r--src/test/ui/issues-71798.stderr18
-rw-r--r--src/test/ui/issues/issue-16922.nll.stderr5
-rw-r--r--src/test/ui/issues/issue-28776.thir.stderr2
-rw-r--r--src/test/ui/issues/issue-29124.stderr12
-rw-r--r--src/test/ui/issues/issue-30438-c.rs1
-rw-r--r--src/test/ui/issues/issue-30438-c.stderr12
-rw-r--r--src/test/ui/issues/issue-3080.thir.stderr2
-rw-r--r--src/test/ui/issues/issue-32709.stderr4
-rw-r--r--src/test/ui/issues/issue-35668.stderr6
-rw-r--r--src/test/ui/issues/issue-57362-1.stderr5
-rw-r--r--src/test/ui/issues/issue-5844.thir.stderr2
-rw-r--r--src/test/ui/iterators/collect-into-array.rs7
-rw-r--r--src/test/ui/iterators/collect-into-array.stderr16
-rw-r--r--src/test/ui/iterators/collect-into-slice.rs2
-rw-r--r--src/test/ui/iterators/collect-into-slice.stderr2
-rw-r--r--src/test/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.rs54
-rw-r--r--src/test/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.stderr720
-rw-r--r--src/test/ui/lifetimes/copy_modulo_regions.rs19
-rw-r--r--src/test/ui/lifetimes/copy_modulo_regions.stderr14
-rw-r--r--src/test/ui/lifetimes/elided-lifetime-in-path-in-impl-Fn.rs19
-rw-r--r--src/test/ui/lifetimes/issue-90170-elision-mismatch.nll.stderr15
-rw-r--r--src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.stderr39
-rw-r--r--src/test/ui/lifetimes/lifetime-elision-return-type-trait.rs1
-rw-r--r--src/test/ui/lifetimes/lifetime-elision-return-type-trait.stderr13
-rw-r--r--src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-2.nll.stderr5
-rw-r--r--src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-3.nll.stderr10
-rw-r--r--src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-return-type-is-anon.nll.stderr5
-rw-r--r--src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-self-is-anon.nll.stderr5
-rw-r--r--src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-fn-items.nll.stderr5
-rw-r--r--src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-impl-items.nll.stderr5
-rw-r--r--src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-trait-objects.nll.stderr5
-rw-r--r--src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions.nll.stderr5
-rw-r--r--src/test/ui/lifetimes/lifetime-errors/issue_74400.nll.stderr7
-rw-r--r--src/test/ui/lint/issue-1866.rs (renamed from src/test/ui/issues/issue-1866.rs)0
-rw-r--r--src/test/ui/lint/issue-1866.stderr (renamed from src/test/ui/issues/issue-1866.stderr)0
-rw-r--r--src/test/ui/lint/issue-20343.rs (renamed from src/test/ui/issues/issue-20343.rs)0
-rw-r--r--src/test/ui/lint/unreachable_pub-pub_crate.rs5
-rw-r--r--src/test/ui/lint/unreachable_pub-pub_crate.stderr18
-rw-r--r--src/test/ui/lint/unreachable_pub.rs5
-rw-r--r--src/test/ui/lint/unreachable_pub.stderr18
-rw-r--r--src/test/ui/macros/issue-41776.rs (renamed from src/test/ui/issues/issue-41776.rs)0
-rw-r--r--src/test/ui/macros/issue-41776.stderr (renamed from src/test/ui/issues/issue-41776.stderr)0
-rw-r--r--src/test/ui/macros/issue-44127.rs (renamed from src/test/ui/issues/issue-44127.rs)0
-rw-r--r--src/test/ui/macros/trace-macro.stderr2
-rw-r--r--src/test/ui/match/issue-82392.stdout2
-rw-r--r--src/test/ui/methods/issues/issue-90315.rs7
-rw-r--r--src/test/ui/methods/issues/issue-90315.stderr13
-rw-r--r--src/test/ui/moves/use_of_moved_value_copy_suggestions.fixed16
-rw-r--r--src/test/ui/moves/use_of_moved_value_copy_suggestions.rs6
-rw-r--r--src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr16
-rw-r--r--src/test/ui/never_type/issue-52443.stderr4
-rw-r--r--src/test/ui/never_type/issue-96335.rs5
-rw-r--r--src/test/ui/never_type/issue-96335.stderr35
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr7
-rw-r--r--src/test/ui/nll/issue-45696-no-variant-box-recur.rs (renamed from src/test/ui/issues/issue-45696-no-variant-box-recur.rs)0
-rw-r--r--src/test/ui/nll/mir_check_cast_unsize.stderr5
-rw-r--r--src/test/ui/nll/ty-outlives/impl-trait-outlives.stderr14
-rw-r--r--src/test/ui/nll/ty-outlives/projection-implied-bounds.stderr7
-rw-r--r--src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr2
-rw-r--r--src/test/ui/nll/ty-outlives/projection-no-regions-fn.stderr2
-rw-r--r--src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr14
-rw-r--r--src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr2
-rw-r--r--src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-bound.nll.stderr1
-rw-r--r--src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.nll.stderr1
-rw-r--r--src/test/ui/nll/ty-outlives/projection-where-clause-none.stderr7
-rw-r--r--src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr7
-rw-r--r--src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr14
-rw-r--r--src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr14
-rw-r--r--src/test/ui/nll/ty-outlives/ty-param-fn-body.stderr7
-rw-r--r--src/test/ui/nll/ty-outlives/ty-param-fn.stderr14
-rw-r--r--src/test/ui/object-lifetime/object-lifetime-default-from-box-error.nll.stderr5
-rw-r--r--src/test/ui/object-safety/issue-19538.rs (renamed from src/test/ui/issues/issue-19538.rs)0
-rw-r--r--src/test/ui/object-safety/issue-19538.stderr (renamed from src/test/ui/issues/issue-19538.stderr)0
-rw-r--r--src/test/ui/or-patterns/missing-bindings.stderr32
-rw-r--r--src/test/ui/parser/issue-61858.rs (renamed from src/test/ui/issues/issue-61858.rs)0
-rw-r--r--src/test/ui/parser/issue-61858.stderr (renamed from src/test/ui/issues/issue-61858.stderr)0
-rw-r--r--src/test/ui/parser/raw/raw-str-unbalanced.rs20
-rw-r--r--src/test/ui/parser/raw/raw-str-unbalanced.stderr36
-rw-r--r--src/test/ui/pattern/issue-72574-1.rs (renamed from src/test/ui/issues/issue-72574-1.rs)0
-rw-r--r--src/test/ui/pattern/issue-72574-1.stderr (renamed from src/test/ui/issues/issue-72574-1.stderr)0
-rw-r--r--src/test/ui/pattern/usefulness/tuple-struct-nonexhaustive.stderr2
-rw-r--r--src/test/ui/polymorphization/generators.stderr12
-rw-r--r--src/test/ui/polymorphization/predicates.stderr6
-rw-r--r--src/test/ui/proc-macro/nodelim-groups.rs2
-rw-r--r--src/test/ui/regions/issue-28848.base.stderr (renamed from src/test/ui/regions/issue-28848.stderr)6
-rw-r--r--src/test/ui/regions/issue-28848.nll.stderr2
-rw-r--r--src/test/ui/regions/issue-28848.rs8
-rw-r--r--src/test/ui/regions/region-invariant-static-error-reporting.base.stderr (renamed from src/test/ui/regions/region-invariant-static-error-reporting.stderr)4
-rw-r--r--src/test/ui/regions/region-invariant-static-error-reporting.nll.stderr2
-rw-r--r--src/test/ui/regions/region-invariant-static-error-reporting.rs12
-rw-r--r--src/test/ui/regions/region-lifetime-bounds-on-fns-where-clause.base.stderr (renamed from src/test/ui/regions/region-lifetime-bounds-on-fns-where-clause.stderr)6
-rw-r--r--src/test/ui/regions/region-lifetime-bounds-on-fns-where-clause.nll.stderr8
-rw-r--r--src/test/ui/regions/region-lifetime-bounds-on-fns-where-clause.rs16
-rw-r--r--src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.base.stderr (renamed from src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.stderr)8
-rw-r--r--src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.nll.stderr10
-rw-r--r--src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.polonius.stderr82
-rw-r--r--src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.rs19
-rw-r--r--src/test/ui/regions/region-object-lifetime-2.base.stderr (renamed from src/test/ui/regions/region-object-lifetime-2.stderr)10
-rw-r--r--src/test/ui/regions/region-object-lifetime-2.nll.stderr2
-rw-r--r--src/test/ui/regions/region-object-lifetime-2.rs8
-rw-r--r--src/test/ui/regions/region-object-lifetime-4.base.stderr (renamed from src/test/ui/regions/region-object-lifetime-4.stderr)10
-rw-r--r--src/test/ui/regions/region-object-lifetime-4.nll.stderr2
-rw-r--r--src/test/ui/regions/region-object-lifetime-4.rs8
-rw-r--r--src/test/ui/regions/region-object-lifetime-in-coercion.base.stderr (renamed from src/test/ui/regions/region-object-lifetime-in-coercion.stderr)20
-rw-r--r--src/test/ui/regions/region-object-lifetime-in-coercion.nll.stderr31
-rw-r--r--src/test/ui/regions/region-object-lifetime-in-coercion.rs20
-rw-r--r--src/test/ui/regions/regions-addr-of-self.base.stderr (renamed from src/test/ui/regions/regions-addr-of-self.stderr)2
-rw-r--r--src/test/ui/regions/regions-addr-of-self.nll.stderr2
-rw-r--r--src/test/ui/regions/regions-addr-of-self.rs8
-rw-r--r--src/test/ui/regions/regions-addr-of-upvar-self.base.stderr (renamed from src/test/ui/regions/regions-addr-of-upvar-self.stderr)8
-rw-r--r--src/test/ui/regions/regions-addr-of-upvar-self.nll.stderr6
-rw-r--r--src/test/ui/regions/regions-addr-of-upvar-self.rs10
-rw-r--r--src/test/ui/regions/regions-bounded-by-trait-requiring-static.base.stderr (renamed from src/test/ui/regions/regions-bounded-by-trait-requiring-static.stderr)24
-rw-r--r--src/test/ui/regions/regions-bounded-by-trait-requiring-static.nll.stderr12
-rw-r--r--src/test/ui/regions/regions-bounded-by-trait-requiring-static.rs28
-rw-r--r--src/test/ui/regions/regions-bounded-method-type-parameters-cross-crate.base.stderr (renamed from src/test/ui/regions/regions-bounded-method-type-parameters-cross-crate.stderr)2
-rw-r--r--src/test/ui/regions/regions-bounded-method-type-parameters-cross-crate.nll.stderr2
-rw-r--r--src/test/ui/regions/regions-bounded-method-type-parameters-cross-crate.rs7
-rw-r--r--src/test/ui/regions/regions-bounded-method-type-parameters-trait-bound.base.stderr (renamed from src/test/ui/regions/regions-bounded-method-type-parameters-trait-bound.stderr)2
-rw-r--r--src/test/ui/regions/regions-bounded-method-type-parameters-trait-bound.nll.stderr2
-rw-r--r--src/test/ui/regions/regions-bounded-method-type-parameters-trait-bound.rs8
-rw-r--r--src/test/ui/regions/regions-bounded-method-type-parameters.base.stderr (renamed from src/test/ui/regions/regions-bounded-method-type-parameters.stderr)4
-rw-r--r--src/test/ui/regions/regions-bounded-method-type-parameters.nll.stderr2
-rw-r--r--src/test/ui/regions/regions-bounded-method-type-parameters.rs7
-rw-r--r--src/test/ui/regions/regions-bounds.base.stderr (renamed from src/test/ui/regions/regions-bounds.stderr)12
-rw-r--r--src/test/ui/regions/regions-bounds.nll.stderr4
-rw-r--r--src/test/ui/regions/regions-bounds.rs12
-rw-r--r--src/test/ui/regions/regions-close-associated-type-into-object.base.stderr (renamed from src/test/ui/regions/regions-close-associated-type-into-object.stderr)8
-rw-r--r--src/test/ui/regions/regions-close-associated-type-into-object.nll.stderr12
-rw-r--r--src/test/ui/regions/regions-close-associated-type-into-object.rs4
-rw-r--r--src/test/ui/regions/regions-close-object-into-object-2.base.stderr (renamed from src/test/ui/regions/regions-close-object-into-object-2.stderr)4
-rw-r--r--src/test/ui/regions/regions-close-object-into-object-2.nll.stderr13
-rw-r--r--src/test/ui/regions/regions-close-object-into-object-2.rs9
-rw-r--r--src/test/ui/regions/regions-close-object-into-object-4.base.stderr (renamed from src/test/ui/regions/regions-close-object-into-object-4.stderr)4
-rw-r--r--src/test/ui/regions/regions-close-object-into-object-4.nll.stderr49
-rw-r--r--src/test/ui/regions/regions-close-object-into-object-4.rs14
-rw-r--r--src/test/ui/regions/regions-close-object-into-object-5.base.stderr (renamed from src/test/ui/regions/regions-close-object-into-object-5.stderr)73
-rw-r--r--src/test/ui/regions/regions-close-object-into-object-5.nll.stderr38
-rw-r--r--src/test/ui/regions/regions-close-object-into-object-5.rs11
-rw-r--r--src/test/ui/regions/regions-close-over-type-parameter-1.base.stderr (renamed from src/test/ui/regions/regions-close-over-type-parameter-1.stderr)18
-rw-r--r--src/test/ui/regions/regions-close-over-type-parameter-1.nll.stderr18
-rw-r--r--src/test/ui/regions/regions-close-over-type-parameter-1.rs3
-rw-r--r--src/test/ui/regions/regions-close-over-type-parameter-multiple.base.stderr (renamed from src/test/ui/regions/regions-close-over-type-parameter-multiple.stderr)10
-rw-r--r--src/test/ui/regions/regions-close-over-type-parameter-multiple.nll.stderr2
-rw-r--r--src/test/ui/regions/regions-close-over-type-parameter-multiple.rs7
-rw-r--r--src/test/ui/regions/regions-close-param-into-object.base.stderr (renamed from src/test/ui/regions/regions-close-param-into-object.stderr)40
-rw-r--r--src/test/ui/regions/regions-close-param-into-object.nll.stderr36
-rw-r--r--src/test/ui/regions/regions-close-param-into-object.rs4
-rw-r--r--src/test/ui/regions/regions-creating-enums3.base.stderr (renamed from src/test/ui/regions/regions-creating-enums3.stderr)2
-rw-r--r--src/test/ui/regions/regions-creating-enums3.nll.stderr2
-rw-r--r--src/test/ui/regions/regions-creating-enums3.rs8
-rw-r--r--src/test/ui/regions/regions-creating-enums4.base.stderr (renamed from src/test/ui/regions/regions-creating-enums4.stderr)10
-rw-r--r--src/test/ui/regions/regions-creating-enums4.nll.stderr2
-rw-r--r--src/test/ui/regions/regions-creating-enums4.rs8
-rw-r--r--src/test/ui/regions/regions-early-bound-error-method.base.stderr (renamed from src/test/ui/regions/regions-early-bound-error-method.stderr)6
-rw-r--r--src/test/ui/regions/regions-early-bound-error-method.nll.stderr2
-rw-r--r--src/test/ui/regions/regions-early-bound-error-method.rs7
-rw-r--r--src/test/ui/regions/regions-early-bound-error.base.stderr (renamed from src/test/ui/regions/regions-early-bound-error.stderr)6
-rw-r--r--src/test/ui/regions/regions-early-bound-error.nll.stderr2
-rw-r--r--src/test/ui/regions/regions-early-bound-error.rs7
-rw-r--r--src/test/ui/regions/regions-fn-subtyping-return-static-fail.base.stderr (renamed from src/test/ui/regions/regions-fn-subtyping-return-static-fail.stderr)2
-rw-r--r--src/test/ui/regions/regions-fn-subtyping-return-static-fail.nll.stderr2
-rw-r--r--src/test/ui/regions/regions-fn-subtyping-return-static-fail.rs4
-rw-r--r--src/test/ui/regions/regions-free-region-ordering-callee.base.stderr (renamed from src/test/ui/regions/regions-free-region-ordering-callee.stderr)4
-rw-r--r--src/test/ui/regions/regions-free-region-ordering-callee.nll.stderr4
-rw-r--r--src/test/ui/regions/regions-free-region-ordering-callee.rs11
-rw-r--r--src/test/ui/regions/regions-free-region-ordering-incorrect.base.stderr (renamed from src/test/ui/regions/regions-free-region-ordering-incorrect.stderr)10
-rw-r--r--src/test/ui/regions/regions-free-region-ordering-incorrect.nll.stderr2
-rw-r--r--src/test/ui/regions/regions-free-region-ordering-incorrect.rs8
-rw-r--r--src/test/ui/regions/regions-free-region-outlives-static-outlives-free-region.rs2
-rw-r--r--src/test/ui/regions/regions-free-region-outlives-static-outlives-free-region.stderr10
-rw-r--r--src/test/ui/regions/regions-implied-bounds-projection-gap-1.base.stderr (renamed from src/test/ui/regions/regions-implied-bounds-projection-gap-1.stderr)10
-rw-r--r--src/test/ui/regions/regions-implied-bounds-projection-gap-1.nll.stderr9
-rw-r--r--src/test/ui/regions/regions-implied-bounds-projection-gap-1.rs4
-rw-r--r--src/test/ui/regions/regions-infer-bound-from-trait-self.base.stderr (renamed from src/test/ui/regions/regions-infer-bound-from-trait-self.stderr)4
-rw-r--r--src/test/ui/regions/regions-infer-bound-from-trait-self.nll.stderr3
-rw-r--r--src/test/ui/regions/regions-infer-bound-from-trait-self.rs4
-rw-r--r--src/test/ui/regions/regions-infer-bound-from-trait.base.stderr (renamed from src/test/ui/regions/regions-infer-bound-from-trait.stderr)20
-rw-r--r--src/test/ui/regions/regions-infer-bound-from-trait.nll.stderr18
-rw-r--r--src/test/ui/regions/regions-infer-bound-from-trait.rs4
-rw-r--r--src/test/ui/regions/regions-infer-contravariance-due-to-decl.base.stderr (renamed from src/test/ui/regions/regions-infer-contravariance-due-to-decl.stderr)2
-rw-r--r--src/test/ui/regions/regions-infer-contravariance-due-to-decl.nll.stderr2
-rw-r--r--src/test/ui/regions/regions-infer-contravariance-due-to-decl.rs8
-rw-r--r--src/test/ui/regions/regions-infer-covariance-due-to-decl.base.stderr (renamed from src/test/ui/regions/regions-infer-covariance-due-to-decl.stderr)2
-rw-r--r--src/test/ui/regions/regions-infer-covariance-due-to-decl.nll.stderr2
-rw-r--r--src/test/ui/regions/regions-infer-covariance-due-to-decl.rs8
-rw-r--r--src/test/ui/regions/regions-infer-invariance-due-to-decl.base.stderr (renamed from src/test/ui/regions/regions-infer-invariance-due-to-decl.stderr)4
-rw-r--r--src/test/ui/regions/regions-infer-invariance-due-to-decl.nll.stderr2
-rw-r--r--src/test/ui/regions/regions-infer-invariance-due-to-decl.rs8
-rw-r--r--src/test/ui/regions/regions-infer-invariance-due-to-mutability-3.base.stderr (renamed from src/test/ui/regions/regions-infer-invariance-due-to-mutability-3.stderr)4
-rw-r--r--src/test/ui/regions/regions-infer-invariance-due-to-mutability-3.nll.stderr2
-rw-r--r--src/test/ui/regions/regions-infer-invariance-due-to-mutability-3.rs8
-rw-r--r--src/test/ui/regions/regions-infer-invariance-due-to-mutability-4.base.stderr (renamed from src/test/ui/regions/regions-infer-invariance-due-to-mutability-4.stderr)4
-rw-r--r--src/test/ui/regions/regions-infer-invariance-due-to-mutability-4.nll.stderr2
-rw-r--r--src/test/ui/regions/regions-infer-invariance-due-to-mutability-4.rs8
-rw-r--r--src/test/ui/regions/regions-infer-not-param.base.stderr (renamed from src/test/ui/regions/regions-infer-not-param.stderr)18
-rw-r--r--src/test/ui/regions/regions-infer-not-param.nll.stderr6
-rw-r--r--src/test/ui/regions/regions-infer-not-param.rs23
-rw-r--r--src/test/ui/regions/regions-infer-paramd-indirect.base.stderr (renamed from src/test/ui/regions/regions-infer-paramd-indirect.stderr)6
-rw-r--r--src/test/ui/regions/regions-infer-paramd-indirect.nll.stderr2
-rw-r--r--src/test/ui/regions/regions-infer-paramd-indirect.rs13
-rw-r--r--src/test/ui/regions/regions-lifetime-bounds-on-fns.base.stderr (renamed from src/test/ui/regions/regions-lifetime-bounds-on-fns.stderr)6
-rw-r--r--src/test/ui/regions/regions-lifetime-bounds-on-fns.nll.stderr8
-rw-r--r--src/test/ui/regions/regions-lifetime-bounds-on-fns.rs16
-rw-r--r--src/test/ui/regions/regions-nested-fns.base.stderr (renamed from src/test/ui/regions/regions-nested-fns.stderr)24
-rw-r--r--src/test/ui/regions/regions-nested-fns.nll.stderr10
-rw-r--r--src/test/ui/regions/regions-nested-fns.rs14
-rw-r--r--src/test/ui/regions/regions-outlives-projection-container.base.stderr (renamed from src/test/ui/regions/regions-outlives-projection-container.stderr)24
-rw-r--r--src/test/ui/regions/regions-outlives-projection-container.nll.stderr8
-rw-r--r--src/test/ui/regions/regions-outlives-projection-container.rs17
-rw-r--r--src/test/ui/regions/regions-proc-bound-capture.base.stderr (renamed from src/test/ui/regions/regions-proc-bound-capture.stderr)4
-rw-r--r--src/test/ui/regions/regions-proc-bound-capture.nll.stderr11
-rw-r--r--src/test/ui/regions/regions-proc-bound-capture.rs8
-rw-r--r--src/test/ui/regions/regions-reborrow-from-shorter-mut-ref-mut-ref.base.stderr (renamed from src/test/ui/regions/regions-reborrow-from-shorter-mut-ref-mut-ref.stderr)2
-rw-r--r--src/test/ui/regions/regions-reborrow-from-shorter-mut-ref-mut-ref.nll.stderr2
-rw-r--r--src/test/ui/regions/regions-reborrow-from-shorter-mut-ref-mut-ref.rs8
-rw-r--r--src/test/ui/regions/regions-reborrow-from-shorter-mut-ref.base.stderr (renamed from src/test/ui/regions/regions-reborrow-from-shorter-mut-ref.stderr)2
-rw-r--r--src/test/ui/regions/regions-reborrow-from-shorter-mut-ref.nll.stderr2
-rw-r--r--src/test/ui/regions/regions-reborrow-from-shorter-mut-ref.rs8
-rw-r--r--src/test/ui/regions/regions-ret-borrowed-1.base.stderr (renamed from src/test/ui/regions/regions-ret-borrowed-1.stderr)10
-rw-r--r--src/test/ui/regions/regions-ret-borrowed-1.nll.stderr2
-rw-r--r--src/test/ui/regions/regions-ret-borrowed-1.rs7
-rw-r--r--src/test/ui/regions/regions-ret-borrowed.base.stderr (renamed from src/test/ui/regions/regions-ret-borrowed.stderr)10
-rw-r--r--src/test/ui/regions/regions-ret-borrowed.nll.stderr2
-rw-r--r--src/test/ui/regions/regions-ret-borrowed.rs7
-rw-r--r--src/test/ui/regions/regions-static-bound-rpass.rs7
-rw-r--r--src/test/ui/regions/regions-static-bound-rpass.stderr26
-rw-r--r--src/test/ui/regions/regions-static-bound.base.stderr (renamed from src/test/ui/regions/regions-static-bound.stderr)32
-rw-r--r--src/test/ui/regions/regions-static-bound.nll.stderr26
-rw-r--r--src/test/ui/regions/regions-static-bound.rs20
-rw-r--r--src/test/ui/regions/regions-trait-object-subtyping.base.stderr (renamed from src/test/ui/regions/regions-trait-object-subtyping.stderr)22
-rw-r--r--src/test/ui/regions/regions-trait-object-subtyping.nll.stderr4
-rw-r--r--src/test/ui/regions/regions-trait-object-subtyping.rs14
-rw-r--r--src/test/ui/regions/regions-variance-contravariant-use-covariant-in-second-position.base.stderr (renamed from src/test/ui/regions/regions-variance-contravariant-use-covariant-in-second-position.stderr)2
-rw-r--r--src/test/ui/regions/regions-variance-contravariant-use-covariant-in-second-position.nll.stderr2
-rw-r--r--src/test/ui/regions/regions-variance-contravariant-use-covariant-in-second-position.rs8
-rw-r--r--src/test/ui/regions/regions-variance-contravariant-use-covariant.base.stderr (renamed from src/test/ui/regions/regions-variance-contravariant-use-covariant.stderr)2
-rw-r--r--src/test/ui/regions/regions-variance-contravariant-use-covariant.nll.stderr2
-rw-r--r--src/test/ui/regions/regions-variance-contravariant-use-covariant.rs8
-rw-r--r--src/test/ui/regions/regions-variance-covariant-use-contravariant.base.stderr (renamed from src/test/ui/regions/regions-variance-covariant-use-contravariant.stderr)2
-rw-r--r--src/test/ui/regions/regions-variance-covariant-use-contravariant.nll.stderr2
-rw-r--r--src/test/ui/regions/regions-variance-covariant-use-contravariant.rs8
-rw-r--r--src/test/ui/regions/regions-variance-invariant-use-contravariant.base.stderr (renamed from src/test/ui/regions/regions-variance-invariant-use-contravariant.stderr)2
-rw-r--r--src/test/ui/regions/regions-variance-invariant-use-contravariant.nll.stderr2
-rw-r--r--src/test/ui/regions/regions-variance-invariant-use-contravariant.rs8
-rw-r--r--src/test/ui/regions/regions-variance-invariant-use-covariant.base.stderr (renamed from src/test/ui/regions/regions-variance-invariant-use-covariant.stderr)4
-rw-r--r--src/test/ui/regions/regions-variance-invariant-use-covariant.nll.stderr2
-rw-r--r--src/test/ui/regions/regions-variance-invariant-use-covariant.rs8
-rw-r--r--src/test/ui/repeat-expr/infer.rs16
-rw-r--r--src/test/ui/repeat-expr/repeat-expr-in-static.rs (renamed from src/test/ui/repeat-expr-in-static.rs)0
-rw-r--r--src/test/ui/repeat-expr/repeat-to-run-dtor-twice.rs (renamed from src/test/ui/repeat-to-run-dtor-twice.rs)0
-rw-r--r--src/test/ui/repeat-expr/repeat-to-run-dtor-twice.stderr (renamed from src/test/ui/repeat-to-run-dtor-twice.stderr)6
-rw-r--r--src/test/ui/repeat-expr/repeat_count.rs (renamed from src/test/ui/repeat_count.rs)0
-rw-r--r--src/test/ui/repeat-expr/repeat_count.stderr (renamed from src/test/ui/repeat_count.stderr)0
-rw-r--r--src/test/ui/resolve/issue-26545.rs (renamed from src/test/ui/issues/issue-26545.rs)0
-rw-r--r--src/test/ui/resolve/issue-26545.stderr (renamed from src/test/ui/issues/issue-26545.stderr)0
-rw-r--r--src/test/ui/resolve/resolve-inconsistent-names.rs9
-rw-r--r--src/test/ui/resolve/resolve-inconsistent-names.stderr22
-rw-r--r--src/test/ui/rfc-2093-infer-outlives/dont-infer-static.stderr6
-rw-r--r--src/test/ui/rfc-2093-infer-outlives/regions-enum-not-wf.stderr22
-rw-r--r--src/test/ui/rfc-2093-infer-outlives/regions-struct-not-wf.stderr13
-rw-r--r--src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.rs2
-rw-r--r--src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr2
-rw-r--r--src/test/ui/rfc1623.base.stderr2
-rw-r--r--src/test/ui/rfc1623.nll.stderr52
-rw-r--r--src/test/ui/rfc1623.rs8
-rw-r--r--src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.mir.stderr18
-rw-r--r--src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.rs40
-rw-r--r--src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.thir.stderr38
-rw-r--r--src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.nll.stderr10
-rw-r--r--src/test/ui/self/elision/lt-ref-self.nll.stderr30
-rw-r--r--src/test/ui/self/elision/ref-mut-self.nll.stderr30
-rw-r--r--src/test/ui/self/elision/ref-mut-struct.nll.stderr25
-rw-r--r--src/test/ui/self/elision/ref-self.nll.stderr35
-rw-r--r--src/test/ui/self/elision/ref-struct.nll.stderr25
-rw-r--r--src/test/ui/span/issue-39698.stderr20
-rw-r--r--src/test/ui/static/static-lifetime-bound.stderr2
-rw-r--r--src/test/ui/statics/issue-17233.rs (renamed from src/test/ui/issues/issue-17233.rs)0
-rw-r--r--src/test/ui/statics/uninhabited-static.rs4
-rw-r--r--src/test/ui/statics/uninhabited-static.stderr18
-rw-r--r--src/test/ui/suggestions/bound-suggestions.fixed2
-rw-r--r--src/test/ui/suggestions/bound-suggestions.stderr6
-rw-r--r--src/test/ui/suggestions/field-has-method.rs23
-rw-r--r--src/test/ui/suggestions/field-has-method.stderr17
-rw-r--r--src/test/ui/suggestions/impl-trait-return-trailing-semicolon.rs4
-rw-r--r--src/test/ui/suggestions/impl-trait-return-trailing-semicolon.stderr40
-rw-r--r--src/test/ui/suggestions/impl-trait-with-missing-trait-bounds-in-arg.stderr2
-rw-r--r--src/test/ui/suggestions/invalid-bin-op.stderr5
-rw-r--r--src/test/ui/suggestions/issue-21673.stderr6
-rw-r--r--src/test/ui/suggestions/issue-53692.rs (renamed from src/test/ui/issues/issue-53692.rs)0
-rw-r--r--src/test/ui/suggestions/issue-53692.stderr (renamed from src/test/ui/issues/issue-53692.stderr)0
-rw-r--r--src/test/ui/suggestions/issue-81098.rs2
-rw-r--r--src/test/ui/suggestions/issue-81098.stderr31
-rw-r--r--src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature-2.nll.stderr12
-rw-r--r--src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature-2.stderr6
-rw-r--r--src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.nll.stderr71
-rw-r--r--src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.rs4
-rw-r--r--src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr170
-rw-r--r--src/test/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.nll.stderr22
-rw-r--r--src/test/ui/suggestions/restrict-type-argument.stderr4
-rw-r--r--src/test/ui/suggestions/suggest-impl-trait-lifetime.nll.stderr7
-rw-r--r--src/test/ui/suggestions/suggest-impl-trait-lifetime.stderr7
-rw-r--r--src/test/ui/suggestions/suggest-using-chars.rs (renamed from src/test/ui/suggest-using-chars.rs)0
-rw-r--r--src/test/ui/suggestions/suggest-using-chars.stderr (renamed from src/test/ui/suggest-using-chars.stderr)0
-rw-r--r--src/test/ui/symbol-names/x86-stdcall.rs13
-rw-r--r--src/test/ui/threads-sendsync/issue-29488.rs (renamed from src/test/ui/issues/issue-29488.rs)0
-rw-r--r--src/test/ui/threads-sendsync/issue-43733.mir.stderr4
-rw-r--r--src/test/ui/threads-sendsync/issue-43733.rs9
-rw-r--r--src/test/ui/threads-sendsync/issue-43733.thir.stderr8
-rw-r--r--src/test/ui/traits/do-not-mention-type-params-by-name-in-suggestion-issue-96292.rs20
-rw-r--r--src/test/ui/traits/do-not-mention-type-params-by-name-in-suggestion-issue-96292.stderr37
-rw-r--r--src/test/ui/traits/issue-43132.rs (renamed from src/test/ui/issues/issue-43132.rs)0
-rw-r--r--src/test/ui/traits/issue-65284-suggest-generic-trait-bound.stderr4
-rw-r--r--src/test/ui/traits/issue-71036.rs (renamed from src/test/ui/issues/issue-71036.rs)0
-rw-r--r--src/test/ui/traits/issue-71036.stderr (renamed from src/test/ui/issues/issue-71036.stderr)0
-rw-r--r--src/test/ui/traits/issue-77982.stderr8
-rw-r--r--src/test/ui/traits/issue-95898.stderr2
-rw-r--r--src/test/ui/traits/resolution-in-overloaded-op.stderr6
-rw-r--r--src/test/ui/try-trait/bad-interconversion.stderr16
-rw-r--r--src/test/ui/try-trait/option-to-result.stderr8
-rw-r--r--src/test/ui/try-trait/try-on-option.stderr4
-rw-r--r--src/test/ui/try-trait/yeet-for-option.rs11
-rw-r--r--src/test/ui/try-trait/yeet-for-result.rs11
-rw-r--r--src/test/ui/type-alias-impl-trait/bounds-are-checked.stderr2
-rw-r--r--src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.base.stderr7
-rw-r--r--src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.nll.stderr7
-rw-r--r--src/test/ui/type-alias-impl-trait/issue-60662.stdout2
-rw-r--r--src/test/ui/type-alias-impl-trait/nested-tait-inference.rs1
-rw-r--r--src/test/ui/type-alias-impl-trait/nested-tait-inference.stderr16
-rw-r--r--src/test/ui/type-alias-impl-trait/nested-tait-inference2.rs1
-rw-r--r--src/test/ui/type-alias-impl-trait/nested-tait-inference2.stderr17
-rw-r--r--src/test/ui/type/type-check/missing_trait_impl.stderr12
-rw-r--r--src/test/ui/typeck/issue-87181/empty-tuple-method.rs14
-rw-r--r--src/test/ui/typeck/issue-87181/empty-tuple-method.stderr16
-rw-r--r--src/test/ui/typeck/issue-87181/enum-variant.rs16
-rw-r--r--src/test/ui/typeck/issue-87181/enum-variant.stderr16
-rw-r--r--src/test/ui/typeck/issue-87181/tuple-field.rs14
-rw-r--r--src/test/ui/typeck/issue-87181/tuple-field.stderr16
-rw-r--r--src/test/ui/typeck/issue-87181/tuple-method.rs14
-rw-r--r--src/test/ui/typeck/issue-87181/tuple-method.stderr16
-rw-r--r--src/test/ui/typeck/typeck-default-trait-impl-cross-crate-coherence.stderr2
-rw-r--r--src/test/ui/typeck/typeck_type_placeholder_item.rs4
-rw-r--r--src/test/ui/typeck/typeck_type_placeholder_item.stderr36
-rw-r--r--src/test/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.stderr6
-rw-r--r--src/test/ui/underscore-lifetime/dyn-trait-underscore.nll.stderr5
-rw-r--r--src/test/ui/underscore-lifetime/underscore-lifetime-elison-mismatch.nll.stderr5
-rw-r--r--src/test/ui/uninhabited/privately-uninhabited-mir-call.rs29
-rw-r--r--src/test/ui/uninhabited/privately-uninhabited-mir-call.stderr9
-rw-r--r--src/test/ui/unsafe/issue-45087-unreachable-unsafe.mir.stderr20
-rw-r--r--src/test/ui/unsafe/issue-45087-unreachable-unsafe.rs23
-rw-r--r--src/test/ui/unsafe/issue-45087-unreachable-unsafe.thir.stderr20
-rw-r--r--src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.mir.stderr30
-rw-r--r--src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs12
-rw-r--r--src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.thir.stderr34
-rw-r--r--src/test/ui/unsafe/unsafe-const-fn.thir.stderr2
-rw-r--r--src/test/ui/unsafe/unsafe-fn-called-from-safe.rs4
-rw-r--r--src/test/ui/unsafe/unsafe-fn-called-from-safe.thir.stderr2
-rw-r--r--src/test/ui/unsafe/unsafe-fn-used-as-value.rs4
-rw-r--r--src/test/ui/unsafe/unsafe-fn-used-as-value.thir.stderr2
-rw-r--r--src/test/ui/unused-crate-deps/deny-attr.rs9
-rw-r--r--src/test/ui/unused-crate-deps/deny-attr.stderr14
-rw-r--r--src/test/ui/unused-crate-deps/deny-cmdline-json-silent.rs8
-rw-r--r--src/test/ui/unused-crate-deps/deny-cmdline-json-silent.stderr1
-rw-r--r--src/test/ui/unused-crate-deps/deny-cmdline-json.rs7
-rw-r--r--src/test/ui/unused-crate-deps/deny-cmdline-json.stderr1
-rw-r--r--src/test/ui/unused-crate-deps/deny-cmdline.rs8
-rw-r--r--src/test/ui/unused-crate-deps/deny-cmdline.stderr10
-rw-r--r--src/test/ui/unused-crate-deps/warn-cmdline-json.rs8
-rw-r--r--src/test/ui/unused-crate-deps/warn-cmdline-json.stderr1
-rw-r--r--src/test/ui/wf/wf-impl-associated-type-region.stderr7
-rw-r--r--src/test/ui/wf/wf-in-fn-type-static.stderr16
-rw-r--r--src/test/ui/wf/wf-in-obj-type-static.stderr8
-rw-r--r--src/test/ui/wf/wf-outlives-ty-in-fn-or-trait.stderr14
m---------src/tools/cargo0
-rw-r--r--src/tools/clippy/clippy_lints/src/cognitive_complexity.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/enum_variants.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/exhaustive_items.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/must_use.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/too_many_arguments.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/lifetimes.rs61
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/new_without_default.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/serde_api.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/trait_bounds.rs36
-rw-r--r--src/tools/clippy/clippy_lints/src/types/borrowed_box.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_async.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/inspector.rs37
-rw-r--r--src/tools/clippy/clippy_lints/src/wildcard_imports.rs4
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/sugg.rs1
-rw-r--r--src/tools/clippy/tests/ui/extra_unused_lifetimes.stderr8
-rw-r--r--src/tools/clippy/tests/ui/needless_lifetimes.stderr8
-rw-r--r--src/tools/compiletest/src/common.rs7
-rw-r--r--src/tools/compiletest/src/header.rs7
-rw-r--r--src/tools/compiletest/src/header/tests.rs3
-rw-r--r--src/tools/compiletest/src/json.rs11
-rw-r--r--src/tools/compiletest/src/main.rs52
-rw-r--r--src/tools/compiletest/src/runtest.rs16
m---------src/tools/miri16
-rw-r--r--src/tools/rustdoc-js/tester.js190
-rw-r--r--src/tools/rustfmt/src/expr.rs10
-rw-r--r--src/tools/rustfmt/src/macros.rs70
-rw-r--r--src/tools/rustfmt/src/overflow.rs8
-rw-r--r--src/tools/rustfmt/src/parse/macros/cfg_if.rs8
-rw-r--r--src/tools/rustfmt/src/parse/macros/mod.rs8
-rw-r--r--src/tools/rustfmt/src/utils.rs1
-rw-r--r--src/tools/rustfmt/src/visitor.rs4
-rw-r--r--src/tools/tidy/src/deps.rs4
-rw-r--r--src/tools/tidy/src/ui_tests.rs4
1065 files changed, 22293 insertions, 12260 deletions
diff --git a/Cargo.lock b/Cargo.lock
index e3a9eb34936..ef4800a2261 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -56,15 +56,13 @@ dependencies = [
 
 [[package]]
 name = "ammonia"
-version = "3.1.3"
+version = "3.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b74b175af97d1aecc1add0878b1cbfcbf3bd4c22d7713eeb6d597da23e29bc0d"
+checksum = "d5ed2509ee88cc023cccee37a6fab35826830fe8b748b3869790e7720c2c4a74"
 dependencies = [
  "html5ever",
- "lazy_static",
  "maplit",
- "markup5ever_rcdom",
- "matches",
+ "once_cell",
  "tendril",
  "url 2.2.2",
 ]
@@ -225,8 +223,10 @@ dependencies = [
  "pretty_assertions",
  "serde",
  "serde_json",
+ "tar",
  "toml",
  "winapi",
+ "xz2",
 ]
 
 [[package]]
@@ -337,6 +337,7 @@ dependencies = [
  "humantime 2.0.1",
  "ignore",
  "im-rc",
+ "indexmap",
  "itertools",
  "jobserver",
  "lazy_static",
@@ -358,11 +359,12 @@ dependencies = [
  "serde_ignored",
  "serde_json",
  "shell-escape",
+ "snapbox",
  "strip-ansi-escapes",
  "tar",
  "tempfile",
  "termcolor",
- "toml_edit",
+ "toml_edit 0.14.3",
  "unicode-width",
  "unicode-xid",
  "url 2.2.2",
@@ -448,7 +450,7 @@ dependencies = [
  "serde_json",
  "tar",
  "termcolor",
- "toml_edit",
+ "toml_edit 0.13.4",
  "url 2.2.2",
 ]
 
@@ -795,6 +797,32 @@ dependencies = [
 ]
 
 [[package]]
+name = "concolor"
+version = "0.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "015267563b1df20adccdd00cb05257b1dfbea70a04928e9cf88ffb850c1a40af"
+dependencies = [
+ "atty",
+ "bitflags",
+ "concolor-query",
+]
+
+[[package]]
+name = "concolor-query"
+version = "0.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6417fe6fc03a8b533fd2177742eeb39a90c7233eedec7bac96d4d6b69a09449"
+
+[[package]]
+name = "content_inspector"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7bda66e858c683005a53a9a60c69a4aca7eeaa45d124526e389f7aec8e62f38"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
 name = "core"
 version = "0.0.0"
 dependencies = [
@@ -853,9 +881,9 @@ dependencies = [
 
 [[package]]
 name = "crossbeam-channel"
-version = "0.5.2"
+version = "0.5.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa"
+checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53"
 dependencies = [
  "cfg-if 1.0.0",
  "crossbeam-utils",
@@ -887,9 +915,9 @@ dependencies = [
 
 [[package]]
 name = "crossbeam-utils"
-version = "0.8.6"
+version = "0.8.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cfcae03edb34f947e64acdb1c33ec169824e20657e9ecb61cef6c8c74dcb8120"
+checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38"
 dependencies = [
  "cfg-if 1.0.0",
  "lazy_static",
@@ -1089,6 +1117,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "dunce"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "453440c271cf5577fd2a40e4942540cb7d0d2f85e27c8d07dd0023c925a67541"
+
+[[package]]
 name = "either"
 version = "1.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1690,9 +1724,9 @@ dependencies = [
 
 [[package]]
 name = "html5ever"
-version = "0.25.1"
+version = "0.26.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aafcf38a1a36118242d29b92e1b08ef84e67e4a5ed06e0a80be20e6a32bfed6b"
+checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7"
 dependencies = [
  "log",
  "mac",
@@ -2014,6 +2048,15 @@ dependencies = [
 ]
 
 [[package]]
+name = "kstring"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec3066350882a1cd6d950d055997f379ac37fd39f81cd4d8ed186032eb3c5747"
+dependencies = [
+ "static_assertions",
+]
+
+[[package]]
 name = "lazy_static"
 version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2200,9 +2243,9 @@ checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
 
 [[package]]
 name = "markup5ever"
-version = "0.10.1"
+version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a24f40fb03852d1cdd84330cddcaf98e9ec08a7b7768e952fad3b4cf048ec8fd"
+checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016"
 dependencies = [
  "log",
  "phf",
@@ -2213,18 +2256,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "markup5ever_rcdom"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f015da43bcd8d4f144559a3423f4591d69b8ce0652c905374da7205df336ae2b"
-dependencies = [
- "html5ever",
- "markup5ever",
- "tendril",
- "xml5ever",
-]
-
-[[package]]
 name = "matchers"
 version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2393,7 +2424,7 @@ dependencies = [
  "libc",
  "log",
  "measureme 9.1.2",
- "rand 0.8.4",
+ "rand 0.8.5",
  "rustc-workspace-hack",
  "rustc_version",
  "shell-escape",
@@ -2418,6 +2449,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "normalize-line-endings"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
+
+[[package]]
 name = "ntapi"
 version = "0.3.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2491,9 +2528,9 @@ dependencies = [
 
 [[package]]
 name = "once_cell"
-version = "1.7.2"
+version = "1.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3"
+checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
 
 [[package]]
 name = "opaque-debug"
@@ -2533,9 +2570,9 @@ checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
 
 [[package]]
 name = "openssl-src"
-version = "111.17.0+1.1.1m"
+version = "111.18.0+1.1.1n"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05d6a336abd10814198f66e2a91ccd7336611f30334119ca8ce300536666fcf4"
+checksum = "7897a926e1e8d00219127dc020130eca4292e5ca666dd592480d72c3eca2ff6c"
 dependencies = [
  "cc",
 ]
@@ -2743,18 +2780,18 @@ dependencies = [
 
 [[package]]
 name = "phf"
-version = "0.8.0"
+version = "0.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
+checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259"
 dependencies = [
  "phf_shared",
 ]
 
 [[package]]
 name = "phf_codegen"
-version = "0.8.0"
+version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815"
+checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd"
 dependencies = [
  "phf_generator",
  "phf_shared",
@@ -2762,19 +2799,19 @@ dependencies = [
 
 [[package]]
 name = "phf_generator"
-version = "0.8.0"
+version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526"
+checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
 dependencies = [
  "phf_shared",
- "rand 0.7.3",
+ "rand 0.8.5",
 ]
 
 [[package]]
 name = "phf_shared"
-version = "0.8.0"
+version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7"
+checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096"
 dependencies = [
  "siphasher",
 ]
@@ -2883,9 +2920,9 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.30"
+version = "1.0.37"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "edc3358ebc67bc8b7fa0c007f945b0b18226f78437d61bec735a9eb96b61ee70"
+checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1"
 dependencies = [
  "unicode-xid",
 ]
@@ -2894,6 +2931,7 @@ dependencies = [
 name = "proc_macro"
 version = "0.0.0"
 dependencies = [
+ "core",
  "std",
 ]
 
@@ -2952,9 +2990,9 @@ checksum = "07589615d719a60c8dd8a4622e7946465dfef20d1a428f969e3443e7386d5f45"
 
 [[package]]
 name = "quote"
-version = "1.0.7"
+version = "1.0.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
+checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
 dependencies = [
  "proc-macro2",
 ]
@@ -2985,20 +3023,18 @@ dependencies = [
  "libc",
  "rand_chacha 0.2.2",
  "rand_core 0.5.1",
- "rand_hc 0.2.0",
- "rand_pcg",
+ "rand_hc",
 ]
 
 [[package]]
 name = "rand"
-version = "0.8.4"
+version = "0.8.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
 dependencies = [
  "libc",
  "rand_chacha 0.3.0",
  "rand_core 0.6.2",
- "rand_hc 0.3.0",
 ]
 
 [[package]]
@@ -3049,24 +3085,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "rand_hc"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
-dependencies = [
- "rand_core 0.6.2",
-]
-
-[[package]]
-name = "rand_pcg"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
-dependencies = [
- "rand_core 0.5.1",
-]
-
-[[package]]
 name = "rand_xorshift"
 version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3204,7 +3222,7 @@ dependencies = [
  "num_cpus",
  "ordslice",
  "racer",
- "rand 0.8.4",
+ "rand 0.8.5",
  "rayon",
  "regex",
  "rls-analysis",
@@ -3225,7 +3243,7 @@ dependencies = [
  "tokio-stream",
  "tokio-util",
  "toml",
- "toml_edit",
+ "toml_edit 0.13.4",
  "url 2.2.2",
  "walkdir",
 ]
@@ -3277,7 +3295,7 @@ dependencies = [
  "env_logger 0.9.0",
  "futures 0.3.19",
  "log",
- "rand 0.8.4",
+ "rand 0.8.5",
  "rls-data",
  "rls-ipc",
  "serde",
@@ -3818,7 +3836,7 @@ dependencies = [
 name = "rustc_incremental"
 version = "0.0.0"
 dependencies = [
- "rand 0.8.4",
+ "rand 0.8.5",
  "rustc_ast",
  "rustc_data_structures",
  "rustc_errors",
@@ -4020,7 +4038,7 @@ dependencies = [
  "either",
  "gsgdt",
  "polonius-engine",
- "rand 0.8.4",
+ "rand 0.8.5",
  "rand_xoshiro 0.6.0",
  "rustc-rayon",
  "rustc-rayon-core",
@@ -4140,6 +4158,7 @@ dependencies = [
  "rustc_errors",
  "rustc_feature",
  "rustc_lexer",
+ "rustc_macros",
  "rustc_session",
  "rustc_span",
  "tracing",
@@ -4495,6 +4514,7 @@ dependencies = [
  "expect-test",
  "itertools",
  "minifier",
+ "once_cell",
  "pulldown-cmark",
  "rayon",
  "regex",
@@ -4790,6 +4810,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "similar"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e24979f63a11545f5f2c60141afe249d4f19f84581ea2138065e400941d83d3"
+
+[[package]]
 name = "siphasher"
 version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4824,6 +4850,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "da73c8f77aebc0e40c300b93f0a5f1bece7a248a36eee287d4e095f35c7b7d6e"
 
 [[package]]
+name = "snapbox"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1f212b806d6f56d19838e36a0aaa7e79a0bc9ca177e873fb87651ad92f983e2"
+dependencies = [
+ "concolor",
+ "content_inspector",
+ "dunce",
+ "filetime",
+ "normalize-line-endings",
+ "similar",
+ "snapbox-macros",
+ "tempfile",
+ "walkdir",
+ "yansi",
+]
+
+[[package]]
+name = "snapbox-macros"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c01dea7e04cbb27ef4c86e9922184608185f7cd95c1763bc30d727cda4a5e930"
+
+[[package]]
 name = "socket2"
 version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4853,6 +4903,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
 name = "std"
 version = "0.0.0"
 dependencies = [
@@ -4891,12 +4947,13 @@ dependencies = [
 
 [[package]]
 name = "string_cache"
-version = "0.8.0"
+version = "0.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2940c75beb4e3bf3a494cef919a747a2cb81e52571e212bfbd185074add7208a"
+checksum = "33994d0838dc2d152d17a62adf608a869b5e846b65b389af7f3dbc1de45c5b26"
 dependencies = [
  "lazy_static",
  "new_debug_unreachable",
+ "parking_lot",
  "phf_shared",
  "precomputed-hash",
  "serde",
@@ -4904,9 +4961,9 @@ dependencies = [
 
 [[package]]
 name = "string_cache_codegen"
-version = "0.5.1"
+version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f24c8e5e19d22a726626f1a5e16fe15b132dcf21d10177fa5a45ce7962996b97"
+checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988"
 dependencies = [
  "phf_generator",
  "phf_shared",
@@ -4979,9 +5036,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "1.0.80"
+version = "1.0.91"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194"
+checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -5019,7 +5076,7 @@ checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
 dependencies = [
  "cfg-if 1.0.0",
  "libc",
- "rand 0.8.4",
+ "rand 0.8.5",
  "redox_syscall",
  "remove_dir_all",
  "winapi",
@@ -5268,7 +5325,20 @@ dependencies = [
  "combine",
  "indexmap",
  "itertools",
- "kstring",
+ "kstring 1.0.6",
+ "serde",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.14.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba98375fd631b83696f87c64e4ed8e29e6a1f3404d6aed95fa95163bad38e705"
+dependencies = [
+ "combine",
+ "indexmap",
+ "itertools",
+ "kstring 2.0.0",
  "serde",
 ]
 
@@ -5667,9 +5737,9 @@ dependencies = [
 
 [[package]]
 name = "walkdir"
-version = "2.3.1"
+version = "2.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d"
+checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
 dependencies = [
  "same-file",
  "winapi",
@@ -5734,18 +5804,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "xml5ever"
-version = "0.16.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b1b52e6e8614d4a58b8e70cf51ec0cc21b256ad8206708bcff8139b5bbd6a59"
-dependencies = [
- "log",
- "mac",
- "markup5ever",
- "time",
-]
-
-[[package]]
 name = "xz2"
 version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -5781,6 +5839,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "yansi"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
+
+[[package]]
 name = "yansi-term"
 version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index b7da276fc7e..1a18d1964c9 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -23,7 +23,7 @@ pub use GenericArgs::*;
 pub use UnsafeSource::*;
 
 use crate::ptr::P;
-use crate::token::{self, CommentKind, DelimToken, Token};
+use crate::token::{self, CommentKind, Delimiter, Token};
 use crate::tokenstream::{DelimSpan, LazyTokenStream, TokenStream, TokenTree};
 
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
@@ -397,6 +397,7 @@ pub struct GenericParam {
     pub bounds: GenericBounds,
     pub is_placeholder: bool,
     pub kind: GenericParamKind,
+    pub colon_span: Option<Span>,
 }
 
 impl GenericParam {
@@ -1274,6 +1275,7 @@ impl Expr {
             ExprKind::Paren(..) => ExprPrecedence::Paren,
             ExprKind::Try(..) => ExprPrecedence::Try,
             ExprKind::Yield(..) => ExprPrecedence::Yield,
+            ExprKind::Yeet(..) => ExprPrecedence::Yeet,
             ExprKind::Err => ExprPrecedence::Err,
         }
     }
@@ -1461,6 +1463,10 @@ pub enum ExprKind {
     /// A `yield`, with an optional value to be yielded.
     Yield(Option<P<Expr>>),
 
+    /// A `do yeet` (aka `throw`/`fail`/`bail`/`raise`/whatever),
+    /// with an optional value to be returned.
+    Yeet(Option<P<Expr>>),
+
     /// Placeholder for an expression that wasn't syntactically well formed in some way.
     Err,
 }
@@ -1542,10 +1548,10 @@ pub enum MacArgs {
 }
 
 impl MacArgs {
-    pub fn delim(&self) -> DelimToken {
+    pub fn delim(&self) -> Option<Delimiter> {
         match self {
-            MacArgs::Delimited(_, delim, _) => delim.to_token(),
-            MacArgs::Empty | MacArgs::Eq(..) => token::NoDelim,
+            MacArgs::Delimited(_, delim, _) => Some(delim.to_token()),
+            MacArgs::Empty | MacArgs::Eq(..) => None,
         }
     }
 
@@ -1582,20 +1588,20 @@ pub enum MacDelimiter {
 }
 
 impl MacDelimiter {
-    pub fn to_token(self) -> DelimToken {
+    pub fn to_token(self) -> Delimiter {
         match self {
-            MacDelimiter::Parenthesis => DelimToken::Paren,
-            MacDelimiter::Bracket => DelimToken::Bracket,
-            MacDelimiter::Brace => DelimToken::Brace,
+            MacDelimiter::Parenthesis => Delimiter::Parenthesis,
+            MacDelimiter::Bracket => Delimiter::Bracket,
+            MacDelimiter::Brace => Delimiter::Brace,
         }
     }
 
-    pub fn from_token(delim: DelimToken) -> Option<MacDelimiter> {
+    pub fn from_token(delim: Delimiter) -> Option<MacDelimiter> {
         match delim {
-            token::Paren => Some(MacDelimiter::Parenthesis),
-            token::Bracket => Some(MacDelimiter::Bracket),
-            token::Brace => Some(MacDelimiter::Brace),
-            token::NoDelim => None,
+            Delimiter::Parenthesis => Some(MacDelimiter::Parenthesis),
+            Delimiter::Bracket => Some(MacDelimiter::Bracket),
+            Delimiter::Brace => Some(MacDelimiter::Brace),
+            Delimiter::Invisible => None,
         }
     }
 }
diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs
index 80caf37d709..b14367aa1c2 100644
--- a/compiler/rustc_ast/src/attr/mod.rs
+++ b/compiler/rustc_ast/src/attr/mod.rs
@@ -5,10 +5,11 @@ use crate::ast::{AttrId, AttrItem, AttrKind, AttrStyle, Attribute};
 use crate::ast::{Lit, LitKind};
 use crate::ast::{MacArgs, MacDelimiter, MetaItem, MetaItemKind, NestedMetaItem};
 use crate::ast::{Path, PathSegment};
-use crate::token::{self, CommentKind, Token};
+use crate::token::{self, CommentKind, Delimiter, Token};
 use crate::tokenstream::{AttrAnnotatedTokenStream, AttrAnnotatedTokenTree};
 use crate::tokenstream::{DelimSpan, Spacing, TokenTree, TreeAndSpacing};
 use crate::tokenstream::{LazyTokenStream, TokenStream};
+use crate::util::comments;
 
 use rustc_index::bit_set::GrowableBitSet;
 use rustc_span::source_map::BytePos;
@@ -262,6 +263,10 @@ impl Attribute {
         }
     }
 
+    pub fn may_have_doc_links(&self) -> bool {
+        self.doc_str().map_or(false, |s| comments::may_have_doc_links(s.as_str()))
+    }
+
     pub fn get_normal_item(&self) -> &AttrItem {
         match self.kind {
             AttrKind::Normal(ref item, _) => item,
@@ -508,7 +513,7 @@ impl MetaItemKind {
                 vec![
                     TokenTree::Delimited(
                         DelimSpan::from_single(span),
-                        token::Paren,
+                        Delimiter::Parenthesis,
                         TokenStream::new(tokens),
                     )
                     .into(),
@@ -535,7 +540,7 @@ impl MetaItemKind {
         tokens: &mut impl Iterator<Item = TokenTree>,
     ) -> Option<MetaItemKind> {
         match tokens.next() {
-            Some(TokenTree::Delimited(_, token::NoDelim, inner_tokens)) => {
+            Some(TokenTree::Delimited(_, Delimiter::Invisible, inner_tokens)) => {
                 MetaItemKind::name_value_from_tokens(&mut inner_tokens.trees())
             }
             Some(TokenTree::Token(token)) => {
@@ -560,7 +565,7 @@ impl MetaItemKind {
         tokens: &mut iter::Peekable<impl Iterator<Item = TokenTree>>,
     ) -> Option<MetaItemKind> {
         match tokens.peek() {
-            Some(TokenTree::Delimited(_, token::Paren, inner_tokens)) => {
+            Some(TokenTree::Delimited(_, Delimiter::Parenthesis, inner_tokens)) => {
                 let inner_tokens = inner_tokens.clone();
                 tokens.next();
                 MetaItemKind::list_from_tokens(inner_tokens)
@@ -601,7 +606,7 @@ impl NestedMetaItem {
                 tokens.next();
                 return Some(NestedMetaItem::Literal(lit));
             }
-            Some(TokenTree::Delimited(_, token::NoDelim, inner_tokens)) => {
+            Some(TokenTree::Delimited(_, Delimiter::Invisible, inner_tokens)) => {
                 let inner_tokens = inner_tokens.clone();
                 tokens.next();
                 return NestedMetaItem::from_tokens(&mut inner_tokens.into_trees().peekable());
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index cba49835f69..4bf3d483f73 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -867,9 +867,12 @@ pub fn noop_flat_map_generic_param<T: MutVisitor>(
     mut param: GenericParam,
     vis: &mut T,
 ) -> SmallVec<[GenericParam; 1]> {
-    let GenericParam { id, ident, attrs, bounds, kind, is_placeholder: _ } = &mut param;
+    let GenericParam { id, ident, attrs, bounds, kind, colon_span, is_placeholder: _ } = &mut param;
     vis.visit_id(id);
     vis.visit_ident(ident);
+    if let Some(ref mut colon_span) = colon_span {
+        vis.visit_span(colon_span);
+    }
     visit_thin_attrs(attrs, vis);
     visit_vec(bounds, |bound| noop_visit_param_bound(bound, vis));
     match kind {
@@ -1391,6 +1394,9 @@ pub fn noop_visit_expr<T: MutVisitor>(
         ExprKind::Ret(expr) => {
             visit_opt(expr, |expr| vis.visit_expr(expr));
         }
+        ExprKind::Yeet(expr) => {
+            visit_opt(expr, |expr| vis.visit_expr(expr));
+        }
         ExprKind::InlineAsm(asm) => vis.visit_inline_asm(asm),
         ExprKind::MacCall(mac) => vis.visit_mac_call(mac),
         ExprKind::Struct(se) => {
diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs
index 5aa8011ca5e..1589a882f08 100644
--- a/compiler/rustc_ast/src/token.rs
+++ b/compiler/rustc_ast/src/token.rs
@@ -1,5 +1,4 @@
 pub use BinOpToken::*;
-pub use DelimToken::*;
 pub use LitKind::*;
 pub use Nonterminal::*;
 pub use TokenKind::*;
@@ -37,18 +36,26 @@ pub enum BinOpToken {
     Shr,
 }
 
-/// A delimiter token.
-#[derive(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Debug, Copy)]
-#[derive(HashStable_Generic)]
-pub enum DelimToken {
-    /// A round parenthesis (i.e., `(` or `)`).
-    Paren,
-    /// A square bracket (i.e., `[` or `]`).
-    Bracket,
-    /// A curly brace (i.e., `{` or `}`).
+/// Describes how a sequence of token trees is delimited.
+/// Cannot use `proc_macro::Delimiter` directly because this
+/// structure should implement some additional traits.
+/// The `None` variant is also renamed to `Invisible` to be
+/// less confusing and better convey the semantics.
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[derive(Encodable, Decodable, Hash, HashStable_Generic)]
+pub enum Delimiter {
+    /// `( ... )`
+    Parenthesis,
+    /// `{ ... }`
     Brace,
-    /// An empty delimiter.
-    NoDelim,
+    /// `[ ... ]`
+    Bracket,
+    /// `Ø ... Ø`
+    /// An invisible delimiter, that may, for example, appear around tokens coming from a
+    /// "macro variable" `$var`. It is important to preserve operator priorities in cases like
+    /// `$var * 3` where `$var` is `1 + 2`.
+    /// Invisible delimiters might not survive roundtrip of a token stream through a string.
+    Invisible,
 }
 
 #[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
@@ -212,9 +219,9 @@ pub enum TokenKind {
     /// Used by proc macros for representing lifetimes, not generated by lexer right now.
     SingleQuote,
     /// An opening delimiter (e.g., `{`).
-    OpenDelim(DelimToken),
+    OpenDelim(Delimiter),
     /// A closing delimiter (e.g., `}`).
-    CloseDelim(DelimToken),
+    CloseDelim(Delimiter),
 
     /* Literals */
     Literal(Lit),
@@ -387,8 +394,8 @@ impl Token {
         match self.uninterpolate().kind {
             Ident(name, is_raw)        =>
                 ident_can_begin_type(name, self.span, is_raw), // type name or keyword
-            OpenDelim(Paren)            | // tuple
-            OpenDelim(Bracket)          | // array
+            OpenDelim(Delimiter::Parenthesis) | // tuple
+            OpenDelim(Delimiter::Bracket)     | // array
             Not                         | // never
             BinOp(Star)                 | // raw pointer
             BinOp(And)                  | // reference
@@ -405,7 +412,7 @@ impl Token {
     /// Returns `true` if the token can appear at the start of a const param.
     pub fn can_begin_const_arg(&self) -> bool {
         match self.kind {
-            OpenDelim(Brace) => true,
+            OpenDelim(Delimiter::Brace) => true,
             Interpolated(ref nt) => matches!(**nt, NtExpr(..) | NtBlock(..) | NtLiteral(..)),
             _ => self.can_begin_literal_maybe_minus(),
         }
@@ -417,7 +424,7 @@ impl Token {
             || self.is_lifetime()
             || self.is_keyword(kw::For)
             || self == &Question
-            || self == &OpenDelim(Paren)
+            || self == &OpenDelim(Delimiter::Parenthesis)
     }
 
     /// Returns `true` if the token is any literal.
diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs
index affb4289cb1..a8f29f33407 100644
--- a/compiler/rustc_ast/src/tokenstream.rs
+++ b/compiler/rustc_ast/src/tokenstream.rs
@@ -13,7 +13,7 @@
 //! and a borrowed `TokenStream` is sufficient to build an owned `TokenStream` without taking
 //! ownership of the original.
 
-use crate::token::{self, DelimToken, Token, TokenKind};
+use crate::token::{self, Delimiter, Token, TokenKind};
 use crate::AttrVec;
 
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
@@ -42,7 +42,7 @@ pub enum TokenTree {
     /// A single token.
     Token(Token),
     /// A delimited sequence of token trees.
-    Delimited(DelimSpan, DelimToken, TokenStream),
+    Delimited(DelimSpan, Delimiter, TokenStream),
 }
 
 #[derive(Copy, Clone)]
@@ -57,7 +57,7 @@ fn _dummy()
 where
     Token: Send + Sync,
     DelimSpan: Send + Sync,
-    DelimToken: Send + Sync,
+    Delimiter: Send + Sync,
     TokenStream: Send + Sync,
 {
 }
@@ -94,16 +94,6 @@ impl TokenTree {
         TokenTree::Token(Token::new(kind, span))
     }
 
-    /// Returns the opening delimiter as a token tree.
-    pub fn open_tt(span: DelimSpan, delim: DelimToken) -> TokenTree {
-        TokenTree::token(token::OpenDelim(delim), span.open)
-    }
-
-    /// Returns the closing delimiter as a token tree.
-    pub fn close_tt(span: DelimSpan, delim: DelimToken) -> TokenTree {
-        TokenTree::token(token::CloseDelim(delim), span.close)
-    }
-
     pub fn uninterpolate(self) -> TokenTree {
         match self {
             TokenTree::Token(token) => TokenTree::Token(token.uninterpolate().into_owned()),
@@ -185,7 +175,7 @@ pub struct AttrAnnotatedTokenStream(pub Lrc<Vec<(AttrAnnotatedTokenTree, Spacing
 #[derive(Clone, Debug, Encodable, Decodable)]
 pub enum AttrAnnotatedTokenTree {
     Token(Token),
-    Delimited(DelimSpan, DelimToken, AttrAnnotatedTokenStream),
+    Delimited(DelimSpan, Delimiter, AttrAnnotatedTokenStream),
     /// Stores the attributes for an attribute target,
     /// along with the tokens for that attribute target.
     /// See `AttributesData` for more information
@@ -585,13 +575,20 @@ impl Cursor {
         Cursor { stream, index: 0 }
     }
 
+    #[inline]
     pub fn next_with_spacing(&mut self) -> Option<TreeAndSpacing> {
-        if self.index < self.stream.len() {
+        self.stream.0.get(self.index).map(|tree| {
             self.index += 1;
-            Some(self.stream.0[self.index - 1].clone())
-        } else {
-            None
-        }
+            tree.clone()
+        })
+    }
+
+    #[inline]
+    pub fn next_with_spacing_ref(&mut self) -> Option<&TreeAndSpacing> {
+        self.stream.0.get(self.index).map(|tree| {
+            self.index += 1;
+            tree
+        })
     }
 
     pub fn index(&self) -> usize {
diff --git a/compiler/rustc_ast/src/util/comments.rs b/compiler/rustc_ast/src/util/comments.rs
index 8730aeb0f3b..b4fff0022e2 100644
--- a/compiler/rustc_ast/src/util/comments.rs
+++ b/compiler/rustc_ast/src/util/comments.rs
@@ -24,6 +24,14 @@ pub struct Comment {
     pub pos: BytePos,
 }
 
+/// A fast conservative estimate on whether the string can contain documentation links.
+/// A pair of square brackets `[]` must exist in the string, but we only search for the
+/// opening bracket because brackets always go in pairs in practice.
+#[inline]
+pub fn may_have_doc_links(s: &str) -> bool {
+    s.contains('[')
+}
+
 /// Makes a doc string more presentable to users.
 /// Used by rustdoc and perhaps other tools, but not by rustc.
 pub fn beautify_doc_string(data: Symbol, kind: CommentKind) -> Symbol {
diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs
index 742a7d1d2df..74b7fe9e249 100644
--- a/compiler/rustc_ast/src/util/parser.rs
+++ b/compiler/rustc_ast/src/util/parser.rs
@@ -247,6 +247,7 @@ pub enum ExprPrecedence {
     Continue,
     Ret,
     Yield,
+    Yeet,
 
     Range,
 
@@ -299,7 +300,8 @@ impl ExprPrecedence {
             ExprPrecedence::Break |
             ExprPrecedence::Continue |
             ExprPrecedence::Ret |
-            ExprPrecedence::Yield => PREC_JUMP,
+            ExprPrecedence::Yield |
+            ExprPrecedence::Yeet => PREC_JUMP,
 
             // `Range` claims to have higher precedence than `Assign`, but `x .. x = x` fails to
             // parse, instead of parsing as `(x .. x) = x`.  Giving `Range` a lower precedence
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index d925c6dd354..fa26716083f 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -33,6 +33,25 @@ pub enum FnCtxt {
 }
 
 #[derive(Copy, Clone, Debug)]
+pub enum BoundKind {
+    /// Trait bounds in generics bounds and type/trait alias.
+    /// E.g., `<T: Bound>`, `type A: Bound`, or `where T: Bound`.
+    Bound,
+
+    /// Trait bounds in `impl` type.
+    /// E.g., `type Foo = impl Bound1 + Bound2 + Bound3`.
+    Impl,
+
+    /// Trait bounds in trait object type.
+    /// E.g., `dyn Bound1 + Bound2 + Bound3`.
+    TraitObject,
+
+    /// Super traits of a trait.
+    /// E.g., `trait A: B`
+    SuperTraits,
+}
+
+#[derive(Copy, Clone, Debug)]
 pub enum FnKind<'a> {
     /// E.g., `fn foo()`, `fn foo(&self)`, or `extern "Abi" fn foo()`.
     Fn(FnCtxt, Ident, &'a FnSig, &'a Visibility, &'a Generics, Option<&'a Block>),
@@ -139,7 +158,7 @@ pub trait Visitor<'ast>: Sized {
     fn visit_trait_ref(&mut self, t: &'ast TraitRef) {
         walk_trait_ref(self, t)
     }
-    fn visit_param_bound(&mut self, bounds: &'ast GenericBound) {
+    fn visit_param_bound(&mut self, bounds: &'ast GenericBound, _ctxt: BoundKind) {
         walk_param_bound(self, bounds)
     }
     fn visit_poly_trait_ref(&mut self, t: &'ast PolyTraitRef, m: &'ast TraitBoundModifier) {
@@ -311,7 +330,7 @@ pub fn walk_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a Item) {
         ItemKind::GlobalAsm(ref asm) => walk_inline_asm(visitor, asm),
         ItemKind::TyAlias(box TyAlias { ref generics, ref bounds, ref ty, .. }) => {
             visitor.visit_generics(generics);
-            walk_list!(visitor, visit_param_bound, bounds);
+            walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound);
             walk_list!(visitor, visit_ty, ty);
         }
         ItemKind::Enum(ref enum_definition, ref generics) => {
@@ -346,12 +365,12 @@ pub fn walk_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a Item) {
             ref items,
         }) => {
             visitor.visit_generics(generics);
-            walk_list!(visitor, visit_param_bound, bounds);
+            walk_list!(visitor, visit_param_bound, bounds, BoundKind::SuperTraits);
             walk_list!(visitor, visit_assoc_item, items, AssocCtxt::Trait);
         }
         ItemKind::TraitAlias(ref generics, ref bounds) => {
             visitor.visit_generics(generics);
-            walk_list!(visitor, visit_param_bound, bounds);
+            walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound);
         }
         ItemKind::MacCall(ref mac) => visitor.visit_mac_call(mac),
         ItemKind::MacroDef(ref ts) => visitor.visit_mac_def(ts, item.id),
@@ -416,8 +435,11 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) {
             visitor.visit_ty(ty);
             visitor.visit_anon_const(length)
         }
-        TyKind::TraitObject(ref bounds, ..) | TyKind::ImplTrait(_, ref bounds) => {
-            walk_list!(visitor, visit_param_bound, bounds);
+        TyKind::TraitObject(ref bounds, ..) => {
+            walk_list!(visitor, visit_param_bound, bounds, BoundKind::TraitObject);
+        }
+        TyKind::ImplTrait(_, ref bounds) => {
+            walk_list!(visitor, visit_param_bound, bounds, BoundKind::Impl);
         }
         TyKind::Typeof(ref expression) => visitor.visit_anon_const(expression),
         TyKind::Infer | TyKind::ImplicitSelf | TyKind::Err => {}
@@ -503,7 +525,7 @@ pub fn walk_assoc_constraint<'a, V: Visitor<'a>>(visitor: &mut V, constraint: &'
             Term::Const(c) => visitor.visit_anon_const(c),
         },
         AssocConstraintKind::Bound { ref bounds } => {
-            walk_list!(visitor, visit_param_bound, bounds);
+            walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound);
         }
     }
 }
@@ -566,7 +588,7 @@ pub fn walk_foreign_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a ForeignI
         }
         ForeignItemKind::TyAlias(box TyAlias { generics, bounds, ty, .. }) => {
             visitor.visit_generics(generics);
-            walk_list!(visitor, visit_param_bound, bounds);
+            walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound);
             walk_list!(visitor, visit_ty, ty);
         }
         ForeignItemKind::MacCall(mac) => {
@@ -585,7 +607,7 @@ pub fn walk_param_bound<'a, V: Visitor<'a>>(visitor: &mut V, bound: &'a GenericB
 pub fn walk_generic_param<'a, V: Visitor<'a>>(visitor: &mut V, param: &'a GenericParam) {
     visitor.visit_ident(param.ident);
     walk_list!(visitor, visit_attribute, param.attrs.iter());
-    walk_list!(visitor, visit_param_bound, &param.bounds);
+    walk_list!(visitor, visit_param_bound, &param.bounds, BoundKind::Bound);
     match param.kind {
         GenericParamKind::Lifetime => (),
         GenericParamKind::Type { ref default } => walk_list!(visitor, visit_ty, default),
@@ -612,14 +634,14 @@ pub fn walk_where_predicate<'a, V: Visitor<'a>>(visitor: &mut V, predicate: &'a
             ..
         }) => {
             visitor.visit_ty(bounded_ty);
-            walk_list!(visitor, visit_param_bound, bounds);
+            walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound);
             walk_list!(visitor, visit_generic_param, bound_generic_params);
         }
         WherePredicate::RegionPredicate(WhereRegionPredicate {
             ref lifetime, ref bounds, ..
         }) => {
             visitor.visit_lifetime(lifetime);
-            walk_list!(visitor, visit_param_bound, bounds);
+            walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound);
         }
         WherePredicate::EqPredicate(WhereEqPredicate { ref lhs_ty, ref rhs_ty, .. }) => {
             visitor.visit_ty(lhs_ty);
@@ -672,7 +694,7 @@ pub fn walk_assoc_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a AssocItem,
         }
         AssocItemKind::TyAlias(box TyAlias { generics, bounds, ty, .. }) => {
             visitor.visit_generics(generics);
-            walk_list!(visitor, visit_param_bound, bounds);
+            walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound);
             walk_list!(visitor, visit_ty, ty);
         }
         AssocItemKind::MacCall(mac) => {
@@ -871,6 +893,9 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
         ExprKind::Ret(ref optional_expression) => {
             walk_list!(visitor, visit_expr, optional_expression);
         }
+        ExprKind::Yeet(ref optional_expression) => {
+            walk_list!(visitor, visit_expr, optional_expression);
+        }
         ExprKind::MacCall(ref mac) => visitor.visit_mac_call(mac),
         ExprKind::Paren(ref subexpression) => visitor.visit_expr(subexpression),
         ExprKind::InlineAsm(ref asm) => walk_inline_asm(visitor, asm),
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 9442e0f1a1f..5c3e3be2116 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -53,7 +53,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
                         e.span,
                         seg,
                         ParamMode::Optional,
-                        0,
                         ParenthesizedGenericArgs::Err,
                         ImplTraitContext::Disallowed(ImplTraitPosition::Path),
                     ));
@@ -222,6 +221,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     let e = e.as_ref().map(|x| self.lower_expr(x));
                     hir::ExprKind::Ret(e)
                 }
+                ExprKind::Yeet(ref sub_expr) => self.lower_expr_yeet(e.span, sub_expr.as_deref()),
                 ExprKind::InlineAsm(ref asm) => {
                     hir::ExprKind::InlineAsm(self.lower_inline_asm(e.span, asm))
                 }
@@ -1544,6 +1544,44 @@ impl<'hir> LoweringContext<'_, 'hir> {
         )
     }
 
+    /// Desugar `ExprKind::Yeet` from: `do yeet <expr>` into:
+    /// ```rust
+    /// // If there is an enclosing `try {...}`:
+    /// break 'catch_target FromResidual::from_residual(Yeet(residual)),
+    /// // Otherwise:
+    /// return FromResidual::from_residual(Yeet(residual)),
+    /// ```
+    /// But to simplify this, there's a `from_yeet` lang item function which
+    /// handles the combined `FromResidual::from_residual(Yeet(residual))`.
+    fn lower_expr_yeet(&mut self, span: Span, sub_expr: Option<&Expr>) -> hir::ExprKind<'hir> {
+        // The expression (if present) or `()` otherwise.
+        let (yeeted_span, yeeted_expr) = if let Some(sub_expr) = sub_expr {
+            (sub_expr.span, self.lower_expr(sub_expr))
+        } else {
+            (self.mark_span_with_reason(DesugaringKind::YeetExpr, span, None), self.expr_unit(span))
+        };
+
+        let unstable_span = self.mark_span_with_reason(
+            DesugaringKind::YeetExpr,
+            span,
+            self.allow_try_trait.clone(),
+        );
+
+        let from_yeet_expr = self.wrap_in_try_constructor(
+            hir::LangItem::TryTraitFromYeet,
+            unstable_span,
+            yeeted_expr,
+            yeeted_span,
+        );
+
+        if let Some(catch_node) = self.catch_scope {
+            let target_id = Ok(self.lower_node_id(catch_node));
+            hir::ExprKind::Break(hir::Destination { label: None, target_id }, Some(from_yeet_expr))
+        } else {
+            hir::ExprKind::Ret(Some(from_yeet_expr))
+        }
+    }
+
     // =========================================================================
     // Helper methods for building HIR.
     // =========================================================================
diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs
index 5eab21bf79a..c506360aa8a 100644
--- a/compiler/rustc_ast_lowering/src/index.rs
+++ b/compiler/rustc_ast_lowering/src/index.rs
@@ -30,6 +30,7 @@ pub(super) struct NodeCollector<'a, 'hir> {
     definitions: &'a definitions::Definitions,
 }
 
+#[tracing::instrument(level = "debug", skip(sess, definitions, bodies))]
 pub(super) fn index_hir<'hir>(
     sess: &Session,
     definitions: &definitions::Definitions,
@@ -65,6 +66,7 @@ pub(super) fn index_hir<'hir>(
 }
 
 impl<'a, 'hir> NodeCollector<'a, 'hir> {
+    #[tracing::instrument(level = "debug", skip(self))]
     fn insert(&mut self, span: Span, hir_id: HirId, node: Node<'hir>) {
         debug_assert_eq!(self.owner, hir_id.owner);
         debug_assert_ne!(hir_id.local_id.as_u32(), 0);
@@ -138,8 +140,8 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
         });
     }
 
+    #[tracing::instrument(level = "debug", skip(self))]
     fn visit_item(&mut self, i: &'hir Item<'hir>) {
-        debug!("visit_item: {:?}", i);
         debug_assert_eq!(i.def_id, self.owner);
         self.with_parent(i.hir_id(), |this| {
             if let ItemKind::Struct(ref struct_def, _) = i.kind {
@@ -152,6 +154,7 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
         });
     }
 
+    #[tracing::instrument(level = "debug", skip(self))]
     fn visit_foreign_item(&mut self, fi: &'hir ForeignItem<'hir>) {
         debug_assert_eq!(fi.def_id, self.owner);
         self.with_parent(fi.hir_id(), |this| {
@@ -170,6 +173,7 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
         })
     }
 
+    #[tracing::instrument(level = "debug", skip(self))]
     fn visit_trait_item(&mut self, ti: &'hir TraitItem<'hir>) {
         debug_assert_eq!(ti.def_id, self.owner);
         self.with_parent(ti.hir_id(), |this| {
@@ -177,6 +181,7 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
         });
     }
 
+    #[tracing::instrument(level = "debug", skip(self))]
     fn visit_impl_item(&mut self, ii: &'hir ImplItem<'hir>) {
         debug_assert_eq!(ii.def_id, self.owner);
         self.with_parent(ii.hir_id(), |this| {
@@ -290,18 +295,6 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
         self.insert(lifetime.span, lifetime.hir_id, Node::Lifetime(lifetime));
     }
 
-    fn visit_vis(&mut self, visibility: &'hir Visibility<'hir>) {
-        match visibility.node {
-            VisibilityKind::Public | VisibilityKind::Crate(_) | VisibilityKind::Inherited => {}
-            VisibilityKind::Restricted { hir_id, .. } => {
-                self.insert(visibility.span, hir_id, Node::Visibility(visibility));
-                self.with_parent(hir_id, |this| {
-                    intravisit::walk_vis(this, visibility);
-                });
-            }
-        }
-    }
-
     fn visit_variant(&mut self, v: &'hir Variant<'hir>, g: &'hir Generics<'hir>, item_id: HirId) {
         self.insert(v.span, v.id, Node::Variant(v));
         self.with_parent(v.id, |this| {
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index a8bd8c92a41..5a95e5b084a 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -1,11 +1,11 @@
-use super::{AnonymousLifetimeMode, LoweringContext, ParamMode};
 use super::{AstOwner, ImplTraitContext, ImplTraitPosition, ResolverAstLowering};
+use super::{LoweringContext, ParamMode};
 use crate::{Arena, FnDeclKind};
 
 use rustc_ast::ptr::P;
 use rustc_ast::visit::AssocCtxt;
 use rustc_ast::*;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sorted_map::SortedMap;
 use rustc_errors::struct_span_err;
 use rustc_hir as hir;
@@ -14,7 +14,7 @@ use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
 use rustc_index::vec::{Idx, IndexVec};
 use rustc_session::utils::NtToTokenstream;
 use rustc_session::Session;
-use rustc_span::source_map::{respan, DesugaringKind};
+use rustc_span::source_map::DesugaringKind;
 use rustc_span::symbol::{kw, sym, Ident};
 use rustc_span::Span;
 use rustc_target::spec::abi;
@@ -81,14 +81,11 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> {
             is_in_loop_condition: false,
             is_in_trait_impl: false,
             is_in_dyn_type: false,
-            anonymous_lifetime_mode: AnonymousLifetimeMode::PassThrough,
             generator_kind: None,
             task_context: None,
             current_item: None,
-            lifetimes_to_define: Vec::new(),
-            is_collecting_anonymous_lifetimes: None,
-            in_scope_lifetimes: Vec::new(),
-            allow_try_trait: Some([sym::try_trait_v2][..].into()),
+            captured_lifetimes: None,
+            allow_try_trait: Some([sym::try_trait_v2, sym::yeet_desugar_details][..].into()),
             allow_gen_future: Some([sym::gen_future][..].into()),
             allow_into_future: Some([sym::into_future][..].into()),
         };
@@ -143,32 +140,14 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> {
             LocalDefId { local_def_index }
         };
 
-        let parent_hir = self.lower_node(parent_id).unwrap().node().expect_item();
+        let parent_hir = self.lower_node(parent_id).unwrap();
         self.with_lctx(item.id, |lctx| {
             // Evaluate with the lifetimes in `params` in-scope.
             // This is used to track which lifetimes have already been defined,
             // and which need to be replicated when lowering an async fn.
-            match parent_hir.kind {
-                hir::ItemKind::Impl(hir::Impl { ref of_trait, ref generics, .. }) => {
+            match parent_hir.node().expect_item().kind {
+                hir::ItemKind::Impl(hir::Impl { ref of_trait, .. }) => {
                     lctx.is_in_trait_impl = of_trait.is_some();
-                    lctx.in_scope_lifetimes = generics
-                        .params
-                        .iter()
-                        .filter(|param| {
-                            matches!(param.kind, hir::GenericParamKind::Lifetime { .. })
-                        })
-                        .map(|param| param.name)
-                        .collect();
-                }
-                hir::ItemKind::Trait(_, _, ref generics, ..) => {
-                    lctx.in_scope_lifetimes = generics
-                        .params
-                        .iter()
-                        .filter(|param| {
-                            matches!(param.kind, hir::GenericParamKind::Lifetime { .. })
-                        })
-                        .map(|param| param.name)
-                        .collect();
                 }
                 _ => {}
             };
@@ -230,15 +209,15 @@ impl<'hir> LoweringContext<'_, 'hir> {
 
     fn lower_item(&mut self, i: &Item) -> &'hir hir::Item<'hir> {
         let mut ident = i.ident;
-        let mut vis = self.lower_visibility(&i.vis);
+        let vis_span = self.lower_span(i.vis.span);
         let hir_id = self.lower_node_id(i.id);
         let attrs = self.lower_attrs(hir_id, &i.attrs);
-        let kind = self.lower_item_kind(i.span, i.id, hir_id, &mut ident, attrs, &mut vis, &i.kind);
+        let kind = self.lower_item_kind(i.span, i.id, hir_id, &mut ident, attrs, vis_span, &i.kind);
         let item = hir::Item {
             def_id: hir_id.expect_owner(),
             ident: self.lower_ident(ident),
             kind,
-            vis,
+            vis_span,
             span: self.lower_span(i.span),
         };
         self.arena.alloc(item)
@@ -251,7 +230,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         hir_id: hir::HirId,
         ident: &mut Ident,
         attrs: Option<&'hir [Attribute]>,
-        vis: &mut hir::Visibility<'hir>,
+        vis_span: Span,
         i: &ItemKind,
     ) -> hir::ItemKind<'hir> {
         match *i {
@@ -260,7 +239,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 // Start with an empty prefix.
                 let prefix = Path { segments: vec![], span: use_tree.span, tokens: None };
 
-                self.lower_use_tree(use_tree, &prefix, id, vis, ident, attrs)
+                self.lower_use_tree(use_tree, &prefix, id, vis_span, ident, attrs)
             }
             ItemKind::Static(ref t, m, ref e) => {
                 let (ty, body_id) = self.lower_const_item(t, span, e.as_deref());
@@ -276,7 +255,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 ref body,
                 ..
             }) => {
-                let fn_def_id = self.resolver.local_def_id(id);
                 self.with_new_scopes(|this| {
                     this.current_item = Some(ident.span);
 
@@ -288,20 +266,16 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     let body_id =
                         this.lower_maybe_async_body(span, &decl, asyncness, body.as_deref());
 
-                    let (generics, decl) = this.add_in_band_defs(
-                        generics,
-                        fn_def_id,
-                        AnonymousLifetimeMode::PassThrough,
-                        |this, idty| {
+                    let (generics, decl) =
+                        this.add_implicit_generics(generics, id, |this, idty, idpb| {
                             let ret_id = asyncness.opt_return_id();
                             this.lower_fn_decl(
                                 &decl,
-                                Some((fn_def_id, idty)),
+                                Some((id, idty, idpb)),
                                 FnDeclKind::Fn,
                                 ret_id,
                             )
-                        },
-                    );
+                        });
                     let sig = hir::FnSig {
                         decl,
                         header: this.lower_fn_header(header),
@@ -339,12 +313,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 //
                 // type Foo = Foo1
                 // opaque type Foo1: Trait
-                let ty = self.lower_ty(
-                    ty,
-                    ImplTraitContext::TypeAliasesOpaqueTy {
-                        capturable_lifetimes: &mut FxHashSet::default(),
-                    },
-                );
+                let ty = self.lower_ty(ty, ImplTraitContext::TypeAliasesOpaqueTy);
                 let mut generics = generics.clone();
                 add_ty_alias_where_clause(&mut generics, where_clauses, true);
                 let generics = self.lower_generics(
@@ -419,12 +388,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 // method, it will not be considered an in-band
                 // lifetime to be added, but rather a reference to a
                 // parent lifetime.
-                let lowered_trait_def_id = hir_id.expect_owner();
-                let (generics, (trait_ref, lowered_ty)) = self.add_in_band_defs(
-                    ast_generics,
-                    lowered_trait_def_id,
-                    AnonymousLifetimeMode::CreateParameter,
-                    |this, _| {
+                let (generics, (trait_ref, lowered_ty)) =
+                    self.add_implicit_generics(ast_generics, id, |this, _, _| {
                         let trait_ref = trait_ref.as_ref().map(|trait_ref| {
                             this.lower_trait_ref(
                                 trait_ref,
@@ -436,16 +401,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
                             .lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type));
 
                         (trait_ref, lowered_ty)
-                    },
-                );
-
-                let new_impl_items =
-                    self.with_in_scope_lifetime_defs(&ast_generics.params, |this| {
-                        this.arena.alloc_from_iter(
-                            impl_items.iter().map(|item| this.lower_impl_item_ref(item)),
-                        )
                     });
 
+                let new_impl_items = self
+                    .arena
+                    .alloc_from_iter(impl_items.iter().map(|item| self.lower_impl_item_ref(item)));
+
                 // `defaultness.has_value()` is never called for an `impl`, always `true` in order
                 // to not cause an assertion failure inside the `lower_defaultness` function.
                 let has_val = true;
@@ -454,7 +415,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     ImplPolarity::Positive => ImplPolarity::Positive,
                     ImplPolarity::Negative(s) => ImplPolarity::Negative(self.lower_span(s)),
                 };
-                hir::ItemKind::Impl(hir::Impl {
+                hir::ItemKind::Impl(self.arena.alloc(hir::Impl {
                     unsafety: self.lower_unsafety(unsafety),
                     polarity,
                     defaultness,
@@ -464,7 +425,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     of_trait: trait_ref,
                     self_ty: lowered_ty,
                     items: new_impl_items,
-                })
+                }))
             }
             ItemKind::Trait(box Trait {
                 is_auto,
@@ -527,12 +488,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
         tree: &UseTree,
         prefix: &Path,
         id: NodeId,
-        vis: &mut hir::Visibility<'hir>,
+        vis_span: Span,
         ident: &mut Ident,
         attrs: Option<&'hir [Attribute]>,
     ) -> hir::ItemKind<'hir> {
         debug!("lower_use_tree(tree={:?})", tree);
-        debug!("lower_use_tree: vis = {:?}", vis);
 
         let path = &tree.prefix;
         let segments = prefix.segments.iter().chain(path.segments.iter()).cloned().collect();
@@ -586,7 +546,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
                         let res = this.lower_res(res);
                         let path = this.lower_path_extra(res, &path, ParamMode::Explicit);
                         let kind = hir::ItemKind::Use(path, hir::UseKind::Single);
-                        let vis = this.rebuild_vis(&vis);
                         if let Some(attrs) = attrs {
                             this.attrs.insert(hir::ItemLocalId::new(0), attrs);
                         }
@@ -595,7 +554,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                             def_id: new_id,
                             ident: this.lower_ident(ident),
                             kind,
-                            vis,
+                            vis_span,
                             span: this.lower_span(span),
                         };
                         hir::OwnerNode::Item(this.arena.alloc(item))
@@ -657,11 +616,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     // own its own names, we have to adjust the owner before
                     // lowering the rest of the import.
                     self.with_hir_id_owner(id, |this| {
-                        let mut vis = this.rebuild_vis(&vis);
                         let mut ident = *ident;
 
                         let kind =
-                            this.lower_use_tree(use_tree, &prefix, id, &mut vis, &mut ident, attrs);
+                            this.lower_use_tree(use_tree, &prefix, id, vis_span, &mut ident, attrs);
                         if let Some(attrs) = attrs {
                             this.attrs.insert(hir::ItemLocalId::new(0), attrs);
                         }
@@ -670,37 +628,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
                             def_id: new_hir_id,
                             ident: this.lower_ident(ident),
                             kind,
-                            vis,
+                            vis_span,
                             span: this.lower_span(use_tree.span),
                         };
                         hir::OwnerNode::Item(this.arena.alloc(item))
                     });
                 }
 
-                // Subtle and a bit hacky: we lower the privacy level
-                // of the list stem to "private" most of the time, but
-                // not for "restricted" paths. The key thing is that
-                // we don't want it to stay as `pub` (with no caveats)
-                // because that affects rustdoc and also the lints
-                // about `pub` items. But we can't *always* make it
-                // private -- particularly not for restricted paths --
-                // because it contains node-ids that would then be
-                // unused, failing the check that HirIds are "densely
-                // assigned".
-                match vis.node {
-                    hir::VisibilityKind::Public
-                    | hir::VisibilityKind::Crate(_)
-                    | hir::VisibilityKind::Inherited => {
-                        *vis = respan(
-                            self.lower_span(prefix.span.shrink_to_lo()),
-                            hir::VisibilityKind::Inherited,
-                        );
-                    }
-                    hir::VisibilityKind::Restricted { .. } => {
-                        // Do nothing here, as described in the comment on the match.
-                    }
-                }
-
                 let res = self.expect_full_res_from_use(id).next().unwrap_or(Res::Err);
                 let res = self.lower_res(res);
                 let path = self.lower_path_extra(res, &prefix, ParamMode::Explicit);
@@ -709,37 +643,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
         }
     }
 
-    /// Paths like the visibility path in `pub(super) use foo::{bar, baz}` are repeated
-    /// many times in the HIR tree; for each occurrence, we need to assign distinct
-    /// `NodeId`s. (See, e.g., #56128.)
-    fn rebuild_use_path(&mut self, path: &hir::Path<'hir>) -> &'hir hir::Path<'hir> {
-        debug!("rebuild_use_path(path = {:?})", path);
-        let segments =
-            self.arena.alloc_from_iter(path.segments.iter().map(|seg| hir::PathSegment {
-                ident: seg.ident,
-                hir_id: seg.hir_id.map(|_| self.next_id()),
-                res: seg.res,
-                args: None,
-                infer_args: seg.infer_args,
-            }));
-        self.arena.alloc(hir::Path { span: path.span, res: path.res, segments })
-    }
-
-    fn rebuild_vis(&mut self, vis: &hir::Visibility<'hir>) -> hir::Visibility<'hir> {
-        let vis_kind = match vis.node {
-            hir::VisibilityKind::Public => hir::VisibilityKind::Public,
-            hir::VisibilityKind::Crate(sugar) => hir::VisibilityKind::Crate(sugar),
-            hir::VisibilityKind::Inherited => hir::VisibilityKind::Inherited,
-            hir::VisibilityKind::Restricted { ref path, hir_id: _ } => {
-                hir::VisibilityKind::Restricted {
-                    path: self.rebuild_use_path(path),
-                    hir_id: self.next_id(),
-                }
-            }
-        };
-        respan(self.lower_span(vis.span), vis_kind)
-    }
-
     fn lower_foreign_item(&mut self, i: &ForeignItem) -> &'hir hir::ForeignItem<'hir> {
         let hir_id = self.lower_node_id(i.id);
         let def_id = hir_id.expect_owner();
@@ -750,18 +653,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
             kind: match i.kind {
                 ForeignItemKind::Fn(box Fn { ref sig, ref generics, .. }) => {
                     let fdec = &sig.decl;
-                    let (generics, (fn_dec, fn_args)) = self.add_in_band_defs(
-                        generics,
-                        def_id,
-                        AnonymousLifetimeMode::PassThrough,
-                        |this, _| {
+                    let (generics, (fn_dec, fn_args)) =
+                        self.add_implicit_generics(generics, i.id, |this, _, _| {
                             (
                                 // Disallow `impl Trait` in foreign items.
                                 this.lower_fn_decl(fdec, None, FnDeclKind::ExternFn, None),
                                 this.lower_fn_params_to_names(fdec),
                             )
-                        },
-                    );
+                        });
 
                     hir::ForeignItemKind::Fn(fn_dec, fn_args, generics)
                 }
@@ -773,7 +672,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 ForeignItemKind::TyAlias(..) => hir::ForeignItemKind::Type,
                 ForeignItemKind::MacCall(_) => panic!("macro shouldn't exist here"),
             },
-            vis: self.lower_visibility(&i.vis),
+            vis_span: self.lower_span(i.vis.span),
             span: self.lower_span(i.span),
         };
         self.arena.alloc(item)
@@ -851,7 +750,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 // FIXME(jseyfried): positional field hygiene.
                 None => Ident::new(sym::integer(index), self.lower_span(f.span)),
             },
-            vis: self.lower_visibility(&f.vis),
+            vis_span: self.lower_span(f.vis.span),
             ty,
         }
     }
@@ -868,13 +767,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
             }
             AssocItemKind::Fn(box Fn { ref sig, ref generics, body: None, .. }) => {
                 let names = self.lower_fn_params_to_names(&sig.decl);
-                let (generics, sig) = self.lower_method_sig(
-                    generics,
-                    sig,
-                    trait_item_def_id,
-                    FnDeclKind::Trait,
-                    None,
-                );
+                let (generics, sig) =
+                    self.lower_method_sig(generics, sig, i.id, FnDeclKind::Trait, None);
                 (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)))
             }
             AssocItemKind::Fn(box Fn { ref sig, ref generics, body: Some(ref body), .. }) => {
@@ -884,7 +778,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 let (generics, sig) = self.lower_method_sig(
                     generics,
                     sig,
-                    trait_item_def_id,
+                    i.id,
                     FnDeclKind::Trait,
                     asyncness.opt_return_id(),
                 );
@@ -958,8 +852,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
     }
 
     fn lower_impl_item(&mut self, i: &AssocItem) -> &'hir hir::ImplItem<'hir> {
-        let impl_item_def_id = self.resolver.local_def_id(i.id);
-
         let (generics, kind) = match &i.kind {
             AssocItemKind::Const(_, ty, expr) => {
                 let ty = self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type));
@@ -976,7 +868,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 let (generics, sig) = self.lower_method_sig(
                     generics,
                     sig,
-                    impl_item_def_id,
+                    i.id,
                     if self.is_in_trait_impl { FnDeclKind::Impl } else { FnDeclKind::Inherent },
                     asyncness.opt_return_id(),
                 );
@@ -996,12 +888,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                         hir::ImplItemKind::TyAlias(ty)
                     }
                     Some(ty) => {
-                        let ty = self.lower_ty(
-                            ty,
-                            ImplTraitContext::TypeAliasesOpaqueTy {
-                                capturable_lifetimes: &mut FxHashSet::default(),
-                            },
-                        );
+                        let ty = self.lower_ty(ty, ImplTraitContext::TypeAliasesOpaqueTy);
                         hir::ImplItemKind::TyAlias(ty)
                     }
                 };
@@ -1016,8 +903,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
             def_id: hir_id.expect_owner(),
             ident: self.lower_ident(i.ident),
             generics,
-            vis: self.lower_visibility(&i.vis),
             kind,
+            vis_span: self.lower_span(i.vis.span),
             span: self.lower_span(i.span),
         };
         self.arena.alloc(item)
@@ -1044,28 +931,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
         }
     }
 
-    /// If an `explicit_owner` is given, this method allocates the `HirId` in
-    /// the address space of that item instead of the item currently being
-    /// lowered. This can happen during `lower_impl_item_ref()` where we need to
-    /// lower a `Visibility` value although we haven't lowered the owning
-    /// `ImplItem` in question yet.
-    fn lower_visibility(&mut self, v: &Visibility) -> hir::Visibility<'hir> {
-        let node = match v.kind {
-            VisibilityKind::Public => hir::VisibilityKind::Public,
-            VisibilityKind::Crate(sugar) => hir::VisibilityKind::Crate(sugar),
-            VisibilityKind::Restricted { ref path, id } => {
-                debug!("lower_visibility: restricted path id = {:?}", id);
-                let lowered_id = self.lower_node_id(id);
-                hir::VisibilityKind::Restricted {
-                    path: self.lower_path(id, path, ParamMode::Explicit),
-                    hir_id: lowered_id,
-                }
-            }
-            VisibilityKind::Inherited => hir::VisibilityKind::Inherited,
-        };
-        respan(self.lower_span(v.span), node)
-    }
-
     fn lower_defaultness(
         &self,
         d: Defaultness,
@@ -1363,17 +1228,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
         &mut self,
         generics: &Generics,
         sig: &FnSig,
-        fn_def_id: LocalDefId,
+        id: NodeId,
         kind: FnDeclKind,
         is_async: Option<NodeId>,
-    ) -> (hir::Generics<'hir>, hir::FnSig<'hir>) {
+    ) -> (&'hir hir::Generics<'hir>, hir::FnSig<'hir>) {
         let header = self.lower_fn_header(sig.header);
-        let (generics, decl) = self.add_in_band_defs(
-            generics,
-            fn_def_id,
-            AnonymousLifetimeMode::PassThrough,
-            |this, idty| this.lower_fn_decl(&sig.decl, Some((fn_def_id, idty)), kind, is_async),
-        );
+        let (generics, decl) = self.add_implicit_generics(generics, id, |this, idty, idpb| {
+            this.lower_fn_decl(&sig.decl, Some((id, idty, idpb)), kind, is_async)
+        });
         (generics, hir::FnSig { header, decl, span: self.lower_span(sig.span) })
     }
 
@@ -1432,7 +1294,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
     pub(super) fn lower_generics_mut(
         &mut self,
         generics: &Generics,
-        itctx: ImplTraitContext<'_, 'hir>,
+        mut itctx: ImplTraitContext<'_, 'hir>,
     ) -> GenericsCtor<'hir> {
         // Error if `?Trait` bounds in where clauses don't refer directly to type parameters.
         // Note: we used to clone these bounds directly onto the type parameter (and avoid lowering
@@ -1481,9 +1343,24 @@ impl<'hir> LoweringContext<'_, 'hir> {
             }
         }
 
+        let mut predicates = SmallVec::new();
+        predicates.extend(generics.params.iter().filter_map(|param| {
+            let bounds = self.lower_param_bounds(&param.bounds, itctx.reborrow());
+            self.lower_generic_bound_predicate(param.ident, param.id, &param.kind, bounds)
+        }));
+        predicates.extend(
+            generics
+                .where_clause
+                .predicates
+                .iter()
+                .map(|predicate| self.lower_where_predicate(predicate)),
+        );
+
         GenericsCtor {
-            params: self.lower_generic_params_mut(&generics.params, itctx).collect(),
-            where_clause: self.lower_where_clause(&generics.where_clause),
+            params: self.lower_generic_params_mut(&generics.params).collect(),
+            predicates,
+            has_where_clause: !generics.where_clause.predicates.is_empty(),
+            where_clause_span: self.lower_span(generics.where_clause.span),
             span: self.lower_span(generics.span),
         }
     }
@@ -1492,20 +1369,75 @@ impl<'hir> LoweringContext<'_, 'hir> {
         &mut self,
         generics: &Generics,
         itctx: ImplTraitContext<'_, 'hir>,
-    ) -> hir::Generics<'hir> {
+    ) -> &'hir hir::Generics<'hir> {
         let generics_ctor = self.lower_generics_mut(generics, itctx);
         generics_ctor.into_generics(self.arena)
     }
 
-    fn lower_where_clause(&mut self, wc: &WhereClause) -> hir::WhereClause<'hir> {
-        self.with_anonymous_lifetime_mode(AnonymousLifetimeMode::ReportError, |this| {
-            hir::WhereClause {
-                predicates: this.arena.alloc_from_iter(
-                    wc.predicates.iter().map(|predicate| this.lower_where_predicate(predicate)),
-                ),
-                span: this.lower_span(wc.span),
+    pub(super) fn lower_generic_bound_predicate(
+        &mut self,
+        ident: Ident,
+        id: NodeId,
+        kind: &GenericParamKind,
+        bounds: &'hir [hir::GenericBound<'hir>],
+    ) -> Option<hir::WherePredicate<'hir>> {
+        // Do not create a clause if we do not have anything inside it.
+        if bounds.is_empty() {
+            return None;
+        }
+        let ident = self.lower_ident(ident);
+        let param_span = ident.span;
+        let span = bounds
+            .iter()
+            .fold(Some(param_span.shrink_to_hi()), |span: Option<Span>, bound| {
+                let bound_span = bound.span();
+                // We include bounds that come from a `#[derive(_)]` but point at the user's code,
+                // as we use this method to get a span appropriate for suggestions.
+                if !bound_span.can_be_used_for_suggestions() {
+                    None
+                } else if let Some(span) = span {
+                    Some(span.to(bound_span))
+                } else {
+                    Some(bound_span)
+                }
+            })
+            .unwrap_or(param_span.shrink_to_hi());
+        match kind {
+            GenericParamKind::Const { .. } => None,
+            GenericParamKind::Type { .. } => {
+                let def_id = self.resolver.local_def_id(id).to_def_id();
+                let ty_path = self.arena.alloc(hir::Path {
+                    span: param_span,
+                    res: Res::Def(DefKind::TyParam, def_id),
+                    segments: self.arena.alloc_from_iter([hir::PathSegment::from_ident(ident)]),
+                });
+                let ty_id = self.next_id();
+                let bounded_ty =
+                    self.ty_path(ty_id, param_span, hir::QPath::Resolved(None, ty_path));
+                Some(hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
+                    bounded_ty: self.arena.alloc(bounded_ty),
+                    bounds,
+                    span,
+                    bound_generic_params: &[],
+                    in_where_clause: false,
+                }))
             }
-        })
+            GenericParamKind::Lifetime => {
+                let ident_span = self.lower_span(ident.span);
+                let ident = self.lower_ident(ident);
+                let res = self.resolver.get_lifetime_res(id).unwrap_or_else(|| {
+                    panic!("Missing resolution for lifetime {:?} at {:?}", id, ident.span)
+                });
+                let lt_id = self.resolver.next_node_id();
+                let lifetime = self.new_named_lifetime_with_res(lt_id, ident_span, ident, res);
+                Some(hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate {
+                    lifetime,
+                    span,
+                    bounds,
+                    in_where_clause: false,
+                }))
+            }
+        }
     }
 
     fn lower_where_predicate(&mut self, pred: &WherePredicate) -> hir::WherePredicate<'hir> {
@@ -1515,24 +1447,18 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 ref bounded_ty,
                 ref bounds,
                 span,
-            }) => self.with_in_scope_lifetime_defs(&bound_generic_params, |this| {
-                hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
-                    bound_generic_params: this.lower_generic_params(
-                        bound_generic_params,
-                        ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
-                    ),
-                    bounded_ty: this.lower_ty(
-                        bounded_ty,
-                        ImplTraitContext::Disallowed(ImplTraitPosition::Type),
-                    ),
-                    bounds: this.arena.alloc_from_iter(bounds.iter().map(|bound| {
-                        this.lower_param_bound(
-                            bound,
-                            ImplTraitContext::Disallowed(ImplTraitPosition::Bound),
-                        )
-                    })),
-                    span: this.lower_span(span),
-                })
+            }) => hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
+                bound_generic_params: self.lower_generic_params(bound_generic_params),
+                bounded_ty: self
+                    .lower_ty(bounded_ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type)),
+                bounds: self.arena.alloc_from_iter(bounds.iter().map(|bound| {
+                    self.lower_param_bound(
+                        bound,
+                        ImplTraitContext::Disallowed(ImplTraitPosition::Bound),
+                    )
+                })),
+                span: self.lower_span(span),
+                in_where_clause: true,
             }),
             WherePredicate::RegionPredicate(WhereRegionPredicate {
                 ref lifetime,
@@ -1545,6 +1471,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     bounds,
                     ImplTraitContext::Disallowed(ImplTraitPosition::Bound),
                 ),
+                in_where_clause: true,
             }),
             WherePredicate::EqPredicate(WhereEqPredicate { id, ref lhs_ty, ref rhs_ty, span }) => {
                 hir::WherePredicate::EqPredicate(hir::WhereEqPredicate {
@@ -1563,16 +1490,20 @@ impl<'hir> LoweringContext<'_, 'hir> {
 /// Helper struct for delayed construction of Generics.
 pub(super) struct GenericsCtor<'hir> {
     pub(super) params: SmallVec<[hir::GenericParam<'hir>; 4]>,
-    where_clause: hir::WhereClause<'hir>,
+    pub(super) predicates: SmallVec<[hir::WherePredicate<'hir>; 4]>,
+    has_where_clause: bool,
+    where_clause_span: Span,
     span: Span,
 }
 
 impl<'hir> GenericsCtor<'hir> {
-    pub(super) fn into_generics(self, arena: &'hir Arena<'hir>) -> hir::Generics<'hir> {
-        hir::Generics {
+    pub(super) fn into_generics(self, arena: &'hir Arena<'hir>) -> &'hir hir::Generics<'hir> {
+        arena.alloc(hir::Generics {
             params: arena.alloc_from_iter(self.params),
-            where_clause: self.where_clause,
+            predicates: arena.alloc_from_iter(self.predicates),
+            has_where_clause: self.has_where_clause,
+            where_clause_span: self.where_clause_span,
             span: self.span,
-        }
+        })
     }
 }
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 9cb205074e7..d433775f85c 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -32,12 +32,13 @@
 
 #![feature(crate_visibility_modifier)]
 #![feature(box_patterns)]
+#![feature(let_chains)]
 #![feature(let_else)]
 #![feature(never_type)]
 #![recursion_limit = "256"]
 #![allow(rustc::potential_query_instability)]
 
-use rustc_ast::token::{self, Token};
+use rustc_ast::token::{Delimiter, Token};
 use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, TokenStream, TokenTree};
 use rustc_ast::visit;
 use rustc_ast::{self as ast, *};
@@ -53,7 +54,6 @@ use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Namespace, PartialRes, PerNS, Res};
 use rustc_hir::def_id::{DefId, DefPathHash, LocalDefId, CRATE_DEF_ID};
 use rustc_hir::definitions::{DefKey, DefPathData, Definitions};
-use rustc_hir::intravisit;
 use rustc_hir::{ConstArg, GenericArg, ItemLocalId, ParamName, TraitCandidate};
 use rustc_index::vec::{Idx, IndexVec};
 use rustc_query_system::ich::StableHashingContext;
@@ -61,7 +61,7 @@ use rustc_session::parse::feature_err;
 use rustc_session::utils::{FlattenNonterminals, NtToTokenstream};
 use rustc_session::Session;
 use rustc_span::hygiene::{ExpnId, MacroKind};
-use rustc_span::source_map::{respan, DesugaringKind};
+use rustc_span::source_map::DesugaringKind;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{Span, DUMMY_SP};
 
@@ -122,27 +122,8 @@ struct LoweringContext<'a, 'hir: 'a> {
     is_in_trait_impl: bool,
     is_in_dyn_type: bool,
 
-    /// What to do when we encounter an "anonymous lifetime
-    /// reference". The term "anonymous" is meant to encompass both
-    /// `'_` lifetimes as well as fully elided cases where nothing is
-    /// written at all (e.g., `&T` or `std::cell::Ref<T>`).
-    anonymous_lifetime_mode: AnonymousLifetimeMode,
-
-    /// Used to create lifetime definitions for anonymous lifetimes.
-    /// When an anonymous lifetime is encountered in a function or impl header and
-    /// requires to create a fresh lifetime parameter, it is added
-    /// to this list. The results of this list are then added to the list of
-    /// lifetime definitions in the corresponding impl or function generics.
-    lifetimes_to_define: Vec<(Span, NodeId)>,
-
-    /// If anonymous lifetimes are being collected, this field holds the parent
-    /// `LocalDefId` to create the fresh lifetime parameters' `LocalDefId`.
-    is_collecting_anonymous_lifetimes: Option<LocalDefId>,
-
-    /// Currently in-scope lifetimes defined in impl headers, fn headers, or HRTB.
-    /// We always store a `normalize_to_macros_2_0()` version of the param-name in this
-    /// vector.
-    in_scope_lifetimes: Vec<ParamName>,
+    /// Used to handle lifetimes appearing in impl-traits.
+    captured_lifetimes: Option<LifetimeCaptureContext>,
 
     current_hir_id_owner: LocalDefId,
     item_local_id_counter: hir::ItemLocalId,
@@ -157,6 +138,68 @@ struct LoweringContext<'a, 'hir: 'a> {
     allow_into_future: Option<Lrc<[Symbol]>>,
 }
 
+/// Resolution for a lifetime appearing in a type.
+#[derive(Copy, Clone, Debug)]
+pub enum LifetimeRes {
+    /// Successfully linked the lifetime to a generic parameter.
+    Param {
+        /// Id of the generic parameter that introduced it.
+        param: LocalDefId,
+        /// Id of the introducing place. That can be:
+        /// - an item's id, for the item's generic parameters;
+        /// - a TraitRef's ref_id, identifying the `for<...>` binder;
+        /// - a BareFn type's id;
+        /// - a Path's id when this path has parenthesized generic args.
+        ///
+        /// This information is used for impl-trait lifetime captures, to know when to or not to
+        /// capture any given lifetime.
+        binder: NodeId,
+    },
+    /// Created a generic parameter for an anonymous lifetime.
+    Fresh {
+        /// Id of the generic parameter that introduced it.
+        param: LocalDefId,
+        /// Id of the introducing place. See `Param`.
+        binder: NodeId,
+    },
+    /// This variant is used for anonymous lifetimes that we did not resolve during
+    /// late resolution.  Shifting the work to the HIR lifetime resolver.
+    Anonymous {
+        /// Id of the introducing place. See `Param`.
+        binder: NodeId,
+        /// Whether this lifetime was spelled or elided.
+        elided: bool,
+    },
+    /// Explicit `'static` lifetime.
+    Static,
+    /// Resolution failure.
+    Error,
+    /// HACK: This is used to recover the NodeId of an elided lifetime.
+    ElidedAnchor { start: NodeId, end: NodeId },
+}
+
+/// When we lower a lifetime, it is inserted in `captures`, and the resolution is modified so
+/// to point to the lifetime parameter impl-trait will generate.
+/// When traversing `for<...>` binders, they are inserted in `binders_to_ignore` so we know *not*
+/// to rebind the introduced lifetimes.
+#[derive(Debug)]
+struct LifetimeCaptureContext {
+    /// parent def_id for new definitions
+    parent_def_id: LocalDefId,
+    /// Set of lifetimes to rebind.
+    captures: FxHashMap<
+        LocalDefId, // original parameter id
+        (
+            Span,        // Span
+            NodeId,      // synthetized parameter id
+            ParamName,   // parameter name
+            LifetimeRes, // original resolution
+        ),
+    >,
+    /// Traversed binders.  The ids in this set should *not* be rebound.
+    binders_to_ignore: FxHashSet<NodeId>,
+}
+
 pub trait ResolverAstLowering {
     fn def_key(&self, id: DefId) -> DefKey;
 
@@ -175,6 +218,12 @@ pub trait ResolverAstLowering {
     /// Obtains resolution for a label with the given `NodeId`.
     fn get_label_res(&self, id: NodeId) -> Option<NodeId>;
 
+    /// Obtains resolution for a lifetime with the given `NodeId`.
+    fn get_lifetime_res(&self, id: NodeId) -> Option<LifetimeRes>;
+
+    /// Obtain the list of lifetimes parameters to add to an item.
+    fn take_extra_lifetime_params(&mut self, id: NodeId) -> Vec<(Ident, NodeId, LifetimeRes)>;
+
     fn create_stable_hashing_context(&self) -> StableHashingContext<'_>;
 
     fn definitions(&self) -> &Definitions;
@@ -210,36 +259,18 @@ enum ImplTraitContext<'b, 'a> {
     /// equivalent to a fresh universal parameter like `fn foo<T: Debug>(x: T)`.
     ///
     /// Newly generated parameters should be inserted into the given `Vec`.
-    Universal(&'b mut Vec<hir::GenericParam<'a>>, LocalDefId),
+    Universal(&'b mut Vec<hir::GenericParam<'a>>, &'b mut Vec<hir::WherePredicate<'a>>, LocalDefId),
 
     /// Treat `impl Trait` as shorthand for a new opaque type.
     /// Example: `fn foo() -> impl Debug`, where `impl Debug` is conceptually
     /// equivalent to a new opaque type like `type T = impl Debug; fn foo() -> T`.
     ///
     ReturnPositionOpaqueTy {
-        /// `DefId` for the parent function, used to look up necessary
-        /// information later.
-        fn_def_id: LocalDefId,
         /// Origin: Either OpaqueTyOrigin::FnReturn or OpaqueTyOrigin::AsyncFn,
         origin: hir::OpaqueTyOrigin,
     },
     /// Impl trait in type aliases.
-    TypeAliasesOpaqueTy {
-        /// Set of lifetimes that this opaque type can capture, if it uses
-        /// them. This includes lifetimes bound since we entered this context.
-        /// For example:
-        ///
-        /// ```
-        /// type A<'b> = impl for<'a> Trait<'a, Out = impl Sized + 'a>;
-        /// ```
-        ///
-        /// Here the inner opaque type captures `'a` because it uses it. It doesn't
-        /// need to capture `'b` because it already inherits the lifetime
-        /// parameter from `A`.
-        // FIXME(impl_trait): but `required_region_bounds` will ICE later
-        // anyway.
-        capturable_lifetimes: &'b mut FxHashSet<hir::LifetimeName>,
-    },
+    TypeAliasesOpaqueTy,
     /// `impl Trait` is not accepted in this position.
     Disallowed(ImplTraitPosition),
 }
@@ -272,13 +303,9 @@ impl<'a> ImplTraitContext<'_, 'a> {
     fn reborrow<'this>(&'this mut self) -> ImplTraitContext<'this, 'a> {
         use self::ImplTraitContext::*;
         match self {
-            Universal(params, parent) => Universal(params, *parent),
-            ReturnPositionOpaqueTy { fn_def_id, origin } => {
-                ReturnPositionOpaqueTy { fn_def_id: *fn_def_id, origin: *origin }
-            }
-            TypeAliasesOpaqueTy { capturable_lifetimes } => {
-                TypeAliasesOpaqueTy { capturable_lifetimes }
-            }
+            Universal(params, bounds, parent) => Universal(params, bounds, *parent),
+            ReturnPositionOpaqueTy { origin } => ReturnPositionOpaqueTy { origin: *origin },
+            TypeAliasesOpaqueTy => TypeAliasesOpaqueTy,
             Disallowed(pos) => Disallowed(*pos),
         }
     }
@@ -453,56 +480,6 @@ enum ParenthesizedGenericArgs {
     Err,
 }
 
-/// What to do when we encounter an **anonymous** lifetime
-/// reference. Anonymous lifetime references come in two flavors. You
-/// have implicit, or fully elided, references to lifetimes, like the
-/// one in `&T` or `Ref<T>`, and you have `'_` lifetimes, like `&'_ T`
-/// or `Ref<'_, T>`. These often behave the same, but not always:
-///
-/// - certain usages of implicit references are deprecated, like
-///   `Ref<T>`, and we sometimes just give hard errors in those cases
-///   as well.
-/// - for object bounds there is a difference: `Box<dyn Foo>` is not
-///   the same as `Box<dyn Foo + '_>`.
-///
-/// We describe the effects of the various modes in terms of three cases:
-///
-/// - **Modern** -- includes all uses of `'_`, but also the lifetime arg
-///   of a `&` (e.g., the missing lifetime in something like `&T`)
-/// - **Dyn Bound** -- if you have something like `Box<dyn Foo>`,
-///   there is an elided lifetime bound (`Box<dyn Foo + 'X>`). These
-///   elided bounds follow special rules. Note that this only covers
-///   cases where *nothing* is written; the `'_` in `Box<dyn Foo +
-///   '_>` is a case of "modern" elision.
-/// - **Deprecated** -- this covers cases like `Ref<T>`, where the lifetime
-///   parameter to ref is completely elided. `Ref<'_, T>` would be the modern,
-///   non-deprecated equivalent.
-///
-/// Currently, the handling of lifetime elision is somewhat spread out
-/// between HIR lowering and -- as described below -- the
-/// `resolve_lifetime` module. Often we "fallthrough" to that code by generating
-/// an "elided" or "underscore" lifetime name. In the future, we probably want to move
-/// everything into HIR lowering.
-#[derive(Copy, Clone, Debug)]
-pub enum AnonymousLifetimeMode {
-    /// For **Modern** cases, create a new anonymous region parameter
-    /// and reference that.
-    ///
-    /// For **Dyn Bound** cases, pass responsibility to
-    /// `resolve_lifetime` code.
-    ///
-    /// For **Deprecated** cases, report an error.
-    CreateParameter,
-
-    /// Give a hard error when either `&` or `'_` is written. Used to
-    /// rule out things like `where T: Foo<'_>`. Does not imply an
-    /// error on default object bounds (e.g., `Box<dyn Foo>`).
-    ReportError,
-
-    /// Pass responsibility to `resolve_lifetime` code for all cases.
-    PassThrough,
-}
-
 impl<'a, 'hir> LoweringContext<'a, 'hir> {
     fn with_hir_id_owner(
         &mut self,
@@ -688,26 +665,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         )
     }
 
-    fn with_anonymous_lifetime_mode<R>(
-        &mut self,
-        anonymous_lifetime_mode: AnonymousLifetimeMode,
-        op: impl FnOnce(&mut Self) -> R,
-    ) -> R {
-        debug!(
-            "with_anonymous_lifetime_mode(anonymous_lifetime_mode={:?})",
-            anonymous_lifetime_mode,
-        );
-        let old_anonymous_lifetime_mode = self.anonymous_lifetime_mode;
-        self.anonymous_lifetime_mode = anonymous_lifetime_mode;
-        let result = op(self);
-        self.anonymous_lifetime_mode = old_anonymous_lifetime_mode;
-        debug!(
-            "with_anonymous_lifetime_mode: restoring anonymous_lifetime_mode={:?}",
-            old_anonymous_lifetime_mode
-        );
-        result
-    }
-
     /// Intercept all spans entering HIR.
     /// Mark a span as relative to the current owning item.
     fn lower_span(&self, span: Span) -> Span {
@@ -723,140 +680,114 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         Ident::new(ident.name, self.lower_span(ident.span))
     }
 
-    /// Creates a new `hir::GenericParam` for every new lifetime and
-    /// type parameter encountered while evaluating `f`. Definitions
-    /// are created with the parent provided. If no `parent_id` is
-    /// provided, no definitions will be returned.
-    ///
-    /// Presuming that in-band lifetimes are enabled, then
-    /// `self.anonymous_lifetime_mode` will be updated to match the
-    /// parameter while `f` is running (and restored afterwards).
-    fn collect_in_band_defs<T>(
-        &mut self,
-        parent_def_id: LocalDefId,
-        f: impl FnOnce(&mut Self) -> T,
-    ) -> (Vec<(Span, NodeId)>, T) {
-        let was_collecting =
-            std::mem::replace(&mut self.is_collecting_anonymous_lifetimes, Some(parent_def_id));
-        let len = self.lifetimes_to_define.len();
-
-        let res = f(self);
-
-        let lifetimes_to_define = self.lifetimes_to_define.split_off(len);
-        self.is_collecting_anonymous_lifetimes = was_collecting;
-        (lifetimes_to_define, res)
-    }
-
     /// Converts a lifetime into a new generic parameter.
-    fn fresh_lifetime_to_generic_param(
+    fn lifetime_res_to_generic_param(
         &mut self,
-        span: Span,
+        ident: Ident,
         node_id: NodeId,
-    ) -> hir::GenericParam<'hir> {
+        res: LifetimeRes,
+    ) -> Option<hir::GenericParam<'hir>> {
+        let (name, kind) = match res {
+            LifetimeRes::Param { .. } => {
+                (hir::ParamName::Plain(ident), hir::LifetimeParamKind::Explicit)
+            }
+            LifetimeRes::Fresh { param, .. } => {
+                (hir::ParamName::Fresh(param), hir::LifetimeParamKind::Elided)
+            }
+            LifetimeRes::Static | LifetimeRes::Error => return None,
+            res => panic!(
+                "Unexpected lifetime resolution {:?} for {:?} at {:?}",
+                res, ident, ident.span
+            ),
+        };
         let hir_id = self.lower_node_id(node_id);
-        let def_id = self.resolver.local_def_id(node_id);
-        hir::GenericParam {
+        Some(hir::GenericParam {
             hir_id,
-            name: hir::ParamName::Fresh(def_id),
-            bounds: &[],
-            span: self.lower_span(span),
+            name,
+            span: self.lower_span(ident.span),
             pure_wrt_drop: false,
-            kind: hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Elided },
-        }
+            kind: hir::GenericParamKind::Lifetime { kind },
+            colon_span: None,
+        })
     }
 
-    /// When we have either an elided or `'_` lifetime in an impl
-    /// header, we convert it to an in-band lifetime.
-    fn collect_fresh_anonymous_lifetime(&mut self, span: Span) -> ParamName {
-        let Some(parent_def_id) = self.is_collecting_anonymous_lifetimes else { panic!() };
-
-        let node_id = self.resolver.next_node_id();
+    /// Creates a new `hir::GenericParam` for every new `Fresh` lifetime and
+    /// universal `impl Trait` type parameter encountered while evaluating `f`.
+    /// Definitions are created with the provided `parent_def_id`.
+    fn add_implicit_generics<T>(
+        &mut self,
+        generics: &Generics,
+        parent_node_id: NodeId,
+        f: impl FnOnce(
+            &mut Self,
+            &mut Vec<hir::GenericParam<'hir>>,
+            &mut Vec<hir::WherePredicate<'hir>>,
+        ) -> T,
+    ) -> (&'hir hir::Generics<'hir>, T) {
+        let mut impl_trait_defs = Vec::new();
+        let mut impl_trait_bounds = Vec::new();
+        let mut lowered_generics = self.lower_generics_mut(
+            generics,
+            ImplTraitContext::Universal(
+                &mut impl_trait_defs,
+                &mut impl_trait_bounds,
+                self.current_hir_id_owner,
+            ),
+        );
+        let res = f(self, &mut impl_trait_defs, &mut impl_trait_bounds);
 
-        // Add a definition for the in-band lifetime def.
-        let param_def_id = self.resolver.create_def(
-            parent_def_id,
-            node_id,
-            DefPathData::LifetimeNs(kw::UnderscoreLifetime),
-            ExpnId::root(),
-            span.with_parent(None),
+        let extra_lifetimes = self.resolver.take_extra_lifetime_params(parent_node_id);
+        lowered_generics.params.extend(
+            extra_lifetimes
+                .into_iter()
+                .filter_map(|(ident, node_id, res)| {
+                    self.lifetime_res_to_generic_param(ident, node_id, res)
+                })
+                .chain(impl_trait_defs),
         );
+        lowered_generics.predicates.extend(impl_trait_bounds);
 
-        let hir_name = ParamName::Fresh(param_def_id);
-        self.lifetimes_to_define.push((span, node_id));
-        hir_name
+        let lowered_generics = lowered_generics.into_generics(self.arena);
+        (lowered_generics, res)
     }
 
-    // Evaluates `f` with the lifetimes in `params` in-scope.
-    // This is used to track which lifetimes have already been defined, and
-    // which are new in-band lifetimes that need to have a definition created
-    // for them.
-    fn with_in_scope_lifetime_defs<T>(
+    /// Setup lifetime capture for and impl-trait.
+    /// The captures will be added to `captures`.
+    fn while_capturing_lifetimes<T>(
         &mut self,
-        params: &[GenericParam],
+        parent_def_id: LocalDefId,
+        captures: &mut FxHashMap<LocalDefId, (Span, NodeId, ParamName, LifetimeRes)>,
         f: impl FnOnce(&mut Self) -> T,
     ) -> T {
-        let old_len = self.in_scope_lifetimes.len();
-        let lt_def_names = params.iter().filter_map(|param| match param.kind {
-            GenericParamKind::Lifetime { .. } => {
-                Some(ParamName::Plain(param.ident.normalize_to_macros_2_0()))
-            }
-            _ => None,
-        });
-        self.in_scope_lifetimes.extend(lt_def_names);
-
-        let res = f(self);
+        let lifetime_stash = std::mem::replace(
+            &mut self.captured_lifetimes,
+            Some(LifetimeCaptureContext {
+                parent_def_id,
+                captures: std::mem::take(captures),
+                binders_to_ignore: Default::default(),
+            }),
+        );
 
-        self.in_scope_lifetimes.truncate(old_len);
-        res
-    }
+        let ret = f(self);
 
-    /// Appends in-band lifetime defs and argument-position `impl
-    /// Trait` defs to the existing set of generics.
-    ///
-    /// Presuming that in-band lifetimes are enabled, then
-    /// `self.anonymous_lifetime_mode` will be updated to match the
-    /// parameter while `f` is running (and restored afterwards).
-    fn add_in_band_defs<T>(
-        &mut self,
-        generics: &Generics,
-        parent_def_id: LocalDefId,
-        anonymous_lifetime_mode: AnonymousLifetimeMode,
-        f: impl FnOnce(&mut Self, &mut Vec<hir::GenericParam<'hir>>) -> T,
-    ) -> (hir::Generics<'hir>, T) {
-        let (lifetimes_to_define, (mut lowered_generics, impl_trait_defs, res)) = self
-            .collect_in_band_defs(parent_def_id, |this| {
-                this.with_anonymous_lifetime_mode(anonymous_lifetime_mode, |this| {
-                    this.with_in_scope_lifetime_defs(&generics.params, |this| {
-                        let mut impl_trait_defs = Vec::new();
-                        // Note: it is necessary to lower generics *before* calling `f`.
-                        // When lowering `async fn`, there's a final step when lowering
-                        // the return type that assumes that all in-scope lifetimes have
-                        // already been added to either `in_scope_lifetimes` or
-                        // `lifetimes_to_define`. If we swapped the order of these two,
-                        // in-band-lifetimes introduced by generics or where-clauses
-                        // wouldn't have been added yet.
-                        let generics = this.lower_generics_mut(
-                            generics,
-                            ImplTraitContext::Universal(
-                                &mut impl_trait_defs,
-                                this.current_hir_id_owner,
-                            ),
-                        );
-                        let res = f(this, &mut impl_trait_defs);
-                        (generics, impl_trait_defs, res)
-                    })
-                })
-            });
+        let ctxt = std::mem::replace(&mut self.captured_lifetimes, lifetime_stash).unwrap();
+        *captures = ctxt.captures;
 
-        lowered_generics.params.extend(
-            lifetimes_to_define
-                .into_iter()
-                .map(|(span, node_id)| self.fresh_lifetime_to_generic_param(span, node_id))
-                .chain(impl_trait_defs),
-        );
+        ret
+    }
 
-        let lowered_generics = lowered_generics.into_generics(self.arena);
-        (lowered_generics, res)
+    /// Register a binder to be ignored for lifetime capture.
+    #[tracing::instrument(level = "debug", skip(self, f))]
+    #[inline]
+    fn with_lifetime_binder<T>(&mut self, binder: NodeId, f: impl FnOnce(&mut Self) -> T) -> T {
+        if let Some(ctxt) = &mut self.captured_lifetimes {
+            ctxt.binders_to_ignore.insert(binder);
+        }
+        let ret = f(self);
+        if let Some(ctxt) = &mut self.captured_lifetimes {
+            ctxt.binders_to_ignore.remove(&binder);
+        }
+        ret
     }
 
     fn with_dyn_type_scope<T>(&mut self, in_scope: bool, f: impl FnOnce(&mut Self) -> T) -> T {
@@ -965,7 +896,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                     match tokens.into_trees().next() {
                         Some(TokenTree::Token(token)) => token,
                         Some(TokenTree::Delimited(_, delim, tokens)) => {
-                            if delim != token::NoDelim {
+                            if delim != Delimiter::Invisible {
                                 sess.diagnostic().delay_span_bug(
                                     span,
                                     "unexpected delimiter in key-value attribute's value",
@@ -1058,7 +989,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 hir::TypeBindingKind::Equality { term }
             }
             AssocConstraintKind::Bound { ref bounds } => {
-                let mut capturable_lifetimes;
                 let mut parent_def_id = self.current_hir_id_owner;
                 // Piggy-back on the `impl Trait` context to figure out the correct behavior.
                 let (desugar_to_impl_trait, itctx) = match itctx {
@@ -1079,7 +1009,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                     // so desugar to
                     //
                     //     fn foo(x: dyn Iterator<Item = impl Debug>)
-                    ImplTraitContext::Universal(_, parent) if self.is_in_dyn_type => {
+                    ImplTraitContext::Universal(_, _, parent) if self.is_in_dyn_type => {
                         parent_def_id = parent;
                         (true, itctx)
                     }
@@ -1091,13 +1021,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                     //
                     // FIXME: this is only needed until `impl Trait` is allowed in type aliases.
                     ImplTraitContext::Disallowed(_) if self.is_in_dyn_type => {
-                        capturable_lifetimes = FxHashSet::default();
-                        (
-                            true,
-                            ImplTraitContext::TypeAliasesOpaqueTy {
-                                capturable_lifetimes: &mut capturable_lifetimes,
-                            },
-                        )
+                        (true, ImplTraitContext::TypeAliasesOpaqueTy)
                     }
 
                     // We are in the parameter position, but not within a dyn type:
@@ -1258,26 +1182,28 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             TyKind::Slice(ref ty) => hir::TyKind::Slice(self.lower_ty(ty, itctx)),
             TyKind::Ptr(ref mt) => hir::TyKind::Ptr(self.lower_mt(mt, itctx)),
             TyKind::Rptr(ref region, ref mt) => {
-                let span = self.sess.source_map().next_point(t.span.shrink_to_lo());
-                let lifetime = match *region {
-                    Some(ref lt) => self.lower_lifetime(lt),
-                    None => self.elided_ref_lifetime(span),
-                };
+                let region = region.unwrap_or_else(|| {
+                    let Some(LifetimeRes::ElidedAnchor { start, end }) = self.resolver.get_lifetime_res(t.id) else {
+                        panic!()
+                    };
+                    debug_assert_eq!(start.plus(1), end);
+                    let span = self.sess.source_map().next_point(t.span.shrink_to_lo());
+                    Lifetime {
+                        ident: Ident::new(kw::UnderscoreLifetime, span),
+                        id: start,
+                    }
+                });
+                let lifetime = self.lower_lifetime(&region);
                 hir::TyKind::Rptr(lifetime, self.lower_mt(mt, itctx))
             }
-            TyKind::BareFn(ref f) => self.with_in_scope_lifetime_defs(&f.generic_params, |this| {
-                this.with_anonymous_lifetime_mode(AnonymousLifetimeMode::PassThrough, |this| {
-                    hir::TyKind::BareFn(this.arena.alloc(hir::BareFnTy {
-                        generic_params: this.lower_generic_params(
-                            &f.generic_params,
-                            ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
-                        ),
-                        unsafety: this.lower_unsafety(f.unsafety),
-                        abi: this.lower_extern(f.ext),
-                        decl: this.lower_fn_decl(&f.decl, None, FnDeclKind::Pointer, None),
-                        param_names: this.lower_fn_params_to_names(&f.decl),
-                    }))
-                })
+            TyKind::BareFn(ref f) => self.with_lifetime_binder(t.id, |this| {
+                hir::TyKind::BareFn(this.arena.alloc(hir::BareFnTy {
+                    generic_params: this.lower_generic_params(&f.generic_params),
+                    unsafety: this.lower_unsafety(f.unsafety),
+                    abi: this.lower_extern(f.ext),
+                    decl: this.lower_fn_decl(&f.decl, None, FnDeclKind::Pointer, None),
+                    param_names: this.lower_fn_params_to_names(&f.decl),
+                }))
             }),
             TyKind::Never => hir::TyKind::Never,
             TyKind::Tup(ref tys) => {
@@ -1342,38 +1268,34 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             TyKind::ImplTrait(def_node_id, ref bounds) => {
                 let span = t.span;
                 match itctx {
-                    ImplTraitContext::ReturnPositionOpaqueTy { fn_def_id, origin } => self
-                        .lower_opaque_impl_trait(
-                            span,
-                            Some(fn_def_id),
-                            origin,
-                            def_node_id,
-                            None,
-                            |this| this.lower_param_bounds(bounds, itctx),
-                        ),
-                    ImplTraitContext::TypeAliasesOpaqueTy { ref capturable_lifetimes } => {
-                        // Reset capturable lifetimes, any nested impl trait
-                        // types will inherit lifetimes from this opaque type,
-                        // so don't need to capture them again.
-                        let nested_itctx = ImplTraitContext::TypeAliasesOpaqueTy {
-                            capturable_lifetimes: &mut FxHashSet::default(),
-                        };
+                    ImplTraitContext::ReturnPositionOpaqueTy { origin } => self
+                        .lower_opaque_impl_trait(span, origin, def_node_id, |this| {
+                            this.lower_param_bounds(bounds, itctx)
+                        }),
+                    ImplTraitContext::TypeAliasesOpaqueTy => {
+                        let nested_itctx = ImplTraitContext::TypeAliasesOpaqueTy;
                         self.lower_opaque_impl_trait(
                             span,
-                            None,
                             hir::OpaqueTyOrigin::TyAlias,
                             def_node_id,
-                            Some(capturable_lifetimes),
                             |this| this.lower_param_bounds(bounds, nested_itctx),
                         )
                     }
-                    ImplTraitContext::Universal(in_band_ty_params, parent_def_id) => {
+                    ImplTraitContext::Universal(
+                        in_band_ty_params,
+                        in_band_ty_bounds,
+                        parent_def_id,
+                    ) => {
                         // Add a definition for the in-band `Param`.
                         let def_id = self.resolver.local_def_id(def_node_id);
 
                         let hir_bounds = self.lower_param_bounds(
                             bounds,
-                            ImplTraitContext::Universal(in_band_ty_params, parent_def_id),
+                            ImplTraitContext::Universal(
+                                in_band_ty_params,
+                                in_band_ty_bounds,
+                                parent_def_id,
+                            ),
                         );
                         // Set the name to `impl Bound1 + Bound2`.
                         let ident = Ident::from_str_and_span(&pprust::ty_to_string(t), span);
@@ -1381,10 +1303,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                             hir_id: self.lower_node_id(def_node_id),
                             name: ParamName::Plain(self.lower_ident(ident)),
                             pure_wrt_drop: false,
-                            bounds: hir_bounds,
                             span: self.lower_span(span),
                             kind: hir::GenericParamKind::Type { default: None, synthetic: true },
+                            colon_span: None,
                         });
+                        if let Some(preds) = self.lower_generic_bound_predicate(
+                            ident,
+                            def_node_id,
+                            &GenericParamKind::Type { default: None },
+                            hir_bounds,
+                        ) {
+                            in_band_ty_bounds.push(preds)
+                        }
 
                         hir::TyKind::Path(hir::QPath::Resolved(
                             None,
@@ -1421,20 +1351,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         hir::Ty { kind, span: self.lower_span(t.span), hir_id: self.lower_node_id(t.id) }
     }
 
+    #[tracing::instrument(level = "debug", skip(self, lower_bounds))]
     fn lower_opaque_impl_trait(
         &mut self,
         span: Span,
-        fn_def_id: Option<LocalDefId>,
         origin: hir::OpaqueTyOrigin,
         opaque_ty_node_id: NodeId,
-        capturable_lifetimes: Option<&FxHashSet<hir::LifetimeName>>,
         lower_bounds: impl FnOnce(&mut Self) -> hir::GenericBounds<'hir>,
     ) -> hir::TyKind<'hir> {
-        debug!(
-            "lower_opaque_impl_trait(fn_def_id={:?}, opaque_ty_node_id={:?}, span={:?})",
-            fn_def_id, opaque_ty_node_id, span,
-        );
-
         // Make sure we know that some funky desugaring has been going on here.
         // This is a first: there is code in other places like for loop
         // desugaring that explicitly states that we don't want to track that.
@@ -1444,57 +1368,51 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
 
         let opaque_ty_def_id = self.resolver.local_def_id(opaque_ty_node_id);
 
-        let mut collected_lifetimes = Vec::new();
+        let mut collected_lifetimes = FxHashMap::default();
         self.with_hir_id_owner(opaque_ty_node_id, |lctx| {
-            let hir_bounds = lower_bounds(lctx);
+            let hir_bounds = if origin == hir::OpaqueTyOrigin::TyAlias {
+                lower_bounds(lctx)
+            } else {
+                lctx.while_capturing_lifetimes(
+                    opaque_ty_def_id,
+                    &mut collected_lifetimes,
+                    lower_bounds,
+                )
+            };
+            debug!(?collected_lifetimes);
 
-            collected_lifetimes = lifetimes_from_impl_trait_bounds(
-                opaque_ty_node_id,
-                &hir_bounds,
-                capturable_lifetimes,
-            );
+            let lifetime_defs = lctx.arena.alloc_from_iter(collected_lifetimes.iter().map(
+                |(_, &(span, p_id, p_name, _))| {
+                    let hir_id = lctx.lower_node_id(p_id);
+                    debug_assert_ne!(lctx.resolver.opt_local_def_id(p_id), None);
 
-            let lifetime_defs =
-                lctx.arena.alloc_from_iter(collected_lifetimes.iter().map(|&(name, span)| {
-                    let def_node_id = lctx.resolver.next_node_id();
-                    lctx.resolver.create_def(
-                        opaque_ty_def_id,
-                        def_node_id,
-                        DefPathData::LifetimeNs(name.ident().name),
-                        ExpnId::root(),
-                        span.with_parent(None),
-                    );
-                    let hir_id = lctx.lower_node_id(def_node_id);
-
-                    let (name, kind) = match name {
-                        hir::LifetimeName::Underscore => (
-                            hir::ParamName::Plain(Ident::with_dummy_span(kw::UnderscoreLifetime)),
-                            hir::LifetimeParamKind::Elided,
-                        ),
-                        hir::LifetimeName::Param(param_name) => {
-                            (param_name, hir::LifetimeParamKind::Explicit)
-                        }
-                        _ => panic!("expected `LifetimeName::Param` or `ParamName::Plain`"),
+                    let kind = if p_name.ident().name == kw::UnderscoreLifetime {
+                        hir::LifetimeParamKind::Elided
+                    } else {
+                        hir::LifetimeParamKind::Explicit
                     };
 
                     hir::GenericParam {
                         hir_id,
-                        name,
+                        name: p_name,
                         span,
                         pure_wrt_drop: false,
-                        bounds: &[],
                         kind: hir::GenericParamKind::Lifetime { kind },
+                        colon_span: None,
                     }
-                }));
+                },
+            ));
 
             debug!("lower_opaque_impl_trait: lifetime_defs={:#?}", lifetime_defs);
 
             let opaque_ty_item = hir::OpaqueTy {
-                generics: hir::Generics {
+                generics: self.arena.alloc(hir::Generics {
                     params: lifetime_defs,
-                    where_clause: hir::WhereClause { predicates: &[], span: lctx.lower_span(span) },
+                    predicates: &[],
+                    has_where_clause: false,
+                    where_clause_span: lctx.lower_span(span),
                     span: lctx.lower_span(span),
-                },
+                }),
                 bounds: hir_bounds,
                 origin,
             };
@@ -1503,10 +1421,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             lctx.generate_opaque_type(opaque_ty_def_id, opaque_ty_item, span, opaque_ty_span)
         });
 
-        let lifetimes =
-            self.arena.alloc_from_iter(collected_lifetimes.into_iter().map(|(name, span)| {
-                hir::GenericArg::Lifetime(hir::Lifetime { hir_id: self.next_id(), span, name })
-            }));
+        let lifetimes = self.arena.alloc_from_iter(collected_lifetimes.into_iter().map(
+            |(_, (span, _, p_name, res))| {
+                let id = self.resolver.next_node_id();
+                let ident = Ident::new(p_name.ident().name, span);
+                let l = self.new_named_lifetime_with_res(id, span, ident, res);
+                hir::GenericArg::Lifetime(l)
+            },
+        ));
 
         debug!("lower_opaque_impl_trait: lifetimes={:#?}", lifetimes);
 
@@ -1530,7 +1452,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             def_id: opaque_ty_id,
             ident: Ident::empty(),
             kind: opaque_ty_item_kind,
-            vis: respan(self.lower_span(span.shrink_to_lo()), hir::VisibilityKind::Inherited),
+            vis_span: self.lower_span(span.shrink_to_lo()),
             span: self.lower_span(opaque_ty_span),
         };
         hir::OwnerNode::Item(self.arena.alloc(opaque_ty_item))
@@ -1565,7 +1487,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
     fn lower_fn_decl(
         &mut self,
         decl: &FnDecl,
-        mut in_band_ty_params: Option<(LocalDefId, &mut Vec<hir::GenericParam<'hir>>)>,
+        mut in_band_ty_params: Option<(
+            NodeId,
+            &mut Vec<hir::GenericParam<'hir>>,
+            &mut Vec<hir::WherePredicate<'hir>>,
+        )>,
         kind: FnDeclKind,
         make_ret_async: Option<NodeId>,
     ) -> &'hir hir::FnDecl<'hir> {
@@ -1577,50 +1503,38 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             make_ret_async: {:?})",
             decl, in_band_ty_params, kind, make_ret_async,
         );
-        let lt_mode = if make_ret_async.is_some() {
-            // In `async fn`, argument-position elided lifetimes
-            // must be transformed into fresh generic parameters so that
-            // they can be applied to the opaque `impl Trait` return type.
-            AnonymousLifetimeMode::CreateParameter
-        } else {
-            self.anonymous_lifetime_mode
-        };
 
         let c_variadic = decl.c_variadic();
 
-        // Remember how many lifetimes were already around so that we can
-        // only look at the lifetime parameters introduced by the arguments.
-        let inputs = self.with_anonymous_lifetime_mode(lt_mode, |this| {
-            // Skip the `...` (`CVarArgs`) trailing arguments from the AST,
-            // as they are not explicit in HIR/Ty function signatures.
-            // (instead, the `c_variadic` flag is set to `true`)
-            let mut inputs = &decl.inputs[..];
-            if c_variadic {
-                inputs = &inputs[..inputs.len() - 1];
+        // Skip the `...` (`CVarArgs`) trailing arguments from the AST,
+        // as they are not explicit in HIR/Ty function signatures.
+        // (instead, the `c_variadic` flag is set to `true`)
+        let mut inputs = &decl.inputs[..];
+        if c_variadic {
+            inputs = &inputs[..inputs.len() - 1];
+        }
+        let inputs = self.arena.alloc_from_iter(inputs.iter().map(|param| {
+            if let Some((_, ibty, ibpb)) = &mut in_band_ty_params {
+                self.lower_ty_direct(
+                    &param.ty,
+                    ImplTraitContext::Universal(ibty, ibpb, self.current_hir_id_owner),
+                )
+            } else {
+                self.lower_ty_direct(
+                    &param.ty,
+                    ImplTraitContext::Disallowed(match kind {
+                        FnDeclKind::Fn | FnDeclKind::Inherent => {
+                            unreachable!("fn should allow in-band lifetimes")
+                        }
+                        FnDeclKind::ExternFn => ImplTraitPosition::ExternFnParam,
+                        FnDeclKind::Closure => ImplTraitPosition::ClosureParam,
+                        FnDeclKind::Pointer => ImplTraitPosition::PointerParam,
+                        FnDeclKind::Trait => ImplTraitPosition::TraitParam,
+                        FnDeclKind::Impl => ImplTraitPosition::ImplParam,
+                    }),
+                )
             }
-            this.arena.alloc_from_iter(inputs.iter().map(|param| {
-                if let Some((_, ibty)) = &mut in_band_ty_params {
-                    this.lower_ty_direct(
-                        &param.ty,
-                        ImplTraitContext::Universal(ibty, this.current_hir_id_owner),
-                    )
-                } else {
-                    this.lower_ty_direct(
-                        &param.ty,
-                        ImplTraitContext::Disallowed(match kind {
-                            FnDeclKind::Fn | FnDeclKind::Inherent => {
-                                unreachable!("fn should allow in-band lifetimes")
-                            }
-                            FnDeclKind::ExternFn => ImplTraitPosition::ExternFnParam,
-                            FnDeclKind::Closure => ImplTraitPosition::ClosureParam,
-                            FnDeclKind::Pointer => ImplTraitPosition::PointerParam,
-                            FnDeclKind::Trait => ImplTraitPosition::TraitParam,
-                            FnDeclKind::Impl => ImplTraitPosition::ImplParam,
-                        }),
-                    )
-                }
-            }))
-        });
+        }));
 
         let output = if let Some(ret_id) = make_ret_async {
             self.lower_async_fn_ret_ty(
@@ -1632,10 +1546,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             match decl.output {
                 FnRetTy::Ty(ref ty) => {
                     let context = match in_band_ty_params {
-                        Some((def_id, _)) if kind.impl_trait_return_allowed() => {
+                        Some((node_id, _, _)) if kind.impl_trait_return_allowed() => {
+                            let fn_def_id = self.resolver.local_def_id(node_id);
                             ImplTraitContext::ReturnPositionOpaqueTy {
-                                fn_def_id: def_id,
-                                origin: hir::OpaqueTyOrigin::FnReturn(def_id),
+                                origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id),
                             }
                         }
                         _ => ImplTraitContext::Disallowed(match kind {
@@ -1696,25 +1610,19 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
     // `fn_def_id`: `DefId` of the parent function (used to create child impl trait definition)
     // `opaque_ty_node_id`: `NodeId` of the opaque `impl Trait` type that should be created
     // `elided_lt_replacement`: replacement for elided lifetimes in the return type
+    #[tracing::instrument(level = "debug", skip(self))]
     fn lower_async_fn_ret_ty(
         &mut self,
         output: &FnRetTy,
-        fn_def_id: LocalDefId,
+        fn_node_id: NodeId,
         opaque_ty_node_id: NodeId,
     ) -> hir::FnRetTy<'hir> {
-        debug!(
-            "lower_async_fn_ret_ty(\
-             output={:?}, \
-             fn_def_id={:?}, \
-             opaque_ty_node_id={:?})",
-            output, fn_def_id, opaque_ty_node_id,
-        );
-
         let span = output.span();
 
         let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::Async, span, None);
 
         let opaque_ty_def_id = self.resolver.local_def_id(opaque_ty_node_id);
+        let fn_def_id = self.resolver.local_def_id(fn_node_id);
 
         // When we create the opaque type for this async fn, it is going to have
         // to capture all the lifetimes involved in the signature (including in the
@@ -1754,100 +1662,95 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         // should be figured out using the ordinary elision rules, and
         // this desugaring achieves that.
 
-        debug!("lower_async_fn_ret_ty: in_scope_lifetimes={:#?}", self.in_scope_lifetimes);
-        debug!("lower_async_fn_ret_ty: lifetimes_to_define={:#?}", self.lifetimes_to_define);
-
         // Calculate all the lifetimes that should be captured
         // by the opaque type. This should include all in-scope
         // lifetime parameters, including those defined in-band.
-        //
-        // `lifetime_params` is a vector of tuple (span, parameter name, lifetime name).
-
-        // Input lifetime like `'a` or `'1`:
-        let mut lifetime_params: Vec<_> = self
-            .in_scope_lifetimes
-            .iter()
-            .cloned()
-            .map(|name| (name.ident().span, hir::LifetimeName::Param(name)))
-            .chain(self.lifetimes_to_define.iter().map(|&(span, node_id)| {
-                let def_id = self.resolver.local_def_id(node_id);
-                let name = hir::ParamName::Fresh(def_id);
-                (span, hir::LifetimeName::Param(name))
-            }))
-            .collect();
+
+        let mut captures = FxHashMap::default();
+
+        let extra_lifetime_params = self.resolver.take_extra_lifetime_params(opaque_ty_node_id);
+        debug!(?extra_lifetime_params);
+        for (ident, outer_node_id, outer_res) in extra_lifetime_params {
+            let Ident { name, span } = ident;
+            let outer_def_id = self.resolver.local_def_id(outer_node_id);
+            let inner_node_id = self.resolver.next_node_id();
+
+            // Add a definition for the in scope lifetime def.
+            self.resolver.create_def(
+                opaque_ty_def_id,
+                inner_node_id,
+                DefPathData::LifetimeNs(name),
+                ExpnId::root(),
+                span.with_parent(None),
+            );
+
+            let (p_name, inner_res) = match outer_res {
+                // Input lifetime like `'a`:
+                LifetimeRes::Param { param, .. } => {
+                    (hir::ParamName::Plain(ident), LifetimeRes::Param { param, binder: fn_node_id })
+                }
+                // Input lifetime like `'1`:
+                LifetimeRes::Fresh { param, .. } => (
+                    hir::ParamName::Fresh(outer_def_id),
+                    LifetimeRes::Fresh { param, binder: fn_node_id },
+                ),
+                LifetimeRes::Static | LifetimeRes::Error => continue,
+                res => {
+                    panic!("Unexpected lifetime resolution {:?} for {:?} at {:?}", res, ident, span)
+                }
+            };
+
+            captures.insert(outer_def_id, (span, inner_node_id, p_name, inner_res));
+        }
+
+        debug!(?captures);
 
         self.with_hir_id_owner(opaque_ty_node_id, |this| {
-            let mut generic_params: Vec<_> = lifetime_params
-                .iter()
-                .map(|&(span, name)| {
-                    // We can only get lifetime names from the outside.
-                    let hir::LifetimeName::Param(hir_name) = name else { panic!() };
-
-                    let node_id = this.resolver.next_node_id();
-
-                    // Add a definition for the in-band lifetime def.
-                    let def_id = this.resolver.create_def(
-                        opaque_ty_def_id,
-                        node_id,
-                        DefPathData::LifetimeNs(hir_name.ident().name),
-                        ExpnId::root(),
-                        span.with_parent(None),
-                    );
+            let future_bound =
+                this.while_capturing_lifetimes(opaque_ty_def_id, &mut captures, |this| {
+                    // We have to be careful to get elision right here. The
+                    // idea is that we create a lifetime parameter for each
+                    // lifetime in the return type.  So, given a return type
+                    // like `async fn foo(..) -> &[&u32]`, we lower to `impl
+                    // Future<Output = &'1 [ &'2 u32 ]>`.
+                    //
+                    // Then, we will create `fn foo(..) -> Foo<'_, '_>`, and
+                    // hence the elision takes place at the fn site.
+                    this.lower_async_fn_output_type_to_future_bound(output, fn_def_id, span)
+                });
+            debug!("lower_async_fn_ret_ty: future_bound={:#?}", future_bound);
+            debug!("lower_async_fn_ret_ty: captures={:#?}", captures);
 
-                    let (kind, name) = match hir_name {
-                        ParamName::Plain(ident) => {
-                            (hir::LifetimeParamKind::Explicit, hir::ParamName::Plain(ident))
-                        }
-                        ParamName::Fresh(_) => {
-                            (hir::LifetimeParamKind::Elided, hir::ParamName::Fresh(def_id))
-                        }
-                        ParamName::Error => (hir::LifetimeParamKind::Error, hir::ParamName::Error),
+            let generic_params =
+                this.arena.alloc_from_iter(captures.iter().map(|(_, &(span, p_id, p_name, _))| {
+                    let hir_id = this.lower_node_id(p_id);
+                    debug_assert_ne!(this.resolver.opt_local_def_id(p_id), None);
+
+                    let kind = if p_name.ident().name == kw::UnderscoreLifetime {
+                        hir::LifetimeParamKind::Elided
+                    } else {
+                        hir::LifetimeParamKind::Explicit
                     };
 
                     hir::GenericParam {
-                        hir_id: this.lower_node_id(node_id),
-                        name,
-                        bounds: &[],
-                        span: this.lower_span(span),
+                        hir_id,
+                        name: p_name,
+                        span,
                         pure_wrt_drop: false,
                         kind: hir::GenericParamKind::Lifetime { kind },
+                        colon_span: None,
                     }
-                })
-                .collect();
-
-            // We have to be careful to get elision right here. The
-            // idea is that we create a lifetime parameter for each
-            // lifetime in the return type.  So, given a return type
-            // like `async fn foo(..) -> &[&u32]`, we lower to `impl
-            // Future<Output = &'1 [ &'2 u32 ]>`.
-            //
-            // Then, we will create `fn foo(..) -> Foo<'_, '_>`, and
-            // hence the elision takes place at the fn site.
-            let (lifetimes_to_define, future_bound) =
-                this.with_anonymous_lifetime_mode(AnonymousLifetimeMode::CreateParameter, |this| {
-                    this.collect_in_band_defs(opaque_ty_def_id, |this| {
-                        this.lower_async_fn_output_type_to_future_bound(output, fn_def_id, span)
-                    })
-                });
-            debug!("lower_async_fn_ret_ty: future_bound={:#?}", future_bound);
-            debug!("lower_async_fn_ret_ty: lifetimes_to_define={:#?}", lifetimes_to_define);
-
-            // Output lifetime like `'_`:
-            for (span, node_id) in lifetimes_to_define {
-                let param = this.fresh_lifetime_to_generic_param(span, node_id);
-                lifetime_params.push((span, hir::LifetimeName::Implicit));
-                generic_params.push(param);
-            }
-            let generic_params = this.arena.alloc_from_iter(generic_params);
-            debug!("lower_async_fn_ret_ty: lifetime_params={:#?}", lifetime_params);
+                }));
             debug!("lower_async_fn_ret_ty: generic_params={:#?}", generic_params);
 
             let opaque_ty_item = hir::OpaqueTy {
-                generics: hir::Generics {
+                generics: this.arena.alloc(hir::Generics {
                     params: generic_params,
-                    where_clause: hir::WhereClause { predicates: &[], span: this.lower_span(span) },
+                    predicates: &[],
+                    has_where_clause: false,
+                    where_clause_span: this.lower_span(span),
                     span: this.lower_span(span),
-                },
+                }),
                 bounds: arena_vec![this; future_bound],
                 origin: hir::OpaqueTyOrigin::AsyncFn(fn_def_id),
             };
@@ -1856,8 +1759,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             this.generate_opaque_type(opaque_ty_def_id, opaque_ty_item, span, opaque_ty_span)
         });
 
-        // As documented above on the variable
-        // `input_lifetimes_count`, we need to create the lifetime
+        // As documented above, we need to create the lifetime
         // arguments to our opaque type. Continuing with our example,
         // we're creating the type arguments for the return type:
         //
@@ -1873,12 +1775,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         // For the "output" lifetime parameters, we just want to
         // generate `'_`.
         let generic_args =
-            self.arena.alloc_from_iter(lifetime_params.into_iter().map(|(span, name)| {
-                GenericArg::Lifetime(hir::Lifetime {
-                    hir_id: self.next_id(),
-                    span: self.lower_span(span),
-                    name,
-                })
+            self.arena.alloc_from_iter(captures.into_iter().map(|(_, (span, _, p_name, res))| {
+                let id = self.resolver.next_node_id();
+                let ident = Ident::new(p_name.ident().name, span);
+                let l = self.new_named_lifetime_with_res(id, span, ident, res);
+                hir::GenericArg::Lifetime(l)
             }));
 
         // Create the `Foo<...>` reference itself. Note that the `type
@@ -1905,7 +1806,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 // `impl Future` opaque type that `async fn` implicitly
                 // generates.
                 let context = ImplTraitContext::ReturnPositionOpaqueTy {
-                    fn_def_id,
                     origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id),
                 };
                 self.lower_ty(ty, context)
@@ -1948,92 +1848,134 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
 
     fn lower_lifetime(&mut self, l: &Lifetime) -> hir::Lifetime {
         let span = self.lower_span(l.ident.span);
-        match l.ident {
-            ident if ident.name == kw::StaticLifetime => {
-                self.new_named_lifetime(l.id, span, hir::LifetimeName::Static)
-            }
-            ident if ident.name == kw::UnderscoreLifetime => match self.anonymous_lifetime_mode {
-                AnonymousLifetimeMode::CreateParameter => {
-                    let fresh_name = self.collect_fresh_anonymous_lifetime(span);
-                    self.new_named_lifetime(l.id, span, hir::LifetimeName::Param(fresh_name))
-                }
-
-                AnonymousLifetimeMode::PassThrough => {
-                    self.new_named_lifetime(l.id, span, hir::LifetimeName::Underscore)
-                }
-
-                AnonymousLifetimeMode::ReportError => self.new_error_lifetime(Some(l.id), span),
-            },
-            ident => {
-                let param_name = ParamName::Plain(self.lower_ident(ident));
-                self.new_named_lifetime(l.id, span, hir::LifetimeName::Param(param_name))
-            }
-        }
+        let ident = self.lower_ident(l.ident);
+        let res = self
+            .resolver
+            .get_lifetime_res(l.id)
+            .unwrap_or_else(|| panic!("Missing resolution for lifetime {:?} at {:?}", l, span));
+        self.new_named_lifetime_with_res(l.id, span, ident, res)
     }
 
-    fn new_named_lifetime(
+    #[tracing::instrument(level = "debug", skip(self))]
+    fn new_named_lifetime_with_res(
         &mut self,
         id: NodeId,
         span: Span,
-        name: hir::LifetimeName,
+        ident: Ident,
+        res: LifetimeRes,
     ) -> hir::Lifetime {
+        debug!(?self.captured_lifetimes);
+        let name = match res {
+            LifetimeRes::Param { param, binder } => {
+                debug_assert_ne!(ident.name, kw::UnderscoreLifetime);
+                let p_name = ParamName::Plain(ident);
+                if let Some(LifetimeCaptureContext { parent_def_id, captures, binders_to_ignore }) =
+                    &mut self.captured_lifetimes
+                    && !binders_to_ignore.contains(&binder)
+                {
+                    match captures.entry(param) {
+                        Entry::Occupied(_) => {}
+                        Entry::Vacant(v) => {
+                            let p_id = self.resolver.next_node_id();
+                            self.resolver.create_def(
+                                *parent_def_id,
+                                p_id,
+                                DefPathData::LifetimeNs(p_name.ident().name),
+                                ExpnId::root(),
+                                span.with_parent(None),
+                            );
+
+                            v.insert((span, p_id, p_name, res));
+                        }
+                    }
+                }
+                hir::LifetimeName::Param(p_name)
+            }
+            LifetimeRes::Fresh { mut param, binder } => {
+                debug_assert_eq!(ident.name, kw::UnderscoreLifetime);
+                if let Some(LifetimeCaptureContext { parent_def_id, captures, binders_to_ignore }) =
+                    &mut self.captured_lifetimes
+                    && !binders_to_ignore.contains(&binder)
+                {
+                    match captures.entry(param) {
+                        Entry::Occupied(o) => param = self.resolver.local_def_id(o.get().1),
+                        Entry::Vacant(v) => {
+                            let p_id = self.resolver.next_node_id();
+                            let p_def_id = self.resolver.create_def(
+                                *parent_def_id,
+                                p_id,
+                                DefPathData::LifetimeNs(kw::UnderscoreLifetime),
+                                ExpnId::root(),
+                                span.with_parent(None),
+                            );
+
+                            let p_name = ParamName::Fresh(param);
+                            v.insert((span, p_id, p_name, res));
+                            param = p_def_id;
+                        }
+                    }
+                }
+                let p_name = ParamName::Fresh(param);
+                hir::LifetimeName::Param(p_name)
+            }
+            LifetimeRes::Anonymous { binder, elided } => {
+                let l_name = if elided {
+                    hir::LifetimeName::Implicit
+                } else {
+                    hir::LifetimeName::Underscore
+                };
+                if let Some(LifetimeCaptureContext { parent_def_id, captures, binders_to_ignore }) =
+                    &mut self.captured_lifetimes
+                    && !binders_to_ignore.contains(&binder)
+                {
+                    let p_id = self.resolver.next_node_id();
+                    let p_def_id = self.resolver.create_def(
+                        *parent_def_id,
+                        p_id,
+                        DefPathData::LifetimeNs(kw::UnderscoreLifetime),
+                        ExpnId::root(),
+                        span.with_parent(None),
+                    );
+                    let p_name = ParamName::Fresh(p_def_id);
+                    captures.insert(p_def_id, (span, p_id, p_name, res));
+                    hir::LifetimeName::Param(p_name)
+                } else {
+                    l_name
+                }
+            }
+            LifetimeRes::Static => hir::LifetimeName::Static,
+            LifetimeRes::Error => hir::LifetimeName::Error,
+            res => panic!("Unexpected lifetime resolution {:?} for {:?} at {:?}", res, ident, span),
+        };
+        debug!(?self.captured_lifetimes);
         hir::Lifetime { hir_id: self.lower_node_id(id), span: self.lower_span(span), name }
     }
 
     fn lower_generic_params_mut<'s>(
         &'s mut self,
         params: &'s [GenericParam],
-        mut itctx: ImplTraitContext<'s, 'hir>,
     ) -> impl Iterator<Item = hir::GenericParam<'hir>> + Captures<'a> + Captures<'s> {
-        params.iter().map(move |param| self.lower_generic_param(param, itctx.reborrow()))
+        params.iter().map(move |param| self.lower_generic_param(param))
     }
 
-    fn lower_generic_params(
-        &mut self,
-        params: &[GenericParam],
-        itctx: ImplTraitContext<'_, 'hir>,
-    ) -> &'hir [hir::GenericParam<'hir>] {
-        self.arena.alloc_from_iter(self.lower_generic_params_mut(params, itctx))
+    fn lower_generic_params(&mut self, params: &[GenericParam]) -> &'hir [hir::GenericParam<'hir>] {
+        self.arena.alloc_from_iter(self.lower_generic_params_mut(params))
     }
 
-    fn lower_generic_param(
-        &mut self,
-        param: &GenericParam,
-        mut itctx: ImplTraitContext<'_, 'hir>,
-    ) -> hir::GenericParam<'hir> {
-        let bounds: Vec<_> = self
-            .with_anonymous_lifetime_mode(AnonymousLifetimeMode::ReportError, |this| {
-                this.lower_param_bounds_mut(&param.bounds, itctx.reborrow()).collect()
-            });
-
+    fn lower_generic_param(&mut self, param: &GenericParam) -> hir::GenericParam<'hir> {
         let (name, kind) = match param.kind {
             GenericParamKind::Lifetime => {
-                let was_collecting_in_band = self.is_collecting_anonymous_lifetimes;
-                self.is_collecting_anonymous_lifetimes = None;
-
-                let lt = self
-                    .with_anonymous_lifetime_mode(AnonymousLifetimeMode::ReportError, |this| {
-                        this.lower_lifetime(&Lifetime { id: param.id, ident: param.ident })
-                    });
-                let param_name = match lt.name {
-                    hir::LifetimeName::Param(param_name) => param_name,
-                    hir::LifetimeName::Implicit | hir::LifetimeName::Underscore => {
-                        hir::ParamName::Plain(lt.name.ident())
-                    }
-                    hir::LifetimeName::ImplicitObjectLifetimeDefault => {
-                        self.sess.diagnostic().span_bug(
-                            param.ident.span,
-                            "object-lifetime-default should not occur here",
-                        );
-                    }
-                    hir::LifetimeName::Static | hir::LifetimeName::Error => ParamName::Error,
+                let param_name = if param.ident.name == kw::StaticLifetime
+                    || param.ident.name == kw::UnderscoreLifetime
+                {
+                    ParamName::Error
+                } else {
+                    let ident = self.lower_ident(param.ident);
+                    ParamName::Plain(ident)
                 };
-
                 let kind =
                     hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit };
 
-                self.is_collecting_anonymous_lifetimes = was_collecting_in_band;
-
                 (param_name, kind)
             }
             GenericParamKind::Type { ref default, .. } => {
@@ -2047,10 +1989,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 (hir::ParamName::Plain(self.lower_ident(param.ident)), kind)
             }
             GenericParamKind::Const { ref ty, kw_span: _, ref default } => {
-                let ty =
-                    self.with_anonymous_lifetime_mode(AnonymousLifetimeMode::ReportError, |this| {
-                        this.lower_ty(&ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type))
-                    });
+                let ty = self.lower_ty(&ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type));
                 let default = default.as_ref().map(|def| self.lower_anon_const(def));
                 (
                     hir::ParamName::Plain(self.lower_ident(param.ident)),
@@ -2070,8 +2009,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             name,
             span: self.lower_span(param.span()),
             pure_wrt_drop: self.sess.contains_name(&param.attrs, sym::may_dangle),
-            bounds: self.arena.alloc_from_iter(bounds),
             kind,
+            colon_span: param.colon_span.map(|s| self.lower_span(s)),
         }
     }
 
@@ -2087,35 +2026,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         hir::TraitRef { path, hir_ref_id: self.lower_node_id(p.ref_id) }
     }
 
+    #[tracing::instrument(level = "debug", skip(self))]
     fn lower_poly_trait_ref(
         &mut self,
         p: &PolyTraitRef,
         mut itctx: ImplTraitContext<'_, 'hir>,
     ) -> hir::PolyTraitRef<'hir> {
-        let bound_generic_params =
-            self.lower_generic_params(&p.bound_generic_params, itctx.reborrow());
-
-        let trait_ref = self.with_in_scope_lifetime_defs(&p.bound_generic_params, |this| {
-            // Any impl Trait types defined within this scope can capture
-            // lifetimes bound on this predicate.
-            let lt_def_names = p.bound_generic_params.iter().filter_map(|param| match param.kind {
-                GenericParamKind::Lifetime { .. } => Some(hir::LifetimeName::Param(
-                    ParamName::Plain(param.ident.normalize_to_macros_2_0()),
-                )),
-                _ => None,
-            });
-            if let ImplTraitContext::TypeAliasesOpaqueTy { ref mut capturable_lifetimes } = itctx {
-                capturable_lifetimes.extend(lt_def_names.clone());
-            }
-
-            let res = this.lower_trait_ref(&p.trait_ref, itctx.reborrow());
+        let bound_generic_params = self.lower_generic_params(&p.bound_generic_params);
 
-            if let ImplTraitContext::TypeAliasesOpaqueTy { ref mut capturable_lifetimes } = itctx {
-                for param in lt_def_names {
-                    capturable_lifetimes.remove(&param);
-                }
-            }
-            res
+        let trait_ref = self.with_lifetime_binder(p.trait_ref.ref_id, |this| {
+            this.lower_trait_ref(&p.trait_ref, itctx.reborrow())
         });
 
         hir::PolyTraitRef { bound_generic_params, trait_ref, span: self.lower_span(p.span) }
@@ -2378,98 +2298,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         hir::Ty { hir_id, kind, span: self.lower_span(span) }
     }
 
-    /// Invoked to create the lifetime argument for a type `&T`
-    /// with no explicit lifetime.
-    fn elided_ref_lifetime(&mut self, span: Span) -> hir::Lifetime {
-        match self.anonymous_lifetime_mode {
-            // Intercept when we are in an impl header or async fn and introduce an in-band
-            // lifetime.
-            // Hence `impl Foo for &u32` becomes `impl<'f> Foo for &'f u32` for some fresh
-            // `'f`.
-            AnonymousLifetimeMode::CreateParameter => {
-                let fresh_name = self.collect_fresh_anonymous_lifetime(span);
-                hir::Lifetime {
-                    hir_id: self.next_id(),
-                    span: self.lower_span(span),
-                    name: hir::LifetimeName::Param(fresh_name),
-                }
-            }
-
-            AnonymousLifetimeMode::ReportError => self.new_error_lifetime(None, span),
-
-            AnonymousLifetimeMode::PassThrough => self.new_implicit_lifetime(span),
-        }
-    }
-
-    /// Report an error on illegal use of `'_` or a `&T` with no explicit lifetime;
-    /// return an "error lifetime".
-    fn new_error_lifetime(&mut self, id: Option<NodeId>, span: Span) -> hir::Lifetime {
-        let id = id.unwrap_or_else(|| self.resolver.next_node_id());
-        self.new_named_lifetime(id, span, hir::LifetimeName::Error)
-    }
-
-    /// Invoked to create the lifetime argument(s) for a path like
-    /// `std::cell::Ref<T>`; note that implicit lifetimes in these
-    /// sorts of cases are deprecated. This may therefore report a warning or an
-    /// error, depending on the mode.
-    fn elided_path_lifetimes<'s>(
-        &'s mut self,
-        span: Span,
-        count: usize,
-    ) -> impl Iterator<Item = hir::Lifetime> + Captures<'a> + Captures<'s> + Captures<'hir> {
-        (0..count).map(move |_| self.elided_path_lifetime(span))
-    }
-
-    fn elided_path_lifetime(&mut self, span: Span) -> hir::Lifetime {
-        match self.anonymous_lifetime_mode {
-            AnonymousLifetimeMode::CreateParameter => {
-                // We should have emitted E0726 when processing this path above
-                self.sess
-                    .delay_span_bug(span, "expected 'implicit elided lifetime not allowed' error");
-                let id = self.resolver.next_node_id();
-                self.new_named_lifetime(id, span, hir::LifetimeName::Error)
-            }
-            // `PassThrough` is the normal case.
-            // `new_error_lifetime`, which would usually be used in the case of `ReportError`,
-            // is unsuitable here, as these can occur from missing lifetime parameters in a
-            // `PathSegment`, for which there is no associated `'_` or `&T` with no explicit
-            // lifetime. Instead, we simply create an implicit lifetime, which will be checked
-            // later, at which point a suitable error will be emitted.
-            AnonymousLifetimeMode::PassThrough | AnonymousLifetimeMode::ReportError => {
-                self.new_implicit_lifetime(span)
-            }
-        }
-    }
-
     /// Invoked to create the lifetime argument(s) for an elided trait object
     /// bound, like the bound in `Box<dyn Debug>`. This method is not invoked
     /// when the bound is written, even if it is written with `'_` like in
     /// `Box<dyn Debug + '_>`. In those cases, `lower_lifetime` is invoked.
     fn elided_dyn_bound(&mut self, span: Span) -> hir::Lifetime {
-        match self.anonymous_lifetime_mode {
-            // NB. We intentionally ignore the create-parameter mode here.
-            // and instead "pass through" to resolve-lifetimes, which will apply
-            // the object-lifetime-defaulting rules. Elided object lifetime defaults
-            // do not act like other elided lifetimes. In other words, given this:
-            //
-            //     impl Foo for Box<dyn Debug>
-            //
-            // we do not introduce a fresh `'_` to serve as the bound, but instead
-            // ultimately translate to the equivalent of:
-            //
-            //     impl Foo for Box<dyn Debug + 'static>
-            //
-            // `resolve_lifetime` has the code to make that happen.
-            AnonymousLifetimeMode::CreateParameter => {}
-
-            AnonymousLifetimeMode::ReportError => {
-                // ReportError applies to explicit use of `'_`.
-            }
-
-            // This is the normal case.
-            AnonymousLifetimeMode::PassThrough => {}
-        }
-
         let r = hir::Lifetime {
             hir_id: self.next_id(),
             span: self.lower_span(span),
@@ -2478,14 +2311,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         debug!("elided_dyn_bound: r={:?}", r);
         r
     }
-
-    fn new_implicit_lifetime(&mut self, span: Span) -> hir::Lifetime {
-        hir::Lifetime {
-            hir_id: self.next_id(),
-            span: self.lower_span(span),
-            name: hir::LifetimeName::Implicit,
-        }
-    }
 }
 
 /// Helper struct for delayed construction of GenericArgs.
@@ -2511,121 +2336,3 @@ impl<'hir> GenericArgsCtor<'hir> {
         this.arena.alloc(ga)
     }
 }
-
-#[tracing::instrument(level = "debug")]
-fn lifetimes_from_impl_trait_bounds(
-    opaque_ty_id: NodeId,
-    bounds: hir::GenericBounds<'_>,
-    lifetimes_to_include: Option<&FxHashSet<hir::LifetimeName>>,
-) -> Vec<(hir::LifetimeName, Span)> {
-    // This visitor walks over `impl Trait` bounds and creates defs for all lifetimes that
-    // appear in the bounds, excluding lifetimes that are created within the bounds.
-    // E.g., `'a`, `'b`, but not `'c` in `impl for<'c> SomeTrait<'a, 'b, 'c>`.
-    struct ImplTraitLifetimeCollector<'r> {
-        collect_elided_lifetimes: bool,
-        currently_bound_lifetimes: Vec<hir::LifetimeName>,
-        already_defined_lifetimes: FxHashSet<hir::LifetimeName>,
-        lifetimes: Vec<(hir::LifetimeName, Span)>,
-        lifetimes_to_include: Option<&'r FxHashSet<hir::LifetimeName>>,
-    }
-
-    impl<'r, 'v> intravisit::Visitor<'v> for ImplTraitLifetimeCollector<'r> {
-        fn visit_generic_args(&mut self, span: Span, parameters: &'v hir::GenericArgs<'v>) {
-            // Don't collect elided lifetimes used inside of `Fn()` syntax.
-            if parameters.parenthesized {
-                let old_collect_elided_lifetimes = self.collect_elided_lifetimes;
-                self.collect_elided_lifetimes = false;
-                intravisit::walk_generic_args(self, span, parameters);
-                self.collect_elided_lifetimes = old_collect_elided_lifetimes;
-            } else {
-                intravisit::walk_generic_args(self, span, parameters);
-            }
-        }
-
-        fn visit_ty(&mut self, t: &'v hir::Ty<'v>) {
-            // Don't collect elided lifetimes used inside of `fn()` syntax.
-            if let hir::TyKind::BareFn(_) = t.kind {
-                let old_collect_elided_lifetimes = self.collect_elided_lifetimes;
-                self.collect_elided_lifetimes = false;
-
-                // Record the "stack height" of `for<'a>` lifetime bindings
-                // to be able to later fully undo their introduction.
-                let old_len = self.currently_bound_lifetimes.len();
-                intravisit::walk_ty(self, t);
-                self.currently_bound_lifetimes.truncate(old_len);
-
-                self.collect_elided_lifetimes = old_collect_elided_lifetimes;
-            } else {
-                intravisit::walk_ty(self, t)
-            }
-        }
-
-        fn visit_poly_trait_ref(
-            &mut self,
-            trait_ref: &'v hir::PolyTraitRef<'v>,
-            modifier: hir::TraitBoundModifier,
-        ) {
-            // Record the "stack height" of `for<'a>` lifetime bindings
-            // to be able to later fully undo their introduction.
-            let old_len = self.currently_bound_lifetimes.len();
-            intravisit::walk_poly_trait_ref(self, trait_ref, modifier);
-            self.currently_bound_lifetimes.truncate(old_len);
-        }
-
-        fn visit_generic_param(&mut self, param: &'v hir::GenericParam<'v>) {
-            // Record the introduction of 'a in `for<'a> ...`.
-            if let hir::GenericParamKind::Lifetime { .. } = param.kind {
-                // Introduce lifetimes one at a time so that we can handle
-                // cases like `fn foo<'d>() -> impl for<'a, 'b: 'a, 'c: 'b + 'd>`.
-                let lt_name = hir::LifetimeName::Param(param.name);
-                self.currently_bound_lifetimes.push(lt_name);
-            }
-
-            intravisit::walk_generic_param(self, param);
-        }
-
-        fn visit_lifetime(&mut self, lifetime: &'v hir::Lifetime) {
-            let name = match lifetime.name {
-                hir::LifetimeName::Implicit | hir::LifetimeName::Underscore => {
-                    if self.collect_elided_lifetimes {
-                        // Use `'_` for both implicit and underscore lifetimes in
-                        // `type Foo<'_> = impl SomeTrait<'_>;`.
-                        hir::LifetimeName::Underscore
-                    } else {
-                        return;
-                    }
-                }
-                hir::LifetimeName::Param(_) => lifetime.name,
-
-                // Refers to some other lifetime that is "in
-                // scope" within the type.
-                hir::LifetimeName::ImplicitObjectLifetimeDefault => return,
-
-                hir::LifetimeName::Error | hir::LifetimeName::Static => return,
-            };
-
-            if !self.currently_bound_lifetimes.contains(&name)
-                && !self.already_defined_lifetimes.contains(&name)
-                && self.lifetimes_to_include.map_or(true, |lifetimes| lifetimes.contains(&name))
-            {
-                self.already_defined_lifetimes.insert(name);
-
-                self.lifetimes.push((name, lifetime.span));
-            }
-        }
-    }
-
-    let mut lifetime_collector = ImplTraitLifetimeCollector {
-        collect_elided_lifetimes: true,
-        currently_bound_lifetimes: Vec::new(),
-        already_defined_lifetimes: FxHashSet::default(),
-        lifetimes: Vec::new(),
-        lifetimes_to_include,
-    };
-
-    for bound in bounds {
-        intravisit::walk_param_bound(&mut lifetime_collector, &bound);
-    }
-
-    lifetime_collector.lifetimes
-}
diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs
index 8bf4b8fcc39..3c9399c1fdf 100644
--- a/compiler/rustc_ast_lowering/src/path.rs
+++ b/compiler/rustc_ast_lowering/src/path.rs
@@ -1,15 +1,14 @@
 use crate::ImplTraitPosition;
 
-use super::{AnonymousLifetimeMode, ImplTraitContext, LoweringContext, ParamMode};
-use super::{GenericArgsCtor, ParenthesizedGenericArgs};
+use super::{GenericArgsCtor, LifetimeRes, ParenthesizedGenericArgs};
+use super::{ImplTraitContext, LoweringContext, ParamMode};
 
 use rustc_ast::{self as ast, *};
 use rustc_errors::{struct_span_err, Applicability};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, PartialRes, Res};
-use rustc_hir::def_id::DefId;
 use rustc_hir::GenericArg;
-use rustc_span::symbol::Ident;
+use rustc_span::symbol::{kw, Ident};
 use rustc_span::{BytePos, Span, DUMMY_SP};
 
 use smallvec::smallvec;
@@ -47,30 +46,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                         _ => param_mode,
                     };
 
-                    // Figure out if this is a type/trait segment,
-                    // which may need lifetime elision performed.
-                    let parent_def_id = |this: &mut Self, def_id: DefId| DefId {
-                        krate: def_id.krate,
-                        index: this.resolver.def_key(def_id).parent.expect("missing parent"),
-                    };
-                    let type_def_id = match partial_res.base_res() {
-                        Res::Def(DefKind::AssocTy, def_id) if i + 2 == proj_start => {
-                            Some(parent_def_id(self, def_id))
-                        }
-                        Res::Def(DefKind::Variant, def_id) if i + 1 == proj_start => {
-                            Some(parent_def_id(self, def_id))
-                        }
-                        Res::Def(DefKind::Struct, def_id)
-                        | Res::Def(DefKind::Union, def_id)
-                        | Res::Def(DefKind::Enum, def_id)
-                        | Res::Def(DefKind::TyAlias, def_id)
-                        | Res::Def(DefKind::Trait, def_id)
-                            if i + 1 == proj_start =>
-                        {
-                            Some(def_id)
-                        }
-                        _ => None,
-                    };
                     let parenthesized_generic_args = match partial_res.base_res() {
                         // `a::b::Trait(Args)`
                         Res::Def(DefKind::Trait, _) if i + 1 == proj_start => {
@@ -90,13 +65,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                         _ => ParenthesizedGenericArgs::Err,
                     };
 
-                    let num_lifetimes = type_def_id
-                        .map_or(0, |def_id| self.resolver.item_generics_num_lifetimes(def_id));
                     self.lower_path_segment(
                         p.span,
                         segment,
                         param_mode,
-                        num_lifetimes,
                         parenthesized_generic_args,
                         itctx.reborrow(),
                     )
@@ -143,7 +115,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 p.span,
                 segment,
                 param_mode,
-                0,
                 ParenthesizedGenericArgs::Err,
                 itctx.reborrow(),
             ));
@@ -184,7 +155,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                     p.span,
                     segment,
                     param_mode,
-                    0,
                     ParenthesizedGenericArgs::Err,
                     ImplTraitContext::Disallowed(ImplTraitPosition::Path),
                 )
@@ -209,14 +179,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         path_span: Span,
         segment: &PathSegment,
         param_mode: ParamMode,
-        expected_lifetimes: usize,
         parenthesized_generic_args: ParenthesizedGenericArgs,
         itctx: ImplTraitContext<'_, 'hir>,
     ) -> hir::PathSegment<'hir> {
-        debug!(
-            "path_span: {:?}, lower_path_segment(segment: {:?}, expected_lifetimes: {:?})",
-            path_span, segment, expected_lifetimes
-        );
+        debug!("path_span: {:?}, lower_path_segment(segment: {:?})", path_span, segment,);
         let (mut generic_args, infer_args) = if let Some(ref generic_args) = segment.args {
             let msg = "parenthesized type parameters may only be used with a `Fn` trait";
             match **generic_args {
@@ -224,7 +190,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                     self.lower_angle_bracketed_parameter_data(data, param_mode, itctx)
                 }
                 GenericArgs::Parenthesized(ref data) => match parenthesized_generic_args {
-                    ParenthesizedGenericArgs::Ok => self.lower_parenthesized_parameter_data(data),
+                    ParenthesizedGenericArgs::Ok => {
+                        self.lower_parenthesized_parameter_data(segment.id, data)
+                    }
                     ParenthesizedGenericArgs::Err => {
                         let mut err = struct_span_err!(self.sess, data.span, E0214, "{}", msg);
                         err.span_label(data.span, "only `Fn` traits may use parentheses");
@@ -274,33 +242,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
 
         let has_lifetimes =
             generic_args.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)));
-        if !generic_args.parenthesized && !has_lifetimes && expected_lifetimes > 0 {
-            // Note: these spans are used for diagnostics when they can't be inferred.
-            // See rustc_resolve::late::lifetimes::LifetimeContext::add_missing_lifetime_specifiers_label
-            let elided_lifetime_span = if generic_args.span.is_empty() {
-                // If there are no brackets, use the identifier span.
-                // HACK: we use find_ancestor_inside to properly suggest elided spans in paths
-                // originating from macros, since the segment's span might be from a macro arg.
-                segment.ident.span.find_ancestor_inside(path_span).unwrap_or(path_span)
-            } else if generic_args.is_empty() {
-                // If there are brackets, but not generic arguments, then use the opening bracket
-                generic_args.span.with_hi(generic_args.span.lo() + BytePos(1))
-            } else {
-                // Else use an empty span right after the opening bracket.
-                generic_args.span.with_lo(generic_args.span.lo() + BytePos(1)).shrink_to_lo()
-            };
-            generic_args.args = self
-                .elided_path_lifetimes(elided_lifetime_span, expected_lifetimes)
-                .map(GenericArg::Lifetime)
-                .chain(generic_args.args.into_iter())
-                .collect();
-            if let (ParamMode::Explicit, AnonymousLifetimeMode::CreateParameter) =
-                (param_mode, self.anonymous_lifetime_mode)
-            {
-                // Late resolver should have issued the error.
-                self.sess
-                    .delay_span_bug(elided_lifetime_span, "implicit lifetime not allowed here");
-            }
+        if !generic_args.parenthesized && !has_lifetimes {
+            self.maybe_insert_elided_lifetimes_in_path(
+                path_span,
+                segment.id,
+                segment.ident.span,
+                &mut generic_args,
+            );
         }
 
         let res = self.expect_full_res(segment.id);
@@ -323,6 +271,49 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         }
     }
 
+    fn maybe_insert_elided_lifetimes_in_path(
+        &mut self,
+        path_span: Span,
+        segment_id: NodeId,
+        segment_ident_span: Span,
+        generic_args: &mut GenericArgsCtor<'hir>,
+    ) {
+        let (start, end) = match self.resolver.get_lifetime_res(segment_id) {
+            Some(LifetimeRes::ElidedAnchor { start, end }) => (start, end),
+            None => return,
+            Some(_) => panic!(),
+        };
+        let expected_lifetimes = end.as_usize() - start.as_usize();
+        debug!(expected_lifetimes);
+
+        // Note: these spans are used for diagnostics when they can't be inferred.
+        // See rustc_resolve::late::lifetimes::LifetimeContext::add_missing_lifetime_specifiers_label
+        let elided_lifetime_span = if generic_args.span.is_empty() {
+            // If there are no brackets, use the identifier span.
+            // HACK: we use find_ancestor_inside to properly suggest elided spans in paths
+            // originating from macros, since the segment's span might be from a macro arg.
+            segment_ident_span.find_ancestor_inside(path_span).unwrap_or(path_span)
+        } else if generic_args.is_empty() {
+            // If there are brackets, but not generic arguments, then use the opening bracket
+            generic_args.span.with_hi(generic_args.span.lo() + BytePos(1))
+        } else {
+            // Else use an empty span right after the opening bracket.
+            generic_args.span.with_lo(generic_args.span.lo() + BytePos(1)).shrink_to_lo()
+        };
+
+        generic_args.args.insert_many(
+            0,
+            (start.as_u32()..end.as_u32()).map(|i| {
+                let id = NodeId::from_u32(i);
+                let l = self.lower_lifetime(&Lifetime {
+                    id,
+                    ident: Ident::new(kw::UnderscoreLifetime, elided_lifetime_span),
+                });
+                GenericArg::Lifetime(l)
+            }),
+        );
+    }
+
     pub(crate) fn lower_angle_bracketed_parameter_data(
         &mut self,
         data: &AngleBracketedArgs,
@@ -354,6 +345,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
 
     fn lower_parenthesized_parameter_data(
         &mut self,
+        id: NodeId,
         data: &ParenthesizedArgs,
     ) -> (GenericArgsCtor<'hir>, bool) {
         // Switch to `PassThrough` mode for anonymous lifetimes; this
@@ -361,7 +353,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         // a hidden lifetime parameter. This is needed for backwards
         // compatibility, even in contexts like an impl header where
         // we generally don't permit such things (see #51008).
-        self.with_anonymous_lifetime_mode(AnonymousLifetimeMode::PassThrough, |this| {
+        self.with_lifetime_binder(id, |this| {
             let ParenthesizedArgs { span, inputs, inputs_span, output } = data;
             let inputs = this.arena.alloc_from_iter(inputs.iter().map(|ty| {
                 this.lower_ty_direct(
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index 8d815e95528..058a0f975a7 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -8,7 +8,7 @@
 
 use itertools::{Either, Itertools};
 use rustc_ast::ptr::P;
-use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor};
+use rustc_ast::visit::{self, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor};
 use rustc_ast::walk_list;
 use rustc_ast::*;
 use rustc_ast_pretty::pprust::{self, State};
@@ -345,23 +345,6 @@ impl<'a> AstValidator<'a> {
         }
     }
 
-    // FIXME(ecstaticmorse): Instead, use `bound_context` to check this in `visit_param_bound`.
-    fn no_questions_in_bounds(&self, bounds: &GenericBounds, where_: &str, is_trait: bool) {
-        for bound in bounds {
-            if let GenericBound::Trait(ref poly, TraitBoundModifier::Maybe) = *bound {
-                let mut err = self.err_handler().struct_span_err(
-                    poly.span,
-                    &format!("`?Trait` is not permitted in {}", where_),
-                );
-                if is_trait {
-                    let path_str = pprust::path_to_string(&poly.trait_ref.path);
-                    err.note(&format!("traits are `?{}` by default", path_str));
-                }
-                err.emit();
-            }
-        }
-    }
-
     fn check_late_bound_lifetime_defs(&self, params: &[GenericParam]) {
         // Check only lifetime parameters are present and that the lifetime
         // parameters that are present have no bounds.
@@ -873,7 +856,6 @@ impl<'a> AstValidator<'a> {
                         any_lifetime_bounds = true;
                     }
                 }
-                self.no_questions_in_bounds(bounds, "trait object types", false);
             }
             TyKind::ImplTrait(_, ref bounds) => {
                 if self.is_impl_trait_banned {
@@ -1242,14 +1224,15 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                     self.deny_where_clause(&generics.where_clause, item.ident.span);
                     self.deny_items(items, item.ident.span);
                 }
-                self.no_questions_in_bounds(bounds, "supertraits", true);
 
                 // Equivalent of `visit::walk_item` for `ItemKind::Trait` that inserts a bound
                 // context for the supertraits.
                 self.visit_vis(&item.vis);
                 self.visit_ident(item.ident);
                 self.visit_generics(generics);
-                self.with_banned_tilde_const(|this| walk_list!(this, visit_param_bound, bounds));
+                self.with_banned_tilde_const(|this| {
+                    walk_list!(this, visit_param_bound, bounds, BoundKind::SuperTraits)
+                });
                 walk_list!(self, visit_assoc_item, items, AssocCtxt::Trait);
                 walk_list!(self, visit_attribute, &item.attrs);
                 return;
@@ -1476,23 +1459,39 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
         visit::walk_generic_param(self, param);
     }
 
-    fn visit_param_bound(&mut self, bound: &'a GenericBound) {
-        match bound {
-            GenericBound::Trait(_, TraitBoundModifier::MaybeConst) => {
-                if !self.is_tilde_const_allowed {
+    fn visit_param_bound(&mut self, bound: &'a GenericBound, ctxt: BoundKind) {
+        if let GenericBound::Trait(ref poly, modify) = *bound {
+            match (ctxt, modify) {
+                (BoundKind::SuperTraits, TraitBoundModifier::Maybe) => {
+                    let mut err = self.err_handler().struct_span_err(
+                        poly.span,
+                        &format!("`?Trait` is not permitted in supertraits"),
+                    );
+                    let path_str = pprust::path_to_string(&poly.trait_ref.path);
+                    err.note(&format!("traits are `?{}` by default", path_str));
+                    err.emit();
+                }
+                (BoundKind::TraitObject, TraitBoundModifier::Maybe) => {
+                    let mut err = self.err_handler().struct_span_err(
+                        poly.span,
+                        &format!("`?Trait` is not permitted in trait object types"),
+                    );
+                    err.emit();
+                }
+                (_, TraitBoundModifier::MaybeConst) => {
+                    if !self.is_tilde_const_allowed {
+                        self.err_handler()
+                            .struct_span_err(bound.span(), "`~const` is not allowed here")
+                            .note("only allowed on bounds on traits' associated types and functions, const fns, const impls and its associated functions")
+                            .emit();
+                    }
+                }
+                (_, TraitBoundModifier::MaybeConstMaybe) => {
                     self.err_handler()
-                        .struct_span_err(bound.span(), "`~const` is not allowed here")
-                        .note("only allowed on bounds on traits' associated types and functions, const fns, const impls and its associated functions")
-                        .emit();
+                        .span_err(bound.span(), "`~const` and `?` are mutually exclusive");
                 }
+                _ => {}
             }
-
-            GenericBound::Trait(_, TraitBoundModifier::MaybeConstMaybe) => {
-                self.err_handler()
-                    .span_err(bound.span(), "`~const` and `?` are mutually exclusive");
-            }
-
-            _ => {}
         }
 
         visit::walk_param_bound(self, bound)
@@ -1662,7 +1661,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 walk_list!(self, visit_attribute, &item.attrs);
                 self.with_tilde_const_allowed(|this| {
                     this.visit_generics(generics);
-                    walk_list!(this, visit_param_bound, bounds);
+                    walk_list!(this, visit_param_bound, bounds, BoundKind::Bound);
                 });
                 walk_list!(self, visit_ty, ty);
             }
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 649af48e48a..0e8af549692 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -783,6 +783,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
     gate_all!(inline_const, "inline-const is experimental");
     gate_all!(inline_const_pat, "inline-const in pattern position is experimental");
     gate_all!(associated_const_equality, "associated const equality is incomplete");
+    gate_all!(yeet_expr, "`do yeet` expression is experimental");
 
     // All uses of `gate_all!` below this point were added in #65742,
     // and subsequently disabled (with the non-early gating readded).
diff --git a/compiler/rustc_ast_passes/src/node_count.rs b/compiler/rustc_ast_passes/src/node_count.rs
index a4a48cc8e8a..48b79809c1b 100644
--- a/compiler/rustc_ast_passes/src/node_count.rs
+++ b/compiler/rustc_ast_passes/src/node_count.rs
@@ -76,7 +76,7 @@ impl<'ast> Visitor<'ast> for NodeCounter {
         self.count += 1;
         walk_trait_ref(self, t)
     }
-    fn visit_param_bound(&mut self, bounds: &GenericBound) {
+    fn visit_param_bound(&mut self, bounds: &GenericBound, _ctxt: BoundKind) {
         self.count += 1;
         walk_param_bound(self, bounds)
     }
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index 3c9bb81bedb..a2ebe3048ce 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -6,7 +6,7 @@ use crate::pp::Breaks::{Consistent, Inconsistent};
 use crate::pp::{self, Breaks};
 
 use rustc_ast::ptr::P;
-use rustc_ast::token::{self, BinOpToken, CommentKind, DelimToken, Nonterminal, Token, TokenKind};
+use rustc_ast::token::{self, BinOpToken, CommentKind, Delimiter, Nonterminal, Token, TokenKind};
 use rustc_ast::tokenstream::{TokenStream, TokenTree};
 use rustc_ast::util::classify;
 use rustc_ast::util::comments::{gather_comments, Comment, CommentStyle};
@@ -155,10 +155,10 @@ fn tt_prepend_space(tt: &TokenTree, prev: &TokenTree) -> bool {
     }
     match tt {
         TokenTree::Token(token) => !matches!(token.kind, token::Comma | token::Not | token::Dot),
-        TokenTree::Delimited(_, DelimToken::Paren, _) => {
+        TokenTree::Delimited(_, Delimiter::Parenthesis, _) => {
             !matches!(prev, TokenTree::Token(Token { kind: token::Ident(..), .. }))
         }
-        TokenTree::Delimited(_, DelimToken::Bracket, _) => {
+        TokenTree::Delimited(_, Delimiter::Bracket, _) => {
             !matches!(prev, TokenTree::Token(Token { kind: token::Pound, .. }))
         }
         TokenTree::Delimited(..) => true,
@@ -464,7 +464,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
                 Some(MacHeader::Path(&item.path)),
                 false,
                 None,
-                delim.to_token(),
+                Some(delim.to_token()),
                 tokens,
                 true,
                 span,
@@ -530,7 +530,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
                     None,
                     false,
                     None,
-                    *delim,
+                    Some(*delim),
                     tts,
                     convert_dollar_crate,
                     dspan.entire(),
@@ -556,12 +556,12 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
         header: Option<MacHeader<'_>>,
         has_bang: bool,
         ident: Option<Ident>,
-        delim: DelimToken,
+        delim: Option<Delimiter>,
         tts: &TokenStream,
         convert_dollar_crate: bool,
         span: Span,
     ) {
-        if delim == DelimToken::Brace {
+        if delim == Some(Delimiter::Brace) {
             self.cbox(INDENT_UNIT);
         }
         match header {
@@ -577,7 +577,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
             self.print_ident(ident);
         }
         match delim {
-            DelimToken::Brace => {
+            Some(Delimiter::Brace) => {
                 if header.is_some() || has_bang || ident.is_some() {
                     self.nbsp();
                 }
@@ -585,23 +585,25 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
                 if !tts.is_empty() {
                     self.space();
                 }
-            }
-            _ => {
-                let token_str = self.token_kind_to_string(&token::OpenDelim(delim));
-                self.word(token_str)
-            }
-        }
-        self.ibox(0);
-        self.print_tts(tts, convert_dollar_crate);
-        self.end();
-        match delim {
-            DelimToken::Brace => {
+                self.ibox(0);
+                self.print_tts(tts, convert_dollar_crate);
+                self.end();
                 let empty = tts.is_empty();
                 self.bclose(span, empty);
             }
-            _ => {
+            Some(delim) => {
+                let token_str = self.token_kind_to_string(&token::OpenDelim(delim));
+                self.word(token_str);
+                self.ibox(0);
+                self.print_tts(tts, convert_dollar_crate);
+                self.end();
                 let token_str = self.token_kind_to_string(&token::CloseDelim(delim));
-                self.word(token_str)
+                self.word(token_str);
+            }
+            None => {
+                self.ibox(0);
+                self.print_tts(tts, convert_dollar_crate);
+                self.end();
             }
         }
     }
@@ -756,13 +758,15 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
             token::RArrow => "->".into(),
             token::LArrow => "<-".into(),
             token::FatArrow => "=>".into(),
-            token::OpenDelim(token::Paren) => "(".into(),
-            token::CloseDelim(token::Paren) => ")".into(),
-            token::OpenDelim(token::Bracket) => "[".into(),
-            token::CloseDelim(token::Bracket) => "]".into(),
-            token::OpenDelim(token::Brace) => "{".into(),
-            token::CloseDelim(token::Brace) => "}".into(),
-            token::OpenDelim(token::NoDelim) | token::CloseDelim(token::NoDelim) => "".into(),
+            token::OpenDelim(Delimiter::Parenthesis) => "(".into(),
+            token::CloseDelim(Delimiter::Parenthesis) => ")".into(),
+            token::OpenDelim(Delimiter::Bracket) => "[".into(),
+            token::CloseDelim(Delimiter::Bracket) => "]".into(),
+            token::OpenDelim(Delimiter::Brace) => "{".into(),
+            token::CloseDelim(Delimiter::Brace) => "}".into(),
+            token::OpenDelim(Delimiter::Invisible) | token::CloseDelim(Delimiter::Invisible) => {
+                "".into()
+            }
             token::Pound => "#".into(),
             token::Dollar => "$".into(),
             token::Question => "?".into(),
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
index 9de4cbbee13..9f44f1b6cc2 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
@@ -64,7 +64,10 @@ impl<'a> State<'a> {
     // parses as the erroneous construct `if (return {})`, not `if (return) {}`.
     pub(super) fn cond_needs_par(expr: &ast::Expr) -> bool {
         match expr.kind {
-            ast::ExprKind::Break(..) | ast::ExprKind::Closure(..) | ast::ExprKind::Ret(..) => true,
+            ast::ExprKind::Break(..)
+            | ast::ExprKind::Closure(..)
+            | ast::ExprKind::Ret(..)
+            | ast::ExprKind::Yeet(..) => true,
             _ => parser::contains_exterior_struct_lit(expr),
         }
     }
@@ -502,6 +505,15 @@ impl<'a> State<'a> {
                     self.print_expr_maybe_paren(expr, parser::PREC_JUMP);
                 }
             }
+            ast::ExprKind::Yeet(ref result) => {
+                self.word("do");
+                self.word(" ");
+                self.word("yeet");
+                if let Some(ref expr) = *result {
+                    self.word(" ");
+                    self.print_expr_maybe_paren(expr, parser::PREC_JUMP);
+                }
+            }
             ast::ExprKind::InlineAsm(ref a) => {
                 self.word("asm!");
                 self.print_inline_asm(a);
diff --git a/compiler/rustc_borrowck/src/constraints/graph.rs b/compiler/rustc_borrowck/src/constraints/graph.rs
index cb9e0234c49..c19a39c393f 100644
--- a/compiler/rustc_borrowck/src/constraints/graph.rs
+++ b/compiler/rustc_borrowck/src/constraints/graph.rs
@@ -156,6 +156,7 @@ impl<'s, 'tcx, D: ConstraintGraphDirecton> Iterator for Edges<'s, 'tcx, D> {
                 sup: self.static_region,
                 sub: next_static_idx.into(),
                 locations: Locations::All(DUMMY_SP),
+                span: DUMMY_SP,
                 category: ConstraintCategory::Internal,
                 variance_info: VarianceDiagInfo::default(),
             })
@@ -189,7 +190,7 @@ impl<'s, 'tcx, D: ConstraintGraphDirecton> RegionGraph<'s, 'tcx, D> {
 
     /// Given a region `R`, iterate over all regions `R1` such that
     /// there exists a constraint `R: R1`.
-    crate fn outgoing_regions(&self, region_sup: RegionVid) -> Successors<'_, 'tcx, D> {
+    crate fn outgoing_regions(&self, region_sup: RegionVid) -> Successors<'s, 'tcx, D> {
         Successors {
             edges: self.constraint_graph.outgoing_edges(region_sup, self.set, self.static_region),
         }
@@ -224,10 +225,7 @@ impl<'s, 'tcx, D: ConstraintGraphDirecton> graph::WithSuccessors for RegionGraph
     }
 }
 
-impl<'s, 'graph, 'tcx, D: ConstraintGraphDirecton> graph::GraphSuccessors<'graph>
-    for RegionGraph<'s, 'tcx, D>
-{
+impl<'s, 'tcx, D: ConstraintGraphDirecton> graph::GraphSuccessors<'_> for RegionGraph<'s, 'tcx, D> {
     type Item = RegionVid;
-    // FIXME - why can't this be `'graph, 'tcx`
-    type Iter = Successors<'graph, 'graph, D>;
+    type Iter = Successors<'s, 'tcx, D>;
 }
diff --git a/compiler/rustc_borrowck/src/constraints/mod.rs b/compiler/rustc_borrowck/src/constraints/mod.rs
index d41143ee763..14f0e5f620a 100644
--- a/compiler/rustc_borrowck/src/constraints/mod.rs
+++ b/compiler/rustc_borrowck/src/constraints/mod.rs
@@ -2,6 +2,7 @@ use rustc_data_structures::graph::scc::Sccs;
 use rustc_index::vec::IndexVec;
 use rustc_middle::mir::ConstraintCategory;
 use rustc_middle::ty::{RegionVid, VarianceDiagInfo};
+use rustc_span::Span;
 use std::fmt;
 use std::ops::Index;
 
@@ -87,6 +88,12 @@ pub struct OutlivesConstraint<'tcx> {
     /// Where did this constraint arise?
     pub locations: Locations,
 
+    /// The `Span` associated with the creation of this constraint.
+    /// This should be used in preference to obtaining the span from
+    /// `locations`, since the `locations` may give a poor span
+    /// in some cases (e.g. converting a constraint from a promoted).
+    pub span: Span,
+
     /// What caused this constraint?
     pub category: ConstraintCategory,
 
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index 9e5fb674772..3b4e9e95b0e 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -7,6 +7,7 @@ use rustc_hir::def_id::DefId;
 use rustc_hir::{AsyncGeneratorKind, GeneratorKind};
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_infer::traits::ObligationCause;
+use rustc_middle::mir::tcx::PlaceTy;
 use rustc_middle::mir::{
     self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory,
     FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef,
@@ -22,6 +23,7 @@ use rustc_trait_selection::traits::TraitEngineExt as _;
 use crate::borrow_set::TwoPhaseActivation;
 use crate::borrowck_errors;
 
+use crate::diagnostics::conflict_errors::StorageDeadOrDrop::LocalStorageDead;
 use crate::diagnostics::find_all_local_uses;
 use crate::{
     borrow_set::BorrowData, diagnostics::Instance, prefixes::IsPrefixOf,
@@ -1484,7 +1486,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                         err.span_suggestion_hidden(
                             return_span,
                             "use `.collect()` to allocate the iterator",
-                            format!("{}{}", snippet, ".collect::<Vec<_>>()"),
+                            format!("{snippet}.collect::<Vec<_>>()"),
                             Applicability::MaybeIncorrect,
                         );
                     }
@@ -1956,45 +1958,46 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
     fn classify_drop_access_kind(&self, place: PlaceRef<'tcx>) -> StorageDeadOrDrop<'tcx> {
         let tcx = self.infcx.tcx;
-        match place.last_projection() {
-            None => StorageDeadOrDrop::LocalStorageDead,
-            Some((place_base, elem)) => {
-                // FIXME(spastorino) make this iterate
-                let base_access = self.classify_drop_access_kind(place_base);
-                match elem {
-                    ProjectionElem::Deref => match base_access {
-                        StorageDeadOrDrop::LocalStorageDead
-                        | StorageDeadOrDrop::BoxedStorageDead => {
-                            assert!(
-                                place_base.ty(self.body, tcx).ty.is_box(),
-                                "Drop of value behind a reference or raw pointer"
-                            );
-                            StorageDeadOrDrop::BoxedStorageDead
-                        }
-                        StorageDeadOrDrop::Destructor(_) => base_access,
-                    },
-                    ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => {
-                        let base_ty = place_base.ty(self.body, tcx).ty;
-                        match base_ty.kind() {
-                            ty::Adt(def, _) if def.has_dtor(tcx) => {
-                                // Report the outermost adt with a destructor
-                                match base_access {
-                                    StorageDeadOrDrop::Destructor(_) => base_access,
-                                    StorageDeadOrDrop::LocalStorageDead
-                                    | StorageDeadOrDrop::BoxedStorageDead => {
-                                        StorageDeadOrDrop::Destructor(base_ty)
+        let (kind, _place_ty) = place.projection.iter().fold(
+            (LocalStorageDead, PlaceTy::from_ty(self.body.local_decls[place.local].ty)),
+            |(kind, place_ty), &elem| {
+                (
+                    match elem {
+                        ProjectionElem::Deref => match kind {
+                            StorageDeadOrDrop::LocalStorageDead
+                            | StorageDeadOrDrop::BoxedStorageDead => {
+                                assert!(
+                                    place_ty.ty.is_box(),
+                                    "Drop of value behind a reference or raw pointer"
+                                );
+                                StorageDeadOrDrop::BoxedStorageDead
+                            }
+                            StorageDeadOrDrop::Destructor(_) => kind,
+                        },
+                        ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => {
+                            match place_ty.ty.kind() {
+                                ty::Adt(def, _) if def.has_dtor(tcx) => {
+                                    // Report the outermost adt with a destructor
+                                    match kind {
+                                        StorageDeadOrDrop::Destructor(_) => kind,
+                                        StorageDeadOrDrop::LocalStorageDead
+                                        | StorageDeadOrDrop::BoxedStorageDead => {
+                                            StorageDeadOrDrop::Destructor(place_ty.ty)
+                                        }
                                     }
                                 }
+                                _ => kind,
                             }
-                            _ => base_access,
                         }
-                    }
-                    ProjectionElem::ConstantIndex { .. }
-                    | ProjectionElem::Subslice { .. }
-                    | ProjectionElem::Index(_) => base_access,
-                }
-            }
-        }
+                        ProjectionElem::ConstantIndex { .. }
+                        | ProjectionElem::Subslice { .. }
+                        | ProjectionElem::Index(_) => kind,
+                    },
+                    place_ty.projection_ty(tcx, elem),
+                )
+            },
+        );
+        kind
     }
 
     /// Describe the reason for the fake borrow that was assigned to `place`.
diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs
index c2b5c16517a..b81360fd6aa 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mod.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs
@@ -40,6 +40,7 @@ crate use outlives_suggestion::OutlivesSuggestionBuilder;
 crate use region_errors::{ErrorConstraintInfo, RegionErrorKind, RegionErrors};
 crate use region_name::{RegionName, RegionNameSource};
 crate use rustc_const_eval::util::CallKind;
+use rustc_middle::mir::tcx::PlaceTy;
 
 pub(super) struct IncludingDowncast(pub(super) bool);
 
@@ -329,30 +330,20 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
     /// End-user visible description of the `field`nth field of `base`
     fn describe_field(&self, place: PlaceRef<'tcx>, field: Field) -> String {
-        // FIXME Place2 Make this work iteratively
-        match place {
-            PlaceRef { local, projection: [] } => {
-                let local = &self.body.local_decls[local];
-                self.describe_field_from_ty(local.ty, field, None)
-            }
+        let place_ty = match place {
+            PlaceRef { local, projection: [] } => PlaceTy::from_ty(self.body.local_decls[local].ty),
             PlaceRef { local, projection: [proj_base @ .., elem] } => match elem {
-                ProjectionElem::Deref => {
-                    self.describe_field(PlaceRef { local, projection: proj_base }, field)
-                }
-                ProjectionElem::Downcast(_, variant_index) => {
-                    let base_ty = place.ty(self.body, self.infcx.tcx).ty;
-                    self.describe_field_from_ty(base_ty, field, Some(*variant_index))
-                }
-                ProjectionElem::Field(_, field_type) => {
-                    self.describe_field_from_ty(*field_type, field, None)
-                }
-                ProjectionElem::Index(..)
+                ProjectionElem::Deref
+                | ProjectionElem::Index(..)
                 | ProjectionElem::ConstantIndex { .. }
                 | ProjectionElem::Subslice { .. } => {
-                    self.describe_field(PlaceRef { local, projection: proj_base }, field)
+                    PlaceRef { local, projection: proj_base }.ty(self.body, self.infcx.tcx)
                 }
+                ProjectionElem::Downcast(..) => place.ty(self.body, self.infcx.tcx),
+                ProjectionElem::Field(_, field_type) => PlaceTy::from_ty(*field_type),
             },
-        }
+        };
+        self.describe_field_from_ty(place_ty.ty, field, place_ty.variant_index)
     }
 
     /// End-user visible description of the `field_index`nth field of `ty`
diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
index 8b12db071b6..fe5e3c5a81b 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
@@ -237,7 +237,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                     err.span_suggestion_verbose(
                         span,
                         "consider changing this to be mutable",
-                        " mut ".into(),
+                        " mut ",
                         Applicability::MaybeIncorrect,
                     );
                 }
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
index 5fd9ecf4513..30fe4ea8662 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
@@ -1,16 +1,20 @@
 //! Error reporting machinery for lifetime errors.
 
-use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
+use rustc_errors::{Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
 use rustc_infer::infer::{
-    error_reporting::nice_region_error::NiceRegionError,
-    error_reporting::unexpected_hidden_region_diagnostic, NllRegionVariableOrigin,
+    error_reporting::nice_region_error::{
+        self, find_anon_type, find_param_with_region, suggest_adding_lifetime_params,
+        NiceRegionError,
+    },
+    error_reporting::unexpected_hidden_region_diagnostic,
+    NllRegionVariableOrigin, RelateParamBound,
 };
 use rustc_middle::hir::place::PlaceBase;
 use rustc_middle::mir::{ConstraintCategory, ReturnConstraint};
-use rustc_middle::ty::subst::{InternalSubsts, Subst};
+use rustc_middle::ty::subst::InternalSubsts;
 use rustc_middle::ty::{self, RegionVid, Ty};
-use rustc_span::symbol::{kw, sym};
-use rustc_span::{BytePos, Span};
+use rustc_span::symbol::sym;
+use rustc_span::Span;
 
 use crate::borrowck_errors;
 
@@ -166,11 +170,14 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                     let type_test_span = type_test.locations.span(&self.body);
 
                     if let Some(lower_bound_region) = lower_bound_region {
+                        let generic_ty = type_test.generic_kind.to_ty(self.infcx.tcx);
+                        let origin = RelateParamBound(type_test_span, generic_ty, None);
                         self.buffer_error(self.infcx.construct_generic_bound_failure(
                             type_test_span,
-                            None,
+                            Some(origin),
                             type_test.generic_kind,
                             lower_bound_region,
+                            self.body.source.def_id().as_local(),
                         ));
                     } else {
                         // FIXME. We should handle this case better. It
@@ -626,6 +633,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         }
 
         self.add_static_impl_trait_suggestion(&mut diag, *fr, fr_name, *outlived_fr);
+        self.suggest_adding_lifetime_params(&mut diag, *fr, *outlived_fr);
 
         diag
     }
@@ -647,82 +655,76 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         fr_name: RegionName,
         outlived_fr: RegionVid,
     ) {
-        if let (Some(f), Some(ty::ReStatic)) =
-            (self.to_error_region(fr), self.to_error_region(outlived_fr).as_deref())
+        if let (Some(f), Some(outlived_f)) =
+            (self.to_error_region(fr), self.to_error_region(outlived_fr))
         {
-            if let Some(&ty::Opaque(did, substs)) = self
+            if *outlived_f != ty::ReStatic {
+                return;
+            }
+
+            let fn_returns = self
                 .infcx
                 .tcx
                 .is_suitable_region(f)
-                .map(|r| r.def_id)
-                .and_then(|id| self.infcx.tcx.return_type_impl_trait(id))
-                .map(|(ty, _)| ty.kind())
-            {
-                // Check whether or not the impl trait return type is intended to capture
-                // data with the static lifetime.
-                //
-                // eg. check for `impl Trait + 'static` instead of `impl Trait`.
-                let has_static_predicate = {
-                    let bounds = self.infcx.tcx.explicit_item_bounds(did);
-
-                    let mut found = false;
-                    for (bound, _) in bounds {
-                        if let ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(_, r)) =
-                            bound.kind().skip_binder()
-                        {
-                            let r = r.subst(self.infcx.tcx, substs);
-                            if r.is_static() {
-                                found = true;
-                                break;
-                            } else {
-                                // If there's already a lifetime bound, don't
-                                // suggest anything.
-                                return;
-                            }
-                        }
-                    }
+                .map(|r| self.infcx.tcx.return_type_impl_or_dyn_traits(r.def_id))
+                .unwrap_or_default();
 
-                    found
-                };
-
-                debug!(
-                    "add_static_impl_trait_suggestion: has_static_predicate={:?}",
-                    has_static_predicate
-                );
-                let static_str = kw::StaticLifetime;
-                // If there is a static predicate, then the only sensible suggestion is to replace
-                // fr with `'static`.
-                if has_static_predicate {
-                    diag.help(&format!("consider replacing `{fr_name}` with `{static_str}`"));
-                } else {
-                    // Otherwise, we should suggest adding a constraint on the return type.
-                    let span = self.infcx.tcx.def_span(did);
-                    if let Ok(snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(span) {
-                        let suggestable_fr_name = if fr_name.was_named() {
-                            fr_name.to_string()
-                        } else {
-                            "'_".to_string()
-                        };
-                        let span = if snippet.ends_with(';') {
-                            // `type X = impl Trait;`
-                            span.with_hi(span.hi() - BytePos(1))
-                        } else {
-                            span
-                        };
-                        let suggestion = format!(" + {suggestable_fr_name}");
-                        let span = span.shrink_to_hi();
-                        diag.span_suggestion(
-                            span,
-                            &format!(
-                                "to allow this `impl Trait` to capture borrowed data with lifetime \
-                                 `{fr_name}`, add `{suggestable_fr_name}` as a bound",
-                            ),
-                            suggestion,
-                            Applicability::MachineApplicable,
-                        );
-                    }
-                }
+            if fn_returns.is_empty() {
+                return;
             }
+
+            let param = if let Some(param) = find_param_with_region(self.infcx.tcx, f, outlived_f) {
+                param
+            } else {
+                return;
+            };
+
+            let lifetime = if f.has_name() { fr_name.to_string() } else { "'_".to_string() };
+
+            let arg = match param.param.pat.simple_ident() {
+                Some(simple_ident) => format!("argument `{}`", simple_ident),
+                None => "the argument".to_string(),
+            };
+            let captures = format!("captures data from {}", arg);
+
+            return nice_region_error::suggest_new_region_bound(
+                self.infcx.tcx,
+                diag,
+                fn_returns,
+                lifetime,
+                Some(arg),
+                captures,
+                Some((param.param_ty_span, param.param_ty.to_string())),
+            );
         }
     }
+
+    fn suggest_adding_lifetime_params(
+        &self,
+        diag: &mut Diagnostic,
+        sub: RegionVid,
+        sup: RegionVid,
+    ) {
+        let (Some(sub), Some(sup)) = (self.to_error_region(sub), self.to_error_region(sup)) else {
+            return
+        };
+
+        let Some((ty_sub, _)) = self
+            .infcx
+            .tcx
+            .is_suitable_region(sub)
+            .and_then(|anon_reg| find_anon_type(self.infcx.tcx, sub, &anon_reg.boundregion)) else {
+            return
+        };
+
+        let Some((ty_sup, _)) = self
+            .infcx
+            .tcx
+            .is_suitable_region(sup)
+            .and_then(|anon_reg| find_anon_type(self.infcx.tcx, sup, &anon_reg.boundregion)) else {
+            return
+        };
+
+        suggest_adding_lifetime_params(self.infcx.tcx, sub, ty_sup, ty_sub, diag);
+    }
 }
diff --git a/compiler/rustc_borrowck/src/region_infer/dump_mir.rs b/compiler/rustc_borrowck/src/region_infer/dump_mir.rs
index 97233b930c3..fe5193102f9 100644
--- a/compiler/rustc_borrowck/src/region_infer/dump_mir.rs
+++ b/compiler/rustc_borrowck/src/region_infer/dump_mir.rs
@@ -74,14 +74,18 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         let mut constraints: Vec<_> = self.constraints.outlives().iter().collect();
         constraints.sort_by_key(|c| (c.sup, c.sub));
         for constraint in &constraints {
-            let OutlivesConstraint { sup, sub, locations, category, variance_info: _ } = constraint;
+            let OutlivesConstraint { sup, sub, locations, category, span, variance_info: _ } =
+                constraint;
             let (name, arg) = match locations {
                 Locations::All(span) => {
                     ("All", tcx.sess.source_map().span_to_embeddable_string(*span))
                 }
                 Locations::Single(loc) => ("Single", format!("{:?}", loc)),
             };
-            with_msg(&format!("{:?}: {:?} due to {:?} at {}({})", sup, sub, category, name, arg))?;
+            with_msg(&format!(
+                "{:?}: {:?} due to {:?} at {}({}) ({:?}",
+                sup, sub, category, name, arg, span
+            ))?;
         }
 
         Ok(())
diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs
index dabf61715ce..cf03f34a4ec 100644
--- a/compiler/rustc_borrowck/src/region_infer/mod.rs
+++ b/compiler/rustc_borrowck/src/region_infer/mod.rs
@@ -1733,7 +1733,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
 
     crate fn retrieve_closure_constraint_info(
         &self,
-        body: &Body<'tcx>,
+        _body: &Body<'tcx>,
         constraint: &OutlivesConstraint<'tcx>,
     ) -> BlameConstraint<'tcx> {
         let loc = match constraint.locations {
@@ -1760,7 +1760,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             .unwrap_or(BlameConstraint {
                 category: constraint.category,
                 from_closure: false,
-                cause: ObligationCause::dummy_with_span(body.source_info(loc).span),
+                cause: ObligationCause::dummy_with_span(constraint.span),
                 variance_info: constraint.variance_info,
             })
     }
@@ -1869,6 +1869,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                     sup: r,
                     sub: constraint.min_choice,
                     locations: Locations::All(p_c.definition_span),
+                    span: p_c.definition_span,
                     category: ConstraintCategory::OpaqueType,
                     variance_info: ty::VarianceDiagInfo::default(),
                 };
@@ -2017,7 +2018,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                         category: constraint.category,
                         from_closure: false,
                         cause: ObligationCause::new(
-                            constraint.locations.span(body),
+                            constraint.span,
                             CRATE_HIR_ID,
                             cause_code.clone(),
                         ),
diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs
index 5022cb98b82..21190a850b7 100644
--- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs
+++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs
@@ -8,7 +8,7 @@ use rustc_middle::mir::ConstraintCategory;
 use rustc_middle::ty::subst::GenericArgKind;
 use rustc_middle::ty::TypeFoldable;
 use rustc_middle::ty::{self, TyCtxt};
-use rustc_span::DUMMY_SP;
+use rustc_span::{Span, DUMMY_SP};
 
 use crate::{
     constraints::OutlivesConstraint,
@@ -26,6 +26,7 @@ crate struct ConstraintConversion<'a, 'tcx> {
     implicit_region_bound: Option<ty::Region<'tcx>>,
     param_env: ty::ParamEnv<'tcx>,
     locations: Locations,
+    span: Span,
     category: ConstraintCategory,
     constraints: &'a mut MirTypeckRegionConstraints<'tcx>,
 }
@@ -38,6 +39,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
         implicit_region_bound: Option<ty::Region<'tcx>>,
         param_env: ty::ParamEnv<'tcx>,
         locations: Locations,
+        span: Span,
         category: ConstraintCategory,
         constraints: &'a mut MirTypeckRegionConstraints<'tcx>,
     ) -> Self {
@@ -49,6 +51,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
             implicit_region_bound,
             param_env,
             locations,
+            span,
             category,
             constraints,
         }
@@ -153,6 +156,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
         self.constraints.outlives_constraints.push(OutlivesConstraint {
             locations: self.locations,
             category: self.category,
+            span: self.span,
             sub,
             sup,
             variance_info: ty::VarianceDiagInfo::default(),
diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
index f8439d2e163..f08f2e1b12d 100644
--- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
+++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
@@ -316,6 +316,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
                 self.implicit_region_bound,
                 self.param_env,
                 Locations::All(DUMMY_SP),
+                DUMMY_SP,
                 ConstraintCategory::Internal,
                 &mut self.constraints,
             )
diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs
index 83c8ecba1f1..e6f996491a4 100644
--- a/compiler/rustc_borrowck/src/type_check/input_output.rs
+++ b/compiler/rustc_borrowck/src/type_check/input_output.rs
@@ -235,6 +235,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                 Some(self.implicit_region_bound),
                 self.param_env,
                 Locations::All(DUMMY_SP),
+                DUMMY_SP,
                 ConstraintCategory::Internal,
                 &mut self.borrowck_context.constraints,
             )
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index ece801716b2..c4a190b44cb 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -37,18 +37,13 @@ use rustc_middle::ty::{
 use rustc_span::def_id::CRATE_DEF_ID;
 use rustc_span::{Span, DUMMY_SP};
 use rustc_target::abi::VariantIdx;
-use rustc_trait_selection::infer::InferCtxtExt as _;
-use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
 use rustc_trait_selection::traits::query::type_op;
 use rustc_trait_selection::traits::query::type_op::custom::scrape_region_constraints;
 use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp;
 use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput};
 use rustc_trait_selection::traits::query::Fallible;
-use rustc_trait_selection::traits::{self, ObligationCause, PredicateObligation};
+use rustc_trait_selection::traits::PredicateObligation;
 
-use rustc_const_eval::transform::{
-    check_consts::ConstCx, promote_consts::is_const_fn_in_array_repeat_expression,
-};
 use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
 use rustc_mir_dataflow::move_paths::MoveData;
 use rustc_mir_dataflow::ResultsCursor;
@@ -1141,6 +1136,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
             Some(self.implicit_region_bound),
             self.param_env,
             locations,
+            locations.span(self.body),
             category,
             &mut self.borrowck_context.constraints,
         )
@@ -1867,41 +1863,17 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                         Operand::Move(place) => {
                             // Make sure that repeated elements implement `Copy`.
                             let span = body.source_info(location).span;
-                            let ty = operand.ty(body, tcx);
-                            if !self.infcx.type_is_copy_modulo_regions(self.param_env, ty, span) {
-                                let ccx = ConstCx::new_with_param_env(tcx, body, self.param_env);
-                                let is_const_fn =
-                                    is_const_fn_in_array_repeat_expression(&ccx, &place, &body);
-
-                                debug!("check_rvalue: is_const_fn={:?}", is_const_fn);
-
-                                let def_id = body.source.def_id().expect_local();
-                                let obligation = traits::Obligation::new(
-                                    ObligationCause::new(
-                                        span,
-                                        self.tcx().hir().local_def_id_to_hir_id(def_id),
-                                        traits::ObligationCauseCode::RepeatElementCopy {
-                                            is_const_fn,
-                                        },
-                                    ),
-                                    self.param_env,
-                                    ty::Binder::dummy(ty::TraitRef::new(
-                                        self.tcx().require_lang_item(
-                                            LangItem::Copy,
-                                            Some(self.last_span),
-                                        ),
-                                        tcx.mk_substs_trait(ty, &[]),
-                                    ))
-                                    .without_const()
-                                    .to_predicate(self.tcx()),
-                                );
-                                self.infcx.report_selection_error(
-                                    obligation.clone(),
-                                    &obligation,
-                                    &traits::SelectionError::Unimplemented,
-                                    false,
-                                );
-                            }
+                            let ty = place.ty(body, tcx).ty;
+                            let trait_ref = ty::TraitRef::new(
+                                tcx.require_lang_item(LangItem::Copy, Some(span)),
+                                tcx.mk_substs_trait(ty, &[]),
+                            );
+
+                            self.prove_trait_ref(
+                                trait_ref,
+                                Locations::Single(location),
+                                ConstraintCategory::CopyBound,
+                            );
                         }
                     }
                 }
@@ -2401,6 +2373,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                                 sup: ref_region.to_region_vid(),
                                 sub: borrow_region.to_region_vid(),
                                 locations: location.to_locations(),
+                                span: location.to_locations().span(body),
                                 category,
                                 variance_info: ty::VarianceDiagInfo::default(),
                             });
diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs
index f0106630797..f98d2c3128c 100644
--- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs
+++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs
@@ -116,6 +116,7 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, 'tcx>
                 sup,
                 sub,
                 locations: self.locations,
+                span: self.locations.span(self.type_checker.body),
                 category: self.category,
                 variance_info: info,
             },
diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs
index 030295d3d8d..e9e3307ca95 100644
--- a/compiler/rustc_builtin_macros/src/asm.rs
+++ b/compiler/rustc_builtin_macros/src/asm.rs
@@ -1,6 +1,6 @@
 use rustc_ast as ast;
 use rustc_ast::ptr::P;
-use rustc_ast::token;
+use rustc_ast::token::{self, Delimiter};
 use rustc_ast::tokenstream::TokenStream;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_errors::{Applicability, PResult};
@@ -395,9 +395,9 @@ fn parse_options<'a>(
 ) -> PResult<'a, ()> {
     let span_start = p.prev_token.span;
 
-    p.expect(&token::OpenDelim(token::DelimToken::Paren))?;
+    p.expect(&token::OpenDelim(Delimiter::Parenthesis))?;
 
-    while !p.eat(&token::CloseDelim(token::DelimToken::Paren)) {
+    while !p.eat(&token::CloseDelim(Delimiter::Parenthesis)) {
         if !is_global_asm && p.eat_keyword(sym::pure) {
             try_set_option(p, args, sym::pure, ast::InlineAsmOptions::PURE);
         } else if !is_global_asm && p.eat_keyword(sym::nomem) {
@@ -421,7 +421,7 @@ fn parse_options<'a>(
         }
 
         // Allow trailing commas
-        if p.eat(&token::CloseDelim(token::DelimToken::Paren)) {
+        if p.eat(&token::CloseDelim(Delimiter::Parenthesis)) {
             break;
         }
         p.expect(&token::Comma)?;
@@ -436,9 +436,9 @@ fn parse_options<'a>(
 fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a, ()> {
     let span_start = p.prev_token.span;
 
-    p.expect(&token::OpenDelim(token::DelimToken::Paren))?;
+    p.expect(&token::OpenDelim(Delimiter::Parenthesis))?;
 
-    if p.eat(&token::CloseDelim(token::DelimToken::Paren)) {
+    if p.eat(&token::CloseDelim(Delimiter::Parenthesis)) {
         let err = p.sess.span_diagnostic.struct_span_err(
             p.token.span,
             "at least one abi must be provided as an argument to `clobber_abi`",
@@ -454,7 +454,7 @@ fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a,
             }
             Err(opt_lit) => {
                 // If the non-string literal is a closing paren then it's the end of the list and is fine
-                if p.eat(&token::CloseDelim(token::DelimToken::Paren)) {
+                if p.eat(&token::CloseDelim(Delimiter::Parenthesis)) {
                     break;
                 }
                 let span = opt_lit.map_or(p.token.span, |lit| lit.span);
@@ -466,7 +466,7 @@ fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a,
         };
 
         // Allow trailing commas
-        if p.eat(&token::CloseDelim(token::DelimToken::Paren)) {
+        if p.eat(&token::CloseDelim(Delimiter::Parenthesis)) {
             break;
         }
         p.expect(&token::Comma)?;
@@ -501,7 +501,7 @@ fn parse_reg<'a>(
     p: &mut Parser<'a>,
     explicit_reg: &mut bool,
 ) -> PResult<'a, ast::InlineAsmRegOrRegClass> {
-    p.expect(&token::OpenDelim(token::DelimToken::Paren))?;
+    p.expect(&token::OpenDelim(Delimiter::Parenthesis))?;
     let result = match p.token.uninterpolate().kind {
         token::Ident(name, false) => ast::InlineAsmRegOrRegClass::RegClass(name),
         token::Literal(token::Lit { kind: token::LitKind::Str, symbol, suffix: _ }) => {
@@ -515,7 +515,7 @@ fn parse_reg<'a>(
         }
     };
     p.bump();
-    p.expect(&token::CloseDelim(token::DelimToken::Paren))?;
+    p.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
     Ok(result)
 }
 
diff --git a/compiler/rustc_builtin_macros/src/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs
index 61681ec66a4..391c46d1813 100644
--- a/compiler/rustc_builtin_macros/src/derive.rs
+++ b/compiler/rustc_builtin_macros/src/derive.rs
@@ -95,6 +95,7 @@ fn dummy_annotatable() -> Annotatable {
         bounds: Default::default(),
         is_placeholder: false,
         kind: GenericParamKind::Lifetime,
+        colon_span: None,
     })
 }
 
diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs
index 2c5260616c7..b49331e2875 100644
--- a/compiler/rustc_builtin_macros/src/deriving/default.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/default.rs
@@ -235,7 +235,7 @@ fn validate_default_attribute(
             .span_suggestion_hidden(
                 attr.span,
                 "try using `#[default]`",
-                "#[default]".into(),
+                "#[default]",
                 Applicability::MaybeIncorrect,
             )
             .emit();
diff --git a/compiler/rustc_codegen_cranelift/Cargo.lock b/compiler/rustc_codegen_cranelift/Cargo.lock
index 30e9627c48d..7b8e43b639f 100644
--- a/compiler/rustc_codegen_cranelift/Cargo.lock
+++ b/compiler/rustc_codegen_cranelift/Cargo.lock
@@ -33,18 +33,18 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
 name = "cranelift-bforest"
-version = "0.82.1"
+version = "0.83.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d16922317bd7dd104d509a373887822caa0242fc1def00de66abb538db221db4"
+checksum = "ed44413e7e2fe3260d0ed73e6956ab188b69c10ee92b892e401e0f4f6808c68b"
 dependencies = [
  "cranelift-entity",
 ]
 
 [[package]]
 name = "cranelift-codegen"
-version = "0.82.1"
+version = "0.83.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b80bf40380256307b68a3dcbe1b91cac92a533e212b5b635abc3e4525781a0a"
+checksum = "0b5d83f0f26bf213f971f45589d17e5b65e4861f9ed22392b0cbb6eaa5bd329c"
 dependencies = [
  "cranelift-bforest",
  "cranelift-codegen-meta",
@@ -59,30 +59,30 @@ dependencies = [
 
 [[package]]
 name = "cranelift-codegen-meta"
-version = "0.82.1"
+version = "0.83.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "703d0ed7d3bc6c7a814ca12858175bf4e93167a3584127858c686e4b5dd6e432"
+checksum = "6800dc386177df6ecc5a32680607ed8ba1fa0d31a2a59c8c61fbf44826b8191d"
 dependencies = [
  "cranelift-codegen-shared",
 ]
 
 [[package]]
 name = "cranelift-codegen-shared"
-version = "0.82.1"
+version = "0.83.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "80f52311e1c90de12dcf8c4b9999c6ebfd1ed360373e88c357160936844511f6"
+checksum = "c961f85070985ebc8fcdb81b838a5cf842294d1e6ed4852446161c7e246fd455"
 
 [[package]]
 name = "cranelift-entity"
-version = "0.82.1"
+version = "0.83.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "66bc82ef522c1f643baf7d4d40b7c52643ee4549d8960b0e6a047daacb83f897"
+checksum = "2347b2b8d1d5429213668f2a8e36c85ee3c73984a2f6a79007e365d3e575e7ed"
 
 [[package]]
 name = "cranelift-frontend"
-version = "0.82.1"
+version = "0.83.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3cc35e4251864b17515845ba47447bca88fec9ca1a4186b19fe42526e36140e8"
+checksum = "4cbcdbf7bed29e363568b778649b69dabc3d727256d5d25236096ef693757654"
 dependencies = [
  "cranelift-codegen",
  "log",
@@ -92,9 +92,9 @@ dependencies = [
 
 [[package]]
 name = "cranelift-jit"
-version = "0.82.1"
+version = "0.83.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93c66d594ad3bfe4e58b1fbd8d17877a7c6564a5f2d6f78cbbf4b0182af1927f"
+checksum = "7c769d4e0d76f59c8b2a3bf0477d89ee149bb0731b53fbb245ee081d49063095"
 dependencies = [
  "anyhow",
  "cranelift-codegen",
@@ -110,9 +110,9 @@ dependencies = [
 
 [[package]]
 name = "cranelift-module"
-version = "0.82.1"
+version = "0.83.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf356697c40232aa09e1e3fb8a350ee894e849ccecc4eac56ff0570a4575c325"
+checksum = "0ab57d399a2401074bb0cc40b3031e420f3d66d46ec0cf21eeae53ac04bd73e2"
 dependencies = [
  "anyhow",
  "cranelift-codegen",
@@ -120,9 +120,9 @@ dependencies = [
 
 [[package]]
 name = "cranelift-native"
-version = "0.82.1"
+version = "0.83.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b882b2251c9845d509d92aebfdb6c8bb3b3b48e207ac951f21fbd20cfe7f90b3"
+checksum = "8f4cdf93552e5ceb2e3c042829ebb4de4378492705f769eadc6a7c6c5251624c"
 dependencies = [
  "cranelift-codegen",
  "libc",
@@ -131,9 +131,9 @@ dependencies = [
 
 [[package]]
 name = "cranelift-object"
-version = "0.82.1"
+version = "0.83.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2d3f1a88e654e567d2591169239ed157ab290811a729a6468f53999c01001263"
+checksum = "cf8e65f4839c26e6237fc0744911d79b0a2ac5e76b4e4eebd14db2b8d849fd31"
 dependencies = [
  "anyhow",
  "cranelift-codegen",
diff --git a/compiler/rustc_codegen_cranelift/Cargo.toml b/compiler/rustc_codegen_cranelift/Cargo.toml
index 70c03da3f29..74f50808a98 100644
--- a/compiler/rustc_codegen_cranelift/Cargo.toml
+++ b/compiler/rustc_codegen_cranelift/Cargo.toml
@@ -8,12 +8,12 @@ crate-type = ["dylib"]
 
 [dependencies]
 # These have to be in sync with each other
-cranelift-codegen = { version = "0.82.1", features = ["unwind", "all-arch"] }
-cranelift-frontend = "0.82.1"
-cranelift-module = "0.82.1"
-cranelift-native = "0.82.1"
-cranelift-jit = { version = "0.82.1", optional = true }
-cranelift-object = "0.82.1"
+cranelift-codegen = { version = "0.83.0", features = ["unwind", "all-arch"] }
+cranelift-frontend = "0.83.0"
+cranelift-module = "0.83.0"
+cranelift-native = "0.83.0"
+cranelift-jit = { version = "0.83.0", optional = true }
+cranelift-object = "0.83.0"
 target-lexicon = "0.12.0"
 gimli = { version = "0.26.0", default-features = false, features = ["write"]}
 object = { version = "0.27.0", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] }
diff --git a/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock b/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock
index f584f54e106..51ba0dbfcc7 100644
--- a/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock
+++ b/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock
@@ -56,9 +56,9 @@ dependencies = [
 
 [[package]]
 name = "compiler_builtins"
-version = "0.1.71"
+version = "0.1.72"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "163437f05ca8f29d7e9128ea728dedf5eb620e445fbca273641d3a3050305f23"
+checksum = "afdbb35d279238cf77f0c9e8d90ad50d6c7bff476ab342baafa29440f0f10bff"
 dependencies = [
  "rustc-std-workspace-core",
 ]
@@ -134,9 +134,9 @@ dependencies = [
 
 [[package]]
 name = "libc"
-version = "0.2.121"
+version = "0.2.124"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f"
+checksum = "21a41fed9d98f27ab1c6d161da622a4fa35e8a54a8adc24bbf3ddd0ef70b0e50"
 dependencies = [
  "rustc-std-workspace-core",
 ]
@@ -203,6 +203,7 @@ dependencies = [
 name = "proc_macro"
 version = "0.0.0"
 dependencies = [
+ "core",
  "std",
 ]
 
diff --git a/compiler/rustc_codegen_cranelift/example/float-minmax-pass.rs b/compiler/rustc_codegen_cranelift/example/float-minmax-pass.rs
new file mode 100644
index 00000000000..b8f901d1ba1
--- /dev/null
+++ b/compiler/rustc_codegen_cranelift/example/float-minmax-pass.rs
@@ -0,0 +1,53 @@
+// Copied from https://github.com/rust-lang/rust/blob/3fe3b89cd57229343eeca753fdd8c63d9b03c65c/src/test/ui/simd/intrinsic/float-minmax-pass.rs
+// run-pass
+// ignore-emscripten
+
+// Test that the simd_f{min,max} intrinsics produce the correct results.
+
+#![feature(repr_simd, platform_intrinsics)]
+#![allow(non_camel_case_types)]
+
+#[repr(simd)]
+#[derive(Copy, Clone, PartialEq, Debug)]
+struct f32x4(pub f32, pub f32, pub f32, pub f32);
+
+extern "platform-intrinsic" {
+    fn simd_fmin<T>(x: T, y: T) -> T;
+    fn simd_fmax<T>(x: T, y: T) -> T;
+}
+
+fn main() {
+    let x = f32x4(1.0, 2.0, 3.0, 4.0);
+    let y = f32x4(2.0, 1.0, 4.0, 3.0);
+
+    #[cfg(not(any(target_arch = "mips", target_arch = "mips64")))]
+    let nan = f32::NAN;
+    // MIPS hardware treats f32::NAN as SNAN. Clear the signaling bit.
+    // See https://github.com/rust-lang/rust/issues/52746.
+    #[cfg(any(target_arch = "mips", target_arch = "mips64"))]
+    let nan = f32::from_bits(f32::NAN.to_bits() - 1);
+
+    let n = f32x4(nan, nan, nan, nan);
+
+    unsafe {
+        let min0 = simd_fmin(x, y);
+        let min1 = simd_fmin(y, x);
+        assert_eq!(min0, min1);
+        let e = f32x4(1.0, 1.0, 3.0, 3.0);
+        assert_eq!(min0, e);
+        let minn = simd_fmin(x, n);
+        assert_eq!(minn, x);
+        let minn = simd_fmin(y, n);
+        assert_eq!(minn, y);
+
+        let max0 = simd_fmax(x, y);
+        let max1 = simd_fmax(y, x);
+        assert_eq!(max0, max1);
+        let e = f32x4(2.0, 2.0, 4.0, 4.0);
+        assert_eq!(max0, e);
+        let maxn = simd_fmax(x, n);
+        assert_eq!(maxn, x);
+        let maxn = simd_fmax(y, n);
+        assert_eq!(maxn, y);
+    }
+}
diff --git a/compiler/rustc_codegen_cranelift/example/mini_core.rs b/compiler/rustc_codegen_cranelift/example/mini_core.rs
index 7efc8dc785a..8da705e0cb0 100644
--- a/compiler/rustc_codegen_cranelift/example/mini_core.rs
+++ b/compiler/rustc_codegen_cranelift/example/mini_core.rs
@@ -16,6 +16,9 @@
 #[lang = "sized"]
 pub trait Sized {}
 
+#[lang = "destruct"]
+pub trait Destruct {}
+
 #[lang = "unsize"]
 pub trait Unsize<T: ?Sized> {}
 
@@ -491,13 +494,20 @@ pub trait Deref {
     fn deref(&self) -> &Self::Target;
 }
 
+#[repr(transparent)]
+#[rustc_layout_scalar_valid_range_start(1)]
+#[rustc_nonnull_optimization_guaranteed]
+pub struct NonNull<T: ?Sized>(pub *mut T);
+
+impl<T: ?Sized, U: ?Sized> CoerceUnsized<NonNull<U>> for NonNull<T> where T: Unsize<U> {}
+impl<T: ?Sized, U: ?Sized> DispatchFromDyn<NonNull<U>> for NonNull<T> where T: Unsize<U> {}
+
 pub struct Unique<T: ?Sized> {
-    pub pointer: *const T,
+    pub pointer: NonNull<T>,
     pub _marker: PhantomData<T>,
 }
 
 impl<T: ?Sized, U: ?Sized> CoerceUnsized<Unique<U>> for Unique<T> where T: Unsize<U> {}
-
 impl<T: ?Sized, U: ?Sized> DispatchFromDyn<Unique<U>> for Unique<T> where T: Unsize<U> {}
 
 #[lang = "owned_box"]
@@ -526,7 +536,7 @@ unsafe fn allocate(size: usize, _align: usize) -> *mut u8 {
 
 #[lang = "box_free"]
 unsafe fn box_free<T: ?Sized>(ptr: Unique<T>, alloc: ()) {
-    libc::free(ptr.pointer as *mut u8);
+    libc::free(ptr.pointer.0 as *mut u8);
 }
 
 #[lang = "drop"]
diff --git a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs
index c4730581335..85ca908d0a2 100644
--- a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs
+++ b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs
@@ -122,7 +122,7 @@ fn call_return_u128_pair() {
 #[allow(unreachable_code)] // FIXME false positive
 fn main() {
     take_unique(Unique {
-        pointer: 0 as *const (),
+        pointer: unsafe { NonNull(1 as *mut ()) },
         _marker: PhantomData,
     });
     take_f32(0.1);
@@ -173,7 +173,7 @@ fn main() {
         assert!(intrinsics::needs_drop::<NoisyDrop>());
 
         Unique {
-            pointer: 0 as *const &str,
+            pointer: NonNull(1 as *mut &str),
             _marker: PhantomData,
         } as Unique<dyn SomeTrait>;
 
diff --git a/compiler/rustc_codegen_cranelift/example/std_example.rs b/compiler/rustc_codegen_cranelift/example/std_example.rs
index 5bc51a541b5..0a2bce2621d 100644
--- a/compiler/rustc_codegen_cranelift/example/std_example.rs
+++ b/compiler/rustc_codegen_cranelift/example/std_example.rs
@@ -1,7 +1,8 @@
-#![feature(core_intrinsics, generators, generator_trait, is_sorted)]
+#![feature(core_intrinsics, generators, generator_trait, is_sorted, bench_black_box)]
 
 #[cfg(target_arch = "x86_64")]
 use std::arch::x86_64::*;
+use std::hint::black_box;
 use std::io::Write;
 use std::ops::Generator;
 
@@ -86,6 +87,9 @@ fn main() {
     assert_eq!(houndred_f64 as i128, 100);
     assert_eq!(1u128.rotate_left(2), 4);
 
+    assert_eq!(black_box(f32::NAN) as i128, 0);
+    assert_eq!(black_box(f32::NAN) as u128, 0);
+
     // Test signed 128bit comparing
     let max = usize::MAX as i128;
     if 100i128 < 0i128 || 100i128 > max {
diff --git a/compiler/rustc_codegen_cranelift/patches/0001-portable-simd-Disable-unsupported-tests.patch b/compiler/rustc_codegen_cranelift/patches/0001-portable-simd-Disable-unsupported-tests.patch
index c1325908691..54e13b090ab 100644
--- a/compiler/rustc_codegen_cranelift/patches/0001-portable-simd-Disable-unsupported-tests.patch
+++ b/compiler/rustc_codegen_cranelift/patches/0001-portable-simd-Disable-unsupported-tests.patch
@@ -102,42 +102,6 @@ index 6a8ecd3..68fcb49 100644
          }
      }
  }
-diff --git a/crates/core_simd/tests/ops_macros.rs b/crates/core_simd/tests/ops_macros.rs
-index 31b7ee2..bd04b3c 100644
---- a/crates/core_simd/tests/ops_macros.rs
-+++ b/crates/core_simd/tests/ops_macros.rs
-@@ -567,6 +567,7 @@ macro_rules! impl_float_tests {
-                     });
-                 }
- 
-+                /*
-                 fn horizontal_max<const LANES: usize>() {
-                     test_helpers::test_1(&|x| {
-                         let vmax = Vector::<LANES>::from_array(x).horizontal_max();
-@@ -590,6 +591,7 @@ macro_rules! impl_float_tests {
-                         Ok(())
-                     });
-                 }
-+                */
-             }
- 
-             #[cfg(feature = "std")]
-@@ -604,6 +606,7 @@ macro_rules! impl_float_tests {
-                         )
-                     }
- 
-+                    /*
-                     fn mul_add<const LANES: usize>() {
-                         test_helpers::test_ternary_elementwise(
-                             &Vector::<LANES>::mul_add,
-@@ -611,6 +614,7 @@ macro_rules! impl_float_tests {
-                             &|_, _, _| true,
-                         )
-                     }
-+                    */
-                 }
-             }
-         }
 -- 
 2.26.2.7.g19db9cfb68
 
diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain b/compiler/rustc_codegen_cranelift/rust-toolchain
index 84d90e5db02..966097c248b 100644
--- a/compiler/rustc_codegen_cranelift/rust-toolchain
+++ b/compiler/rustc_codegen_cranelift/rust-toolchain
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2022-03-19"
+channel = "nightly-2022-04-21"
 components = ["rust-src", "rustc-dev", "llvm-tools-preview"]
diff --git a/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs b/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs
index a0e99267c2b..f4e863e5494 100755
--- a/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs
+++ b/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 #![forbid(unsafe_code)]/* This line is ignored by bash
 # This block is ignored by rustc
 pushd $(dirname "$0")/../
diff --git a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh
index 85c0109c6f6..cabbaaa8922 100644
--- a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh
+++ b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 
 ./y.rs build --no-unstable-features
diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh
index a32e6df2208..4cf24c02235 100755
--- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh
+++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 
 cd $(dirname "$0")/../
@@ -11,7 +11,7 @@ pushd rust
 command -v rg >/dev/null 2>&1 || cargo install ripgrep
 
 rm -r src/test/ui/{extern/,unsized-locals/,lto/,linkage*} || true
-for test in $(rg --files-with-matches "asm!|lto|// needs-asm-support|// needs-unwind" src/test/{ui,incremental}); do
+for test in $(rg --files-with-matches "lto|// needs-asm-support|// needs-unwind" src/test/{ui,incremental}); do
   rm $test
 done
 
@@ -25,14 +25,8 @@ git checkout -- src/test/ui/issues/auxiliary/issue-3136-a.rs # contains //~ERROR
 # ================
 
 # requires stack unwinding
-rm src/test/ui/backtrace.rs
-rm src/test/ui/process/multi-panic.rs
-rm src/test/ui/numbers-arithmetic/issue-8460.rs
 rm src/test/incremental/change_crate_dep_kind.rs
 rm src/test/incremental/issue-80691-bad-eval-cache.rs # -Cpanic=abort causes abort instead of exit(101)
-rm src/test/ui/panic-while-printing.rs
-rm src/test/ui/test-attrs/test-panic-while-printing.rs
-rm src/test/ui/test-attrs/test-type.rs
 
 # requires compiling with -Cpanic=unwind
 rm src/test/ui/test-attrs/test-fn-signature-verification-for-explicit-return-type.rs # "Cannot run dynamic test fn out-of-process"
@@ -85,8 +79,6 @@ rm src/test/ui/abi/stack-protector.rs # requires stack protector support
 
 # giving different but possibly correct results
 # =============================================
-rm src/test/ui/numbers-arithmetic/saturating-float-casts.rs # intrinsic gives different but valid result
-rm src/test/ui/simd/intrinsic/float-minmax-pass.rs # same
 rm src/test/ui/mir/mir_misc_casts.rs # depends on deduplication of constants
 rm src/test/ui/mir/mir_raw_fat_ptr.rs # same
 rm src/test/ui/consts/issue-33537.rs # same
@@ -112,9 +104,14 @@ rm src/test/ui/mir/ssa-analysis-regression-50041.rs # produces ICE
 
 rm src/test/ui/simd/intrinsic/generic-reduction-pass.rs # simd_reduce_add_unordered doesn't accept an accumulator for integer vectors
 
+rm src/test/ui/rfc-2091-track-caller/intrinsic-wrapper.rs # wrong result from `Location::caller()`
+
 # bugs in the test suite
 # ======================
-rm src/test/ui/unsafe/union.rs # has UB caught by cg_clif. see rust-lang/rust#95075
+rm src/test/ui/backtrace.rs # TODO warning
+rm src/test/ui/empty_global_asm.rs # TODO add needs-asm-support
+rm src/test/ui/simple_global_asm.rs # TODO add needs-asm-support
+rm src/test/ui/test-attrs/test-type.rs # TODO panic message on stderr. correct stdout
 
 echo "[TEST] rustc test suite"
 RUST_TEST_NOCAPTURE=1 COMPILETEST_FORCE_STAGE0=1 ./x.py test --stage 0 src/test/{codegen-units,run-make,run-pass-valgrind,ui,incremental}
diff --git a/compiler/rustc_codegen_cranelift/scripts/tests.sh b/compiler/rustc_codegen_cranelift/scripts/tests.sh
index fee1012c8f1..aae626081f6 100755
--- a/compiler/rustc_codegen_cranelift/scripts/tests.sh
+++ b/compiler/rustc_codegen_cranelift/scripts/tests.sh
@@ -72,6 +72,10 @@ function base_sysroot_tests() {
     $MY_RUSTC example/track-caller-attribute.rs --crate-type bin -Cpanic=abort --target "$TARGET_TRIPLE"
     $RUN_WRAPPER ./target/out/track-caller-attribute
 
+    echo "[AOT] float-minmax-pass"
+    $MY_RUSTC example/float-minmax-pass.rs --crate-type bin -Cpanic=abort --target "$TARGET_TRIPLE"
+    $RUN_WRAPPER ./target/out/float-minmax-pass
+
     echo "[AOT] mod_bench"
     $MY_RUSTC example/mod_bench.rs --crate-type bin --target "$TARGET_TRIPLE"
     $RUN_WRAPPER ./target/out/mod_bench
diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs
index 8c45993a8b7..65346cb3962 100644
--- a/compiler/rustc_codegen_cranelift/src/base.rs
+++ b/compiler/rustc_codegen_cranelift/src/base.rs
@@ -821,7 +821,8 @@ pub(crate) fn codegen_place<'tcx>(
                 if cplace.layout().ty.is_box() {
                     cplace = cplace
                         .place_field(fx, Field::new(0)) // Box<T> -> Unique<T>
-                        .place_field(fx, Field::new(0)) // Unique<T> -> *const T
+                        .place_field(fx, Field::new(0)) // Unique<T> -> NonNull<T>
+                        .place_field(fx, Field::new(0)) // NonNull<T> -> *mut T
                         .place_deref(fx);
                 } else {
                     cplace = cplace.place_deref(fx);
diff --git a/compiler/rustc_codegen_cranelift/src/cast.rs b/compiler/rustc_codegen_cranelift/src/cast.rs
index e7e6afeb865..e19070774c6 100644
--- a/compiler/rustc_codegen_cranelift/src/cast.rs
+++ b/compiler/rustc_codegen_cranelift/src/cast.rs
@@ -84,7 +84,7 @@ pub(crate) fn clif_int_or_float_cast(
             fx.bcx.ins().fcvt_from_uint(to_ty, from)
         }
     } else if from_ty.is_float() && to_ty.is_int() {
-        if to_ty == types::I128 {
+        let val = if to_ty == types::I128 {
             // _____sssf___
             // __fix   sfti: f32 -> i128
             // __fix   dfti: f64 -> i128
@@ -109,13 +109,9 @@ pub(crate) fn clif_int_or_float_cast(
 
             let to_rust_ty = if to_signed { fx.tcx.types.i128 } else { fx.tcx.types.u128 };
 
-            return fx
-                .easy_call(&name, &[CValue::by_val(from, fx.layout_of(from_rust_ty))], to_rust_ty)
-                .load_scalar(fx);
-        }
-
-        // float -> int-like
-        if to_ty == types::I8 || to_ty == types::I16 {
+            fx.easy_call(&name, &[CValue::by_val(from, fx.layout_of(from_rust_ty))], to_rust_ty)
+                .load_scalar(fx)
+        } else if to_ty == types::I8 || to_ty == types::I16 {
             // FIXME implement fcvt_to_*int_sat.i8/i16
             let val = if to_signed {
                 fx.bcx.ins().fcvt_to_sint_sat(types::I32, from)
@@ -146,6 +142,23 @@ pub(crate) fn clif_int_or_float_cast(
             fx.bcx.ins().fcvt_to_sint_sat(to_ty, from)
         } else {
             fx.bcx.ins().fcvt_to_uint_sat(to_ty, from)
+        };
+
+        if let Some(false) = fx.tcx.sess.opts.debugging_opts.saturating_float_casts {
+            return val;
+        }
+
+        let is_not_nan = fx.bcx.ins().fcmp(FloatCC::Equal, from, from);
+        if to_ty == types::I128 {
+            // FIXME(bytecodealliance/wasmtime#3963): select.i128 on fcmp eq miscompiles
+            let (lsb, msb) = fx.bcx.ins().isplit(val);
+            let zero = fx.bcx.ins().iconst(types::I64, 0);
+            let lsb = fx.bcx.ins().select(is_not_nan, lsb, zero);
+            let msb = fx.bcx.ins().select(is_not_nan, msb, zero);
+            fx.bcx.ins().iconcat(lsb, msb)
+        } else {
+            let zero = fx.bcx.ins().iconst(to_ty, 0);
+            fx.bcx.ins().select(is_not_nan, val, zero)
         }
     } else if from_ty.is_float() && to_ty.is_float() {
         // float -> float
diff --git a/compiler/rustc_codegen_cranelift/src/config.rs b/compiler/rustc_codegen_cranelift/src/config.rs
index eef3c8c8d6e..e59a0cb0a23 100644
--- a/compiler/rustc_codegen_cranelift/src/config.rs
+++ b/compiler/rustc_codegen_cranelift/src/config.rs
@@ -48,12 +48,6 @@ pub struct BackendConfig {
     /// Can be set using `-Cllvm-args=display_cg_time=...`.
     pub display_cg_time: bool,
 
-    /// The register allocator to use.
-    ///
-    /// Defaults to the value of `CG_CLIF_REGALLOC` or `backtracking` otherwise. Can be set using
-    /// `-Cllvm-args=regalloc=...`.
-    pub regalloc: String,
-
     /// Enable the Cranelift ir verifier for all compilation passes. If not set it will only run
     /// once before passing the clif ir to Cranelift for compilation.
     ///
@@ -80,8 +74,6 @@ impl Default for BackendConfig {
                 args.split(' ').map(|arg| arg.to_string()).collect()
             },
             display_cg_time: bool_env_var("CG_CLIF_DISPLAY_CG_TIME"),
-            regalloc: std::env::var("CG_CLIF_REGALLOC")
-                .unwrap_or_else(|_| "backtracking".to_string()),
             enable_verifier: cfg!(debug_assertions) || bool_env_var("CG_CLIF_ENABLE_VERIFIER"),
             disable_incr_cache: bool_env_var("CG_CLIF_DISABLE_INCR_CACHE"),
         }
@@ -101,7 +93,6 @@ impl BackendConfig {
                 match name {
                     "mode" => config.codegen_mode = value.parse()?,
                     "display_cg_time" => config.display_cg_time = parse_bool(name, value)?,
-                    "regalloc" => config.regalloc = value.to_string(),
                     "enable_verifier" => config.enable_verifier = parse_bool(name, value)?,
                     "disable_incr_cache" => config.disable_incr_cache = parse_bool(name, value)?,
                     _ => return Err(format!("Unknown option `{}`", name)),
diff --git a/compiler/rustc_codegen_cranelift/src/discriminant.rs b/compiler/rustc_codegen_cranelift/src/discriminant.rs
index 357cb4a6d24..f619bb5ed5e 100644
--- a/compiler/rustc_codegen_cranelift/src/discriminant.rs
+++ b/compiler/rustc_codegen_cranelift/src/discriminant.rs
@@ -128,8 +128,16 @@ pub(crate) fn codegen_get_discriminant<'tcx>(
             let relative_discr = if niche_start == 0 {
                 tag
             } else {
-                // FIXME handle niche_start > i64::MAX
-                fx.bcx.ins().iadd_imm(tag, -i64::try_from(niche_start).unwrap())
+                let niche_start = match fx.bcx.func.dfg.value_type(tag) {
+                    types::I128 => {
+                        let lsb = fx.bcx.ins().iconst(types::I64, niche_start as u64 as i64);
+                        let msb =
+                            fx.bcx.ins().iconst(types::I64, (niche_start >> 64) as u64 as i64);
+                        fx.bcx.ins().iconcat(lsb, msb)
+                    }
+                    ty => fx.bcx.ins().iconst(ty, niche_start as i64),
+                };
+                fx.bcx.ins().isub(tag, niche_start)
             };
             let relative_max = niche_variants.end().as_u32() - niche_variants.start().as_u32();
             let is_niche = {
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
index 310d27c6dec..d76dfca7960 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
@@ -1019,39 +1019,23 @@ fn codegen_regular_intrinsic_call<'tcx>(
             ret.write_cvalue(fx, old);
         };
 
-        // In Rust floating point min and max don't propagate NaN. In Cranelift they do however.
-        // For this reason it is necessary to use `a.is_nan() ? b : (a >= b ? b : a)` for `minnumf*`
-        // and `a.is_nan() ? b : (a <= b ? b : a)` for `maxnumf*`. NaN checks are done by comparing
-        // a float against itself. Only in case of NaN is it not equal to itself.
         minnumf32, (v a, v b) {
-            let a_is_nan = fx.bcx.ins().fcmp(FloatCC::NotEqual, a, a);
-            let a_ge_b = fx.bcx.ins().fcmp(FloatCC::GreaterThanOrEqual, a, b);
-            let temp = fx.bcx.ins().select(a_ge_b, b, a);
-            let val = fx.bcx.ins().select(a_is_nan, b, temp);
+            let val = crate::num::codegen_float_min(fx, a, b);
             let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f32));
             ret.write_cvalue(fx, val);
         };
         minnumf64, (v a, v b) {
-            let a_is_nan = fx.bcx.ins().fcmp(FloatCC::NotEqual, a, a);
-            let a_ge_b = fx.bcx.ins().fcmp(FloatCC::GreaterThanOrEqual, a, b);
-            let temp = fx.bcx.ins().select(a_ge_b, b, a);
-            let val = fx.bcx.ins().select(a_is_nan, b, temp);
+            let val = crate::num::codegen_float_min(fx, a, b);
             let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f64));
             ret.write_cvalue(fx, val);
         };
         maxnumf32, (v a, v b) {
-            let a_is_nan = fx.bcx.ins().fcmp(FloatCC::NotEqual, a, a);
-            let a_le_b = fx.bcx.ins().fcmp(FloatCC::LessThanOrEqual, a, b);
-            let temp = fx.bcx.ins().select(a_le_b, b, a);
-            let val = fx.bcx.ins().select(a_is_nan, b, temp);
+            let val = crate::num::codegen_float_max(fx, a, b);
             let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f32));
             ret.write_cvalue(fx, val);
         };
         maxnumf64, (v a, v b) {
-            let a_is_nan = fx.bcx.ins().fcmp(FloatCC::NotEqual, a, a);
-            let a_le_b = fx.bcx.ins().fcmp(FloatCC::LessThanOrEqual, a, b);
-            let temp = fx.bcx.ins().select(a_le_b, b, a);
-            let val = fx.bcx.ins().select(a_is_nan, b, temp);
+            let val = crate::num::codegen_float_max(fx, a, b);
             let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f64));
             ret.write_cvalue(fx, val);
         };
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs
index bc21d736166..d1ca9edf2e0 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs
@@ -322,20 +322,21 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
             }
             assert_eq!(a.layout(), b.layout());
             assert_eq!(a.layout(), c.layout());
-            let layout = a.layout();
+            assert_eq!(a.layout(), ret.layout());
 
-            let (lane_count, _lane_ty) = layout.ty.simd_size_and_type(fx.tcx);
-            let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx);
-            assert_eq!(lane_count, ret_lane_count);
-            let ret_lane_layout = fx.layout_of(ret_lane_ty);
+            let layout = a.layout();
+            let (lane_count, lane_ty) = layout.ty.simd_size_and_type(fx.tcx);
 
             for lane in 0..lane_count {
-                let a_lane = a.value_lane(fx, lane).load_scalar(fx);
-                let b_lane = b.value_lane(fx, lane).load_scalar(fx);
-                let c_lane = c.value_lane(fx, lane).load_scalar(fx);
+                let a_lane = a.value_lane(fx, lane);
+                let b_lane = b.value_lane(fx, lane);
+                let c_lane = c.value_lane(fx, lane);
 
-                let mul_lane = fx.bcx.ins().fmul(a_lane, b_lane);
-                let res_lane = CValue::by_val(fx.bcx.ins().fadd(mul_lane, c_lane), ret_lane_layout);
+                let res_lane = match lane_ty.kind() {
+                    ty::Float(FloatTy::F32) => fx.easy_call("fmaf", &[a_lane, b_lane, c_lane], lane_ty),
+                    ty::Float(FloatTy::F64) => fx.easy_call("fma", &[a_lane, b_lane, c_lane], lane_ty),
+                    _ => unreachable!(),
+                };
 
                 ret.place_lane(fx, lane).write_cvalue(fx, res_lane);
             }
@@ -354,8 +355,8 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
                     _ => unreachable!("{:?}", lane_ty),
                 }
                 match intrinsic {
-                    sym::simd_fmin => fx.bcx.ins().fmin(x_lane, y_lane),
-                    sym::simd_fmax => fx.bcx.ins().fmax(x_lane, y_lane),
+                    sym::simd_fmin => crate::num::codegen_float_min(fx, x_lane, y_lane),
+                    sym::simd_fmax => crate::num::codegen_float_max(fx, x_lane, y_lane),
                     _ => unreachable!(),
                 }
             });
@@ -495,7 +496,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
                 let lt = match ty.kind() {
                     ty::Int(_) => fx.bcx.ins().icmp(IntCC::SignedLessThan, a, b),
                     ty::Uint(_) => fx.bcx.ins().icmp(IntCC::UnsignedLessThan, a, b),
-                    ty::Float(_) => fx.bcx.ins().fcmp(FloatCC::LessThan, a, b),
+                    ty::Float(_) => return crate::num::codegen_float_min(fx, a, b),
                     _ => unreachable!(),
                 };
                 fx.bcx.ins().select(lt, a, b)
@@ -512,7 +513,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
                 let gt = match ty.kind() {
                     ty::Int(_) => fx.bcx.ins().icmp(IntCC::SignedGreaterThan, a, b),
                     ty::Uint(_) => fx.bcx.ins().icmp(IntCC::UnsignedGreaterThan, a, b),
-                    ty::Float(_) => fx.bcx.ins().fcmp(FloatCC::GreaterThan, a, b),
+                    ty::Float(_) => return crate::num::codegen_float_max(fx, a, b),
                     _ => unreachable!(),
                 };
                 fx.bcx.ins().select(gt, a, b)
diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs
index 878b9390e13..9d2e12f9898 100644
--- a/compiler/rustc_codegen_cranelift/src/lib.rs
+++ b/compiler/rustc_codegen_cranelift/src/lib.rs
@@ -256,8 +256,6 @@ fn build_isa(sess: &Session, backend_config: &BackendConfig) -> Box<dyn isa::Tar
 
     flags_builder.set("enable_llvm_abi_extensions", "true").unwrap();
 
-    flags_builder.set("regalloc", &backend_config.regalloc).unwrap();
-
     use rustc_session::config::OptLevel;
     match sess.opts.optimize {
         OptLevel::No => {
diff --git a/compiler/rustc_codegen_cranelift/src/num.rs b/compiler/rustc_codegen_cranelift/src/num.rs
index 545d390e269..4ce8adb182e 100644
--- a/compiler/rustc_codegen_cranelift/src/num.rs
+++ b/compiler/rustc_codegen_cranelift/src/num.rs
@@ -420,3 +420,21 @@ pub(crate) fn codegen_ptr_binop<'tcx>(
         CValue::by_val(fx.bcx.ins().bint(types::I8, res), fx.layout_of(fx.tcx.types.bool))
     }
 }
+
+// In Rust floating point min and max don't propagate NaN. In Cranelift they do however.
+// For this reason it is necessary to use `a.is_nan() ? b : (a >= b ? b : a)` for `minnumf*`
+// and `a.is_nan() ? b : (a <= b ? b : a)` for `maxnumf*`. NaN checks are done by comparing
+// a float against itself. Only in case of NaN is it not equal to itself.
+pub(crate) fn codegen_float_min(fx: &mut FunctionCx<'_, '_, '_>, a: Value, b: Value) -> Value {
+    let a_is_nan = fx.bcx.ins().fcmp(FloatCC::NotEqual, a, a);
+    let a_ge_b = fx.bcx.ins().fcmp(FloatCC::GreaterThanOrEqual, a, b);
+    let temp = fx.bcx.ins().select(a_ge_b, b, a);
+    fx.bcx.ins().select(a_is_nan, b, temp)
+}
+
+pub(crate) fn codegen_float_max(fx: &mut FunctionCx<'_, '_, '_>, a: Value, b: Value) -> Value {
+    let a_is_nan = fx.bcx.ins().fcmp(FloatCC::NotEqual, a, a);
+    let a_le_b = fx.bcx.ins().fcmp(FloatCC::LessThanOrEqual, a, b);
+    let temp = fx.bcx.ins().select(a_le_b, b, a);
+    fx.bcx.ins().select(a_is_nan, b, temp)
+}
diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs
index 2af050f0c75..2e8cd934eb2 100644
--- a/compiler/rustc_codegen_gcc/src/asm.rs
+++ b/compiler/rustc_codegen_gcc/src/asm.rs
@@ -589,6 +589,7 @@ fn reg_to_gcc(reg: InlineAsmRegOrRegClass) -> ConstraintOrRegister {
             | InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg) => "x",
             InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => "v",
             InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => unimplemented!(),
+            InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg0) => unimplemented!(),
             InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => unimplemented!(),
             InlineAsmRegClass::X86(
                 X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg,
@@ -654,6 +655,7 @@ fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegCl
         | InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => cx.type_f32(),
         InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg) => unimplemented!(),
         InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => cx.type_i16(),
+        InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg0) => cx.type_i16(),
         InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => cx.type_i32(),
         InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
             bug!("LLVM backend does not support SPIR-V")
@@ -784,6 +786,7 @@ fn modifier_to_gcc(arch: InlineAsmArch, reg: InlineAsmRegClass, modifier: Option
             _ => unreachable!(),
         },
         InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => None,
+        InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg0) => None,
         InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg) => {
             unreachable!("clobber-only")
         }
diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs
index dff32007918..e994001f96f 100644
--- a/compiler/rustc_codegen_llvm/src/asm.rs
+++ b/compiler/rustc_codegen_llvm/src/asm.rs
@@ -602,7 +602,9 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'_>>) ->
             InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => "v",
             InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => "^Yk",
             InlineAsmRegClass::X86(
-                X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg,
+                X86InlineAsmRegClass::x87_reg
+                | X86InlineAsmRegClass::mmx_reg
+                | X86InlineAsmRegClass::kreg0,
             ) => unreachable!("clobber-only"),
             InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => "r",
             InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::reg) => "r",
@@ -687,7 +689,11 @@ fn modifier_to_llvm(
             _ => unreachable!(),
         },
         InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => None,
-        InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg) => {
+        InlineAsmRegClass::X86(
+            X86InlineAsmRegClass::x87_reg
+            | X86InlineAsmRegClass::mmx_reg
+            | X86InlineAsmRegClass::kreg0,
+        ) => {
             unreachable!("clobber-only")
         }
         InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => None,
@@ -757,7 +763,11 @@ fn dummy_output_type<'ll>(cx: &CodegenCx<'ll, '_>, reg: InlineAsmRegClass) -> &'
         | InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg)
         | InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => cx.type_f32(),
         InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => cx.type_i16(),
-        InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg) => {
+        InlineAsmRegClass::X86(
+            X86InlineAsmRegClass::x87_reg
+            | X86InlineAsmRegClass::mmx_reg
+            | X86InlineAsmRegClass::kreg0,
+        ) => {
             unreachable!("clobber-only")
         }
         InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => cx.type_i32(),
diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs
index 7a747a9cdee..5c63bd8c1bd 100644
--- a/compiler/rustc_codegen_llvm/src/back/lto.rs
+++ b/compiler/rustc_codegen_llvm/src/back/lto.rs
@@ -16,7 +16,7 @@ use rustc_errors::{FatalError, Handler};
 use rustc_hir::def_id::LOCAL_CRATE;
 use rustc_middle::bug;
 use rustc_middle::dep_graph::WorkProduct;
-use rustc_middle::middle::exported_symbols::SymbolExportLevel;
+use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel};
 use rustc_session::cgu_reuse_tracker::CguReuse;
 use rustc_session::config::{self, CrateType, Lto};
 use tracing::{debug, info};
@@ -55,8 +55,8 @@ fn prepare_lto(
         Lto::No => panic!("didn't request LTO but we're doing LTO"),
     };
 
-    let symbol_filter = &|&(ref name, level): &(String, SymbolExportLevel)| {
-        if level.is_below_threshold(export_threshold) {
+    let symbol_filter = &|&(ref name, info): &(String, SymbolExportInfo)| {
+        if info.level.is_below_threshold(export_threshold) || info.used {
             Some(CString::new(name.as_str()).unwrap())
         } else {
             None
@@ -625,7 +625,7 @@ pub(crate) fn run_pass_manager(
             if thin {
                 llvm::LLVMRustPassManagerBuilderPopulateThinLTOPassManager(b, pm);
             } else {
-                llvm::LLVMPassManagerBuilderPopulateLTOPassManager(
+                llvm::LLVMRustPassManagerBuilderPopulateLTOPassManager(
                     b, pm, /* Internalize = */ False, /* RunInliner = */ True,
                 );
             }
diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs
index 7ef3b12cd08..99e30531c22 100644
--- a/compiler/rustc_codegen_llvm/src/back/write.rs
+++ b/compiler/rustc_codegen_llvm/src/back/write.rs
@@ -523,6 +523,12 @@ pub(crate) unsafe fn optimize(
     let module_name = module.name.clone();
     let module_name = Some(&module_name[..]);
 
+    if let Some(false) = config.new_llvm_pass_manager && llvm_util::get_version() >= (15, 0, 0) {
+        diag_handler.warn(
+            "ignoring `-Z new-llvm-pass-manager=no`, which is no longer supported with LLVM 15",
+        );
+    }
+
     if config.emit_no_opt_bc {
         let out = cgcx.output_filenames.temp_path_ext("no-opt.bc", module_name);
         let out = path_to_c_string(&out);
@@ -628,8 +634,8 @@ pub(crate) unsafe fn optimize(
                         extra_passes.as_ptr(),
                         extra_passes.len() as size_t,
                     );
-                    llvm::LLVMPassManagerBuilderPopulateFunctionPassManager(b, fpm);
-                    llvm::LLVMPassManagerBuilderPopulateModulePassManager(b, mpm);
+                    llvm::LLVMRustPassManagerBuilderPopulateFunctionPassManager(b, fpm);
+                    llvm::LLVMRustPassManagerBuilderPopulateModulePassManager(b, mpm);
                 });
 
                 have_name_anon_globals_pass = have_name_anon_globals_pass || prepare_for_thin_lto;
@@ -1085,7 +1091,7 @@ pub unsafe fn with_llvm_pmb(
     // Create the PassManagerBuilder for LLVM. We configure it with
     // reasonable defaults and prepare it to actually populate the pass
     // manager.
-    let builder = llvm::LLVMPassManagerBuilderCreate();
+    let builder = llvm::LLVMRustPassManagerBuilderCreate();
     let opt_size = config.opt_size.map_or(llvm::CodeGenOptSizeNone, |x| to_llvm_opt_settings(x).1);
     let inline_threshold = config.inline_threshold;
     let pgo_gen_path = get_pgo_gen_path(config);
@@ -1102,14 +1108,9 @@ pub unsafe fn with_llvm_pmb(
         pgo_gen_path.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
         pgo_use_path.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
         pgo_sample_use_path.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
+        opt_size as c_int,
     );
 
-    llvm::LLVMPassManagerBuilderSetSizeLevel(builder, opt_size as u32);
-
-    if opt_size != llvm::CodeGenOptSizeNone {
-        llvm::LLVMPassManagerBuilderSetDisableUnrollLoops(builder, 1);
-    }
-
     llvm::LLVMRustAddBuilderLibraryInfo(builder, llmod, config.no_builtins);
 
     // Here we match what clang does (kinda). For O0 we only inline
@@ -1118,16 +1119,16 @@ pub unsafe fn with_llvm_pmb(
     // thresholds copied from clang.
     match (opt_level, opt_size, inline_threshold) {
         (.., Some(t)) => {
-            llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, t);
+            llvm::LLVMRustPassManagerBuilderUseInlinerWithThreshold(builder, t);
         }
         (llvm::CodeGenOptLevel::Aggressive, ..) => {
-            llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 275);
+            llvm::LLVMRustPassManagerBuilderUseInlinerWithThreshold(builder, 275);
         }
         (_, llvm::CodeGenOptSizeDefault, _) => {
-            llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 75);
+            llvm::LLVMRustPassManagerBuilderUseInlinerWithThreshold(builder, 75);
         }
         (_, llvm::CodeGenOptSizeAggressive, _) => {
-            llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 25);
+            llvm::LLVMRustPassManagerBuilderUseInlinerWithThreshold(builder, 25);
         }
         (llvm::CodeGenOptLevel::None, ..) => {
             llvm::LLVMRustAddAlwaysInlinePass(builder, config.emit_lifetime_markers);
@@ -1136,12 +1137,12 @@ pub unsafe fn with_llvm_pmb(
             llvm::LLVMRustAddAlwaysInlinePass(builder, config.emit_lifetime_markers);
         }
         (llvm::CodeGenOptLevel::Default, ..) => {
-            llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 225);
+            llvm::LLVMRustPassManagerBuilderUseInlinerWithThreshold(builder, 225);
         }
     }
 
     f(builder);
-    llvm::LLVMPassManagerBuilderDispose(builder);
+    llvm::LLVMRustPassManagerBuilderDispose(builder);
 }
 
 // Create a `__imp_<symbol> = &symbol` global for every public static `symbol`.
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs
index 76caa3ceaaf..99e4ded62f1 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs
@@ -20,7 +20,6 @@ pub fn compute_mir_scopes<'ll, 'tcx>(
     cx: &CodegenCx<'ll, 'tcx>,
     instance: Instance<'tcx>,
     mir: &Body<'tcx>,
-    fn_dbg_scope: &'ll DIScope,
     debug_context: &mut FunctionDebugContext<&'ll DIScope, &'ll DILocation>,
 ) {
     // Find all scopes with variables defined in them.
@@ -38,40 +37,41 @@ pub fn compute_mir_scopes<'ll, 'tcx>(
         // Nothing to emit, of course.
         None
     };
-
+    let mut instantiated = BitSet::new_empty(mir.source_scopes.len());
     // Instantiate all scopes.
     for idx in 0..mir.source_scopes.len() {
         let scope = SourceScope::new(idx);
-        make_mir_scope(cx, instance, mir, fn_dbg_scope, &variables, debug_context, scope);
+        make_mir_scope(cx, instance, mir, &variables, debug_context, &mut instantiated, scope);
     }
+    assert!(instantiated.count() == mir.source_scopes.len());
 }
 
 fn make_mir_scope<'ll, 'tcx>(
     cx: &CodegenCx<'ll, 'tcx>,
     instance: Instance<'tcx>,
     mir: &Body<'tcx>,
-    fn_dbg_scope: &'ll DIScope,
     variables: &Option<BitSet<SourceScope>>,
     debug_context: &mut FunctionDebugContext<&'ll DIScope, &'ll DILocation>,
+    instantiated: &mut BitSet<SourceScope>,
     scope: SourceScope,
 ) {
-    if debug_context.scopes[scope].dbg_scope.is_some() {
+    if instantiated.contains(scope) {
         return;
     }
 
     let scope_data = &mir.source_scopes[scope];
     let parent_scope = if let Some(parent) = scope_data.parent_scope {
-        make_mir_scope(cx, instance, mir, fn_dbg_scope, variables, debug_context, parent);
+        make_mir_scope(cx, instance, mir, variables, debug_context, instantiated, parent);
         debug_context.scopes[parent]
     } else {
         // The root is the function itself.
         let loc = cx.lookup_debug_loc(mir.span.lo());
         debug_context.scopes[scope] = DebugScope {
-            dbg_scope: Some(fn_dbg_scope),
-            inlined_at: None,
             file_start_pos: loc.file.start_pos,
             file_end_pos: loc.file.end_pos,
+            ..debug_context.scopes[scope]
         };
+        instantiated.insert(scope);
         return;
     };
 
@@ -79,6 +79,7 @@ fn make_mir_scope<'ll, 'tcx>(
         // Do not create a DIScope if there are no variables defined in this
         // MIR `SourceScope`, and it's not `inlined`, to avoid debuginfo bloat.
         debug_context.scopes[scope] = parent_scope;
+        instantiated.insert(scope);
         return;
     }
 
@@ -100,7 +101,7 @@ fn make_mir_scope<'ll, 'tcx>(
         None => unsafe {
             llvm::LLVMRustDIBuilderCreateLexicalBlock(
                 DIB(cx),
-                parent_scope.dbg_scope.unwrap(),
+                parent_scope.dbg_scope,
                 file_metadata,
                 loc.line,
                 loc.col,
@@ -116,9 +117,10 @@ fn make_mir_scope<'ll, 'tcx>(
     });
 
     debug_context.scopes[scope] = DebugScope {
-        dbg_scope: Some(dbg_scope),
+        dbg_scope,
         inlined_at: inlined_at.or(parent_scope.inlined_at),
         file_start_pos: loc.file.start_pos,
         file_end_pos: loc.file.end_pos,
     };
+    instantiated.insert(scope);
 }
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
index 74e194750fa..f2cf3b1ef5c 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
@@ -437,11 +437,9 @@ pub fn type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll D
 
     let DINodeCreationResult { di_node, already_stored_in_typemap } = match *t.kind() {
         ty::Never | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) => {
-            DINodeCreationResult::new(build_basic_type_di_node(cx, t), false)
-        }
-        ty::Tuple(elements) if elements.is_empty() => {
-            DINodeCreationResult::new(build_basic_type_di_node(cx, t), false)
+            build_basic_type_di_node(cx, t)
         }
+        ty::Tuple(elements) if elements.is_empty() => build_basic_type_di_node(cx, t),
         ty::Array(..) => build_fixed_size_array_di_node(cx, unique_type_id, t),
         ty::Slice(_) | ty::Str => build_slice_type_di_node(cx, t, unique_type_id),
         ty::Dynamic(..) => build_dyn_type_di_node(cx, t, unique_type_id),
@@ -640,7 +638,10 @@ impl MsvcBasicName for ty::FloatTy {
     }
 }
 
-fn build_basic_type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll DIType {
+fn build_basic_type_di_node<'ll, 'tcx>(
+    cx: &CodegenCx<'ll, 'tcx>,
+    t: Ty<'tcx>,
+) -> DINodeCreationResult<'ll> {
     debug!("build_basic_type_di_node: {:?}", t);
 
     // When targeting MSVC, emit MSVC style type names for compatibility with
@@ -649,7 +650,13 @@ fn build_basic_type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -
 
     let (name, encoding) = match t.kind() {
         ty::Never => ("!", DW_ATE_unsigned),
-        ty::Tuple(elements) if elements.is_empty() => ("()", DW_ATE_unsigned),
+        ty::Tuple(elements) if elements.is_empty() => {
+            if cpp_like_debuginfo {
+                return build_tuple_type_di_node(cx, UniqueTypeId::for_ty(cx.tcx, t));
+            } else {
+                ("()", DW_ATE_unsigned)
+            }
+        }
         ty::Bool => ("bool", DW_ATE_boolean),
         ty::Char => ("char", DW_ATE_UTF),
         ty::Int(int_ty) if cpp_like_debuginfo => (int_ty.msvc_basic_name(), DW_ATE_signed),
@@ -672,14 +679,14 @@ fn build_basic_type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -
     };
 
     if !cpp_like_debuginfo {
-        return ty_di_node;
+        return DINodeCreationResult::new(ty_di_node, false);
     }
 
     let typedef_name = match t.kind() {
         ty::Int(int_ty) => int_ty.name_str(),
         ty::Uint(uint_ty) => uint_ty.name_str(),
         ty::Float(float_ty) => float_ty.name_str(),
-        _ => return ty_di_node,
+        _ => return DINodeCreationResult::new(ty_di_node, false),
     };
 
     let typedef_di_node = unsafe {
@@ -694,7 +701,7 @@ fn build_basic_type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -
         )
     };
 
-    typedef_di_node
+    DINodeCreationResult::new(typedef_di_node, false)
 }
 
 fn build_foreign_type_di_node<'ll, 'tcx>(
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
index 4e6d3f88e67..6a164557a47 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
@@ -286,9 +286,8 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
         }
 
         // Initialize fn debug context (including scopes).
-        // FIXME(eddyb) figure out a way to not need `Option` for `dbg_scope`.
         let empty_scope = DebugScope {
-            dbg_scope: None,
+            dbg_scope: self.dbg_scope_fn(instance, fn_abi, Some(llfn)),
             inlined_at: None,
             file_start_pos: BytePos(0),
             file_end_pos: BytePos(0),
@@ -297,13 +296,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
             FunctionDebugContext { scopes: IndexVec::from_elem(empty_scope, &mir.source_scopes) };
 
         // Fill in all the scopes, with the information from the MIR body.
-        compute_mir_scopes(
-            self,
-            instance,
-            mir,
-            self.dbg_scope_fn(instance, fn_abi, Some(llfn)),
-            &mut fn_debug_context,
-        );
+        compute_mir_scopes(self, instance, mir, &mut fn_debug_context);
 
         Some(fn_debug_context)
     }
diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
index 7f533b0552a..13baaddccd4 100644
--- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
@@ -1825,24 +1825,22 @@ extern "C" {
 
     pub fn LLVMAddAnalysisPasses<'a>(T: &'a TargetMachine, PM: &PassManager<'a>);
 
-    pub fn LLVMPassManagerBuilderCreate() -> &'static mut PassManagerBuilder;
-    pub fn LLVMPassManagerBuilderDispose(PMB: &'static mut PassManagerBuilder);
-    pub fn LLVMPassManagerBuilderSetSizeLevel(PMB: &PassManagerBuilder, Value: Bool);
-    pub fn LLVMPassManagerBuilderSetDisableUnrollLoops(PMB: &PassManagerBuilder, Value: Bool);
-    pub fn LLVMPassManagerBuilderUseInlinerWithThreshold(
+    pub fn LLVMRustPassManagerBuilderCreate() -> &'static mut PassManagerBuilder;
+    pub fn LLVMRustPassManagerBuilderDispose(PMB: &'static mut PassManagerBuilder);
+    pub fn LLVMRustPassManagerBuilderUseInlinerWithThreshold(
         PMB: &PassManagerBuilder,
         threshold: c_uint,
     );
-    pub fn LLVMPassManagerBuilderPopulateModulePassManager(
+    pub fn LLVMRustPassManagerBuilderPopulateModulePassManager(
         PMB: &PassManagerBuilder,
         PM: &PassManager<'_>,
     );
 
-    pub fn LLVMPassManagerBuilderPopulateFunctionPassManager(
+    pub fn LLVMRustPassManagerBuilderPopulateFunctionPassManager(
         PMB: &PassManagerBuilder,
         PM: &PassManager<'_>,
     );
-    pub fn LLVMPassManagerBuilderPopulateLTOPassManager(
+    pub fn LLVMRustPassManagerBuilderPopulateLTOPassManager(
         PMB: &PassManagerBuilder,
         PM: &PassManager<'_>,
         Internalize: Bool,
@@ -2308,6 +2306,7 @@ extern "C" {
         PGOGenPath: *const c_char,
         PGOUsePath: *const c_char,
         PGOSampleUsePath: *const c_char,
+        SizeLevel: c_int,
     );
     pub fn LLVMRustAddLibraryInfo<'a>(
         PM: &PassManager<'a>,
diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs
index c24e369ae72..7b407c94e7b 100644
--- a/compiler/rustc_codegen_llvm/src/llvm_util.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs
@@ -542,6 +542,11 @@ pub(crate) fn should_use_new_llvm_pass_manager(user_opt: &Option<bool>, target_a
     // The new pass manager is enabled by default for LLVM >= 13.
     // This matches Clang, which also enables it since Clang 13.
 
+    // Since LLVM 15, the legacy pass manager is no longer supported.
+    if llvm_util::get_version() >= (15, 0, 0) {
+        return true;
+    }
+
     // There are some perf issues with the new pass manager when targeting
     // s390x with LLVM 13, so enable the new pass manager only with LLVM 14.
     // See https://github.com/rust-lang/rust/issues/89609.
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index cf32d558d4a..886ca9681e2 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -7,6 +7,7 @@ use rustc_errors::{ErrorGuaranteed, Handler};
 use rustc_fs_util::fix_windows_verbatim_for_gcc;
 use rustc_hir::def_id::CrateNum;
 use rustc_middle::middle::dependency_format::Linkage;
+use rustc_middle::middle::exported_symbols::SymbolExportKind;
 use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, LdImpl, Strip};
 use rustc_session::config::{OutputFilenames, OutputType, PrintRequest, SplitDwarfKind};
 use rustc_session::cstore::DllImport;
@@ -987,7 +988,7 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>(
 
         // On MSVC packed debug information is produced by the linker itself so
         // there's no need to do anything else here.
-        SplitDebuginfo::Packed if sess.target.is_like_msvc => {}
+        SplitDebuginfo::Packed if sess.target.is_like_windows => {}
 
         // ... and otherwise we're processing a `*.dwp` packed dwarf file.
         //
@@ -1655,6 +1656,100 @@ fn add_post_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor
     }
 }
 
+/// Add a synthetic object file that contains reference to all symbols that we want to expose to
+/// the linker.
+///
+/// Background: we implement rlibs as static library (archives). Linkers treat archives
+/// differently from object files: all object files participate in linking, while archives will
+/// only participate in linking if they can satisfy at least one undefined reference (version
+/// scripts doesn't count). This causes `#[no_mangle]` or `#[used]` items to be ignored by the
+/// linker, and since they never participate in the linking, using `KEEP` in the linker scripts
+/// can't keep them either. This causes #47384.
+///
+/// To keep them around, we could use `--whole-archive` and equivalents to force rlib to
+/// participate in linking like object files, but this proves to be expensive (#93791). Therefore
+/// we instead just introduce an undefined reference to them. This could be done by `-u` command
+/// line option to the linker or `EXTERN(...)` in linker scripts, however they does not only
+/// introduce an undefined reference, but also make them the GC roots, preventing `--gc-sections`
+/// from removing them, and this is especially problematic for embedded programming where every
+/// byte counts.
+///
+/// This method creates a synthetic object file, which contains undefined references to all symbols
+/// that are necessary for the linking. They are only present in symbol table but not actually
+/// used in any sections, so the linker will therefore pick relevant rlibs for linking, but
+/// unused `#[no_mangle]` or `#[used]` can still be discard by GC sections.
+fn add_linked_symbol_object(
+    cmd: &mut dyn Linker,
+    sess: &Session,
+    tmpdir: &Path,
+    symbols: &[(String, SymbolExportKind)],
+) {
+    if symbols.is_empty() {
+        return;
+    }
+
+    let Some(mut file) = super::metadata::create_object_file(sess) else {
+        return;
+    };
+
+    // NOTE(nbdd0121): MSVC will hang if the input object file contains no sections,
+    // so add an empty section.
+    if file.format() == object::BinaryFormat::Coff {
+        file.add_section(Vec::new(), ".text".into(), object::SectionKind::Text);
+
+        // We handle the name decoration of COFF targets in `symbol_export.rs`, so disable the
+        // default mangler in `object` crate.
+        file.set_mangling(object::write::Mangling::None);
+
+        // Add feature flags to the object file. On MSVC this is optional but LLD will complain if
+        // not present.
+        let mut feature = 0;
+
+        if file.architecture() == object::Architecture::I386 {
+            // Indicate that all SEH handlers are registered in .sxdata section.
+            // We don't have generate any code, so we don't need .sxdata section but LLD still
+            // expects us to set this bit (see #96498).
+            // Reference: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
+            feature |= 1;
+        }
+
+        file.add_symbol(object::write::Symbol {
+            name: "@feat.00".into(),
+            value: feature,
+            size: 0,
+            kind: object::SymbolKind::Data,
+            scope: object::SymbolScope::Compilation,
+            weak: false,
+            section: object::write::SymbolSection::Absolute,
+            flags: object::SymbolFlags::None,
+        });
+    }
+
+    for (sym, kind) in symbols.iter() {
+        file.add_symbol(object::write::Symbol {
+            name: sym.clone().into(),
+            value: 0,
+            size: 0,
+            kind: match kind {
+                SymbolExportKind::Text => object::SymbolKind::Text,
+                SymbolExportKind::Data => object::SymbolKind::Data,
+                SymbolExportKind::Tls => object::SymbolKind::Tls,
+            },
+            scope: object::SymbolScope::Unknown,
+            weak: false,
+            section: object::write::SymbolSection::Undefined,
+            flags: object::SymbolFlags::None,
+        });
+    }
+
+    let path = tmpdir.join("symbols.o");
+    let result = std::fs::write(&path, file.write().unwrap());
+    if let Err(e) = result {
+        sess.fatal(&format!("failed to write {}: {}", path.display(), e));
+    }
+    cmd.add_object(&path);
+}
+
 /// Add object files containing code from the current crate.
 fn add_local_crate_regular_objects(cmd: &mut dyn Linker, codegen_results: &CodegenResults) {
     for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) {
@@ -1795,6 +1890,13 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
     // Pre-link CRT objects.
     add_pre_link_objects(cmd, sess, link_output_kind, crt_objects_fallback);
 
+    add_linked_symbol_object(
+        cmd,
+        sess,
+        tmpdir,
+        &codegen_results.crate_info.linked_symbols[&crate_type],
+    );
+
     // Sanitizer libraries.
     add_sanitizer_libraries(sess, crate_type, cmd);
 
diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs
index 2c15ed83167..0805df5dad6 100644
--- a/compiler/rustc_codegen_ssa/src/back/linker.rs
+++ b/compiler/rustc_codegen_ssa/src/back/linker.rs
@@ -12,6 +12,7 @@ use std::{env, mem, str};
 
 use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
 use rustc_middle::middle::dependency_format::Linkage;
+use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo, SymbolExportKind};
 use rustc_middle::ty::TyCtxt;
 use rustc_serialize::{json, Encoder};
 use rustc_session::config::{self, CrateType, DebugInfo, LinkerPluginLto, Lto, OptLevel, Strip};
@@ -1518,6 +1519,29 @@ impl<'a> L4Bender<'a> {
     }
 }
 
+fn for_each_exported_symbols_include_dep<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    crate_type: CrateType,
+    mut callback: impl FnMut(ExportedSymbol<'tcx>, SymbolExportInfo, CrateNum),
+) {
+    for &(symbol, info) in tcx.exported_symbols(LOCAL_CRATE).iter() {
+        callback(symbol, info, LOCAL_CRATE);
+    }
+
+    let formats = tcx.dependency_formats(());
+    let deps = formats.iter().find_map(|(t, list)| (*t == crate_type).then_some(list)).unwrap();
+
+    for (index, dep_format) in deps.iter().enumerate() {
+        let cnum = CrateNum::new(index + 1);
+        // For each dependency that we are linking to statically ...
+        if *dep_format == Linkage::Static {
+            for &(symbol, info) in tcx.exported_symbols(cnum).iter() {
+                callback(symbol, info, cnum);
+            }
+        }
+    }
+}
+
 pub(crate) fn exported_symbols(tcx: TyCtxt<'_>, crate_type: CrateType) -> Vec<String> {
     if let Some(ref exports) = tcx.sess.target.override_export_symbols {
         return exports.iter().map(ToString::to_string).collect();
@@ -1526,34 +1550,38 @@ pub(crate) fn exported_symbols(tcx: TyCtxt<'_>, crate_type: CrateType) -> Vec<St
     let mut symbols = Vec::new();
 
     let export_threshold = symbol_export::crates_export_threshold(&[crate_type]);
-    for &(symbol, level) in tcx.exported_symbols(LOCAL_CRATE).iter() {
-        if level.is_below_threshold(export_threshold) {
-            symbols.push(symbol_export::symbol_name_for_instance_in_crate(
-                tcx,
-                symbol,
-                LOCAL_CRATE,
-            ));
+    for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| {
+        if info.level.is_below_threshold(export_threshold) {
+            symbols.push(symbol_export::symbol_name_for_instance_in_crate(tcx, symbol, cnum));
         }
-    }
+    });
 
-    let formats = tcx.dependency_formats(());
-    let deps = formats.iter().find_map(|(t, list)| (*t == crate_type).then_some(list)).unwrap();
-
-    for (index, dep_format) in deps.iter().enumerate() {
-        let cnum = CrateNum::new(index + 1);
-        // For each dependency that we are linking to statically ...
-        if *dep_format == Linkage::Static {
-            // ... we add its symbol list to our export list.
-            for &(symbol, level) in tcx.exported_symbols(cnum).iter() {
-                if !level.is_below_threshold(export_threshold) {
-                    continue;
-                }
+    symbols
+}
 
-                symbols.push(symbol_export::symbol_name_for_instance_in_crate(tcx, symbol, cnum));
-            }
+pub(crate) fn linked_symbols(
+    tcx: TyCtxt<'_>,
+    crate_type: CrateType,
+) -> Vec<(String, SymbolExportKind)> {
+    match crate_type {
+        CrateType::Executable | CrateType::Cdylib => (),
+        CrateType::Staticlib | CrateType::ProcMacro | CrateType::Rlib | CrateType::Dylib => {
+            return Vec::new();
         }
     }
 
+    let mut symbols = Vec::new();
+
+    let export_threshold = symbol_export::crates_export_threshold(&[crate_type]);
+    for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| {
+        if info.level.is_below_threshold(export_threshold) || info.used {
+            symbols.push((
+                symbol_export::linking_symbol_name_for_instance_in_crate(tcx, symbol, cnum),
+                info.kind,
+            ));
+        }
+    });
+
     symbols
 }
 
diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs
index c52269805c4..2e422728056 100644
--- a/compiler/rustc_codegen_ssa/src/back/metadata.rs
+++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs
@@ -94,7 +94,7 @@ fn search_for_metadata<'a>(
         .map_err(|e| format!("failed to read {} section in '{}': {}", section, path.display(), e))
 }
 
-fn create_object_file(sess: &Session) -> Option<write::Object<'static>> {
+pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static>> {
     let endianness = match sess.target.options.endian {
         Endian::Little => Endianness::Little,
         Endian::Big => Endianness::Big,
diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
index 7b7c676c26c..f651814be7e 100644
--- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
+++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
@@ -7,12 +7,12 @@ use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LOCAL_CRATE};
 use rustc_hir::Node;
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use rustc_middle::middle::exported_symbols::{
-    metadata_symbol_name, ExportedSymbol, SymbolExportLevel,
+    metadata_symbol_name, ExportedSymbol, SymbolExportInfo, SymbolExportKind, SymbolExportLevel,
 };
 use rustc_middle::ty::query::{ExternProviders, Providers};
 use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
 use rustc_middle::ty::Instance;
-use rustc_middle::ty::{SymbolName, TyCtxt};
+use rustc_middle::ty::{self, SymbolName, TyCtxt};
 use rustc_session::config::CrateType;
 use rustc_target::spec::SanitizerSet;
 
@@ -40,7 +40,7 @@ pub fn crates_export_threshold(crate_types: &[CrateType]) -> SymbolExportLevel {
     }
 }
 
-fn reachable_non_generics_provider(tcx: TyCtxt<'_>, cnum: CrateNum) -> DefIdMap<SymbolExportLevel> {
+fn reachable_non_generics_provider(tcx: TyCtxt<'_>, cnum: CrateNum) -> DefIdMap<SymbolExportInfo> {
     assert_eq!(cnum, LOCAL_CRATE);
 
     if !tcx.sess.opts.output_types.should_codegen() {
@@ -103,36 +103,51 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, cnum: CrateNum) -> DefIdMap<
             }
         })
         .map(|def_id| {
-            let export_level = if special_runtime_crate {
+            let (export_level, used) = if special_runtime_crate {
                 let name = tcx.symbol_name(Instance::mono(tcx, def_id.to_def_id())).name;
-                // We can probably do better here by just ensuring that
-                // it has hidden visibility rather than public
-                // visibility, as this is primarily here to ensure it's
-                // not stripped during LTO.
-                //
-                // In general though we won't link right if these
-                // symbols are stripped, and LTO currently strips them.
-                match name {
+                // We won't link right if these symbols are stripped during LTO.
+                let used = match name {
                     "rust_eh_personality"
                     | "rust_eh_register_frames"
-                    | "rust_eh_unregister_frames" =>
-                        SymbolExportLevel::C,
-                    _ => SymbolExportLevel::Rust,
-                }
+                    | "rust_eh_unregister_frames" => true,
+                    _ => false,
+                };
+                (SymbolExportLevel::Rust, used)
             } else {
-                symbol_export_level(tcx, def_id.to_def_id())
+                (symbol_export_level(tcx, def_id.to_def_id()), false)
             };
+            let codegen_attrs = tcx.codegen_fn_attrs(def_id.to_def_id());
             debug!(
                 "EXPORTED SYMBOL (local): {} ({:?})",
                 tcx.symbol_name(Instance::mono(tcx, def_id.to_def_id())),
                 export_level
             );
-            (def_id.to_def_id(), export_level)
+            (def_id.to_def_id(), SymbolExportInfo {
+                level: export_level,
+                kind: if tcx.is_static(def_id.to_def_id()) {
+                    if codegen_attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) {
+                        SymbolExportKind::Tls
+                    } else {
+                        SymbolExportKind::Data
+                    }
+                } else {
+                    SymbolExportKind::Text
+                },
+                used: codegen_attrs.flags.contains(CodegenFnAttrFlags::USED)
+                    || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) || used,
+            })
         })
         .collect();
 
     if let Some(id) = tcx.proc_macro_decls_static(()) {
-        reachable_non_generics.insert(id.to_def_id(), SymbolExportLevel::C);
+        reachable_non_generics.insert(
+            id.to_def_id(),
+            SymbolExportInfo {
+                level: SymbolExportLevel::C,
+                kind: SymbolExportKind::Data,
+                used: false,
+            },
+        );
     }
 
     reachable_non_generics
@@ -141,8 +156,8 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, cnum: CrateNum) -> DefIdMap<
 fn is_reachable_non_generic_provider_local(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
     let export_threshold = threshold(tcx);
 
-    if let Some(&level) = tcx.reachable_non_generics(def_id.krate).get(&def_id) {
-        level.is_below_threshold(export_threshold)
+    if let Some(&info) = tcx.reachable_non_generics(def_id.krate).get(&def_id) {
+        info.level.is_below_threshold(export_threshold)
     } else {
         false
     }
@@ -155,7 +170,7 @@ fn is_reachable_non_generic_provider_extern(tcx: TyCtxt<'_>, def_id: DefId) -> b
 fn exported_symbols_provider_local<'tcx>(
     tcx: TyCtxt<'tcx>,
     cnum: CrateNum,
-) -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportLevel)] {
+) -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportInfo)] {
     assert_eq!(cnum, LOCAL_CRATE);
 
     if !tcx.sess.opts.output_types.should_codegen() {
@@ -165,13 +180,20 @@ fn exported_symbols_provider_local<'tcx>(
     let mut symbols: Vec<_> = tcx
         .reachable_non_generics(LOCAL_CRATE)
         .iter()
-        .map(|(&def_id, &level)| (ExportedSymbol::NonGeneric(def_id), level))
+        .map(|(&def_id, &info)| (ExportedSymbol::NonGeneric(def_id), info))
         .collect();
 
     if tcx.entry_fn(()).is_some() {
         let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, "main"));
 
-        symbols.push((exported_symbol, SymbolExportLevel::C));
+        symbols.push((
+            exported_symbol,
+            SymbolExportInfo {
+                level: SymbolExportLevel::C,
+                kind: SymbolExportKind::Text,
+                used: false,
+            },
+        ));
     }
 
     if tcx.allocator_kind(()).is_some() {
@@ -179,7 +201,14 @@ fn exported_symbols_provider_local<'tcx>(
             let symbol_name = format!("__rust_{}", method.name);
             let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, &symbol_name));
 
-            symbols.push((exported_symbol, SymbolExportLevel::Rust));
+            symbols.push((
+                exported_symbol,
+                SymbolExportInfo {
+                    level: SymbolExportLevel::Rust,
+                    kind: SymbolExportKind::Text,
+                    used: false,
+                },
+            ));
         }
     }
 
@@ -192,17 +221,39 @@ fn exported_symbols_provider_local<'tcx>(
 
         symbols.extend(PROFILER_WEAK_SYMBOLS.iter().map(|sym| {
             let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, sym));
-            (exported_symbol, SymbolExportLevel::C)
+            (
+                exported_symbol,
+                SymbolExportInfo {
+                    level: SymbolExportLevel::C,
+                    kind: SymbolExportKind::Data,
+                    used: false,
+                },
+            )
         }));
     }
 
     if tcx.sess.opts.debugging_opts.sanitizer.contains(SanitizerSet::MEMORY) {
+        let mut msan_weak_symbols = Vec::new();
+
         // Similar to profiling, preserve weak msan symbol during LTO.
-        const MSAN_WEAK_SYMBOLS: [&str; 2] = ["__msan_track_origins", "__msan_keep_going"];
+        if tcx.sess.opts.debugging_opts.sanitizer_recover.contains(SanitizerSet::MEMORY) {
+            msan_weak_symbols.push("__msan_keep_going");
+        }
 
-        symbols.extend(MSAN_WEAK_SYMBOLS.iter().map(|sym| {
+        if tcx.sess.opts.debugging_opts.sanitizer_memory_track_origins != 0 {
+            msan_weak_symbols.push("__msan_track_origins");
+        }
+
+        symbols.extend(msan_weak_symbols.into_iter().map(|sym| {
             let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, sym));
-            (exported_symbol, SymbolExportLevel::C)
+            (
+                exported_symbol,
+                SymbolExportInfo {
+                    level: SymbolExportLevel::C,
+                    kind: SymbolExportKind::Data,
+                    used: false,
+                },
+            )
         }));
     }
 
@@ -210,7 +261,14 @@ fn exported_symbols_provider_local<'tcx>(
         let symbol_name = metadata_symbol_name(tcx);
         let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, &symbol_name));
 
-        symbols.push((exported_symbol, SymbolExportLevel::Rust));
+        symbols.push((
+            exported_symbol,
+            SymbolExportInfo {
+                level: SymbolExportLevel::Rust,
+                kind: SymbolExportKind::Data,
+                used: false,
+            },
+        ));
     }
 
     if tcx.sess.opts.share_generics() && tcx.local_crate_exports_generics() {
@@ -243,7 +301,14 @@ fn exported_symbols_provider_local<'tcx>(
                 MonoItem::Fn(Instance { def: InstanceDef::Item(def), substs }) => {
                     if substs.non_erasable_generics().next().is_some() {
                         let symbol = ExportedSymbol::Generic(def.did, substs);
-                        symbols.push((symbol, SymbolExportLevel::Rust));
+                        symbols.push((
+                            symbol,
+                            SymbolExportInfo {
+                                level: SymbolExportLevel::Rust,
+                                kind: SymbolExportKind::Text,
+                                used: false,
+                            },
+                        ));
                     }
                 }
                 MonoItem::Fn(Instance { def: InstanceDef::DropGlue(_, Some(ty)), substs }) => {
@@ -252,7 +317,14 @@ fn exported_symbols_provider_local<'tcx>(
                         substs.non_erasable_generics().next(),
                         Some(GenericArgKind::Type(ty))
                     );
-                    symbols.push((ExportedSymbol::DropGlue(ty), SymbolExportLevel::Rust));
+                    symbols.push((
+                        ExportedSymbol::DropGlue(ty),
+                        SymbolExportInfo {
+                            level: SymbolExportLevel::Rust,
+                            kind: SymbolExportKind::Text,
+                            used: false,
+                        },
+                    ));
                 }
                 _ => {
                     // Any other symbols don't qualify for sharing
@@ -421,6 +493,76 @@ pub fn symbol_name_for_instance_in_crate<'tcx>(
     }
 }
 
+/// This is the symbol name of the given instance as seen by the linker.
+///
+/// On 32-bit Windows symbols are decorated according to their calling conventions.
+pub fn linking_symbol_name_for_instance_in_crate<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    symbol: ExportedSymbol<'tcx>,
+    instantiating_crate: CrateNum,
+) -> String {
+    use rustc_target::abi::call::Conv;
+
+    let mut undecorated = symbol_name_for_instance_in_crate(tcx, symbol, instantiating_crate);
+
+    let target = &tcx.sess.target;
+    if !target.is_like_windows {
+        // Mach-O has a global "_" suffix and `object` crate will handle it.
+        // ELF does not have any symbol decorations.
+        return undecorated;
+    }
+
+    let x86 = match &target.arch[..] {
+        "x86" => true,
+        "x86_64" => false,
+        // Only x86/64 use symbol decorations.
+        _ => return undecorated,
+    };
+
+    let instance = match symbol {
+        ExportedSymbol::NonGeneric(def_id) | ExportedSymbol::Generic(def_id, _)
+            if tcx.is_static(def_id) =>
+        {
+            None
+        }
+        ExportedSymbol::NonGeneric(def_id) => Some(Instance::mono(tcx, def_id)),
+        ExportedSymbol::Generic(def_id, substs) => Some(Instance::new(def_id, substs)),
+        // DropGlue always use the Rust calling convention and thus follow the target's default
+        // symbol decoration scheme.
+        ExportedSymbol::DropGlue(..) => None,
+        // NoDefId always follow the target's default symbol decoration scheme.
+        ExportedSymbol::NoDefId(..) => None,
+    };
+
+    let (conv, args) = instance
+        .map(|i| {
+            tcx.fn_abi_of_instance(ty::ParamEnv::reveal_all().and((i, ty::List::empty())))
+                .unwrap_or_else(|_| bug!("fn_abi_of_instance({i:?}) failed"))
+        })
+        .map(|fnabi| (fnabi.conv, &fnabi.args[..]))
+        .unwrap_or((Conv::Rust, &[]));
+
+    // Decorate symbols with prefices, suffices and total number of bytes of arguments.
+    // Reference: https://docs.microsoft.com/en-us/cpp/build/reference/decorated-names?view=msvc-170
+    let (prefix, suffix) = match conv {
+        Conv::X86Fastcall => ("@", "@"),
+        Conv::X86Stdcall => ("_", "@"),
+        Conv::X86VectorCall => ("", "@@"),
+        _ => {
+            if x86 {
+                undecorated.insert(0, '_');
+            }
+            return undecorated;
+        }
+    };
+
+    let args_in_bytes: u64 = args
+        .iter()
+        .map(|abi| abi.layout.size.bytes().next_multiple_of(target.pointer_width as u64 / 8))
+        .sum();
+    format!("{prefix}{undecorated}{suffix}{args_in_bytes}")
+}
+
 fn wasm_import_module_map(tcx: TyCtxt<'_>, cnum: CrateNum) -> FxHashMap<DefId, String> {
     // Build up a map from DefId to a `NativeLib` structure, where
     // `NativeLib` internally contains information about
diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs
index d5d21775f0a..98dc5fe8d64 100644
--- a/compiler/rustc_codegen_ssa/src/back/write.rs
+++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -23,7 +23,7 @@ use rustc_incremental::{
 };
 use rustc_metadata::EncodedMetadata;
 use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
-use rustc_middle::middle::exported_symbols::SymbolExportLevel;
+use rustc_middle::middle::exported_symbols::SymbolExportInfo;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::cgu_reuse_tracker::CguReuseTracker;
 use rustc_session::config::{self, CrateType, Lto, OutputFilenames, OutputType};
@@ -304,7 +304,7 @@ pub type TargetMachineFactoryFn<B> = Arc<
         + Sync,
 >;
 
-pub type ExportedSymbols = FxHashMap<CrateNum, Arc<Vec<(String, SymbolExportLevel)>>>;
+pub type ExportedSymbols = FxHashMap<CrateNum, Arc<Vec<(String, SymbolExportInfo)>>>;
 
 /// Additional resources used by optimize_and_codegen (not module specific)
 #[derive(Clone)]
diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs
index 7933afb50e8..019c9c179d8 100644
--- a/compiler/rustc_codegen_ssa/src/base.rs
+++ b/compiler/rustc_codegen_ssa/src/base.rs
@@ -801,6 +801,12 @@ impl CrateInfo {
             .iter()
             .map(|&c| (c, crate::back::linker::exported_symbols(tcx, c)))
             .collect();
+        let linked_symbols = tcx
+            .sess
+            .crate_types()
+            .iter()
+            .map(|&c| (c, crate::back::linker::linked_symbols(tcx, c)))
+            .collect();
         let local_crate_name = tcx.crate_name(LOCAL_CRATE);
         let crate_attrs = tcx.hir().attrs(rustc_hir::CRATE_HIR_ID);
         let subsystem = tcx.sess.first_attr_value_str_by_name(crate_attrs, sym::windows_subsystem);
@@ -834,6 +840,7 @@ impl CrateInfo {
         let mut info = CrateInfo {
             target_cpu,
             exported_symbols,
+            linked_symbols,
             local_crate_name,
             compiler_builtins: None,
             profiler_runtime: None,
diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs
index ae54442e884..1574b30497b 100644
--- a/compiler/rustc_codegen_ssa/src/common.rs
+++ b/compiler/rustc_codegen_ssa/src/common.rs
@@ -2,7 +2,6 @@
 
 use rustc_errors::struct_span_err;
 use rustc_hir as hir;
-use rustc_hir::def_id::DefId;
 use rustc_hir::LangItem;
 use rustc_middle::mir::interpret::ConstValue;
 use rustc_middle::ty::{self, layout::TyAndLayout, Ty, TyCtxt};
@@ -10,7 +9,6 @@ use rustc_session::Session;
 use rustc_span::Span;
 
 use crate::base;
-use crate::traits::BuilderMethods;
 use crate::traits::*;
 
 pub enum IntPredicate {
@@ -118,14 +116,15 @@ mod temp_stable_hash_impls {
     }
 }
 
-pub fn langcall(tcx: TyCtxt<'_>, span: Option<Span>, msg: &str, li: LangItem) -> DefId {
-    tcx.lang_items().require(li).unwrap_or_else(|s| {
-        let msg = format!("{} {}", msg, s);
-        match span {
-            Some(span) => tcx.sess.span_fatal(span, &msg),
-            None => tcx.sess.fatal(&msg),
-        }
-    })
+pub fn build_langcall<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
+    bx: &Bx,
+    span: Option<Span>,
+    li: LangItem,
+) -> (Bx::FnAbiOfResult, Bx::Value) {
+    let tcx = bx.tcx();
+    let def_id = tcx.require_lang_item(li, span);
+    let instance = ty::Instance::mono(tcx, def_id);
+    (bx.fn_abi_of_instance(instance, ty::List::empty()), bx.get_fn_addr(instance))
 }
 
 // To avoid UB from LLVM, these two functions mask RHS with an
diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs
index 5273b6cc837..05d32972dab 100644
--- a/compiler/rustc_codegen_ssa/src/lib.rs
+++ b/compiler/rustc_codegen_ssa/src/lib.rs
@@ -7,6 +7,7 @@
 #![feature(nll)]
 #![feature(associated_type_bounds)]
 #![feature(strict_provenance)]
+#![feature(int_roundings)]
 #![recursion_limit = "256"]
 #![allow(rustc::potential_query_instability)]
 
@@ -28,6 +29,7 @@ use rustc_hir::def_id::CrateNum;
 use rustc_hir::LangItem;
 use rustc_middle::dep_graph::WorkProduct;
 use rustc_middle::middle::dependency_format::Dependencies;
+use rustc_middle::middle::exported_symbols::SymbolExportKind;
 use rustc_middle::ty::query::{ExternProviders, Providers};
 use rustc_serialize::{opaque, Decodable, Decoder, Encoder};
 use rustc_session::config::{CrateType, OutputFilenames, OutputType, RUST_CGU_EXT};
@@ -141,6 +143,7 @@ impl From<&cstore::NativeLib> for NativeLib {
 pub struct CrateInfo {
     pub target_cpu: String,
     pub exported_symbols: FxHashMap<CrateType, Vec<String>>,
+    pub linked_symbols: FxHashMap<CrateType, Vec<(String, SymbolExportKind)>>,
     pub local_crate_name: Symbol,
     pub compiler_builtins: Option<CrateNum>,
     pub profiler_runtime: Option<CrateNum>,
diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs
index b7d760bfbab..a185eb298e0 100644
--- a/compiler/rustc_codegen_ssa/src/mir/block.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/block.rs
@@ -489,11 +489,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             }
         };
 
-        // Obtain the panic entry point.
-        let def_id = common::langcall(bx.tcx(), Some(span), "", lang_item);
-        let instance = ty::Instance::mono(bx.tcx(), def_id);
-        let fn_abi = bx.fn_abi_of_instance(instance, ty::List::empty());
-        let llfn = bx.get_fn_addr(instance);
+        let (fn_abi, llfn) = common::build_langcall(&bx, Some(span), lang_item);
 
         // Codegen the actual panic invoke/call.
         helper.do_call(self, &mut bx, fn_abi, llfn, &args, None, cleanup);
@@ -509,10 +505,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         self.set_debug_loc(&mut bx, terminator.source_info);
 
         // Obtain the panic entry point.
-        let def_id = common::langcall(bx.tcx(), Some(span), "", LangItem::PanicNoUnwind);
-        let instance = ty::Instance::mono(bx.tcx(), def_id);
-        let fn_abi = bx.fn_abi_of_instance(instance, ty::List::empty());
-        let llfn = bx.get_fn_addr(instance);
+        let (fn_abi, llfn) = common::build_langcall(&bx, Some(span), LangItem::PanicNoUnwind);
 
         // Codegen the actual panic invoke/call.
         helper.do_call(self, &mut bx, fn_abi, llfn, &[], None, None);
@@ -573,12 +566,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 let location = self.get_caller_location(bx, source_info).immediate();
 
                 // Obtain the panic entry point.
-                // FIXME: dedup this with `codegen_assert_terminator` above.
-                let def_id =
-                    common::langcall(bx.tcx(), Some(source_info.span), "", LangItem::Panic);
-                let instance = ty::Instance::mono(bx.tcx(), def_id);
-                let fn_abi = bx.fn_abi_of_instance(instance, ty::List::empty());
-                let llfn = bx.get_fn_addr(instance);
+                let (fn_abi, llfn) =
+                    common::build_langcall(bx, Some(source_info.span), LangItem::Panic);
 
                 // Codegen the actual panic invoke/call.
                 helper.do_call(
@@ -1440,10 +1429,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             let llretty = self.landing_pad_type();
             bx.cleanup_landing_pad(llretty, llpersonality);
 
-            let def_id = common::langcall(bx.tcx(), None, "", LangItem::PanicNoUnwind);
-            let instance = ty::Instance::mono(bx.tcx(), def_id);
-            let fn_abi = bx.fn_abi_of_instance(instance, ty::List::empty());
-            let fn_ptr = bx.get_fn_addr(instance);
+            let (fn_abi, fn_ptr) = common::build_langcall(&bx, None, LangItem::PanicNoUnwind);
             let fn_ty = bx.fn_decl_backend_type(&fn_abi);
 
             let llret = bx.call(fn_ty, fn_ptr, &[], None);
diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
index bb53c722a24..f2d1827c792 100644
--- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
@@ -39,8 +39,7 @@ pub struct PerLocalVarDebugInfo<'tcx, D> {
 
 #[derive(Clone, Copy, Debug)]
 pub struct DebugScope<S, L> {
-    // FIXME(eddyb) this should never be `None`, after initialization.
-    pub dbg_scope: Option<S>,
+    pub dbg_scope: S,
 
     /// Call site location, if this scope was inlined from another function.
     pub inlined_at: Option<L>,
@@ -61,17 +60,12 @@ impl<'tcx, S: Copy, L: Copy> DebugScope<S, L> {
         cx: &Cx,
         span: Span,
     ) -> S {
-        // FIXME(eddyb) this should never be `None`.
-        let dbg_scope = self
-            .dbg_scope
-            .unwrap_or_else(|| bug!("`dbg_scope` is only `None` during initialization"));
-
         let pos = span.lo();
         if pos < self.file_start_pos || pos >= self.file_end_pos {
             let sm = cx.sess().source_map();
-            cx.extend_scope_to_file(dbg_scope, &sm.lookup_char_pos(pos).file)
+            cx.extend_scope_to_file(self.dbg_scope, &sm.lookup_char_pos(pos).file)
         } else {
-            dbg_scope
+            self.dbg_scope
         }
     }
 }
diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs
index 6c139df0a85..0c958de64fa 100644
--- a/compiler/rustc_codegen_ssa/src/mir/mod.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs
@@ -244,7 +244,6 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     fx.debug_introduce_locals(&mut bx);
 
     // Codegen the body of each block using reverse postorder
-    // FIXME(eddyb) reuse RPO iterator between `analysis` and this.
     for (bb, _) in traversal::reverse_postorder(&mir) {
         fx.codegen_block(bb);
     }
diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
index 7cca6178ab2..38fecf7232e 100644
--- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
+++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
@@ -106,6 +106,7 @@ pub(super) fn mk_eval_cx<'mir, 'tcx>(
 
 /// This function converts an interpreter value into a constant that is meant for use in the
 /// type system.
+#[instrument(skip(ecx), level = "debug")]
 pub(super) fn op_to_const<'tcx>(
     ecx: &CompileTimeEvalContext<'_, 'tcx>,
     op: &OpTy<'tcx>,
@@ -140,21 +141,26 @@ pub(super) fn op_to_const<'tcx>(
         op.try_as_mplace()
     };
 
+    debug!(?immediate);
+
     // We know `offset` is relative to the allocation, so we can use `into_parts`.
-    let to_const_value = |mplace: &MPlaceTy<'_>| match mplace.ptr.into_parts() {
-        (Some(alloc_id), offset) => {
-            let alloc = ecx.tcx.global_alloc(alloc_id).unwrap_memory();
-            ConstValue::ByRef { alloc, offset }
-        }
-        (None, offset) => {
-            assert!(mplace.layout.is_zst());
-            assert_eq!(
-                offset.bytes() % mplace.layout.align.abi.bytes(),
-                0,
-                "this MPlaceTy must come from a validated constant, thus we can assume the \
+    let to_const_value = |mplace: &MPlaceTy<'_>| {
+        debug!("to_const_value(mplace: {:?})", mplace);
+        match mplace.ptr.into_parts() {
+            (Some(alloc_id), offset) => {
+                let alloc = ecx.tcx.global_alloc(alloc_id).unwrap_memory();
+                ConstValue::ByRef { alloc, offset }
+            }
+            (None, offset) => {
+                assert!(mplace.layout.is_zst());
+                assert_eq!(
+                    offset.bytes() % mplace.layout.align.abi.bytes(),
+                    0,
+                    "this MPlaceTy must come from a validated constant, thus we can assume the \
                 alignment is correct",
-            );
-            ConstValue::Scalar(Scalar::ZST)
+                );
+                ConstValue::Scalar(Scalar::ZST)
+            }
         }
     };
     match immediate {
@@ -166,6 +172,7 @@ pub(super) fn op_to_const<'tcx>(
                 ScalarMaybeUninit::Uninit => to_const_value(&op.assert_mem_place()),
             },
             Immediate::ScalarPair(a, b) => {
+                debug!("ScalarPair(a: {:?}, b: {:?})", a, b);
                 // We know `offset` is relative to the allocation, so we can use `into_parts`.
                 let (data, start) =
                     match ecx.scalar_to_ptr(a.check_init().unwrap()).unwrap().into_parts() {
@@ -209,7 +216,10 @@ fn turn_into_const_value<'tcx>(
     );
 
     // Turn this into a proper constant.
-    op_to_const(&ecx, &mplace.into())
+    let const_val = op_to_const(&ecx, &mplace.into());
+    debug!(?const_val);
+
+    const_val
 }
 
 pub fn eval_to_const_value_raw_provider<'tcx>(
diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs
index 80270f82563..96c18d488ee 100644
--- a/compiler/rustc_const_eval/src/const_eval/mod.rs
+++ b/compiler/rustc_const_eval/src/const_eval/mod.rs
@@ -3,29 +3,26 @@
 use std::convert::TryFrom;
 
 use rustc_hir::Mutability;
-use rustc_middle::ty::layout::HasTyCtxt;
+use rustc_middle::mir;
 use rustc_middle::ty::{self, TyCtxt};
-use rustc_middle::{
-    mir::{self, interpret::ConstAlloc},
-    ty::ScalarInt,
-};
 use rustc_span::{source_map::DUMMY_SP, symbol::Symbol};
-use rustc_target::abi::VariantIdx;
 
 use crate::interpret::{
-    intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, InterpResult, MPlaceTy,
-    MemPlaceMeta, Scalar,
+    intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, InterpResult, MemPlaceMeta,
+    Scalar,
 };
 
 mod error;
 mod eval_queries;
 mod fn_queries;
 mod machine;
+mod valtrees;
 
 pub use error::*;
 pub use eval_queries::*;
 pub use fn_queries::*;
 pub use machine::*;
+pub(crate) use valtrees::{const_to_valtree, valtree_to_const_value};
 
 pub(crate) fn const_caller_location(
     tcx: TyCtxt<'_>,
@@ -38,129 +35,7 @@ pub(crate) fn const_caller_location(
     if intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &loc_place).is_err() {
         bug!("intern_const_alloc_recursive should not error in this case")
     }
-    ConstValue::Scalar(Scalar::from_pointer(loc_place.ptr.into_pointer_or_addr().unwrap(), &tcx))
-}
-
-/// Convert an evaluated constant to a type level constant
-pub(crate) fn const_to_valtree<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
-    raw: ConstAlloc<'tcx>,
-) -> Option<ty::ValTree<'tcx>> {
-    let ecx = mk_eval_cx(
-        tcx, DUMMY_SP, param_env,
-        // It is absolutely crucial for soundness that
-        // we do not read from static items or other mutable memory.
-        false,
-    );
-    let place = ecx.raw_const_to_mplace(raw).unwrap();
-    const_to_valtree_inner(&ecx, &place)
-}
-
-#[instrument(skip(ecx), level = "debug")]
-fn branches<'tcx>(
-    ecx: &CompileTimeEvalContext<'tcx, 'tcx>,
-    place: &MPlaceTy<'tcx>,
-    n: usize,
-    variant: Option<VariantIdx>,
-) -> Option<ty::ValTree<'tcx>> {
-    let place = match variant {
-        Some(variant) => ecx.mplace_downcast(&place, variant).unwrap(),
-        None => *place,
-    };
-    let variant = variant.map(|variant| Some(ty::ValTree::Leaf(ScalarInt::from(variant.as_u32()))));
-    debug!(?place, ?variant);
-
-    let fields = (0..n).map(|i| {
-        let field = ecx.mplace_field(&place, i).unwrap();
-        const_to_valtree_inner(ecx, &field)
-    });
-    // For enums, we prepend their variant index before the variant's fields so we can figure out
-    // the variant again when just seeing a valtree.
-    let branches = variant.into_iter().chain(fields);
-    Some(ty::ValTree::Branch(ecx.tcx.arena.alloc_from_iter(branches.collect::<Option<Vec<_>>>()?)))
-}
-
-fn slice_branches<'tcx>(
-    ecx: &CompileTimeEvalContext<'tcx, 'tcx>,
-    place: &MPlaceTy<'tcx>,
-) -> Option<ty::ValTree<'tcx>> {
-    let n = place.len(&ecx.tcx()).expect(&format!("expected to use len of place {:?}", place));
-    let branches = (0..n).map(|i| {
-        let place_elem = ecx.mplace_index(place, i).unwrap();
-        const_to_valtree_inner(ecx, &place_elem)
-    });
-
-    Some(ty::ValTree::Branch(ecx.tcx.arena.alloc_from_iter(branches.collect::<Option<Vec<_>>>()?)))
-}
-
-#[instrument(skip(ecx), level = "debug")]
-fn const_to_valtree_inner<'tcx>(
-    ecx: &CompileTimeEvalContext<'tcx, 'tcx>,
-    place: &MPlaceTy<'tcx>,
-) -> Option<ty::ValTree<'tcx>> {
-    match place.layout.ty.kind() {
-        ty::FnDef(..) => Some(ty::ValTree::zst()),
-        ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => {
-            let val = ecx.read_immediate(&place.into()).unwrap();
-            let val = val.to_scalar().unwrap();
-            Some(ty::ValTree::Leaf(val.assert_int()))
-        }
-
-        // Raw pointers are not allowed in type level constants, as we cannot properly test them for
-        // equality at compile-time (see `ptr_guaranteed_eq`/`_ne`).
-        // Technically we could allow function pointers (represented as `ty::Instance`), but this is not guaranteed to
-        // agree with runtime equality tests.
-        ty::FnPtr(_) | ty::RawPtr(_) => None,
-
-        ty::Ref(_, _, _)  => {
-            let derefd_place = ecx.deref_operand(&place.into()).unwrap_or_else(|e| bug!("couldn't deref {:?}, error: {:?}", place, e));
-            debug!(?derefd_place);
-
-            const_to_valtree_inner(ecx, &derefd_place)
-        }
-
-        ty::Str | ty::Slice(_) | ty::Array(_, _) => {
-            let valtree = slice_branches(ecx, place);
-            debug!(?valtree);
-
-            valtree
-        }
-        // Trait objects are not allowed in type level constants, as we have no concept for
-        // resolving their backing type, even if we can do that at const eval time. We may
-        // hypothetically be able to allow `dyn StructuralEq` trait objects in the future,
-        // but it is unclear if this is useful.
-        ty::Dynamic(..) => None,
-
-        ty::Tuple(substs) => branches(ecx, place, substs.len(), None),
-
-        ty::Adt(def, _) => {
-            if def.variants().is_empty() {
-                bug!("uninhabited types should have errored and never gotten converted to valtree")
-            }
-
-            let variant = ecx.read_discriminant(&place.into()).unwrap().1;
-
-            branches(ecx, place, def.variant(variant).fields.len(), def.is_enum().then_some(variant))
-        }
-
-        ty::Never
-        | ty::Error(_)
-        | ty::Foreign(..)
-        | ty::Infer(ty::FreshIntTy(_))
-        | ty::Infer(ty::FreshFloatTy(_))
-        | ty::Projection(..)
-        | ty::Param(_)
-        | ty::Bound(..)
-        | ty::Placeholder(..)
-        // FIXME(oli-obk): we could look behind opaque types
-        | ty::Opaque(..)
-        | ty::Infer(_)
-        // FIXME(oli-obk): we can probably encode closures just like structs
-        | ty::Closure(..)
-        | ty::Generator(..)
-        | ty::GeneratorWitness(..) => None,
-    }
+    ConstValue::Scalar(Scalar::from_maybe_pointer(loc_place.ptr, &tcx))
 }
 
 /// This function should never fail for validated constants. However, it is also invoked from the
@@ -202,6 +77,7 @@ pub(crate) fn try_destructure_const<'tcx>(
     Ok(mir::DestructuredConst { variant, fields })
 }
 
+#[instrument(skip(tcx), level = "debug")]
 pub(crate) fn deref_const<'tcx>(
     tcx: TyCtxt<'tcx>,
     param_env: ty::ParamEnv<'tcx>,
diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs
new file mode 100644
index 00000000000..374179d0cc2
--- /dev/null
+++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs
@@ -0,0 +1,459 @@
+use super::eval_queries::{mk_eval_cx, op_to_const};
+use super::machine::CompileTimeEvalContext;
+use crate::interpret::{
+    intern_const_alloc_recursive, ConstValue, ImmTy, Immediate, InternKind, MemPlaceMeta,
+    MemoryKind, PlaceTy, Scalar, ScalarMaybeUninit,
+};
+use rustc_middle::mir::interpret::ConstAlloc;
+use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt};
+use rustc_span::source_map::DUMMY_SP;
+use rustc_target::abi::{Align, VariantIdx};
+
+use crate::interpret::MPlaceTy;
+use crate::interpret::Value;
+
+/// Convert an evaluated constant to a type level constant
+#[instrument(skip(tcx), level = "debug")]
+pub(crate) fn const_to_valtree<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    raw: ConstAlloc<'tcx>,
+) -> Option<ty::ValTree<'tcx>> {
+    let ecx = mk_eval_cx(
+        tcx, DUMMY_SP, param_env,
+        // It is absolutely crucial for soundness that
+        // we do not read from static items or other mutable memory.
+        false,
+    );
+    let place = ecx.raw_const_to_mplace(raw).unwrap();
+    const_to_valtree_inner(&ecx, &place)
+}
+
+#[instrument(skip(ecx), level = "debug")]
+fn branches<'tcx>(
+    ecx: &CompileTimeEvalContext<'tcx, 'tcx>,
+    place: &MPlaceTy<'tcx>,
+    n: usize,
+    variant: Option<VariantIdx>,
+) -> Option<ty::ValTree<'tcx>> {
+    let place = match variant {
+        Some(variant) => ecx.mplace_downcast(&place, variant).unwrap(),
+        None => *place,
+    };
+    let variant = variant.map(|variant| Some(ty::ValTree::Leaf(ScalarInt::from(variant.as_u32()))));
+    debug!(?place, ?variant);
+
+    let fields = (0..n).map(|i| {
+        let field = ecx.mplace_field(&place, i).unwrap();
+        const_to_valtree_inner(ecx, &field)
+    });
+    // For enums, we preped their variant index before the variant's fields so we can figure out
+    // the variant again when just seeing a valtree.
+    let branches = variant.into_iter().chain(fields);
+    Some(ty::ValTree::Branch(ecx.tcx.arena.alloc_from_iter(branches.collect::<Option<Vec<_>>>()?)))
+}
+
+#[instrument(skip(ecx), level = "debug")]
+fn slice_branches<'tcx>(
+    ecx: &CompileTimeEvalContext<'tcx, 'tcx>,
+    place: &MPlaceTy<'tcx>,
+) -> Option<ty::ValTree<'tcx>> {
+    let n = place.len(&ecx.tcx.tcx).expect(&format!("expected to use len of place {:?}", place));
+    let branches = (0..n).map(|i| {
+        let place_elem = ecx.mplace_index(place, i).unwrap();
+        const_to_valtree_inner(ecx, &place_elem)
+    });
+
+    Some(ty::ValTree::Branch(ecx.tcx.arena.alloc_from_iter(branches.collect::<Option<Vec<_>>>()?)))
+}
+
+#[instrument(skip(ecx), level = "debug")]
+fn const_to_valtree_inner<'tcx>(
+    ecx: &CompileTimeEvalContext<'tcx, 'tcx>,
+    place: &MPlaceTy<'tcx>,
+) -> Option<ty::ValTree<'tcx>> {
+    match place.layout.ty.kind() {
+        ty::FnDef(..) => Some(ty::ValTree::zst()),
+        ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => {
+            let val = ecx.read_immediate(&place.into()).unwrap();
+            let val = val.to_scalar().unwrap();
+            Some(ty::ValTree::Leaf(val.assert_int()))
+        }
+
+        // Raw pointers are not allowed in type level constants, as we cannot properly test them for
+        // equality at compile-time (see `ptr_guaranteed_eq`/`_ne`).
+        // Technically we could allow function pointers (represented as `ty::Instance`), but this is not guaranteed to
+        // agree with runtime equality tests.
+        ty::FnPtr(_) | ty::RawPtr(_) => None,
+
+        ty::Ref(_, _, _)  => {
+            let derefd_place = ecx.deref_operand(&place.into()).unwrap_or_else(|e| bug!("couldn't deref {:?}, error: {:?}", place, e));
+            debug!(?derefd_place);
+
+            const_to_valtree_inner(ecx, &derefd_place)
+        }
+
+        ty::Str | ty::Slice(_) | ty::Array(_, _) => {
+            let valtree = slice_branches(ecx, place);
+            debug!(?valtree);
+
+            valtree
+        }
+        // Trait objects are not allowed in type level constants, as we have no concept for
+        // resolving their backing type, even if we can do that at const eval time. We may
+        // hypothetically be able to allow `dyn StructuralEq` trait objects in the future,
+        // but it is unclear if this is useful.
+        ty::Dynamic(..) => None,
+
+        ty::Tuple(substs) => branches(ecx, place, substs.len(), None),
+
+        ty::Adt(def, _) => {
+            if def.is_union() {
+                return None
+            } else if def.variants().is_empty() {
+                bug!("uninhabited types should have errored and never gotten converted to valtree")
+            }
+
+            let variant = ecx.read_discriminant(&place.into()).unwrap().1;
+
+            branches(ecx, place, def.variant(variant).fields.len(), def.is_enum().then_some(variant))
+        }
+
+        ty::Never
+        | ty::Error(_)
+        | ty::Foreign(..)
+        | ty::Infer(ty::FreshIntTy(_))
+        | ty::Infer(ty::FreshFloatTy(_))
+        | ty::Projection(..)
+        | ty::Param(_)
+        | ty::Bound(..)
+        | ty::Placeholder(..)
+        // FIXME(oli-obk): we could look behind opaque types
+        | ty::Opaque(..)
+        | ty::Infer(_)
+        // FIXME(oli-obk): we can probably encode closures just like structs
+        | ty::Closure(..)
+        | ty::Generator(..)
+        | ty::GeneratorWitness(..) => None,
+    }
+}
+
+#[instrument(skip(ecx), level = "debug")]
+fn create_mplace_from_layout<'tcx>(
+    ecx: &mut CompileTimeEvalContext<'tcx, 'tcx>,
+    ty: Ty<'tcx>,
+) -> MPlaceTy<'tcx> {
+    let tcx = ecx.tcx;
+    let param_env = ecx.param_env;
+    let layout = tcx.layout_of(param_env.and(ty)).unwrap();
+    debug!(?layout);
+
+    ecx.allocate(layout, MemoryKind::Stack).unwrap()
+}
+
+// Walks custom DSTs and gets the type of the unsized field and the number of elements
+// in the unsized field.
+fn get_info_on_unsized_field<'tcx>(
+    ty: Ty<'tcx>,
+    valtree: ty::ValTree<'tcx>,
+    tcx: TyCtxt<'tcx>,
+) -> (Ty<'tcx>, usize) {
+    let mut last_valtree = valtree;
+    let tail = tcx.struct_tail_with_normalize(
+        ty,
+        |ty| ty,
+        || {
+            let branches = last_valtree.unwrap_branch();
+            last_valtree = branches[branches.len() - 1];
+            debug!(?branches, ?last_valtree);
+        },
+    );
+    let unsized_inner_ty = match tail.kind() {
+        ty::Slice(t) => *t,
+        ty::Str => tail,
+        _ => bug!("expected Slice or Str"),
+    };
+
+    // Have to adjust type for ty::Str
+    let unsized_inner_ty = match unsized_inner_ty.kind() {
+        ty::Str => tcx.mk_ty(ty::Uint(ty::UintTy::U8)),
+        _ => unsized_inner_ty,
+    };
+
+    // Get the number of elements in the unsized field
+    let num_elems = last_valtree.unwrap_branch().len();
+
+    (unsized_inner_ty, num_elems)
+}
+
+#[instrument(skip(ecx), level = "debug")]
+fn create_pointee_place<'tcx>(
+    ecx: &mut CompileTimeEvalContext<'tcx, 'tcx>,
+    ty: Ty<'tcx>,
+    valtree: ty::ValTree<'tcx>,
+) -> MPlaceTy<'tcx> {
+    let tcx = ecx.tcx.tcx;
+
+    if !ty.is_sized(ecx.tcx, ty::ParamEnv::empty()) {
+        // We need to create `Allocation`s for custom DSTs
+
+        let (unsized_inner_ty, num_elems) = get_info_on_unsized_field(ty, valtree, tcx);
+        let unsized_inner_ty = match unsized_inner_ty.kind() {
+            ty::Str => tcx.mk_ty(ty::Uint(ty::UintTy::U8)),
+            _ => unsized_inner_ty,
+        };
+        let unsized_inner_ty_size =
+            tcx.layout_of(ty::ParamEnv::empty().and(unsized_inner_ty)).unwrap().layout.size();
+        debug!(?unsized_inner_ty, ?unsized_inner_ty_size, ?num_elems);
+
+        // for custom DSTs only the last field/element is unsized, but we need to also allocate
+        // space for the other fields/elements
+        let layout = tcx.layout_of(ty::ParamEnv::empty().and(ty)).unwrap();
+        let size_of_sized_part = layout.layout.size();
+
+        // Get the size of the memory behind the DST
+        let dst_size = unsized_inner_ty_size.checked_mul(num_elems as u64, &tcx).unwrap();
+
+        let ptr = ecx
+            .allocate_ptr(
+                size_of_sized_part.checked_add(dst_size, &tcx).unwrap(),
+                Align::from_bytes(1).unwrap(),
+                MemoryKind::Stack,
+            )
+            .unwrap();
+        debug!(?ptr);
+
+        let mut place = MPlaceTy::from_aligned_ptr(ptr.into(), layout);
+        place.meta = MemPlaceMeta::Meta(Scalar::from_u64(num_elems as u64));
+        debug!(?place);
+
+        place
+    } else {
+        create_mplace_from_layout(ecx, ty)
+    }
+}
+
+/// Converts a `ValTree` to a `ConstValue`, which is needed after mir
+/// construction has finished.
+// FIXME Merge `valtree_to_const_value` and `fill_place_recursively` into one function
+#[instrument(skip(tcx), level = "debug")]
+pub fn valtree_to_const_value<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    param_env_ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
+    valtree: ty::ValTree<'tcx>,
+) -> ConstValue<'tcx> {
+    // Basic idea: We directly construct `Scalar` values from trivial `ValTree`s
+    // (those for constants with type bool, int, uint, float or char).
+    // For all other types we create an `MPlace` and fill that by walking
+    // the `ValTree` and using `place_projection` and `place_field` to
+    // create inner `MPlace`s which are filled recursively.
+    // FIXME Does this need an example?
+
+    let (param_env, ty) = param_env_ty.into_parts();
+    let mut ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
+
+    match ty.kind() {
+        ty::FnDef(..) => {
+            assert!(valtree.unwrap_branch().is_empty());
+            ConstValue::Scalar(Scalar::ZST)
+        }
+        ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => match valtree {
+            ty::ValTree::Leaf(scalar_int) => ConstValue::Scalar(Scalar::Int(scalar_int)),
+            ty::ValTree::Branch(_) => bug!(
+                "ValTrees for Bool, Int, Uint, Float or Char should have the form ValTree::Leaf"
+            ),
+        },
+        ty::Ref(_, _, _) | ty::Tuple(_) | ty::Array(_, _) | ty::Adt(..) => {
+            let mut place = match ty.kind() {
+                ty::Ref(_, inner_ty, _) => {
+                    // Need to create a place for the pointee to fill for Refs
+                    create_pointee_place(&mut ecx, *inner_ty, valtree)
+                }
+                _ => create_mplace_from_layout(&mut ecx, ty),
+            };
+            debug!(?place);
+
+            fill_place_recursively(&mut ecx, &mut place, valtree);
+            dump_place(&ecx, place.into());
+            intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &place).unwrap();
+
+            let const_val = match ty.kind() {
+                ty::Ref(_, _, _) => {
+                    let ref_place = place.to_ref(&tcx);
+                    let imm =
+                        ImmTy::from_immediate(ref_place, tcx.layout_of(param_env_ty).unwrap());
+
+                    op_to_const(&ecx, &imm.into())
+                }
+                _ => op_to_const(&ecx, &place.into()),
+            };
+            debug!(?const_val);
+
+            const_val
+        }
+        ty::Never
+        | ty::Error(_)
+        | ty::Foreign(..)
+        | ty::Infer(ty::FreshIntTy(_))
+        | ty::Infer(ty::FreshFloatTy(_))
+        | ty::Projection(..)
+        | ty::Param(_)
+        | ty::Bound(..)
+        | ty::Placeholder(..)
+        | ty::Opaque(..)
+        | ty::Infer(_)
+        | ty::Closure(..)
+        | ty::Generator(..)
+        | ty::GeneratorWitness(..)
+        | ty::FnPtr(_)
+        | ty::RawPtr(_)
+        | ty::Str
+        | ty::Slice(_)
+        | ty::Dynamic(..) => bug!("no ValTree should have been created for type {:?}", ty.kind()),
+    }
+}
+
+// FIXME Needs a better/correct name
+#[instrument(skip(ecx), level = "debug")]
+fn fill_place_recursively<'tcx>(
+    ecx: &mut CompileTimeEvalContext<'tcx, 'tcx>,
+    place: &mut MPlaceTy<'tcx>,
+    valtree: ty::ValTree<'tcx>,
+) {
+    // This will match on valtree and write the value(s) corresponding to the ValTree
+    // inside the place recursively.
+
+    let tcx = ecx.tcx.tcx;
+    let ty = place.layout.ty;
+
+    match ty.kind() {
+        ty::FnDef(_, _) => {
+            ecx.write_immediate(
+                Immediate::Scalar(ScalarMaybeUninit::Scalar(Scalar::ZST)),
+                &(*place).into(),
+            )
+            .unwrap();
+        }
+        ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => {
+            let scalar_int = valtree.unwrap_leaf();
+            debug!("writing trivial valtree {:?} to place {:?}", scalar_int, place);
+            ecx.write_immediate(
+                Immediate::Scalar(ScalarMaybeUninit::Scalar(scalar_int.into())),
+                &(*place).into(),
+            )
+            .unwrap();
+        }
+        ty::Ref(_, inner_ty, _) => {
+            let mut pointee_place = create_pointee_place(ecx, *inner_ty, valtree);
+            debug!(?pointee_place);
+
+            fill_place_recursively(ecx, &mut pointee_place, valtree);
+            dump_place(ecx, pointee_place.into());
+            intern_const_alloc_recursive(ecx, InternKind::Constant, &pointee_place).unwrap();
+
+            let imm = match inner_ty.kind() {
+                ty::Slice(_) | ty::Str => {
+                    let len = valtree.unwrap_branch().len();
+                    let len_scalar = ScalarMaybeUninit::Scalar(Scalar::from_u64(len as u64));
+
+                    Immediate::ScalarPair(
+                        ScalarMaybeUninit::from_maybe_pointer((*pointee_place).ptr, &tcx),
+                        len_scalar,
+                    )
+                }
+                _ => pointee_place.to_ref(&tcx),
+            };
+            debug!(?imm);
+
+            ecx.write_immediate(imm, &(*place).into()).unwrap();
+        }
+        ty::Adt(_, _) | ty::Tuple(_) | ty::Array(_, _) | ty::Str | ty::Slice(_) => {
+            let branches = valtree.unwrap_branch();
+
+            // Need to downcast place for enums
+            let (place_adjusted, branches, variant_idx) = match ty.kind() {
+                ty::Adt(def, _) if def.is_enum() => {
+                    // First element of valtree corresponds to variant
+                    let scalar_int = branches[0].unwrap_leaf();
+                    let variant_idx = VariantIdx::from_u32(scalar_int.try_to_u32().unwrap());
+                    let variant = def.variant(variant_idx);
+                    debug!(?variant);
+
+                    (
+                        place.project_downcast(ecx, variant_idx).unwrap(),
+                        &branches[1..],
+                        Some(variant_idx),
+                    )
+                }
+                _ => (*place, branches, None),
+            };
+            debug!(?place_adjusted, ?branches);
+
+            // Create the places (by indexing into `place`) for the fields and fill
+            // them recursively
+            for (i, inner_valtree) in branches.iter().enumerate() {
+                debug!(?i, ?inner_valtree);
+
+                let mut place_inner = match ty.kind() {
+                    ty::Str | ty::Slice(_) => ecx.mplace_index(&place, i as u64).unwrap(),
+                    _ if !ty.is_sized(ecx.tcx, ty::ParamEnv::empty())
+                        && i == branches.len() - 1 =>
+                    {
+                        // Note: For custom DSTs we need to manually process the last unsized field.
+                        // We created a `Pointer` for the `Allocation` of the complete sized version of
+                        // the Adt in `create_pointee_place` and now we fill that `Allocation` with the
+                        // values in the ValTree. For the unsized field we have to additionally add the meta
+                        // data.
+
+                        let (unsized_inner_ty, num_elems) =
+                            get_info_on_unsized_field(ty, valtree, tcx);
+                        debug!(?unsized_inner_ty);
+
+                        let inner_ty = match ty.kind() {
+                            ty::Adt(def, substs) => {
+                                def.variant(VariantIdx::from_u32(0)).fields[i].ty(tcx, substs)
+                            }
+                            ty::Tuple(inner_tys) => inner_tys[i],
+                            _ => bug!("unexpected unsized type {:?}", ty),
+                        };
+
+                        let inner_layout =
+                            tcx.layout_of(ty::ParamEnv::empty().and(inner_ty)).unwrap();
+                        debug!(?inner_layout);
+
+                        let offset = place_adjusted.layout.fields.offset(i);
+                        place
+                            .offset(
+                                offset,
+                                MemPlaceMeta::Meta(Scalar::from_u64(num_elems as u64)),
+                                inner_layout,
+                                &tcx,
+                            )
+                            .unwrap()
+                    }
+                    _ => ecx.mplace_field(&place_adjusted, i).unwrap(),
+                };
+
+                debug!(?place_inner);
+                fill_place_recursively(ecx, &mut place_inner, *inner_valtree);
+                dump_place(&ecx, place_inner.into());
+            }
+
+            debug!("dump of place_adjusted:");
+            dump_place(ecx, place_adjusted.into());
+
+            if let Some(variant_idx) = variant_idx {
+                // don't forget filling the place with the discriminant of the enum
+                ecx.write_discriminant(variant_idx, &(*place).into()).unwrap();
+            }
+
+            debug!("dump of place after writing discriminant:");
+            dump_place(ecx, (*place).into());
+        }
+        _ => bug!("shouldn't have created a ValTree for {:?}", ty),
+    }
+}
+
+fn dump_place<'tcx>(ecx: &CompileTimeEvalContext<'tcx, 'tcx>, place: PlaceTy<'tcx>) {
+    trace!("{:?}", ecx.dump_place(*place));
+}
diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs
index f0fff602fe4..827959113b9 100644
--- a/compiler/rustc_const_eval/src/interpret/eval_context.rs
+++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs
@@ -679,7 +679,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         return_place: Option<&PlaceTy<'tcx, M::PointerTag>>,
         return_to_block: StackPopCleanup,
     ) -> InterpResult<'tcx> {
-        debug!("body: {:#?}", body);
+        trace!("body: {:#?}", body);
         // first push a stack frame so we have access to the local substs
         let pre_frame = Frame {
             body,
@@ -836,7 +836,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             return Ok(());
         }
 
-        debug!("locals: {:#?}", frame.locals);
+        trace!("locals: {:#?}", frame.locals);
 
         // Cleanup: deallocate all locals that are backed by an allocation.
         for local in &frame.locals {
diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs
index ddfbcbdd22e..7721485771b 100644
--- a/compiler/rustc_const_eval/src/interpret/machine.rs
+++ b/compiler/rustc_const_eval/src/interpret/machine.rs
@@ -88,6 +88,10 @@ pub trait Machine<'mir, 'tcx>: Sized {
     /// Pointers are "tagged" with provenance information; typically the `AllocId` they belong to.
     type PointerTag: Provenance + Eq + Hash + 'static;
 
+    /// When getting the AllocId of a pointer, some extra data is also obtained from the tag
+    /// that is passed to memory access hooks so they can do things with it.
+    type TagExtra: Copy + 'static;
+
     /// Machines can define extra (non-instance) things that represent values of function pointers.
     /// For example, Miri uses this to return a function pointer from `dlsym`
     /// that can later be called to execute the right thing.
@@ -122,6 +126,8 @@ pub trait Machine<'mir, 'tcx>: Sized {
 
     /// Whether, when checking alignment, we should `force_int` and thus support
     /// custom alignment logic based on whatever the integer address happens to be.
+    ///
+    /// Requires PointerTag::OFFSET_IS_ADDR to be true.
     fn force_int_for_alignment_check(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
 
     /// Whether to enforce the validity invariant
@@ -285,11 +291,14 @@ pub trait Machine<'mir, 'tcx>: Sized {
         addr: u64,
     ) -> Pointer<Option<Self::PointerTag>>;
 
-    /// Convert a pointer with provenance into an allocation-offset pair.
+    /// Convert a pointer with provenance into an allocation-offset pair
+    /// and extra provenance info.
+    ///
+    /// The returned `AllocId` must be the same as `ptr.provenance.get_alloc_id()`.
     fn ptr_get_alloc(
         ecx: &InterpCx<'mir, 'tcx, Self>,
         ptr: Pointer<Self::PointerTag>,
-    ) -> (AllocId, Size);
+    ) -> (AllocId, Size, Self::TagExtra);
 
     /// Called to initialize the "extra" state of an allocation and make the pointers
     /// it contains (in relocations) tagged.  The way we construct allocations is
@@ -321,7 +330,7 @@ pub trait Machine<'mir, 'tcx>: Sized {
         _tcx: TyCtxt<'tcx>,
         _machine: &Self,
         _alloc_extra: &Self::AllocExtra,
-        _tag: Self::PointerTag,
+        _tag: (AllocId, Self::TagExtra),
         _range: AllocRange,
     ) -> InterpResult<'tcx> {
         Ok(())
@@ -333,7 +342,7 @@ pub trait Machine<'mir, 'tcx>: Sized {
         _tcx: TyCtxt<'tcx>,
         _machine: &mut Self,
         _alloc_extra: &mut Self::AllocExtra,
-        _tag: Self::PointerTag,
+        _tag: (AllocId, Self::TagExtra),
         _range: AllocRange,
     ) -> InterpResult<'tcx> {
         Ok(())
@@ -345,7 +354,7 @@ pub trait Machine<'mir, 'tcx>: Sized {
         _tcx: TyCtxt<'tcx>,
         _machine: &mut Self,
         _alloc_extra: &mut Self::AllocExtra,
-        _tag: Self::PointerTag,
+        _tag: (AllocId, Self::TagExtra),
         _range: AllocRange,
     ) -> InterpResult<'tcx> {
         Ok(())
@@ -397,6 +406,8 @@ pub trait Machine<'mir, 'tcx>: Sized {
 // (CTFE and ConstProp) use the same instance.  Here, we share that code.
 pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
     type PointerTag = AllocId;
+    type TagExtra = ();
+
     type ExtraFnVal = !;
 
     type MemoryMap =
@@ -474,9 +485,12 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
     }
 
     #[inline(always)]
-    fn ptr_get_alloc(_ecx: &InterpCx<$mir, $tcx, Self>, ptr: Pointer<AllocId>) -> (AllocId, Size) {
+    fn ptr_get_alloc(
+        _ecx: &InterpCx<$mir, $tcx, Self>,
+        ptr: Pointer<AllocId>,
+    ) -> (AllocId, Size, Self::TagExtra) {
         // We know `offset` is relative to the allocation, so we can use `into_parts`.
         let (alloc_id, offset) = ptr.into_parts();
-        (alloc_id, offset)
+        (alloc_id, offset, ())
     }
 }
diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs
index a165fa23f30..b1d7ab6a098 100644
--- a/compiler/rustc_const_eval/src/interpret/memory.rs
+++ b/compiler/rustc_const_eval/src/interpret/memory.rs
@@ -158,8 +158,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         &self,
         ptr: Pointer<AllocId>,
     ) -> InterpResult<'tcx, Pointer<M::PointerTag>> {
-        // We know `offset` is relative to the allocation, so we can use `into_parts`.
-        let (alloc_id, offset) = ptr.into_parts();
+        let alloc_id = ptr.provenance;
         // We need to handle `extern static`.
         match self.tcx.get_global_alloc(alloc_id) {
             Some(GlobalAlloc::Static(def_id)) if self.tcx.is_thread_local_static(def_id) => {
@@ -171,7 +170,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             _ => {}
         }
         // And we need to get the tag.
-        Ok(M::tag_alloc_base_pointer(self, Pointer::new(alloc_id, offset)))
+        Ok(M::tag_alloc_base_pointer(self, ptr))
     }
 
     pub fn create_fn_alloc_ptr(
@@ -238,7 +237,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         new_align: Align,
         kind: MemoryKind<M::MemoryKind>,
     ) -> InterpResult<'tcx, Pointer<M::PointerTag>> {
-        let (alloc_id, offset, ptr) = self.ptr_get_alloc_id(ptr)?;
+        let (alloc_id, offset, _tag) = self.ptr_get_alloc_id(ptr)?;
         if offset.bytes() != 0 {
             throw_ub_format!(
                 "reallocating {:?} which does not point to the beginning of an object",
@@ -255,14 +254,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         };
         // This will also call the access hooks.
         self.mem_copy(
-            ptr.into(),
+            ptr,
             Align::ONE,
             new_ptr.into(),
             Align::ONE,
             old_size.min(new_size),
             /*nonoverlapping*/ true,
         )?;
-        self.deallocate_ptr(ptr.into(), old_size_and_align, kind)?;
+        self.deallocate_ptr(ptr, old_size_and_align, kind)?;
 
         Ok(new_ptr)
     }
@@ -274,7 +273,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         old_size_and_align: Option<(Size, Align)>,
         kind: MemoryKind<M::MemoryKind>,
     ) -> InterpResult<'tcx> {
-        let (alloc_id, offset, ptr) = self.ptr_get_alloc_id(ptr)?;
+        let (alloc_id, offset, tag) = self.ptr_get_alloc_id(ptr)?;
         trace!("deallocating: {}", alloc_id);
 
         if offset.bytes() != 0 {
@@ -330,7 +329,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             *self.tcx,
             &mut self.machine,
             &mut alloc.extra,
-            ptr.provenance,
+            (alloc_id, tag),
             alloc_range(Size::ZERO, size),
         )?;
 
@@ -350,17 +349,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         ptr: Pointer<Option<M::PointerTag>>,
         size: Size,
         align: Align,
-    ) -> InterpResult<'tcx, Option<(AllocId, Size, Pointer<M::PointerTag>)>> {
+    ) -> InterpResult<'tcx, Option<(AllocId, Size, M::TagExtra)>> {
         let align = M::enforce_alignment(&self).then_some(align);
         self.check_and_deref_ptr(
             ptr,
             size,
             align,
             CheckInAllocMsg::MemoryAccessTest,
-            |alloc_id, offset, ptr| {
+            |alloc_id, offset, tag| {
                 let (size, align) =
                     self.get_alloc_size_and_align(alloc_id, AllocCheck::Dereferenceable)?;
-                Ok((size, align, (alloc_id, offset, ptr)))
+                Ok((size, align, (alloc_id, offset, tag)))
             },
         )
     }
@@ -401,11 +400,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         size: Size,
         align: Option<Align>,
         msg: CheckInAllocMsg,
-        alloc_size: impl FnOnce(
-            AllocId,
-            Size,
-            Pointer<M::PointerTag>,
-        ) -> InterpResult<'tcx, (Size, Align, T)>,
+        alloc_size: impl FnOnce(AllocId, Size, M::TagExtra) -> InterpResult<'tcx, (Size, Align, T)>,
     ) -> InterpResult<'tcx, Option<T>> {
         fn check_offset_align(offset: u64, align: Align) -> InterpResult<'static> {
             if offset % align.bytes() == 0 {
@@ -433,8 +428,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 }
                 None
             }
-            Ok((alloc_id, offset, ptr)) => {
-                let (alloc_size, alloc_align, ret_val) = alloc_size(alloc_id, offset, ptr)?;
+            Ok((alloc_id, offset, tag)) => {
+                let (alloc_size, alloc_align, ret_val) = alloc_size(alloc_id, offset, tag)?;
                 // Test bounds. This also ensures non-null.
                 // It is sufficient to check this for the end pointer. Also check for overflow!
                 if offset.checked_add(size, &self.tcx).map_or(true, |end| end > alloc_size) {
@@ -450,10 +445,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 // we want the error to be about the bounds.
                 if let Some(align) = align {
                     if M::force_int_for_alignment_check(self) {
-                        let addr = Scalar::from_pointer(ptr, &self.tcx)
-                            .to_machine_usize(&self.tcx)
-                            .expect("ptr-to-int cast for align check should never fail");
-                        check_offset_align(addr, align)?;
+                        // `force_int_for_alignment_check` can only be true if `OFFSET_IS_ADDR` is true.
+                        check_offset_align(ptr.addr().bytes(), align)?;
                     } else {
                         // Check allocation alignment and offset alignment.
                         if alloc_align.bytes() < align.bytes() {
@@ -569,14 +562,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             size,
             align,
             CheckInAllocMsg::MemoryAccessTest,
-            |alloc_id, offset, ptr| {
+            |alloc_id, offset, tag| {
                 let alloc = self.get_alloc_raw(alloc_id)?;
-                Ok((alloc.size(), alloc.align, (alloc_id, offset, ptr, alloc)))
+                Ok((alloc.size(), alloc.align, (alloc_id, offset, tag, alloc)))
             },
         )?;
-        if let Some((alloc_id, offset, ptr, alloc)) = ptr_and_alloc {
+        if let Some((alloc_id, offset, tag, alloc)) = ptr_and_alloc {
             let range = alloc_range(offset, size);
-            M::memory_read(*self.tcx, &self.machine, &alloc.extra, ptr.provenance, range)?;
+            M::memory_read(*self.tcx, &self.machine, &alloc.extra, (alloc_id, tag), range)?;
             Ok(Some(AllocRef { alloc, range, tcx: *self.tcx, alloc_id }))
         } else {
             // Even in this branch we have to be sure that we actually access the allocation, in
@@ -631,13 +624,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         align: Align,
     ) -> InterpResult<'tcx, Option<AllocRefMut<'a, 'tcx, M::PointerTag, M::AllocExtra>>> {
         let parts = self.get_ptr_access(ptr, size, align)?;
-        if let Some((alloc_id, offset, ptr)) = parts {
+        if let Some((alloc_id, offset, tag)) = parts {
             let tcx = *self.tcx;
             // FIXME: can we somehow avoid looking up the allocation twice here?
             // We cannot call `get_raw_mut` inside `check_and_deref_ptr` as that would duplicate `&mut self`.
             let (alloc, machine) = self.get_alloc_raw_mut(alloc_id)?;
             let range = alloc_range(offset, size);
-            M::memory_written(tcx, machine, &mut alloc.extra, ptr.provenance, range)?;
+            M::memory_written(tcx, machine, &mut alloc.extra, (alloc_id, tag), range)?;
             Ok(Some(AllocRefMut { alloc, range, tcx, alloc_id }))
         } else {
             Ok(None)
@@ -732,7 +725,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         ptr: Pointer<Option<M::PointerTag>>,
     ) -> InterpResult<'tcx, FnVal<'tcx, M::ExtraFnVal>> {
         trace!("get_fn({:?})", ptr);
-        let (alloc_id, offset, _ptr) = self.ptr_get_alloc_id(ptr)?;
+        let (alloc_id, offset, _tag) = self.ptr_get_alloc_id(ptr)?;
         if offset.bytes() != 0 {
             throw_ub!(InvalidFunctionPointer(Pointer::new(alloc_id, offset)))
         }
@@ -877,9 +870,17 @@ impl<'tcx, 'a, Tag: Provenance, Extra> AllocRefMut<'a, 'tcx, Tag, Extra> {
         range: AllocRange,
         val: ScalarMaybeUninit<Tag>,
     ) -> InterpResult<'tcx> {
+        let range = self.range.subrange(range);
+        debug!(
+            "write_scalar in {} at {:#x}, size {}: {:?}",
+            self.alloc_id,
+            range.start.bytes(),
+            range.size.bytes(),
+            val
+        );
         Ok(self
             .alloc
-            .write_scalar(&self.tcx, self.range.subrange(range), val)
+            .write_scalar(&self.tcx, range, val)
             .map_err(|e| e.to_interp_error(self.alloc_id))?)
     }
 
@@ -892,17 +893,29 @@ impl<'tcx, 'a, Tag: Provenance, Extra> AllocRefMut<'a, 'tcx, Tag, Extra> {
     }
 
     /// Mark the entire referenced range as uninitalized
-    pub fn write_uninit(&mut self) {
-        self.alloc.mark_init(self.range, false);
+    pub fn write_uninit(&mut self) -> InterpResult<'tcx> {
+        Ok(self
+            .alloc
+            .write_uninit(&self.tcx, self.range)
+            .map_err(|e| e.to_interp_error(self.alloc_id))?)
     }
 }
 
 impl<'tcx, 'a, Tag: Provenance, Extra> AllocRef<'a, 'tcx, Tag, Extra> {
     pub fn read_scalar(&self, range: AllocRange) -> InterpResult<'tcx, ScalarMaybeUninit<Tag>> {
-        Ok(self
+        let range = self.range.subrange(range);
+        let res = self
             .alloc
-            .read_scalar(&self.tcx, self.range.subrange(range))
-            .map_err(|e| e.to_interp_error(self.alloc_id))?)
+            .read_scalar(&self.tcx, range)
+            .map_err(|e| e.to_interp_error(self.alloc_id))?;
+        debug!(
+            "read_scalar in {} at {:#x}, size {}: {:?}",
+            self.alloc_id,
+            range.start.bytes(),
+            range.size.bytes(),
+            res
+        );
+        Ok(res)
     }
 
     pub fn read_ptr_sized(&self, offset: Size) -> InterpResult<'tcx, ScalarMaybeUninit<Tag>> {
@@ -1009,16 +1022,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         // and once below to get the underlying `&[mut] Allocation`.
 
         // Source alloc preparations and access hooks.
-        let Some((src_alloc_id, src_offset, src)) = src_parts else {
+        let Some((src_alloc_id, src_offset, src_tag)) = src_parts else {
             // Zero-sized *source*, that means dst is also zero-sized and we have nothing to do.
             return Ok(());
         };
         let src_alloc = self.get_alloc_raw(src_alloc_id)?;
         let src_range = alloc_range(src_offset, size);
-        M::memory_read(*tcx, &self.machine, &src_alloc.extra, src.provenance, src_range)?;
+        M::memory_read(*tcx, &self.machine, &src_alloc.extra, (src_alloc_id, src_tag), src_range)?;
         // We need the `dest` ptr for the next operation, so we get it now.
         // We already did the source checks and called the hooks so we are good to return early.
-        let Some((dest_alloc_id, dest_offset, dest)) = dest_parts else {
+        let Some((dest_alloc_id, dest_offset, dest_tag)) = dest_parts else {
             // Zero-sized *destination*.
             return Ok(());
         };
@@ -1040,7 +1053,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         // Destination alloc preparations and access hooks.
         let (dest_alloc, extra) = self.get_alloc_raw_mut(dest_alloc_id)?;
         let dest_range = alloc_range(dest_offset, size * num_copies);
-        M::memory_written(*tcx, extra, &mut dest_alloc.extra, dest.provenance, dest_range)?;
+        M::memory_written(
+            *tcx,
+            extra,
+            &mut dest_alloc.extra,
+            (dest_alloc_id, dest_tag),
+            dest_range,
+        )?;
         let dest_bytes = dest_alloc
             .get_bytes_mut_ptr(&tcx, dest_range)
             .map_err(|e| e.to_interp_error(dest_alloc_id))?
@@ -1053,8 +1072,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             // This also avoids writing to the target bytes so that the backing allocation is never
             // touched if the bytes stay uninitialized for the whole interpreter execution. On contemporary
             // operating system this can avoid physically allocating the page.
-            dest_alloc.mark_init(dest_range, false); // `Size` multiplication
-            dest_alloc.mark_relocation_range(relocations);
+            dest_alloc
+                .write_uninit(&tcx, dest_range)
+                .map_err(|e| e.to_interp_error(dest_alloc_id))?;
+            // We can forget about the relocations, this is all not initialized anyway.
             return Ok(());
         }
 
@@ -1159,11 +1180,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     pub fn ptr_try_get_alloc_id(
         &self,
         ptr: Pointer<Option<M::PointerTag>>,
-    ) -> Result<(AllocId, Size, Pointer<M::PointerTag>), u64> {
+    ) -> Result<(AllocId, Size, M::TagExtra), u64> {
         match ptr.into_pointer_or_addr() {
             Ok(ptr) => {
-                let (alloc_id, offset) = M::ptr_get_alloc(self, ptr);
-                Ok((alloc_id, offset, ptr))
+                let (alloc_id, offset, extra) = M::ptr_get_alloc(self, ptr);
+                Ok((alloc_id, offset, extra))
             }
             Err(addr) => Err(addr.bytes()),
         }
@@ -1174,7 +1195,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     pub fn ptr_get_alloc_id(
         &self,
         ptr: Pointer<Option<M::PointerTag>>,
-    ) -> InterpResult<'tcx, (AllocId, Size, Pointer<M::PointerTag>)> {
+    ) -> InterpResult<'tcx, (AllocId, Size, M::TagExtra)> {
         self.ptr_try_get_alloc_id(ptr).map_err(|offset| {
             err_ub!(DanglingIntPointer(offset, CheckInAllocMsg::InboundsTest)).into()
         })
diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs
index 2b9fe565997..69d6c8470a2 100644
--- a/compiler/rustc_const_eval/src/interpret/mod.rs
+++ b/compiler/rustc_const_eval/src/interpret/mod.rs
@@ -27,7 +27,7 @@ pub use self::memory::{AllocCheck, AllocRef, AllocRefMut, FnVal, Memory, MemoryK
 pub use self::operand::{ImmTy, Immediate, OpTy, Operand};
 pub use self::place::{MPlaceTy, MemPlace, MemPlaceMeta, Place, PlaceTy};
 pub use self::validity::{CtfeValidationMode, RefTracking};
-pub use self::visitor::{MutValueVisitor, ValueVisitor};
+pub use self::visitor::{MutValueVisitor, Value, ValueVisitor};
 
 crate use self::intrinsics::eval_nullary_intrinsic;
 use eval_context::{from_known_layout, mir_assign_valid_types};
diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs
index dfc0028e87f..f2d833b3202 100644
--- a/compiler/rustc_const_eval/src/interpret/operand.rs
+++ b/compiler/rustc_const_eval/src/interpret/operand.rs
@@ -424,6 +424,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         })
     }
 
+    #[instrument(skip(self), level = "debug")]
     pub fn operand_projection(
         &self,
         base: &OpTy<'tcx, M::PointerTag>,
diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs
index 31da4522a1f..380eb526361 100644
--- a/compiler/rustc_const_eval/src/interpret/place.rs
+++ b/compiler/rustc_const_eval/src/interpret/place.rs
@@ -115,6 +115,12 @@ impl<'tcx, Tag: Provenance> std::ops::Deref for MPlaceTy<'tcx, Tag> {
     }
 }
 
+impl<'tcx, Tag: Provenance> std::ops::DerefMut for MPlaceTy<'tcx, Tag> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.mplace
+    }
+}
+
 impl<'tcx, Tag: Provenance> From<MPlaceTy<'tcx, Tag>> for PlaceTy<'tcx, Tag> {
     #[inline(always)]
     fn from(mplace: MPlaceTy<'tcx, Tag>) -> Self {
@@ -294,6 +300,7 @@ where
 
     /// Take an operand, representing a pointer, and dereference it to a place -- that
     /// will always be a MemPlace.  Lives in `place.rs` because it creates a place.
+    #[instrument(skip(self), level = "debug")]
     pub fn deref_operand(
         &self,
         src: &OpTy<'tcx, M::PointerTag>,
@@ -487,7 +494,8 @@ where
     }
 
     /// Project into an mplace
-    pub(super) fn mplace_projection(
+    #[instrument(skip(self), level = "debug")]
+    pub(crate) fn mplace_projection(
         &self,
         base: &MPlaceTy<'tcx, M::PointerTag>,
         proj_elem: mir::PlaceElem<'tcx>,
@@ -548,6 +556,7 @@ where
     /// Just a convenience function, but used quite a bit.
     /// This is the only projection that might have a side-effect: We cannot project
     /// into the field of a local `ScalarPair`, we have to first allocate it.
+    #[instrument(skip(self), level = "debug")]
     pub fn place_field(
         &mut self,
         base: &PlaceTy<'tcx, M::PointerTag>,
@@ -617,6 +626,7 @@ where
 
     /// Computes a place. You should only use this if you intend to write into this
     /// place; for reading, a more efficient alternative is `eval_place_for_read`.
+    #[instrument(skip(self), level = "debug")]
     pub fn eval_place(
         &mut self,
         place: mir::Place<'tcx>,
@@ -646,6 +656,7 @@ where
 
     /// Write an immediate to a place
     #[inline(always)]
+    #[instrument(skip(self), level = "debug")]
     pub fn write_immediate(
         &mut self,
         src: Immediate<M::PointerTag>,
@@ -823,13 +834,14 @@ where
             // Zero-sized access
             return Ok(());
         };
-        alloc.write_uninit();
+        alloc.write_uninit()?;
         Ok(())
     }
 
     /// Copies the data from an operand to a place. This does not support transmuting!
     /// Use `copy_op_transmute` if the layouts could disagree.
     #[inline(always)]
+    #[instrument(skip(self), level = "debug")]
     pub fn copy_op(
         &mut self,
         src: &OpTy<'tcx, M::PointerTag>,
@@ -849,6 +861,7 @@ where
     /// Use `copy_op_transmute` if the layouts could disagree.
     /// Also, if you use this you are responsible for validating that things get copied at the
     /// right type.
+    #[instrument(skip(self), level = "debug")]
     fn copy_op_no_validate(
         &mut self,
         src: &OpTy<'tcx, M::PointerTag>,
@@ -955,6 +968,7 @@ where
     /// This supports unsized types and returns the computed size to avoid some
     /// redundant computation when copying; use `force_allocation` for a simpler, sized-only
     /// version.
+    #[instrument(skip(self), level = "debug")]
     pub fn force_allocation_maybe_sized(
         &mut self,
         place: &PlaceTy<'tcx, M::PointerTag>,
@@ -1037,6 +1051,7 @@ where
     }
 
     /// Writes the discriminant of the given variant.
+    #[instrument(skip(self), level = "debug")]
     pub fn write_discriminant(
         &mut self,
         variant_index: VariantIdx,
diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs
index 4a0aa41de73..71d29be97d5 100644
--- a/compiler/rustc_const_eval/src/interpret/validity.rs
+++ b/compiler/rustc_const_eval/src/interpret/validity.rs
@@ -432,7 +432,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
         if let Some(ref mut ref_tracking) = self.ref_tracking {
             // Proceed recursively even for ZST, no reason to skip them!
             // `!` is a ZST and we want to validate it.
-            if let Ok((alloc_id, _offset, _ptr)) = self.ecx.ptr_try_get_alloc_id(place.ptr) {
+            if let Ok((alloc_id, _offset, _tag)) = self.ecx.ptr_try_get_alloc_id(place.ptr) {
                 // Special handling for pointers to statics (irrespective of their type).
                 let alloc_kind = self.ecx.tcx.get_global_alloc(alloc_id);
                 if let Some(GlobalAlloc::Static(did)) = alloc_kind {
diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs
index d688331ae0a..34a00452519 100644
--- a/compiler/rustc_const_eval/src/lib.rs
+++ b/compiler/rustc_const_eval/src/lib.rs
@@ -35,6 +35,7 @@ pub mod transform;
 pub mod util;
 
 use rustc_middle::ty::query::Providers;
+use rustc_middle::ty::ParamEnv;
 
 pub fn provide(providers: &mut Providers) {
     const_eval::provide(providers);
@@ -49,6 +50,9 @@ pub fn provide(providers: &mut Providers) {
         let (param_env, raw) = param_env_and_value.into_parts();
         const_eval::const_to_valtree(tcx, param_env, raw)
     };
+    providers.valtree_to_const_val = |tcx, (ty, valtree)| {
+        const_eval::valtree_to_const_value(tcx, ParamEnv::empty().and(ty), valtree)
+    };
     providers.deref_const = |tcx, param_env_and_value| {
         let (param_env, value) = param_env_and_value.into_parts();
         const_eval::deref_const(tcx, param_env, value)
diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs
index faea2111d92..1052d588fad 100644
--- a/compiler/rustc_const_eval/src/transform/promote_consts.rs
+++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs
@@ -13,7 +13,7 @@
 //! move analysis runs after promotion on broken MIR.
 
 use rustc_hir as hir;
-use rustc_middle::mir::traversal::ReversePostorder;
+use rustc_middle::mir::traversal::ReversePostorderIter;
 use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor};
 use rustc_middle::mir::*;
 use rustc_middle::ty::cast::CastTy;
@@ -170,7 +170,7 @@ impl<'tcx> Visitor<'tcx> for Collector<'_, 'tcx> {
 
 pub fn collect_temps_and_candidates<'tcx>(
     ccx: &ConstCx<'_, 'tcx>,
-    rpo: &mut ReversePostorder<'_, 'tcx>,
+    rpo: &mut ReversePostorderIter<'_, 'tcx>,
 ) -> (IndexVec<Local, TempState>, Vec<Candidate>) {
     let mut collector = Collector {
         temps: IndexVec::from_elem(TempState::Undefined, &ccx.body.local_decls),
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index 79d427ccc44..f71bc586b48 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -214,7 +214,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
 
     fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
         // This check is somewhat expensive, so only run it when -Zvalidate-mir is passed.
-        if self.tcx.sess.opts.debugging_opts.validate_mir {
+        if self.tcx.sess.opts.debugging_opts.validate_mir && self.mir_phase < MirPhase::DropsLowered
+        {
             // `Operand::Copy` is only supposed to be used with `Copy` types.
             if let Operand::Copy(place) = operand {
                 let ty = place.ty(&self.body.local_decls, self.tcx).ty;
diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs
index 85ad0f2f7f5..25353290fd5 100644
--- a/compiler/rustc_data_structures/src/stable_hasher.rs
+++ b/compiler/rustc_data_structures/src/stable_hasher.rs
@@ -207,9 +207,14 @@ pub trait ToStableHashKey<HCX> {
     fn to_stable_hash_key(&self, hcx: &HCX) -> Self::KeyType;
 }
 
-// Implement HashStable by just calling `Hash::hash()`. This works fine for
-// self-contained values that don't depend on the hashing context `CTX`.
-#[macro_export]
+/// Implement HashStable by just calling `Hash::hash()`.
+///
+/// **WARNING** This is only valid for types that *really* don't need any context for fingerprinting.
+/// But it is easy to misuse this macro (see [#96013](https://github.com/rust-lang/rust/issues/96013)
+/// for examples). Therefore this macro is not exported and should only be used in the limited cases
+/// here in this module.
+///
+/// Use `#[derive(HashStable_Generic)]` instead.
 macro_rules! impl_stable_hash_via_hash {
     ($t:ty) => {
         impl<CTX> $crate::stable_hasher::HashStable<CTX> for $t {
@@ -246,12 +251,14 @@ impl<CTX> HashStable<CTX> for ! {
 }
 
 impl<CTX> HashStable<CTX> for ::std::num::NonZeroU32 {
+    #[inline]
     fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
         self.get().hash_stable(ctx, hasher)
     }
 }
 
 impl<CTX> HashStable<CTX> for ::std::num::NonZeroUsize {
+    #[inline]
     fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
         self.get().hash_stable(ctx, hasher)
     }
@@ -272,12 +279,14 @@ impl<CTX> HashStable<CTX> for f64 {
 }
 
 impl<CTX> HashStable<CTX> for ::std::cmp::Ordering {
+    #[inline]
     fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
         (*self as i8).hash_stable(ctx, hasher);
     }
 }
 
 impl<T1: HashStable<CTX>, CTX> HashStable<CTX> for (T1,) {
+    #[inline]
     fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
         let (ref _0,) = *self;
         _0.hash_stable(ctx, hasher);
diff --git a/compiler/rustc_error_messages/locales/en-US/parser.ftl b/compiler/rustc_error_messages/locales/en-US/parser.ftl
index 24e59a93cea..3143b81b609 100644
--- a/compiler/rustc_error_messages/locales/en-US/parser.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/parser.ftl
@@ -1,3 +1,7 @@
 parser-struct-literal-body-without-path =
     struct literal body without path
     .suggestion = you might have forgotten to add the struct literal inside the block
+
+parser-maybe-report-ambiguous-plus =
+    ambiguous `+` in a type
+    .suggestion = use parentheses to disambiguate
diff --git a/compiler/rustc_error_messages/locales/en-US/typeck.ftl b/compiler/rustc_error_messages/locales/en-US/typeck.ftl
index 721201d9312..6a3235fc772 100644
--- a/compiler/rustc_error_messages/locales/en-US/typeck.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/typeck.ftl
@@ -82,3 +82,11 @@ typeck-value-of-associated-struct-already-specified =
 
 typeck-address-of-temporary-taken = cannot take address of a temporary
     .label = temporary value
+
+typeck-add-return-type-add = try adding a return type
+
+typeck-add-return-type-missing-here = a return type might be missing here
+
+typeck-expected-default-return-type = expected `()` because of default return type
+
+typeck-expected-return-type = expected `{$expected}` because of return type
diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs
index de0dd18cc6e..e1e0ed7222d 100644
--- a/compiler/rustc_error_messages/src/lib.rs
+++ b/compiler/rustc_error_messages/src/lib.rs
@@ -338,18 +338,12 @@ impl MultiSpan {
 
     /// Returns `true` if any of the primary spans are displayable.
     pub fn has_primary_spans(&self) -> bool {
-        self.primary_spans.iter().any(|sp| !sp.is_dummy())
+        !self.is_dummy()
     }
 
     /// Returns `true` if this contains only a dummy primary span with any hygienic context.
     pub fn is_dummy(&self) -> bool {
-        let mut is_dummy = true;
-        for span in &self.primary_spans {
-            if !span.is_dummy() {
-                is_dummy = false;
-            }
-        }
-        is_dummy
+        self.primary_spans.iter().all(|sp| sp.is_dummy())
     }
 
     /// Replaces all occurrences of one Span with another. Used to move `Span`s in areas that don't
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs
index cd17726c785..83e6a751394 100644
--- a/compiler/rustc_errors/src/diagnostic.rs
+++ b/compiler/rustc_errors/src/diagnostic.rs
@@ -78,6 +78,13 @@ impl<'source> Into<FluentValue<'source>> for DiagnosticArgValue<'source> {
     }
 }
 
+/// Trait implemented by error types. This should not be implemented manually. Instead, use
+/// `#[derive(SessionSubdiagnostic)]` -- see [rustc_macros::SessionSubdiagnostic].
+pub trait AddSubdiagnostic {
+    /// Add a subdiagnostic to an existing diagnostic.
+    fn add_to_diagnostic(self, diag: &mut Diagnostic);
+}
+
 #[must_use]
 #[derive(Clone, Debug, Encodable, Decodable)]
 pub struct Diagnostic {
@@ -605,7 +612,7 @@ impl Diagnostic {
         &mut self,
         sp: Span,
         msg: impl Into<DiagnosticMessage>,
-        suggestion: String,
+        suggestion: impl ToString,
         applicability: Applicability,
     ) -> &mut Self {
         self.span_suggestion_with_style(
@@ -623,13 +630,13 @@ impl Diagnostic {
         &mut self,
         sp: Span,
         msg: impl Into<DiagnosticMessage>,
-        suggestion: String,
+        suggestion: impl ToString,
         applicability: Applicability,
         style: SuggestionStyle,
     ) -> &mut Self {
         self.push_suggestion(CodeSuggestion {
             substitutions: vec![Substitution {
-                parts: vec![SubstitutionPart { snippet: suggestion, span: sp }],
+                parts: vec![SubstitutionPart { snippet: suggestion.to_string(), span: sp }],
             }],
             msg: msg.into(),
             style,
@@ -643,7 +650,7 @@ impl Diagnostic {
         &mut self,
         sp: Span,
         msg: impl Into<DiagnosticMessage>,
-        suggestion: String,
+        suggestion: impl ToString,
         applicability: Applicability,
     ) -> &mut Self {
         self.span_suggestion_with_style(
@@ -711,7 +718,7 @@ impl Diagnostic {
         &mut self,
         sp: Span,
         msg: impl Into<DiagnosticMessage>,
-        suggestion: String,
+        suggestion: impl ToString,
         applicability: Applicability,
     ) -> &mut Self {
         self.span_suggestion_with_style(
@@ -734,7 +741,7 @@ impl Diagnostic {
         &mut self,
         sp: Span,
         msg: impl Into<DiagnosticMessage>,
-        suggestion: String,
+        suggestion: impl ToString,
         applicability: Applicability,
     ) -> &mut Self {
         self.span_suggestion_with_style(
@@ -755,7 +762,7 @@ impl Diagnostic {
         &mut self,
         sp: Span,
         msg: impl Into<DiagnosticMessage>,
-        suggestion: String,
+        suggestion: impl ToString,
         applicability: Applicability,
     ) -> &mut Self {
         self.span_suggestion_with_style(
@@ -768,6 +775,13 @@ impl Diagnostic {
         self
     }
 
+    /// Add a subdiagnostic from a type that implements `SessionSubdiagnostic` - see
+    /// [rustc_macros::SessionSubdiagnostic].
+    pub fn subdiagnostic(&mut self, subdiagnostic: impl AddSubdiagnostic) -> &mut Self {
+        subdiagnostic.add_to_diagnostic(self);
+        self
+    }
+
     pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self {
         self.span = sp.into();
         if let Some(span) = self.span.primary_span() {
diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs
index 74e0f742946..96b730c2baa 100644
--- a/compiler/rustc_errors/src/diagnostic_builder.rs
+++ b/compiler/rustc_errors/src/diagnostic_builder.rs
@@ -255,19 +255,6 @@ impl EmissionGuarantee for ! {
 /// instead of a `&DiagnosticBuilder<'a>`. This `forward!` macro makes
 /// it easy to declare such methods on the builder.
 macro_rules! forward {
-    // Forward pattern for &self -> &Self
-    (
-        $(#[$attrs:meta])*
-        pub fn $n:ident(&self, $($name:ident: $ty:ty),* $(,)?) -> &Self
-    ) => {
-        $(#[$attrs])*
-        #[doc = concat!("See [`Diagnostic::", stringify!($n), "()`].")]
-        pub fn $n(&self, $($name: $ty),*) -> &Self {
-            self.diagnostic.$n($($name),*);
-            self
-        }
-    };
-
     // Forward pattern for &mut self -> &mut Self
     (
         $(#[$attrs:meta])*
@@ -490,7 +477,7 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
         &mut self,
         sp: Span,
         msg: impl Into<DiagnosticMessage>,
-        suggestion: String,
+        suggestion: impl ToString,
         applicability: Applicability,
     ) -> &mut Self);
     forward!(pub fn span_suggestions(
@@ -510,28 +497,28 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
         &mut self,
         sp: Span,
         msg: impl Into<DiagnosticMessage>,
-        suggestion: String,
+        suggestion: impl ToString,
         applicability: Applicability,
     ) -> &mut Self);
     forward!(pub fn span_suggestion_verbose(
         &mut self,
         sp: Span,
         msg: impl Into<DiagnosticMessage>,
-        suggestion: String,
+        suggestion: impl ToString,
         applicability: Applicability,
     ) -> &mut Self);
     forward!(pub fn span_suggestion_hidden(
         &mut self,
         sp: Span,
         msg: impl Into<DiagnosticMessage>,
-        suggestion: String,
+        suggestion: impl ToString,
         applicability: Applicability,
     ) -> &mut Self);
     forward!(pub fn tool_only_span_suggestion(
         &mut self,
         sp: Span,
         msg: impl Into<DiagnosticMessage>,
-        suggestion: String,
+        suggestion: impl ToString,
         applicability: Applicability,
     ) -> &mut Self);
 
@@ -543,6 +530,11 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
         name: impl Into<Cow<'static, str>>,
         arg: DiagnosticArgValue<'static>,
     ) -> &mut Self);
+
+    forward!(pub fn subdiagnostic(
+        &mut self,
+        subdiagnostic: impl crate::AddSubdiagnostic
+    ) -> &mut Self);
 }
 
 impl<G: EmissionGuarantee> Debug for DiagnosticBuilder<'_, G> {
diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs
index 47cdf39cd52..5dd743e8d00 100644
--- a/compiler/rustc_errors/src/emitter.rs
+++ b/compiler/rustc_errors/src/emitter.rs
@@ -212,7 +212,12 @@ pub trait Emitter {
     fn emit_future_breakage_report(&mut self, _diags: Vec<Diagnostic>) {}
 
     /// Emit list of unused externs
-    fn emit_unused_externs(&mut self, _lint_level: &str, _unused_externs: &[&str]) {}
+    fn emit_unused_externs(
+        &mut self,
+        _lint_level: rustc_lint_defs::Level,
+        _unused_externs: &[&str],
+    ) {
+    }
 
     /// Checks if should show explanations about "rustc --explain"
     fn should_show_explain(&self) -> bool {
diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs
index d680e7fab70..6ff52182d6b 100644
--- a/compiler/rustc_errors/src/json.rs
+++ b/compiler/rustc_errors/src/json.rs
@@ -171,7 +171,8 @@ impl Emitter for JsonEmitter {
         }
     }
 
-    fn emit_unused_externs(&mut self, lint_level: &str, unused_externs: &[&str]) {
+    fn emit_unused_externs(&mut self, lint_level: rustc_lint_defs::Level, unused_externs: &[&str]) {
+        let lint_level = lint_level.as_str();
         let data = UnusedExterns { lint_level, unused_extern_names: unused_externs };
         let result = if self.pretty {
             writeln!(&mut self.dst, "{}", as_pretty_json(&data))
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 339bcc24751..df41fc00714 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -37,6 +37,7 @@ pub use rustc_error_messages::{
 };
 pub use rustc_lint_defs::{pluralize, Applicability};
 use rustc_span::source_map::SourceMap;
+use rustc_span::HashStableContext;
 use rustc_span::{Loc, Span};
 
 use std::borrow::Cow;
@@ -369,8 +370,8 @@ impl fmt::Display for ExplicitBug {
 impl error::Error for ExplicitBug {}
 
 pub use diagnostic::{
-    Diagnostic, DiagnosticArg, DiagnosticArgValue, DiagnosticId, DiagnosticStyledString,
-    IntoDiagnosticArg, SubDiagnostic,
+    AddSubdiagnostic, Diagnostic, DiagnosticArg, DiagnosticArgValue, DiagnosticId,
+    DiagnosticStyledString, IntoDiagnosticArg, SubDiagnostic,
 };
 pub use diagnostic_builder::{DiagnosticBuilder, EmissionGuarantee};
 use std::backtrace::Backtrace;
@@ -968,8 +969,19 @@ impl Handler {
         self.inner.borrow_mut().emitter.emit_future_breakage_report(diags)
     }
 
-    pub fn emit_unused_externs(&self, lint_level: &str, unused_externs: &[&str]) {
-        self.inner.borrow_mut().emit_unused_externs(lint_level, unused_externs)
+    pub fn emit_unused_externs(
+        &self,
+        lint_level: rustc_lint_defs::Level,
+        loud: bool,
+        unused_externs: &[&str],
+    ) {
+        let mut inner = self.inner.borrow_mut();
+
+        if loud && lint_level.is_error() {
+            inner.bump_err_count();
+        }
+
+        inner.emit_unused_externs(lint_level, unused_externs)
     }
 
     pub fn update_unstable_expectation_id(
@@ -1140,7 +1152,7 @@ impl HandlerInner {
         self.emitter.emit_artifact_notification(path, artifact_type);
     }
 
-    fn emit_unused_externs(&mut self, lint_level: &str, unused_externs: &[&str]) {
+    fn emit_unused_externs(&mut self, lint_level: rustc_lint_defs::Level, unused_externs: &[&str]) {
         self.emitter.emit_unused_externs(lint_level, unused_externs);
     }
 
@@ -1494,6 +1506,7 @@ pub fn add_elided_lifetime_in_path_suggestion(
 /// Useful type to use with `Result<>` indicate that an error has already
 /// been reported to the user, so no need to continue checking.
 #[derive(Clone, Copy, Debug, Encodable, Decodable, Hash, PartialEq, Eq, PartialOrd, Ord)]
+#[derive(HashStable_Generic)]
 pub struct ErrorGuaranteed(());
 
 impl ErrorGuaranteed {
@@ -1503,5 +1516,3 @@ impl ErrorGuaranteed {
         ErrorGuaranteed(())
     }
 }
-
-rustc_data_structures::impl_stable_hash_via_hash!(ErrorGuaranteed);
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index 3799623563f..ae1b50a4176 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -1272,9 +1272,7 @@ pub fn parse_macro_name_and_helper_attrs(
     // Once we've located the `#[proc_macro_derive]` attribute, verify
     // that it's of the form `#[proc_macro_derive(Foo)]` or
     // `#[proc_macro_derive(Foo, attributes(A, ..))]`
-    let Some(list) = attr.meta_item_list() else {
-        return None;
-    };
+    let list = attr.meta_item_list()?;
     if list.len() != 1 && list.len() != 2 {
         diag.span_err(attr.span, "attribute must have either one or two arguments");
         return None;
diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs
index b8ed75cb6bb..301c67f7026 100644
--- a/compiler/rustc_expand/src/build.rs
+++ b/compiler/rustc_expand/src/build.rs
@@ -113,6 +113,7 @@ impl<'a> ExtCtxt<'a> {
             bounds,
             kind: ast::GenericParamKind::Type { default },
             is_placeholder: false,
+            colon_span: None,
         }
     }
 
diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs
index fa628cd9ebd..c91125105d7 100644
--- a/compiler/rustc_expand/src/config.rs
+++ b/compiler/rustc_expand/src/config.rs
@@ -1,7 +1,7 @@
 //! Conditional compilation stripping.
 
 use rustc_ast::ptr::P;
-use rustc_ast::token::{DelimToken, Token, TokenKind};
+use rustc_ast::token::{Delimiter, Token, TokenKind};
 use rustc_ast::tokenstream::{AttrAnnotatedTokenStream, AttrAnnotatedTokenTree};
 use rustc_ast::tokenstream::{DelimSpan, Spacing};
 use rustc_ast::tokenstream::{LazyTokenStream, TokenTree};
@@ -418,7 +418,7 @@ impl<'a> StripUnconfigured<'a> {
         // in `#[attr]`, so just use the span of the `#` token.
         let bracket_group = AttrAnnotatedTokenTree::Delimited(
             DelimSpan::from_single(pound_span),
-            DelimToken::Bracket,
+            Delimiter::Bracket,
             item.tokens
                 .as_ref()
                 .unwrap_or_else(|| panic!("Missing tokens for {:?}", item))
@@ -511,7 +511,7 @@ pub fn parse_cfg<'a>(meta_item: &'a MetaItem, sess: &Session) -> Option<&'a Meta
             err.span_suggestion(
                 span,
                 "expected syntax is",
-                suggestion.into(),
+                suggestion,
                 Applicability::HasPlaceholders,
             );
         }
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index 9b224a73356..5bd89f3f42f 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -8,7 +8,7 @@ use crate::placeholders::{placeholder, PlaceholderExpander};
 use rustc_ast as ast;
 use rustc_ast::mut_visit::*;
 use rustc_ast::ptr::P;
-use rustc_ast::token;
+use rustc_ast::token::{self, Delimiter};
 use rustc_ast::tokenstream::TokenStream;
 use rustc_ast::visit::{self, AssocCtxt, Visitor};
 use rustc_ast::{AssocItemKind, AstLike, AstLikeWrapper, AttrStyle, ExprKind, ForeignItemKind};
@@ -884,7 +884,7 @@ pub fn parse_ast_fragment<'a>(
         AstFragmentKind::Stmts => {
             let mut stmts = SmallVec::new();
             // Won't make progress on a `}`.
-            while this.token != token::Eof && this.token != token::CloseDelim(token::Brace) {
+            while this.token != token::Eof && this.token != token::CloseDelim(Delimiter::Brace) {
                 if let Some(stmt) = this.parse_full_stmt(AttemptLocalParseRecovery::Yes)? {
                     stmts.push(stmt);
                 }
diff --git a/compiler/rustc_expand/src/mbe.rs b/compiler/rustc_expand/src/mbe.rs
index a5b8571fefe..36295da74ad 100644
--- a/compiler/rustc_expand/src/mbe.rs
+++ b/compiler/rustc_expand/src/mbe.rs
@@ -11,16 +11,16 @@ crate mod quoted;
 crate mod transcribe;
 
 use metavar_expr::MetaVarExpr;
-use rustc_ast::token::{self, NonterminalKind, Token, TokenKind};
+use rustc_ast::token::{Delimiter, NonterminalKind, Token, TokenKind};
 use rustc_ast::tokenstream::DelimSpan;
 use rustc_span::symbol::Ident;
 use rustc_span::Span;
 
-/// Contains the sub-token-trees of a "delimited" token tree such as `(a b c)`. The delimiters
-/// might be `NoDelim`, but they are not represented explicitly.
+/// Contains the sub-token-trees of a "delimited" token tree such as `(a b c)`.
+/// The delimiters are not represented explicitly in the `tts` vector.
 #[derive(PartialEq, Encodable, Decodable, Debug)]
 struct Delimited {
-    delim: token::DelimToken,
+    delim: Delimiter,
     /// FIXME: #67062 has details about why this is sub-optimal.
     tts: Vec<TokenTree>,
 }
diff --git a/compiler/rustc_expand/src/mbe/macro_check.rs b/compiler/rustc_expand/src/mbe/macro_check.rs
index c6a6e3d125f..35b5e0d0f2f 100644
--- a/compiler/rustc_expand/src/mbe/macro_check.rs
+++ b/compiler/rustc_expand/src/mbe/macro_check.rs
@@ -106,7 +106,7 @@
 //! bound.
 use crate::mbe::{KleeneToken, TokenTree};
 
-use rustc_ast::token::{DelimToken, Token, TokenKind};
+use rustc_ast::token::{Delimiter, Token, TokenKind};
 use rustc_ast::{NodeId, DUMMY_NODE_ID};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::MultiSpan;
@@ -439,7 +439,7 @@ fn check_nested_occurrences(
             }
             (NestedMacroState::MacroRulesNotName, &TokenTree::Delimited(_, ref del))
             | (NestedMacroState::MacroName, &TokenTree::Delimited(_, ref del))
-                if del.delim == DelimToken::Brace =>
+                if del.delim == Delimiter::Brace =>
             {
                 let macro_rules = state == NestedMacroState::MacroRulesNotName;
                 state = NestedMacroState::Empty;
@@ -469,7 +469,7 @@ fn check_nested_occurrences(
                 check_occurrences(sess, node_id, tt, macros, binders, ops, valid);
             }
             (NestedMacroState::MacroName, &TokenTree::Delimited(_, ref del))
-                if del.delim == DelimToken::Paren =>
+                if del.delim == Delimiter::Parenthesis =>
             {
                 state = NestedMacroState::MacroNameParen;
                 nested_binders = Binders::default();
@@ -484,7 +484,7 @@ fn check_nested_occurrences(
                 );
             }
             (NestedMacroState::MacroNameParen, &TokenTree::Delimited(_, ref del))
-                if del.delim == DelimToken::Brace =>
+                if del.delim == Delimiter::Brace =>
             {
                 state = NestedMacroState::Empty;
                 check_occurrences(
diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs
index 2cfd6968acc..050710097c3 100644
--- a/compiler/rustc_expand/src/mbe/macro_rules.rs
+++ b/compiler/rustc_expand/src/mbe/macro_rules.rs
@@ -8,7 +8,7 @@ use crate::mbe::macro_parser::{MatchedSeq, MatchedTokenTree, MatcherLoc};
 use crate::mbe::transcribe::transcribe;
 
 use rustc_ast as ast;
-use rustc_ast::token::{self, NonterminalKind, Token, TokenKind, TokenKind::*};
+use rustc_ast::token::{self, Delimiter, NonterminalKind, Token, TokenKind, TokenKind::*};
 use rustc_ast::tokenstream::{DelimSpan, TokenStream};
 use rustc_ast::{NodeId, DUMMY_NODE_ID};
 use rustc_ast_pretty::pprust;
@@ -260,16 +260,15 @@ fn generic_extension<'cx, 'tt>(
                 // Merge the gated spans from parsing the matcher with the pre-existing ones.
                 sess.gated_spans.merge(gated_spans_snapshot);
 
-                // Ignore the delimiters on the RHS.
-                let rhs = match &rhses[i] {
-                    mbe::TokenTree::Delimited(_, delimited) => &delimited.tts,
+                let (rhs, rhs_span): (&mbe::Delimited, DelimSpan) = match &rhses[i] {
+                    mbe::TokenTree::Delimited(span, delimited) => (&delimited, *span),
                     _ => cx.span_bug(sp, "malformed macro rhs"),
                 };
                 let arm_span = rhses[i].span();
 
-                let rhs_spans = rhs.iter().map(|t| t.span()).collect::<Vec<_>>();
+                let rhs_spans = rhs.tts.iter().map(|t| t.span()).collect::<Vec<_>>();
                 // rhs has holes ( `$id` and `$(...)` that need filled)
-                let mut tts = match transcribe(cx, &named_matches, &rhs, transparency) {
+                let mut tts = match transcribe(cx, &named_matches, &rhs, rhs_span, transparency) {
                     Ok(tts) => tts,
                     Err(mut err) => {
                         err.emit();
@@ -1251,8 +1250,8 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow {
                 ];
                 match tok {
                     TokenTree::Token(token) => match token.kind {
-                        OpenDelim(token::DelimToken::Brace)
-                        | OpenDelim(token::DelimToken::Bracket)
+                        OpenDelim(Delimiter::Brace)
+                        | OpenDelim(Delimiter::Bracket)
                         | Comma
                         | FatArrow
                         | Colon
diff --git a/compiler/rustc_expand/src/mbe/metavar_expr.rs b/compiler/rustc_expand/src/mbe/metavar_expr.rs
index 52a656e1d1c..cdc5e204236 100644
--- a/compiler/rustc_expand/src/mbe/metavar_expr.rs
+++ b/compiler/rustc_expand/src/mbe/metavar_expr.rs
@@ -1,4 +1,4 @@
-use rustc_ast::token;
+use rustc_ast::token::{self, Delimiter};
 use rustc_ast::tokenstream::{Cursor, TokenStream, TokenTree};
 use rustc_ast::{LitIntType, LitKind};
 use rustc_ast_pretty::pprust;
@@ -35,7 +35,7 @@ impl MetaVarExpr {
     ) -> PResult<'sess, MetaVarExpr> {
         let mut tts = input.trees();
         let ident = parse_ident(&mut tts, sess, outer_span)?;
-        let Some(TokenTree::Delimited(_, token::Paren, args)) = tts.next() else {
+        let Some(TokenTree::Delimited(_, Delimiter::Parenthesis, args)) = tts.next() else {
             let msg = "meta-variable expression parameter must be wrapped in parentheses";
             return Err(sess.span_diagnostic.struct_span_err(ident.span, msg));
         };
diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs
index 0bce6967a10..d52de24c393 100644
--- a/compiler/rustc_expand/src/mbe/quoted.rs
+++ b/compiler/rustc_expand/src/mbe/quoted.rs
@@ -1,7 +1,7 @@
 use crate::mbe::macro_parser::count_metavar_decls;
 use crate::mbe::{Delimited, KleeneOp, KleeneToken, MetaVarExpr, SequenceRepetition, TokenTree};
 
-use rustc_ast::token::{self, Token};
+use rustc_ast::token::{self, Delimiter, Token};
 use rustc_ast::{tokenstream, NodeId};
 use rustc_ast_pretty::pprust;
 use rustc_feature::Features;
@@ -147,11 +147,11 @@ fn parse_tree(
     match tree {
         // `tree` is a `$` token. Look at the next token in `trees`
         tokenstream::TokenTree::Token(Token { kind: token::Dollar, span }) => {
-            // FIXME: Handle `None`-delimited groups in a more systematic way
+            // FIXME: Handle `Invisible`-delimited groups in a more systematic way
             // during parsing.
             let mut next = outer_trees.next();
             let mut trees: Box<dyn Iterator<Item = tokenstream::TokenTree>>;
-            if let Some(tokenstream::TokenTree::Delimited(_, token::NoDelim, tts)) = next {
+            if let Some(tokenstream::TokenTree::Delimited(_, Delimiter::Invisible, tts)) = next {
                 trees = Box::new(tts.into_trees());
                 next = trees.next();
             } else {
@@ -162,7 +162,7 @@ fn parse_tree(
                 // `tree` is followed by a delimited set of token trees.
                 Some(tokenstream::TokenTree::Delimited(delim_span, delim, tts)) => {
                     if parsing_patterns {
-                        if delim != token::Paren {
+                        if delim != Delimiter::Parenthesis {
                             span_dollar_dollar_or_metavar_in_the_lhs_err(
                                 sess,
                                 &Token { kind: token::OpenDelim(delim), span: delim_span.entire() },
@@ -170,7 +170,7 @@ fn parse_tree(
                         }
                     } else {
                         match delim {
-                            token::Brace => {
+                            Delimiter::Brace => {
                                 // The delimiter is `{`.  This indicates the beginning
                                 // of a meta-variable expression (e.g. `${count(ident)}`).
                                 // Try to parse the meta-variable expression.
@@ -191,7 +191,7 @@ fn parse_tree(
                                     }
                                 }
                             }
-                            token::Paren => {}
+                            Delimiter::Parenthesis => {}
                             _ => {
                                 let tok = pprust::token_kind_to_string(&token::OpenDelim(delim));
                                 let msg = format!("expected `(` or `{{`, found `{}`", tok);
diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs
index d25f044234c..94b6c3153ca 100644
--- a/compiler/rustc_expand/src/mbe/transcribe.rs
+++ b/compiler/rustc_expand/src/mbe/transcribe.rs
@@ -2,7 +2,7 @@ use crate::base::ExtCtxt;
 use crate::mbe::macro_parser::{MatchedNonterminal, MatchedSeq, MatchedTokenTree, NamedMatch};
 use crate::mbe::{self, MetaVarExpr};
 use rustc_ast::mut_visit::{self, MutVisitor};
-use rustc_ast::token::{self, Token, TokenKind};
+use rustc_ast::token::{self, Delimiter, Token, TokenKind};
 use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndSpacing};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{pluralize, PResult};
@@ -27,23 +27,14 @@ impl MutVisitor for Marker {
 
 /// An iterator over the token trees in a delimited token tree (`{ ... }`) or a sequence (`$(...)`).
 enum Frame<'a> {
-    Delimited {
-        tts: &'a [mbe::TokenTree],
-        delim_token: token::DelimToken,
-        idx: usize,
-        span: DelimSpan,
-    },
-    Sequence {
-        tts: &'a [mbe::TokenTree],
-        idx: usize,
-        sep: Option<Token>,
-    },
+    Delimited { tts: &'a [mbe::TokenTree], idx: usize, delim: Delimiter, span: DelimSpan },
+    Sequence { tts: &'a [mbe::TokenTree], idx: usize, sep: Option<Token> },
 }
 
 impl<'a> Frame<'a> {
     /// Construct a new frame around the delimited set of tokens.
-    fn new(tts: &'a [mbe::TokenTree]) -> Frame<'a> {
-        Frame::Delimited { tts, delim_token: token::NoDelim, idx: 0, span: DelimSpan::dummy() }
+    fn new(src: &'a mbe::Delimited, span: DelimSpan) -> Frame<'a> {
+        Frame::Delimited { tts: &src.tts, idx: 0, delim: src.delim, span }
     }
 }
 
@@ -85,17 +76,18 @@ impl<'a> Iterator for Frame<'a> {
 pub(super) fn transcribe<'a>(
     cx: &ExtCtxt<'a>,
     interp: &FxHashMap<MacroRulesNormalizedIdent, NamedMatch>,
-    src: &[mbe::TokenTree],
+    src: &mbe::Delimited,
+    src_span: DelimSpan,
     transparency: Transparency,
 ) -> PResult<'a, TokenStream> {
     // Nothing for us to transcribe...
-    if src.is_empty() {
+    if src.tts.is_empty() {
         return Ok(TokenStream::default());
     }
 
     // We descend into the RHS (`src`), expanding things as we go. This stack contains the things
     // we have yet to expand/are still expanding. We start the stack off with the whole RHS.
-    let mut stack: SmallVec<[Frame<'_>; 1]> = smallvec![Frame::new(&src)];
+    let mut stack: SmallVec<[Frame<'_>; 1]> = smallvec![Frame::new(&src, src_span)];
 
     // As we descend in the RHS, we will need to be able to match nested sequences of matchers.
     // `repeats` keeps track of where we are in matching at each level, with the last element being
@@ -149,14 +141,14 @@ pub(super) fn transcribe<'a>(
                 // We are done processing a Delimited. If this is the top-level delimited, we are
                 // done. Otherwise, we unwind the result_stack to append what we have produced to
                 // any previous results.
-                Frame::Delimited { delim_token, span, .. } => {
+                Frame::Delimited { delim, span, .. } => {
                     if result_stack.is_empty() {
                         // No results left to compute! We are back at the top-level.
                         return Ok(TokenStream::new(result));
                     }
 
                     // Step back into the parent Delimited.
-                    let tree = TokenTree::Delimited(span, delim_token, TokenStream::new(result));
+                    let tree = TokenTree::Delimited(span, delim, TokenStream::new(result));
                     result = result_stack.pop().unwrap();
                     result.push(tree.into());
                 }
@@ -239,7 +231,7 @@ pub(super) fn transcribe<'a>(
                         }
                         MatchedNonterminal(ref nt) => {
                             // Other variables are emitted into the output stream as groups with
-                            // `Delimiter::None` to maintain parsing priorities.
+                            // `Delimiter::Invisible` to maintain parsing priorities.
                             // `Interpolated` is currently used for such groups in rustc parser.
                             marker.visit_span(&mut sp);
                             let token = TokenTree::token(token::Interpolated(nt.clone()), sp);
@@ -277,7 +269,7 @@ pub(super) fn transcribe<'a>(
                 mut_visit::visit_delim_span(&mut span, &mut marker);
                 stack.push(Frame::Delimited {
                     tts: &delimited.tts,
-                    delim_token: delimited.delim,
+                    delim: delimited.delim,
                     idx: 0,
                     span,
                 });
diff --git a/compiler/rustc_expand/src/parse/tests.rs b/compiler/rustc_expand/src/parse/tests.rs
index 4a8236b2cf3..5d447d911e7 100644
--- a/compiler/rustc_expand/src/parse/tests.rs
+++ b/compiler/rustc_expand/src/parse/tests.rs
@@ -1,7 +1,7 @@
 use crate::tests::{matches_codepattern, string_to_stream, with_error_checking_parse};
 
 use rustc_ast::ptr::P;
-use rustc_ast::token::{self, Token};
+use rustc_ast::token::{self, Delimiter, Token};
 use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree};
 use rustc_ast::visit;
 use rustc_ast::{self as ast, PatKind};
@@ -77,13 +77,14 @@ fn string_to_tts_macro() {
                         TokenTree::Delimited(_, first_delim, first_tts),
                         TokenTree::Token(Token { kind: token::FatArrow, .. }),
                         TokenTree::Delimited(_, second_delim, second_tts),
-                    ] if macro_delim == &token::Paren => {
+                    ] if macro_delim == &Delimiter::Parenthesis => {
                         let tts = &first_tts.trees().collect::<Vec<_>>();
                         match &tts[..] {
                             [
                                 TokenTree::Token(Token { kind: token::Dollar, .. }),
                                 TokenTree::Token(Token { kind: token::Ident(name, false), .. }),
-                            ] if first_delim == &token::Paren && name.as_str() == "a" => {}
+                            ] if first_delim == &Delimiter::Parenthesis && name.as_str() == "a" => {
+                            }
                             _ => panic!("value 3: {:?} {:?}", first_delim, first_tts),
                         }
                         let tts = &second_tts.trees().collect::<Vec<_>>();
@@ -91,7 +92,8 @@ fn string_to_tts_macro() {
                             [
                                 TokenTree::Token(Token { kind: token::Dollar, .. }),
                                 TokenTree::Token(Token { kind: token::Ident(name, false), .. }),
-                            ] if second_delim == &token::Paren && name.as_str() == "a" => {}
+                            ] if second_delim == &Delimiter::Parenthesis
+                                && name.as_str() == "a" => {}
                             _ => panic!("value 4: {:?} {:?}", second_delim, second_tts),
                         }
                     }
@@ -113,7 +115,7 @@ fn string_to_tts_1() {
             TokenTree::token(token::Ident(Symbol::intern("a"), false), sp(3, 4)).into(),
             TokenTree::Delimited(
                 DelimSpan::from_pair(sp(5, 6), sp(13, 14)),
-                token::DelimToken::Paren,
+                Delimiter::Parenthesis,
                 TokenStream::new(vec![
                     TokenTree::token(token::Ident(Symbol::intern("b"), false), sp(6, 7)).into(),
                     TokenTree::token(token::Colon, sp(8, 9)).into(),
@@ -124,7 +126,7 @@ fn string_to_tts_1() {
             .into(),
             TokenTree::Delimited(
                 DelimSpan::from_pair(sp(15, 16), sp(20, 21)),
-                token::DelimToken::Brace,
+                Delimiter::Brace,
                 TokenStream::new(vec![
                     TokenTree::token(token::Ident(Symbol::intern("b"), false), sp(17, 18)).into(),
                     TokenTree::token(token::Semi, sp(18, 19)).into(),
diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs
index 15af5fdc5f8..0d5d6ee0794 100644
--- a/compiler/rustc_expand/src/placeholders.rs
+++ b/compiler/rustc_expand/src/placeholders.rs
@@ -149,6 +149,7 @@ pub fn placeholder(
                 ident,
                 is_placeholder: true,
                 kind: ast::GenericParamKind::Lifetime,
+                colon_span: None,
             }
         }]),
         AstFragmentKind::Params => AstFragment::Params(smallvec![ast::Param {
diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs
index aec401a041c..8e1966a0711 100644
--- a/compiler/rustc_expand/src/proc_macro.rs
+++ b/compiler/rustc_expand/src/proc_macro.rs
@@ -9,6 +9,7 @@ use rustc_data_structures::sync::Lrc;
 use rustc_errors::ErrorGuaranteed;
 use rustc_parse::nt_to_tokenstream;
 use rustc_parse::parser::ForceCollect;
+use rustc_span::profiling::SpannedEventArgRecorder;
 use rustc_span::{Span, DUMMY_SP};
 
 const EXEC_STRATEGY: pm::bridge::server::SameThread = pm::bridge::server::SameThread;
@@ -25,7 +26,10 @@ impl base::ProcMacro for BangProcMacro {
         input: TokenStream,
     ) -> Result<TokenStream, ErrorGuaranteed> {
         let _timer =
-            ecx.sess.prof.generic_activity_with_arg("expand_proc_macro", ecx.expansion_descr());
+            ecx.sess.prof.generic_activity_with_arg_recorder("expand_proc_macro", |recorder| {
+                recorder.record_arg_with_span(ecx.expansion_descr(), span);
+            });
+
         let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace;
         let server = proc_macro_server::Rustc::new(ecx);
         self.client.run(&EXEC_STRATEGY, server, input, proc_macro_backtrace).map_err(|e| {
@@ -51,7 +55,10 @@ impl base::AttrProcMacro for AttrProcMacro {
         annotated: TokenStream,
     ) -> Result<TokenStream, ErrorGuaranteed> {
         let _timer =
-            ecx.sess.prof.generic_activity_with_arg("expand_proc_macro", ecx.expansion_descr());
+            ecx.sess.prof.generic_activity_with_arg_recorder("expand_proc_macro", |recorder| {
+                recorder.record_arg_with_span(ecx.expansion_descr(), span);
+            });
+
         let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace;
         let server = proc_macro_server::Rustc::new(ecx);
         self.client
@@ -103,7 +110,9 @@ impl MultiItemModifier for ProcMacroDerive {
 
         let stream = {
             let _timer =
-                ecx.sess.prof.generic_activity_with_arg("expand_proc_macro", ecx.expansion_descr());
+                ecx.sess.prof.generic_activity_with_arg_recorder("expand_proc_macro", |recorder| {
+                    recorder.record_arg_with_span(ecx.expansion_descr(), span);
+                });
             let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace;
             let server = proc_macro_server::Rustc::new(ecx);
             match self.client.run(&EXEC_STRATEGY, server, input, proc_macro_backtrace) {
diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs
index bd6f0b77ebf..b7230cec3e4 100644
--- a/compiler/rustc_expand/src/proc_macro_server.rs
+++ b/compiler/rustc_expand/src/proc_macro_server.rs
@@ -28,24 +28,24 @@ trait ToInternal<T> {
     fn to_internal(self) -> T;
 }
 
-impl FromInternal<token::DelimToken> for Delimiter {
-    fn from_internal(delim: token::DelimToken) -> Delimiter {
+impl FromInternal<token::Delimiter> for Delimiter {
+    fn from_internal(delim: token::Delimiter) -> Delimiter {
         match delim {
-            token::Paren => Delimiter::Parenthesis,
-            token::Brace => Delimiter::Brace,
-            token::Bracket => Delimiter::Bracket,
-            token::NoDelim => Delimiter::None,
+            token::Delimiter::Parenthesis => Delimiter::Parenthesis,
+            token::Delimiter::Brace => Delimiter::Brace,
+            token::Delimiter::Bracket => Delimiter::Bracket,
+            token::Delimiter::Invisible => Delimiter::None,
         }
     }
 }
 
-impl ToInternal<token::DelimToken> for Delimiter {
-    fn to_internal(self) -> token::DelimToken {
+impl ToInternal<token::Delimiter> for Delimiter {
+    fn to_internal(self) -> token::Delimiter {
         match self {
-            Delimiter::Parenthesis => token::Paren,
-            Delimiter::Brace => token::Brace,
-            Delimiter::Bracket => token::Bracket,
-            Delimiter::None => token::NoDelim,
+            Delimiter::Parenthesis => token::Delimiter::Parenthesis,
+            Delimiter::Brace => token::Delimiter::Brace,
+            Delimiter::Bracket => token::Delimiter::Bracket,
+            Delimiter::None => token::Delimiter::Invisible,
         }
     }
 }
@@ -61,7 +61,7 @@ impl FromInternal<(TreeAndSpacing, &'_ mut Vec<Self>, &mut Rustc<'_, '_>)>
         let joint = spacing == Joint;
         let Token { kind, span } = match tree {
             tokenstream::TokenTree::Delimited(span, delim, tts) => {
-                let delimiter = Delimiter::from_internal(delim);
+                let delimiter = pm::Delimiter::from_internal(delim);
                 return TokenTree::Group(Group { delimiter, stream: tts, span, flatten: false });
             }
             tokenstream::TokenTree::Token(token) => token,
@@ -164,7 +164,7 @@ impl FromInternal<(TreeAndSpacing, &'_ mut Vec<Self>, &mut Rustc<'_, '_>)>
                 .map(|kind| tokenstream::TokenTree::token(kind, span))
                 .collect();
                 stack.push(TokenTree::Group(Group {
-                    delimiter: Delimiter::Bracket,
+                    delimiter: pm::Delimiter::Bracket,
                     stream,
                     span: DelimSpan::from_single(span),
                     flatten: false,
@@ -181,7 +181,7 @@ impl FromInternal<(TreeAndSpacing, &'_ mut Vec<Self>, &mut Rustc<'_, '_>)>
             Interpolated(nt) => {
                 let stream = nt_to_tokenstream(&nt, rustc.sess(), CanSynthesizeMissingTokens::No);
                 TokenTree::Group(Group {
-                    delimiter: Delimiter::None,
+                    delimiter: pm::Delimiter::None,
                     stream,
                     span: DelimSpan::from_single(span),
                     flatten: crate::base::pretty_printing_compatibility_hack(&nt, rustc.sess()),
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index f3d4c8ab438..9159d60463c 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -544,6 +544,8 @@ declare_features! (
     (active, used_with_arg, "1.60.0", Some(93798), None),
     /// Allows `extern "wasm" fn`
     (active, wasm_abi, "1.53.0", Some(83788), None),
+    /// Allows `do yeet` expressions
+    (active, yeet_expr, "1.62.0", Some(96373), None),
     // !!!!    !!!!    !!!!    !!!!   !!!!    !!!!    !!!!    !!!!    !!!!    !!!!    !!!!
     // Features are listed in alphabetical order. Tidy will fail if you don't keep it this way.
     // !!!!    !!!!    !!!!    !!!!   !!!!    !!!!    !!!!    !!!!    !!!!    !!!!    !!!!
diff --git a/compiler/rustc_hir/src/arena.rs b/compiler/rustc_hir/src/arena.rs
index 27ec4619064..5d1314ebb48 100644
--- a/compiler/rustc_hir/src/arena.rs
+++ b/compiler/rustc_hir/src/arena.rs
@@ -15,11 +15,13 @@ macro_rules! arena_types {
             [] block: rustc_hir::Block<'tcx>,
             [] bare_fn_ty: rustc_hir::BareFnTy<'tcx>,
             [] body: rustc_hir::Body<'tcx>,
+            [] generics: rustc_hir::Generics<'tcx>,
             [] generic_arg: rustc_hir::GenericArg<'tcx>,
             [] generic_args: rustc_hir::GenericArgs<'tcx>,
             [] generic_bound: rustc_hir::GenericBound<'tcx>,
             [] generic_param: rustc_hir::GenericParam<'tcx>,
             [] expr: rustc_hir::Expr<'tcx>,
+            [] impl_: rustc_hir::Impl<'tcx>,
             [] let_expr: rustc_hir::Let<'tcx>,
             [] expr_field: rustc_hir::ExprField<'tcx>,
             [] pat_field: rustc_hir::PatField<'tcx>,
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index b3de86662eb..dfeee3f356f 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -4,8 +4,8 @@ crate use crate::hir_id::{HirId, ItemLocalId};
 use crate::intravisit::FnKind;
 use crate::LangItem;
 
+use rustc_ast as ast;
 use rustc_ast::util::parser::ExprPrecedence;
-use rustc_ast::{self as ast, CrateSugar};
 use rustc_ast::{Attribute, FloatTy, IntTy, Label, LitKind, TraitObjectSyntax, UintTy};
 pub use rustc_ast::{BorrowKind, ImplPolarity, IsAuto};
 pub use rustc_ast::{CaptureBy, Movability, Mutability};
@@ -17,7 +17,7 @@ use rustc_error_messages::MultiSpan;
 use rustc_index::vec::IndexVec;
 use rustc_macros::HashStable_Generic;
 use rustc_span::hygiene::MacroKind;
-use rustc_span::source_map::{SourceMap, Spanned};
+use rustc_span::source_map::Spanned;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{def_id::LocalDefId, BytePos, Span, DUMMY_SP};
 use rustc_target::asm::InlineAsmRegOrRegClass;
@@ -93,8 +93,6 @@ pub enum LifetimeName {
     Param(ParamName),
 
     /// User wrote nothing (e.g., the lifetime in `&u32`).
-    ///
-    /// The bool indicates whether the user should have written something.
     Implicit,
 
     /// Implicit lifetime in a context like `dyn Foo`. This is
@@ -444,9 +442,6 @@ pub enum GenericBound<'hir> {
     Outlives(Lifetime),
 }
 
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
-rustc_data_structures::static_assert_size!(GenericBound<'_>, 48);
-
 impl GenericBound<'_> {
     pub fn trait_ref(&self) -> Option<&TraitRef<'_>> {
         match self {
@@ -501,61 +496,25 @@ pub enum GenericParamKind<'hir> {
 pub struct GenericParam<'hir> {
     pub hir_id: HirId,
     pub name: ParamName,
-    pub bounds: GenericBounds<'hir>,
     pub span: Span,
     pub pure_wrt_drop: bool,
     pub kind: GenericParamKind<'hir>,
+    pub colon_span: Option<Span>,
 }
 
 impl<'hir> GenericParam<'hir> {
-    pub fn bounds_span_for_suggestions(&self) -> Option<Span> {
-        self.bounds
-            .iter()
-            .fold(None, |span: Option<Span>, bound| {
-                // We include bounds that come from a `#[derive(_)]` but point at the user's code,
-                // as we use this method to get a span appropriate for suggestions.
-                if !bound.span().can_be_used_for_suggestions() {
-                    None
-                } else {
-                    let span = span.map(|s| s.to(bound.span())).unwrap_or_else(|| bound.span());
-                    Some(span)
-                }
-            })
-            .map(|sp| sp.shrink_to_hi())
+    /// Synthetic type-parameters are inserted after normal ones.
+    /// In order for normal parameters to be able to refer to synthetic ones,
+    /// scans them first.
+    pub fn is_impl_trait(&self) -> bool {
+        matches!(self.kind, GenericParamKind::Type { synthetic: true, .. })
     }
 
-    /// Returns the span of `:` after a generic parameter.
-    ///
-    /// For example:
-    ///
-    /// ```text
-    /// fn a<T:>()
-    ///       ^
-    ///       |      here
-    ///       here   |
-    ///              v
-    /// fn b<T       :>()
+    /// This can happen for `async fn`, e.g. `async fn f<'_>(&'_ self)`.
     ///
-    /// fn c<T
-    ///
-    /// :>()
-    /// ^
-    /// |
-    /// here
-    /// ```
-    pub fn colon_span_for_suggestions(&self, source_map: &SourceMap) -> Option<Span> {
-        let sp = source_map
-            .span_extend_while(self.span.shrink_to_hi(), |c| c.is_whitespace() || c == ':')
-            .ok()?;
-
-        let snippet = source_map.span_to_snippet(sp).ok()?;
-        let offset = snippet.find(':')?;
-
-        let colon_sp = sp
-            .with_lo(BytePos(sp.lo().0 + offset as u32))
-            .with_hi(BytePos(sp.lo().0 + (offset + ':'.len_utf8()) as u32));
-
-        Some(colon_sp)
+    /// See `lifetime_to_generic_param` in `rustc_ast_lowering` for more information.
+    pub fn is_elided_lifetime(&self) -> bool {
+        matches!(self.kind, GenericParamKind::Lifetime { kind: LifetimeParamKind::Elided })
     }
 }
 
@@ -572,17 +531,22 @@ pub struct GenericParamCount {
 #[derive(Debug, HashStable_Generic)]
 pub struct Generics<'hir> {
     pub params: &'hir [GenericParam<'hir>],
-    pub where_clause: WhereClause<'hir>,
+    pub predicates: &'hir [WherePredicate<'hir>],
+    pub has_where_clause: bool,
+    pub where_clause_span: Span,
     pub span: Span,
 }
 
 impl<'hir> Generics<'hir> {
-    pub const fn empty() -> Generics<'hir> {
-        Generics {
+    pub const fn empty() -> &'hir Generics<'hir> {
+        const NOPE: Generics<'_> = Generics {
             params: &[],
-            where_clause: WhereClause { predicates: &[], span: DUMMY_SP },
+            predicates: &[],
+            has_where_clause: false,
+            where_clause_span: DUMMY_SP,
             span: DUMMY_SP,
-        }
+        };
+        &NOPE
     }
 
     pub fn get_named(&self, name: Symbol) -> Option<&GenericParam<'_>> {
@@ -601,32 +565,122 @@ impl<'hir> Generics<'hir> {
             self.params.iter().map(|p| p.span).collect::<Vec<Span>>().into()
         }
     }
-}
 
-/// A where-clause in a definition.
-#[derive(Debug, HashStable_Generic)]
-pub struct WhereClause<'hir> {
-    pub predicates: &'hir [WherePredicate<'hir>],
-    // Only valid if predicates aren't empty.
-    pub span: Span,
-}
+    /// If there are generic parameters, return where to introduce a new one.
+    pub fn span_for_param_suggestion(&self) -> Option<Span> {
+        if self.params.iter().any(|p| self.span.contains(p.span)) {
+            // `fn foo<A>(t: impl Trait)`
+            //          ^ suggest `, T: Trait` here
+            let span = self.span.with_lo(self.span.hi() - BytePos(1)).shrink_to_lo();
+            Some(span)
+        } else {
+            None
+        }
+    }
 
-impl WhereClause<'_> {
-    pub fn span(&self) -> Option<Span> {
-        if self.predicates.is_empty() { None } else { Some(self.span) }
+    pub fn where_clause_span(&self) -> Option<Span> {
+        if self.predicates.is_empty() { None } else { Some(self.where_clause_span) }
     }
 
-    /// The `WhereClause` under normal circumstances points at either the predicates or the empty
+    /// The `where_span` under normal circumstances points at either the predicates or the empty
     /// space where the `where` clause should be. Only of use for diagnostic suggestions.
     pub fn span_for_predicates_or_empty_place(&self) -> Span {
-        self.span
+        self.where_clause_span
     }
 
     /// `Span` where further predicates would be suggested, accounting for trailing commas, like
     ///  in `fn foo<T>(t: T) where T: Foo,` so we don't suggest two trailing commas.
-    pub fn tail_span_for_suggestion(&self) -> Span {
+    pub fn tail_span_for_predicate_suggestion(&self) -> Span {
         let end = self.span_for_predicates_or_empty_place().shrink_to_hi();
-        self.predicates.last().map_or(end, |p| p.span()).shrink_to_hi().to(end)
+        if self.has_where_clause {
+            self.predicates
+                .iter()
+                .filter(|p| p.in_where_clause())
+                .last()
+                .map_or(end, |p| p.span())
+                .shrink_to_hi()
+                .to(end)
+        } else {
+            end
+        }
+    }
+
+    pub fn bounds_for_param(
+        &self,
+        param_def_id: LocalDefId,
+    ) -> impl Iterator<Item = &WhereBoundPredicate<'_>> {
+        self.predicates.iter().filter_map(move |pred| match pred {
+            WherePredicate::BoundPredicate(bp) if bp.is_param_bound(param_def_id.to_def_id()) => {
+                Some(bp)
+            }
+            _ => None,
+        })
+    }
+
+    pub fn bounds_span_for_suggestions(&self, param_def_id: LocalDefId) -> Option<Span> {
+        self.bounds_for_param(param_def_id).flat_map(|bp| bp.bounds.iter().rev()).find_map(
+            |bound| {
+                // We include bounds that come from a `#[derive(_)]` but point at the user's code,
+                // as we use this method to get a span appropriate for suggestions.
+                let bs = bound.span();
+                if bs.can_be_used_for_suggestions() { Some(bs.shrink_to_hi()) } else { None }
+            },
+        )
+    }
+
+    pub fn span_for_predicate_removal(&self, pos: usize) -> Span {
+        let predicate = &self.predicates[pos];
+        let span = predicate.span();
+
+        if !predicate.in_where_clause() {
+            // <T: ?Sized, U>
+            //   ^^^^^^^^
+            return span;
+        }
+
+        // We need to find out which comma to remove.
+        if pos < self.predicates.len() - 1 {
+            let next_pred = &self.predicates[pos + 1];
+            if next_pred.in_where_clause() {
+                // where T: ?Sized, Foo: Bar,
+                //       ^^^^^^^^^^^
+                return span.until(next_pred.span());
+            }
+        }
+
+        if pos > 0 {
+            let prev_pred = &self.predicates[pos - 1];
+            if prev_pred.in_where_clause() {
+                // where Foo: Bar, T: ?Sized,
+                //               ^^^^^^^^^^^
+                return prev_pred.span().shrink_to_hi().to(span);
+            }
+        }
+
+        // This is the only predicate in the where clause.
+        // where T: ?Sized
+        // ^^^^^^^^^^^^^^^
+        self.where_clause_span
+    }
+
+    pub fn span_for_bound_removal(&self, predicate_pos: usize, bound_pos: usize) -> Span {
+        let predicate = &self.predicates[predicate_pos];
+        let bounds = predicate.bounds();
+
+        if bounds.len() == 1 {
+            return self.span_for_predicate_removal(predicate_pos);
+        }
+
+        let span = bounds[bound_pos].span();
+        if bound_pos == 0 {
+            // where T: ?Sized + Bar, Foo: Bar,
+            //          ^^^^^^^^^
+            span.to(bounds[1].span().shrink_to_lo())
+        } else {
+            // where T: Bar + ?Sized, Foo: Bar,
+            //             ^^^^^^^^^
+            bounds[bound_pos - 1].span().shrink_to_hi().to(span)
+        }
     }
 }
 
@@ -649,12 +703,29 @@ impl<'hir> WherePredicate<'hir> {
             WherePredicate::EqPredicate(p) => p.span,
         }
     }
+
+    pub fn in_where_clause(&self) -> bool {
+        match self {
+            WherePredicate::BoundPredicate(p) => p.in_where_clause,
+            WherePredicate::RegionPredicate(p) => p.in_where_clause,
+            WherePredicate::EqPredicate(_) => false,
+        }
+    }
+
+    pub fn bounds(&self) -> GenericBounds<'hir> {
+        match self {
+            WherePredicate::BoundPredicate(p) => p.bounds,
+            WherePredicate::RegionPredicate(p) => p.bounds,
+            WherePredicate::EqPredicate(_) => &[],
+        }
+    }
 }
 
 /// A type bound (e.g., `for<'c> Foo: Send + Clone + 'c`).
 #[derive(Debug, HashStable_Generic)]
 pub struct WhereBoundPredicate<'hir> {
     pub span: Span,
+    pub in_where_clause: bool,
     /// Any generics from a `for` binding.
     pub bound_generic_params: &'hir [GenericParam<'hir>],
     /// The type being bounded.
@@ -666,14 +737,7 @@ pub struct WhereBoundPredicate<'hir> {
 impl<'hir> WhereBoundPredicate<'hir> {
     /// Returns `true` if `param_def_id` matches the `bounded_ty` of this predicate.
     pub fn is_param_bound(&self, param_def_id: DefId) -> bool {
-        let TyKind::Path(QPath::Resolved(None, path)) = self.bounded_ty.kind else {
-            return false;
-        };
-        match path.res {
-            Res::Def(DefKind::TyParam, def_id)
-            | Res::SelfTy { trait_: Some(def_id), alias_to: None } => def_id == param_def_id,
-            _ => false,
-        }
+        self.bounded_ty.as_generic_param().map_or(false, |(def_id, _)| def_id == param_def_id)
     }
 }
 
@@ -681,6 +745,7 @@ impl<'hir> WhereBoundPredicate<'hir> {
 #[derive(Debug, HashStable_Generic)]
 pub struct WhereRegionPredicate<'hir> {
     pub span: Span,
+    pub in_where_clause: bool,
     pub lifetime: Lifetime,
     pub bounds: GenericBounds<'hir>,
 }
@@ -2080,7 +2145,7 @@ impl TraitItemId {
 pub struct TraitItem<'hir> {
     pub ident: Ident,
     pub def_id: LocalDefId,
-    pub generics: Generics<'hir>,
+    pub generics: &'hir Generics<'hir>,
     pub kind: TraitItemKind<'hir>,
     pub span: Span,
 }
@@ -2140,10 +2205,10 @@ impl ImplItemId {
 pub struct ImplItem<'hir> {
     pub ident: Ident,
     pub def_id: LocalDefId,
-    pub vis: Visibility<'hir>,
-    pub generics: Generics<'hir>,
+    pub generics: &'hir Generics<'hir>,
     pub kind: ImplItemKind<'hir>,
     pub span: Span,
+    pub vis_span: Span,
 }
 
 impl ImplItem<'_> {
@@ -2246,6 +2311,23 @@ pub struct Ty<'hir> {
     pub span: Span,
 }
 
+impl<'hir> Ty<'hir> {
+    /// Returns `true` if `param_def_id` matches the `bounded_ty` of this predicate.
+    pub fn as_generic_param(&self) -> Option<(DefId, Ident)> {
+        let TyKind::Path(QPath::Resolved(None, path)) = self.kind else {
+            return None;
+        };
+        let [segment] = &path.segments else {
+            return None;
+        };
+        match path.res {
+            Res::Def(DefKind::TyParam, def_id)
+            | Res::SelfTy { trait_: Some(def_id), alias_to: None } => Some((def_id, segment.ident)),
+            _ => None,
+        }
+    }
+}
+
 /// Not represented directly in the AST; referred to by name through a `ty_path`.
 #[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Hash, Debug)]
 #[derive(HashStable_Generic)]
@@ -2345,7 +2427,7 @@ pub struct BareFnTy<'hir> {
 
 #[derive(Debug, HashStable_Generic)]
 pub struct OpaqueTy<'hir> {
-    pub generics: Generics<'hir>,
+    pub generics: &'hir Generics<'hir>,
     pub bounds: GenericBounds<'hir>,
     pub origin: OpaqueTyOrigin,
 }
@@ -2645,34 +2727,11 @@ pub struct PolyTraitRef<'hir> {
     pub span: Span,
 }
 
-pub type Visibility<'hir> = Spanned<VisibilityKind<'hir>>;
-
-#[derive(Copy, Clone, Debug, HashStable_Generic)]
-pub enum VisibilityKind<'hir> {
-    Public,
-    Crate(CrateSugar),
-    Restricted { path: &'hir Path<'hir>, hir_id: HirId },
-    Inherited,
-}
-
-impl VisibilityKind<'_> {
-    pub fn is_pub(&self) -> bool {
-        matches!(*self, VisibilityKind::Public)
-    }
-
-    pub fn is_pub_restricted(&self) -> bool {
-        match *self {
-            VisibilityKind::Public | VisibilityKind::Inherited => false,
-            VisibilityKind::Crate(..) | VisibilityKind::Restricted { .. } => true,
-        }
-    }
-}
-
 #[derive(Debug, HashStable_Generic)]
 pub struct FieldDef<'hir> {
     pub span: Span,
+    pub vis_span: Span,
     pub ident: Ident,
-    pub vis: Visibility<'hir>,
     pub hir_id: HirId,
     pub ty: &'hir Ty<'hir>,
 }
@@ -2744,8 +2803,8 @@ pub struct Item<'hir> {
     pub ident: Ident,
     pub def_id: LocalDefId,
     pub kind: ItemKind<'hir>,
-    pub vis: Visibility<'hir>,
     pub span: Span,
+    pub vis_span: Span,
 }
 
 impl Item<'_> {
@@ -2842,7 +2901,7 @@ pub enum ItemKind<'hir> {
     /// A `const` item.
     Const(&'hir Ty<'hir>, BodyId),
     /// A function declaration.
-    Fn(FnSig<'hir>, Generics<'hir>, BodyId),
+    Fn(FnSig<'hir>, &'hir Generics<'hir>, BodyId),
     /// A MBE macro definition (`macro_rules!` or `macro`).
     Macro(ast::MacroDef, MacroKind),
     /// A module.
@@ -2852,22 +2911,22 @@ pub enum ItemKind<'hir> {
     /// Module-level inline assembly (from `global_asm!`).
     GlobalAsm(&'hir InlineAsm<'hir>),
     /// A type alias, e.g., `type Foo = Bar<u8>`.
-    TyAlias(&'hir Ty<'hir>, Generics<'hir>),
+    TyAlias(&'hir Ty<'hir>, &'hir Generics<'hir>),
     /// An opaque `impl Trait` type alias, e.g., `type Foo = impl Bar;`.
     OpaqueTy(OpaqueTy<'hir>),
     /// An enum definition, e.g., `enum Foo<A, B> {C<A>, D<B>}`.
-    Enum(EnumDef<'hir>, Generics<'hir>),
+    Enum(EnumDef<'hir>, &'hir Generics<'hir>),
     /// A struct definition, e.g., `struct Foo<A> {x: A}`.
-    Struct(VariantData<'hir>, Generics<'hir>),
+    Struct(VariantData<'hir>, &'hir Generics<'hir>),
     /// A union definition, e.g., `union Foo<A, B> {x: A, y: B}`.
-    Union(VariantData<'hir>, Generics<'hir>),
+    Union(VariantData<'hir>, &'hir Generics<'hir>),
     /// A trait definition.
-    Trait(IsAuto, Unsafety, Generics<'hir>, GenericBounds<'hir>, &'hir [TraitItemRef]),
+    Trait(IsAuto, Unsafety, &'hir Generics<'hir>, GenericBounds<'hir>, &'hir [TraitItemRef]),
     /// A trait alias.
-    TraitAlias(Generics<'hir>, GenericBounds<'hir>),
+    TraitAlias(&'hir Generics<'hir>, GenericBounds<'hir>),
 
     /// An implementation, e.g., `impl<A> Trait for Foo { .. }`.
-    Impl(Impl<'hir>),
+    Impl(&'hir Impl<'hir>),
 }
 
 #[derive(Debug, HashStable_Generic)]
@@ -2879,7 +2938,7 @@ pub struct Impl<'hir> {
     // decoding as `Span`s cannot be decoded when a `Session` is not available.
     pub defaultness_span: Option<Span>,
     pub constness: Constness,
-    pub generics: Generics<'hir>,
+    pub generics: &'hir Generics<'hir>,
 
     /// The trait being implemented, if any.
     pub of_trait: Option<TraitRef<'hir>>,
@@ -3002,7 +3061,7 @@ pub struct ForeignItem<'hir> {
     pub kind: ForeignItemKind<'hir>,
     pub def_id: LocalDefId,
     pub span: Span,
-    pub vis: Visibility<'hir>,
+    pub vis_span: Span,
 }
 
 impl ForeignItem<'_> {
@@ -3021,7 +3080,7 @@ impl ForeignItem<'_> {
 #[derive(Debug, HashStable_Generic)]
 pub enum ForeignItemKind<'hir> {
     /// A foreign function.
-    Fn(&'hir FnDecl<'hir>, &'hir [Ident], Generics<'hir>),
+    Fn(&'hir FnDecl<'hir>, &'hir [Ident], &'hir Generics<'hir>),
     /// A foreign static item (`static ext: u8`).
     Static(&'hir Ty<'hir>, Mutability),
     /// A foreign type.
@@ -3210,7 +3269,6 @@ pub enum Node<'hir> {
 
     Lifetime(&'hir Lifetime),
     GenericParam(&'hir GenericParam<'hir>),
-    Visibility(&'hir Visibility<'hir>),
 
     Crate(&'hir Mod<'hir>),
 
@@ -3253,7 +3311,6 @@ impl<'hir> Node<'hir> {
             | Node::Binding(..)
             | Node::Arm(..)
             | Node::Local(..)
-            | Node::Visibility(..)
             | Node::Crate(..)
             | Node::Ty(..)
             | Node::TraitRef(..)
@@ -3318,18 +3375,18 @@ impl<'hir> Node<'hir> {
         match self {
             Node::Item(i) => match i.kind {
                 ItemKind::Fn(ref sig, ref generics, _) => {
-                    Some(FnKind::ItemFn(i.ident, generics, sig.header, &i.vis))
+                    Some(FnKind::ItemFn(i.ident, generics, sig.header))
                 }
                 _ => None,
             },
             Node::TraitItem(ti) => match ti.kind {
                 TraitItemKind::Fn(ref sig, TraitFn::Provided(_)) => {
-                    Some(FnKind::Method(ti.ident, sig, None))
+                    Some(FnKind::Method(ti.ident, sig))
                 }
                 _ => None,
             },
             Node::ImplItem(ii) => match ii.kind {
-                ImplItemKind::Fn(ref sig, _) => Some(FnKind::Method(ii.ident, sig, Some(&ii.vis))),
+                ImplItemKind::Fn(ref sig, _) => Some(FnKind::Method(ii.ident, sig)),
                 _ => None,
             },
             Node::Expr(e) => match e.kind {
@@ -3339,6 +3396,12 @@ impl<'hir> Node<'hir> {
             _ => None,
         }
     }
+
+    /// Get the fields for the tuple-constructor,
+    /// if this node is a tuple constructor, otherwise None
+    pub fn tuple_fields(&self) -> Option<&'hir [FieldDef<'hir>]> {
+        if let Node::Ctor(&VariantData::Tuple(fields, _)) = self { Some(fields) } else { None }
+    }
 }
 
 // Some nodes are used a lot. Make sure they don't unintentionally get bigger.
@@ -3349,9 +3412,12 @@ mod size_asserts {
     rustc_data_structures::static_assert_size!(super::Pat<'static>, 88);
     rustc_data_structures::static_assert_size!(super::QPath<'static>, 24);
     rustc_data_structures::static_assert_size!(super::Ty<'static>, 72);
-
-    rustc_data_structures::static_assert_size!(super::Item<'static>, 184);
-    rustc_data_structures::static_assert_size!(super::TraitItem<'static>, 128);
-    rustc_data_structures::static_assert_size!(super::ImplItem<'static>, 144);
-    rustc_data_structures::static_assert_size!(super::ForeignItem<'static>, 136);
+    rustc_data_structures::static_assert_size!(super::GenericBound<'_>, 48);
+    rustc_data_structures::static_assert_size!(super::Generics<'static>, 56);
+    rustc_data_structures::static_assert_size!(super::Impl<'static>, 80);
+
+    rustc_data_structures::static_assert_size!(super::Item<'static>, 80);
+    rustc_data_structures::static_assert_size!(super::TraitItem<'static>, 88);
+    rustc_data_structures::static_assert_size!(super::ImplItem<'static>, 80);
+    rustc_data_structures::static_assert_size!(super::ForeignItem<'static>, 72);
 }
diff --git a/compiler/rustc_hir/src/hir_id.rs b/compiler/rustc_hir/src/hir_id.rs
index e0ff28dff15..346ac9e9644 100644
--- a/compiler/rustc_hir/src/hir_id.rs
+++ b/compiler/rustc_hir/src/hir_id.rs
@@ -76,9 +76,10 @@ rustc_index::newtype_index! {
     /// integers starting at zero, so a mapping that maps all or most nodes within
     /// an "item-like" to something else can be implemented by a `Vec` instead of a
     /// tree or hash map.
+    #[derive(HashStable_Generic)]
     pub struct ItemLocalId { .. }
 }
-rustc_data_structures::impl_stable_hash_via_hash!(ItemLocalId);
+
 impl ItemLocalId {
     /// Signal local id which should never be used.
     pub const INVALID: ItemLocalId = ItemLocalId::MAX;
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index 8689e2c2afa..977c0eb3cd2 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -100,10 +100,10 @@ where
 #[derive(Copy, Clone, Debug)]
 pub enum FnKind<'a> {
     /// `#[xxx] pub async/const/extern "Abi" fn foo()`
-    ItemFn(Ident, &'a Generics<'a>, FnHeader, &'a Visibility<'a>),
+    ItemFn(Ident, &'a Generics<'a>, FnHeader),
 
     /// `fn foo(&self)`
-    Method(Ident, &'a FnSig<'a>, Option<&'a Visibility<'a>>),
+    Method(Ident, &'a FnSig<'a>),
 
     /// `|x, y| {}`
     Closure,
@@ -112,8 +112,8 @@ pub enum FnKind<'a> {
 impl<'a> FnKind<'a> {
     pub fn header(&self) -> Option<&FnHeader> {
         match *self {
-            FnKind::ItemFn(_, _, ref header, _) => Some(header),
-            FnKind::Method(_, ref sig, _) => Some(&sig.header),
+            FnKind::ItemFn(_, _, ref header) => Some(header),
+            FnKind::Method(_, ref sig) => Some(&sig.header),
             FnKind::Closure => None,
         }
     }
@@ -163,10 +163,15 @@ impl<'hir> Map<'hir> for ! {
 pub mod nested_filter {
     use super::Map;
 
-    /// Specifies what nested things a visitor wants to visit. The most
-    /// common choice is `OnlyBodies`, which will cause the visitor to
-    /// visit fn bodies for fns that it encounters, but skip over nested
-    /// item-like things.
+    /// Specifies what nested things a visitor wants to visit. By "nested
+    /// things", we are referring to bits of HIR that are not directly embedded
+    /// within one another but rather indirectly, through a table in the crate.
+    /// This is done to control dependencies during incremental compilation: the
+    /// non-inline bits of HIR can be tracked and hashed separately.
+    ///
+    /// The most common choice is `OnlyBodies`, which will cause the visitor to
+    /// visit fn bodies for fns that it encounters, and closure bodies, but
+    /// skip over nested item-like things.
     ///
     /// See the comments on `ItemLikeVisitor` for more details on the overall
     /// visit strategy.
@@ -217,27 +222,23 @@ use nested_filter::NestedFilter;
 pub trait Visitor<'v>: Sized {
     // this type should not be overridden, it exists for convenient usage as `Self::Map`
     type Map: Map<'v> = <Self::NestedFilter as NestedFilter<'v>>::Map;
-    type NestedFilter: NestedFilter<'v> = nested_filter::None;
 
     ///////////////////////////////////////////////////////////////////////////
     // Nested items.
 
-    /// The default versions of the `visit_nested_XXX` routines invoke
-    /// this method to get a map to use. By selecting an enum variant,
-    /// you control which kinds of nested HIR are visited; see
-    /// `NestedVisitorMap` for details. By "nested HIR", we are
-    /// referring to bits of HIR that are not directly embedded within
-    /// one another but rather indirectly, through a table in the
-    /// crate. This is done to control dependencies during incremental
-    /// compilation: the non-inline bits of HIR can be tracked and
-    /// hashed separately.
+    /// Override this type to control which nested HIR are visited; see
+    /// [`NestedFilter`] for details. If you override this type, you
+    /// must also override [`nested_visit_map`](Self::nested_visit_map).
     ///
     /// **If for some reason you want the nested behavior, but don't
-    /// have a `Map` at your disposal:** then you should override the
-    /// `visit_nested_XXX` methods, and override this method to
-    /// `panic!()`. This way, if a new `visit_nested_XXX` variant is
-    /// added in the future, we will see the panic in your code and
-    /// fix it appropriately.
+    /// have a `Map` at your disposal:** then override the
+    /// `visit_nested_XXX` methods. If a new `visit_nested_XXX` variant is
+    /// added in the future, it will cause a panic which can be detected
+    /// and fixed appropriately.
+    type NestedFilter: NestedFilter<'v> = nested_filter::None;
+
+    /// If `type NestedFilter` is set to visit nested items, this method
+    /// must also be overridden to provide a map to retrieve nested items.
     fn nested_visit_map(&mut self) -> Self::Map {
         panic!(
             "nested_visit_map must be implemented or consider using \
@@ -245,14 +246,14 @@ pub trait Visitor<'v>: Sized {
         );
     }
 
-    /// Invoked when a nested item is encountered. By default does
-    /// nothing unless you override `nested_visit_map` to return other than
-    /// `None`, in which case it will walk the item. **You probably
-    /// don't want to override this method** -- instead, override
-    /// `nested_visit_map` or use the "shallow" or "deep" visit
-    /// patterns described on `itemlikevisit::ItemLikeVisitor`. The only
-    /// reason to override this method is if you want a nested pattern
-    /// but cannot supply a `Map`; see `nested_visit_map` for advice.
+    /// Invoked when a nested item is encountered. By default, when
+    /// `Self::NestedFilter` is `nested_filter::None`, this method does
+    /// nothing. **You probably don't want to override this method** --
+    /// instead, override [`Self::NestedFilter`] or use the "shallow" or
+    /// "deep" visit patterns described on
+    /// `itemlikevisit::ItemLikeVisitor`. The only reason to override
+    /// this method is if you want a nested pattern but cannot supply a
+    /// [`Map`]; see `nested_visit_map` for advice.
     fn visit_nested_item(&mut self, id: ItemId) {
         if Self::NestedFilter::INTER {
             let item = self.nested_visit_map().item(id);
@@ -291,9 +292,8 @@ pub trait Visitor<'v>: Sized {
     }
 
     /// Invoked to visit the body of a function, method or closure. Like
-    /// visit_nested_item, does nothing by default unless you override
-    /// `nested_visit_map` to return other than `None`, in which case it will walk
-    /// the body.
+    /// `visit_nested_item`, does nothing by default unless you override
+    /// `Self::NestedFilter`.
     fn visit_nested_body(&mut self, id: BodyId) {
         if Self::NestedFilter::INTRA {
             let body = self.nested_visit_map().body(id);
@@ -475,9 +475,6 @@ pub trait Visitor<'v>: Sized {
         walk_assoc_type_binding(self, type_binding)
     }
     fn visit_attribute(&mut self, _id: HirId, _attr: &'v Attribute) {}
-    fn visit_vis(&mut self, vis: &'v Visibility<'v>) {
-        walk_vis(self, vis)
-    }
     fn visit_associated_item_kind(&mut self, kind: &'v AssocItemKind) {
         walk_associated_item_kind(self, kind);
     }
@@ -554,7 +551,6 @@ pub fn walk_param<'v, V: Visitor<'v>>(visitor: &mut V, param: &'v Param<'v>) {
 }
 
 pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) {
-    visitor.visit_vis(&item.vis);
     visitor.visit_ident(item.ident);
     match item.kind {
         ItemKind::ExternCrate(orig_name) => {
@@ -572,7 +568,7 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) {
             visitor.visit_nested_body(body);
         }
         ItemKind::Fn(ref sig, ref generics, body_id) => visitor.visit_fn(
-            FnKind::ItemFn(item.ident, generics, sig.header, &item.vis),
+            FnKind::ItemFn(item.ident, generics, sig.header),
             &sig.decl,
             body_id,
             item.span,
@@ -623,7 +619,7 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) {
             visitor.visit_generics(generics);
             walk_list!(visitor, visit_trait_ref, of_trait);
             visitor.visit_ty(self_ty);
-            walk_list!(visitor, visit_impl_item_ref, items);
+            walk_list!(visitor, visit_impl_item_ref, *items);
         }
         ItemKind::Struct(ref struct_definition, ref generics)
         | ItemKind::Union(ref struct_definition, ref generics) => {
@@ -859,7 +855,6 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) {
 
 pub fn walk_foreign_item<'v, V: Visitor<'v>>(visitor: &mut V, foreign_item: &'v ForeignItem<'v>) {
     visitor.visit_id(foreign_item.hir_id());
-    visitor.visit_vis(&foreign_item.vis);
     visitor.visit_ident(foreign_item.ident);
 
     match foreign_item.kind {
@@ -904,7 +899,6 @@ pub fn walk_generic_param<'v, V: Visitor<'v>>(visitor: &mut V, param: &'v Generi
             }
         }
     }
-    walk_list!(visitor, visit_param_bound, param.bounds);
 }
 
 pub fn walk_const_param_default<'v, V: Visitor<'v>>(visitor: &mut V, ct: &'v AnonConst) {
@@ -913,7 +907,7 @@ pub fn walk_const_param_default<'v, V: Visitor<'v>>(visitor: &mut V, ct: &'v Ano
 
 pub fn walk_generics<'v, V: Visitor<'v>>(visitor: &mut V, generics: &'v Generics<'v>) {
     walk_list!(visitor, visit_generic_param, generics.params);
-    walk_list!(visitor, visit_where_predicate, generics.where_clause.predicates);
+    walk_list!(visitor, visit_where_predicate, generics.predicates);
 }
 
 pub fn walk_where_predicate<'v, V: Visitor<'v>>(
@@ -999,7 +993,7 @@ pub fn walk_trait_item<'v, V: Visitor<'v>>(visitor: &mut V, trait_item: &'v Trai
         }
         TraitItemKind::Fn(ref sig, TraitFn::Provided(body_id)) => {
             visitor.visit_fn(
-                FnKind::Method(trait_item.ident, sig, None),
+                FnKind::Method(trait_item.ident, sig),
                 &sig.decl,
                 body_id,
                 trait_item.span,
@@ -1025,10 +1019,9 @@ pub fn walk_trait_item_ref<'v, V: Visitor<'v>>(visitor: &mut V, trait_item_ref:
 
 pub fn walk_impl_item<'v, V: Visitor<'v>>(visitor: &mut V, impl_item: &'v ImplItem<'v>) {
     // N.B., deliberately force a compilation error if/when new fields are added.
-    let ImplItem { def_id: _, ident, ref vis, ref generics, ref kind, span: _ } = *impl_item;
+    let ImplItem { def_id: _, ident, ref generics, ref kind, span: _, vis_span: _ } = *impl_item;
 
     visitor.visit_ident(ident);
-    visitor.visit_vis(vis);
     visitor.visit_generics(generics);
     match *kind {
         ImplItemKind::Const(ref ty, body) => {
@@ -1038,7 +1031,7 @@ pub fn walk_impl_item<'v, V: Visitor<'v>>(visitor: &mut V, impl_item: &'v ImplIt
         }
         ImplItemKind::Fn(ref sig, body_id) => {
             visitor.visit_fn(
-                FnKind::Method(impl_item.ident, sig, Some(&impl_item.vis)),
+                FnKind::Method(impl_item.ident, sig),
                 &sig.decl,
                 body_id,
                 impl_item.span,
@@ -1082,7 +1075,6 @@ pub fn walk_struct_def<'v, V: Visitor<'v>>(
 
 pub fn walk_field_def<'v, V: Visitor<'v>>(visitor: &mut V, field: &'v FieldDef<'v>) {
     visitor.visit_id(field.hir_id);
-    visitor.visit_vis(&field.vis);
     visitor.visit_ident(field.ident);
     visitor.visit_ty(&field.ty);
 }
@@ -1250,13 +1242,6 @@ pub fn walk_arm<'v, V: Visitor<'v>>(visitor: &mut V, arm: &'v Arm<'v>) {
     visitor.visit_expr(&arm.body);
 }
 
-pub fn walk_vis<'v, V: Visitor<'v>>(visitor: &mut V, vis: &'v Visibility<'v>) {
-    if let VisibilityKind::Restricted { ref path, hir_id } = vis.node {
-        visitor.visit_id(hir_id);
-        visitor.visit_path(path, hir_id)
-    }
-}
-
 pub fn walk_associated_item_kind<'v, V: Visitor<'v>>(_: &mut V, _: &'v AssocItemKind) {
     // No visitable content here: this fn exists so you can call it if
     // the right thing to do, should content be added in the future,
diff --git a/compiler/rustc_hir/src/itemlikevisit.rs b/compiler/rustc_hir/src/itemlikevisit.rs
index db70002c2d6..b2c6ca1354f 100644
--- a/compiler/rustc_hir/src/itemlikevisit.rs
+++ b/compiler/rustc_hir/src/itemlikevisit.rs
@@ -17,8 +17,8 @@ use super::{ForeignItem, ImplItem, Item, TraitItem};
 ///    an item, but don't care about how item-like things are nested
 ///    within one another.
 ///    - Example: Examine each expression to look for its type and do some check or other.
-///    - How: Implement `intravisit::Visitor` and override the `nested_visit_map()` method
-///      to return `NestedVisitorMap::OnlyBodies` and use
+///    - How: Implement `intravisit::Visitor` and override the `NestedFilter` type to
+///      `nested_filter::OnlyBodies` (and implement `nested_visit_map`), and use
 ///      `tcx.hir().visit_all_item_likes(&mut visitor.as_deep_visitor())`. Within your
 ///      `intravisit::Visitor` impl, implement methods like `visit_expr()` (don't forget to invoke
 ///      `intravisit::walk_expr()` to keep walking the subparts).
@@ -29,9 +29,9 @@ use super::{ForeignItem, ImplItem, Item, TraitItem};
 ///    item-like things.
 ///    - Example: Lifetime resolution, which wants to bring lifetimes declared on the
 ///      impl into scope while visiting the impl-items, and then back out again.
-///    - How: Implement `intravisit::Visitor` and override the `nested_visit_map()` method
-///      to return `NestedVisitorMap::All`. Walk your crate with `intravisit::walk_crate()`
-///      invoked on `tcx.hir().krate()`.
+///    - How: Implement `intravisit::Visitor` and override the `NestedFilter` type to
+///      `nested_filter::All` (and implement `nested_visit_map`). Walk your crate with
+///      `tcx.hir().walk_toplevel_module(visitor)` invoked on `tcx.hir().krate()`.
 ///    - Pro: Visitor methods for any kind of HIR node, not just item-like things.
 ///    - Pro: Preserves nesting information
 ///    - Con: Does not integrate well into dependency tracking.
diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs
index 9318ebb40b0..b3c22d4ec21 100644
--- a/compiler/rustc_hir/src/lang_items.rs
+++ b/compiler/rustc_hir/src/lang_items.rs
@@ -293,6 +293,7 @@ language_item_table! {
     TryTraitFromResidual,    sym::from_residual,       from_residual_fn,           Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
     TryTraitFromOutput,      sym::from_output,         from_output_fn,             Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
     TryTraitBranch,          sym::branch,              branch_fn,                  Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
+    TryTraitFromYeet,        sym::from_yeet,           from_yeet_fn,               Target::Fn,             GenericRequirement::None;
 
     PollReady,               sym::Ready,               poll_ready_variant,         Target::Variant,        GenericRequirement::None;
     PollPending,             sym::Pending,             poll_pending_variant,       Target::Variant,        GenericRequirement::None;
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index 27f07a479b1..7af9622b2cf 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -8,12 +8,11 @@ use rustc_ast_pretty::pprust::{Comments, PrintState};
 use rustc_hir as hir;
 use rustc_hir::{GenericArg, GenericParam, GenericParamKind, Node, Term};
 use rustc_hir::{GenericBound, PatKind, RangeEnd, TraitBoundModifier};
-use rustc_span::source_map::{SourceMap, Spanned};
+use rustc_span::source_map::SourceMap;
 use rustc_span::symbol::{kw, Ident, IdentPrinter, Symbol};
 use rustc_span::{self, FileName};
 use rustc_target::spec::abi::Abi;
 
-use std::borrow::Cow;
 use std::cell::Cell;
 use std::vec;
 
@@ -98,7 +97,6 @@ impl<'a> State<'a> {
                 self.print_block(&a)
             }
             Node::Lifetime(a) => self.print_lifetime(&a),
-            Node::Visibility(a) => self.print_visibility(&a),
             Node::GenericParam(_) => panic!("cannot print Node::GenericParam"),
             Node::Field(_) => panic!("cannot print Node::Field"),
             // These cases do not carry enough information in the
@@ -191,13 +189,6 @@ where
     printer.s.eof()
 }
 
-pub fn visibility_qualified<S: Into<Cow<'static, str>>>(vis: &hir::Visibility<'_>, w: S) -> String {
-    to_string(NO_ANN, |s| {
-        s.print_visibility(vis);
-        s.word(w)
-    })
-}
-
 pub fn generic_params_to_string(generic_params: &[GenericParam<'_>]) -> String {
     to_string(NO_ANN, |s| s.print_generic_params(generic_params))
 }
@@ -223,11 +214,10 @@ pub fn fn_to_string(
     header: hir::FnHeader,
     name: Option<Symbol>,
     generics: &hir::Generics<'_>,
-    vis: &hir::Visibility<'_>,
     arg_names: &[Ident],
     body_id: Option<hir::BodyId>,
 ) -> String {
-    to_string(NO_ANN, |s| s.print_fn(decl, header, name, generics, vis, arg_names, body_id))
+    to_string(NO_ANN, |s| s.print_fn(decl, header, name, generics, arg_names, body_id))
 }
 
 pub fn enum_def_to_string(
@@ -235,9 +225,8 @@ pub fn enum_def_to_string(
     generics: &hir::Generics<'_>,
     name: Symbol,
     span: rustc_span::Span,
-    visibility: &hir::Visibility<'_>,
 ) -> String {
-    to_string(NO_ANN, |s| s.print_enum_def(enum_definition, generics, name, span, visibility))
+    to_string(NO_ANN, |s| s.print_enum_def(enum_definition, generics, name, span))
 }
 
 impl<'a> State<'a> {
@@ -395,7 +384,6 @@ impl<'a> State<'a> {
                     },
                     Some(item.ident.name),
                     generics,
-                    &item.vis,
                     arg_names,
                     None,
                 );
@@ -404,7 +392,7 @@ impl<'a> State<'a> {
                 self.end() // end the outer fn box
             }
             hir::ForeignItemKind::Static(ref t, m) => {
-                self.head(visibility_qualified(&item.vis, "static"));
+                self.head("static");
                 if m == hir::Mutability::Mut {
                     self.word_space("mut");
                 }
@@ -416,7 +404,7 @@ impl<'a> State<'a> {
                 self.end() // end the outer cbox
             }
             hir::ForeignItemKind::Type => {
-                self.head(visibility_qualified(&item.vis, "type"));
+                self.head("type");
                 self.print_ident(item.ident);
                 self.word(";");
                 self.end(); // end the head-ibox
@@ -430,9 +418,8 @@ impl<'a> State<'a> {
         ident: Ident,
         ty: &hir::Ty<'_>,
         default: Option<hir::BodyId>,
-        vis: &hir::Visibility<'_>,
     ) {
-        self.word(visibility_qualified(vis, ""));
+        self.head("");
         self.word_space("const");
         self.print_ident(ident);
         self.word_space(":");
@@ -458,7 +445,7 @@ impl<'a> State<'a> {
         if let Some(bounds) = bounds {
             self.print_bounds(":", bounds);
         }
-        self.print_where_clause(&generics.where_clause);
+        self.print_where_clause(generics);
         if let Some(ty) = ty {
             self.space();
             self.word_space("=");
@@ -473,12 +460,12 @@ impl<'a> State<'a> {
         generics: &hir::Generics<'_>,
         inner: impl Fn(&mut Self),
     ) {
-        self.head(visibility_qualified(&item.vis, "type"));
+        self.head("type");
         self.print_ident(item.ident);
         self.print_generic_params(&generics.params);
         self.end(); // end the inner ibox
 
-        self.print_where_clause(&generics.where_clause);
+        self.print_where_clause(generics);
         self.space();
         inner(self);
         self.word(";");
@@ -494,7 +481,7 @@ impl<'a> State<'a> {
         self.ann.pre(self, AnnNode::Item(item));
         match item.kind {
             hir::ItemKind::ExternCrate(orig_name) => {
-                self.head(visibility_qualified(&item.vis, "extern crate"));
+                self.head("extern crate");
                 if let Some(orig_name) = orig_name {
                     self.print_name(orig_name);
                     self.space();
@@ -507,7 +494,7 @@ impl<'a> State<'a> {
                 self.end(); // end outer head-block
             }
             hir::ItemKind::Use(ref path, kind) => {
-                self.head(visibility_qualified(&item.vis, "use"));
+                self.head("use");
                 self.print_path(path, false);
 
                 match kind {
@@ -526,7 +513,7 @@ impl<'a> State<'a> {
                 self.end(); // end outer head-block
             }
             hir::ItemKind::Static(ref ty, m, expr) => {
-                self.head(visibility_qualified(&item.vis, "static"));
+                self.head("static");
                 if m == hir::Mutability::Mut {
                     self.word_space("mut");
                 }
@@ -542,7 +529,7 @@ impl<'a> State<'a> {
                 self.end(); // end the outer cbox
             }
             hir::ItemKind::Const(ref ty, expr) => {
-                self.head(visibility_qualified(&item.vis, "const"));
+                self.head("const");
                 self.print_ident(item.ident);
                 self.word_space(":");
                 self.print_type(&ty);
@@ -561,7 +548,6 @@ impl<'a> State<'a> {
                     sig.header,
                     Some(item.ident.name),
                     param_names,
-                    &item.vis,
                     &[],
                     Some(body),
                 );
@@ -571,12 +557,10 @@ impl<'a> State<'a> {
                 self.ann.nested(self, Nested::Body(body));
             }
             hir::ItemKind::Macro(ref macro_def, _) => {
-                self.print_mac_def(macro_def, &item.ident, item.span, |state| {
-                    state.print_visibility(&item.vis)
-                });
+                self.print_mac_def(macro_def, &item.ident, item.span, |_| {});
             }
             hir::ItemKind::Mod(ref _mod) => {
-                self.head(visibility_qualified(&item.vis, "mod"));
+                self.head("mod");
                 self.print_ident(item.ident);
                 self.nbsp();
                 self.bopen();
@@ -594,7 +578,7 @@ impl<'a> State<'a> {
                 self.bclose(item.span);
             }
             hir::ItemKind::GlobalAsm(ref asm) => {
-                self.head(visibility_qualified(&item.vis, "global_asm!"));
+                self.head("global_asm!");
                 self.print_inline_asm(asm);
                 self.end()
             }
@@ -620,14 +604,14 @@ impl<'a> State<'a> {
                 });
             }
             hir::ItemKind::Enum(ref enum_definition, ref params) => {
-                self.print_enum_def(enum_definition, params, item.ident.name, item.span, &item.vis);
+                self.print_enum_def(enum_definition, params, item.ident.name, item.span);
             }
             hir::ItemKind::Struct(ref struct_def, ref generics) => {
-                self.head(visibility_qualified(&item.vis, "struct"));
+                self.head("struct");
                 self.print_struct(struct_def, generics, item.ident.name, item.span, true);
             }
             hir::ItemKind::Union(ref struct_def, ref generics) => {
-                self.head(visibility_qualified(&item.vis, "union"));
+                self.head("union");
                 self.print_struct(struct_def, generics, item.ident.name, item.span, true);
             }
             hir::ItemKind::Impl(hir::Impl {
@@ -642,9 +626,8 @@ impl<'a> State<'a> {
                 items,
             }) => {
                 self.head("");
-                self.print_visibility(&item.vis);
-                self.print_defaultness(defaultness);
-                self.print_unsafety(unsafety);
+                self.print_defaultness(*defaultness);
+                self.print_unsafety(*unsafety);
                 self.word_nbsp("impl");
 
                 if !generics.params.is_empty() {
@@ -652,7 +635,7 @@ impl<'a> State<'a> {
                     self.space();
                 }
 
-                if constness == hir::Constness::Const {
+                if *constness == hir::Constness::Const {
                     self.word_nbsp("const");
                 }
 
@@ -667,19 +650,18 @@ impl<'a> State<'a> {
                 }
 
                 self.print_type(&self_ty);
-                self.print_where_clause(&generics.where_clause);
+                self.print_where_clause(generics);
 
                 self.space();
                 self.bopen();
                 self.print_inner_attributes(attrs);
-                for impl_item in items {
+                for impl_item in *items {
                     self.ann.nested(self, Nested::ImplItem(impl_item.id));
                 }
                 self.bclose(item.span);
             }
             hir::ItemKind::Trait(is_auto, unsafety, ref generics, ref bounds, trait_items) => {
                 self.head("");
-                self.print_visibility(&item.vis);
                 self.print_is_auto(is_auto);
                 self.print_unsafety(unsafety);
                 self.word_nbsp("trait");
@@ -696,7 +678,7 @@ impl<'a> State<'a> {
                     }
                 }
                 self.print_bounds(":", real_bounds);
-                self.print_where_clause(&generics.where_clause);
+                self.print_where_clause(generics);
                 self.word(" ");
                 self.bopen();
                 for trait_item in trait_items {
@@ -705,7 +687,7 @@ impl<'a> State<'a> {
                 self.bclose(item.span);
             }
             hir::ItemKind::TraitAlias(ref generics, ref bounds) => {
-                self.head(visibility_qualified(&item.vis, "trait"));
+                self.head("trait");
                 self.print_ident(item.ident);
                 self.print_generic_params(&generics.params);
                 let mut real_bounds = Vec::with_capacity(bounds.len());
@@ -721,7 +703,7 @@ impl<'a> State<'a> {
                 }
                 self.nbsp();
                 self.print_bounds("=", real_bounds);
-                self.print_where_clause(&generics.where_clause);
+                self.print_where_clause(generics);
                 self.word(";");
                 self.end(); // end inner head-block
                 self.end(); // end outer head-block
@@ -753,12 +735,11 @@ impl<'a> State<'a> {
         generics: &hir::Generics<'_>,
         name: Symbol,
         span: rustc_span::Span,
-        visibility: &hir::Visibility<'_>,
     ) {
-        self.head(visibility_qualified(visibility, "enum"));
+        self.head("enum");
         self.print_name(name);
         self.print_generic_params(&generics.params);
-        self.print_where_clause(&generics.where_clause);
+        self.print_where_clause(generics);
         self.space();
         self.print_variants(&enum_definition.variants, span)
     }
@@ -778,27 +759,6 @@ impl<'a> State<'a> {
         self.bclose(span)
     }
 
-    pub fn print_visibility(&mut self, vis: &hir::Visibility<'_>) {
-        match vis.node {
-            hir::VisibilityKind::Public => self.word_nbsp("pub"),
-            hir::VisibilityKind::Crate(ast::CrateSugar::JustCrate) => self.word_nbsp("crate"),
-            hir::VisibilityKind::Crate(ast::CrateSugar::PubCrate) => self.word_nbsp("pub(crate)"),
-            hir::VisibilityKind::Restricted { ref path, .. } => {
-                self.word("pub(");
-                if path.segments.len() == 1 && path.segments[0].ident.name == kw::Super {
-                    // Special case: `super` can print like `pub(super)`.
-                    self.word("super");
-                } else {
-                    // Everything else requires `in` at present.
-                    self.word_nbsp("in");
-                    self.print_path(path, false);
-                }
-                self.word_nbsp(")");
-            }
-            hir::VisibilityKind::Inherited => (),
-        }
-    }
-
     pub fn print_defaultness(&mut self, defaultness: hir::Defaultness) {
         match defaultness {
             hir::Defaultness::Default { .. } => self.word_nbsp("default"),
@@ -823,12 +783,11 @@ impl<'a> State<'a> {
                     self.commasep(Inconsistent, struct_def.fields(), |s, field| {
                         s.maybe_print_comment(field.span.lo());
                         s.print_outer_attributes(s.attrs(field.hir_id));
-                        s.print_visibility(&field.vis);
                         s.print_type(&field.ty)
                     });
                     self.pclose();
                 }
-                self.print_where_clause(&generics.where_clause);
+                self.print_where_clause(generics);
                 if print_finalizer {
                     self.word(";");
                 }
@@ -836,7 +795,7 @@ impl<'a> State<'a> {
                 self.end() // close the outer-box
             }
             hir::VariantData::Struct(..) => {
-                self.print_where_clause(&generics.where_clause);
+                self.print_where_clause(generics);
                 self.nbsp();
                 self.bopen();
                 self.hardbreak_if_not_bol();
@@ -845,7 +804,6 @@ impl<'a> State<'a> {
                     self.hardbreak_if_not_bol();
                     self.maybe_print_comment(field.span.lo());
                     self.print_outer_attributes(self.attrs(field.hir_id));
-                    self.print_visibility(&field.vis);
                     self.print_ident(field.ident);
                     self.word_nbsp(":");
                     self.print_type(&field.ty);
@@ -872,11 +830,10 @@ impl<'a> State<'a> {
         ident: Ident,
         m: &hir::FnSig<'_>,
         generics: &hir::Generics<'_>,
-        vis: &hir::Visibility<'_>,
         arg_names: &[Ident],
         body_id: Option<hir::BodyId>,
     ) {
-        self.print_fn(&m.decl, m.header, Some(ident.name), generics, vis, arg_names, body_id)
+        self.print_fn(&m.decl, m.header, Some(ident.name), generics, arg_names, body_id)
     }
 
     pub fn print_trait_item(&mut self, ti: &hir::TraitItem<'_>) {
@@ -886,21 +843,15 @@ impl<'a> State<'a> {
         self.print_outer_attributes(self.attrs(ti.hir_id()));
         match ti.kind {
             hir::TraitItemKind::Const(ref ty, default) => {
-                let vis =
-                    Spanned { span: rustc_span::DUMMY_SP, node: hir::VisibilityKind::Inherited };
-                self.print_associated_const(ti.ident, &ty, default, &vis);
+                self.print_associated_const(ti.ident, &ty, default);
             }
             hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(ref arg_names)) => {
-                let vis =
-                    Spanned { span: rustc_span::DUMMY_SP, node: hir::VisibilityKind::Inherited };
-                self.print_method_sig(ti.ident, sig, &ti.generics, &vis, arg_names, None);
+                self.print_method_sig(ti.ident, sig, &ti.generics, arg_names, None);
                 self.word(";");
             }
             hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => {
-                let vis =
-                    Spanned { span: rustc_span::DUMMY_SP, node: hir::VisibilityKind::Inherited };
                 self.head("");
-                self.print_method_sig(ti.ident, sig, &ti.generics, &vis, &[], Some(body));
+                self.print_method_sig(ti.ident, sig, &ti.generics, &[], Some(body));
                 self.nbsp();
                 self.end(); // need to close a box
                 self.end(); // need to close a box
@@ -926,11 +877,11 @@ impl<'a> State<'a> {
 
         match ii.kind {
             hir::ImplItemKind::Const(ref ty, expr) => {
-                self.print_associated_const(ii.ident, &ty, Some(expr), &ii.vis);
+                self.print_associated_const(ii.ident, &ty, Some(expr));
             }
             hir::ImplItemKind::Fn(ref sig, body) => {
                 self.head("");
-                self.print_method_sig(ii.ident, sig, &ii.generics, &ii.vis, &[], Some(body));
+                self.print_method_sig(ii.ident, sig, &ii.generics, &[], Some(body));
                 self.nbsp();
                 self.end(); // need to close a box
                 self.end(); // need to close a box
@@ -2008,11 +1959,10 @@ impl<'a> State<'a> {
         header: hir::FnHeader,
         name: Option<Symbol>,
         generics: &hir::Generics<'_>,
-        vis: &hir::Visibility<'_>,
         arg_names: &[Ident],
         body_id: Option<hir::BodyId>,
     ) {
-        self.print_fn_header_info(header, vis);
+        self.print_fn_header_info(header);
 
         if let Some(name) = name {
             self.nbsp();
@@ -2045,7 +1995,7 @@ impl<'a> State<'a> {
         self.pclose();
 
         self.print_fn_output(decl);
-        self.print_where_clause(&generics.where_clause)
+        self.print_where_clause(generics)
     }
 
     fn print_closure_params(&mut self, decl: &hir::FnDecl<'_>, body_id: hir::BodyId) {
@@ -2146,21 +2096,8 @@ impl<'a> State<'a> {
         self.print_ident(param.name.ident());
 
         match param.kind {
-            GenericParamKind::Lifetime { .. } => {
-                let mut sep = ":";
-                for bound in param.bounds {
-                    match bound {
-                        GenericBound::Outlives(ref lt) => {
-                            self.word(sep);
-                            self.print_lifetime(lt);
-                            sep = "+";
-                        }
-                        _ => panic!(),
-                    }
-                }
-            }
+            GenericParamKind::Lifetime { .. } => {}
             GenericParamKind::Type { ref default, .. } => {
-                self.print_bounds(":", param.bounds);
                 if let Some(default) = default {
                     self.space();
                     self.word_space("=");
@@ -2183,15 +2120,15 @@ impl<'a> State<'a> {
         self.print_ident(lifetime.name.ident())
     }
 
-    pub fn print_where_clause(&mut self, where_clause: &hir::WhereClause<'_>) {
-        if where_clause.predicates.is_empty() {
+    pub fn print_where_clause(&mut self, generics: &hir::Generics<'_>) {
+        if generics.predicates.is_empty() {
             return;
         }
 
         self.space();
         self.word_space("where");
 
-        for (i, predicate) in where_clause.predicates.iter().enumerate() {
+        for (i, predicate) in generics.predicates.iter().enumerate() {
             if i != 0 {
                 self.word_space(",");
             }
@@ -2286,11 +2223,7 @@ impl<'a> State<'a> {
     ) {
         self.ibox(INDENT_UNIT);
         self.print_formal_generic_params(generic_params);
-        let generics = hir::Generics {
-            params: &[],
-            where_clause: hir::WhereClause { predicates: &[], span: rustc_span::DUMMY_SP },
-            span: rustc_span::DUMMY_SP,
-        };
+        let generics = hir::Generics::empty();
         self.print_fn(
             decl,
             hir::FnHeader {
@@ -2301,16 +2234,13 @@ impl<'a> State<'a> {
             },
             name,
             &generics,
-            &Spanned { span: rustc_span::DUMMY_SP, node: hir::VisibilityKind::Inherited },
             arg_names,
             None,
         );
         self.end();
     }
 
-    pub fn print_fn_header_info(&mut self, header: hir::FnHeader, vis: &hir::Visibility<'_>) {
-        self.word(visibility_qualified(vis, ""));
-
+    pub fn print_fn_header_info(&mut self, header: hir::FnHeader) {
         match header.constness {
             hir::Constness::NotConst => {}
             hir::Constness::Const => self.word_nbsp("const"),
diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs
index 60b48e9bc8a..2d06c9d8ec9 100644
--- a/compiler/rustc_incremental/src/assert_dep_graph.rs
+++ b/compiler/rustc_incremental/src/assert_dep_graph.rs
@@ -241,7 +241,7 @@ fn dump_graph(query: &DepGraphQuery) {
             let targets = node_set(&query, &edge_filter.target);
             filter_nodes(&query, &sources, &targets)
         }
-        Err(_) => query.nodes().into_iter().collect(),
+        Err(_) => query.nodes().into_iter().map(|n| n.kind).collect(),
     };
     let edges = filter_edges(&query, &nodes);
 
@@ -264,33 +264,33 @@ fn dump_graph(query: &DepGraphQuery) {
 }
 
 #[allow(missing_docs)]
-pub struct GraphvizDepGraph<'q>(FxHashSet<&'q DepNode>, Vec<(&'q DepNode, &'q DepNode)>);
+pub struct GraphvizDepGraph(FxHashSet<DepKind>, Vec<(DepKind, DepKind)>);
 
-impl<'a, 'q> dot::GraphWalk<'a> for GraphvizDepGraph<'q> {
-    type Node = &'q DepNode;
-    type Edge = (&'q DepNode, &'q DepNode);
-    fn nodes(&self) -> dot::Nodes<'_, &'q DepNode> {
+impl<'a> dot::GraphWalk<'a> for GraphvizDepGraph {
+    type Node = DepKind;
+    type Edge = (DepKind, DepKind);
+    fn nodes(&self) -> dot::Nodes<'_, DepKind> {
         let nodes: Vec<_> = self.0.iter().cloned().collect();
         nodes.into()
     }
-    fn edges(&self) -> dot::Edges<'_, (&'q DepNode, &'q DepNode)> {
+    fn edges(&self) -> dot::Edges<'_, (DepKind, DepKind)> {
         self.1[..].into()
     }
-    fn source(&self, edge: &(&'q DepNode, &'q DepNode)) -> &'q DepNode {
+    fn source(&self, edge: &(DepKind, DepKind)) -> DepKind {
         edge.0
     }
-    fn target(&self, edge: &(&'q DepNode, &'q DepNode)) -> &'q DepNode {
+    fn target(&self, edge: &(DepKind, DepKind)) -> DepKind {
         edge.1
     }
 }
 
-impl<'a, 'q> dot::Labeller<'a> for GraphvizDepGraph<'q> {
-    type Node = &'q DepNode;
-    type Edge = (&'q DepNode, &'q DepNode);
+impl<'a> dot::Labeller<'a> for GraphvizDepGraph {
+    type Node = DepKind;
+    type Edge = (DepKind, DepKind);
     fn graph_id(&self) -> dot::Id<'_> {
         dot::Id::new("DependencyGraph").unwrap()
     }
-    fn node_id(&self, n: &&'q DepNode) -> dot::Id<'_> {
+    fn node_id(&self, n: &DepKind) -> dot::Id<'_> {
         let s: String = format!("{:?}", n)
             .chars()
             .map(|c| if c == '_' || c.is_alphanumeric() { c } else { '_' })
@@ -298,7 +298,7 @@ impl<'a, 'q> dot::Labeller<'a> for GraphvizDepGraph<'q> {
         debug!("n={:?} s={:?}", n, s);
         dot::Id::new(s).unwrap()
     }
-    fn node_label(&self, n: &&'q DepNode) -> dot::LabelText<'_> {
+    fn node_label(&self, n: &DepKind) -> dot::LabelText<'_> {
         dot::LabelText::label(format!("{:?}", n))
     }
 }
@@ -323,7 +323,7 @@ fn filter_nodes<'q>(
     query: &'q DepGraphQuery,
     sources: &Option<FxHashSet<&'q DepNode>>,
     targets: &Option<FxHashSet<&'q DepNode>>,
-) -> FxHashSet<&'q DepNode> {
+) -> FxHashSet<DepKind> {
     if let Some(sources) = sources {
         if let Some(targets) = targets {
             walk_between(query, sources, targets)
@@ -333,7 +333,7 @@ fn filter_nodes<'q>(
     } else if let Some(targets) = targets {
         walk_nodes(query, targets, INCOMING)
     } else {
-        query.nodes().into_iter().collect()
+        query.nodes().into_iter().map(|n| n.kind).collect()
     }
 }
 
@@ -341,17 +341,17 @@ fn walk_nodes<'q>(
     query: &'q DepGraphQuery,
     starts: &FxHashSet<&'q DepNode>,
     direction: Direction,
-) -> FxHashSet<&'q DepNode> {
+) -> FxHashSet<DepKind> {
     let mut set = FxHashSet::default();
     for &start in starts {
         debug!("walk_nodes: start={:?} outgoing?={:?}", start, direction == OUTGOING);
-        if set.insert(start) {
+        if set.insert(start.kind) {
             let mut stack = vec![query.indices[start]];
             while let Some(index) = stack.pop() {
                 for (_, edge) in query.graph.adjacent_edges(index, direction) {
                     let neighbor_index = edge.source_or_target(direction);
                     let neighbor = query.graph.node_data(neighbor_index);
-                    if set.insert(neighbor) {
+                    if set.insert(neighbor.kind) {
                         stack.push(neighbor_index);
                     }
                 }
@@ -365,7 +365,7 @@ fn walk_between<'q>(
     query: &'q DepGraphQuery,
     sources: &FxHashSet<&'q DepNode>,
     targets: &FxHashSet<&'q DepNode>,
-) -> FxHashSet<&'q DepNode> {
+) -> FxHashSet<DepKind> {
     // This is a bit tricky. We want to include a node only if it is:
     // (a) reachable from a source and (b) will reach a target. And we
     // have to be careful about cycles etc.  Luckily efficiency is not
@@ -396,6 +396,7 @@ fn walk_between<'q>(
             let index = query.indices[n];
             node_states[index.0] == State::Included
         })
+        .map(|n| n.kind)
         .collect();
 
     fn recurse(query: &DepGraphQuery, node_states: &mut [State], node: NodeIndex) -> bool {
@@ -433,11 +434,13 @@ fn walk_between<'q>(
 
 fn filter_edges<'q>(
     query: &'q DepGraphQuery,
-    nodes: &FxHashSet<&'q DepNode>,
-) -> Vec<(&'q DepNode, &'q DepNode)> {
-    query
+    nodes: &FxHashSet<DepKind>,
+) -> Vec<(DepKind, DepKind)> {
+    let uniq: FxHashSet<_> = query
         .edges()
         .into_iter()
-        .filter(|&(source, target)| nodes.contains(source) && nodes.contains(target))
-        .collect()
+        .map(|(s, t)| (s.kind, t.kind))
+        .filter(|(source, target)| nodes.contains(source) && nodes.contains(target))
+        .collect();
+    uniq.into_iter().collect()
 }
diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs
index cdc05095e68..33a8d6c11ff 100644
--- a/compiler/rustc_index/src/bit_set.rs
+++ b/compiler/rustc_index/src/bit_set.rs
@@ -1714,7 +1714,7 @@ impl<R: Idx, C: Idx> SparseBitMatrix<R, C> {
     }
 
     pub fn row(&self, row: R) -> Option<&HybridBitSet<C>> {
-        if let Some(Some(row)) = self.rows.get(row) { Some(row) } else { None }
+        self.rows.get(row)?.as_ref()
     }
 
     /// Intersects `row` with `set`. `set` can be either `BitSet` or
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index f2dd4f5d5cb..2e50dbff510 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -61,7 +61,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_errors::{pluralize, struct_span_err, Diagnostic, ErrorGuaranteed};
 use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString, MultiSpan};
 use rustc_hir as hir;
-use rustc_hir::def_id::DefId;
+use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::lang_items::LangItem;
 use rustc_hir::{Item, ItemKind, Node};
 use rustc_middle::dep_graph::DepContext;
@@ -2198,7 +2198,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
             err.span_suggestion(
                 span.with_hi(before_close).shrink_to_hi(),
                 msg,
-                ",".into(),
+                ",",
                 Applicability::MachineApplicable,
             );
         } else {
@@ -2285,7 +2285,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         bound_kind: GenericKind<'tcx>,
         sub: Region<'tcx>,
     ) {
-        self.construct_generic_bound_failure(span, origin, bound_kind, sub).emit();
+        let owner =
+            self.in_progress_typeck_results.map(|typeck_results| typeck_results.borrow().hir_owner);
+        self.construct_generic_bound_failure(span, origin, bound_kind, sub, owner).emit();
     }
 
     pub fn construct_generic_bound_failure(
@@ -2294,31 +2296,29 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         origin: Option<SubregionOrigin<'tcx>>,
         bound_kind: GenericKind<'tcx>,
         sub: Region<'tcx>,
+        owner: Option<LocalDefId>,
     ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
         let hir = self.tcx.hir();
         // Attempt to obtain the span of the parameter so we can
         // suggest adding an explicit lifetime bound to it.
-        let generics = self
-            .in_progress_typeck_results
-            .map(|typeck_results| typeck_results.borrow().hir_owner)
-            .map(|owner| {
-                let hir_id = hir.local_def_id_to_hir_id(owner);
-                let parent_id = hir.get_parent_item(hir_id);
-                (
-                    // Parent item could be a `mod`, so we check the HIR before calling:
-                    if let Some(Node::Item(Item {
-                        kind: ItemKind::Trait(..) | ItemKind::Impl { .. },
-                        ..
-                    })) = hir.find_by_def_id(parent_id)
-                    {
-                        Some(self.tcx.generics_of(parent_id))
-                    } else {
-                        None
-                    },
-                    self.tcx.generics_of(owner.to_def_id()),
-                    hir.span(hir_id),
-                )
-            });
+        let generics = owner.map(|owner| {
+            let hir_id = hir.local_def_id_to_hir_id(owner);
+            let parent_id = hir.get_parent_item(hir_id);
+            (
+                // Parent item could be a `mod`, so we check the HIR before calling:
+                if let Some(Node::Item(Item {
+                    kind: ItemKind::Trait(..) | ItemKind::Impl { .. },
+                    ..
+                })) = hir.find_by_def_id(parent_id)
+                {
+                    Some(self.tcx.generics_of(parent_id))
+                } else {
+                    None
+                },
+                self.tcx.generics_of(owner.to_def_id()),
+                hir.span(hir_id),
+            )
+        });
 
         let span = match generics {
             // This is to get around the trait identity obligation, that has a `DUMMY_SP` as signal
@@ -2327,6 +2327,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
             _ => span,
         };
 
+        // type_param_span is (span, has_bounds)
         let type_param_span = match (generics, bound_kind) {
             (Some((_, ref generics, _)), GenericKind::Param(ref param)) => {
                 // Account for the case where `param` corresponds to `Self`,
@@ -2337,25 +2338,18 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                         // Get the `hir::Param` to verify whether it already has any bounds.
                         // We do this to avoid suggesting code that ends up as `T: 'a'b`,
                         // instead we suggest `T: 'a + 'b` in that case.
-                        let id = hir.local_def_id_to_hir_id(def_id);
-                        let mut has_bounds = false;
-                        if let Node::GenericParam(param) = hir.get(id) {
-                            has_bounds = !param.bounds.is_empty();
-                        }
-                        let sp = self.tcx.def_span(def_id);
+                        let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
+                        let ast_generics = self.tcx.hir().get_generics(hir_id.owner);
+                        let bounds =
+                            ast_generics.and_then(|g| g.bounds_span_for_suggestions(def_id));
                         // `sp` only covers `T`, change it so that it covers
                         // `T:` when appropriate
-                        let is_impl_trait = bound_kind.to_string().starts_with("impl ");
-                        let sp = if has_bounds && !is_impl_trait {
-                            sp.to(self
-                                .tcx
-                                .sess
-                                .source_map()
-                                .next_point(self.tcx.sess.source_map().next_point(sp)))
+                        if let Some(span) = bounds {
+                            (span, true)
                         } else {
-                            sp
-                        };
-                        (sp, has_bounds, is_impl_trait)
+                            let sp = self.tcx.def_span(def_id);
+                            (sp.shrink_to_hi(), false)
+                        }
                     })
                 } else {
                     None
@@ -2411,52 +2405,37 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
 
         fn binding_suggestion<'tcx, S: fmt::Display>(
             err: &mut Diagnostic,
-            type_param_span: Option<(Span, bool, bool)>,
+            type_param_span: Option<(Span, bool)>,
             bound_kind: GenericKind<'tcx>,
             sub: S,
         ) {
             let msg = "consider adding an explicit lifetime bound";
-            if let Some((sp, has_lifetimes, is_impl_trait)) = type_param_span {
-                let suggestion = if is_impl_trait {
-                    format!("{} + {}", bound_kind, sub)
-                } else {
-                    let tail = if has_lifetimes { " + " } else { "" };
-                    format!("{}: {}{}", bound_kind, sub, tail)
-                };
-                err.span_suggestion(
+            if let Some((sp, has_lifetimes)) = type_param_span {
+                let suggestion =
+                    if has_lifetimes { format!(" + {}", sub) } else { format!(": {}", sub) };
+                err.span_suggestion_verbose(
                     sp,
                     &format!("{}...", msg),
                     suggestion,
                     Applicability::MaybeIncorrect, // Issue #41966
                 );
             } else {
-                let consider = format!(
-                    "{} {}...",
-                    msg,
-                    if type_param_span.map_or(false, |(_, _, is_impl_trait)| is_impl_trait) {
-                        format!(" `{}` to `{}`", sub, bound_kind)
-                    } else {
-                        format!("`{}: {}`", bound_kind, sub)
-                    },
-                );
+                let consider = format!("{} `{}: {}`...", msg, bound_kind, sub,);
                 err.help(&consider);
             }
         }
 
         let new_binding_suggestion =
-            |err: &mut Diagnostic,
-             type_param_span: Option<(Span, bool, bool)>,
-             bound_kind: GenericKind<'tcx>| {
+            |err: &mut Diagnostic, type_param_span: Option<(Span, bool)>| {
                 let msg = "consider introducing an explicit lifetime bound";
-                if let Some((sp, has_lifetimes, is_impl_trait)) = type_param_span {
-                    let suggestion = if is_impl_trait {
-                        (sp.shrink_to_hi(), format!(" + {}", new_lt))
+                if let Some((sp, has_lifetimes)) = type_param_span {
+                    let suggestion = if has_lifetimes {
+                        format!(" + {}", new_lt)
                     } else {
-                        let tail = if has_lifetimes { " +" } else { "" };
-                        (sp, format!("{}: {}{}", bound_kind, new_lt, tail))
+                        format!(": {}", new_lt)
                     };
                     let mut sugg =
-                        vec![suggestion, (span.shrink_to_hi(), format!(" + {}", new_lt))];
+                        vec![(sp, suggestion), (span.shrink_to_hi(), format!(" + {}", new_lt))];
                     if let Some(lt) = add_lt_sugg {
                         sugg.push(lt);
                         sugg.rotate_right(1);
@@ -2543,11 +2522,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 let pred = format!("{}: {}", bound_kind, sub);
                 let suggestion = format!(
                     "{} {}",
-                    if !generics.where_clause.predicates.is_empty() { "," } else { " where" },
+                    if !generics.predicates.is_empty() { "," } else { " where" },
                     pred,
                 );
                 err.span_suggestion(
-                    generics.where_clause.tail_span_for_suggestion(),
+                    generics.tail_span_for_predicate_suggestion(),
                     "consider adding a where clause",
                     suggestion,
                     Applicability::MaybeIncorrect,
@@ -2606,11 +2585,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                     None,
                 );
                 if let Some(infer::RelateParamBound(_, t, _)) = origin {
-                    let return_impl_trait = self
-                        .in_progress_typeck_results
-                        .map(|typeck_results| typeck_results.borrow().hir_owner)
-                        .and_then(|owner| self.tcx.return_type_impl_trait(owner))
-                        .is_some();
+                    let return_impl_trait =
+                        owner.and_then(|owner| self.tcx.return_type_impl_trait(owner)).is_some();
                     let t = self.resolve_vars_if_possible(t);
                     match t.kind() {
                         // We've got:
@@ -2618,7 +2594,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                         // suggest:
                         // fn get_later<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'a
                         ty::Closure(_, _substs) | ty::Opaque(_, _substs) if return_impl_trait => {
-                            new_binding_suggestion(&mut err, type_param_span, bound_kind);
+                            new_binding_suggestion(&mut err, type_param_span);
                         }
                         _ => {
                             binding_suggestion(&mut err, type_param_span, bound_kind, new_lt);
diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
index a9a92fdbd64..465358de932 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
@@ -734,22 +734,28 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 if !impl_candidates.is_empty() && e.span.contains(span)
                     && let Some(expr) = exprs.first()
                     && let ExprKind::Path(hir::QPath::Resolved(_, path)) = expr.kind
-                    && let [path_segment] = path.segments
+                    && let [_] = path.segments
                 {
+                    let mut eraser = TypeParamEraser(self.tcx);
                     let candidate_len = impl_candidates.len();
-                    let suggestions = impl_candidates.iter().map(|candidate| {
-                        format!(
-                            "{}::{}({})",
-                            candidate, segment.ident, path_segment.ident
-                        )
-                    });
-                    err.span_suggestions(
-                        e.span,
+                    let mut suggestions: Vec<_> = impl_candidates.iter().map(|candidate| {
+                        let candidate = candidate.super_fold_with(&mut eraser);
+                        vec![
+                            (expr.span.shrink_to_lo(), format!("{}::{}(", candidate, segment.ident)),
+                            if exprs.len() == 1 {
+                                (expr.span.shrink_to_hi().with_hi(e.span.hi()), ")".to_string())
+                            } else {
+                                (expr.span.shrink_to_hi().with_hi(exprs[1].span.lo()), ", ".to_string())
+                            },
+                        ]
+                    }).collect();
+                    suggestions.sort_by(|a, b| a[0].1.cmp(&b[0].1));
+                    err.multipart_suggestions(
                         &format!(
                             "use the fully qualified path for the potential candidate{}",
                             pluralize!(candidate_len),
                         ),
-                        suggestions,
+                        suggestions.into_iter(),
                         Applicability::MaybeIncorrect,
                     );
                 }
@@ -1037,3 +1043,18 @@ impl<'tcx> TypeFolder<'tcx> for ErrTypeParamEraser<'tcx> {
         }
     }
 }
+
+/// Replace type parameters with `ty::Infer(ty::Var)` to display `_`.
+struct TypeParamEraser<'tcx>(TyCtxt<'tcx>);
+
+impl<'tcx> TypeFolder<'tcx> for TypeParamEraser<'tcx> {
+    fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
+        self.0
+    }
+    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
+        match t.kind() {
+            ty::Param(_) | ty::Error(_) => self.tcx().mk_ty_var(ty::TyVid::from_u32(0)),
+            _ => t.super_fold_with(self),
+        }
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs
index 7721e00c141..be9db6aa25b 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs
@@ -6,6 +6,7 @@ use crate::infer::error_reporting::nice_region_error::util::AnonymousParamInfo;
 use crate::infer::error_reporting::nice_region_error::NiceRegionError;
 use crate::infer::lexical_region_resolve::RegionResolutionError;
 use crate::infer::SubregionOrigin;
+use crate::infer::TyCtxt;
 
 use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed};
 use rustc_hir as hir;
@@ -145,84 +146,83 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
             }
         }
 
-        self.suggest_adding_lifetime_params(sub, ty_sup, ty_sub, &mut err);
+        if suggest_adding_lifetime_params(self.tcx(), sub, ty_sup, ty_sub, &mut err) {
+            err.note("each elided lifetime in input position becomes a distinct lifetime");
+        }
 
         let reported = err.emit();
         Some(reported)
     }
+}
 
-    fn suggest_adding_lifetime_params(
-        &self,
-        sub: Region<'tcx>,
-        ty_sup: &Ty<'_>,
-        ty_sub: &Ty<'_>,
-        err: &mut Diagnostic,
-    ) {
-        if let (
-            hir::Ty { kind: hir::TyKind::Rptr(lifetime_sub, _), .. },
-            hir::Ty { kind: hir::TyKind::Rptr(lifetime_sup, _), .. },
-        ) = (ty_sub, ty_sup)
-        {
-            if lifetime_sub.name.is_elided() && lifetime_sup.name.is_elided() {
-                if let Some(anon_reg) = self.tcx().is_suitable_region(sub) {
-                    let hir_id = self.tcx().hir().local_def_id_to_hir_id(anon_reg.def_id);
-
-                    let node = self.tcx().hir().get(hir_id);
-                    let is_impl = matches!(&node, hir::Node::ImplItem(_));
-                    let generics = match node {
-                        hir::Node::Item(&hir::Item {
-                            kind: hir::ItemKind::Fn(_, ref generics, ..),
-                            ..
-                        })
-                        | hir::Node::TraitItem(&hir::TraitItem { ref generics, .. })
-                        | hir::Node::ImplItem(&hir::ImplItem { ref generics, .. }) => generics,
-                        _ => return,
-                    };
-
-                    let (suggestion_param_name, introduce_new) = generics
-                        .params
-                        .iter()
-                        .find(|p| matches!(p.kind, GenericParamKind::Lifetime { .. }))
-                        .and_then(|p| self.tcx().sess.source_map().span_to_snippet(p.span).ok())
-                        .map(|name| (name, false))
-                        .unwrap_or_else(|| ("'a".to_string(), true));
-
-                    let mut suggestions = vec![
-                        if let hir::LifetimeName::Underscore = lifetime_sub.name {
-                            (lifetime_sub.span, suggestion_param_name.clone())
-                        } else {
-                            (lifetime_sub.span.shrink_to_hi(), suggestion_param_name.clone() + " ")
-                        },
-                        if let hir::LifetimeName::Underscore = lifetime_sup.name {
-                            (lifetime_sup.span, suggestion_param_name.clone())
-                        } else {
-                            (lifetime_sup.span.shrink_to_hi(), suggestion_param_name.clone() + " ")
-                        },
-                    ];
-
-                    if introduce_new {
-                        let new_param_suggestion = match &generics.params {
-                            [] => (generics.span, format!("<{}>", suggestion_param_name)),
-                            [first, ..] => {
-                                (first.span.shrink_to_lo(), format!("{}, ", suggestion_param_name))
-                            }
-                        };
-
-                        suggestions.push(new_param_suggestion);
-                    }
-
-                    let mut sugg = String::from("consider introducing a named lifetime parameter");
-                    if is_impl {
-                        sugg.push_str(" and update trait if needed");
-                    }
-                    err.multipart_suggestion(
-                        sugg.as_str(),
-                        suggestions,
-                        Applicability::MaybeIncorrect,
-                    );
-                    err.note("each elided lifetime in input position becomes a distinct lifetime");
-                }
-            }
-        }
+pub fn suggest_adding_lifetime_params<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    sub: Region<'tcx>,
+    ty_sup: &Ty<'_>,
+    ty_sub: &Ty<'_>,
+    err: &mut Diagnostic,
+) -> bool {
+    let (
+        hir::Ty { kind: hir::TyKind::Rptr(lifetime_sub, _), .. },
+        hir::Ty { kind: hir::TyKind::Rptr(lifetime_sup, _), .. },
+    ) = (ty_sub, ty_sup) else {
+        return false;
+    };
+
+    if !lifetime_sub.name.is_elided() || !lifetime_sup.name.is_elided() {
+        return false;
+    };
+
+    let Some(anon_reg) = tcx.is_suitable_region(sub) else {
+        return false;
+    };
+
+    let hir_id = tcx.hir().local_def_id_to_hir_id(anon_reg.def_id);
+
+    let node = tcx.hir().get(hir_id);
+    let is_impl = matches!(&node, hir::Node::ImplItem(_));
+    let generics = match node {
+        hir::Node::Item(&hir::Item { kind: hir::ItemKind::Fn(_, ref generics, ..), .. })
+        | hir::Node::TraitItem(&hir::TraitItem { ref generics, .. })
+        | hir::Node::ImplItem(&hir::ImplItem { ref generics, .. }) => generics,
+        _ => return false,
+    };
+
+    let (suggestion_param_name, introduce_new) = generics
+        .params
+        .iter()
+        .find(|p| matches!(p.kind, GenericParamKind::Lifetime { .. }))
+        .and_then(|p| tcx.sess.source_map().span_to_snippet(p.span).ok())
+        .map(|name| (name, false))
+        .unwrap_or_else(|| ("'a".to_string(), true));
+
+    let mut suggestions = vec![
+        if let hir::LifetimeName::Underscore = lifetime_sub.name {
+            (lifetime_sub.span, suggestion_param_name.clone())
+        } else {
+            (lifetime_sub.span.shrink_to_hi(), suggestion_param_name.clone() + " ")
+        },
+        if let hir::LifetimeName::Underscore = lifetime_sup.name {
+            (lifetime_sup.span, suggestion_param_name.clone())
+        } else {
+            (lifetime_sup.span.shrink_to_hi(), suggestion_param_name.clone() + " ")
+        },
+    ];
+
+    if introduce_new {
+        let new_param_suggestion = match &generics.params {
+            [] => (generics.span, format!("<{}>", suggestion_param_name)),
+            [first, ..] => (first.span.shrink_to_lo(), format!("{}, ", suggestion_param_name)),
+        };
+
+        suggestions.push(new_param_suggestion);
     }
+
+    let mut sugg = String::from("consider introducing a named lifetime parameter");
+    if is_impl {
+        sugg.push_str(" and update trait if needed");
+    }
+    err.multipart_suggestion(sugg.as_str(), suggestions, Applicability::MaybeIncorrect);
+
+    true
 }
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs
index 135714af2a6..9c5b944c1e9 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs
@@ -20,26 +20,21 @@ use rustc_middle::ty::{self, Region, TyCtxt};
 /// ```
 /// The function returns the nested type corresponding to the anonymous region
 /// for e.g., `&u8` and `Vec<&u8>`.
-pub(crate) fn find_anon_type<'tcx>(
+pub fn find_anon_type<'tcx>(
     tcx: TyCtxt<'tcx>,
     region: Region<'tcx>,
     br: &ty::BoundRegionKind,
 ) -> Option<(&'tcx hir::Ty<'tcx>, &'tcx hir::FnSig<'tcx>)> {
-    if let Some(anon_reg) = tcx.is_suitable_region(region) {
-        let hir_id = tcx.hir().local_def_id_to_hir_id(anon_reg.def_id);
-        let Some(fn_sig) = tcx.hir().get(hir_id).fn_sig() else {
-            return None
-        };
+    let anon_reg = tcx.is_suitable_region(region)?;
+    let hir_id = tcx.hir().local_def_id_to_hir_id(anon_reg.def_id);
+    let fn_sig = tcx.hir().get(hir_id).fn_sig()?;
 
-        fn_sig
-            .decl
-            .inputs
-            .iter()
-            .find_map(|arg| find_component_for_bound_region(tcx, arg, br))
-            .map(|ty| (ty, fn_sig))
-    } else {
-        None
-    }
+    fn_sig
+        .decl
+        .inputs
+        .iter()
+        .find_map(|arg| find_component_for_bound_region(tcx, arg, br))
+        .map(|ty| (ty, fn_sig))
 }
 
 // This method creates a FindNestedTypeVisitor which returns the type corresponding
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs
index df81aea6ef9..9948d15c431 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs
@@ -14,7 +14,10 @@ mod static_impl_trait;
 mod trait_impl_difference;
 mod util;
 
+pub use different_lifetimes::suggest_adding_lifetime_params;
+pub use find_anon_type::find_anon_type;
 pub use static_impl_trait::suggest_new_region_bound;
+pub use util::find_param_with_region;
 
 impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
     pub fn try_report_nice_region_error(&self, error: &RegionResolutionError<'tcx>) -> bool {
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs
index 719f6b37a43..7d3ed2ed38a 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs
@@ -2,6 +2,7 @@
 //! anonymous regions.
 
 use crate::infer::error_reporting::nice_region_error::NiceRegionError;
+use crate::infer::TyCtxt;
 use rustc_hir as hir;
 use rustc_hir::def_id::LocalDefId;
 use rustc_middle::ty::{self, Binder, DefIdTree, Region, Ty, TypeFoldable};
@@ -9,7 +10,7 @@ use rustc_span::Span;
 
 /// Information about the anonymous region we are searching for.
 #[derive(Debug)]
-pub(super) struct AnonymousParamInfo<'tcx> {
+pub struct AnonymousParamInfo<'tcx> {
     /// The parameter corresponding to the anonymous region.
     pub param: &'tcx hir::Param<'tcx>,
     /// The type corresponding to the anonymous region parameter.
@@ -22,76 +23,83 @@ pub(super) struct AnonymousParamInfo<'tcx> {
     pub is_first: bool,
 }
 
+// This method walks the Type of the function body parameters using
+// `fold_regions()` function and returns the
+// &hir::Param of the function parameter corresponding to the anonymous
+// region and the Ty corresponding to the named region.
+// Currently only the case where the function declaration consists of
+// one named region and one anonymous region is handled.
+// Consider the example `fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32`
+// Here, we would return the hir::Param for y, we return the type &'a
+// i32, which is the type of y but with the anonymous region replaced
+// with 'a, the corresponding bound region and is_first which is true if
+// the hir::Param is the first parameter in the function declaration.
+pub fn find_param_with_region<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    anon_region: Region<'tcx>,
+    replace_region: Region<'tcx>,
+) -> Option<AnonymousParamInfo<'tcx>> {
+    let (id, bound_region) = match *anon_region {
+        ty::ReFree(ref free_region) => (free_region.scope, free_region.bound_region),
+        ty::ReEarlyBound(ebr) => {
+            (tcx.parent(ebr.def_id).unwrap(), ty::BoundRegionKind::BrNamed(ebr.def_id, ebr.name))
+        }
+        _ => return None, // not a free region
+    };
+
+    let hir = &tcx.hir();
+    let hir_id = hir.local_def_id_to_hir_id(id.as_local()?);
+    let body_id = hir.maybe_body_owned_by(hir_id)?;
+    let body = hir.body(body_id);
+    let owner_id = hir.body_owner(body_id);
+    let fn_decl = hir.fn_decl_by_hir_id(owner_id).unwrap();
+    let poly_fn_sig = tcx.fn_sig(id);
+    let fn_sig = tcx.liberate_late_bound_regions(id, poly_fn_sig);
+    body.params
+        .iter()
+        .take(if fn_sig.c_variadic {
+            fn_sig.inputs().len()
+        } else {
+            assert_eq!(fn_sig.inputs().len(), body.params.len());
+            body.params.len()
+        })
+        .enumerate()
+        .find_map(|(index, param)| {
+            // May return None; sometimes the tables are not yet populated.
+            let ty = fn_sig.inputs()[index];
+            let mut found_anon_region = false;
+            let new_param_ty = tcx.fold_regions(ty, &mut false, |r, _| {
+                if r == anon_region {
+                    found_anon_region = true;
+                    replace_region
+                } else {
+                    r
+                }
+            });
+            if found_anon_region {
+                let ty_hir_id = fn_decl.inputs[index].hir_id;
+                let param_ty_span = hir.span(ty_hir_id);
+                let is_first = index == 0;
+                Some(AnonymousParamInfo {
+                    param,
+                    param_ty: new_param_ty,
+                    param_ty_span,
+                    bound_region,
+                    is_first,
+                })
+            } else {
+                None
+            }
+        })
+}
+
 impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
-    // This method walks the Type of the function body parameters using
-    // `fold_regions()` function and returns the
-    // &hir::Param of the function parameter corresponding to the anonymous
-    // region and the Ty corresponding to the named region.
-    // Currently only the case where the function declaration consists of
-    // one named region and one anonymous region is handled.
-    // Consider the example `fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32`
-    // Here, we would return the hir::Param for y, we return the type &'a
-    // i32, which is the type of y but with the anonymous region replaced
-    // with 'a, the corresponding bound region and is_first which is true if
-    // the hir::Param is the first parameter in the function declaration.
     pub(super) fn find_param_with_region(
         &self,
         anon_region: Region<'tcx>,
         replace_region: Region<'tcx>,
     ) -> Option<AnonymousParamInfo<'_>> {
-        let (id, bound_region) = match *anon_region {
-            ty::ReFree(ref free_region) => (free_region.scope, free_region.bound_region),
-            ty::ReEarlyBound(ebr) => (
-                self.tcx().parent(ebr.def_id).unwrap(),
-                ty::BoundRegionKind::BrNamed(ebr.def_id, ebr.name),
-            ),
-            _ => return None, // not a free region
-        };
-
-        let hir = &self.tcx().hir();
-        let hir_id = hir.local_def_id_to_hir_id(id.as_local()?);
-        let body_id = hir.maybe_body_owned_by(hir_id)?;
-        let body = hir.body(body_id);
-        let owner_id = hir.body_owner(body_id);
-        let fn_decl = hir.fn_decl_by_hir_id(owner_id).unwrap();
-        let poly_fn_sig = self.tcx().fn_sig(id);
-        let fn_sig = self.tcx().liberate_late_bound_regions(id, poly_fn_sig);
-        body.params
-            .iter()
-            .take(if fn_sig.c_variadic {
-                fn_sig.inputs().len()
-            } else {
-                assert_eq!(fn_sig.inputs().len(), body.params.len());
-                body.params.len()
-            })
-            .enumerate()
-            .find_map(|(index, param)| {
-                // May return None; sometimes the tables are not yet populated.
-                let ty = fn_sig.inputs()[index];
-                let mut found_anon_region = false;
-                let new_param_ty = self.tcx().fold_regions(ty, &mut false, |r, _| {
-                    if r == anon_region {
-                        found_anon_region = true;
-                        replace_region
-                    } else {
-                        r
-                    }
-                });
-                if found_anon_region {
-                    let ty_hir_id = fn_decl.inputs[index].hir_id;
-                    let param_ty_span = hir.span(ty_hir_id);
-                    let is_first = index == 0;
-                    Some(AnonymousParamInfo {
-                        param,
-                        param_ty: new_param_ty,
-                        param_ty_span,
-                        bound_region,
-                        is_first,
-                    })
-                } else {
-                    None
-                }
-            })
+        find_param_with_region(self.tcx(), anon_region, replace_region)
     }
 
     // Here, we check for the case where the anonymous region
diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs
index baea3e8285a..cbdcf013522 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/note.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs
@@ -372,8 +372,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                         .hir()
                         .get_generics(impl_item_def_id)
                         .unwrap()
-                        .where_clause
-                        .tail_span_for_suggestion();
+                        .where_clause_span
+                        .shrink_to_hi();
 
                     let suggestion = format!(
                         "{} {}",
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index fe75ee8b37b..1327bf6fcd4 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -66,6 +66,7 @@ where
         location: ExternLocation::ExactPaths(locations),
         is_private_dep: false,
         add_prelude: true,
+        nounused_dep: false,
     }
 }
 
@@ -751,6 +752,7 @@ fn test_debugging_options_tracking_hash() {
     tracked!(location_detail, LocationDetail { file: true, line: false, column: false });
     tracked!(merge_functions, Some(MergeFunctions::Disabled));
     tracked!(mir_emit_retag, true);
+    tracked!(mir_enable_passes, vec![("DestProp".to_string(), false)]);
     tracked!(mir_opt_level, Some(4));
     tracked!(move_size_limit, Some(4096));
     tracked!(mutable_noalias, Some(true));
diff --git a/compiler/rustc_lint/src/array_into_iter.rs b/compiler/rustc_lint/src/array_into_iter.rs
index a14d6020361..dff2e31c607 100644
--- a/compiler/rustc_lint/src/array_into_iter.rs
+++ b/compiler/rustc_lint/src/array_into_iter.rs
@@ -129,7 +129,7 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter {
                 diag.span_suggestion(
                     call.ident.span,
                     "use `.iter()` instead of `.into_iter()` to avoid ambiguity",
-                    "iter".into(),
+                    "iter",
                     Applicability::MachineApplicable,
                 );
                 if self.for_expr_span == expr.span {
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 77fe76af2de..3564f15e210 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -36,8 +36,7 @@ use rustc_feature::{deprecated_attributes, AttributeGate, BuiltinAttribute, Gate
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdSet, CRATE_DEF_ID};
-use rustc_hir::{ForeignItemKind, GenericParamKind, PatKind};
-use rustc_hir::{HirId, Node};
+use rustc_hir::{ForeignItemKind, GenericParamKind, HirId, PatKind};
 use rustc_index::vec::Idx;
 use rustc_middle::lint::LintDiagnosticBuilder;
 use rustc_middle::ty::layout::{LayoutError, LayoutOf};
@@ -487,9 +486,6 @@ declare_lint! {
 pub struct MissingDoc {
     /// Stack of whether `#[doc(hidden)]` is set at each level which has lint attributes.
     doc_hidden_stack: Vec<bool>,
-
-    /// Private traits or trait items that leaked through. Don't check their methods.
-    private_traits: FxHashSet<hir::HirId>,
 }
 
 impl_lint_pass!(MissingDoc => [MISSING_DOCS]);
@@ -520,7 +516,7 @@ fn has_doc(attr: &ast::Attribute) -> bool {
 
 impl MissingDoc {
     pub fn new() -> MissingDoc {
-        MissingDoc { doc_hidden_stack: vec![false], private_traits: FxHashSet::default() }
+        MissingDoc { doc_hidden_stack: vec![false] }
     }
 
     fn doc_hidden(&self) -> bool {
@@ -598,30 +594,16 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
 
     fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
         match it.kind {
-            hir::ItemKind::Trait(.., trait_item_refs) => {
+            hir::ItemKind::Trait(..) => {
                 // Issue #11592: traits are always considered exported, even when private.
-                if let hir::VisibilityKind::Inherited = it.vis.node {
-                    self.private_traits.insert(it.hir_id());
-                    for trait_item_ref in trait_item_refs {
-                        self.private_traits.insert(trait_item_ref.id.hir_id());
-                    }
+                if cx.tcx.visibility(it.def_id)
+                    == ty::Visibility::Restricted(
+                        cx.tcx.parent_module_from_def_id(it.def_id).to_def_id(),
+                    )
+                {
                     return;
                 }
             }
-            hir::ItemKind::Impl(hir::Impl { of_trait: Some(ref trait_ref), items, .. }) => {
-                // If the trait is private, add the impl items to `private_traits` so they don't get
-                // reported for missing docs.
-                let real_trait = trait_ref.path.res.def_id();
-                let Some(def_id) = real_trait.as_local() else { return };
-                let Some(Node::Item(item)) = cx.tcx.hir().find_by_def_id(def_id) else { return };
-                if let hir::VisibilityKind::Inherited = item.vis.node {
-                    for impl_item_ref in items {
-                        self.private_traits.insert(impl_item_ref.id.hir_id());
-                    }
-                }
-                return;
-            }
-
             hir::ItemKind::TyAlias(..)
             | hir::ItemKind::Fn(..)
             | hir::ItemKind::Macro(..)
@@ -641,10 +623,6 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
     }
 
     fn check_trait_item(&mut self, cx: &LateContext<'_>, trait_item: &hir::TraitItem<'_>) {
-        if self.private_traits.contains(&trait_item.hir_id()) {
-            return;
-        }
-
         let (article, desc) = cx.tcx.article_and_description(trait_item.def_id.to_def_id());
 
         self.check_missing_docs_attrs(cx, trait_item.def_id, trait_item.span, article, desc);
@@ -1221,8 +1199,8 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
                     });
                 }
             }
-            hir::ItemKind::Impl(hir::Impl { ref generics, items, .. }) => {
-                for it in items {
+            hir::ItemKind::Impl(hir::Impl { generics, items, .. }) => {
+                for it in *items {
                     if let hir::AssocItemKind::Fn { .. } = it.kind {
                         if let Some(no_mangle_attr) = cx
                             .sess()
@@ -1384,46 +1362,47 @@ impl UnreachablePub {
         cx: &LateContext<'_>,
         what: &str,
         def_id: LocalDefId,
-        vis: &hir::Visibility<'_>,
         span: Span,
+        vis_span: Span,
         exportable: bool,
     ) {
         let mut applicability = Applicability::MachineApplicable;
-        match vis.node {
-            hir::VisibilityKind::Public if !cx.access_levels.is_reachable(def_id) => {
-                if span.from_expansion() {
-                    applicability = Applicability::MaybeIncorrect;
+        if cx.tcx.visibility(def_id).is_public() && !cx.access_levels.is_reachable(def_id) {
+            if vis_span.from_expansion() {
+                applicability = Applicability::MaybeIncorrect;
+            }
+            let def_span = cx.tcx.sess.source_map().guess_head_span(span);
+            cx.struct_span_lint(UNREACHABLE_PUB, def_span, |lint| {
+                let mut err = lint.build(&format!("unreachable `pub` {}", what));
+                let replacement = if cx.tcx.features().crate_visibility_modifier {
+                    "crate"
+                } else {
+                    "pub(crate)"
                 }
-                let def_span = cx.tcx.sess.source_map().guess_head_span(span);
-                cx.struct_span_lint(UNREACHABLE_PUB, def_span, |lint| {
-                    let mut err = lint.build(&format!("unreachable `pub` {}", what));
-                    let replacement = if cx.tcx.features().crate_visibility_modifier {
-                        "crate"
-                    } else {
-                        "pub(crate)"
-                    }
-                    .to_owned();
+                .to_owned();
 
-                    err.span_suggestion(
-                        vis.span,
-                        "consider restricting its visibility",
-                        replacement,
-                        applicability,
-                    );
-                    if exportable {
-                        err.help("or consider exporting it for use by other crates");
-                    }
-                    err.emit();
-                });
-            }
-            _ => {}
+                err.span_suggestion(
+                    vis_span,
+                    "consider restricting its visibility",
+                    replacement,
+                    applicability,
+                );
+                if exportable {
+                    err.help("or consider exporting it for use by other crates");
+                }
+                err.emit();
+            });
         }
     }
 }
 
 impl<'tcx> LateLintPass<'tcx> for UnreachablePub {
     fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
-        self.perform_lint(cx, "item", item.def_id, &item.vis, item.span, true);
+        // Do not warn for fake `use` statements.
+        if let hir::ItemKind::Use(_, hir::UseKind::ListStem) = &item.kind {
+            return;
+        }
+        self.perform_lint(cx, "item", item.def_id, item.span, item.vis_span, true);
     }
 
     fn check_foreign_item(&mut self, cx: &LateContext<'_>, foreign_item: &hir::ForeignItem<'tcx>) {
@@ -1431,19 +1410,29 @@ impl<'tcx> LateLintPass<'tcx> for UnreachablePub {
             cx,
             "item",
             foreign_item.def_id,
-            &foreign_item.vis,
             foreign_item.span,
+            foreign_item.vis_span,
             true,
         );
     }
 
     fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) {
         let def_id = cx.tcx.hir().local_def_id(field.hir_id);
-        self.perform_lint(cx, "field", def_id, &field.vis, field.span, false);
+        self.perform_lint(cx, "field", def_id, field.span, field.vis_span, false);
     }
 
     fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) {
-        self.perform_lint(cx, "item", impl_item.def_id, &impl_item.vis, impl_item.span, false);
+        // Only lint inherent impl items.
+        if cx.tcx.associated_item(impl_item.def_id).trait_item_def_id.is_none() {
+            self.perform_lint(
+                cx,
+                "item",
+                impl_item.def_id,
+                impl_item.span,
+                impl_item.vis_span,
+                false,
+            );
+        }
     }
 }
 
@@ -1528,59 +1517,61 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
             // Bounds are respected for `type X = impl Trait`
             return;
         }
-        let mut suggested_changing_assoc_types = false;
         // There must not be a where clause
-        if !type_alias_generics.where_clause.predicates.is_empty() {
-            cx.lint(
-                TYPE_ALIAS_BOUNDS,
-                |lint| {
-                    let mut err = lint.build("where clauses are not enforced in type aliases");
-                    let spans: Vec<_> = type_alias_generics
-                        .where_clause
-                        .predicates
-                        .iter()
-                        .map(|pred| pred.span())
-                        .collect();
-                    err.set_span(spans);
-                    err.span_suggestion(
-                        type_alias_generics.where_clause.span_for_predicates_or_empty_place(),
-                        "the clause will not be checked when the type alias is used, and should be removed",
-                        String::new(),
-                        Applicability::MachineApplicable,
-                    );
-                    if !suggested_changing_assoc_types {
-                        TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err);
-                        suggested_changing_assoc_types = true;
-                    }
-                    err.emit();
-                },
-            );
+        if type_alias_generics.predicates.is_empty() {
+            return;
         }
-        // The parameters must not have bounds
-        for param in type_alias_generics.params.iter() {
-            let spans: Vec<_> = param.bounds.iter().map(|b| b.span()).collect();
-            let suggestion = spans
-                .iter()
-                .map(|sp| {
-                    let start = param.span.between(*sp); // Include the `:` in `T: Bound`.
-                    (start.to(*sp), String::new())
-                })
-                .collect();
-            if !spans.is_empty() {
-                cx.struct_span_lint(TYPE_ALIAS_BOUNDS, spans, |lint| {
-                    let mut err =
-                        lint.build("bounds on generic parameters are not enforced in type aliases");
-                    let msg = "the bound will not be checked when the type alias is used, \
-                                   and should be removed";
-                    err.multipart_suggestion(msg, suggestion, Applicability::MachineApplicable);
-                    if !suggested_changing_assoc_types {
-                        TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err);
-                        suggested_changing_assoc_types = true;
-                    }
-                    err.emit();
-                });
+
+        let mut where_spans = Vec::new();
+        let mut inline_spans = Vec::new();
+        let mut inline_sugg = Vec::new();
+        for p in type_alias_generics.predicates {
+            let span = p.span();
+            if p.in_where_clause() {
+                where_spans.push(span);
+            } else {
+                for b in p.bounds() {
+                    inline_spans.push(b.span());
+                }
+                inline_sugg.push((span, String::new()));
             }
         }
+
+        let mut suggested_changing_assoc_types = false;
+        if !where_spans.is_empty() {
+            cx.lint(TYPE_ALIAS_BOUNDS, |lint| {
+                let mut err = lint.build("where clauses are not enforced in type aliases");
+                err.set_span(where_spans);
+                err.span_suggestion(
+                    type_alias_generics.where_clause_span,
+                    "the clause will not be checked when the type alias is used, and should be removed",
+                    String::new(),
+                    Applicability::MachineApplicable,
+                );
+                if !suggested_changing_assoc_types {
+                    TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err);
+                    suggested_changing_assoc_types = true;
+                }
+                err.emit();
+            });
+        }
+
+        if !inline_spans.is_empty() {
+            cx.lint(TYPE_ALIAS_BOUNDS, |lint| {
+                let mut err =
+                    lint.build("bounds on generic parameters are not enforced in type aliases");
+                err.set_span(inline_spans);
+                err.multipart_suggestion(
+                    "the bound will not be checked when the type alias is used, and should be removed",
+                    inline_sugg,
+                    Applicability::MachineApplicable,
+                );
+                if !suggested_changing_assoc_types {
+                    TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err);
+                }
+                err.emit();
+            });
+        }
     }
 }
 
@@ -2096,27 +2087,6 @@ impl ExplicitOutlivesRequirements {
             .collect()
     }
 
-    fn collect_outlived_lifetimes<'tcx>(
-        &self,
-        param: &'tcx hir::GenericParam<'tcx>,
-        tcx: TyCtxt<'tcx>,
-        inferred_outlives: &'tcx [(ty::Predicate<'tcx>, Span)],
-        ty_generics: &'tcx ty::Generics,
-    ) -> Vec<ty::Region<'tcx>> {
-        let index =
-            ty_generics.param_def_id_to_index[&tcx.hir().local_def_id(param.hir_id).to_def_id()];
-
-        match param.kind {
-            hir::GenericParamKind::Lifetime { .. } => {
-                Self::lifetimes_outliving_lifetime(inferred_outlives, index)
-            }
-            hir::GenericParamKind::Type { .. } => {
-                Self::lifetimes_outliving_type(inferred_outlives, index)
-            }
-            hir::GenericParamKind::Const { .. } => Vec::new(),
-        }
-    }
-
     fn collect_outlives_bound_spans<'tcx>(
         &self,
         tcx: TyCtxt<'tcx>,
@@ -2224,41 +2194,11 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
 
             let mut bound_count = 0;
             let mut lint_spans = Vec::new();
-
-            for param in hir_generics.params {
-                let has_lifetime_bounds = param
-                    .bounds
-                    .iter()
-                    .any(|bound| matches!(bound, hir::GenericBound::Outlives(_)));
-                if !has_lifetime_bounds {
-                    continue;
-                }
-
-                let relevant_lifetimes =
-                    self.collect_outlived_lifetimes(param, cx.tcx, inferred_outlives, ty_generics);
-                if relevant_lifetimes.is_empty() {
-                    continue;
-                }
-
-                let bound_spans = self.collect_outlives_bound_spans(
-                    cx.tcx,
-                    &param.bounds,
-                    &relevant_lifetimes,
-                    infer_static,
-                );
-                bound_count += bound_spans.len();
-                lint_spans.extend(self.consolidate_outlives_bound_spans(
-                    param.span.shrink_to_hi(),
-                    &param.bounds,
-                    bound_spans,
-                ));
-            }
-
             let mut where_lint_spans = Vec::new();
             let mut dropped_predicate_count = 0;
-            let num_predicates = hir_generics.where_clause.predicates.len();
-            for (i, where_predicate) in hir_generics.where_clause.predicates.iter().enumerate() {
-                let (relevant_lifetimes, bounds, span) = match where_predicate {
+            let num_predicates = hir_generics.predicates.len();
+            for (i, where_predicate) in hir_generics.predicates.iter().enumerate() {
+                let (relevant_lifetimes, bounds, span, in_where_clause) = match where_predicate {
                     hir::WherePredicate::RegionPredicate(predicate) => {
                         if let Some(Region::EarlyBound(index, ..)) =
                             cx.tcx.named_region(predicate.lifetime.hir_id)
@@ -2267,6 +2207,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
                                 Self::lifetimes_outliving_lifetime(inferred_outlives, index),
                                 &predicate.bounds,
                                 predicate.span,
+                                predicate.in_where_clause,
                             )
                         } else {
                             continue;
@@ -2285,6 +2226,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
                                     Self::lifetimes_outliving_type(inferred_outlives, index),
                                     &predicate.bounds,
                                     predicate.span,
+                                    predicate.in_where_clause,
                                 )
                             }
                             _ => {
@@ -2311,10 +2253,12 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
                     dropped_predicate_count += 1;
                 }
 
-                // If all the bounds on a predicate were inferable and there are
-                // further predicates, we want to eat the trailing comma.
-                if drop_predicate && i + 1 < num_predicates {
-                    let next_predicate_span = hir_generics.where_clause.predicates[i + 1].span();
+                if drop_predicate && !in_where_clause {
+                    lint_spans.push(span);
+                } else if drop_predicate && i + 1 < num_predicates {
+                    // If all the bounds on a predicate were inferable and there are
+                    // further predicates, we want to eat the trailing comma.
+                    let next_predicate_span = hir_generics.predicates[i + 1].span();
                     where_lint_spans.push(span.to(next_predicate_span.shrink_to_lo()));
                 } else {
                     where_lint_spans.extend(self.consolidate_outlives_bound_spans(
@@ -2327,10 +2271,9 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
 
             // If all predicates are inferable, drop the entire clause
             // (including the `where`)
-            if num_predicates > 0 && dropped_predicate_count == num_predicates {
+            if hir_generics.has_where_clause && dropped_predicate_count == num_predicates {
                 let where_span = hir_generics
-                    .where_clause
-                    .span()
+                    .where_clause_span()
                     .expect("span of (nonempty) where clause should exist");
                 // Extend the where clause back to the closing `>` of the
                 // generics, except for tuple struct, which have the `where`
@@ -2357,7 +2300,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
                             },
                             lint_spans
                                 .into_iter()
-                                .map(|span| (span, "".to_owned()))
+                                .map(|span| (span, String::new()))
                                 .collect::<Vec<_>>(),
                             Applicability::MachineApplicable,
                         )
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index 0ffa65b79b5..d7cd5ec04f3 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -738,7 +738,7 @@ pub trait LintContext: Sized {
                     db.span_suggestion_verbose(
                         span.shrink_to_hi(),
                         "insert whitespace here to avoid this being parsed as a prefix in Rust 2021",
-                        " ".into(),
+                        " ",
                         Applicability::MachineApplicable,
                     );
                 }
diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs
index f21f25c3584..71769fceec1 100644
--- a/compiler/rustc_lint/src/non_fmt_panic.rs
+++ b/compiler/rustc_lint/src/non_fmt_panic.rs
@@ -176,7 +176,7 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
                 l.span_suggestion_verbose(
                     arg_span.shrink_to_lo(),
                     "add a \"{}\" format string to Display the message",
-                    "\"{}\", ".into(),
+                    "\"{}\", ",
                     fmt_applicability,
                 );
             } else if suggest_debug {
@@ -186,7 +186,7 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
                         "add a \"{{:?}}\" format string to use the Debug implementation of `{}`",
                         ty,
                     ),
-                    "\"{:?}\", ".into(),
+                    "\"{:?}\", ",
                     fmt_applicability,
                 );
             }
@@ -266,13 +266,13 @@ fn check_panic_str<'tcx>(
                 l.span_suggestion(
                     arg.span.shrink_to_hi(),
                     &format!("add the missing argument{}", pluralize!(n_arguments)),
-                    ", ...".into(),
+                    ", ...",
                     Applicability::HasPlaceholders,
                 );
                 l.span_suggestion(
                     arg.span.shrink_to_lo(),
                     "or add a \"{}\" format string to use the message literally",
-                    "\"{}\", ".into(),
+                    "\"{}\", ",
                     Applicability::MachineApplicable,
                 );
             }
@@ -297,7 +297,7 @@ fn check_panic_str<'tcx>(
                 l.span_suggestion(
                     arg.span.shrink_to_lo(),
                     "add a \"{}\" format string to use the message literally",
-                    "\"{}\", ".into(),
+                    "\"{}\", ",
                     Applicability::MachineApplicable,
                 );
             }
diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs
index f73388c675e..e1507d0fbb4 100644
--- a/compiler/rustc_lint/src/nonstandard_style.rs
+++ b/compiler/rustc_lint/src/nonstandard_style.rs
@@ -406,7 +406,7 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase {
                 }
                 _ => (),
             },
-            FnKind::ItemFn(ident, _, header, _) => {
+            FnKind::ItemFn(ident, _, header) => {
                 // Skip foreign-ABI #[no_mangle] functions (Issue #31924)
                 if header.abi != Abi::Rust && cx.sess().contains_name(attrs, sym::no_mangle) {
                     return;
diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs
index bde773a32db..57b4f96dc10 100644
--- a/compiler/rustc_lint_defs/src/lib.rs
+++ b/compiler/rustc_lint_defs/src/lib.rs
@@ -8,6 +8,7 @@ use rustc_ast::node_id::{NodeId, NodeMap};
 use rustc_ast::{AttrId, Attribute};
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey};
 use rustc_error_messages::MultiSpan;
+use rustc_hir::HashStableContext;
 use rustc_hir::HirId;
 use rustc_span::edition::Edition;
 use rustc_span::{sym, symbol::Ident, Span, Symbol};
@@ -146,7 +147,7 @@ impl<HCX: rustc_hir::HashStableContext> ToStableHashKey<HCX> for LintExpectation
 /// Setting for how to handle a lint.
 ///
 /// See: <https://doc.rust-lang.org/rustc/lints/levels.html>
-#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
+#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash, HashStable_Generic)]
 pub enum Level {
     /// The `allow` level will not issue any message.
     Allow,
@@ -174,8 +175,6 @@ pub enum Level {
     Forbid,
 }
 
-rustc_data_structures::impl_stable_hash_via_hash!(Level);
-
 impl Level {
     /// Converts a level to a lower-case string.
     pub fn as_str(self) -> &'static str {
@@ -215,6 +214,13 @@ impl Level {
             _ => None,
         }
     }
+
+    pub fn is_error(self) -> bool {
+        match self {
+            Level::Allow | Level::Expect(_) | Level::Warn | Level::ForceWarn => false,
+            Level::Deny | Level::Forbid => true,
+        }
+    }
 }
 
 /// Specification of a single lint.
diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
index 71f21dc6666..38fddbdba54 100644
--- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
@@ -107,6 +107,7 @@ static LLVMRustPassKind toRust(PassKind Kind) {
 }
 
 extern "C" LLVMPassRef LLVMRustFindAndCreatePass(const char *PassName) {
+#if LLVM_VERSION_LT(15, 0)
   StringRef SR(PassName);
   PassRegistry *PR = PassRegistry::getPassRegistry();
 
@@ -115,36 +116,59 @@ extern "C" LLVMPassRef LLVMRustFindAndCreatePass(const char *PassName) {
     return wrap(PI->createPass());
   }
   return nullptr;
+#else
+  report_fatal_error("Legacy PM not supported with LLVM 15");
+#endif
 }
 
 extern "C" LLVMPassRef LLVMRustCreateAddressSanitizerFunctionPass(bool Recover) {
+#if LLVM_VERSION_LT(15, 0)
   const bool CompileKernel = false;
   const bool UseAfterScope = true;
 
   return wrap(createAddressSanitizerFunctionPass(CompileKernel, Recover, UseAfterScope));
+#else
+  report_fatal_error("Legacy PM not supported with LLVM 15");
+#endif
 }
 
 extern "C" LLVMPassRef LLVMRustCreateModuleAddressSanitizerPass(bool Recover) {
+#if LLVM_VERSION_LT(15, 0)
   const bool CompileKernel = false;
 
   return wrap(createModuleAddressSanitizerLegacyPassPass(CompileKernel, Recover));
+#else
+  report_fatal_error("Legacy PM not supported with LLVM 15");
+#endif
 }
 
 extern "C" LLVMPassRef LLVMRustCreateMemorySanitizerPass(int TrackOrigins, bool Recover) {
+#if LLVM_VERSION_LT(15, 0)
   const bool CompileKernel = false;
 
   return wrap(createMemorySanitizerLegacyPassPass(
       MemorySanitizerOptions{TrackOrigins, Recover, CompileKernel}));
+#else
+  report_fatal_error("Legacy PM not supported with LLVM 15");
+#endif
 }
 
 extern "C" LLVMPassRef LLVMRustCreateThreadSanitizerPass() {
+#if LLVM_VERSION_LT(15, 0)
   return wrap(createThreadSanitizerLegacyPassPass());
+#else
+  report_fatal_error("Legacy PM not supported with LLVM 15");
+#endif
 }
 
 extern "C" LLVMPassRef LLVMRustCreateHWAddressSanitizerPass(bool Recover) {
+#if LLVM_VERSION_LT(15, 0)
   const bool CompileKernel = false;
 
   return wrap(createHWAddressSanitizerLegacyPassPass(CompileKernel, Recover));
+#else
+  report_fatal_error("Legacy PM not supported with LLVM 15");
+#endif
 }
 
 extern "C" LLVMRustPassKind LLVMRustPassKind(LLVMPassRef RustPass) {
@@ -154,10 +178,57 @@ extern "C" LLVMRustPassKind LLVMRustPassKind(LLVMPassRef RustPass) {
 }
 
 extern "C" void LLVMRustAddPass(LLVMPassManagerRef PMR, LLVMPassRef RustPass) {
+#if LLVM_VERSION_LT(15, 0)
   assert(RustPass);
   Pass *Pass = unwrap(RustPass);
   PassManagerBase *PMB = unwrap(PMR);
   PMB->add(Pass);
+#else
+  report_fatal_error("Legacy PM not supported with LLVM 15");
+#endif
+}
+
+extern "C" LLVMPassManagerBuilderRef LLVMRustPassManagerBuilderCreate() {
+#if LLVM_VERSION_LT(15, 0)
+  return LLVMPassManagerBuilderCreate();
+#else
+  report_fatal_error("Legacy PM not supported with LLVM 15");
+#endif
+}
+
+extern "C" void LLVMRustPassManagerBuilderDispose(LLVMPassManagerBuilderRef PMB) {
+#if LLVM_VERSION_LT(15, 0)
+  LLVMPassManagerBuilderDispose(PMB);
+#else
+  report_fatal_error("Legacy PM not supported with LLVM 15");
+#endif
+}
+
+extern "C" void LLVMRustPassManagerBuilderPopulateFunctionPassManager(
+  LLVMPassManagerBuilderRef PMB, LLVMPassManagerRef PM) {
+#if LLVM_VERSION_LT(15, 0)
+  LLVMPassManagerBuilderPopulateFunctionPassManager(PMB, PM);
+#else
+  report_fatal_error("Legacy PM not supported with LLVM 15");
+#endif
+}
+
+extern "C" void LLVMRustPassManagerBuilderPopulateModulePassManager(
+  LLVMPassManagerBuilderRef PMB, LLVMPassManagerRef PM) {
+#if LLVM_VERSION_LT(15, 0)
+  LLVMPassManagerBuilderPopulateModulePassManager(PMB, PM);
+#else
+  report_fatal_error("Legacy PM not supported with LLVM 15");
+#endif
+}
+
+extern "C" void LLVMRustPassManagerBuilderPopulateLTOPassManager(
+  LLVMPassManagerBuilderRef PMB, LLVMPassManagerRef PM, bool Internalize, bool RunInliner) {
+#if LLVM_VERSION_LT(15, 0)
+  LLVMPassManagerBuilderPopulateLTOPassManager(PMB, PM, Internalize, RunInliner);
+#else
+  report_fatal_error("Legacy PM not supported with LLVM 15");
+#endif
 }
 
 extern "C"
@@ -165,12 +236,26 @@ void LLVMRustPassManagerBuilderPopulateThinLTOPassManager(
   LLVMPassManagerBuilderRef PMBR,
   LLVMPassManagerRef PMR
 ) {
+#if LLVM_VERSION_LT(15, 0)
   unwrap(PMBR)->populateThinLTOPassManager(*unwrap(PMR));
+#else
+  report_fatal_error("Legacy PM not supported with LLVM 15");
+#endif
+}
+
+extern "C" void LLVMRustPassManagerBuilderUseInlinerWithThreshold(
+  LLVMPassManagerBuilderRef PMB, unsigned Threshold) {
+#if LLVM_VERSION_LT(15, 0)
+  LLVMPassManagerBuilderUseInlinerWithThreshold(PMB, Threshold);
+#else
+  report_fatal_error("Legacy PM not supported with LLVM 15");
+#endif
 }
 
 extern "C"
 void LLVMRustAddLastExtensionPasses(
     LLVMPassManagerBuilderRef PMBR, LLVMPassRef *Passes, size_t NumPasses) {
+#if LLVM_VERSION_LT(15, 0)
   auto AddExtensionPasses = [Passes, NumPasses](
       const PassManagerBuilder &Builder, PassManagerBase &PM) {
     for (size_t I = 0; I < NumPasses; I++) {
@@ -183,6 +268,9 @@ void LLVMRustAddLastExtensionPasses(
                              AddExtensionPasses);
   unwrap(PMBR)->addExtension(PassManagerBuilder::EP_EnabledOnOptLevel0,
                              AddExtensionPasses);
+#else
+  report_fatal_error("Legacy PM not supported with LLVM 15");
+#endif
 }
 
 #ifdef LLVM_COMPONENT_X86
@@ -533,12 +621,16 @@ extern "C" void LLVMRustDisposeTargetMachine(LLVMTargetMachineRef TM) {
 extern "C" void LLVMRustConfigurePassManagerBuilder(
     LLVMPassManagerBuilderRef PMBR, LLVMRustCodeGenOptLevel OptLevel,
     bool MergeFunctions, bool SLPVectorize, bool LoopVectorize, bool PrepareForThinLTO,
-    const char* PGOGenPath, const char* PGOUsePath, const char* PGOSampleUsePath) {
+    const char* PGOGenPath, const char* PGOUsePath, const char* PGOSampleUsePath,
+    int SizeLevel) {
+#if LLVM_VERSION_LT(15, 0)
   unwrap(PMBR)->MergeFunctions = MergeFunctions;
   unwrap(PMBR)->SLPVectorize = SLPVectorize;
   unwrap(PMBR)->OptLevel = fromRust(OptLevel);
   unwrap(PMBR)->LoopVectorize = LoopVectorize;
   unwrap(PMBR)->PrepareForThinLTO = PrepareForThinLTO;
+  unwrap(PMBR)->SizeLevel = SizeLevel;
+  unwrap(PMBR)->DisableUnrollLoops = SizeLevel != 0;
 
   if (PGOGenPath) {
     assert(!PGOUsePath && !PGOSampleUsePath);
@@ -550,6 +642,9 @@ extern "C" void LLVMRustConfigurePassManagerBuilder(
   } else if (PGOSampleUsePath) {
     unwrap(PMBR)->PGOSampleUse = PGOSampleUsePath;
   }
+#else
+  report_fatal_error("Legacy PM not supported with LLVM 15");
+#endif
 }
 
 // Unfortunately, the LLVM C API doesn't provide a way to set the `LibraryInfo`
diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
index 3ed4396d1e9..6d79e662a42 100644
--- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
@@ -6,6 +6,7 @@
 #include "llvm/IR/GlobalVariable.h"
 #include "llvm/IR/Instructions.h"
 #include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/Mangler.h"
 #include "llvm/Object/Archive.h"
 #include "llvm/Object/COFFImportFile.h"
 #include "llvm/Object/ObjectFile.h"
@@ -1216,6 +1217,11 @@ extern "C" LLVMTypeKind LLVMRustGetTypeKind(LLVMTypeRef Ty) {
     return LLVMBFloatTypeKind;
   case Type::X86_AMXTyID:
     return LLVMX86_AMXTypeKind;
+#if LLVM_VERSION_GE(15, 0)
+  case Type::DXILPointerTyID:
+    report_fatal_error("Rust does not support DirectX typed pointers.");
+    break;
+#endif
   }
   report_fatal_error("Unhandled TypeID.");
 }
diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs
new file mode 100644
index 00000000000..f49166433fa
--- /dev/null
+++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs
@@ -0,0 +1,586 @@
+#![deny(unused_must_use)]
+
+use crate::diagnostics::error::{
+    invalid_nested_attr, span_err, throw_invalid_attr, throw_invalid_nested_attr, throw_span_err,
+    SessionDiagnosticDeriveError,
+};
+use crate::diagnostics::utils::{
+    option_inner_ty, report_error_if_not_applied_to_span, type_matches_path, Applicability,
+    FieldInfo, HasFieldMap, SetOnce,
+};
+use proc_macro2::TokenStream;
+use quote::{format_ident, quote};
+use std::collections::HashMap;
+use std::str::FromStr;
+use syn::{spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, Type};
+use synstructure::Structure;
+
+/// The central struct for constructing the `into_diagnostic` method from an annotated struct.
+pub(crate) struct SessionDiagnosticDerive<'a> {
+    structure: Structure<'a>,
+    builder: SessionDiagnosticDeriveBuilder,
+}
+
+impl<'a> SessionDiagnosticDerive<'a> {
+    pub(crate) fn new(diag: syn::Ident, sess: syn::Ident, structure: Structure<'a>) -> Self {
+        // Build the mapping of field names to fields. This allows attributes to peek values from
+        // other fields.
+        let mut fields_map = HashMap::new();
+
+        // Convenience bindings.
+        let ast = structure.ast();
+
+        if let syn::Data::Struct(syn::DataStruct { fields, .. }) = &ast.data {
+            for field in fields.iter() {
+                if let Some(ident) = &field.ident {
+                    fields_map.insert(ident.to_string(), quote! { &self.#ident });
+                }
+            }
+        }
+
+        Self {
+            builder: SessionDiagnosticDeriveBuilder {
+                diag,
+                sess,
+                fields: fields_map,
+                kind: None,
+                code: None,
+                slug: None,
+            },
+            structure,
+        }
+    }
+
+    pub(crate) fn into_tokens(self) -> TokenStream {
+        let SessionDiagnosticDerive { mut structure, mut builder } = self;
+
+        let ast = structure.ast();
+        let attrs = &ast.attrs;
+
+        let (implementation, param_ty) = {
+            if let syn::Data::Struct(..) = ast.data {
+                let preamble = {
+                    let preamble = attrs.iter().map(|attr| {
+                        builder
+                            .generate_structure_code(attr)
+                            .unwrap_or_else(|v| v.to_compile_error())
+                    });
+
+                    quote! {
+                        #(#preamble)*;
+                    }
+                };
+
+                // Generates calls to `span_label` and similar functions based on the attributes
+                // on fields. Code for suggestions uses formatting machinery and the value of
+                // other fields - because any given field can be referenced multiple times, it
+                // should be accessed through a borrow. When passing fields to `set_arg` (which
+                // happens below) for Fluent, we want to move the data, so that has to happen
+                // in a separate pass over the fields.
+                let attrs = structure.each(|field_binding| {
+                    let field = field_binding.ast();
+                    let result = field.attrs.iter().map(|attr| {
+                        builder
+                            .generate_field_attr_code(
+                                attr,
+                                FieldInfo {
+                                    vis: &field.vis,
+                                    binding: field_binding,
+                                    ty: &field.ty,
+                                    span: &field.span(),
+                                },
+                            )
+                            .unwrap_or_else(|v| v.to_compile_error())
+                    });
+
+                    quote! { #(#result);* }
+                });
+
+                // When generating `set_arg` calls, move data rather than borrow it to avoid
+                // requiring clones - this must therefore be the last use of each field (for
+                // example, any formatting machinery that might refer to a field should be
+                // generated already).
+                structure.bind_with(|_| synstructure::BindStyle::Move);
+                let args = structure.each(|field_binding| {
+                    let field = field_binding.ast();
+                    // When a field has attributes like `#[label]` or `#[note]` then it doesn't
+                    // need to be passed as an argument to the diagnostic. But when a field has no
+                    // attributes then it must be passed as an argument to the diagnostic so that
+                    // it can be referred to by Fluent messages.
+                    if field.attrs.is_empty() {
+                        let diag = &builder.diag;
+                        let ident = field_binding.ast().ident.as_ref().unwrap();
+                        quote! {
+                            #diag.set_arg(
+                                stringify!(#ident),
+                                #field_binding.into_diagnostic_arg()
+                            );
+                        }
+                    } else {
+                        quote! {}
+                    }
+                });
+
+                let span = ast.span().unwrap();
+                let (diag, sess) = (&builder.diag, &builder.sess);
+                let init = match (builder.kind, builder.slug) {
+                    (None, _) => {
+                        span_err(span, "diagnostic kind not specified")
+                            .help("use the `#[error(...)]` attribute to create an error")
+                            .emit();
+                        return SessionDiagnosticDeriveError::ErrorHandled.to_compile_error();
+                    }
+                    (Some((kind, _)), None) => {
+                        span_err(span, "`slug` not specified")
+                            .help(&format!("use the `#[{}(slug = \"...\")]` attribute to set this diagnostic's slug", kind.descr()))
+                            .emit();
+                        return SessionDiagnosticDeriveError::ErrorHandled.to_compile_error();
+                    }
+                    (Some((SessionDiagnosticKind::Error, _)), Some((slug, _))) => {
+                        quote! {
+                            let mut #diag = #sess.struct_err(
+                                rustc_errors::DiagnosticMessage::fluent(#slug),
+                            );
+                        }
+                    }
+                    (Some((SessionDiagnosticKind::Warn, _)), Some((slug, _))) => {
+                        quote! {
+                            let mut #diag = #sess.struct_warn(
+                                rustc_errors::DiagnosticMessage::fluent(#slug),
+                            );
+                        }
+                    }
+                };
+
+                let implementation = quote! {
+                    #init
+                    #preamble
+                    match self {
+                        #attrs
+                    }
+                    match self {
+                        #args
+                    }
+                    #diag
+                };
+                let param_ty = match builder.kind {
+                    Some((SessionDiagnosticKind::Error, _)) => {
+                        quote! { rustc_errors::ErrorGuaranteed }
+                    }
+                    Some((SessionDiagnosticKind::Warn, _)) => quote! { () },
+                    _ => unreachable!(),
+                };
+
+                (implementation, param_ty)
+            } else {
+                span_err(
+                    ast.span().unwrap(),
+                    "`#[derive(SessionDiagnostic)]` can only be used on structs",
+                )
+                .emit();
+
+                let implementation = SessionDiagnosticDeriveError::ErrorHandled.to_compile_error();
+                let param_ty = quote! { rustc_errors::ErrorGuaranteed };
+                (implementation, param_ty)
+            }
+        };
+
+        let sess = &builder.sess;
+        structure.gen_impl(quote! {
+            gen impl<'__session_diagnostic_sess> rustc_session::SessionDiagnostic<'__session_diagnostic_sess, #param_ty>
+                    for @Self
+            {
+                fn into_diagnostic(
+                    self,
+                    #sess: &'__session_diagnostic_sess rustc_session::parse::ParseSess
+                ) -> rustc_errors::DiagnosticBuilder<'__session_diagnostic_sess, #param_ty> {
+                    use rustc_errors::IntoDiagnosticArg;
+                    #implementation
+                }
+            }
+        })
+    }
+}
+
+/// What kind of session diagnostic is being derived - an error or a warning?
+#[derive(Copy, Clone)]
+enum SessionDiagnosticKind {
+    /// `#[error(..)]`
+    Error,
+    /// `#[warn(..)]`
+    Warn,
+}
+
+impl SessionDiagnosticKind {
+    /// Returns human-readable string corresponding to the kind.
+    fn descr(&self) -> &'static str {
+        match self {
+            SessionDiagnosticKind::Error => "error",
+            SessionDiagnosticKind::Warn => "warning",
+        }
+    }
+}
+
+/// Tracks persistent information required for building up the individual calls to diagnostic
+/// methods for the final generated method. This is a separate struct to `SessionDiagnosticDerive`
+/// only to be able to destructure and split `self.builder` and the `self.structure` up to avoid a
+/// double mut borrow later on.
+struct SessionDiagnosticDeriveBuilder {
+    /// Name of the session parameter that's passed in to the `as_error` method.
+    sess: syn::Ident,
+    /// The identifier to use for the generated `DiagnosticBuilder` instance.
+    diag: syn::Ident,
+
+    /// Store a map of field name to its corresponding field. This is built on construction of the
+    /// derive builder.
+    fields: HashMap<String, TokenStream>,
+
+    /// Kind of diagnostic requested via the struct attribute.
+    kind: Option<(SessionDiagnosticKind, proc_macro::Span)>,
+    /// Slug is a mandatory part of the struct attribute as corresponds to the Fluent message that
+    /// has the actual diagnostic message.
+    slug: Option<(String, proc_macro::Span)>,
+    /// Error codes are a optional part of the struct attribute - this is only set to detect
+    /// multiple specifications.
+    code: Option<(String, proc_macro::Span)>,
+}
+
+impl HasFieldMap for SessionDiagnosticDeriveBuilder {
+    fn get_field_binding(&self, field: &String) -> Option<&TokenStream> {
+        self.fields.get(field)
+    }
+}
+
+impl SessionDiagnosticDeriveBuilder {
+    /// Establishes state in the `SessionDiagnosticDeriveBuilder` resulting from the struct
+    /// attributes like `#[error(..)#`, such as the diagnostic kind and slug. Generates
+    /// diagnostic builder calls for setting error code and creating note/help messages.
+    fn generate_structure_code(
+        &mut self,
+        attr: &Attribute,
+    ) -> Result<TokenStream, SessionDiagnosticDeriveError> {
+        let span = attr.span().unwrap();
+
+        let name = attr.path.segments.last().unwrap().ident.to_string();
+        let name = name.as_str();
+        let meta = attr.parse_meta()?;
+
+        if matches!(name, "help" | "note") && matches!(meta, Meta::Path(_) | Meta::NameValue(_)) {
+            let diag = &self.diag;
+            let slug = match &self.slug {
+                Some((slug, _)) => slug.as_str(),
+                None => throw_span_err!(
+                    span,
+                    &format!(
+                        "`#[{}{}]` must come after `#[error(..)]` or `#[warn(..)]`",
+                        name,
+                        match meta {
+                            Meta::Path(_) => "",
+                            Meta::NameValue(_) => " = ...",
+                            _ => unreachable!(),
+                        }
+                    )
+                ),
+            };
+            let id = match meta {
+                Meta::Path(..) => quote! { #name },
+                Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => {
+                    quote! { #s }
+                }
+                _ => unreachable!(),
+            };
+            let fn_name = proc_macro2::Ident::new(name, attr.span());
+
+            return Ok(quote! {
+                #diag.#fn_name(rustc_errors::DiagnosticMessage::fluent_attr(#slug, #id));
+            });
+        }
+
+        let nested = match meta {
+            Meta::List(MetaList { ref nested, .. }) => nested,
+            _ => throw_invalid_attr!(attr, &meta),
+        };
+
+        let kind = match name {
+            "error" => SessionDiagnosticKind::Error,
+            "warning" => SessionDiagnosticKind::Warn,
+            _ => throw_invalid_attr!(attr, &meta, |diag| {
+                diag.help("only `error` and `warning` are valid attributes")
+            }),
+        };
+        self.kind.set_once((kind, span));
+
+        let mut tokens = Vec::new();
+        for nested_attr in nested {
+            let meta = match nested_attr {
+                syn::NestedMeta::Meta(meta) => meta,
+                _ => throw_invalid_nested_attr!(attr, &nested_attr),
+            };
+
+            let path = meta.path();
+            let nested_name = path.segments.last().unwrap().ident.to_string();
+            match &meta {
+                // Struct attributes are only allowed to be applied once, and the diagnostic
+                // changes will be set in the initialisation code.
+                Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => {
+                    let span = s.span().unwrap();
+                    match nested_name.as_str() {
+                        "slug" => {
+                            self.slug.set_once((s.value(), span));
+                        }
+                        "code" => {
+                            self.code.set_once((s.value(), span));
+                            let (diag, code) = (&self.diag, &self.code.as_ref().map(|(v, _)| v));
+                            tokens.push(quote! {
+                                #diag.code(rustc_errors::DiagnosticId::Error(#code.to_string()));
+                            });
+                        }
+                        _ => invalid_nested_attr(attr, &nested_attr)
+                            .help("only `slug` and `code` are valid nested attributes")
+                            .emit(),
+                    }
+                }
+                _ => invalid_nested_attr(attr, &nested_attr).emit(),
+            }
+        }
+
+        Ok(tokens.drain(..).collect())
+    }
+
+    fn generate_field_attr_code(
+        &mut self,
+        attr: &syn::Attribute,
+        info: FieldInfo<'_>,
+    ) -> Result<TokenStream, SessionDiagnosticDeriveError> {
+        let field_binding = &info.binding.binding;
+        let option_ty = option_inner_ty(&info.ty);
+        let generated_code = self.generate_non_option_field_code(
+            attr,
+            FieldInfo {
+                vis: info.vis,
+                binding: info.binding,
+                ty: option_ty.unwrap_or(&info.ty),
+                span: info.span,
+            },
+        )?;
+
+        if option_ty.is_none() {
+            Ok(quote! { #generated_code })
+        } else {
+            Ok(quote! {
+                if let Some(#field_binding) = #field_binding {
+                    #generated_code
+                }
+            })
+        }
+    }
+
+    fn generate_non_option_field_code(
+        &mut self,
+        attr: &Attribute,
+        info: FieldInfo<'_>,
+    ) -> Result<TokenStream, SessionDiagnosticDeriveError> {
+        let diag = &self.diag;
+        let field_binding = &info.binding.binding;
+
+        let name = attr.path.segments.last().unwrap().ident.to_string();
+        let name = name.as_str();
+
+        let meta = attr.parse_meta()?;
+        match meta {
+            Meta::Path(_) => match name {
+                "skip_arg" => {
+                    // Don't need to do anything - by virtue of the attribute existing, the
+                    // `set_arg` call will not be generated.
+                    Ok(quote! {})
+                }
+                "primary_span" => {
+                    report_error_if_not_applied_to_span(attr, &info)?;
+                    Ok(quote! {
+                        #diag.set_span(*#field_binding);
+                    })
+                }
+                "label" | "note" | "help" => {
+                    report_error_if_not_applied_to_span(attr, &info)?;
+                    Ok(self.add_subdiagnostic(field_binding, name, name))
+                }
+                "subdiagnostic" => Ok(quote! { #diag.subdiagnostic(*#field_binding); }),
+                _ => throw_invalid_attr!(attr, &meta, |diag| {
+                    diag
+                        .help("only `skip_arg`, `primary_span`, `label`, `note`, `help` and `subdiagnostic` are valid field attributes")
+                }),
+            },
+            Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(ref s), .. }) => match name {
+                "label" | "note" | "help" => {
+                    report_error_if_not_applied_to_span(attr, &info)?;
+                    Ok(self.add_subdiagnostic(field_binding, name, &s.value()))
+                }
+                _ => throw_invalid_attr!(attr, &meta, |diag| {
+                    diag.help("only `label`, `note` and `help` are valid field attributes")
+                }),
+            },
+            Meta::List(MetaList { ref path, ref nested, .. }) => {
+                let name = path.segments.last().unwrap().ident.to_string();
+                let name = name.as_ref();
+
+                match name {
+                    "suggestion" | "suggestion_short" | "suggestion_hidden"
+                    | "suggestion_verbose" => (),
+                    _ => throw_invalid_attr!(attr, &meta, |diag| {
+                        diag
+                            .help("only `suggestion{,_short,_hidden,_verbose}` are valid field attributes")
+                    }),
+                };
+
+                let (span_field, mut applicability) = self.span_and_applicability_of_ty(info)?;
+
+                let mut msg = None;
+                let mut code = None;
+
+                for nested_attr in nested {
+                    let meta = match nested_attr {
+                        syn::NestedMeta::Meta(ref meta) => meta,
+                        syn::NestedMeta::Lit(_) => throw_invalid_nested_attr!(attr, &nested_attr),
+                    };
+
+                    let nested_name = meta.path().segments.last().unwrap().ident.to_string();
+                    let nested_name = nested_name.as_str();
+                    match meta {
+                        Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => {
+                            let span = meta.span().unwrap();
+                            match nested_name {
+                                "message" => {
+                                    msg = Some(s.value());
+                                }
+                                "code" => {
+                                    let formatted_str = self.build_format(&s.value(), s.span());
+                                    code = Some(formatted_str);
+                                }
+                                "applicability" => {
+                                    applicability = match applicability {
+                                        Some(v) => {
+                                            span_err(
+                                                span,
+                                                "applicability cannot be set in both the field and attribute"
+                                            ).emit();
+                                            Some(v)
+                                        }
+                                        None => match Applicability::from_str(&s.value()) {
+                                            Ok(v) => Some(quote! { #v }),
+                                            Err(()) => {
+                                                span_err(span, "invalid applicability").emit();
+                                                None
+                                            }
+                                        },
+                                    }
+                                }
+                                _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
+                                    diag.help(
+                                        "only `message`, `code` and `applicability` are valid field attributes",
+                                    )
+                                }),
+                            }
+                        }
+                        _ => throw_invalid_nested_attr!(attr, &nested_attr),
+                    }
+                }
+
+                let applicability = applicability
+                    .unwrap_or_else(|| quote!(rustc_errors::Applicability::Unspecified));
+
+                let method = format_ident!("span_{}", name);
+
+                let slug = self
+                    .slug
+                    .as_ref()
+                    .map(|(slug, _)| slug.as_str())
+                    .unwrap_or_else(|| "missing-slug");
+                let msg = msg.as_deref().unwrap_or("suggestion");
+                let msg = quote! { rustc_errors::DiagnosticMessage::fluent_attr(#slug, #msg) };
+                let code = code.unwrap_or_else(|| quote! { String::new() });
+
+                Ok(quote! { #diag.#method(#span_field, #msg, #code, #applicability); })
+            }
+            _ => throw_invalid_attr!(attr, &meta),
+        }
+    }
+
+    /// Adds a subdiagnostic by generating a `diag.span_$kind` call with the current slug and
+    /// `fluent_attr_identifier`.
+    fn add_subdiagnostic(
+        &self,
+        field_binding: &proc_macro2::Ident,
+        kind: &str,
+        fluent_attr_identifier: &str,
+    ) -> TokenStream {
+        let diag = &self.diag;
+
+        let slug =
+            self.slug.as_ref().map(|(slug, _)| slug.as_str()).unwrap_or_else(|| "missing-slug");
+        let fn_name = format_ident!("span_{}", kind);
+        quote! {
+            #diag.#fn_name(
+                *#field_binding,
+                rustc_errors::DiagnosticMessage::fluent_attr(#slug, #fluent_attr_identifier)
+            );
+        }
+    }
+
+    fn span_and_applicability_of_ty(
+        &self,
+        info: FieldInfo<'_>,
+    ) -> Result<(TokenStream, Option<TokenStream>), SessionDiagnosticDeriveError> {
+        match &info.ty {
+            // If `ty` is `Span` w/out applicability, then use `Applicability::Unspecified`.
+            ty @ Type::Path(..) if type_matches_path(ty, &["rustc_span", "Span"]) => {
+                let binding = &info.binding.binding;
+                Ok((quote!(*#binding), None))
+            }
+            // If `ty` is `(Span, Applicability)` then return tokens accessing those.
+            Type::Tuple(tup) => {
+                let mut span_idx = None;
+                let mut applicability_idx = None;
+
+                for (idx, elem) in tup.elems.iter().enumerate() {
+                    if type_matches_path(elem, &["rustc_span", "Span"]) {
+                        if span_idx.is_none() {
+                            span_idx = Some(syn::Index::from(idx));
+                        } else {
+                            throw_span_err!(
+                                info.span.unwrap(),
+                                "type of field annotated with `#[suggestion(...)]` contains more than one `Span`"
+                            );
+                        }
+                    } else if type_matches_path(elem, &["rustc_errors", "Applicability"]) {
+                        if applicability_idx.is_none() {
+                            applicability_idx = Some(syn::Index::from(idx));
+                        } else {
+                            throw_span_err!(
+                                info.span.unwrap(),
+                                "type of field annotated with `#[suggestion(...)]` contains more than one Applicability"
+                            );
+                        }
+                    }
+                }
+
+                if let Some(span_idx) = span_idx {
+                    let binding = &info.binding.binding;
+                    let span = quote!(#binding.#span_idx);
+                    let applicability = applicability_idx
+                        .map(|applicability_idx| quote!(#binding.#applicability_idx))
+                        .unwrap_or_else(|| quote!(rustc_errors::Applicability::Unspecified));
+
+                    return Ok((span, Some(applicability)));
+                }
+
+                throw_span_err!(info.span.unwrap(), "wrong types for suggestion", |diag| {
+                    diag.help("`#[suggestion(...)]` on a tuple field must be applied to fields of type `(Span, Applicability)`")
+                });
+            }
+            // If `ty` isn't a `Span` or `(Span, Applicability)` then emit an error.
+            _ => throw_span_err!(info.span.unwrap(), "wrong field type for suggestion", |diag| {
+                diag.help("`#[suggestion(...)]` should be applied to fields of type `Span` or `(Span, Applicability)`")
+            }),
+        }
+    }
+}
diff --git a/compiler/rustc_macros/src/diagnostics/error.rs b/compiler/rustc_macros/src/diagnostics/error.rs
new file mode 100644
index 00000000000..fd1dc2f3073
--- /dev/null
+++ b/compiler/rustc_macros/src/diagnostics/error.rs
@@ -0,0 +1,132 @@
+use proc_macro::{Diagnostic, Level, MultiSpan};
+use proc_macro2::TokenStream;
+use quote::quote;
+use syn::{spanned::Spanned, Attribute, Error as SynError, Meta, NestedMeta};
+
+#[derive(Debug)]
+pub(crate) enum SessionDiagnosticDeriveError {
+    SynError(SynError),
+    ErrorHandled,
+}
+
+impl SessionDiagnosticDeriveError {
+    pub(crate) fn to_compile_error(self) -> TokenStream {
+        match self {
+            SessionDiagnosticDeriveError::SynError(e) => e.to_compile_error(),
+            SessionDiagnosticDeriveError::ErrorHandled => {
+                // Return ! to avoid having to create a blank DiagnosticBuilder to return when an
+                // error has already been emitted to the compiler.
+                quote! {
+                    { unreachable!(); }
+                }
+            }
+        }
+    }
+}
+
+impl From<SynError> for SessionDiagnosticDeriveError {
+    fn from(e: SynError) -> Self {
+        SessionDiagnosticDeriveError::SynError(e)
+    }
+}
+
+/// Helper function for use with `throw_*` macros - constraints `$f` to an `impl FnOnce`.
+pub(crate) fn _throw_err(
+    diag: Diagnostic,
+    f: impl FnOnce(Diagnostic) -> Diagnostic,
+) -> SessionDiagnosticDeriveError {
+    f(diag).emit();
+    SessionDiagnosticDeriveError::ErrorHandled
+}
+
+/// Returns an error diagnostic on span `span` with msg `msg`.
+pub(crate) fn span_err(span: impl MultiSpan, msg: &str) -> Diagnostic {
+    Diagnostic::spanned(span, Level::Error, msg)
+}
+
+/// Emit a diagnostic on span `$span` with msg `$msg` (optionally performing additional decoration
+/// using the `FnOnce` passed in `diag`) and return `Err(ErrorHandled)`.
+///
+/// For methods that return a `Result<_, SessionDiagnosticDeriveError>`:
+macro_rules! throw_span_err {
+    ($span:expr, $msg:expr) => {{ throw_span_err!($span, $msg, |diag| diag) }};
+    ($span:expr, $msg:expr, $f:expr) => {{
+        let diag = span_err($span, $msg);
+        return Err(crate::diagnostics::error::_throw_err(diag, $f));
+    }};
+}
+
+pub(crate) use throw_span_err;
+
+/// Returns an error diagnostic for an invalid attribute.
+pub(crate) fn invalid_attr(attr: &Attribute, meta: &Meta) -> Diagnostic {
+    let span = attr.span().unwrap();
+    let name = attr.path.segments.last().unwrap().ident.to_string();
+    let name = name.as_str();
+
+    match meta {
+        Meta::Path(_) => span_err(span, &format!("`#[{}]` is not a valid attribute", name)),
+        Meta::NameValue(_) => {
+            span_err(span, &format!("`#[{} = ...]` is not a valid attribute", name))
+        }
+        Meta::List(_) => span_err(span, &format!("`#[{}(...)]` is not a valid attribute", name)),
+    }
+}
+
+/// Emit a error diagnostic for an invalid attribute (optionally performing additional decoration
+/// using the `FnOnce` passed in `diag`) and return `Err(ErrorHandled)`.
+///
+/// For methods that return a `Result<_, SessionDiagnosticDeriveError>`:
+macro_rules! throw_invalid_attr {
+    ($attr:expr, $meta:expr) => {{ throw_invalid_attr!($attr, $meta, |diag| diag) }};
+    ($attr:expr, $meta:expr, $f:expr) => {{
+        let diag = crate::diagnostics::error::invalid_attr($attr, $meta);
+        return Err(crate::diagnostics::error::_throw_err(diag, $f));
+    }};
+}
+
+pub(crate) use throw_invalid_attr;
+
+/// Returns an error diagnostic for an invalid nested attribute.
+pub(crate) fn invalid_nested_attr(attr: &Attribute, nested: &NestedMeta) -> Diagnostic {
+    let name = attr.path.segments.last().unwrap().ident.to_string();
+    let name = name.as_str();
+
+    let span = nested.span().unwrap();
+    let meta = match nested {
+        syn::NestedMeta::Meta(meta) => meta,
+        syn::NestedMeta::Lit(_) => {
+            return span_err(span, &format!("`#[{}(\"...\")]` is not a valid attribute", name));
+        }
+    };
+
+    let span = meta.span().unwrap();
+    let nested_name = meta.path().segments.last().unwrap().ident.to_string();
+    let nested_name = nested_name.as_str();
+    match meta {
+        Meta::NameValue(..) => span_err(
+            span,
+            &format!("`#[{}({} = ...)]` is not a valid attribute", name, nested_name),
+        ),
+        Meta::Path(..) => {
+            span_err(span, &format!("`#[{}({})]` is not a valid attribute", name, nested_name))
+        }
+        Meta::List(..) => {
+            span_err(span, &format!("`#[{}({}(...))]` is not a valid attribute", name, nested_name))
+        }
+    }
+}
+
+/// Emit a error diagnostic for an invalid nested attribute (optionally performing additional
+/// decoration using the `FnOnce` passed in `diag`) and return `Err(ErrorHandled)`.
+///
+/// For methods that return a `Result<_, SessionDiagnosticDeriveError>`:
+macro_rules! throw_invalid_nested_attr {
+    ($attr:expr, $nested_attr:expr) => {{ throw_invalid_nested_attr!($attr, $nested_attr, |diag| diag) }};
+    ($attr:expr, $nested_attr:expr, $f:expr) => {{
+        let diag = crate::diagnostics::error::invalid_nested_attr($attr, $nested_attr);
+        return Err(crate::diagnostics::error::_throw_err(diag, $f));
+    }};
+}
+
+pub(crate) use throw_invalid_nested_attr;
diff --git a/compiler/rustc_macros/src/diagnostics/mod.rs b/compiler/rustc_macros/src/diagnostics/mod.rs
new file mode 100644
index 00000000000..73ad415d6c3
--- /dev/null
+++ b/compiler/rustc_macros/src/diagnostics/mod.rs
@@ -0,0 +1,114 @@
+mod diagnostic;
+mod error;
+mod subdiagnostic;
+mod utils;
+
+use diagnostic::SessionDiagnosticDerive;
+use proc_macro2::TokenStream;
+use quote::format_ident;
+use subdiagnostic::SessionSubdiagnosticDerive;
+use synstructure::Structure;
+
+/// Implements `#[derive(SessionDiagnostic)]`, which allows for errors to be specified as a struct,
+/// independent from the actual diagnostics emitting code.
+///
+/// ```ignore (pseudo-rust)
+/// # extern crate rustc_errors;
+/// # use rustc_errors::Applicability;
+/// # extern crate rustc_span;
+/// # use rustc_span::{symbol::Ident, Span};
+/// # extern crate rust_middle;
+/// # use rustc_middle::ty::Ty;
+/// #[derive(SessionDiagnostic)]
+/// #[error(code = "E0505", slug = "borrowck-move-out-of-borrow")]
+/// pub struct MoveOutOfBorrowError<'tcx> {
+///     pub name: Ident,
+///     pub ty: Ty<'tcx>,
+///     #[primary_span]
+///     #[label]
+///     pub span: Span,
+///     #[label = "first-borrow-label"]
+///     pub first_borrow_span: Span,
+///     #[suggestion(code = "{name}.clone()")]
+///     pub clone_sugg: Option<(Span, Applicability)>
+/// }
+/// ```
+///
+/// ```fluent
+/// move-out-of-borrow = cannot move out of {$name} because it is borrowed
+///     .label = cannot move out of borrow
+///     .first-borrow-label = `{$ty}` first borrowed here
+///     .suggestion = consider cloning here
+/// ```
+///
+/// Then, later, to emit the error:
+///
+/// ```ignore (pseudo-rust)
+/// sess.emit_err(MoveOutOfBorrowError {
+///     expected,
+///     actual,
+///     span,
+///     first_borrow_span,
+///     clone_sugg: Some(suggestion, Applicability::MachineApplicable),
+/// });
+/// ```
+///
+/// See rustc dev guide for more examples on using the `#[derive(SessionDiagnostic)]`:
+/// <https://rustc-dev-guide.rust-lang.org/diagnostics/sessiondiagnostic.html>
+pub fn session_diagnostic_derive(s: Structure<'_>) -> TokenStream {
+    // Names for the diagnostic we build and the session we build it from.
+    let diag = format_ident!("diag");
+    let sess = format_ident!("sess");
+
+    SessionDiagnosticDerive::new(diag, sess, s).into_tokens()
+}
+
+/// Implements `#[derive(SessionSubdiagnostic)]`, which allows for labels, notes, helps and
+/// suggestions to be specified as a structs or enums, independent from the actual diagnostics
+/// emitting code or diagnostic derives.
+///
+/// ```ignore (pseudo-rust)
+/// #[derive(SessionSubdiagnostic)]
+/// pub enum ExpectedIdentifierLabel<'tcx> {
+///     #[label(slug = "parser-expected-identifier")]
+///     WithoutFound {
+///         #[primary_span]
+///         span: Span,
+///     }
+///     #[label(slug = "parser-expected-identifier-found")]
+///     WithFound {
+///         #[primary_span]
+///         span: Span,
+///         found: String,
+///     }
+/// }
+///
+/// #[derive(SessionSubdiagnostic)]
+/// #[suggestion_verbose(slug = "parser-raw-identifier")]
+/// pub struct RawIdentifierSuggestion<'tcx> {
+///     #[primary_span]
+///     span: Span,
+///     #[applicability]
+///     applicability: Applicability,
+///     ident: Ident,
+/// }
+/// ```
+///
+/// ```fluent
+/// parser-expected-identifier = expected identifier
+///
+/// parser-expected-identifier-found = expected identifier, found {$found}
+///
+/// parser-raw-identifier = escape `{$ident}` to use it as an identifier
+/// ```
+///
+/// Then, later, to add the subdiagnostic:
+///
+/// ```ignore (pseudo-rust)
+/// diag.subdiagnostic(ExpectedIdentifierLabel::WithoutFound { span });
+///
+/// diag.subdiagnostic(RawIdentifierSuggestion { span, applicability, ident });
+/// ```
+pub fn session_subdiagnostic_derive(s: Structure<'_>) -> TokenStream {
+    SessionSubdiagnosticDerive::new(s).into_tokens()
+}
diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
new file mode 100644
index 00000000000..961b42f424f
--- /dev/null
+++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
@@ -0,0 +1,437 @@
+#![deny(unused_must_use)]
+
+use crate::diagnostics::error::{
+    span_err, throw_invalid_attr, throw_invalid_nested_attr, throw_span_err,
+    SessionDiagnosticDeriveError,
+};
+use crate::diagnostics::utils::{
+    option_inner_ty, report_error_if_not_applied_to_applicability,
+    report_error_if_not_applied_to_span, Applicability, FieldInfo, HasFieldMap, SetOnce,
+};
+use proc_macro2::TokenStream;
+use quote::{format_ident, quote};
+use std::collections::HashMap;
+use std::fmt;
+use std::str::FromStr;
+use syn::{spanned::Spanned, Meta, MetaList, MetaNameValue};
+use synstructure::{BindingInfo, Structure, VariantInfo};
+
+/// Which kind of suggestion is being created?
+#[derive(Clone, Copy)]
+enum SubdiagnosticSuggestionKind {
+    /// `#[suggestion]`
+    Normal,
+    /// `#[suggestion_short]`
+    Short,
+    /// `#[suggestion_hidden]`
+    Hidden,
+    /// `#[suggestion_verbose]`
+    Verbose,
+}
+
+/// Which kind of subdiagnostic is being created from a variant?
+#[derive(Clone, Copy)]
+enum SubdiagnosticKind {
+    /// `#[label(...)]`
+    Label,
+    /// `#[note(...)]`
+    Note,
+    /// `#[help(...)]`
+    Help,
+    /// `#[suggestion{,_short,_hidden,_verbose}]`
+    Suggestion(SubdiagnosticSuggestionKind),
+}
+
+impl FromStr for SubdiagnosticKind {
+    type Err = ();
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match s {
+            "label" => Ok(SubdiagnosticKind::Label),
+            "note" => Ok(SubdiagnosticKind::Note),
+            "help" => Ok(SubdiagnosticKind::Help),
+            "suggestion" => Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal)),
+            "suggestion_short" => {
+                Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Short))
+            }
+            "suggestion_hidden" => {
+                Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Hidden))
+            }
+            "suggestion_verbose" => {
+                Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Verbose))
+            }
+            _ => Err(()),
+        }
+    }
+}
+
+impl quote::IdentFragment for SubdiagnosticKind {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            SubdiagnosticKind::Label => write!(f, "label"),
+            SubdiagnosticKind::Note => write!(f, "note"),
+            SubdiagnosticKind::Help => write!(f, "help"),
+            SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal) => {
+                write!(f, "suggestion")
+            }
+            SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Short) => {
+                write!(f, "suggestion_short")
+            }
+            SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Hidden) => {
+                write!(f, "suggestion_hidden")
+            }
+            SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Verbose) => {
+                write!(f, "suggestion_verbose")
+            }
+        }
+    }
+
+    fn span(&self) -> Option<proc_macro2::Span> {
+        None
+    }
+}
+
+/// The central struct for constructing the `add_to_diagnostic` method from an annotated struct.
+pub(crate) struct SessionSubdiagnosticDerive<'a> {
+    structure: Structure<'a>,
+    diag: syn::Ident,
+}
+
+impl<'a> SessionSubdiagnosticDerive<'a> {
+    pub(crate) fn new(structure: Structure<'a>) -> Self {
+        let diag = format_ident!("diag");
+        Self { structure, diag }
+    }
+
+    pub(crate) fn into_tokens(self) -> TokenStream {
+        let SessionSubdiagnosticDerive { mut structure, diag } = self;
+        let implementation = {
+            let ast = structure.ast();
+            let span = ast.span().unwrap();
+            match ast.data {
+                syn::Data::Struct(..) | syn::Data::Enum(..) => (),
+                syn::Data::Union(..) => {
+                    span_err(
+                        span,
+                        "`#[derive(SessionSubdiagnostic)]` can only be used on structs and enums",
+                    );
+                }
+            }
+
+            if matches!(ast.data, syn::Data::Enum(..)) {
+                for attr in &ast.attrs {
+                    span_err(
+                        attr.span().unwrap(),
+                        "unsupported type attribute for subdiagnostic enum",
+                    )
+                    .emit();
+                }
+            }
+
+            structure.bind_with(|_| synstructure::BindStyle::Move);
+            let variants_ = structure.each_variant(|variant| {
+                // Build the mapping of field names to fields. This allows attributes to peek
+                // values from other fields.
+                let mut fields_map = HashMap::new();
+                for binding in variant.bindings() {
+                    let field = binding.ast();
+                    if let Some(ident) = &field.ident {
+                        fields_map.insert(ident.to_string(), quote! { #binding });
+                    }
+                }
+
+                let mut builder = SessionSubdiagnosticDeriveBuilder {
+                    diag: &diag,
+                    variant,
+                    span,
+                    fields: fields_map,
+                    kind: None,
+                    slug: None,
+                    code: None,
+                    span_field: None,
+                    applicability: None,
+                };
+                builder.into_tokens().unwrap_or_else(|v| v.to_compile_error())
+            });
+
+            quote! {
+                match self {
+                    #variants_
+                }
+            }
+        };
+
+        let ret = structure.gen_impl(quote! {
+            gen impl rustc_errors::AddSubdiagnostic for @Self {
+                fn add_to_diagnostic(self, #diag: &mut rustc_errors::Diagnostic) {
+                    use rustc_errors::{Applicability, IntoDiagnosticArg};
+                    #implementation
+                }
+            }
+        });
+        ret
+    }
+}
+
+/// Tracks persistent information required for building up the call to add to the diagnostic
+/// for the final generated method. This is a separate struct to `SessionSubdiagnosticDerive`
+/// only to be able to destructure and split `self.builder` and the `self.structure` up to avoid a
+/// double mut borrow later on.
+struct SessionSubdiagnosticDeriveBuilder<'a> {
+    /// The identifier to use for the generated `DiagnosticBuilder` instance.
+    diag: &'a syn::Ident,
+
+    /// Info for the current variant (or the type if not an enum).
+    variant: &'a VariantInfo<'a>,
+    /// Span for the entire type.
+    span: proc_macro::Span,
+
+    /// Store a map of field name to its corresponding field. This is built on construction of the
+    /// derive builder.
+    fields: HashMap<String, TokenStream>,
+
+    /// Subdiagnostic kind of the type/variant.
+    kind: Option<(SubdiagnosticKind, proc_macro::Span)>,
+
+    /// Slug of the subdiagnostic - corresponds to the Fluent identifier for the message - from the
+    /// `#[kind(slug = "...")]` attribute on the type or variant.
+    slug: Option<(String, proc_macro::Span)>,
+    /// If a suggestion, the code to suggest as a replacement - from the `#[kind(code = "...")]`
+    /// attribute on the type or variant.
+    code: Option<(TokenStream, proc_macro::Span)>,
+
+    /// Identifier for the binding to the `#[primary_span]` field.
+    span_field: Option<(proc_macro2::Ident, proc_macro::Span)>,
+    /// If a suggestion, the identifier for the binding to the `#[applicability]` field or a
+    /// `rustc_errors::Applicability::*` variant directly.
+    applicability: Option<(TokenStream, proc_macro::Span)>,
+}
+
+impl<'a> HasFieldMap for SessionSubdiagnosticDeriveBuilder<'a> {
+    fn get_field_binding(&self, field: &String) -> Option<&TokenStream> {
+        self.fields.get(field)
+    }
+}
+
+impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
+    fn identify_kind(&mut self) -> Result<(), SessionDiagnosticDeriveError> {
+        for attr in self.variant.ast().attrs {
+            let span = attr.span().unwrap();
+
+            let name = attr.path.segments.last().unwrap().ident.to_string();
+            let name = name.as_str();
+
+            let meta = attr.parse_meta()?;
+            let kind = match meta {
+                Meta::List(MetaList { ref nested, .. }) => {
+                    for nested_attr in nested {
+                        let meta = match nested_attr {
+                            syn::NestedMeta::Meta(ref meta) => meta,
+                            _ => throw_invalid_nested_attr!(attr, &nested_attr),
+                        };
+
+                        let span = meta.span().unwrap();
+                        let nested_name = meta.path().segments.last().unwrap().ident.to_string();
+                        let nested_name = nested_name.as_str();
+
+                        match meta {
+                            Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => {
+                                match nested_name {
+                                    "code" => {
+                                        let formatted_str = self.build_format(&s.value(), s.span());
+                                        self.code.set_once((formatted_str, span));
+                                    }
+                                    "slug" => self.slug.set_once((s.value(), span)),
+                                    "applicability" => {
+                                        let value = match Applicability::from_str(&s.value()) {
+                                            Ok(v) => v,
+                                            Err(()) => {
+                                                span_err(span, "invalid applicability").emit();
+                                                Applicability::Unspecified
+                                            }
+                                        };
+                                        self.applicability.set_once((quote! { #value }, span));
+                                    }
+                                    _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
+                                        diag.help("only `code`, `slug` and `applicability` are valid nested attributes")
+                                    }),
+                                }
+                            }
+                            _ => throw_invalid_nested_attr!(attr, &nested_attr),
+                        }
+                    }
+
+                    let Ok(kind) = SubdiagnosticKind::from_str(name) else {
+                        throw_invalid_attr!(attr, &meta)
+                    };
+
+                    kind
+                }
+                _ => throw_invalid_attr!(attr, &meta),
+            };
+
+            if matches!(
+                kind,
+                SubdiagnosticKind::Label | SubdiagnosticKind::Help | SubdiagnosticKind::Note
+            ) && self.code.is_some()
+            {
+                throw_span_err!(
+                    span,
+                    &format!("`code` is not a valid nested attribute of a `{}` attribute", name)
+                );
+            }
+
+            if self.slug.is_none() {
+                throw_span_err!(
+                    span,
+                    &format!("`slug` must be set in a `#[{}(...)]` attribute", name)
+                );
+            }
+
+            self.kind.set_once((kind, span));
+        }
+
+        Ok(())
+    }
+
+    fn generate_field_code(
+        &mut self,
+        binding: &BindingInfo<'_>,
+        is_suggestion: bool,
+    ) -> Result<TokenStream, SessionDiagnosticDeriveError> {
+        let ast = binding.ast();
+
+        let option_ty = option_inner_ty(&ast.ty);
+        let info = FieldInfo {
+            vis: &ast.vis,
+            binding: binding,
+            ty: option_ty.unwrap_or(&ast.ty),
+            span: &ast.span(),
+        };
+
+        for attr in &ast.attrs {
+            let name = attr.path.segments.last().unwrap().ident.to_string();
+            let name = name.as_str();
+            let span = attr.span().unwrap();
+
+            let meta = attr.parse_meta()?;
+            match meta {
+                Meta::Path(_) => match name {
+                    "primary_span" => {
+                        report_error_if_not_applied_to_span(attr, &info)?;
+                        self.span_field.set_once((binding.binding.clone(), span));
+                        return Ok(quote! {});
+                    }
+                    "applicability" if is_suggestion => {
+                        report_error_if_not_applied_to_applicability(attr, &info)?;
+                        let binding = binding.binding.clone();
+                        self.applicability.set_once((quote! { #binding }, span));
+                        return Ok(quote! {});
+                    }
+                    "applicability" => {
+                        span_err(span, "`#[applicability]` is only valid on suggestions").emit();
+                        return Ok(quote! {});
+                    }
+                    "skip_arg" => {
+                        return Ok(quote! {});
+                    }
+                    _ => throw_invalid_attr!(attr, &meta, |diag| {
+                        diag.help("only `primary_span`, `applicability` and `skip_arg` are valid field attributes")
+                    }),
+                },
+                _ => throw_invalid_attr!(attr, &meta),
+            }
+        }
+
+        let ident = ast.ident.as_ref().unwrap();
+
+        let diag = &self.diag;
+        let generated = quote! {
+            #diag.set_arg(
+                stringify!(#ident),
+                #binding.into_diagnostic_arg()
+            );
+        };
+
+        if option_ty.is_none() {
+            Ok(quote! { #generated })
+        } else {
+            Ok(quote! {
+                if let Some(#binding) = #binding {
+                    #generated
+                }
+            })
+        }
+    }
+
+    fn into_tokens(&mut self) -> Result<TokenStream, SessionDiagnosticDeriveError> {
+        self.identify_kind()?;
+        let Some(kind) = self.kind.map(|(kind, _)| kind) else {
+            throw_span_err!(
+                self.variant.ast().ident.span().unwrap(),
+                "subdiagnostic kind not specified"
+            );
+        };
+
+        let is_suggestion = matches!(kind, SubdiagnosticKind::Suggestion(_));
+
+        let mut args = TokenStream::new();
+        for binding in self.variant.bindings() {
+            let arg = self
+                .generate_field_code(binding, is_suggestion)
+                .unwrap_or_else(|v| v.to_compile_error());
+            args.extend(arg);
+        }
+
+        // Missing slug errors will already have been reported.
+        let slug = self.slug.as_ref().map(|(slug, _)| &**slug).unwrap_or("missing-slug");
+        let code = match self.code.as_ref() {
+            Some((code, _)) => Some(quote! { #code }),
+            None if is_suggestion => {
+                span_err(self.span, "suggestion without `code = \"...\"`").emit();
+                Some(quote! { /* macro error */ "..." })
+            }
+            None => None,
+        };
+
+        let span_field = self.span_field.as_ref().map(|(span, _)| span);
+        let applicability = match self.applicability.clone() {
+            Some((applicability, _)) => Some(applicability),
+            None if is_suggestion => {
+                span_err(self.span, "suggestion without `applicability`").emit();
+                Some(quote! { rustc_errors::Applicability::Unspecified })
+            }
+            None => None,
+        };
+
+        let diag = &self.diag;
+        let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind);
+        let message = quote! { rustc_errors::DiagnosticMessage::fluent(#slug) };
+        let call = if matches!(kind, SubdiagnosticKind::Suggestion(..)) {
+            if let Some(span) = span_field {
+                quote! { #diag.#name(#span, #message, #code, #applicability); }
+            } else {
+                span_err(self.span, "suggestion without `#[primary_span]` field").emit();
+                quote! { unreachable!(); }
+            }
+        } else if matches!(kind, SubdiagnosticKind::Label) {
+            if let Some(span) = span_field {
+                quote! { #diag.#name(#span, #message); }
+            } else {
+                span_err(self.span, "label without `#[primary_span]` field").emit();
+                quote! { unreachable!(); }
+            }
+        } else {
+            if let Some(span) = span_field {
+                quote! { #diag.#name(#span, #message); }
+            } else {
+                quote! { #diag.#name(#message); }
+            }
+        };
+
+        Ok(quote! {
+            #call
+            #args
+        })
+    }
+}
diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs
new file mode 100644
index 00000000000..1f36af0a20b
--- /dev/null
+++ b/compiler/rustc_macros/src/diagnostics/utils.rs
@@ -0,0 +1,267 @@
+use crate::diagnostics::error::{span_err, throw_span_err, SessionDiagnosticDeriveError};
+use proc_macro::Span;
+use proc_macro2::TokenStream;
+use quote::{format_ident, quote};
+use std::collections::BTreeSet;
+use std::str::FromStr;
+use syn::{spanned::Spanned, Attribute, Meta, Type, Visibility};
+use synstructure::BindingInfo;
+
+/// Checks whether the type name of `ty` matches `name`.
+///
+/// Given some struct at `a::b::c::Foo`, this will return true for `c::Foo`, `b::c::Foo`, or
+/// `a::b::c::Foo`. This reasonably allows qualified names to be used in the macro.
+pub(crate) fn type_matches_path(ty: &Type, name: &[&str]) -> bool {
+    if let Type::Path(ty) = ty {
+        ty.path
+            .segments
+            .iter()
+            .map(|s| s.ident.to_string())
+            .rev()
+            .zip(name.iter().rev())
+            .all(|(x, y)| &x.as_str() == y)
+    } else {
+        false
+    }
+}
+
+/// Reports an error if the field's type is not `Applicability`.
+fn report_error_if_not_applied_to_ty(
+    attr: &Attribute,
+    info: &FieldInfo<'_>,
+    path: &[&str],
+    ty_name: &str,
+) -> Result<(), SessionDiagnosticDeriveError> {
+    if !type_matches_path(&info.ty, path) {
+        let name = attr.path.segments.last().unwrap().ident.to_string();
+        let name = name.as_str();
+        let meta = attr.parse_meta()?;
+
+        throw_span_err!(
+            attr.span().unwrap(),
+            &format!(
+                "the `#[{}{}]` attribute can only be applied to fields of type `{}`",
+                name,
+                match meta {
+                    Meta::Path(_) => "",
+                    Meta::NameValue(_) => " = ...",
+                    Meta::List(_) => "(...)",
+                },
+                ty_name
+            )
+        );
+    }
+
+    Ok(())
+}
+
+/// Reports an error if the field's type is not `Applicability`.
+pub(crate) fn report_error_if_not_applied_to_applicability(
+    attr: &Attribute,
+    info: &FieldInfo<'_>,
+) -> Result<(), SessionDiagnosticDeriveError> {
+    report_error_if_not_applied_to_ty(
+        attr,
+        info,
+        &["rustc_errors", "Applicability"],
+        "Applicability",
+    )
+}
+
+/// Reports an error if the field's type is not `Span`.
+pub(crate) fn report_error_if_not_applied_to_span(
+    attr: &Attribute,
+    info: &FieldInfo<'_>,
+) -> Result<(), SessionDiagnosticDeriveError> {
+    report_error_if_not_applied_to_ty(attr, info, &["rustc_span", "Span"], "Span")
+}
+
+/// If `ty` is an Option, returns `Some(inner type)`, otherwise returns `None`.
+pub(crate) fn option_inner_ty(ty: &Type) -> Option<&Type> {
+    if type_matches_path(ty, &["std", "option", "Option"]) {
+        if let Type::Path(ty_path) = ty {
+            let path = &ty_path.path;
+            let ty = path.segments.iter().last().unwrap();
+            if let syn::PathArguments::AngleBracketed(bracketed) = &ty.arguments {
+                if bracketed.args.len() == 1 {
+                    if let syn::GenericArgument::Type(ty) = &bracketed.args[0] {
+                        return Some(ty);
+                    }
+                }
+            }
+        }
+    }
+    None
+}
+
+/// Field information passed to the builder. Deliberately omits attrs to discourage the
+/// `generate_*` methods from walking the attributes themselves.
+pub(crate) struct FieldInfo<'a> {
+    pub(crate) vis: &'a Visibility,
+    pub(crate) binding: &'a BindingInfo<'a>,
+    pub(crate) ty: &'a Type,
+    pub(crate) span: &'a proc_macro2::Span,
+}
+
+/// Small helper trait for abstracting over `Option` fields that contain a value and a `Span`
+/// for error reporting if they are set more than once.
+pub(crate) trait SetOnce<T> {
+    fn set_once(&mut self, value: T);
+}
+
+impl<T> SetOnce<(T, Span)> for Option<(T, Span)> {
+    fn set_once(&mut self, (value, span): (T, Span)) {
+        match self {
+            None => {
+                *self = Some((value, span));
+            }
+            Some((_, prev_span)) => {
+                span_err(span, "specified multiple times")
+                    .span_note(*prev_span, "previously specified here")
+                    .emit();
+            }
+        }
+    }
+}
+
+pub(crate) trait HasFieldMap {
+    /// Returns the binding for the field with the given name, if it exists on the type.
+    fn get_field_binding(&self, field: &String) -> Option<&TokenStream>;
+
+    /// In the strings in the attributes supplied to this macro, we want callers to be able to
+    /// reference fields in the format string. For example:
+    ///
+    /// ```ignore (not-usage-example)
+    /// /// Suggest `==` when users wrote `===`.
+    /// #[suggestion(slug = "parser-not-javascript-eq", code = "{lhs} == {rhs}")]
+    /// struct NotJavaScriptEq {
+    ///     #[primary_span]
+    ///     span: Span,
+    ///     lhs: Ident,
+    ///     rhs: Ident,
+    /// }
+    /// ```
+    ///
+    /// We want to automatically pick up that `{lhs}` refers `self.lhs` and `{rhs}` refers to
+    /// `self.rhs`, then generate this call to `format!`:
+    ///
+    /// ```ignore (not-usage-example)
+    /// format!("{lhs} == {rhs}", lhs = self.lhs, rhs = self.rhs)
+    /// ```
+    ///
+    /// This function builds the entire call to `format!`.
+    fn build_format(&self, input: &str, span: proc_macro2::Span) -> TokenStream {
+        // This set is used later to generate the final format string. To keep builds reproducible,
+        // the iteration order needs to be deterministic, hence why we use a `BTreeSet` here
+        // instead of a `HashSet`.
+        let mut referenced_fields: BTreeSet<String> = BTreeSet::new();
+
+        // At this point, we can start parsing the format string.
+        let mut it = input.chars().peekable();
+
+        // Once the start of a format string has been found, process the format string and spit out
+        // the referenced fields. Leaves `it` sitting on the closing brace of the format string, so
+        // the next call to `it.next()` retrieves the next character.
+        while let Some(c) = it.next() {
+            if c == '{' && *it.peek().unwrap_or(&'\0') != '{' {
+                let mut eat_argument = || -> Option<String> {
+                    let mut result = String::new();
+                    // Format specifiers look like:
+                    //
+                    //   format   := '{' [ argument ] [ ':' format_spec ] '}' .
+                    //
+                    // Therefore, we only need to eat until ':' or '}' to find the argument.
+                    while let Some(c) = it.next() {
+                        result.push(c);
+                        let next = *it.peek().unwrap_or(&'\0');
+                        if next == '}' {
+                            break;
+                        } else if next == ':' {
+                            // Eat the ':' character.
+                            assert_eq!(it.next().unwrap(), ':');
+                            break;
+                        }
+                    }
+                    // Eat until (and including) the matching '}'
+                    while it.next()? != '}' {
+                        continue;
+                    }
+                    Some(result)
+                };
+
+                if let Some(referenced_field) = eat_argument() {
+                    referenced_fields.insert(referenced_field);
+                }
+            }
+        }
+
+        // At this point, `referenced_fields` contains a set of the unique fields that were
+        // referenced in the format string. Generate the corresponding "x = self.x" format
+        // string parameters:
+        let args = referenced_fields.into_iter().map(|field: String| {
+            let field_ident = format_ident!("{}", field);
+            let value = match self.get_field_binding(&field) {
+                Some(value) => value.clone(),
+                // This field doesn't exist. Emit a diagnostic.
+                None => {
+                    span_err(
+                        span.unwrap(),
+                        &format!("`{}` doesn't refer to a field on this type", field),
+                    )
+                    .emit();
+                    quote! {
+                        "{#field}"
+                    }
+                }
+            };
+            quote! {
+                #field_ident = #value
+            }
+        });
+        quote! {
+            format!(#input #(,#args)*)
+        }
+    }
+}
+
+/// `Applicability` of a suggestion - mirrors `rustc_errors::Applicability` - and used to represent
+/// the user's selection of applicability if specified in an attribute.
+pub(crate) enum Applicability {
+    MachineApplicable,
+    MaybeIncorrect,
+    HasPlaceholders,
+    Unspecified,
+}
+
+impl FromStr for Applicability {
+    type Err = ();
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match s {
+            "machine-applicable" => Ok(Applicability::MachineApplicable),
+            "maybe-incorrect" => Ok(Applicability::MaybeIncorrect),
+            "has-placeholders" => Ok(Applicability::HasPlaceholders),
+            "unspecified" => Ok(Applicability::Unspecified),
+            _ => Err(()),
+        }
+    }
+}
+
+impl quote::ToTokens for Applicability {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+        tokens.extend(match self {
+            Applicability::MachineApplicable => {
+                quote! { rustc_errors::Applicability::MachineApplicable }
+            }
+            Applicability::MaybeIncorrect => {
+                quote! { rustc_errors::Applicability::MaybeIncorrect }
+            }
+            Applicability::HasPlaceholders => {
+                quote! { rustc_errors::Applicability::HasPlaceholders }
+            }
+            Applicability::Unspecified => {
+                quote! { rustc_errors::Applicability::Unspecified }
+            }
+        });
+    }
+}
diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs
index b53ef816135..b01e01414e8 100644
--- a/compiler/rustc_macros/src/lib.rs
+++ b/compiler/rustc_macros/src/lib.rs
@@ -1,5 +1,6 @@
-#![feature(proc_macro_diagnostic)]
 #![feature(allow_internal_unstable)]
+#![feature(let_else)]
+#![feature(proc_macro_diagnostic)]
 #![allow(rustc::default_hash_types)]
 #![recursion_limit = "128"]
 
@@ -7,12 +8,12 @@ use synstructure::decl_derive;
 
 use proc_macro::TokenStream;
 
+mod diagnostics;
 mod hash_stable;
 mod lift;
 mod newtype;
 mod query;
 mod serialize;
-mod session_diagnostic;
 mod symbols;
 mod type_foldable;
 
@@ -72,8 +73,24 @@ decl_derive!(
         skip_arg,
         primary_span,
         label,
+        subdiagnostic,
         suggestion,
         suggestion_short,
         suggestion_hidden,
-        suggestion_verbose)] => session_diagnostic::session_diagnostic_derive
+        suggestion_verbose)] => diagnostics::session_diagnostic_derive
+);
+decl_derive!(
+    [SessionSubdiagnostic, attributes(
+        // struct/variant attributes
+        label,
+        help,
+        note,
+        suggestion,
+        suggestion_short,
+        suggestion_hidden,
+        suggestion_verbose,
+        // field attributes
+        skip_arg,
+        primary_span,
+        applicability)] => diagnostics::session_subdiagnostic_derive
 );
diff --git a/compiler/rustc_macros/src/session_diagnostic.rs b/compiler/rustc_macros/src/session_diagnostic.rs
deleted file mode 100644
index 46f698f6f9b..00000000000
--- a/compiler/rustc_macros/src/session_diagnostic.rs
+++ /dev/null
@@ -1,956 +0,0 @@
-#![deny(unused_must_use)]
-use proc_macro::Diagnostic;
-use quote::{format_ident, quote};
-use syn::spanned::Spanned;
-
-use std::collections::{BTreeSet, HashMap};
-
-/// Implements `#[derive(SessionDiagnostic)]`, which allows for errors to be specified as a struct,
-/// independent from the actual diagnostics emitting code.
-///
-/// ```ignore (pseudo-rust)
-/// # extern crate rustc_errors;
-/// # use rustc_errors::Applicability;
-/// # extern crate rustc_span;
-/// # use rustc_span::{symbol::Ident, Span};
-/// # extern crate rust_middle;
-/// # use rustc_middle::ty::Ty;
-/// #[derive(SessionDiagnostic)]
-/// #[error(code = "E0505", slug = "move-out-of-borrow-error")]
-/// pub struct MoveOutOfBorrowError<'tcx> {
-///     pub name: Ident,
-///     pub ty: Ty<'tcx>,
-///     #[primary_span]
-///     #[label = "cannot move out of borrow"]
-///     pub span: Span,
-///     #[label = "`{ty}` first borrowed here"]
-///     pub other_span: Span,
-///     #[suggestion(message = "consider cloning here", code = "{name}.clone()")]
-///     pub opt_sugg: Option<(Span, Applicability)>
-/// }
-/// ```
-///
-/// Then, later, to emit the error:
-///
-/// ```ignore (pseudo-rust)
-/// sess.emit_err(MoveOutOfBorrowError {
-///     expected,
-///     actual,
-///     span,
-///     other_span,
-///     opt_sugg: Some(suggestion, Applicability::MachineApplicable),
-/// });
-/// ```
-pub fn session_diagnostic_derive(s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
-    // Names for the diagnostic we build and the session we build it from.
-    let diag = format_ident!("diag");
-    let sess = format_ident!("sess");
-
-    SessionDiagnosticDerive::new(diag, sess, s).into_tokens()
-}
-
-/// Checks whether the type name of `ty` matches `name`.
-///
-/// Given some struct at `a::b::c::Foo`, this will return true for `c::Foo`, `b::c::Foo`, or
-/// `a::b::c::Foo`. This reasonably allows qualified names to be used in the macro.
-fn type_matches_path(ty: &syn::Type, name: &[&str]) -> bool {
-    if let syn::Type::Path(ty) = ty {
-        ty.path
-            .segments
-            .iter()
-            .map(|s| s.ident.to_string())
-            .rev()
-            .zip(name.iter().rev())
-            .all(|(x, y)| &x.as_str() == y)
-    } else {
-        false
-    }
-}
-
-/// The central struct for constructing the `as_error` method from an annotated struct.
-struct SessionDiagnosticDerive<'a> {
-    structure: synstructure::Structure<'a>,
-    builder: SessionDiagnosticDeriveBuilder<'a>,
-}
-
-impl std::convert::From<syn::Error> for SessionDiagnosticDeriveError {
-    fn from(e: syn::Error) -> Self {
-        SessionDiagnosticDeriveError::SynError(e)
-    }
-}
-
-#[derive(Debug)]
-enum SessionDiagnosticDeriveError {
-    SynError(syn::Error),
-    ErrorHandled,
-}
-
-impl SessionDiagnosticDeriveError {
-    fn to_compile_error(self) -> proc_macro2::TokenStream {
-        match self {
-            SessionDiagnosticDeriveError::SynError(e) => e.to_compile_error(),
-            SessionDiagnosticDeriveError::ErrorHandled => {
-                // Return ! to avoid having to create a blank DiagnosticBuilder to return when an
-                // error has already been emitted to the compiler.
-                quote! {
-                    { unreachable!(); }
-                }
-            }
-        }
-    }
-}
-
-fn span_err(span: impl proc_macro::MultiSpan, msg: &str) -> proc_macro::Diagnostic {
-    Diagnostic::spanned(span, proc_macro::Level::Error, msg)
-}
-
-/// For methods that return a `Result<_, SessionDiagnosticDeriveError>`:
-///
-/// Emit a diagnostic on span `$span` with msg `$msg` (optionally performing additional decoration
-/// using the `FnOnce` passed in `diag`) and return `Err(ErrorHandled)`.
-macro_rules! throw_span_err {
-    ($span:expr, $msg:expr) => {{ throw_span_err!($span, $msg, |diag| diag) }};
-    ($span:expr, $msg:expr, $f:expr) => {{
-        return Err(_throw_span_err($span, $msg, $f));
-    }};
-}
-
-/// When possible, prefer using `throw_span_err!` over using this function directly. This only
-/// exists as a function to constrain `f` to an `impl FnOnce`.
-fn _throw_span_err(
-    span: impl proc_macro::MultiSpan,
-    msg: &str,
-    f: impl FnOnce(proc_macro::Diagnostic) -> proc_macro::Diagnostic,
-) -> SessionDiagnosticDeriveError {
-    let diag = span_err(span, msg);
-    f(diag).emit();
-    SessionDiagnosticDeriveError::ErrorHandled
-}
-
-impl<'a> SessionDiagnosticDerive<'a> {
-    fn new(diag: syn::Ident, sess: syn::Ident, structure: synstructure::Structure<'a>) -> Self {
-        // Build the mapping of field names to fields. This allows attributes to peek values from
-        // other fields.
-        let mut fields_map = HashMap::new();
-
-        // Convenience bindings.
-        let ast = structure.ast();
-
-        if let syn::Data::Struct(syn::DataStruct { fields, .. }) = &ast.data {
-            for field in fields.iter() {
-                if let Some(ident) = &field.ident {
-                    fields_map.insert(ident.to_string(), field);
-                }
-            }
-        }
-
-        Self {
-            builder: SessionDiagnosticDeriveBuilder {
-                diag,
-                sess,
-                fields: fields_map,
-                kind: None,
-                code: None,
-                slug: None,
-            },
-            structure,
-        }
-    }
-
-    fn into_tokens(self) -> proc_macro2::TokenStream {
-        let SessionDiagnosticDerive { mut structure, mut builder } = self;
-
-        let ast = structure.ast();
-        let attrs = &ast.attrs;
-
-        let (implementation, param_ty) = {
-            if let syn::Data::Struct(..) = ast.data {
-                let preamble = {
-                    let preamble = attrs.iter().map(|attr| {
-                        builder
-                            .generate_structure_code(attr)
-                            .unwrap_or_else(|v| v.to_compile_error())
-                    });
-
-                    quote! {
-                        #(#preamble)*;
-                    }
-                };
-
-                // Generates calls to `span_label` and similar functions based on the attributes
-                // on fields. Code for suggestions uses formatting machinery and the value of
-                // other fields - because any given field can be referenced multiple times, it
-                // should be accessed through a borrow. When passing fields to `set_arg` (which
-                // happens below) for Fluent, we want to move the data, so that has to happen
-                // in a separate pass over the fields.
-                let attrs = structure.each(|field_binding| {
-                    let field = field_binding.ast();
-                    let result = field.attrs.iter().map(|attr| {
-                        builder
-                            .generate_field_attr_code(
-                                attr,
-                                FieldInfo {
-                                    vis: &field.vis,
-                                    binding: field_binding,
-                                    ty: &field.ty,
-                                    span: &field.span(),
-                                },
-                            )
-                            .unwrap_or_else(|v| v.to_compile_error())
-                    });
-
-                    quote! { #(#result);* }
-                });
-
-                // When generating `set_arg` calls, move data rather than borrow it to avoid
-                // requiring clones - this must therefore be the last use of each field (for
-                // example, any formatting machinery that might refer to a field should be
-                // generated already).
-                structure.bind_with(|_| synstructure::BindStyle::Move);
-                let args = structure.each(|field_binding| {
-                    let field = field_binding.ast();
-                    // When a field has attributes like `#[label]` or `#[note]` then it doesn't
-                    // need to be passed as an argument to the diagnostic. But when a field has no
-                    // attributes then it must be passed as an argument to the diagnostic so that
-                    // it can be referred to by Fluent messages.
-                    if field.attrs.is_empty() {
-                        let diag = &builder.diag;
-                        let ident = field_binding.ast().ident.as_ref().unwrap();
-                        quote! {
-                            #diag.set_arg(
-                                stringify!(#ident),
-                                #field_binding.into_diagnostic_arg()
-                            );
-                        }
-                    } else {
-                        quote! {}
-                    }
-                });
-
-                let span = ast.span().unwrap();
-                let (diag, sess) = (&builder.diag, &builder.sess);
-                let init = match (builder.kind, builder.slug) {
-                    (None, _) => {
-                        span_err(span, "diagnostic kind not specified")
-                            .help("use the `#[error(...)]` attribute to create an error")
-                            .emit();
-                        return SessionDiagnosticDeriveError::ErrorHandled.to_compile_error();
-                    }
-                    (Some((kind, _)), None) => {
-                        span_err(span, "`slug` not specified")
-                            .help(&format!("use the `#[{}(slug = \"...\")]` attribute to set this diagnostic's slug", kind.descr()))
-                            .emit();
-                        return SessionDiagnosticDeriveError::ErrorHandled.to_compile_error();
-                    }
-                    (Some((SessionDiagnosticKind::Error, _)), Some((slug, _))) => {
-                        quote! {
-                            let mut #diag = #sess.struct_err(
-                                rustc_errors::DiagnosticMessage::fluent(#slug),
-                            );
-                        }
-                    }
-                    (Some((SessionDiagnosticKind::Warn, _)), Some((slug, _))) => {
-                        quote! {
-                            let mut #diag = #sess.struct_warn(
-                                rustc_errors::DiagnosticMessage::fluent(#slug),
-                            );
-                        }
-                    }
-                };
-
-                let implementation = quote! {
-                    #init
-                    #preamble
-                    match self {
-                        #attrs
-                    }
-                    match self {
-                        #args
-                    }
-                    #diag
-                };
-                let param_ty = match builder.kind {
-                    Some((SessionDiagnosticKind::Error, _)) => {
-                        quote! { rustc_errors::ErrorGuaranteed }
-                    }
-                    Some((SessionDiagnosticKind::Warn, _)) => quote! { () },
-                    _ => unreachable!(),
-                };
-
-                (implementation, param_ty)
-            } else {
-                span_err(
-                    ast.span().unwrap(),
-                    "`#[derive(SessionDiagnostic)]` can only be used on structs",
-                )
-                .emit();
-
-                let implementation = SessionDiagnosticDeriveError::ErrorHandled.to_compile_error();
-                let param_ty = quote! { rustc_errors::ErrorGuaranteed };
-                (implementation, param_ty)
-            }
-        };
-
-        let sess = &builder.sess;
-        structure.gen_impl(quote! {
-            gen impl<'__session_diagnostic_sess> rustc_session::SessionDiagnostic<'__session_diagnostic_sess, #param_ty>
-                    for @Self
-            {
-                fn into_diagnostic(
-                    self,
-                    #sess: &'__session_diagnostic_sess rustc_session::Session
-                ) -> rustc_errors::DiagnosticBuilder<'__session_diagnostic_sess, #param_ty> {
-                    use rustc_errors::IntoDiagnosticArg;
-                    #implementation
-                }
-            }
-        })
-    }
-}
-
-/// Field information passed to the builder. Deliberately omits attrs to discourage the
-/// `generate_*` methods from walking the attributes themselves.
-struct FieldInfo<'a> {
-    vis: &'a syn::Visibility,
-    binding: &'a synstructure::BindingInfo<'a>,
-    ty: &'a syn::Type,
-    span: &'a proc_macro2::Span,
-}
-
-/// What kind of session diagnostic is being derived - an error or a warning?
-#[derive(Copy, Clone)]
-enum SessionDiagnosticKind {
-    /// `#[error(..)]`
-    Error,
-    /// `#[warn(..)]`
-    Warn,
-}
-
-impl SessionDiagnosticKind {
-    /// Returns human-readable string corresponding to the kind.
-    fn descr(&self) -> &'static str {
-        match self {
-            SessionDiagnosticKind::Error => "error",
-            SessionDiagnosticKind::Warn => "warning",
-        }
-    }
-}
-
-/// Tracks persistent information required for building up the individual calls to diagnostic
-/// methods for the final generated method. This is a separate struct to `SessionDiagnosticDerive`
-/// only to be able to destructure and split `self.builder` and the `self.structure` up to avoid a
-/// double mut borrow later on.
-struct SessionDiagnosticDeriveBuilder<'a> {
-    /// Name of the session parameter that's passed in to the `as_error` method.
-    sess: syn::Ident,
-    /// The identifier to use for the generated `DiagnosticBuilder` instance.
-    diag: syn::Ident,
-
-    /// Store a map of field name to its corresponding field. This is built on construction of the
-    /// derive builder.
-    fields: HashMap<String, &'a syn::Field>,
-
-    /// Kind of diagnostic requested via the struct attribute.
-    kind: Option<(SessionDiagnosticKind, proc_macro::Span)>,
-    /// Slug is a mandatory part of the struct attribute as corresponds to the Fluent message that
-    /// has the actual diagnostic message.
-    slug: Option<(String, proc_macro::Span)>,
-    /// Error codes are a optional part of the struct attribute - this is only set to detect
-    /// multiple specifications.
-    code: Option<proc_macro::Span>,
-}
-
-impl<'a> SessionDiagnosticDeriveBuilder<'a> {
-    /// Establishes state in the `SessionDiagnosticDeriveBuilder` resulting from the struct
-    /// attributes like `#[error(..)#`, such as the diagnostic kind and slug. Generates
-    /// diagnostic builder calls for setting error code and creating note/help messages.
-    fn generate_structure_code(
-        &mut self,
-        attr: &syn::Attribute,
-    ) -> Result<proc_macro2::TokenStream, SessionDiagnosticDeriveError> {
-        let span = attr.span().unwrap();
-
-        let name = attr.path.segments.last().unwrap().ident.to_string();
-        let name = name.as_str();
-        let meta = attr.parse_meta()?;
-
-        if matches!(name, "help" | "note")
-            && matches!(meta, syn::Meta::Path(_) | syn::Meta::NameValue(_))
-        {
-            let diag = &self.diag;
-            let slug = match &self.slug {
-                Some((slug, _)) => slug.as_str(),
-                None => throw_span_err!(
-                    span,
-                    &format!(
-                        "`#[{}{}]` must come after `#[error(..)]` or `#[warn(..)]`",
-                        name,
-                        match meta {
-                            syn::Meta::Path(_) => "",
-                            syn::Meta::NameValue(_) => " = ...",
-                            _ => unreachable!(),
-                        }
-                    )
-                ),
-            };
-            let id = match meta {
-                syn::Meta::Path(..) => quote! { #name },
-                syn::Meta::NameValue(syn::MetaNameValue { lit: syn::Lit::Str(s), .. }) => {
-                    quote! { #s }
-                }
-                _ => unreachable!(),
-            };
-            let fn_name = proc_macro2::Ident::new(name, attr.span());
-
-            return Ok(quote! {
-                #diag.#fn_name(rustc_errors::DiagnosticMessage::fluent_attr(#slug, #id));
-            });
-        }
-
-        let nested = match meta {
-            syn::Meta::List(syn::MetaList { nested, .. }) => nested,
-            syn::Meta::Path(..) => throw_span_err!(
-                span,
-                &format!("`#[{}]` is not a valid `SessionDiagnostic` struct attribute", name)
-            ),
-            syn::Meta::NameValue(..) => throw_span_err!(
-                span,
-                &format!("`#[{} = ...]` is not a valid `SessionDiagnostic` struct attribute", name)
-            ),
-        };
-
-        let kind = match name {
-            "error" => SessionDiagnosticKind::Error,
-            "warning" => SessionDiagnosticKind::Warn,
-            other => throw_span_err!(
-                span,
-                &format!("`#[{}(...)]` is not a valid `SessionDiagnostic` struct attribute", other)
-            ),
-        };
-        self.set_kind_once(kind, span)?;
-
-        let mut tokens = Vec::new();
-        for attr in nested {
-            let span = attr.span().unwrap();
-            let meta = match attr {
-                syn::NestedMeta::Meta(meta) => meta,
-                syn::NestedMeta::Lit(_) => throw_span_err!(
-                    span,
-                    &format!(
-                        "`#[{}(\"...\")]` is not a valid `SessionDiagnostic` struct attribute",
-                        name
-                    )
-                ),
-            };
-
-            let path = meta.path();
-            let nested_name = path.segments.last().unwrap().ident.to_string();
-            match &meta {
-                // Struct attributes are only allowed to be applied once, and the diagnostic
-                // changes will be set in the initialisation code.
-                syn::Meta::NameValue(syn::MetaNameValue { lit: syn::Lit::Str(s), .. }) => {
-                    match nested_name.as_str() {
-                        "slug" => {
-                            self.set_slug_once(s.value(), s.span().unwrap());
-                        }
-                        "code" => {
-                            tokens.push(self.set_code_once(s.value(), s.span().unwrap()));
-                        }
-                        other => {
-                            let diag = span_err(
-                                span,
-                                &format!(
-                                    "`#[{}({} = ...)]` is not a valid `SessionDiagnostic` struct attribute",
-                                    name, other
-                                ),
-                            );
-                            diag.emit();
-                        }
-                    }
-                }
-                syn::Meta::NameValue(..) => {
-                    span_err(
-                        span,
-                        &format!(
-                            "`#[{}({} = ...)]` is not a valid `SessionDiagnostic` struct attribute",
-                            name, nested_name
-                        ),
-                    )
-                    .help("value must be a string")
-                    .emit();
-                }
-                syn::Meta::Path(..) => {
-                    span_err(
-                        span,
-                        &format!(
-                            "`#[{}({})]` is not a valid `SessionDiagnostic` struct attribute",
-                            name, nested_name
-                        ),
-                    )
-                    .emit();
-                }
-                syn::Meta::List(..) => {
-                    span_err(
-                        span,
-                        &format!(
-                            "`#[{}({}(...))]` is not a valid `SessionDiagnostic` struct attribute",
-                            name, nested_name
-                        ),
-                    )
-                    .emit();
-                }
-            }
-        }
-
-        Ok(tokens.drain(..).collect())
-    }
-
-    #[must_use]
-    fn set_kind_once(
-        &mut self,
-        kind: SessionDiagnosticKind,
-        span: proc_macro::Span,
-    ) -> Result<(), SessionDiagnosticDeriveError> {
-        match self.kind {
-            None => {
-                self.kind = Some((kind, span));
-                Ok(())
-            }
-            Some((prev_kind, prev_span)) => {
-                let existing = prev_kind.descr();
-                let current = kind.descr();
-
-                let msg = if current == existing {
-                    format!("`{}` specified multiple times", existing)
-                } else {
-                    format!("`{}` specified when `{}` was already specified", current, existing)
-                };
-                throw_span_err!(span, &msg, |diag| diag
-                    .span_note(prev_span, "previously specified here"));
-            }
-        }
-    }
-
-    fn set_code_once(&mut self, code: String, span: proc_macro::Span) -> proc_macro2::TokenStream {
-        match self.code {
-            None => {
-                self.code = Some(span);
-            }
-            Some(prev_span) => {
-                span_err(span, "`code` specified multiple times")
-                    .span_note(prev_span, "previously specified here")
-                    .emit();
-            }
-        }
-
-        let diag = &self.diag;
-        quote! { #diag.code(rustc_errors::DiagnosticId::Error(#code.to_string())); }
-    }
-
-    fn set_slug_once(&mut self, slug: String, span: proc_macro::Span) {
-        match self.slug {
-            None => {
-                self.slug = Some((slug, span));
-            }
-            Some((_, prev_span)) => {
-                span_err(span, "`slug` specified multiple times")
-                    .span_note(prev_span, "previously specified here")
-                    .emit();
-            }
-        }
-    }
-
-    fn generate_field_attr_code(
-        &mut self,
-        attr: &syn::Attribute,
-        info: FieldInfo<'_>,
-    ) -> Result<proc_macro2::TokenStream, SessionDiagnosticDeriveError> {
-        let field_binding = &info.binding.binding;
-        let option_ty = option_inner_ty(&info.ty);
-        let generated_code = self.generate_non_option_field_code(
-            attr,
-            FieldInfo {
-                vis: info.vis,
-                binding: info.binding,
-                ty: option_ty.unwrap_or(&info.ty),
-                span: info.span,
-            },
-        )?;
-
-        if option_ty.is_none() {
-            Ok(quote! { #generated_code })
-        } else {
-            Ok(quote! {
-                if let Some(#field_binding) = #field_binding {
-                    #generated_code
-                }
-            })
-        }
-    }
-
-    fn generate_non_option_field_code(
-        &mut self,
-        attr: &syn::Attribute,
-        info: FieldInfo<'_>,
-    ) -> Result<proc_macro2::TokenStream, SessionDiagnosticDeriveError> {
-        let diag = &self.diag;
-        let span = attr.span().unwrap();
-        let field_binding = &info.binding.binding;
-
-        let name = attr.path.segments.last().unwrap().ident.to_string();
-        let name = name.as_str();
-
-        let meta = attr.parse_meta()?;
-        match meta {
-            syn::Meta::Path(_) => match name {
-                "skip_arg" => {
-                    // Don't need to do anything - by virtue of the attribute existing, the
-                    // `set_arg` call will not be generated.
-                    Ok(quote! {})
-                }
-                "primary_span" => {
-                    self.report_error_if_not_applied_to_span(attr, info)?;
-                    Ok(quote! {
-                        #diag.set_span(*#field_binding);
-                    })
-                }
-                "label" | "note" | "help" => {
-                    self.report_error_if_not_applied_to_span(attr, info)?;
-                    Ok(self.add_subdiagnostic(field_binding, name, name))
-                }
-                other => throw_span_err!(
-                    span,
-                    &format!("`#[{}]` is not a valid `SessionDiagnostic` field attribute", other)
-                ),
-            },
-            syn::Meta::NameValue(syn::MetaNameValue { lit: syn::Lit::Str(s), .. }) => match name {
-                "label" | "note" | "help" => {
-                    self.report_error_if_not_applied_to_span(attr, info)?;
-                    Ok(self.add_subdiagnostic(field_binding, name, &s.value()))
-                }
-                other => throw_span_err!(
-                    span,
-                    &format!(
-                        "`#[{} = ...]` is not a valid `SessionDiagnostic` field attribute",
-                        other
-                    )
-                ),
-            },
-            syn::Meta::NameValue(_) => throw_span_err!(
-                span,
-                &format!("`#[{} = ...]` is not a valid `SessionDiagnostic` field attribute", name),
-                |diag| diag.help("value must be a string")
-            ),
-            syn::Meta::List(syn::MetaList { path, nested, .. }) => {
-                let name = path.segments.last().unwrap().ident.to_string();
-                let name = name.as_ref();
-
-                match name {
-                    "suggestion" | "suggestion_short" | "suggestion_hidden"
-                    | "suggestion_verbose" => (),
-                    other => throw_span_err!(
-                        span,
-                        &format!(
-                            "`#[{}(...)]` is not a valid `SessionDiagnostic` field attribute",
-                            other
-                        )
-                    ),
-                };
-
-                let (span_, applicability) = self.span_and_applicability_of_ty(info)?;
-
-                let mut msg = None;
-                let mut code = None;
-
-                for attr in nested {
-                    let meta = match attr {
-                        syn::NestedMeta::Meta(meta) => meta,
-                        syn::NestedMeta::Lit(_) => throw_span_err!(
-                            span,
-                            &format!(
-                                "`#[{}(\"...\")]` is not a valid `SessionDiagnostic` field attribute",
-                                name
-                            )
-                        ),
-                    };
-
-                    let span = meta.span().unwrap();
-                    let nested_name = meta.path().segments.last().unwrap().ident.to_string();
-                    let nested_name = nested_name.as_str();
-
-                    match meta {
-                        syn::Meta::NameValue(syn::MetaNameValue {
-                            lit: syn::Lit::Str(s), ..
-                        }) => match nested_name {
-                            "message" => {
-                                msg = Some(s.value());
-                            }
-                            "code" => {
-                                let formatted_str = self.build_format(&s.value(), s.span());
-                                code = Some(formatted_str);
-                            }
-                            other => throw_span_err!(
-                                span,
-                                &format!(
-                                    "`#[{}({} = ...)]` is not a valid `SessionDiagnostic` field attribute",
-                                    name, other
-                                )
-                            ),
-                        },
-                        syn::Meta::NameValue(..) => throw_span_err!(
-                            span,
-                            &format!(
-                                "`#[{}({} = ...)]` is not a valid `SessionDiagnostic` struct attribute",
-                                name, nested_name
-                            ),
-                            |diag| diag.help("value must be a string")
-                        ),
-                        syn::Meta::Path(..) => throw_span_err!(
-                            span,
-                            &format!(
-                                "`#[{}({})]` is not a valid `SessionDiagnostic` struct attribute",
-                                name, nested_name
-                            )
-                        ),
-                        syn::Meta::List(..) => throw_span_err!(
-                            span,
-                            &format!(
-                                "`#[{}({}(...))]` is not a valid `SessionDiagnostic` struct attribute",
-                                name, nested_name
-                            )
-                        ),
-                    }
-                }
-
-                let method = format_ident!("span_{}", name);
-
-                let slug = self
-                    .slug
-                    .as_ref()
-                    .map(|(slug, _)| slug.as_str())
-                    .unwrap_or_else(|| "missing-slug");
-                let msg = msg.as_deref().unwrap_or("suggestion");
-                let msg = quote! { rustc_errors::DiagnosticMessage::fluent_attr(#slug, #msg) };
-                let code = code.unwrap_or_else(|| quote! { String::new() });
-
-                Ok(quote! { #diag.#method(#span_, #msg, #code, #applicability); })
-            }
-        }
-    }
-
-    /// Reports an error if the field's type is not `Span`.
-    fn report_error_if_not_applied_to_span(
-        &self,
-        attr: &syn::Attribute,
-        info: FieldInfo<'_>,
-    ) -> Result<(), SessionDiagnosticDeriveError> {
-        if !type_matches_path(&info.ty, &["rustc_span", "Span"]) {
-            let name = attr.path.segments.last().unwrap().ident.to_string();
-            let name = name.as_str();
-            let meta = attr.parse_meta()?;
-
-            throw_span_err!(
-                attr.span().unwrap(),
-                &format!(
-                    "the `#[{}{}]` attribute can only be applied to fields of type `Span`",
-                    name,
-                    match meta {
-                        syn::Meta::Path(_) => "",
-                        syn::Meta::NameValue(_) => " = ...",
-                        syn::Meta::List(_) => "(...)",
-                    }
-                )
-            );
-        }
-
-        Ok(())
-    }
-
-    /// Adds a subdiagnostic by generating a `diag.span_$kind` call with the current slug and
-    /// `fluent_attr_identifier`.
-    fn add_subdiagnostic(
-        &self,
-        field_binding: &proc_macro2::Ident,
-        kind: &str,
-        fluent_attr_identifier: &str,
-    ) -> proc_macro2::TokenStream {
-        let diag = &self.diag;
-
-        let slug =
-            self.slug.as_ref().map(|(slug, _)| slug.as_str()).unwrap_or_else(|| "missing-slug");
-        let fn_name = format_ident!("span_{}", kind);
-        quote! {
-            #diag.#fn_name(
-                *#field_binding,
-                rustc_errors::DiagnosticMessage::fluent_attr(#slug, #fluent_attr_identifier)
-            );
-        }
-    }
-
-    fn span_and_applicability_of_ty(
-        &self,
-        info: FieldInfo<'_>,
-    ) -> Result<(proc_macro2::TokenStream, proc_macro2::TokenStream), SessionDiagnosticDeriveError>
-    {
-        match &info.ty {
-            // If `ty` is `Span` w/out applicability, then use `Applicability::Unspecified`.
-            ty @ syn::Type::Path(..) if type_matches_path(ty, &["rustc_span", "Span"]) => {
-                let binding = &info.binding.binding;
-                Ok((quote!(*#binding), quote!(rustc_errors::Applicability::Unspecified)))
-            }
-            // If `ty` is `(Span, Applicability)` then return tokens accessing those.
-            syn::Type::Tuple(tup) => {
-                let mut span_idx = None;
-                let mut applicability_idx = None;
-
-                for (idx, elem) in tup.elems.iter().enumerate() {
-                    if type_matches_path(elem, &["rustc_span", "Span"]) {
-                        if span_idx.is_none() {
-                            span_idx = Some(syn::Index::from(idx));
-                        } else {
-                            throw_span_err!(
-                                info.span.unwrap(),
-                                "type of field annotated with `#[suggestion(...)]` contains more than one `Span`"
-                            );
-                        }
-                    } else if type_matches_path(elem, &["rustc_errors", "Applicability"]) {
-                        if applicability_idx.is_none() {
-                            applicability_idx = Some(syn::Index::from(idx));
-                        } else {
-                            throw_span_err!(
-                                info.span.unwrap(),
-                                "type of field annotated with `#[suggestion(...)]` contains more than one Applicability"
-                            );
-                        }
-                    }
-                }
-
-                if let Some(span_idx) = span_idx {
-                    let binding = &info.binding.binding;
-                    let span = quote!(#binding.#span_idx);
-                    let applicability = applicability_idx
-                        .map(|applicability_idx| quote!(#binding.#applicability_idx))
-                        .unwrap_or_else(|| quote!(rustc_errors::Applicability::Unspecified));
-
-                    return Ok((span, applicability));
-                }
-
-                throw_span_err!(info.span.unwrap(), "wrong types for suggestion", |diag| {
-                    diag.help("`#[suggestion(...)]` on a tuple field must be applied to fields of type `(Span, Applicability)`")
-                });
-            }
-            // If `ty` isn't a `Span` or `(Span, Applicability)` then emit an error.
-            _ => throw_span_err!(info.span.unwrap(), "wrong field type for suggestion", |diag| {
-                diag.help("`#[suggestion(...)]` should be applied to fields of type `Span` or `(Span, Applicability)`")
-            }),
-        }
-    }
-
-    /// In the strings in the attributes supplied to this macro, we want callers to be able to
-    /// reference fields in the format string. For example:
-    ///
-    /// ```ignore (not-usage-example)
-    /// struct Point {
-    ///     #[error = "Expected a point greater than ({x}, {y})"]
-    ///     x: i32,
-    ///     y: i32,
-    /// }
-    /// ```
-    ///
-    /// We want to automatically pick up that `{x}` refers `self.x` and `{y}` refers to `self.y`,
-    /// then generate this call to `format!`:
-    ///
-    /// ```ignore (not-usage-example)
-    /// format!("Expected a point greater than ({x}, {y})", x = self.x, y = self.y)
-    /// ```
-    ///
-    /// This function builds the entire call to `format!`.
-    fn build_format(&self, input: &str, span: proc_macro2::Span) -> proc_macro2::TokenStream {
-        // This set is used later to generate the final format string. To keep builds reproducible,
-        // the iteration order needs to be deterministic, hence why we use a BTreeSet here instead
-        // of a HashSet.
-        let mut referenced_fields: BTreeSet<String> = BTreeSet::new();
-
-        // At this point, we can start parsing the format string.
-        let mut it = input.chars().peekable();
-        // Once the start of a format string has been found, process the format string and spit out
-        // the referenced fields. Leaves `it` sitting on the closing brace of the format string, so the
-        // next call to `it.next()` retrieves the next character.
-        while let Some(c) = it.next() {
-            if c == '{' && *it.peek().unwrap_or(&'\0') != '{' {
-                let mut eat_argument = || -> Option<String> {
-                    let mut result = String::new();
-                    // Format specifiers look like
-                    // format   := '{' [ argument ] [ ':' format_spec ] '}' .
-                    // Therefore, we only need to eat until ':' or '}' to find the argument.
-                    while let Some(c) = it.next() {
-                        result.push(c);
-                        let next = *it.peek().unwrap_or(&'\0');
-                        if next == '}' {
-                            break;
-                        } else if next == ':' {
-                            // Eat the ':' character.
-                            assert_eq!(it.next().unwrap(), ':');
-                            break;
-                        }
-                    }
-                    // Eat until (and including) the matching '}'
-                    while it.next()? != '}' {
-                        continue;
-                    }
-                    Some(result)
-                };
-
-                if let Some(referenced_field) = eat_argument() {
-                    referenced_fields.insert(referenced_field);
-                }
-            }
-        }
-        // At this point, `referenced_fields` contains a set of the unique fields that were
-        // referenced in the format string. Generate the corresponding "x = self.x" format
-        // string parameters:
-        let args = referenced_fields.into_iter().map(|field: String| {
-            let field_ident = format_ident!("{}", field);
-            let value = if self.fields.contains_key(&field) {
-                quote! {
-                    &self.#field_ident
-                }
-            } else {
-                // This field doesn't exist. Emit a diagnostic.
-                Diagnostic::spanned(
-                    span.unwrap(),
-                    proc_macro::Level::Error,
-                    format!("`{}` doesn't refer to a field on this type", field),
-                )
-                .emit();
-                quote! {
-                    "{#field}"
-                }
-            };
-            quote! {
-                #field_ident = #value
-            }
-        });
-        quote! {
-            format!(#input #(,#args)*)
-        }
-    }
-}
-
-/// If `ty` is an Option, returns `Some(inner type)`, otherwise returns `None`.
-fn option_inner_ty(ty: &syn::Type) -> Option<&syn::Type> {
-    if type_matches_path(ty, &["std", "option", "Option"]) {
-        if let syn::Type::Path(ty_path) = ty {
-            let path = &ty_path.path;
-            let ty = path.segments.iter().last().unwrap();
-            if let syn::PathArguments::AngleBracketed(bracketed) = &ty.arguments {
-                if bracketed.args.len() == 1 {
-                    if let syn::GenericArgument::Type(ty) = &bracketed.args[0] {
-                        return Some(ty);
-                    }
-                }
-            }
-        }
-    }
-    None
-}
diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs
index e5d0cd28925..3c545e6a0d2 100644
--- a/compiler/rustc_metadata/src/creader.rs
+++ b/compiler/rustc_metadata/src/creader.rs
@@ -195,10 +195,12 @@ impl CStore {
     }
 
     pub fn report_unused_deps(&self, tcx: TyCtxt<'_>) {
+        let json_unused_externs = tcx.sess.opts.json_unused_externs;
+
         // We put the check for the option before the lint_level_at_node call
         // because the call mutates internal state and introducing it
         // leads to some ui tests failing.
-        if !tcx.sess.opts.json_unused_externs {
+        if !json_unused_externs.is_enabled() {
             return;
         }
         let level = tcx
@@ -208,10 +210,11 @@ impl CStore {
             let unused_externs =
                 self.unused_externs.iter().map(|ident| ident.to_ident_string()).collect::<Vec<_>>();
             let unused_externs = unused_externs.iter().map(String::as_str).collect::<Vec<&str>>();
-            tcx.sess
-                .parse_sess
-                .span_diagnostic
-                .emit_unused_externs(level.as_str(), &unused_externs);
+            tcx.sess.parse_sess.span_diagnostic.emit_unused_externs(
+                level,
+                json_unused_externs.is_loud(),
+                &unused_externs,
+            );
         }
     }
 }
@@ -907,13 +910,17 @@ impl<'a> CrateLoader<'a> {
                 // Don't worry about pathless `--extern foo` sysroot references
                 continue;
             }
+            if entry.nounused_dep {
+                // We're not worried about this one
+                continue;
+            }
             let name_interned = Symbol::intern(name);
             if self.used_extern_options.contains(&name_interned) {
                 continue;
             }
 
             // Got a real unused --extern
-            if self.sess.opts.json_unused_externs {
+            if self.sess.opts.json_unused_externs.is_enabled() {
                 self.cstore.unused_externs.push(name_interned);
                 continue;
             }
diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs
index c0f2319f003..1edb62e189f 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder.rs
@@ -22,7 +22,7 @@ use rustc_hir::lang_items;
 use rustc_index::vec::{Idx, IndexVec};
 use rustc_middle::arena::ArenaAllocatable;
 use rustc_middle::metadata::ModChild;
-use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel};
+use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo};
 use rustc_middle::middle::stability::DeprecationEntry;
 use rustc_middle::mir::interpret::{AllocDecodingSession, AllocDecodingState};
 use rustc_middle::thir;
@@ -1429,7 +1429,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
     fn exported_symbols(
         self,
         tcx: TyCtxt<'tcx>,
-    ) -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportLevel)] {
+    ) -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportInfo)] {
         tcx.arena.alloc_from_iter(self.root.exported_symbols.decode((self, tcx)))
     }
 
@@ -1744,6 +1744,10 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
                 adjustments: generator_data.adjustments,
             })
     }
+
+    fn get_may_have_doc_links(self, index: DefIndex) -> bool {
+        self.root.tables.may_have_doc_links.get(self, index).is_some()
+    }
 }
 
 impl CrateMetadata {
diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
index 6a8f1dec0c5..da1dd6af65a 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
@@ -190,9 +190,9 @@ provide! { <'tcx> tcx, def_id, other, cdata,
         let reachable_non_generics = tcx
             .exported_symbols(cdata.cnum)
             .iter()
-            .filter_map(|&(exported_symbol, export_level)| {
+            .filter_map(|&(exported_symbol, export_info)| {
                 if let ExportedSymbol::NonGeneric(def_id) = exported_symbol {
-                    Some((def_id, export_level))
+                    Some((def_id, export_info))
                 } else {
                     None
                 }
@@ -531,6 +531,18 @@ impl CStore {
     ) -> impl Iterator<Item = DefId> + '_ {
         self.get_crate_data(cnum).get_all_incoherent_impls()
     }
+
+    pub fn associated_item_def_ids_untracked<'a>(
+        &'a self,
+        def_id: DefId,
+        sess: &'a Session,
+    ) -> impl Iterator<Item = DefId> + 'a {
+        self.get_crate_data(def_id.krate).get_associated_item_def_ids(def_id.index, sess)
+    }
+
+    pub fn may_have_doc_links_untracked(&self, def_id: DefId) -> bool {
+        self.get_crate_data(def_id.krate).get_may_have_doc_links(def_id.index)
+    }
 }
 
 impl CrateStore for CStore {
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index 066bcb428f6..b46ea955a3a 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -22,7 +22,7 @@ use rustc_index::vec::Idx;
 use rustc_middle::hir::nested_filter;
 use rustc_middle::middle::dependency_format::Linkage;
 use rustc_middle::middle::exported_symbols::{
-    metadata_symbol_name, ExportedSymbol, SymbolExportLevel,
+    metadata_symbol_name, ExportedSymbol, SymbolExportInfo,
 };
 use rustc_middle::mir::interpret;
 use rustc_middle::thir;
@@ -977,6 +977,14 @@ fn should_encode_generics(def_kind: DefKind) -> bool {
 }
 
 impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
+    fn encode_attrs(&mut self, def_id: DefId) {
+        let attrs = self.tcx.get_attrs(def_id);
+        record!(self.tables.attributes[def_id] <- attrs);
+        if attrs.iter().any(|attr| attr.may_have_doc_links()) {
+            self.tables.may_have_doc_links.set(def_id.index, ());
+        }
+    }
+
     fn encode_def_ids(&mut self) {
         if self.is_proc_macro {
             return;
@@ -989,7 +997,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
             let Some(def_kind) = def_kind else { continue };
             self.tables.opt_def_kind.set(def_id.index, def_kind);
             record!(self.tables.def_span[def_id] <- tcx.def_span(def_id));
-            record!(self.tables.attributes[def_id] <- tcx.get_attrs(def_id));
+            self.encode_attrs(def_id);
             record!(self.tables.expn_that_defined[def_id] <- self.tcx.expn_that_defined(def_id));
             if should_encode_visibility(def_kind) {
                 record!(self.tables.visibility[def_id] <- self.tcx.visibility(def_id));
@@ -1454,8 +1462,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
                 }))
             }
             hir::ItemKind::Impl(hir::Impl { defaultness, constness, .. }) => {
-                self.tables.impl_defaultness.set(def_id.index, defaultness);
-                self.tables.impl_constness.set(def_id.index, constness);
+                self.tables.impl_defaultness.set(def_id.index, *defaultness);
+                self.tables.impl_constness.set(def_id.index, *constness);
 
                 let trait_ref = self.tcx.impl_trait_ref(def_id);
                 if let Some(trait_ref) = trait_ref {
@@ -1651,7 +1659,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
 
             self.tables.opt_def_kind.set(LOCAL_CRATE.as_def_id().index, DefKind::Mod);
             record!(self.tables.def_span[LOCAL_CRATE.as_def_id()] <- tcx.def_span(LOCAL_CRATE.as_def_id()));
-            record!(self.tables.attributes[LOCAL_CRATE.as_def_id()] <- tcx.get_attrs(LOCAL_CRATE.as_def_id()));
+            self.encode_attrs(LOCAL_CRATE.as_def_id());
             record!(self.tables.visibility[LOCAL_CRATE.as_def_id()] <- tcx.visibility(LOCAL_CRATE.as_def_id()));
             if let Some(stability) = stability {
                 record!(self.tables.lookup_stability[LOCAL_CRATE.as_def_id()] <- stability);
@@ -1692,7 +1700,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
                 let def_id = id.to_def_id();
                 self.tables.opt_def_kind.set(def_id.index, DefKind::Macro(macro_kind));
                 record!(self.tables.kind[def_id] <- EntryKind::ProcMacro(macro_kind));
-                record!(self.tables.attributes[def_id] <- attrs);
+                self.encode_attrs(def_id);
                 record!(self.tables.def_keys[def_id] <- def_key);
                 record!(self.tables.def_ident_span[def_id] <- span);
                 record!(self.tables.def_span[def_id] <- span);
@@ -1866,8 +1874,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
     // definition (as that's not defined in this crate).
     fn encode_exported_symbols(
         &mut self,
-        exported_symbols: &[(ExportedSymbol<'tcx>, SymbolExportLevel)],
-    ) -> Lazy<[(ExportedSymbol<'tcx>, SymbolExportLevel)]> {
+        exported_symbols: &[(ExportedSymbol<'tcx>, SymbolExportInfo)],
+    ) -> Lazy<[(ExportedSymbol<'tcx>, SymbolExportInfo)]> {
         empty_proc_macro!(self);
         // The metadata symbol name is special. It should not show up in
         // downstream crates.
diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs
index e1a1589adb3..cdbed90e6b9 100644
--- a/compiler/rustc_metadata/src/rmeta/mod.rs
+++ b/compiler/rustc_metadata/src/rmeta/mod.rs
@@ -14,7 +14,7 @@ use rustc_hir::definitions::DefKey;
 use rustc_hir::lang_items;
 use rustc_index::{bit_set::FiniteBitSet, vec::IndexVec};
 use rustc_middle::metadata::ModChild;
-use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel};
+use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo};
 use rustc_middle::mir;
 use rustc_middle::thir;
 use rustc_middle::ty::fast_reject::SimplifiedType;
@@ -220,7 +220,7 @@ crate struct CrateRoot<'tcx> {
 
     tables: LazyTables<'tcx>,
 
-    exported_symbols: Lazy!([(ExportedSymbol<'tcx>, SymbolExportLevel)]),
+    exported_symbols: Lazy!([(ExportedSymbol<'tcx>, SymbolExportInfo)]),
 
     syntax_contexts: SyntaxContextTable,
     expn_data: ExpnDataTable,
@@ -360,6 +360,7 @@ define_tables! {
     def_path_hashes: Table<DefIndex, DefPathHash>,
     proc_macro_quoted_spans: Table<usize, Lazy<Span>>,
     generator_diagnostic_data: Table<DefIndex, Lazy<GeneratorDiagnosticData<'tcx>>>,
+    may_have_doc_links: Table<DefIndex, ()>,
 }
 
 #[derive(Copy, Clone, MetadataEncodable, MetadataDecodable)]
diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs
index 7a23cba536a..53fc2efe00b 100644
--- a/compiler/rustc_metadata/src/rmeta/table.rs
+++ b/compiler/rustc_metadata/src/rmeta/table.rs
@@ -186,6 +186,20 @@ impl FixedSizeEncoding for Option<RawDefId> {
     }
 }
 
+impl FixedSizeEncoding for Option<()> {
+    type ByteArray = [u8; 1];
+
+    #[inline]
+    fn from_bytes(b: &[u8; 1]) -> Self {
+        (b[0] != 0).then(|| ())
+    }
+
+    #[inline]
+    fn write_to_bytes(self, b: &mut [u8; 1]) {
+        b[0] = self.is_some() as u8
+    }
+}
+
 // NOTE(eddyb) there could be an impl for `usize`, which would enable a more
 // generic `Lazy<T>` impl, but in the general case we might not need / want to
 // fit every `usize` in `u32`.
diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs
index d74759e31a2..e0ed4022839 100644
--- a/compiler/rustc_middle/src/hir/map/mod.rs
+++ b/compiler/rustc_middle/src/hir/map/mod.rs
@@ -14,7 +14,6 @@ use rustc_hir::*;
 use rustc_index::vec::Idx;
 use rustc_middle::hir::nested_filter;
 use rustc_span::def_id::StableCrateId;
-use rustc_span::source_map::Spanned;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::Span;
 use rustc_target::spec::abi::Abi;
@@ -304,7 +303,6 @@ impl<'hir> Map<'hir> {
             | Node::Param(_)
             | Node::Arm(_)
             | Node::Lifetime(_)
-            | Node::Visibility(_)
             | Node::Block(_) => return None,
         };
         Some(def_kind)
@@ -1000,12 +998,7 @@ impl<'hir> Map<'hir> {
             },
             Node::Lifetime(lifetime) => lifetime.span,
             Node::GenericParam(param) => param.span,
-            Node::Visibility(&Spanned {
-                node: VisibilityKind::Restricted { ref path, .. },
-                ..
-            }) => path.span,
             Node::Infer(i) => i.span,
-            Node::Visibility(v) => bug!("unexpected Visibility {:?}", v),
             Node::Local(local) => local.span,
             Node::Crate(item) => item.spans.inner_span,
         };
@@ -1128,6 +1121,10 @@ pub(super) fn crate_hash(tcx: TyCtxt<'_>, crate_num: CrateNum) -> Svh {
     }
     tcx.sess.opts.dep_tracking_hash(true).hash_stable(&mut hcx, &mut stable_hasher);
     tcx.sess.local_stable_crate_id().hash_stable(&mut hcx, &mut stable_hasher);
+    // Hash visibility information since it does not appear in HIR.
+    let resolutions = tcx.resolutions(());
+    resolutions.visibilities.hash_stable(&mut hcx, &mut stable_hasher);
+    resolutions.has_pub_restricted.hash_stable(&mut hcx, &mut stable_hasher);
 
     let crate_hash: Fingerprint = stable_hasher.finish();
     Svh::new(crate_hash.to_smaller_hash())
@@ -1232,7 +1229,6 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String {
         Some(Node::Ctor(..)) => format!("ctor {}{}", path_str(), id_str),
         Some(Node::Lifetime(_)) => node_str("lifetime"),
         Some(Node::GenericParam(ref param)) => format!("generic_param {:?}{}", param, id_str),
-        Some(Node::Visibility(ref vis)) => format!("visibility {:?}{}", vis, id_str),
         Some(Node::Crate(..)) => String::from("root_crate"),
         None => format!("unknown node{}", id_str),
     }
diff --git a/compiler/rustc_middle/src/middle/exported_symbols.rs b/compiler/rustc_middle/src/middle/exported_symbols.rs
index 5ea78e087f8..631fd09ec4c 100644
--- a/compiler/rustc_middle/src/middle/exported_symbols.rs
+++ b/compiler/rustc_middle/src/middle/exported_symbols.rs
@@ -21,6 +21,23 @@ impl SymbolExportLevel {
     }
 }
 
+/// Kind of exported symbols.
+#[derive(Eq, PartialEq, Debug, Copy, Clone, Encodable, Decodable, HashStable)]
+pub enum SymbolExportKind {
+    Text,
+    Data,
+    Tls,
+}
+
+/// The `SymbolExportInfo` of a symbols specifies symbol-related information
+/// that is relevant to code generation and linking.
+#[derive(Eq, PartialEq, Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)]
+pub struct SymbolExportInfo {
+    pub level: SymbolExportLevel,
+    pub kind: SymbolExportKind,
+    pub used: bool,
+}
+
 #[derive(Eq, PartialEq, Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)]
 pub enum ExportedSymbol<'tcx> {
     NonGeneric(DefId),
diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs
index 438f356f072..8cfc5ed0a95 100644
--- a/compiler/rustc_middle/src/mir/interpret/allocation.rs
+++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs
@@ -269,7 +269,7 @@ impl<Tag: Provenance, Extra> Allocation<Tag, Extra> {
     /// `get_bytes_with_uninit_and_ptr` instead,
     ///
     /// This function also guarantees that the resulting pointer will remain stable
-    /// even when new allocations are pushed to the `HashMap`. `copy_repeatedly` relies
+    /// even when new allocations are pushed to the `HashMap`. `mem_copy_repeatedly` relies
     /// on that.
     ///
     /// It is the caller's responsibility to check bounds and alignment beforehand.
@@ -418,6 +418,7 @@ impl<Tag: Provenance, Extra> Allocation<Tag, Extra> {
     ///
     /// It is the caller's responsibility to check bounds and alignment beforehand.
     /// Most likely, you want to call `InterpCx::write_scalar` instead of this method.
+    #[instrument(skip(self, cx), level = "debug")]
     pub fn write_scalar(
         &mut self,
         cx: &impl HasDataLayout,
@@ -429,8 +430,7 @@ impl<Tag: Provenance, Extra> Allocation<Tag, Extra> {
         let val = match val {
             ScalarMaybeUninit::Scalar(scalar) => scalar,
             ScalarMaybeUninit::Uninit => {
-                self.mark_init(range, false);
-                return Ok(());
+                return self.write_uninit(cx, range);
             }
         };
 
@@ -455,6 +455,13 @@ impl<Tag: Provenance, Extra> Allocation<Tag, Extra> {
 
         Ok(())
     }
+
+    /// Write "uninit" to the given memory range.
+    pub fn write_uninit(&mut self, cx: &impl HasDataLayout, range: AllocRange) -> AllocResult {
+        self.mark_init(range, false);
+        self.clear_relocations(cx, range)?;
+        return Ok(());
+    }
 }
 
 /// Relocations.
@@ -509,6 +516,9 @@ impl<Tag: Copy, Extra> Allocation<Tag, Extra> {
             if Tag::ERR_ON_PARTIAL_PTR_OVERWRITE {
                 return Err(AllocError::PartialPointerOverwrite(first));
             }
+            warn!(
+                "Partial pointer overwrite! De-initializing memory at offsets {first:?}..{start:?}."
+            );
             self.init_mask.set_range(first, start, false);
         }
         if last > end {
@@ -517,10 +527,15 @@ impl<Tag: Copy, Extra> Allocation<Tag, Extra> {
                     last - cx.data_layout().pointer_size,
                 ));
             }
+            warn!(
+                "Partial pointer overwrite! De-initializing memory at offsets {end:?}..{last:?}."
+            );
             self.init_mask.set_range(end, last, false);
         }
 
         // Forget all the relocations.
+        // Since relocations do not overlap, we know that removing until `last` (exclusive) is fine,
+        // i.e., this will not remove any other relocations just after the ones we care about.
         self.relocations.0.remove_range(first..last);
 
         Ok(())
@@ -561,8 +576,10 @@ impl<Tag> Deref for Relocations<Tag> {
 }
 
 /// A partial, owned list of relocations to transfer into another allocation.
+///
+/// Offsets are already adjusted to the destination allocation.
 pub struct AllocationRelocations<Tag> {
-    relative_relocations: Vec<(Size, Tag)>,
+    dest_relocations: Vec<(Size, Tag)>,
 }
 
 impl<Tag: Copy, Extra> Allocation<Tag, Extra> {
@@ -575,12 +592,17 @@ impl<Tag: Copy, Extra> Allocation<Tag, Extra> {
     ) -> AllocationRelocations<Tag> {
         let relocations = self.get_relocations(cx, src);
         if relocations.is_empty() {
-            return AllocationRelocations { relative_relocations: Vec::new() };
+            return AllocationRelocations { dest_relocations: Vec::new() };
         }
 
         let size = src.size;
         let mut new_relocations = Vec::with_capacity(relocations.len() * (count as usize));
 
+        // If `count` is large, this is rather wasteful -- we are allocating a big array here, which
+        // is mostly filled with redundant information since it's just N copies of the same `Tag`s
+        // at slightly adjusted offsets. The reason we do this is so that in `mark_relocation_range`
+        // we can use `insert_presorted`. That wouldn't work with an `Iterator` that just produces
+        // the right sequence of relocations for all N copies.
         for i in 0..count {
             new_relocations.extend(relocations.iter().map(|&(offset, reloc)| {
                 // compute offset for current repetition
@@ -593,14 +615,17 @@ impl<Tag: Copy, Extra> Allocation<Tag, Extra> {
             }));
         }
 
-        AllocationRelocations { relative_relocations: new_relocations }
+        AllocationRelocations { dest_relocations: new_relocations }
     }
 
     /// Applies a relocation copy.
     /// The affected range, as defined in the parameters to `prepare_relocation_copy` is expected
     /// to be clear of relocations.
+    ///
+    /// This is dangerous to use as it can violate internal `Allocation` invariants!
+    /// It only exists to support an efficient implementation of `mem_copy_repeatedly`.
     pub fn mark_relocation_range(&mut self, relocations: AllocationRelocations<Tag>) {
-        self.relocations.0.insert_presorted(relocations.relative_relocations);
+        self.relocations.0.insert_presorted(relocations.dest_relocations);
     }
 }
 
@@ -1056,7 +1081,7 @@ impl<Tag: Copy, Extra> Allocation<Tag, Extra> {
         })
     }
 
-    pub fn mark_init(&mut self, range: AllocRange, is_init: bool) {
+    fn mark_init(&mut self, range: AllocRange, is_init: bool) {
         if range.size.bytes() == 0 {
             return;
         }
@@ -1118,6 +1143,9 @@ impl<Tag, Extra> Allocation<Tag, Extra> {
     }
 
     /// Applies multiple instances of the run-length encoding to the initialization mask.
+    ///
+    /// This is dangerous to use as it can violate internal `Allocation` invariants!
+    /// It only exists to support an efficient implementation of `mem_copy_repeatedly`.
     pub fn mark_compressed_init_range(
         &mut self,
         defined: &InitMaskCompressed,
diff --git a/compiler/rustc_middle/src/mir/interpret/pointer.rs b/compiler/rustc_middle/src/mir/interpret/pointer.rs
index 813c0912f53..c71aea417ec 100644
--- a/compiler/rustc_middle/src/mir/interpret/pointer.rs
+++ b/compiler/rustc_middle/src/mir/interpret/pointer.rs
@@ -163,6 +163,9 @@ pub struct Pointer<Tag = AllocId> {
 }
 
 static_assert_size!(Pointer, 16);
+// `Option<Tag>` pointers are also passed around quite a bit
+// (but not stored in permanent machine state).
+static_assert_size!(Pointer<Option<AllocId>>, 16);
 
 // We want the `Debug` output to be readable as it is used by `derive(Debug)` for
 // all the Miri types.
@@ -198,12 +201,26 @@ impl<Tag> From<Pointer<Tag>> for Pointer<Option<Tag>> {
 }
 
 impl<Tag> Pointer<Option<Tag>> {
+    /// Convert this pointer that *might* have a tag into a pointer that *definitely* has a tag, or
+    /// an absolute address.
+    ///
+    /// This is rarely what you want; call `ptr_try_get_alloc_id` instead.
     pub fn into_pointer_or_addr(self) -> Result<Pointer<Tag>, Size> {
         match self.provenance {
             Some(tag) => Ok(Pointer::new(tag, self.offset)),
             None => Err(self.offset),
         }
     }
+
+    /// Returns the absolute address the pointer points to.
+    /// Only works if Tag::OFFSET_IS_ADDR is true!
+    pub fn addr(self) -> Size
+    where
+        Tag: Provenance,
+    {
+        assert!(Tag::OFFSET_IS_ADDR);
+        self.offset
+    }
 }
 
 impl<Tag> Pointer<Option<Tag>> {
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index 4f4b6cf704f..0dff71be4b8 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -62,7 +62,9 @@ pub mod spanview;
 mod switch_sources;
 pub mod tcx;
 pub mod terminator;
+use crate::mir::traversal::PostorderCache;
 pub use terminator::*;
+
 pub mod traversal;
 mod type_foldable;
 pub mod visit;
@@ -166,7 +168,8 @@ pub enum MirPhase {
     /// * [`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.
+    /// terminator means that the auto-generated drop glue will be invoked. Also, `Copy` operands
+    /// are allowed for non-`Copy` types.
     DropsLowered = 3,
     /// Beginning with this phase, the following variant is disallowed:
     /// * [`Rvalue::Aggregate`] for any `AggregateKind` except `Array`
@@ -322,6 +325,7 @@ pub struct Body<'tcx> {
     predecessor_cache: PredecessorCache,
     switch_source_cache: SwitchSourceCache,
     is_cyclic: GraphIsCyclicCache,
+    postorder_cache: PostorderCache,
 
     pub tainted_by_errors: Option<ErrorGuaranteed>,
 }
@@ -371,6 +375,7 @@ impl<'tcx> Body<'tcx> {
             predecessor_cache: PredecessorCache::new(),
             switch_source_cache: SwitchSourceCache::new(),
             is_cyclic: GraphIsCyclicCache::new(),
+            postorder_cache: PostorderCache::new(),
             tainted_by_errors,
         };
         body.is_polymorphic = body.has_param_types_or_consts();
@@ -400,6 +405,7 @@ impl<'tcx> Body<'tcx> {
             predecessor_cache: PredecessorCache::new(),
             switch_source_cache: SwitchSourceCache::new(),
             is_cyclic: GraphIsCyclicCache::new(),
+            postorder_cache: PostorderCache::new(),
             tainted_by_errors: None,
         };
         body.is_polymorphic = body.has_param_types_or_consts();
@@ -421,6 +427,7 @@ impl<'tcx> Body<'tcx> {
         self.predecessor_cache.invalidate();
         self.switch_source_cache.invalidate();
         self.is_cyclic.invalidate();
+        self.postorder_cache.invalidate();
         &mut self.basic_blocks
     }
 
@@ -431,6 +438,7 @@ impl<'tcx> Body<'tcx> {
         self.predecessor_cache.invalidate();
         self.switch_source_cache.invalidate();
         self.is_cyclic.invalidate();
+        self.postorder_cache.invalidate();
         (&mut self.basic_blocks, &mut self.local_decls)
     }
 
@@ -445,6 +453,7 @@ impl<'tcx> Body<'tcx> {
         self.predecessor_cache.invalidate();
         self.switch_source_cache.invalidate();
         self.is_cyclic.invalidate();
+        self.postorder_cache.invalidate();
         (&mut self.basic_blocks, &mut self.local_decls, &mut self.var_debug_info)
     }
 
@@ -1078,6 +1087,8 @@ pub enum LocalInfo<'tcx> {
     /// A temporary created during the creation of an aggregate
     /// (e.g. a temporary for `foo` in `MyStruct { my_field: foo }`)
     AggregateTemp,
+    /// A temporary created during the pass `Derefer` to avoid it's retagging
+    DerefTemp,
 }
 
 impl<'tcx> LocalDecl<'tcx> {
@@ -2330,7 +2341,10 @@ pub struct SourceScopeLocalData {
 /// validator.
 #[derive(Clone, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)]
 pub enum Operand<'tcx> {
-    /// Creates a value by loading the given place. The type of the place must be `Copy`
+    /// Creates a value by loading the given place.
+    ///
+    /// Before drop elaboration, the type of the place must be `Copy`. After drop elaboration there
+    /// is no such requirement.
     Copy(Place<'tcx>),
 
     /// Creates a value by performing loading the place, just like the `Copy` operand.
@@ -2577,8 +2591,6 @@ pub enum Rvalue<'tcx> {
     /// This is different from a normal transmute because dataflow analysis will treat the box as
     /// initialized but its content as uninitialized. Like other pointer casts, this in general
     /// affects alias analysis.
-    ///
-    /// Disallowed after drop elaboration.
     ShallowInitBox(Operand<'tcx>, Ty<'tcx>),
 }
 
diff --git a/compiler/rustc_middle/src/mir/patch.rs b/compiler/rustc_middle/src/mir/patch.rs
index 1f571a36441..d03f9235efd 100644
--- a/compiler/rustc_middle/src/mir/patch.rs
+++ b/compiler/rustc_middle/src/mir/patch.rs
@@ -78,13 +78,24 @@ impl<'tcx> MirPatch<'tcx> {
         Location { block: bb, statement_index: offset }
     }
 
-    pub fn new_temp(&mut self, ty: Ty<'tcx>, span: Span) -> Local {
+    pub fn new_local_with_info(
+        &mut self,
+        ty: Ty<'tcx>,
+        span: Span,
+        local_info: Option<Box<LocalInfo<'tcx>>>,
+    ) -> Local {
         let index = self.next_local;
         self.next_local += 1;
-        self.new_locals.push(LocalDecl::new(ty, span));
+        let mut new_decl = LocalDecl::new(ty, span);
+        new_decl.local_info = local_info;
+        self.new_locals.push(new_decl);
         Local::new(index as usize)
     }
 
+    pub fn new_temp(&mut self, ty: Ty<'tcx>, span: Span) -> Local {
+        self.new_local_with_info(ty, span, None)
+    }
+
     pub fn new_internal(&mut self, ty: Ty<'tcx>, span: Span) -> Local {
         let index = self.next_local;
         self.next_local += 1;
@@ -141,6 +152,7 @@ impl<'tcx> MirPatch<'tcx> {
 
         let mut delta = 0;
         let mut last_bb = START_BLOCK;
+        let mut stmts_and_targets: Vec<(Statement<'_>, BasicBlock)> = Vec::new();
         for (mut loc, stmt) in new_statements {
             if loc.block != last_bb {
                 delta = 0;
@@ -149,11 +161,30 @@ impl<'tcx> MirPatch<'tcx> {
             debug!("MirPatch: adding statement {:?} at loc {:?}+{}", stmt, loc, delta);
             loc.statement_index += delta;
             let source_info = Self::source_info_for_index(&body[loc.block], loc);
+
+            // For mir-opt `Derefer` to work in all cases we need to
+            // get terminator's targets and apply the statement to all of them.
+            if loc.statement_index > body[loc.block].statements.len() {
+                let term = body[loc.block].terminator();
+                let successors = term.successors().clone();
+
+                for i in successors {
+                    stmts_and_targets
+                        .push((Statement { source_info, kind: stmt.clone() }, i.clone()));
+                }
+                delta += 1;
+                continue;
+            }
+
             body[loc.block]
                 .statements
                 .insert(loc.statement_index, Statement { source_info, kind: stmt });
             delta += 1;
         }
+
+        for (stmt, target) in stmts_and_targets.into_iter().rev() {
+            body[target].statements.insert(0, stmt);
+        }
     }
 
     pub fn source_info_for_index(data: &BasicBlockData<'_>, loc: Location) -> SourceInfo {
diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs
index 69dac038839..b7f695da544 100644
--- a/compiler/rustc_middle/src/mir/pretty.rs
+++ b/compiler/rustc_middle/src/mir/pretty.rs
@@ -851,6 +851,7 @@ fn write_allocation_bytes<'tcx, Tag: Provenance, Extra>(
         }
         if let Some(&tag) = alloc.relocations().get(&i) {
             // Memory with a relocation must be defined
+            assert!(alloc.init_mask().is_range_initialized(i, i + ptr_size).is_ok());
             let j = i.bytes_usize();
             let offset = alloc
                 .inspect_with_uninit_and_ptr_outside_interpreter(j..j + ptr_size.bytes_usize());
diff --git a/compiler/rustc_middle/src/mir/traversal.rs b/compiler/rustc_middle/src/mir/traversal.rs
index d08bede1d73..8d831cc73b8 100644
--- a/compiler/rustc_middle/src/mir/traversal.rs
+++ b/compiler/rustc_middle/src/mir/traversal.rs
@@ -1,4 +1,7 @@
+use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
+use rustc_data_structures::sync::OnceCell;
 use rustc_index::bit_set::BitSet;
+use rustc_serialize as serialize;
 
 use super::*;
 
@@ -268,10 +271,6 @@ impl<'a, 'tcx> ReversePostorder<'a, 'tcx> {
     }
 }
 
-pub fn reverse_postorder<'a, 'tcx>(body: &'a Body<'tcx>) -> ReversePostorder<'a, 'tcx> {
-    ReversePostorder::new(body, START_BLOCK)
-}
-
 impl<'a, 'tcx> Iterator for ReversePostorder<'a, 'tcx> {
     type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
 
@@ -307,3 +306,86 @@ pub fn reachable_as_bitset<'tcx>(body: &Body<'tcx>) -> BitSet<BasicBlock> {
     (&mut iter).for_each(drop);
     iter.visited
 }
+
+#[derive(Clone)]
+pub struct ReversePostorderIter<'a, 'tcx> {
+    body: &'a Body<'tcx>,
+    blocks: &'a Vec<BasicBlock>,
+    idx: usize,
+}
+
+impl<'a, 'tcx> Iterator for ReversePostorderIter<'a, 'tcx> {
+    type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
+
+    fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> {
+        if self.idx == 0 {
+            return None;
+        }
+        self.idx -= 1;
+
+        self.blocks.get(self.idx).map(|&bb| (bb, &self.body[bb]))
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (self.idx, Some(self.idx))
+    }
+}
+
+impl<'a, 'tcx> ExactSizeIterator for ReversePostorderIter<'a, 'tcx> {}
+
+pub fn reverse_postorder<'a, 'tcx>(body: &'a Body<'tcx>) -> ReversePostorderIter<'a, 'tcx> {
+    let blocks = body.postorder_cache.compute(body);
+
+    let len = blocks.len();
+
+    ReversePostorderIter { body, blocks, idx: len }
+}
+
+#[derive(Clone, Debug)]
+pub(super) struct PostorderCache {
+    cache: OnceCell<Vec<BasicBlock>>,
+}
+
+impl PostorderCache {
+    #[inline]
+    pub(super) fn new() -> Self {
+        PostorderCache { cache: OnceCell::new() }
+    }
+
+    /// Invalidates the postorder cache.
+    #[inline]
+    pub(super) fn invalidate(&mut self) {
+        self.cache = OnceCell::new();
+    }
+
+    /// Returns the &Vec<BasicBlocks> represents the postorder graph for this MIR.
+    #[inline]
+    pub(super) fn compute(&self, body: &Body<'_>) -> &Vec<BasicBlock> {
+        self.cache.get_or_init(|| Postorder::new(body, START_BLOCK).map(|(bb, _)| bb).collect())
+    }
+}
+
+impl<S: serialize::Encoder> serialize::Encodable<S> for PostorderCache {
+    #[inline]
+    fn encode(&self, s: &mut S) -> Result<(), S::Error> {
+        s.emit_unit()
+    }
+}
+
+impl<D: serialize::Decoder> serialize::Decodable<D> for PostorderCache {
+    #[inline]
+    fn decode(_: &mut D) -> Self {
+        Self::new()
+    }
+}
+
+impl<CTX> HashStable<CTX> for PostorderCache {
+    #[inline]
+    fn hash_stable(&self, _: &mut CTX, _: &mut StableHasher) {
+        // do nothing
+    }
+}
+
+TrivialTypeFoldableAndLiftImpls! {
+    PostorderCache,
+}
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 78a33832433..cc80ab8f16e 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -936,6 +936,11 @@ rustc_queries! {
         remap_env_constness
     }
 
+    /// Converts a type level constant value into `ConstValue`
+    query valtree_to_const_val(key: (Ty<'tcx>, ty::ValTree<'tcx>)) -> ConstValue<'tcx> {
+        desc { "convert type-level constant value to mir constant value"}
+    }
+
     /// Destructure a constant ADT or array into its variant index and its
     /// field values or return `None` if constant is invalid.
     ///
@@ -1359,7 +1364,7 @@ rustc_queries! {
     // Does not include external symbols that don't have a corresponding DefId,
     // like the compiler-generated `main` function and so on.
     query reachable_non_generics(_: CrateNum)
-        -> DefIdMap<SymbolExportLevel> {
+        -> DefIdMap<SymbolExportInfo> {
         storage(ArenaCacheSelector<'tcx>)
         desc { "looking up the exported symbols of a crate" }
         separate_provide_extern
@@ -1675,7 +1680,7 @@ rustc_queries! {
     ///   correspond to a publicly visible symbol in `cnum` machine code.
     /// - The `exported_symbols` sets of different crates do not intersect.
     query exported_symbols(_: CrateNum)
-        -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportLevel)] {
+        -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportInfo)] {
         desc { "exported_symbols" }
         separate_provide_extern
     }
diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs
index e56efb8d497..fdf5ecfdaf7 100644
--- a/compiler/rustc_middle/src/thir.rs
+++ b/compiler/rustc_middle/src/thir.rs
@@ -66,7 +66,7 @@ macro_rules! thir_with_elements {
         /// A container for a THIR body.
         ///
         /// This can be indexed directly by any THIR index (e.g. [`ExprId`]).
-        #[derive(Debug, HashStable)]
+        #[derive(Debug, HashStable, Clone)]
         pub struct Thir<'tcx> {
             $(
                 pub $name: IndexVec<$id, $value>,
@@ -106,7 +106,7 @@ pub enum LintLevel {
     Explicit(hir::HirId),
 }
 
-#[derive(Debug, HashStable)]
+#[derive(Clone, Debug, HashStable)]
 pub struct Block {
     /// Whether the block itself has a label. Used by `label: {}`
     /// and `try` blocks.
@@ -125,7 +125,7 @@ pub struct Block {
     pub safety_mode: BlockSafety,
 }
 
-#[derive(Debug, HashStable)]
+#[derive(Clone, Debug, HashStable)]
 pub struct Adt<'tcx> {
     /// The ADT we're constructing.
     pub adt_def: AdtDef<'tcx>,
@@ -151,13 +151,13 @@ pub enum BlockSafety {
     ExplicitUnsafe(hir::HirId),
 }
 
-#[derive(Debug, HashStable)]
+#[derive(Clone, Debug, HashStable)]
 pub struct Stmt<'tcx> {
     pub kind: StmtKind<'tcx>,
     pub opt_destruction_scope: Option<region::Scope>,
 }
 
-#[derive(Debug, HashStable)]
+#[derive(Clone, Debug, HashStable)]
 pub enum StmtKind<'tcx> {
     /// An expression with a trailing semicolon.
     Expr {
@@ -196,7 +196,7 @@ pub enum StmtKind<'tcx> {
 rustc_data_structures::static_assert_size!(Expr<'_>, 104);
 
 /// A THIR expression.
-#[derive(Debug, HashStable)]
+#[derive(Clone, Debug, HashStable)]
 pub struct Expr<'tcx> {
     /// The type of this expression
     pub ty: Ty<'tcx>,
@@ -212,7 +212,7 @@ pub struct Expr<'tcx> {
     pub kind: ExprKind<'tcx>,
 }
 
-#[derive(Debug, HashStable)]
+#[derive(Clone, Debug, HashStable)]
 pub enum ExprKind<'tcx> {
     /// `Scope`s are used to explicitly mark destruction scopes,
     /// and to track the `HirId` of the expressions within the scope.
@@ -461,20 +461,20 @@ impl<'tcx> ExprKind<'tcx> {
 /// Represents the association of a field identifier and an expression.
 ///
 /// This is used in struct constructors.
-#[derive(Debug, HashStable)]
+#[derive(Clone, Debug, HashStable)]
 pub struct FieldExpr {
     pub name: Field,
     pub expr: ExprId,
 }
 
-#[derive(Debug, HashStable)]
+#[derive(Clone, Debug, HashStable)]
 pub struct FruInfo<'tcx> {
     pub base: ExprId,
     pub field_types: Box<[Ty<'tcx>]>,
 }
 
 /// A `match` arm.
-#[derive(Debug, HashStable)]
+#[derive(Clone, Debug, HashStable)]
 pub struct Arm<'tcx> {
     pub pattern: Pat<'tcx>,
     pub guard: Option<Guard<'tcx>>,
@@ -485,7 +485,7 @@ pub struct Arm<'tcx> {
 }
 
 /// A `match` guard.
-#[derive(Debug, HashStable)]
+#[derive(Clone, Debug, HashStable)]
 pub enum Guard<'tcx> {
     If(ExprId),
     IfLet(Pat<'tcx>, ExprId),
@@ -499,7 +499,7 @@ pub enum LogicalOp {
     Or,
 }
 
-#[derive(Debug, HashStable)]
+#[derive(Clone, Debug, HashStable)]
 pub enum InlineAsmOperand<'tcx> {
     In {
         reg: InlineAsmRegOrRegClass,
diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs
index 7fcc46cc7c2..23c377651cc 100644
--- a/compiler/rustc_middle/src/ty/codec.rs
+++ b/compiler/rustc_middle/src/ty/codec.rs
@@ -453,9 +453,6 @@ macro_rules! impl_arena_allocatable_decoder {
             }
         }
     };
-    ([$ignore:ident $(, $attrs:ident)*]$args:tt) => {
-        impl_arena_allocatable_decoder!([$($attrs),*]$args);
-    };
 }
 
 macro_rules! impl_arena_allocatable_decoders {
diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs
index 4b7c1d44cea..7af7eb4f5ec 100644
--- a/compiler/rustc_middle/src/ty/consts.rs
+++ b/compiler/rustc_middle/src/ty/consts.rs
@@ -44,10 +44,12 @@ pub struct ConstS<'tcx> {
 static_assert_size!(ConstS<'_>, 48);
 
 impl<'tcx> Const<'tcx> {
+    #[inline]
     pub fn ty(self) -> Ty<'tcx> {
         self.0.ty
     }
 
+    #[inline]
     pub fn val(self) -> ConstKind<'tcx> {
         self.0.val
     }
diff --git a/compiler/rustc_middle/src/ty/consts/int.rs b/compiler/rustc_middle/src/ty/consts/int.rs
index 72623ba54ee..a3ce674c115 100644
--- a/compiler/rustc_middle/src/ty/consts/int.rs
+++ b/compiler/rustc_middle/src/ty/consts/int.rs
@@ -237,6 +237,98 @@ impl ScalarInt {
     pub fn try_to_machine_usize<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Result<u64, Size> {
         Ok(self.to_bits(tcx.data_layout.pointer_size)? as u64)
     }
+
+    /// Tries to convert the `ScalarInt` to an unsigned integer of the given size.
+    /// Fails if the size of the `ScalarInt` is unequal to `size` and returns the
+    /// `ScalarInt`s size in that case.
+    #[inline]
+    pub fn try_to_uint(self, size: Size) -> Result<u128, Size> {
+        self.to_bits(size)
+    }
+
+    // Tries to convert the `ScalarInt` to `u8`. Fails if the `size` of the `ScalarInt`
+    // in not equal to `Size { raw: 1 }` and returns the `size` value of the `ScalarInt` in
+    // that case.
+    #[inline]
+    pub fn try_to_u8(self) -> Result<u8, Size> {
+        self.to_bits(Size::from_bits(8)).map(|v| u8::try_from(v).unwrap())
+    }
+
+    /// Tries to convert the `ScalarInt` to `u16`. Fails if the size of the `ScalarInt`
+    /// in not equal to `Size { raw: 2 }` and returns the `size` value of the `ScalarInt` in
+    /// that case.
+    #[inline]
+    pub fn try_to_u16(self) -> Result<u16, Size> {
+        self.to_bits(Size::from_bits(16)).map(|v| u16::try_from(v).unwrap())
+    }
+
+    /// Tries to convert the `ScalarInt` to `u32`. Fails if the `size` of the `ScalarInt`
+    /// in not equal to `Size { raw: 4 }` and returns the `size` value of the `ScalarInt` in
+    /// that case.
+    #[inline]
+    pub fn try_to_u32(self) -> Result<u32, Size> {
+        self.to_bits(Size::from_bits(32)).map(|v| u32::try_from(v).unwrap())
+    }
+
+    /// Tries to convert the `ScalarInt` to `u64`. Fails if the `size` of the `ScalarInt`
+    /// in not equal to `Size { raw: 8 }` and returns the `size` value of the `ScalarInt` in
+    /// that case.
+    #[inline]
+    pub fn try_to_u64(self) -> Result<u64, Size> {
+        self.to_bits(Size::from_bits(64)).map(|v| u64::try_from(v).unwrap())
+    }
+
+    /// Tries to convert the `ScalarInt` to `u128`. Fails if the `size` of the `ScalarInt`
+    /// in not equal to `Size { raw: 16 }` and returns the `size` value of the `ScalarInt` in
+    /// that case.
+    #[inline]
+    pub fn try_to_u128(self) -> Result<u128, Size> {
+        self.to_bits(Size::from_bits(128))
+    }
+
+    /// Tries to convert the `ScalarInt` to a signed integer of the given size.
+    /// Fails if the size of the `ScalarInt` is unequal to `size` and returns the
+    /// `ScalarInt`s size in that case.
+    #[inline]
+    pub fn try_to_int(self, size: Size) -> Result<i128, Size> {
+        let b = self.to_bits(size)?;
+        Ok(size.sign_extend(b) as i128)
+    }
+
+    /// Tries to convert the `ScalarInt` to i8.
+    /// Fails if the size of the `ScalarInt` is unequal to `Size { raw: 1 }`
+    /// and returns the `ScalarInt`s size in that case.
+    pub fn try_to_i8(self) -> Result<i8, Size> {
+        self.try_to_int(Size::from_bits(8)).map(|v| i8::try_from(v).unwrap())
+    }
+
+    /// Tries to convert the `ScalarInt` to i16.
+    /// Fails if the size of the `ScalarInt` is unequal to `Size { raw: 2 }`
+    /// and returns the `ScalarInt`s size in that case.
+    pub fn try_to_i16(self) -> Result<i16, Size> {
+        self.try_to_int(Size::from_bits(16)).map(|v| i16::try_from(v).unwrap())
+    }
+
+    /// Tries to convert the `ScalarInt` to i32.
+    /// Fails if the size of the `ScalarInt` is unequal to `Size { raw: 4 }`
+    /// and returns the `ScalarInt`s size in that case.
+    pub fn try_to_i32(self) -> Result<i32, Size> {
+        self.try_to_int(Size::from_bits(32)).map(|v| i32::try_from(v).unwrap())
+    }
+
+    /// Tries to convert the `ScalarInt` to i64.
+    /// Fails if the size of the `ScalarInt` is unequal to `Size { raw: 8 }`
+    /// and returns the `ScalarInt`s size in that case.
+    pub fn try_to_i64(self) -> Result<i64, Size> {
+        self.try_to_int(Size::from_bits(64)).map(|v| i64::try_from(v).unwrap())
+    }
+
+    /// Tries to convert the `ScalarInt` to i128.
+    /// Fails if the size of the `ScalarInt` is unequal to `Size { raw: 16 }`
+    /// and returns the `ScalarInt`s size in that case.
+    pub fn try_to_i128(self) -> Result<i128, Size> {
+        self.try_to_int(Size::from_bits(128)).map(|v| i128::try_from(v).unwrap())
+    }
 }
 
 macro_rules! from {
diff --git a/compiler/rustc_middle/src/ty/consts/valtree.rs b/compiler/rustc_middle/src/ty/consts/valtree.rs
index 195760c0590..418848f69d7 100644
--- a/compiler/rustc_middle/src/ty/consts/valtree.rs
+++ b/compiler/rustc_middle/src/ty/consts/valtree.rs
@@ -20,6 +20,9 @@ pub enum ValTree<'tcx> {
     /// See the `ScalarInt` documentation for how `ScalarInt` guarantees that equal values
     /// of these types have the same representation.
     Leaf(ScalarInt),
+
+    //SliceOrStr(ValSlice<'tcx>),
+    // dont use SliceOrStr for now
     /// The fields of any kind of aggregate. Structs, tuples and arrays are represented by
     /// listing their fields' values in order.
     /// Enums are represented by storing their discriminant as a field, followed by all
@@ -31,4 +34,20 @@ impl<'tcx> ValTree<'tcx> {
     pub fn zst() -> Self {
         Self::Branch(&[])
     }
+
+    #[inline]
+    pub fn unwrap_leaf(self) -> ScalarInt {
+        match self {
+            Self::Leaf(s) => s,
+            _ => bug!("expected leaf, got {:?}", self),
+        }
+    }
+
+    #[inline]
+    pub fn unwrap_branch(self) -> &'tcx [Self] {
+        match self {
+            Self::Branch(branch) => branch,
+            _ => bug!("expected branch, got {:?}", self),
+        }
+    }
 }
diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs
index 3b044b19259..f53dc0000ca 100644
--- a/compiler/rustc_middle/src/ty/diagnostics.rs
+++ b/compiler/rustc_middle/src/ty/diagnostics.rs
@@ -3,15 +3,15 @@
 use crate::ty::subst::{GenericArg, GenericArgKind};
 use crate::ty::TyKind::*;
 use crate::ty::{
-    ConstKind, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, InferTy,
-    ProjectionTy, Term, Ty, TyCtxt, TypeAndMut,
+    ConstKind, DefIdTree, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef,
+    InferTy, ProjectionTy, Term, Ty, TyCtxt, TypeAndMut,
 };
 
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{Applicability, Diagnostic, DiagnosticArgValue, IntoDiagnosticArg};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
-use rustc_hir::{QPath, TyKind, WhereBoundPredicate, WherePredicate};
+use rustc_hir::WherePredicate;
 use rustc_span::Span;
 
 impl<'tcx> IntoDiagnosticArg for Ty<'tcx> {
@@ -74,10 +74,10 @@ impl<'tcx> Ty<'tcx> {
     }
 
     /// Whether the type can be safely suggested during error recovery.
-    pub fn is_suggestable(self) -> bool {
-        fn generic_arg_is_suggestible(arg: GenericArg<'_>) -> bool {
+    pub fn is_suggestable(self, tcx: TyCtxt<'tcx>) -> bool {
+        fn generic_arg_is_suggestible<'tcx>(arg: GenericArg<'tcx>, tcx: TyCtxt<'tcx>) -> bool {
             match arg.unpack() {
-                GenericArgKind::Type(ty) => ty.is_suggestable(),
+                GenericArgKind::Type(ty) => ty.is_suggestable(tcx),
                 GenericArgKind::Const(c) => const_is_suggestable(c.val()),
                 _ => true,
             }
@@ -99,8 +99,7 @@ impl<'tcx> Ty<'tcx> {
         // temporary, so I'll leave this as a fixme.
 
         match self.kind() {
-            Opaque(..)
-            | FnDef(..)
+            FnDef(..)
             | Closure(..)
             | Infer(..)
             | Generator(..)
@@ -108,27 +107,38 @@ impl<'tcx> Ty<'tcx> {
             | Bound(_, _)
             | Placeholder(_)
             | Error(_) => false,
+            Opaque(did, substs) => {
+                let parent = tcx.parent(*did).expect("opaque types always have a parent");
+                if let hir::def::DefKind::TyAlias | hir::def::DefKind::AssocTy = tcx.def_kind(parent)
+                    && let Opaque(parent_did, _) = tcx.type_of(parent).kind()
+                    && parent_did == did
+                {
+                    substs.iter().all(|a| generic_arg_is_suggestible(a, tcx))
+                } else {
+                    false
+                }
+            }
             Dynamic(dty, _) => dty.iter().all(|pred| match pred.skip_binder() {
                 ExistentialPredicate::Trait(ExistentialTraitRef { substs, .. }) => {
-                    substs.iter().all(generic_arg_is_suggestible)
+                    substs.iter().all(|a| generic_arg_is_suggestible(a, tcx))
                 }
                 ExistentialPredicate::Projection(ExistentialProjection {
                     substs, term, ..
                 }) => {
                     let term_is_suggestable = match term {
-                        Term::Ty(ty) => ty.is_suggestable(),
+                        Term::Ty(ty) => ty.is_suggestable(tcx),
                         Term::Const(c) => const_is_suggestable(c.val()),
                     };
-                    term_is_suggestable && substs.iter().all(generic_arg_is_suggestible)
+                    term_is_suggestable && substs.iter().all(|a| generic_arg_is_suggestible(a, tcx))
                 }
                 _ => true,
             }),
             Projection(ProjectionTy { substs: args, .. }) | Adt(_, args) => {
-                args.iter().all(generic_arg_is_suggestible)
+                args.iter().all(|a| generic_arg_is_suggestible(a, tcx))
             }
-            Tuple(args) => args.iter().all(|ty| ty.is_suggestable()),
-            Slice(ty) | RawPtr(TypeAndMut { ty, .. }) | Ref(_, ty, _) => ty.is_suggestable(),
-            Array(ty, c) => ty.is_suggestable() && const_is_suggestable(c.val()),
+            Tuple(args) => args.iter().all(|ty| ty.is_suggestable(tcx)),
+            Slice(ty) | RawPtr(TypeAndMut { ty, .. }) | Ref(_, ty, _) => ty.is_suggestable(tcx),
+            Array(ty, c) => ty.is_suggestable(tcx) && const_is_suggestable(c.val()),
             _ => true,
         }
     }
@@ -146,13 +156,13 @@ pub fn suggest_arbitrary_trait_bound(
         _ => {}
     }
     // Suggest a where clause bound for a non-type parameter.
-    let (action, prefix) = if generics.where_clause.predicates.is_empty() {
-        ("introducing a", " where ")
-    } else {
+    let (action, prefix) = if generics.has_where_clause {
         ("extending the", ", ")
+    } else {
+        ("introducing a", " where ")
     };
     err.span_suggestion_verbose(
-        generics.where_clause.tail_span_for_suggestion(),
+        generics.tail_span_for_predicate_suggestion(),
         &format!(
             "consider {} `where` bound, but there might be an alternative better way to express \
              this requirement",
@@ -173,104 +183,37 @@ enum SuggestChangingConstraintsMessage<'a> {
 }
 
 fn suggest_removing_unsized_bound(
+    tcx: TyCtxt<'_>,
     generics: &hir::Generics<'_>,
     suggestions: &mut Vec<(Span, String, SuggestChangingConstraintsMessage<'_>)>,
-    param_name: &str,
     param: &hir::GenericParam<'_>,
     def_id: Option<DefId>,
 ) {
     // See if there's a `?Sized` bound that can be removed to suggest that.
     // First look at the `where` clause because we can have `where T: ?Sized`,
     // then look at params.
-    for (where_pos, predicate) in generics.where_clause.predicates.iter().enumerate() {
-        match predicate {
-            WherePredicate::BoundPredicate(WhereBoundPredicate {
-                bounded_ty:
-                    hir::Ty {
-                        kind:
-                            hir::TyKind::Path(hir::QPath::Resolved(
-                                None,
-                                hir::Path {
-                                    segments: [segment],
-                                    res: hir::def::Res::Def(hir::def::DefKind::TyParam, _),
-                                    ..
-                                },
-                            )),
-                        ..
-                    },
-                bounds,
-                span,
-                ..
-            }) if segment.ident.as_str() == param_name => {
-                for (pos, bound) in bounds.iter().enumerate() {
-                    match bound {
-                        hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe)
-                            if poly.trait_ref.trait_def_id() == def_id => {}
-                        _ => continue,
-                    }
-                    let sp = match (
-                        bounds.len(),
-                        pos,
-                        generics.where_clause.predicates.len(),
-                        where_pos,
-                    ) {
-                        // where T: ?Sized
-                        // ^^^^^^^^^^^^^^^
-                        (1, _, 1, _) => generics.where_clause.span,
-                        // where Foo: Bar, T: ?Sized,
-                        //               ^^^^^^^^^^^
-                        (1, _, len, pos) if pos == len - 1 => generics.where_clause.predicates
-                            [pos - 1]
-                            .span()
-                            .shrink_to_hi()
-                            .to(*span),
-                        // where T: ?Sized, Foo: Bar,
-                        //       ^^^^^^^^^^^
-                        (1, _, _, pos) => {
-                            span.until(generics.where_clause.predicates[pos + 1].span())
-                        }
-                        // where T: ?Sized + Bar, Foo: Bar,
-                        //          ^^^^^^^^^
-                        (_, 0, _, _) => bound.span().to(bounds[1].span().shrink_to_lo()),
-                        // where T: Bar + ?Sized, Foo: Bar,
-                        //             ^^^^^^^^^
-                        (_, pos, _, _) => bounds[pos - 1].span().shrink_to_hi().to(bound.span()),
-                    };
+    let param_def_id = tcx.hir().local_def_id(param.hir_id);
+    for (where_pos, predicate) in generics.predicates.iter().enumerate() {
+        let WherePredicate::BoundPredicate(predicate) = predicate else {
+            continue;
+        };
+        if !predicate.is_param_bound(param_def_id.to_def_id()) {
+            continue;
+        };
 
-                    suggestions.push((
-                        sp,
-                        String::new(),
-                        SuggestChangingConstraintsMessage::RemovingQSized,
-                    ));
-                }
-            }
-            _ => {}
-        }
-    }
-    for (pos, bound) in param.bounds.iter().enumerate() {
-        match bound {
-            hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe)
-                if poly.trait_ref.trait_def_id() == def_id =>
-            {
-                let sp = match (param.bounds.len(), pos) {
-                    // T: ?Sized,
-                    //  ^^^^^^^^
-                    (1, _) => param.span.shrink_to_hi().to(bound.span()),
-                    // T: ?Sized + Bar,
-                    //    ^^^^^^^^^
-                    (_, 0) => bound.span().to(param.bounds[1].span().shrink_to_lo()),
-                    // T: Bar + ?Sized,
-                    //       ^^^^^^^^^
-                    (_, pos) => param.bounds[pos - 1].span().shrink_to_hi().to(bound.span()),
-                };
-
-                suggestions.push((
-                    sp,
-                    String::new(),
-                    SuggestChangingConstraintsMessage::RemovingQSized,
-                ));
+        for (pos, bound) in predicate.bounds.iter().enumerate() {
+            let    hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe) = bound else {
+                continue;
+            };
+            if poly.trait_ref.trait_def_id() != def_id {
+                continue;
             }
-            _ => {}
+            let sp = generics.span_for_bound_removal(where_pos, pos);
+            suggestions.push((
+                sp,
+                String::new(),
+                SuggestChangingConstraintsMessage::RemovingQSized,
+            ));
         }
     }
 }
@@ -321,13 +264,7 @@ pub fn suggest_constraining_type_params<'a>(
                     param.span,
                     &format!("this type parameter needs to be `{}`", constraint),
                 );
-                suggest_removing_unsized_bound(
-                    generics,
-                    &mut suggestions,
-                    param_name,
-                    param,
-                    def_id,
-                );
+                suggest_removing_unsized_bound(tcx, generics, &mut suggestions, param, def_id);
             }
         }
 
@@ -348,76 +285,45 @@ pub fn suggest_constraining_type_params<'a>(
             ))
         };
 
-        if param_name.starts_with("impl ") {
-            // If there's an `impl Trait` used in argument position, suggest
-            // restricting it:
-            //
-            //   fn foo(t: impl Foo) { ... }
-            //             --------
-            //             |
-            //             help: consider further restricting this bound with `+ Bar`
-            //
-            // Suggestion for tools in this case is:
-            //
-            //   fn foo(t: impl Foo) { ... }
-            //             --------
-            //             |
-            //             replace with: `impl Foo + Bar`
-
-            // `impl Trait` must have at least one trait in the list
-            let bound_list_non_empty = true;
-
-            suggest_restrict(param.span.shrink_to_hi(), bound_list_non_empty);
+        // When the type parameter has been provided bounds
+        //
+        //    Message:
+        //      fn foo<T>(t: T) where T: Foo { ... }
+        //                            ^^^^^^
+        //                            |
+        //                            help: consider further restricting this bound with `+ Bar`
+        //
+        //    Suggestion:
+        //      fn foo<T>(t: T) where T: Foo { ... }
+        //                                  ^
+        //                                  |
+        //                                  replace with: ` + Bar`
+        //
+        // Or, if user has provided some bounds, suggest restricting them:
+        //
+        //   fn foo<T: Foo>(t: T) { ... }
+        //             ---
+        //             |
+        //             help: consider further restricting this bound with `+ Bar`
+        //
+        // Suggestion for tools in this case is:
+        //
+        //   fn foo<T: Foo>(t: T) { ... }
+        //          --
+        //          |
+        //          replace with: `T: Bar +`
+        let param_def_id = tcx.hir().local_def_id(param.hir_id);
+        if let Some(span) = generics.bounds_span_for_suggestions(param_def_id) {
+            suggest_restrict(span, true);
             continue;
         }
 
-        if generics.where_clause.predicates.is_empty()
-        // Given `trait Base<T = String>: Super<T>` where `T: Copy`, suggest restricting in the
-        // `where` clause instead of `trait Base<T: Copy = String>: Super<T>`.
-        && !matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
-        {
-            if let Some(span) = param.bounds_span_for_suggestions() {
-                // If user has provided some bounds, suggest restricting them:
-                //
-                //   fn foo<T: Foo>(t: T) { ... }
-                //             ---
-                //             |
-                //             help: consider further restricting this bound with `+ Bar`
-                //
-                // Suggestion for tools in this case is:
-                //
-                //   fn foo<T: Foo>(t: T) { ... }
-                //          --
-                //          |
-                //          replace with: `T: Bar +`
-
-                // `bounds_span_for_suggestions` returns `None` if the list is empty
-                let bound_list_non_empty = true;
-
-                suggest_restrict(span, bound_list_non_empty);
-            } else {
-                let (colon, span) = match param.colon_span_for_suggestions(tcx.sess.source_map()) {
-                    // If there is already a colon after generic, do not suggest adding it again
-                    Some(sp) => ("", sp.shrink_to_hi()),
-                    None => (":", param.span.shrink_to_hi()),
-                };
-
-                // If user hasn't provided any bounds, suggest adding a new one:
-                //
-                //   fn foo<T>(t: T) { ... }
-                //          - help: consider restricting this type parameter with `T: Foo`
-                suggestions.push((
-                    span,
-                    format!("{colon} {constraint}"),
-                    SuggestChangingConstraintsMessage::RestrictType { ty: param_name },
-                ));
-            }
-        } else {
+        if generics.has_where_clause {
             // This part is a bit tricky, because using the `where` clause user can
             // provide zero, one or many bounds for the same type parameter, so we
             // have following cases to consider:
             //
-            // 1) When the type parameter has been provided zero bounds
+            // When the type parameter has been provided zero bounds
             //
             //    Message:
             //      fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
@@ -426,95 +332,59 @@ pub fn suggest_constraining_type_params<'a>(
             //    Suggestion:
             //      fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
             //                                           - insert: `, X: Bar`
-            //
-            //
-            // 2) When the type parameter has been provided one bound
-            //
-            //    Message:
-            //      fn foo<T>(t: T) where T: Foo { ... }
-            //                            ^^^^^^
-            //                            |
-            //                            help: consider further restricting this bound with `+ Bar`
-            //
-            //    Suggestion:
-            //      fn foo<T>(t: T) where T: Foo { ... }
-            //                            ^^
-            //                            |
-            //                            replace with: `T: Bar +`
-            //
-            //
-            // 3) When the type parameter has been provided many bounds
-            //
-            //    Message:
-            //      fn foo<T>(t: T) where T: Foo, T: Bar {... }
-            //             - help: consider further restricting this type parameter with `where T: Zar`
-            //
-            //    Suggestion:
-            //      fn foo<T>(t: T) where T: Foo, T: Bar {... }
-            //                                          - insert: `, T: Zar`
-            //
-            // Additionally, there may be no `where` clause whatsoever in the case that this was
-            // reached because the generic parameter has a default:
-            //
-            //    Message:
-            //      trait Foo<T=()> {... }
-            //             - help: consider further restricting this type parameter with `where T: Zar`
-            //
-            //    Suggestion:
-            //      trait Foo<T=()> where T: Zar {... }
-            //                     - insert: `where T: Zar`
-
-            if matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
-                && generics.where_clause.predicates.len() == 0
-            {
-                // Suggest a bound, but there is no existing `where` clause *and* the type param has a
-                // default (`<T=Foo>`), so we suggest adding `where T: Bar`.
-                suggestions.push((
-                    generics.where_clause.tail_span_for_suggestion(),
-                    format!(" where {}: {}", param_name, constraint),
-                    SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name },
-                ));
-            } else {
-                let mut param_spans = Vec::new();
-                let mut non_empty = false;
-
-                for predicate in generics.where_clause.predicates {
-                    if let WherePredicate::BoundPredicate(WhereBoundPredicate {
-                        span,
-                        bounded_ty,
-                        bounds,
-                        ..
-                    }) = predicate
-                    {
-                        if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind {
-                            if let Some(segment) = path.segments.first() {
-                                if segment.ident.to_string() == param_name {
-                                    non_empty = !bounds.is_empty();
-
-                                    param_spans.push(span);
-                                }
-                            }
-                        }
-                    }
-                }
+            suggestions.push((
+                generics.tail_span_for_predicate_suggestion(),
+                constraints
+                    .iter()
+                    .map(|&(constraint, _)| format!(", {}: {}", param_name, constraint))
+                    .collect::<String>(),
+                SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name },
+            ));
+            continue;
+        }
 
-                match param_spans[..] {
-                    [&param_span] => suggest_restrict(param_span.shrink_to_hi(), non_empty),
-                    _ => {
-                        suggestions.push((
-                            generics.where_clause.tail_span_for_suggestion(),
-                            constraints
-                                .iter()
-                                .map(|&(constraint, _)| format!(", {}: {}", param_name, constraint))
-                                .collect::<String>(),
-                            SuggestChangingConstraintsMessage::RestrictTypeFurther {
-                                ty: param_name,
-                            },
-                        ));
-                    }
-                }
-            }
+        // Additionally, there may be no `where` clause but the generic parameter has a default:
+        //
+        //    Message:
+        //      trait Foo<T=()> {... }
+        //                - help: consider further restricting this type parameter with `where T: Zar`
+        //
+        //    Suggestion:
+        //      trait Foo<T=()> {... }
+        //                     - insert: `where T: Zar`
+        if matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. }) {
+            // Suggest a bound, but there is no existing `where` clause *and* the type param has a
+            // default (`<T=Foo>`), so we suggest adding `where T: Bar`.
+            suggestions.push((
+                generics.tail_span_for_predicate_suggestion(),
+                format!(" where {}: {}", param_name, constraint),
+                SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name },
+            ));
+            continue;
         }
+
+        // If user has provided a colon, don't suggest adding another:
+        //
+        //   fn foo<T:>(t: T) { ... }
+        //            - insert: consider restricting this type parameter with `T: Foo`
+        if let Some(colon_span) = param.colon_span {
+            suggestions.push((
+                colon_span.shrink_to_hi(),
+                format!(" {}", constraint),
+                SuggestChangingConstraintsMessage::RestrictType { ty: param_name },
+            ));
+            continue;
+        }
+
+        // If user hasn't provided any bounds, suggest adding a new one:
+        //
+        //   fn foo<T>(t: T) { ... }
+        //          - help: consider restricting this type parameter with `T: Foo`
+        suggestions.push((
+            param.span.shrink_to_hi(),
+            format!(": {}", constraint),
+            SuggestChangingConstraintsMessage::RestrictType { ty: param_name },
+        ));
     }
 
     if suggestions.len() == 1 {
diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs
index 07878defa8c..da0934b67c5 100644
--- a/compiler/rustc_middle/src/ty/error.rs
+++ b/compiler/rustc_middle/src/ty/error.rs
@@ -602,53 +602,24 @@ impl<T> Trait<T> for X {
                 } else {
                     return false;
                 };
+                let Some(def_id) = def_id.as_local() else {
+                    return false;
+                };
 
                 // First look in the `where` clause, as this might be
                 // `fn foo<T>(x: T) where T: Trait`.
-                for predicate in hir_generics.where_clause.predicates {
-                    if let hir::WherePredicate::BoundPredicate(pred) = predicate {
-                        if let hir::TyKind::Path(hir::QPath::Resolved(None, path)) =
-                            pred.bounded_ty.kind
-                        {
-                            if path.res.opt_def_id() == Some(def_id) {
-                                // This predicate is binding type param `A` in `<A as T>::Foo` to
-                                // something, potentially `T`.
-                            } else {
-                                continue;
-                            }
-                        } else {
-                            continue;
-                        }
-
-                        if self.constrain_generic_bound_associated_type_structured_suggestion(
-                            diag,
-                            &trait_ref,
-                            pred.bounds,
-                            &assoc,
-                            assoc_substs,
-                            ty,
-                            msg,
-                            false,
-                        ) {
-                            return true;
-                        }
-                    }
-                }
-                for param in hir_generics.params {
-                    if self.hir().opt_local_def_id(param.hir_id).map(|id| id.to_def_id())
-                        == Some(def_id)
-                    {
-                        // This is type param `A` in `<A as T>::Foo`.
-                        return self.constrain_generic_bound_associated_type_structured_suggestion(
-                            diag,
-                            &trait_ref,
-                            param.bounds,
-                            &assoc,
-                            assoc_substs,
-                            ty,
-                            msg,
-                            false,
-                        );
+                for pred in hir_generics.bounds_for_param(def_id) {
+                    if self.constrain_generic_bound_associated_type_structured_suggestion(
+                        diag,
+                        &trait_ref,
+                        pred.bounds,
+                        &assoc,
+                        assoc_substs,
+                        ty,
+                        msg,
+                        false,
+                    ) {
+                        return true;
                     }
                 }
             }
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index f6d139fe59d..cd4b23fca39 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -1120,21 +1120,12 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
                                 match st[i].abi() {
                                     Abi::Scalar(_) => Abi::Scalar(niche_scalar),
                                     Abi::ScalarPair(first, second) => {
-                                        // We need to use scalar_unit to reset the
-                                        // valid range to the maximal one for that
-                                        // primitive, because only the niche is
-                                        // guaranteed to be initialised, not the
-                                        // other primitive.
+                                        // Only the niche is guaranteed to be initialised,
+                                        // so use union layout for the other primitive.
                                         if offset.bytes() == 0 {
-                                            Abi::ScalarPair(
-                                                niche_scalar,
-                                                scalar_unit(second.primitive()),
-                                            )
+                                            Abi::ScalarPair(niche_scalar, second.to_union())
                                         } else {
-                                            Abi::ScalarPair(
-                                                scalar_unit(first.primitive()),
-                                                niche_scalar,
-                                            )
+                                            Abi::ScalarPair(first.to_union(), niche_scalar)
                                         }
                                     }
                                     _ => Abi::Aggregate { sized: true },
@@ -1329,6 +1320,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
                 } else {
                     // Try to use a ScalarPair for all tagged enums.
                     let mut common_prim = None;
+                    let mut common_prim_initialized_in_all_variants = true;
                     for (field_layouts, layout_variant) in iter::zip(&variants, &layout_variants) {
                         let FieldsShape::Arbitrary { ref offsets, .. } = layout_variant.fields else {
                             bug!();
@@ -1336,7 +1328,10 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
                         let mut fields =
                             iter::zip(field_layouts, offsets).filter(|p| !p.0.is_zst());
                         let (field, offset) = match (fields.next(), fields.next()) {
-                            (None, None) => continue,
+                            (None, None) => {
+                                common_prim_initialized_in_all_variants = false;
+                                continue;
+                            }
                             (Some(pair), None) => pair,
                             _ => {
                                 common_prim = None;
@@ -1344,7 +1339,11 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
                             }
                         };
                         let prim = match field.abi {
-                            Abi::Scalar(scalar) => scalar.primitive(),
+                            Abi::Scalar(scalar) => {
+                                common_prim_initialized_in_all_variants &=
+                                    matches!(scalar, Scalar::Initialized { .. });
+                                scalar.primitive()
+                            }
                             _ => {
                                 common_prim = None;
                                 break;
@@ -1364,7 +1363,13 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
                         }
                     }
                     if let Some((prim, offset)) = common_prim {
-                        let pair = self.scalar_pair(tag, scalar_unit(prim));
+                        let prim_scalar = if common_prim_initialized_in_all_variants {
+                            scalar_unit(prim)
+                        } else {
+                            // Common prim might be uninit.
+                            Scalar::Union { value: prim }
+                        };
+                        let pair = self.scalar_pair(tag, prim_scalar);
                         let pair_offsets = match pair.fields {
                             FieldsShape::Arbitrary { ref offsets, ref memory_index } => {
                                 assert_eq!(memory_index, &[0, 1]);
@@ -2587,6 +2592,22 @@ where
 
         pointee_info
     }
+
+    fn is_adt(this: TyAndLayout<'tcx>) -> bool {
+        matches!(this.ty.kind(), ty::Adt(..))
+    }
+
+    fn is_never(this: TyAndLayout<'tcx>) -> bool {
+        this.ty.kind() == &ty::Never
+    }
+
+    fn is_tuple(this: TyAndLayout<'tcx>) -> bool {
+        matches!(this.ty.kind(), ty::Tuple(..))
+    }
+
+    fn is_unit(this: TyAndLayout<'tcx>) -> bool {
+        matches!(this.ty.kind(), ty::Tuple(list) if list.len() == 0)
+    }
 }
 
 impl<'tcx> ty::Instance<'tcx> {
diff --git a/compiler/rustc_middle/src/ty/list.rs b/compiler/rustc_middle/src/ty/list.rs
index adba7d13159..197dc9205b4 100644
--- a/compiler/rustc_middle/src/ty/list.rs
+++ b/compiler/rustc_middle/src/ty/list.rs
@@ -61,6 +61,10 @@ impl<T> List<T> {
         static EMPTY_SLICE: InOrder<usize, MaxAlign> = InOrder(0, MaxAlign);
         unsafe { &*(&EMPTY_SLICE as *const _ as *const List<T>) }
     }
+
+    pub fn len(&self) -> usize {
+        self.len
+    }
 }
 
 impl<T: Copy> List<T> {
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index d59fdf47904..ec416722c21 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -36,7 +36,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::tagged_ptr::CopyTaggedPtr;
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
-use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LocalDefIdMap, CRATE_DEF_ID};
+use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LocalDefIdMap};
 use rustc_hir::Node;
 use rustc_macros::HashStable;
 use rustc_query_system::ich::StableHashingContext;
@@ -131,6 +131,8 @@ pub struct ResolverOutputs {
     pub definitions: rustc_hir::definitions::Definitions,
     pub cstore: Box<CrateStoreDyn>,
     pub visibilities: FxHashMap<LocalDefId, Visibility>,
+    /// This field is used to decide whether we should make `PRIVATE_IN_PUBLIC` a hard error.
+    pub has_pub_restricted: bool,
     pub access_levels: AccessLevels,
     pub extern_crate_map: FxHashMap<LocalDefId, CrateNum>,
     pub maybe_unused_trait_imports: FxHashSet<LocalDefId>,
@@ -317,22 +319,6 @@ impl<'tcx> DefIdTree for TyCtxt<'tcx> {
 }
 
 impl Visibility {
-    pub fn from_hir(visibility: &hir::Visibility<'_>, id: hir::HirId, tcx: TyCtxt<'_>) -> Self {
-        match visibility.node {
-            hir::VisibilityKind::Public => Visibility::Public,
-            hir::VisibilityKind::Crate(_) => Visibility::Restricted(CRATE_DEF_ID.to_def_id()),
-            hir::VisibilityKind::Restricted { ref path, .. } => match path.res {
-                // If there is no resolution, `resolve` will have already reported an error, so
-                // assume that the visibility is public to avoid reporting more privacy errors.
-                Res::Err => Visibility::Public,
-                def => Visibility::Restricted(def.def_id()),
-            },
-            hir::VisibilityKind::Inherited => {
-                Visibility::Restricted(tcx.parent_module(id).to_def_id())
-            }
-        }
-    }
-
     /// Returns `true` if an item with this visibility is accessible from the given block.
     pub fn is_accessible_from<T: DefIdTree>(self, module: DefId, tree: T) -> bool {
         let restriction = match self {
diff --git a/compiler/rustc_middle/src/ty/query.rs b/compiler/rustc_middle/src/ty/query.rs
index 7629d7a8259..fb937ded65a 100644
--- a/compiler/rustc_middle/src/ty/query.rs
+++ b/compiler/rustc_middle/src/ty/query.rs
@@ -3,7 +3,7 @@ use crate::infer::canonical::{self, Canonical};
 use crate::lint::LintLevelMap;
 use crate::metadata::ModChild;
 use crate::middle::codegen_fn_attrs::CodegenFnAttrs;
-use crate::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel};
+use crate::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo};
 use crate::middle::lib_features::LibFeatures;
 use crate::middle::privacy::AccessLevels;
 use crate::middle::region;
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index 5a13216846d..1509de0e930 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -2273,7 +2273,7 @@ impl<'tcx> Ty<'tcx> {
         tcx: TyCtxt<'tcx>,
         normalize: impl FnMut(Ty<'tcx>) -> Ty<'tcx>,
     ) -> (Ty<'tcx>, bool) {
-        let tail = tcx.struct_tail_with_normalize(self, normalize);
+        let tail = tcx.struct_tail_with_normalize(self, normalize, || {});
         match tail.kind() {
             // Sized types
             ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index 39038e85b11..918fe49e8e3 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -187,7 +187,7 @@ impl<'tcx> TyCtxt<'tcx> {
     /// if input `ty` is not a structure at all.
     pub fn struct_tail_without_normalization(self, ty: Ty<'tcx>) -> Ty<'tcx> {
         let tcx = self;
-        tcx.struct_tail_with_normalize(ty, |ty| ty)
+        tcx.struct_tail_with_normalize(ty, |ty| ty, || {})
     }
 
     /// Returns the deeply last field of nested structures, or the same type if
@@ -203,7 +203,7 @@ impl<'tcx> TyCtxt<'tcx> {
         param_env: ty::ParamEnv<'tcx>,
     ) -> Ty<'tcx> {
         let tcx = self;
-        tcx.struct_tail_with_normalize(ty, |ty| tcx.normalize_erasing_regions(param_env, ty))
+        tcx.struct_tail_with_normalize(ty, |ty| tcx.normalize_erasing_regions(param_env, ty), || {})
     }
 
     /// Returns the deeply last field of nested structures, or the same type if
@@ -220,6 +220,10 @@ impl<'tcx> TyCtxt<'tcx> {
         self,
         mut ty: Ty<'tcx>,
         mut normalize: impl FnMut(Ty<'tcx>) -> Ty<'tcx>,
+        // This is currently used to allow us to walk a ValTree
+        // in lockstep with the type in order to get the ValTree branch that
+        // corresponds to an unsized field.
+        mut f: impl FnMut() -> (),
     ) -> Ty<'tcx> {
         let recursion_limit = self.recursion_limit();
         for iteration in 0.. {
@@ -235,12 +239,16 @@ impl<'tcx> TyCtxt<'tcx> {
                         break;
                     }
                     match def.non_enum_variant().fields.last() {
-                        Some(f) => ty = f.ty(self, substs),
+                        Some(field) => {
+                            f();
+                            ty = field.ty(self, substs);
+                        }
                         None => break,
                     }
                 }
 
                 ty::Tuple(tys) if let Some((&last_ty, _)) = tys.split_last() => {
+                    f();
                     ty = last_ty;
                 }
 
diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs
index a04ac338274..cee657e9da2 100644
--- a/compiler/rustc_mir_build/src/build/expr/into.rs
+++ b/compiler/rustc_mir_build/src/build/expr/into.rs
@@ -255,10 +255,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                         func: fun,
                         args,
                         cleanup: None,
-                        // FIXME(varkor): replace this with an uninhabitedness-based check.
-                        // This requires getting access to the current module to call
-                        // `tcx.is_ty_uninhabited_from`, which is currently tricky to do.
-                        destination: if expr.ty.is_never() {
+                        // The presence or absence of a return edge affects control-flow sensitive
+                        // MIR checks and ultimately whether code is accepted or not. We can only
+                        // omit the return edge if a return type is visibly uninhabited to a module
+                        // that makes the call.
+                        destination: if this.tcx.is_ty_uninhabited_from(
+                            this.parent_module,
+                            expr.ty,
+                            this.param_env,
+                        ) {
                             None
                         } else {
                             Some((destination, success))
diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs
index 901635dc488..3c51f791862 100644
--- a/compiler/rustc_mir_build/src/build/mod.rs
+++ b/compiler/rustc_mir_build/src/build/mod.rs
@@ -350,6 +350,7 @@ struct Builder<'a, 'tcx> {
 
     def_id: DefId,
     hir_id: hir::HirId,
+    parent_module: DefId,
     check_overflow: bool,
     fn_span: Span,
     arg_count: usize,
@@ -807,15 +808,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         );
 
         let lint_level = LintLevel::Explicit(hir_id);
+        let param_env = tcx.param_env(def.did);
         let mut builder = Builder {
             thir,
             tcx,
             infcx,
             typeck_results: tcx.typeck_opt_const_arg(def),
             region_scope_tree: tcx.region_scope_tree(def.did),
-            param_env: tcx.param_env(def.did),
+            param_env,
             def_id: def.did.to_def_id(),
             hir_id,
+            parent_module: tcx.parent_module(hir_id).to_def_id(),
             check_overflow,
             cfg: CFG { basic_blocks: IndexVec::new() },
             fn_span: span,
diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs
index eadce3dc9c4..a841cce23de 100644
--- a/compiler/rustc_mir_build/src/check_unsafety.rs
+++ b/compiler/rustc_mir_build/src/check_unsafety.rs
@@ -12,6 +12,7 @@ use rustc_span::def_id::{DefId, LocalDefId};
 use rustc_span::symbol::Symbol;
 use rustc_span::Span;
 
+use std::borrow::Cow;
 use std::ops::Bound;
 
 struct UnsafetyVisitor<'a, 'tcx> {
@@ -70,7 +71,6 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
     }
 
     fn requires_unsafe(&mut self, span: Span, kind: UnsafeOpKind) {
-        let (description, note) = kind.description_and_note();
         let unsafe_op_in_unsafe_fn_allowed = self.unsafe_op_in_unsafe_fn_allowed();
         match self.safety_context {
             SafetyContext::BuiltinUnsafeBlock => {}
@@ -82,6 +82,7 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
             }
             SafetyContext::UnsafeFn if unsafe_op_in_unsafe_fn_allowed => {}
             SafetyContext::UnsafeFn => {
+                let (description, note) = kind.description_and_note(self.tcx);
                 // unsafe_op_in_unsafe_fn is disallowed
                 self.tcx.struct_span_lint_hir(
                     UNSAFE_OP_IN_UNSAFE_FN,
@@ -92,13 +93,14 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
                             "{} is unsafe and requires unsafe block (error E0133)",
                             description,
                         ))
-                        .span_label(span, description)
+                        .span_label(span, kind.simple_description())
                         .note(note)
                         .emit();
                     },
                 )
             }
             SafetyContext::Safe => {
+                let (description, note) = kind.description_and_note(self.tcx);
                 let fn_sugg = if unsafe_op_in_unsafe_fn_allowed { " function or" } else { "" };
                 struct_span_err!(
                     self.tcx.sess,
@@ -108,7 +110,7 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
                     description,
                     fn_sugg,
                 )
-                .span_label(span, description)
+                .span_label(span, kind.simple_description())
                 .note(note)
                 .emit();
             }
@@ -350,7 +352,12 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
             }
             ExprKind::Call { fun, ty: _, args: _, from_hir_call: _, fn_span: _ } => {
                 if self.thir[fun].ty.fn_sig(self.tcx).unsafety() == hir::Unsafety::Unsafe {
-                    self.requires_unsafe(expr.span, CallToUnsafeFunction);
+                    let func_id = if let ty::FnDef(func_id, _) = self.thir[fun].ty.kind() {
+                        Some(*func_id)
+                    } else {
+                        None
+                    };
+                    self.requires_unsafe(expr.span, CallToUnsafeFunction(func_id));
                 } else if let &ty::FnDef(func_did, _) = self.thir[fun].ty.kind() {
                     // If the called function has target features the calling function hasn't,
                     // the call requires `unsafe`. Don't check this on wasm
@@ -364,7 +371,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
                             .iter()
                             .all(|feature| self.body_target_features.contains(feature))
                     {
-                        self.requires_unsafe(expr.span, CallToFunctionWith);
+                        self.requires_unsafe(expr.span, CallToFunctionWith(func_did));
                     }
                 }
             }
@@ -523,7 +530,7 @@ impl BodyUnsafety {
 
 #[derive(Clone, Copy, PartialEq)]
 enum UnsafeOpKind {
-    CallToUnsafeFunction,
+    CallToUnsafeFunction(Option<DefId>),
     UseOfInlineAssembly,
     InitializingTypeWith,
     UseOfMutableStatic,
@@ -533,64 +540,89 @@ enum UnsafeOpKind {
     AccessToUnionField,
     MutationOfLayoutConstrainedField,
     BorrowOfLayoutConstrainedField,
-    CallToFunctionWith,
+    CallToFunctionWith(DefId),
 }
 
 use UnsafeOpKind::*;
 
 impl UnsafeOpKind {
-    pub fn description_and_note(&self) -> (&'static str, &'static str) {
+    pub fn simple_description(&self) -> &'static str {
         match self {
-            CallToUnsafeFunction => (
-                "call to unsafe function",
+            CallToUnsafeFunction(..) => "call to unsafe function",
+            UseOfInlineAssembly => "use of inline assembly",
+            InitializingTypeWith => "initializing type with `rustc_layout_scalar_valid_range` attr",
+            UseOfMutableStatic => "use of mutable static",
+            UseOfExternStatic => "use of extern static",
+            DerefOfRawPointer => "dereference of raw pointer",
+            AssignToDroppingUnionField => "assignment to union field that might need dropping",
+            AccessToUnionField => "access to union field",
+            MutationOfLayoutConstrainedField => "mutation of layout constrained field",
+            BorrowOfLayoutConstrainedField => {
+                "borrow of layout constrained field with interior mutability"
+            }
+            CallToFunctionWith(..) => "call to function with `#[target_feature]`",
+        }
+    }
+
+    pub fn description_and_note(&self, tcx: TyCtxt<'_>) -> (Cow<'static, str>, &'static str) {
+        match self {
+            CallToUnsafeFunction(did) => (
+                if let Some(did) = did {
+                    Cow::from(format!("call to unsafe function `{}`", tcx.def_path_str(*did)))
+                } else {
+                    Cow::Borrowed(self.simple_description())
+                },
                 "consult the function's documentation for information on how to avoid undefined \
                  behavior",
             ),
             UseOfInlineAssembly => (
-                "use of inline assembly",
+                Cow::Borrowed(self.simple_description()),
                 "inline assembly is entirely unchecked and can cause undefined behavior",
             ),
             InitializingTypeWith => (
-                "initializing type with `rustc_layout_scalar_valid_range` attr",
+                Cow::Borrowed(self.simple_description()),
                 "initializing a layout restricted type's field with a value outside the valid \
                  range is undefined behavior",
             ),
             UseOfMutableStatic => (
-                "use of mutable static",
+                Cow::Borrowed(self.simple_description()),
                 "mutable statics can be mutated by multiple threads: aliasing violations or data \
                  races will cause undefined behavior",
             ),
             UseOfExternStatic => (
-                "use of extern static",
+                Cow::Borrowed(self.simple_description()),
                 "extern statics are not controlled by the Rust type system: invalid data, \
                  aliasing violations or data races will cause undefined behavior",
             ),
             DerefOfRawPointer => (
-                "dereference of raw pointer",
+                Cow::Borrowed(self.simple_description()),
                 "raw pointers may be null, dangling or unaligned; they can violate aliasing rules \
                  and cause data races: all of these are undefined behavior",
             ),
             AssignToDroppingUnionField => (
-                "assignment to union field that might need dropping",
+                Cow::Borrowed(self.simple_description()),
                 "the previous content of the field will be dropped, which causes undefined \
                  behavior if the field was not properly initialized",
             ),
             AccessToUnionField => (
-                "access to union field",
+                Cow::Borrowed(self.simple_description()),
                 "the field may not be properly initialized: using uninitialized data will cause \
                  undefined behavior",
             ),
             MutationOfLayoutConstrainedField => (
-                "mutation of layout constrained field",
+                Cow::Borrowed(self.simple_description()),
                 "mutating layout constrained fields cannot statically be checked for valid values",
             ),
             BorrowOfLayoutConstrainedField => (
-                "borrow of layout constrained field with interior mutability",
+                Cow::Borrowed(self.simple_description()),
                 "references to fields of layout constrained fields lose the constraints. Coupled \
                  with interior mutability, the field can be changed to invalid values",
             ),
-            CallToFunctionWith => (
-                "call to function with `#[target_feature]`",
+            CallToFunctionWith(did) => (
+                Cow::from(format!(
+                    "call to function `{}` with `#[target_feature]`",
+                    tcx.def_path_str(*did)
+                )),
                 "can only be called if the required target features are available",
             ),
         }
diff --git a/compiler/rustc_mir_transform/src/add_retag.rs b/compiler/rustc_mir_transform/src/add_retag.rs
index 28a5a22dd9d..a245da658b9 100644
--- a/compiler/rustc_mir_transform/src/add_retag.rs
+++ b/compiler/rustc_mir_transform/src/add_retag.rs
@@ -57,6 +57,17 @@ fn may_be_reference(ty: Ty<'_>) -> bool {
     }
 }
 
+/// Determines whether or not this LocalDecl is temp, if not it needs retagging.
+fn is_not_temp<'tcx>(local_decl: &LocalDecl<'tcx>) -> bool {
+    if let Some(local_info) = &local_decl.local_info {
+        match local_info.as_ref() {
+            LocalInfo::DerefTemp => return false,
+            _ => (),
+        };
+    }
+    return true;
+}
+
 impl<'tcx> MirPass<'tcx> for AddRetag {
     fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
         sess.opts.debugging_opts.mir_emit_retag
@@ -71,7 +82,9 @@ impl<'tcx> MirPass<'tcx> for AddRetag {
         let needs_retag = |place: &Place<'tcx>| {
             // FIXME: Instead of giving up for unstable places, we should introduce
             // a temporary and retag on that.
-            is_stable(place.as_ref()) && may_be_reference(place.ty(&*local_decls, tcx).ty)
+            is_stable(place.as_ref())
+                && may_be_reference(place.ty(&*local_decls, tcx).ty)
+                && is_not_temp(&local_decls[place.local])
         };
         let place_base_raw = |place: &Place<'tcx>| {
             // If this is a `Deref`, get the type of what we are deref'ing.
diff --git a/compiler/rustc_mir_transform/src/check_unsafety.rs b/compiler/rustc_mir_transform/src/check_unsafety.rs
index 1b4510b6220..dde79214b16 100644
--- a/compiler/rustc_mir_transform/src/check_unsafety.rs
+++ b/compiler/rustc_mir_transform/src/check_unsafety.rs
@@ -70,6 +70,8 @@ impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> {
 
             TerminatorKind::Call { ref func, .. } => {
                 let func_ty = func.ty(self.body, self.tcx);
+                let func_id =
+                    if let ty::FnDef(func_id, _) = func_ty.kind() { Some(func_id) } else { None };
                 let sig = func_ty.fn_sig(self.tcx);
                 if let hir::Unsafety::Unsafe = sig.unsafety() {
                     self.require_unsafe(
@@ -78,7 +80,7 @@ impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> {
                     )
                 }
 
-                if let ty::FnDef(func_id, _) = func_ty.kind() {
+                if let Some(func_id) = func_id {
                     self.check_target_features(*func_id);
                 }
             }
diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs
index 13b49256d48..691f4fb0e54 100644
--- a/compiler/rustc_mir_transform/src/const_prop.rs
+++ b/compiler/rustc_mir_transform/src/const_prop.rs
@@ -313,9 +313,7 @@ struct ConstPropagator<'mir, 'tcx> {
     ecx: InterpCx<'mir, 'tcx, ConstPropMachine<'mir, 'tcx>>,
     tcx: TyCtxt<'tcx>,
     param_env: ParamEnv<'tcx>,
-    // FIXME(eddyb) avoid cloning this field more than once,
-    // by accessing it through `ecx` instead.
-    local_decls: IndexVec<Local, LocalDecl<'tcx>>,
+    local_decls: &'mir 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>,
@@ -361,10 +359,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
         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() {
@@ -374,7 +368,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
         }
         let mut ecx = InterpCx::new(
             tcx,
-            span,
+            tcx.def_span(def_id),
             param_env,
             ConstPropMachine::new(only_propagate_inside_block_locals, can_const_prop),
         );
@@ -405,10 +399,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
             ecx,
             tcx,
             param_env,
-            // 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(),
+            local_decls: &dummy_body.local_decls,
             source_info: None,
         }
     }
@@ -511,7 +502,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
             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_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();
diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs
index d6331a88c5b..4945c10c9aa 100644
--- a/compiler/rustc_mir_transform/src/const_prop_lint.rs
+++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs
@@ -308,10 +308,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>>,
-    local_decls: IndexVec<Local, LocalDecl<'tcx>>,
+    source_scopes: &'mir IndexVec<SourceScope, SourceScopeData<'tcx>>,
+    local_decls: &'mir 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>,
@@ -357,10 +355,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
         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() {
@@ -370,7 +364,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
         }
         let mut ecx = InterpCx::new(
             tcx,
-            span,
+            tcx.def_span(def_id),
             param_env,
             ConstPropMachine::new(only_propagate_inside_block_locals, can_const_prop),
         );
@@ -401,11 +395,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(wesleywiser) we can't steal this because `Visitor::super_visit_body()` needs it
-            local_decls: body.local_decls.clone(),
+            source_scopes: &dummy_body.source_scopes,
+            local_decls: &dummy_body.local_decls,
             source_info: None,
         }
     }
@@ -435,7 +426,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
     }
 
     fn lint_root(&self, source_info: SourceInfo) -> Option<HirId> {
-        source_info.scope.lint_root(&self.source_scopes)
+        source_info.scope.lint_root(self.source_scopes)
     }
 
     fn use_ecx<F, T>(&mut self, f: F) -> Option<T>
@@ -572,7 +563,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
             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_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();
diff --git a/compiler/rustc_mir_transform/src/deref_separator.rs b/compiler/rustc_mir_transform/src/deref_separator.rs
index 24b626ad966..57a95a67df7 100644
--- a/compiler/rustc_mir_transform/src/deref_separator.rs
+++ b/compiler/rustc_mir_transform/src/deref_separator.rs
@@ -1,68 +1,92 @@
 use crate::MirPass;
+use rustc_index::vec::IndexVec;
 use rustc_middle::mir::patch::MirPatch;
+use rustc_middle::mir::visit::{MutVisitor, PlaceContext};
 use rustc_middle::mir::*;
 use rustc_middle::ty::TyCtxt;
 pub struct Derefer;
 
-pub fn deref_finder<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
-    let mut patch = MirPatch::new(body);
-    let (basic_blocks, local_decl) = body.basic_blocks_and_local_decls_mut();
-    for (block, data) in basic_blocks.iter_enumerated_mut() {
-        for (i, stmt) in data.statements.iter_mut().enumerate() {
-            match stmt.kind {
-                StatementKind::Assign(box (og_place, Rvalue::Ref(region, borrow_knd, place))) => {
-                    let mut place_local = place.local;
-                    let mut last_len = 0;
-                    for (idx, (p_ref, p_elem)) in place.iter_projections().enumerate() {
-                        if p_elem == ProjectionElem::Deref && !p_ref.projection.is_empty() {
-                            // The type that we are derefing.
-                            let ty = p_ref.ty(local_decl, tcx).ty;
-                            let temp = patch.new_temp(ty, stmt.source_info.span);
-
-                            // Because we are assigning this right before original statement
-                            // we are using index i of statement.
-                            let loc = Location { block: block, statement_index: i };
-                            patch.add_statement(loc, StatementKind::StorageLive(temp));
-
-                            // We are adding current p_ref's projections to our
-                            // temp value, excluding projections we already covered.
-                            let deref_place = Place::from(place_local)
-                                .project_deeper(&p_ref.projection[last_len..], tcx);
-                            patch.add_assign(
-                                loc,
-                                Place::from(temp),
-                                Rvalue::Use(Operand::Move(deref_place)),
-                            );
-
-                            place_local = temp;
-                            last_len = p_ref.projection.len();
-
-                            // We are creating a place by using our temp value's location
-                            // and copying derefed values which we need to create new statement.
-                            let temp_place =
-                                Place::from(temp).project_deeper(&place.projection[idx..], tcx);
-                            let new_stmt = Statement {
-                                source_info: stmt.source_info,
-                                kind: StatementKind::Assign(Box::new((
-                                    og_place,
-                                    Rvalue::Ref(region, borrow_knd, temp_place),
-                                ))),
-                            };
-
-                            // Replace current statement with newly created one.
-                            *stmt = new_stmt;
-
-                            // Since our job with the temp is done it should be gone
-                            let loc = Location { block: block, statement_index: i + 1 };
-                            patch.add_statement(loc, StatementKind::StorageDead(temp));
-                        }
-                    }
+pub struct DerefChecker<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    patcher: MirPatch<'tcx>,
+    local_decls: IndexVec<Local, LocalDecl<'tcx>>,
+}
+
+impl<'tcx> MutVisitor<'tcx> for DerefChecker<'tcx> {
+    fn tcx(&self) -> TyCtxt<'tcx> {
+        self.tcx
+    }
+
+    fn visit_place(&mut self, place: &mut Place<'tcx>, _: PlaceContext, loc: Location) {
+        let mut place_local = place.local;
+        let mut last_len = 0;
+        let mut last_deref_idx = 0;
+
+        let mut prev_temp: Option<Local> = None;
+
+        for (idx, (p_ref, p_elem)) in place.iter_projections().enumerate() {
+            if p_elem == ProjectionElem::Deref && !p_ref.projection.is_empty() {
+                last_deref_idx = idx;
+            }
+        }
+
+        for (idx, (p_ref, p_elem)) in place.iter_projections().enumerate() {
+            if p_elem == ProjectionElem::Deref && !p_ref.projection.is_empty() {
+                let ty = p_ref.ty(&self.local_decls, self.tcx).ty;
+                let temp = self.patcher.new_local_with_info(
+                    ty,
+                    self.local_decls[p_ref.local].source_info.span,
+                    Some(Box::new(LocalInfo::DerefTemp)),
+                );
+
+                self.patcher.add_statement(loc, StatementKind::StorageLive(temp));
+
+                // We are adding current p_ref's projections to our
+                // temp value, excluding projections we already covered.
+                let deref_place = Place::from(place_local)
+                    .project_deeper(&p_ref.projection[last_len..], self.tcx);
+
+                self.patcher.add_assign(
+                    loc,
+                    Place::from(temp),
+                    Rvalue::Use(Operand::Move(deref_place)),
+                );
+                place_local = temp;
+                last_len = p_ref.projection.len();
+
+                // Change `Place` only if we are actually at the Place's last deref
+                if idx == last_deref_idx {
+                    let temp_place =
+                        Place::from(temp).project_deeper(&place.projection[idx..], self.tcx);
+                    *place = temp_place;
+                }
+
+                // We are destroying the previous temp since it's no longer used.
+                if let Some(prev_temp) = prev_temp {
+                    self.patcher.add_statement(loc, StatementKind::StorageDead(prev_temp));
                 }
-                _ => (),
+
+                prev_temp = Some(temp);
             }
         }
+
+        // Since we won't be able to reach final temp, we destroy it outside the loop.
+        if let Some(prev_temp) = prev_temp {
+            let last_loc = Location { block: loc.block, statement_index: loc.statement_index + 1 };
+            self.patcher.add_statement(last_loc, StatementKind::StorageDead(prev_temp));
+        }
     }
-    patch.apply(body);
+}
+
+pub fn deref_finder<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+    let patch = MirPatch::new(body);
+    let mut checker = DerefChecker { tcx, patcher: patch, local_decls: body.local_decls.clone() };
+
+    for (bb, data) in body.basic_blocks_mut().iter_enumerated_mut() {
+        checker.visit_basic_block_data(bb, data);
+    }
+
+    checker.patcher.apply(body);
 }
 
 impl<'tcx> MirPass<'tcx> for Derefer {
diff --git a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs
index 5bde0c01412..33f201cbd28 100644
--- a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs
+++ b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs
@@ -336,9 +336,7 @@ fn evaluate_candidate<'tcx>(
             Some(poss)
         }
     };
-    let Some((_, child)) = targets.iter().next() else {
-        return None
-    };
+    let (_, child) = targets.iter().next()?;
     let child_terminator = &bbs[child].terminator();
     let TerminatorKind::SwitchInt {
         switch_ty: child_ty,
diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs
index d395ccd3819..40cc6dafe61 100644
--- a/compiler/rustc_mir_transform/src/lib.rs
+++ b/compiler/rustc_mir_transform/src/lib.rs
@@ -426,13 +426,13 @@ fn run_post_borrowck_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tc
         &add_moves_for_packed_drops::AddMovesForPackedDrops,
         // `AddRetag` needs to run after `ElaborateDrops`. Otherwise it should run fairly late,
         // but before optimizations begin.
+        &deref_separator::Derefer,
         &add_retag::AddRetag,
         &lower_intrinsics::LowerIntrinsics,
         &simplify::SimplifyCfg::new("elaborate-drops"),
         // `Deaggregator` is conceptually part of MIR building, some backends rely on it happening
         // and it can help optimizations.
         &deaggregator::Deaggregator,
-        &deref_separator::Derefer,
         &Lint(const_prop_lint::ConstProp),
     ];
 
diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs
index 740a2168b41..2380391d09a 100644
--- a/compiler/rustc_mir_transform/src/pass_manager.rs
+++ b/compiler/rustc_mir_transform/src/pass_manager.rs
@@ -77,17 +77,30 @@ pub fn run_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, passes: &[&dyn
     let mut cnt = 0;
 
     let validate = tcx.sess.opts.debugging_opts.validate_mir;
+    let overridden_passes = &tcx.sess.opts.debugging_opts.mir_enable_passes;
+    trace!(?overridden_passes);
 
     if validate {
         validate_body(tcx, body, format!("start of phase transition from {:?}", start_phase));
     }
 
     for pass in passes {
-        if !pass.is_enabled(&tcx.sess) {
-            continue;
-        }
-
         let name = pass.name();
+
+        if let Some((_, polarity)) = overridden_passes.iter().rev().find(|(s, _)| s == &*name) {
+            trace!(
+                pass = %name,
+                "{} as requested by flag",
+                if *polarity { "Running" } else { "Not running" },
+            );
+            if !polarity {
+                continue;
+            }
+        } else {
+            if !pass.is_enabled(&tcx.sess) {
+                continue;
+            }
+        }
         let dump_enabled = pass.is_mir_dump_enabled();
 
         if dump_enabled {
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index 0e0a4fbc215..1828aecb375 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -182,7 +182,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::sync::{par_iter, MTLock, MTRef, ParallelIterator};
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
-use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId, LOCAL_CRATE};
+use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId};
 use rustc_hir::lang_items::LangItem;
 use rustc_index::bit_set::GrowableBitSet;
 use rustc_middle::mir::interpret::{AllocId, ConstValue};
@@ -393,6 +393,7 @@ fn collect_items_rec<'tcx>(
     // error count. If it has changed, a PME occurred, and we trigger some diagnostics about the
     // current step of mono items collection.
     //
+    // FIXME: don't rely on global state, instead bubble up errors. Note: this is very hard to do.
     let error_count = tcx.sess.diagnostic().err_count();
 
     match starting_point.node {
@@ -473,12 +474,9 @@ fn collect_items_rec<'tcx>(
     }
 
     // Check for PMEs and emit a diagnostic if one happened. To try to show relevant edges of the
-    // mono item graph where the PME diagnostics are currently the most problematic (e.g. ones
-    // involving a dependency, and the lack of context is confusing) in this MVP, we focus on
-    // diagnostics on edges crossing a crate boundary: the collected mono items which are not
-    // defined in the local crate.
+    // mono item graph.
     if tcx.sess.diagnostic().err_count() > error_count
-        && starting_point.node.krate() != LOCAL_CRATE
+        && starting_point.node.is_generic_fn()
         && starting_point.node.is_user_defined()
     {
         let formatted_item = with_no_trimmed_paths!(starting_point.node.to_string());
diff --git a/compiler/rustc_monomorphize/src/partitioning/default.rs b/compiler/rustc_monomorphize/src/partitioning/default.rs
index 16774198879..bf99cd6e424 100644
--- a/compiler/rustc_monomorphize/src/partitioning/default.rs
+++ b/compiler/rustc_monomorphize/src/partitioning/default.rs
@@ -5,7 +5,7 @@ use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, LOCAL_CRATE};
 use rustc_hir::definitions::DefPathDataName;
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
-use rustc_middle::middle::exported_symbols::SymbolExportLevel;
+use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel};
 use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, Linkage, Visibility};
 use rustc_middle::mir::mono::{InstantiationMode, MonoItem};
 use rustc_middle::ty::print::characteristic_def_id_of_type;
@@ -554,7 +554,7 @@ fn default_visibility(tcx: TyCtxt<'_>, id: DefId, is_generic: bool) -> Visibilit
     // C-export level items remain at `Default`, all other internal
     // items become `Hidden`.
     match tcx.reachable_non_generics(id.krate).get(&id) {
-        Some(SymbolExportLevel::C) => Visibility::Default,
+        Some(SymbolExportInfo { level: SymbolExportLevel::C, .. }) => Visibility::Default,
         _ => Visibility::Hidden,
     }
 }
diff --git a/compiler/rustc_parse/Cargo.toml b/compiler/rustc_parse/Cargo.toml
index a823607ab0e..c6ca260e983 100644
--- a/compiler/rustc_parse/Cargo.toml
+++ b/compiler/rustc_parse/Cargo.toml
@@ -13,6 +13,7 @@ rustc_ast_pretty = { path = "../rustc_ast_pretty" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_feature = { path = "../rustc_feature" }
 rustc_lexer = { path = "../rustc_lexer" }
+rustc_macros = { path = "../rustc_macros" }
 rustc_errors = { path = "../rustc_errors" }
 rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs
index bfa13ce79ba..ee54dd44f71 100644
--- a/compiler/rustc_parse/src/lexer/mod.rs
+++ b/compiler/rustc_parse/src/lexer/mod.rs
@@ -1,6 +1,6 @@
 use crate::lexer::unicode_chars::UNICODE_ARRAY;
 use rustc_ast::ast::{self, AttrStyle};
-use rustc_ast::token::{self, CommentKind, Token, TokenKind};
+use rustc_ast::token::{self, CommentKind, Delimiter, Token, TokenKind};
 use rustc_ast::tokenstream::{Spacing, TokenStream};
 use rustc_ast::util::unicode::contains_text_flow_control_chars;
 use rustc_errors::{error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed, PResult};
@@ -24,8 +24,8 @@ use unescape_error_reporting::{emit_unescape_error, escaped_char};
 
 #[derive(Clone, Debug)]
 pub struct UnmatchedBrace {
-    pub expected_delim: token::DelimToken,
-    pub found_delim: Option<token::DelimToken>,
+    pub expected_delim: Delimiter,
+    pub found_delim: Option<Delimiter>,
     pub found_span: Span,
     pub unclosed_span: Option<Span>,
     pub candidate_span: Option<Span>,
@@ -284,12 +284,12 @@ impl<'a> StringReader<'a> {
             rustc_lexer::TokenKind::Semi => token::Semi,
             rustc_lexer::TokenKind::Comma => token::Comma,
             rustc_lexer::TokenKind::Dot => token::Dot,
-            rustc_lexer::TokenKind::OpenParen => token::OpenDelim(token::Paren),
-            rustc_lexer::TokenKind::CloseParen => token::CloseDelim(token::Paren),
-            rustc_lexer::TokenKind::OpenBrace => token::OpenDelim(token::Brace),
-            rustc_lexer::TokenKind::CloseBrace => token::CloseDelim(token::Brace),
-            rustc_lexer::TokenKind::OpenBracket => token::OpenDelim(token::Bracket),
-            rustc_lexer::TokenKind::CloseBracket => token::CloseDelim(token::Bracket),
+            rustc_lexer::TokenKind::OpenParen => token::OpenDelim(Delimiter::Parenthesis),
+            rustc_lexer::TokenKind::CloseParen => token::CloseDelim(Delimiter::Parenthesis),
+            rustc_lexer::TokenKind::OpenBrace => token::OpenDelim(Delimiter::Brace),
+            rustc_lexer::TokenKind::CloseBrace => token::CloseDelim(Delimiter::Brace),
+            rustc_lexer::TokenKind::OpenBracket => token::OpenDelim(Delimiter::Bracket),
+            rustc_lexer::TokenKind::CloseBracket => token::CloseDelim(Delimiter::Bracket),
             rustc_lexer::TokenKind::At => token::At,
             rustc_lexer::TokenKind::Pound => token::Pound,
             rustc_lexer::TokenKind::Tilde => token::Tilde,
@@ -612,14 +612,14 @@ impl<'a> StringReader<'a> {
                 err.span_suggestion_verbose(
                     prefix_span,
                     "use `br` for a raw byte string",
-                    "br".to_string(),
+                    "br",
                     Applicability::MaybeIncorrect,
                 );
             } else if expn_data.is_root() {
                 err.span_suggestion_verbose(
                     prefix_span.shrink_to_hi(),
                     "consider inserting whitespace here",
-                    " ".into(),
+                    " ",
                     Applicability::MaybeIncorrect,
                 );
             }
diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs
index 8318aec8726..ef84f95ec83 100644
--- a/compiler/rustc_parse/src/lexer/tokentrees.rs
+++ b/compiler/rustc_parse/src/lexer/tokentrees.rs
@@ -1,6 +1,6 @@
 use super::{StringReader, UnmatchedBrace};
 
-use rustc_ast::token::{self, DelimToken, Token};
+use rustc_ast::token::{self, Delimiter, Token};
 use rustc_ast::tokenstream::{
     DelimSpan,
     Spacing::{self, *},
@@ -32,15 +32,15 @@ struct TokenTreesReader<'a> {
     string_reader: StringReader<'a>,
     token: Token,
     /// Stack of open delimiters and their spans. Used for error message.
-    open_braces: Vec<(token::DelimToken, Span)>,
+    open_braces: Vec<(Delimiter, Span)>,
     unmatched_braces: Vec<UnmatchedBrace>,
     /// The type and spans for all braces
     ///
     /// Used only for error recovery when arriving to EOF with mismatched braces.
-    matching_delim_spans: Vec<(token::DelimToken, Span, Span)>,
+    matching_delim_spans: Vec<(Delimiter, Span, Span)>,
     last_unclosed_found_span: Option<Span>,
     /// Collect empty block spans that might have been auto-inserted by editors.
-    last_delim_empty_block_spans: FxHashMap<token::DelimToken, Span>,
+    last_delim_empty_block_spans: FxHashMap<Delimiter, Span>,
     /// Collect the spans of braces (Open, Close). Used only
     /// for detecting if blocks are empty and only braces.
     matching_block_spans: Vec<(Span, Span)>,
@@ -88,7 +88,7 @@ impl<'a> TokenTreesReader<'a> {
                 for &(_, sp) in &self.open_braces {
                     err.span_label(sp, "unclosed delimiter");
                     self.unmatched_braces.push(UnmatchedBrace {
-                        expected_delim: token::DelimToken::Brace,
+                        expected_delim: Delimiter::Brace,
                         found_delim: None,
                         found_span: self.token.span,
                         unclosed_span: Some(sp),
@@ -150,7 +150,7 @@ impl<'a> TokenTreesReader<'a> {
                         }
 
                         //only add braces
-                        if let (DelimToken::Brace, DelimToken::Brace) = (open_brace, delim) {
+                        if let (Delimiter::Brace, Delimiter::Brace) = (open_brace, delim) {
                             self.matching_block_spans.push((open_brace_span, close_brace_span));
                         }
 
diff --git a/compiler/rustc_parse/src/lexer/unicode_chars.rs b/compiler/rustc_parse/src/lexer/unicode_chars.rs
index 1d63b79adc5..faa686c3e57 100644
--- a/compiler/rustc_parse/src/lexer/unicode_chars.rs
+++ b/compiler/rustc_parse/src/lexer/unicode_chars.rs
@@ -2,7 +2,7 @@
 // https://www.unicode.org/Public/security/10.0.0/confusables.txt
 
 use super::StringReader;
-use crate::token;
+use crate::token::{self, Delimiter};
 use rustc_errors::{Applicability, Diagnostic};
 use rustc_span::{symbol::kw, BytePos, Pos, Span};
 
@@ -312,12 +312,12 @@ const ASCII_ARRAY: &[(char, &str, Option<token::TokenKind>)] = &[
     ('!', "Exclamation Mark", Some(token::Not)),
     ('?', "Question Mark", Some(token::Question)),
     ('.', "Period", Some(token::Dot)),
-    ('(', "Left Parenthesis", Some(token::OpenDelim(token::Paren))),
-    (')', "Right Parenthesis", Some(token::CloseDelim(token::Paren))),
-    ('[', "Left Square Bracket", Some(token::OpenDelim(token::Bracket))),
-    (']', "Right Square Bracket", Some(token::CloseDelim(token::Bracket))),
-    ('{', "Left Curly Brace", Some(token::OpenDelim(token::Brace))),
-    ('}', "Right Curly Brace", Some(token::CloseDelim(token::Brace))),
+    ('(', "Left Parenthesis", Some(token::OpenDelim(Delimiter::Parenthesis))),
+    (')', "Right Parenthesis", Some(token::CloseDelim(Delimiter::Parenthesis))),
+    ('[', "Left Square Bracket", Some(token::OpenDelim(Delimiter::Bracket))),
+    (']', "Right Square Bracket", Some(token::CloseDelim(Delimiter::Bracket))),
+    ('{', "Left Curly Brace", Some(token::OpenDelim(Delimiter::Brace))),
+    ('}', "Right Curly Brace", Some(token::CloseDelim(Delimiter::Brace))),
     ('*', "Asterisk", Some(token::BinOp(token::Star))),
     ('/', "Slash", Some(token::BinOp(token::Slash))),
     ('\\', "Backslash", None),
@@ -338,9 +338,7 @@ pub(super) fn check_for_substitution<'a>(
     ch: char,
     err: &mut Diagnostic,
 ) -> Option<token::TokenKind> {
-    let Some(&(_u_char, u_name, ascii_char)) = UNICODE_ARRAY.iter().find(|&&(c, _, _)| c == ch) else {
-        return None;
-    };
+    let &(_u_char, u_name, ascii_char) = UNICODE_ARRAY.iter().find(|&&(c, _, _)| c == ch)?;
 
     let span = Span::with_root_ctxt(pos, pos + Pos::from_usize(ch.len_utf8()));
 
diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs
index 1724bab5caa..358b01df3b9 100644
--- a/compiler/rustc_parse/src/parser/attr.rs
+++ b/compiler/rustc_parse/src/parser/attr.rs
@@ -1,7 +1,7 @@
 use super::{AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, PathStyle};
 use rustc_ast as ast;
 use rustc_ast::attr;
-use rustc_ast::token::{self, Nonterminal};
+use rustc_ast::token::{self, Delimiter, Nonterminal};
 use rustc_ast_pretty::pprust;
 use rustc_errors::{error_code, Diagnostic, PResult};
 use rustc_span::{sym, BytePos, Span};
@@ -130,9 +130,9 @@ impl<'a> Parser<'a> {
                     ast::AttrStyle::Outer
                 };
 
-                this.expect(&token::OpenDelim(token::Bracket))?;
+                this.expect(&token::OpenDelim(Delimiter::Bracket))?;
                 let item = this.parse_attr_item(false)?;
-                this.expect(&token::CloseDelim(token::Bracket))?;
+                this.expect(&token::CloseDelim(Delimiter::Bracket))?;
                 let attr_sp = lo.to(this.prev_token.span);
 
                 // Emit error if inner attribute is encountered and forbidden.
@@ -403,7 +403,7 @@ impl<'a> Parser<'a> {
     crate fn parse_meta_item_kind(&mut self) -> PResult<'a, ast::MetaItemKind> {
         Ok(if self.eat(&token::Eq) {
             ast::MetaItemKind::NameValue(self.parse_unsuffixed_lit()?)
-        } else if self.check(&token::OpenDelim(token::Paren)) {
+        } else if self.check(&token::OpenDelim(Delimiter::Parenthesis)) {
             // Matches `meta_seq = ( COMMASEP(meta_item_inner) )`.
             let (list, _) = self.parse_paren_comma_seq(|p| p.parse_meta_item_inner())?;
             ast::MetaItemKind::List(list)
diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs
index 5ee9c339bb7..a12621564ab 100644
--- a/compiler/rustc_parse/src/parser/attr_wrapper.rs
+++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs
@@ -1,11 +1,11 @@
 use super::{Capturing, FlatToken, ForceCollect, Parser, ReplaceRange, TokenCursor, TrailingToken};
-use rustc_ast::token::{self, DelimToken, Token, TokenKind};
+use rustc_ast::token::{self, Delimiter, Token, TokenKind};
 use rustc_ast::tokenstream::{AttrAnnotatedTokenStream, AttributesData, CreateTokenStream};
 use rustc_ast::tokenstream::{AttrAnnotatedTokenTree, DelimSpan, LazyTokenStream, Spacing};
 use rustc_ast::{self as ast};
 use rustc_ast::{AstLike, AttrVec, Attribute};
 use rustc_errors::PResult;
-use rustc_span::{sym, Span, DUMMY_SP};
+use rustc_span::{sym, Span};
 
 use std::convert::TryInto;
 use std::ops::Range;
@@ -100,21 +100,16 @@ rustc_data_structures::static_assert_size!(LazyTokenStreamImpl, 144);
 
 impl CreateTokenStream for LazyTokenStreamImpl {
     fn create_token_stream(&self) -> AttrAnnotatedTokenStream {
-        // The token produced by the final call to `{,inlined_}next` or
-        // `{,inlined_}next_desugared` was not actually consumed by the
-        // callback. The combination of chaining the initial token and using
-        // `take` produces the desired result - we produce an empty
-        // `TokenStream` if no calls were made, and omit the final token
-        // otherwise.
+        // The token produced by the final call to `{,inlined_}next` was not
+        // actually consumed by the callback. The combination of chaining the
+        // initial token and using `take` produces the desired result - we
+        // produce an empty `TokenStream` if no calls were made, and omit the
+        // final token otherwise.
         let mut cursor_snapshot = self.cursor_snapshot.clone();
         let tokens =
             std::iter::once((FlatToken::Token(self.start_token.0.clone()), self.start_token.1))
                 .chain((0..self.num_calls).map(|_| {
-                    let token = if cursor_snapshot.desugar_doc_comments {
-                        cursor_snapshot.next_desugared()
-                    } else {
-                        cursor_snapshot.next()
-                    };
+                    let token = cursor_snapshot.next(cursor_snapshot.desugar_doc_comments);
                     (FlatToken::Token(token.0), token.1)
                 }))
                 .take(self.num_calls);
@@ -393,11 +388,11 @@ impl<'a> Parser<'a> {
 /// Converts a flattened iterator of tokens (including open and close delimiter tokens)
 /// into a `TokenStream`, creating a `TokenTree::Delimited` for each matching pair
 /// of open and close delims.
-// FIXME(#67062): Currently, we don't parse `None`-delimited groups correctly,
-// which can cause us to end up with mismatched `None` delimiters in our
+// FIXME(#67062): Currently, we don't parse `Invisible`-delimited groups correctly,
+// which can cause us to end up with mismatched `Invisible` delimiters in our
 // captured tokens. This function contains several hacks to work around this -
-// essentially, we throw away mismatched `None` delimiters when we encounter them.
-// Once we properly parse `None` delimiters, they can be captured just like any
+// essentially, we throw away mismatched `Invisible` delimiters when we encounter them.
+// Once we properly parse `Invisible` delimiters, they can be captured just like any
 // other tokens, and these hacks can be removed.
 fn make_token_stream(
     mut iter: impl Iterator<Item = (FlatToken, Spacing)>,
@@ -405,24 +400,26 @@ fn make_token_stream(
 ) -> AttrAnnotatedTokenStream {
     #[derive(Debug)]
     struct FrameData {
-        open: Span,
-        open_delim: DelimToken,
+        // This is `None` for the first frame, `Some` for all others.
+        open_delim_sp: Option<(Delimiter, Span)>,
         inner: Vec<(AttrAnnotatedTokenTree, Spacing)>,
     }
-    let mut stack =
-        vec![FrameData { open: DUMMY_SP, open_delim: DelimToken::NoDelim, inner: vec![] }];
+    let mut stack = vec![FrameData { open_delim_sp: None, inner: vec![] }];
     let mut token_and_spacing = iter.next();
     while let Some((token, spacing)) = token_and_spacing {
         match token {
             FlatToken::Token(Token { kind: TokenKind::OpenDelim(delim), span }) => {
-                stack.push(FrameData { open: span, open_delim: delim, inner: vec![] });
+                stack.push(FrameData { open_delim_sp: Some((delim, span)), inner: vec![] });
             }
             FlatToken::Token(Token { kind: TokenKind::CloseDelim(delim), span }) => {
-                // HACK: If we encounter a mismatched `None` delimiter at the top
+                // HACK: If we encounter a mismatched `Invisible` delimiter at the top
                 // level, just ignore it.
-                if matches!(delim, DelimToken::NoDelim)
+                if matches!(delim, Delimiter::Invisible)
                     && (stack.len() == 1
-                        || !matches!(stack.last_mut().unwrap().open_delim, DelimToken::NoDelim))
+                        || !matches!(
+                            stack.last_mut().unwrap().open_delim_sp.unwrap().0,
+                            Delimiter::Invisible
+                        ))
                 {
                     token_and_spacing = iter.next();
                     continue;
@@ -431,11 +428,11 @@ fn make_token_stream(
                     .pop()
                     .unwrap_or_else(|| panic!("Token stack was empty for token: {:?}", token));
 
-                // HACK: If our current frame has a mismatched opening `None` delimiter,
+                // HACK: If our current frame has a mismatched opening `Invisible` delimiter,
                 // merge our current frame with the one above it. That is, transform
                 // `[ { < first second } third ]` into `[ { first second } third ]`
-                if !matches!(delim, DelimToken::NoDelim)
-                    && matches!(frame_data.open_delim, DelimToken::NoDelim)
+                if !matches!(delim, Delimiter::Invisible)
+                    && matches!(frame_data.open_delim_sp.unwrap().0, Delimiter::Invisible)
                 {
                     stack.last_mut().unwrap().inner.extend(frame_data.inner);
                     // Process our closing delimiter again, this time at the previous
@@ -444,12 +441,13 @@ fn make_token_stream(
                     continue;
                 }
 
+                let (open_delim, open_sp) = frame_data.open_delim_sp.unwrap();
                 assert_eq!(
-                    frame_data.open_delim, delim,
+                    open_delim, delim,
                     "Mismatched open/close delims: open={:?} close={:?}",
-                    frame_data.open, span
+                    open_delim, span
                 );
-                let dspan = DelimSpan::from_pair(frame_data.open, span);
+                let dspan = DelimSpan::from_pair(open_sp, span);
                 let stream = AttrAnnotatedTokenStream::new(frame_data.inner);
                 let delimited = AttrAnnotatedTokenTree::Delimited(dspan, delim, stream);
                 stack
@@ -474,10 +472,10 @@ fn make_token_stream(
         }
         token_and_spacing = iter.next();
     }
-    // HACK: If we don't have a closing `None` delimiter for our last
+    // HACK: If we don't have a closing `Invisible` delimiter for our last
     // frame, merge the frame with the top-level frame. That is,
     // turn `< first second` into `first second`
-    if stack.len() == 2 && stack[1].open_delim == DelimToken::NoDelim {
+    if stack.len() == 2 && stack[1].open_delim_sp.unwrap().0 == Delimiter::Invisible {
         let temp_buf = stack.pop().unwrap();
         stack.last_mut().unwrap().inner.extend(temp_buf.inner);
     }
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index ed264045170..beffbdc5de4 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -8,7 +8,7 @@ use super::{
 use crate::lexer::UnmatchedBrace;
 use rustc_ast as ast;
 use rustc_ast::ptr::P;
-use rustc_ast::token::{self, Lit, LitKind, TokenKind};
+use rustc_ast::token::{self, Delimiter, Lit, LitKind, TokenKind};
 use rustc_ast::util::parser::AssocOp;
 use rustc_ast::{
     AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingMode, Block,
@@ -21,6 +21,7 @@ use rustc_errors::{pluralize, struct_span_err, Diagnostic, EmissionGuarantee, Er
 use rustc_errors::{
     Applicability, DiagnosticBuilder, DiagnosticMessage, Handler, MultiSpan, PResult,
 };
+use rustc_macros::SessionDiagnostic;
 use rustc_span::source_map::Spanned;
 use rustc_span::symbol::{kw, Ident};
 use rustc_span::{Span, SpanSnippetError, DUMMY_SP};
@@ -241,6 +242,16 @@ impl MultiSugg {
         err.multipart_suggestions(msg, suggestions.map(|s| s.patches), applicability);
     }
 }
+
+#[derive(SessionDiagnostic)]
+#[error(slug = "parser-maybe-report-ambiguous-plus")]
+struct AmbiguousPlus {
+    pub sum_ty: String,
+    #[primary_span]
+    #[suggestion(code = "({sum_ty})")]
+    pub span: Span,
+}
+
 // SnapshotParser is used to create a snapshot of the parser
 // without causing duplicate errors being emitted when the `Parser`
 // is dropped.
@@ -326,10 +337,10 @@ impl<'a> Parser<'a> {
             TokenKind::Comma,
             TokenKind::Semi,
             TokenKind::ModSep,
-            TokenKind::OpenDelim(token::DelimToken::Brace),
-            TokenKind::OpenDelim(token::DelimToken::Paren),
-            TokenKind::CloseDelim(token::DelimToken::Brace),
-            TokenKind::CloseDelim(token::DelimToken::Paren),
+            TokenKind::OpenDelim(Delimiter::Brace),
+            TokenKind::OpenDelim(Delimiter::Parenthesis),
+            TokenKind::CloseDelim(Delimiter::Brace),
+            TokenKind::CloseDelim(Delimiter::Parenthesis),
         ];
         match self.token.ident() {
             Some((ident, false))
@@ -402,7 +413,7 @@ impl<'a> Parser<'a> {
             } else if !sm.is_multiline(self.prev_token.span.until(self.token.span)) {
                 // The current token is in the same line as the prior token, not recoverable.
             } else if [token::Comma, token::Colon].contains(&self.token.kind)
-                && self.prev_token.kind == token::CloseDelim(token::Paren)
+                && self.prev_token.kind == token::CloseDelim(Delimiter::Parenthesis)
             {
                 // Likely typo: The current token is on a new line and is expected to be
                 // `.`, `;`, `?`, or an operator after a close delimiter token.
@@ -413,7 +424,7 @@ impl<'a> Parser<'a> {
                 //         ^
                 // https://github.com/rust-lang/rust/issues/72253
             } else if self.look_ahead(1, |t| {
-                t == &token::CloseDelim(token::Brace)
+                t == &token::CloseDelim(Delimiter::Brace)
                     || t.can_begin_expr() && t.kind != token::Colon
             }) && [token::Comma, token::Colon].contains(&self.token.kind)
             {
@@ -430,11 +441,12 @@ impl<'a> Parser<'a> {
                     .emit();
                 return Ok(true);
             } else if self.look_ahead(0, |t| {
-                t == &token::CloseDelim(token::Brace)
-                    || (
-                        t.can_begin_expr() && t != &token::Semi && t != &token::Pound
-                        // Avoid triggering with too many trailing `#` in raw string.
-                    )
+                t == &token::CloseDelim(Delimiter::Brace)
+                    || (t.can_begin_expr() && t != &token::Semi && t != &token::Pound)
+                    // Avoid triggering with too many trailing `#` in raw string.
+                    || (sm.is_multiline(
+                        self.prev_token.span.shrink_to_hi().until(self.token.span.shrink_to_lo())
+                    ) && t == &token::Pound)
             }) {
                 // Missing semicolon typo. This is triggered if the next token could either start a
                 // new statement or is a block close. For example:
@@ -508,7 +520,12 @@ impl<'a> Parser<'a> {
         }
 
         if self.check_too_many_raw_str_terminators(&mut err) {
-            return Err(err);
+            if expected.contains(&TokenType::Token(token::Semi)) && self.eat(&token::Semi) {
+                err.emit();
+                return Ok(true);
+            } else {
+                return Err(err);
+            }
         }
 
         if self.prev_token.span == DUMMY_SP {
@@ -538,6 +555,7 @@ impl<'a> Parser<'a> {
     }
 
     fn check_too_many_raw_str_terminators(&mut self, err: &mut Diagnostic) -> bool {
+        let sm = self.sess.source_map();
         match (&self.prev_token.kind, &self.token.kind) {
             (
                 TokenKind::Literal(Lit {
@@ -545,15 +563,33 @@ impl<'a> Parser<'a> {
                     ..
                 }),
                 TokenKind::Pound,
-            ) => {
+            ) if !sm.is_multiline(
+                self.prev_token.span.shrink_to_hi().until(self.token.span.shrink_to_lo()),
+            ) =>
+            {
+                let n_hashes: u8 = *n_hashes;
                 err.set_primary_message("too many `#` when terminating raw string");
+                let str_span = self.prev_token.span;
+                let mut span = self.token.span;
+                let mut count = 0;
+                while self.token.kind == TokenKind::Pound
+                    && !sm.is_multiline(span.shrink_to_hi().until(self.token.span.shrink_to_lo()))
+                {
+                    span = span.with_hi(self.token.span.hi());
+                    self.bump();
+                    count += 1;
+                }
+                err.set_span(span);
                 err.span_suggestion(
-                    self.token.span,
-                    "remove the extra `#`",
+                    span,
+                    &format!("remove the extra `#`{}", pluralize!(count)),
                     String::new(),
                     Applicability::MachineApplicable,
                 );
-                err.note(&format!("the raw string started with {n_hashes} `#`s"));
+                err.span_label(
+                    str_span,
+                    &format!("this raw string started with {n_hashes} `#`{}", pluralize!(n_hashes)),
+                );
                 true
             }
             _ => false,
@@ -619,7 +655,7 @@ impl<'a> Parser<'a> {
                 (Err(snapshot_err), Err(err)) => {
                     // We don't know what went wrong, emit the normal error.
                     snapshot_err.cancel();
-                    self.consume_block(token::Brace, ConsumeClosingDelim::Yes);
+                    self.consume_block(Delimiter::Brace, ConsumeClosingDelim::Yes);
                     Err(err)
                 }
                 (Ok(_), Ok(mut tail)) => {
@@ -830,7 +866,7 @@ impl<'a> Parser<'a> {
                         trailing_span = trailing_span.to(self.token.span);
                         self.bump();
                     }
-                    if self.token.kind == token::OpenDelim(token::Paren) {
+                    if self.token.kind == token::OpenDelim(Delimiter::Parenthesis) {
                         // Recover from bad turbofish: `foo.collect::Vec<_>()`.
                         let args = AngleBracketedArgs { args, span }.into();
                         segment.args = args;
@@ -1062,7 +1098,7 @@ impl<'a> Parser<'a> {
                             [(token::Lt, 1), (token::Gt, -1), (token::BinOp(token::Shr), -2)];
                         self.consume_tts(1, &modifiers);
 
-                        if !&[token::OpenDelim(token::Paren), token::ModSep]
+                        if !&[token::OpenDelim(Delimiter::Parenthesis), token::ModSep]
                             .contains(&self.token.kind)
                         {
                             // We don't have `foo< bar >(` or `foo< bar >::`, so we rewind the
@@ -1096,7 +1132,7 @@ impl<'a> Parser<'a> {
                                 Err(err)
                             }
                         }
-                    } else if token::OpenDelim(token::Paren) == self.token.kind {
+                    } else if token::OpenDelim(Delimiter::Parenthesis) == self.token.kind {
                         // We have high certainty that this was a bad turbofish at this point.
                         // `foo< bar >(`
                         suggest(&mut err);
@@ -1150,8 +1186,10 @@ impl<'a> Parser<'a> {
         self.bump(); // `(`
 
         // Consume the fn call arguments.
-        let modifiers =
-            [(token::OpenDelim(token::Paren), 1), (token::CloseDelim(token::Paren), -1)];
+        let modifiers = [
+            (token::OpenDelim(Delimiter::Parenthesis), 1),
+            (token::CloseDelim(Delimiter::Parenthesis), -1),
+        ];
         self.consume_tts(1, &modifiers);
 
         if self.token.kind == token::Eof {
@@ -1171,15 +1209,7 @@ impl<'a> Parser<'a> {
         ty: &Ty,
     ) {
         if matches!(allow_plus, AllowPlus::No) && impl_dyn_multi {
-            let sum_with_parens = format!("({})", pprust::ty_to_string(&ty));
-            self.struct_span_err(ty.span, "ambiguous `+` in a type")
-                .span_suggestion(
-                    ty.span,
-                    "use parentheses to disambiguate",
-                    sum_with_parens,
-                    Applicability::MachineApplicable,
-                )
-                .emit();
+            self.sess.emit_err(AmbiguousPlus { sum_ty: pprust::ty_to_string(&ty), span: ty.span });
         }
     }
 
@@ -1551,15 +1581,15 @@ impl<'a> Parser<'a> {
 
     fn recover_await_macro(&mut self) -> PResult<'a, (Span, P<Expr>, bool)> {
         self.expect(&token::Not)?;
-        self.expect(&token::OpenDelim(token::Paren))?;
+        self.expect(&token::OpenDelim(Delimiter::Parenthesis))?;
         let expr = self.parse_expr()?;
-        self.expect(&token::CloseDelim(token::Paren))?;
+        self.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
         Ok((self.prev_token.span, expr, false))
     }
 
     fn recover_await_prefix(&mut self, await_sp: Span) -> PResult<'a, (Span, P<Expr>, bool)> {
         let is_question = self.eat(&token::Question); // Handle `await? <expr>`.
-        let expr = if self.token == token::OpenDelim(token::Brace) {
+        let expr = if self.token == token::OpenDelim(Delimiter::Brace) {
             // Handle `await { <expr> }`.
             // This needs to be handled separately from the next arm to avoid
             // interpreting `await { <expr> }?` as `<expr>?.await`.
@@ -1591,8 +1621,8 @@ impl<'a> Parser<'a> {
 
     /// If encountering `future.await()`, consumes and emits an error.
     pub(super) fn recover_from_await_method_call(&mut self) {
-        if self.token == token::OpenDelim(token::Paren)
-            && self.look_ahead(1, |t| t == &token::CloseDelim(token::Paren))
+        if self.token == token::OpenDelim(Delimiter::Parenthesis)
+            && self.look_ahead(1, |t| t == &token::CloseDelim(Delimiter::Parenthesis))
         {
             // future.await()
             let lo = self.token.span;
@@ -1613,7 +1643,7 @@ impl<'a> Parser<'a> {
     pub(super) fn try_macro_suggestion(&mut self) -> PResult<'a, P<Expr>> {
         let is_try = self.token.is_keyword(kw::Try);
         let is_questionmark = self.look_ahead(1, |t| t == &token::Not); //check for !
-        let is_open = self.look_ahead(2, |t| t == &token::OpenDelim(token::Paren)); //check for (
+        let is_open = self.look_ahead(2, |t| t == &token::OpenDelim(Delimiter::Parenthesis)); //check for (
 
         if is_try && is_questionmark && is_open {
             let lo = self.token.span;
@@ -1621,8 +1651,8 @@ impl<'a> Parser<'a> {
             self.bump(); //remove !
             let try_span = lo.to(self.token.span); //we take the try!( span
             self.bump(); //remove (
-            let is_empty = self.token == token::CloseDelim(token::Paren); //check if the block is empty
-            self.consume_block(token::Paren, ConsumeClosingDelim::No); //eat the block
+            let is_empty = self.token == token::CloseDelim(Delimiter::Parenthesis); //check if the block is empty
+            self.consume_block(Delimiter::Parenthesis, ConsumeClosingDelim::No); //eat the block
             let hi = self.token.span;
             self.bump(); //remove )
             let mut err = self.struct_span_err(lo.to(hi), "use of deprecated `try` macro");
@@ -1653,7 +1683,7 @@ impl<'a> Parser<'a> {
         begin_paren: Option<Span>,
     ) -> P<Pat> {
         match (&self.token.kind, begin_paren) {
-            (token::CloseDelim(token::Paren), Some(begin_par_sp)) => {
+            (token::CloseDelim(Delimiter::Parenthesis), Some(begin_par_sp)) => {
                 self.bump();
 
                 self.struct_span_err(
@@ -1686,8 +1716,8 @@ impl<'a> Parser<'a> {
             || self.token.is_ident() &&
             matches!(node, ast::ExprKind::Path(..) | ast::ExprKind::Field(..)) &&
             !self.token.is_reserved_ident() &&           // v `foo:bar(baz)`
-            self.look_ahead(1, |t| t == &token::OpenDelim(token::Paren))
-            || self.look_ahead(1, |t| t == &token::OpenDelim(token::Brace)) // `foo:bar {`
+            self.look_ahead(1, |t| t == &token::OpenDelim(Delimiter::Parenthesis))
+            || self.look_ahead(1, |t| t == &token::OpenDelim(Delimiter::Brace)) // `foo:bar {`
             || self.look_ahead(1, |t| t == &token::Colon) &&     // `foo:bar::<baz`
             self.look_ahead(2, |t| t == &token::Lt) &&
             self.look_ahead(3, |t| t.is_ident())
@@ -1700,7 +1730,7 @@ impl<'a> Parser<'a> {
 
     pub(super) fn recover_seq_parse_error(
         &mut self,
-        delim: token::DelimToken,
+        delim: Delimiter,
         lo: Span,
         result: PResult<'a, P<Expr>>,
     ) -> P<Expr> {
@@ -1817,7 +1847,7 @@ impl<'a> Parser<'a> {
         loop {
             debug!("recover_stmt_ loop {:?}", self.token);
             match self.token.kind {
-                token::OpenDelim(token::DelimToken::Brace) => {
+                token::OpenDelim(Delimiter::Brace) => {
                     brace_depth += 1;
                     self.bump();
                     if break_on_block == BlockMode::Break && brace_depth == 1 && bracket_depth == 0
@@ -1825,11 +1855,11 @@ impl<'a> Parser<'a> {
                         in_block = true;
                     }
                 }
-                token::OpenDelim(token::DelimToken::Bracket) => {
+                token::OpenDelim(Delimiter::Bracket) => {
                     bracket_depth += 1;
                     self.bump();
                 }
-                token::CloseDelim(token::DelimToken::Brace) => {
+                token::CloseDelim(Delimiter::Brace) => {
                     if brace_depth == 0 {
                         debug!("recover_stmt_ return - close delim {:?}", self.token);
                         break;
@@ -1841,7 +1871,7 @@ impl<'a> Parser<'a> {
                         break;
                     }
                 }
-                token::CloseDelim(token::DelimToken::Bracket) => {
+                token::CloseDelim(Delimiter::Bracket) => {
                     bracket_depth -= 1;
                     if bracket_depth < 0 {
                         bracket_depth = 0;
@@ -1899,11 +1929,11 @@ impl<'a> Parser<'a> {
             .emit();
             self.bump();
         } else if self.token == token::Pound
-            && self.look_ahead(1, |t| *t == token::OpenDelim(token::Bracket))
+            && self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Bracket))
         {
             let lo = self.token.span;
             // Skip every token until next possible arg.
-            while self.token != token::CloseDelim(token::Bracket) {
+            while self.token != token::CloseDelim(Delimiter::Bracket) {
                 self.bump();
             }
             let sp = lo.to(self.token.span);
@@ -1924,7 +1954,9 @@ impl<'a> Parser<'a> {
         // If we find a pattern followed by an identifier, it could be an (incorrect)
         // C-style parameter declaration.
         if self.check_ident()
-            && self.look_ahead(1, |t| *t == token::Comma || *t == token::CloseDelim(token::Paren))
+            && self.look_ahead(1, |t| {
+                *t == token::Comma || *t == token::CloseDelim(Delimiter::Parenthesis)
+            })
         {
             // `fn foo(String s) {}`
             let ident = self.parse_ident().unwrap();
@@ -1940,7 +1972,7 @@ impl<'a> Parser<'a> {
         } else if require_name
             && (self.token == token::Comma
                 || self.token == token::Lt
-                || self.token == token::CloseDelim(token::Paren))
+                || self.token == token::CloseDelim(Delimiter::Parenthesis))
         {
             let rfc_note = "anonymous parameters are removed in the 2018 edition (see RFC 1685)";
 
@@ -2058,11 +2090,7 @@ impl<'a> Parser<'a> {
         Ok(param)
     }
 
-    pub(super) fn consume_block(
-        &mut self,
-        delim: token::DelimToken,
-        consume_close: ConsumeClosingDelim,
-    ) {
+    pub(super) fn consume_block(&mut self, delim: Delimiter, consume_close: ConsumeClosingDelim) {
         let mut brace_depth = 0;
         loop {
             if self.eat(&token::OpenDelim(delim)) {
@@ -2081,7 +2109,8 @@ impl<'a> Parser<'a> {
                     brace_depth -= 1;
                     continue;
                 }
-            } else if self.token == token::Eof || self.eat(&token::CloseDelim(token::NoDelim)) {
+            } else if self.token == token::Eof || self.eat(&token::CloseDelim(Delimiter::Invisible))
+            {
                 return;
             } else {
                 self.bump();
@@ -2527,7 +2556,7 @@ impl<'a> Parser<'a> {
 
     crate fn maybe_recover_unexpected_block_label(&mut self) -> bool {
         let Some(label) = self.eat_label().filter(|_| {
-            self.eat(&token::Colon) && self.token.kind == token::OpenDelim(token::Brace)
+            self.eat(&token::Colon) && self.token.kind == token::OpenDelim(Delimiter::Brace)
         }) else {
             return false;
         };
@@ -2624,7 +2653,7 @@ impl<'a> Parser<'a> {
     /// Parse and throw away a parenthesized comma separated
     /// sequence of patterns until `)` is reached.
     fn skip_pat_list(&mut self) -> PResult<'a, ()> {
-        while !self.check(&token::CloseDelim(token::Paren)) {
+        while !self.check(&token::CloseDelim(Delimiter::Parenthesis)) {
             self.parse_pat_no_top_alt(None)?;
             if !self.eat(&token::Comma) {
                 return Ok(());
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 7efc0ca2da2..6114e7aaa7b 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -7,9 +7,8 @@ use super::{
 };
 use crate::maybe_recover_from_interpolated_ty_qpath;
 
-use ast::token::DelimToken;
 use rustc_ast::ptr::P;
-use rustc_ast::token::{self, Token, TokenKind};
+use rustc_ast::token::{self, Delimiter, Token, TokenKind};
 use rustc_ast::tokenstream::Spacing;
 use rustc_ast::util::classify;
 use rustc_ast::util::literal::LitError;
@@ -495,7 +494,7 @@ impl<'a> Parser<'a> {
     fn is_at_start_of_range_notation_rhs(&self) -> bool {
         if self.token.can_begin_expr() {
             // Parse `for i in 1.. { }` as infinite loop, not as `for i in (1..{})`.
-            if self.token == token::OpenDelim(token::Brace) {
+            if self.token == token::OpenDelim(Delimiter::Brace) {
                 return !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL);
             }
             true
@@ -992,8 +991,8 @@ impl<'a> Parser<'a> {
                 return Ok(e);
             }
             e = match self.token.kind {
-                token::OpenDelim(token::Paren) => self.parse_fn_call_expr(lo, e),
-                token::OpenDelim(token::Bracket) => self.parse_index_expr(lo, e)?,
+                token::OpenDelim(Delimiter::Parenthesis) => self.parse_fn_call_expr(lo, e),
+                token::OpenDelim(Delimiter::Bracket) => self.parse_index_expr(lo, e)?,
                 _ => return Ok(e),
             }
         }
@@ -1156,7 +1155,7 @@ impl<'a> Parser<'a> {
 
     /// Parse a function call expression, `expr(...)`.
     fn parse_fn_call_expr(&mut self, lo: Span, fun: P<Expr>) -> P<Expr> {
-        let snapshot = if self.token.kind == token::OpenDelim(token::Paren)
+        let snapshot = if self.token.kind == token::OpenDelim(Delimiter::Parenthesis)
             && self.look_ahead_type_ascription_as_field()
         {
             Some((self.create_snapshot_for_diagnostic(), fun.kind.clone()))
@@ -1173,7 +1172,7 @@ impl<'a> Parser<'a> {
         {
             return expr;
         }
-        self.recover_seq_parse_error(token::Paren, lo, seq)
+        self.recover_seq_parse_error(Delimiter::Parenthesis, lo, seq)
     }
 
     /// If we encounter a parser state that looks like the user has written a `struct` literal with
@@ -1190,8 +1189,10 @@ impl<'a> Parser<'a> {
             (Err(err), Some((mut snapshot, ExprKind::Path(None, path)))) => {
                 let name = pprust::path_to_string(&path);
                 snapshot.bump(); // `(`
-                match snapshot.parse_struct_fields(path, false, token::Paren) {
-                    Ok((fields, ..)) if snapshot.eat(&token::CloseDelim(token::Paren)) => {
+                match snapshot.parse_struct_fields(path, false, Delimiter::Parenthesis) {
+                    Ok((fields, ..))
+                        if snapshot.eat(&token::CloseDelim(Delimiter::Parenthesis)) =>
+                    {
                         // We are certain we have `Enum::Foo(a: 3, b: 4)`, suggest
                         // `Enum::Foo { a: 3, b: 4 }` or `Enum::Foo(3, 4)`.
                         self.restore_snapshot(snapshot);
@@ -1241,7 +1242,7 @@ impl<'a> Parser<'a> {
     fn parse_index_expr(&mut self, lo: Span, base: P<Expr>) -> PResult<'a, P<Expr>> {
         self.bump(); // `[`
         let index = self.parse_expr()?;
-        self.expect(&token::CloseDelim(token::Bracket))?;
+        self.expect(&token::CloseDelim(Delimiter::Bracket))?;
         Ok(self.mk_expr(lo.to(self.prev_token.span), self.mk_index(base, index), AttrVec::new()))
     }
 
@@ -1253,10 +1254,10 @@ impl<'a> Parser<'a> {
 
         let fn_span_lo = self.token.span;
         let mut segment = self.parse_path_segment(PathStyle::Expr, None)?;
-        self.check_trailing_angle_brackets(&segment, &[&token::OpenDelim(token::Paren)]);
+        self.check_trailing_angle_brackets(&segment, &[&token::OpenDelim(Delimiter::Parenthesis)]);
         self.check_turbofish_missing_angle_brackets(&mut segment);
 
-        if self.check(&token::OpenDelim(token::Paren)) {
+        if self.check(&token::OpenDelim(Delimiter::Parenthesis)) {
             // Method call `expr.f()`
             let mut args = self.parse_paren_expr_seq()?;
             args.insert(0, self_arg);
@@ -1302,9 +1303,9 @@ impl<'a> Parser<'a> {
             // could be removed without changing functionality, but it's faster
             // to have it here, especially for programs with large constants.
             self.parse_lit_expr(attrs)
-        } else if self.check(&token::OpenDelim(token::Paren)) {
+        } else if self.check(&token::OpenDelim(Delimiter::Parenthesis)) {
             self.parse_tuple_parens_expr(attrs)
-        } else if self.check(&token::OpenDelim(token::Brace)) {
+        } else if self.check(&token::OpenDelim(Delimiter::Brace)) {
             self.parse_block_expr(None, lo, BlockCheckMode::Default, attrs)
         } else if self.check(&token::BinOp(token::Or)) || self.check(&token::OrOr) {
             self.parse_closure_expr(attrs).map_err(|mut err| {
@@ -1315,8 +1316,8 @@ impl<'a> Parser<'a> {
                 }
                 err
             })
-        } else if self.check(&token::OpenDelim(token::Bracket)) {
-            self.parse_array_or_repeat_expr(attrs, token::Bracket)
+        } else if self.check(&token::OpenDelim(Delimiter::Bracket)) {
+            self.parse_array_or_repeat_expr(attrs, Delimiter::Bracket)
         } else if self.check_path() {
             self.parse_path_start_expr(attrs)
         } else if self.check_keyword(kw::Move) || self.check_keyword(kw::Static) {
@@ -1373,6 +1374,8 @@ impl<'a> Parser<'a> {
             self.parse_break_expr(attrs)
         } else if self.eat_keyword(kw::Yield) {
             self.parse_yield_expr(attrs)
+        } else if self.is_do_yeet() {
+            self.parse_yeet_expr(attrs)
         } else if self.eat_keyword(kw::Let) {
             self.parse_let_expr(attrs)
         } else if self.eat_keyword(kw::Underscore) {
@@ -1422,14 +1425,16 @@ impl<'a> Parser<'a> {
 
     fn parse_tuple_parens_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
         let lo = self.token.span;
-        self.expect(&token::OpenDelim(token::Paren))?;
+        self.expect(&token::OpenDelim(Delimiter::Parenthesis))?;
         let (es, trailing_comma) = match self.parse_seq_to_end(
-            &token::CloseDelim(token::Paren),
+            &token::CloseDelim(Delimiter::Parenthesis),
             SeqSep::trailing_allowed(token::Comma),
             |p| p.parse_expr_catch_underscore(),
         ) {
             Ok(x) => x,
-            Err(err) => return Ok(self.recover_seq_parse_error(token::Paren, lo, Err(err))),
+            Err(err) => {
+                return Ok(self.recover_seq_parse_error(Delimiter::Parenthesis, lo, Err(err)));
+            }
         };
         let kind = if es.len() == 1 && !trailing_comma {
             // `(e)` is parenthesized `e`.
@@ -1445,7 +1450,7 @@ impl<'a> Parser<'a> {
     fn parse_array_or_repeat_expr(
         &mut self,
         attrs: AttrVec,
-        close_delim: token::DelimToken,
+        close_delim: Delimiter,
     ) -> PResult<'a, P<Expr>> {
         let lo = self.token.span;
         self.bump(); // `[` or other open delim
@@ -1500,7 +1505,7 @@ impl<'a> Parser<'a> {
                 prior_type_ascription: self.last_type_ascription,
             };
             (self.prev_token.span, ExprKind::MacCall(mac))
-        } else if self.check(&token::OpenDelim(token::Brace)) {
+        } else if self.check(&token::OpenDelim(Delimiter::Brace)) {
             if let Some(expr) = self.maybe_parse_struct_expr(qself.as_ref(), &path, &attrs) {
                 if qself.is_some() {
                     self.sess.gated_spans.gate(sym::more_qualified_paths, path.span);
@@ -1533,7 +1538,7 @@ impl<'a> Parser<'a> {
             self.parse_for_expr(label, lo, attrs)
         } else if self.eat_keyword(kw::Loop) {
             self.parse_loop_expr(label, lo, attrs)
-        } else if self.check(&token::OpenDelim(token::Brace)) || self.token.is_whole_block() {
+        } else if self.check(&token::OpenDelim(Delimiter::Brace)) || self.token.is_whole_block() {
             self.parse_block_expr(label, lo, BlockCheckMode::Default, attrs)
         } else if !ate_colon && (self.check(&TokenKind::Comma) || self.check(&TokenKind::Gt)) {
             // We're probably inside of a `Path<'a>` that needs a turbofish
@@ -1602,6 +1607,21 @@ impl<'a> Parser<'a> {
         self.maybe_recover_from_bad_qpath(expr, true)
     }
 
+    /// Parse `"do" "yeet" expr?`.
+    fn parse_yeet_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
+        let lo = self.token.span;
+
+        self.bump(); // `do`
+        self.bump(); // `yeet`
+
+        let kind = ExprKind::Yeet(self.parse_expr_opt()?);
+
+        let span = lo.to(self.prev_token.span);
+        self.sess.gated_spans.gate(sym::yeet_expr, span);
+        let expr = self.mk_expr(span, kind, attrs);
+        self.maybe_recover_from_bad_qpath(expr, true)
+    }
+
     /// Parse `"break" (('label (:? expr)?) | expr?)` with `"break"` token already eaten.
     /// If the label is followed immediately by a `:` token, the label and `:` are
     /// parsed as part of the expression (i.e. a labeled loop). The language team has
@@ -1631,7 +1651,7 @@ impl<'a> Parser<'a> {
             )
             .emit();
             Some(lexpr)
-        } else if self.token != token::OpenDelim(token::Brace)
+        } else if self.token != token::OpenDelim(Delimiter::Brace)
             || !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL)
         {
             let expr = self.parse_expr_opt()?;
@@ -1768,7 +1788,7 @@ impl<'a> Parser<'a> {
             .span_suggestion(
                 token.span,
                 "must have an integer part",
-                pprust::token_to_string(token).into(),
+                pprust::token_to_string(token),
                 Applicability::MachineApplicable,
             )
             .emit();
@@ -1940,7 +1960,7 @@ impl<'a> Parser<'a> {
         attrs: AttrVec,
     ) -> Option<P<Expr>> {
         let mut snapshot = self.create_snapshot_for_diagnostic();
-        match snapshot.parse_array_or_repeat_expr(attrs, token::Brace) {
+        match snapshot.parse_array_or_repeat_expr(attrs, Delimiter::Brace) {
             Ok(arr) => {
                 let hi = snapshot.prev_token.span;
                 self.struct_span_err(arr.span, "this is a block expression, not an array")
@@ -2043,7 +2063,8 @@ impl<'a> Parser<'a> {
             self.sess.gated_spans.gate(sym::async_closure, span);
         }
 
-        if self.token.kind == TokenKind::Semi && self.token_cursor.frame.delim == DelimToken::Paren
+        if self.token.kind == TokenKind::Semi
+            && matches!(self.token_cursor.frame.delim_sp, Some((Delimiter::Parenthesis, _)))
         {
             // It is likely that the closure body is a block but where the
             // braces have been removed. We will recover and eat the next
@@ -2157,7 +2178,7 @@ impl<'a> Parser<'a> {
             }
         } else {
             let attrs = self.parse_outer_attributes()?.take_for_recovery(); // For recovery.
-            let not_block = self.token != token::OpenDelim(token::Brace);
+            let not_block = self.token != token::OpenDelim(Delimiter::Brace);
             let block = self.parse_block().map_err(|err| {
                 if not_block {
                     self.error_missing_if_then_block(lo, Some(err), missing_then_block_binop_span())
@@ -2282,7 +2303,7 @@ impl<'a> Parser<'a> {
         // This is used below for recovery in case of `for ( $stuff ) $block`
         // in which case we will suggest `for $stuff $block`.
         let begin_paren = match self.token.kind {
-            token::OpenDelim(token::Paren) => Some(self.token.span),
+            token::OpenDelim(Delimiter::Parenthesis) => Some(self.token.span),
             _ => None,
         };
 
@@ -2320,7 +2341,7 @@ impl<'a> Parser<'a> {
             .span_suggestion_short(
                 span,
                 msg,
-                sugg.into(),
+                sugg,
                 // Has been misleading, at least in the past (closed Issue #48492).
                 Applicability::MaybeIncorrect,
             )
@@ -2371,7 +2392,7 @@ impl<'a> Parser<'a> {
         let match_span = self.prev_token.span;
         let lo = self.prev_token.span;
         let scrutinee = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;
-        if let Err(mut e) = self.expect(&token::OpenDelim(token::Brace)) {
+        if let Err(mut e) = self.expect(&token::OpenDelim(Delimiter::Brace)) {
             if self.token == token::Semi {
                 e.span_suggestion_short(
                     match_span,
@@ -2390,7 +2411,7 @@ impl<'a> Parser<'a> {
         attrs.extend(self.parse_inner_attributes()?);
 
         let mut arms: Vec<Arm> = Vec::new();
-        while self.token != token::CloseDelim(token::Brace) {
+        while self.token != token::CloseDelim(Delimiter::Brace) {
             match self.parse_arm() {
                 Ok(arm) => arms.push(arm),
                 Err(mut e) => {
@@ -2398,7 +2419,7 @@ impl<'a> Parser<'a> {
                     e.emit();
                     self.recover_stmt();
                     let span = lo.to(self.token.span);
-                    if self.token == token::CloseDelim(token::Brace) {
+                    if self.token == token::CloseDelim(Delimiter::Brace) {
                         self.bump();
                     }
                     return Ok(self.mk_expr(span, ExprKind::Match(scrutinee, arms), attrs));
@@ -2462,7 +2483,7 @@ impl<'a> Parser<'a> {
         // We might have either a `,` -> `;` typo, or a block without braces. We need
         // a more subtle parsing strategy.
         loop {
-            if self.token.kind == token::CloseDelim(token::Brace) {
+            if self.token.kind == token::CloseDelim(Delimiter::Brace) {
                 // We have reached the closing brace of the `match` expression.
                 return Some(err(self, stmts));
             }
@@ -2570,7 +2591,7 @@ impl<'a> Parser<'a> {
             })?;
 
             let require_comma = classify::expr_requires_semi_to_be_stmt(&expr)
-                && this.token != token::CloseDelim(token::Brace);
+                && this.token != token::CloseDelim(Delimiter::Brace);
 
             let hi = this.prev_token.span;
 
@@ -2591,8 +2612,8 @@ impl<'a> Parser<'a> {
                         TrailingToken::None,
                     ));
                 }
-                this.expect_one_of(&[token::Comma], &[token::CloseDelim(token::Brace)]).map_err(
-                    |mut err| {
+                this.expect_one_of(&[token::Comma], &[token::CloseDelim(Delimiter::Brace)])
+                    .map_err(|mut err| {
                         match (sm.span_to_lines(expr.span), sm.span_to_lines(arm_start_span)) {
                             (Ok(ref expr_lines), Ok(ref arm_start_lines))
                                 if arm_start_lines.lines[0].end_col
@@ -2626,8 +2647,7 @@ impl<'a> Parser<'a> {
                             }
                         }
                         err
-                    },
-                )?;
+                    })?;
             } else {
                 this.eat(&token::Comma);
             }
@@ -2669,13 +2689,17 @@ impl<'a> Parser<'a> {
     fn is_do_catch_block(&self) -> bool {
         self.token.is_keyword(kw::Do)
             && self.is_keyword_ahead(1, &[kw::Catch])
-            && self.look_ahead(2, |t| *t == token::OpenDelim(token::Brace))
+            && self.look_ahead(2, |t| *t == token::OpenDelim(Delimiter::Brace))
             && !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL)
     }
 
+    fn is_do_yeet(&self) -> bool {
+        self.token.is_keyword(kw::Do) && self.is_keyword_ahead(1, &[kw::Yeet])
+    }
+
     fn is_try_block(&self) -> bool {
         self.token.is_keyword(kw::Try)
-            && self.look_ahead(1, |t| *t == token::OpenDelim(token::Brace))
+            && self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Brace))
             && self.token.uninterpolated_span().rust_2018()
     }
 
@@ -2695,10 +2719,10 @@ impl<'a> Parser<'a> {
             && ((
                 // `async move {`
                 self.is_keyword_ahead(1, &[kw::Move])
-                    && self.look_ahead(2, |t| *t == token::OpenDelim(token::Brace))
+                    && self.look_ahead(2, |t| *t == token::OpenDelim(Delimiter::Brace))
             ) || (
                 // `async {`
-                self.look_ahead(1, |t| *t == token::OpenDelim(token::Brace))
+                self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Brace))
             ))
     }
 
@@ -2725,7 +2749,7 @@ impl<'a> Parser<'a> {
     ) -> Option<PResult<'a, P<Expr>>> {
         let struct_allowed = !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL);
         if struct_allowed || self.is_certainly_not_a_block() {
-            if let Err(err) = self.expect(&token::OpenDelim(token::Brace)) {
+            if let Err(err) = self.expect(&token::OpenDelim(Delimiter::Brace)) {
                 return Some(Err(err));
             }
             let expr = self.parse_struct_expr(qself.cloned(), path.clone(), attrs.clone(), true);
@@ -2752,7 +2776,7 @@ impl<'a> Parser<'a> {
         &mut self,
         pth: ast::Path,
         recover: bool,
-        close_delim: token::DelimToken,
+        close_delim: Delimiter,
     ) -> PResult<'a, (Vec<ExprField>, ast::StructRest, bool)> {
         let mut fields = Vec::new();
         let mut base = ast::StructRest::None;
@@ -2825,7 +2849,7 @@ impl<'a> Parser<'a> {
                             e.span_suggestion(
                                 self.prev_token.span.shrink_to_hi(),
                                 "try adding a comma",
-                                ",".into(),
+                                ",",
                                 Applicability::MachineApplicable,
                             );
                         }
@@ -2852,9 +2876,9 @@ impl<'a> Parser<'a> {
     ) -> PResult<'a, P<Expr>> {
         let lo = pth.span;
         let (fields, base, recover_async) =
-            self.parse_struct_fields(pth.clone(), recover, token::Brace)?;
+            self.parse_struct_fields(pth.clone(), recover, Delimiter::Brace)?;
         let span = lo.to(self.token.span);
-        self.expect(&token::CloseDelim(token::Brace))?;
+        self.expect(&token::CloseDelim(Delimiter::Brace))?;
         let expr = if recover_async {
             ExprKind::Err
         } else {
diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs
index 29fe2b76101..8081bac7cfd 100644
--- a/compiler/rustc_parse/src/parser/generics.rs
+++ b/compiler/rustc_parse/src/parser/generics.rs
@@ -30,8 +30,10 @@ impl<'a> Parser<'a> {
         let ident = self.parse_ident()?;
 
         // Parse optional colon and param bounds.
+        let mut colon_span = None;
         let bounds = if self.eat(&token::Colon) {
-            self.parse_generic_bounds(Some(self.prev_token.span))?
+            colon_span = Some(self.prev_token.span);
+            self.parse_generic_bounds(colon_span)?
         } else {
             Vec::new()
         };
@@ -45,6 +47,7 @@ impl<'a> Parser<'a> {
             bounds,
             kind: GenericParamKind::Type { default },
             is_placeholder: false,
+            colon_span,
         })
     }
 
@@ -69,6 +72,7 @@ impl<'a> Parser<'a> {
             bounds: Vec::new(),
             kind: GenericParamKind::Const { ty, kw_span: const_span, default },
             is_placeholder: false,
+            colon_span: None,
         })
     }
 
@@ -97,10 +101,10 @@ impl<'a> Parser<'a> {
                     let param = if this.check_lifetime() {
                         let lifetime = this.expect_lifetime();
                         // Parse lifetime parameter.
-                        let bounds = if this.eat(&token::Colon) {
-                            this.parse_lt_param_bounds()
+                        let (colon_span, bounds) = if this.eat(&token::Colon) {
+                            (Some(this.prev_token.span), this.parse_lt_param_bounds())
                         } else {
-                            Vec::new()
+                            (None, Vec::new())
                         };
                         Some(ast::GenericParam {
                             ident: lifetime.ident,
@@ -109,6 +113,7 @@ impl<'a> Parser<'a> {
                             bounds,
                             kind: ast::GenericParamKind::Lifetime,
                             is_placeholder: false,
+                            colon_span,
                         })
                     } else if this.check_keyword(kw::Const) {
                         // Parse const parameter.
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index ca81921faed..10f1daf1129 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -4,7 +4,7 @@ use super::{AttrWrapper, FollowedByType, ForceCollect, Parser, PathStyle, Traili
 
 use rustc_ast::ast::*;
 use rustc_ast::ptr::P;
-use rustc_ast::token::{self, TokenKind};
+use rustc_ast::token::{self, Delimiter, TokenKind};
 use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree};
 use rustc_ast::{self as ast, AttrVec, Attribute, DUMMY_NODE_ID};
 use rustc_ast::{Async, Const, Defaultness, IsAuto, Mutability, Unsafe, UseTree, UseTreeKind};
@@ -39,9 +39,9 @@ impl<'a> Parser<'a> {
         let mod_kind = if self.eat(&token::Semi) {
             ModKind::Unloaded
         } else {
-            self.expect(&token::OpenDelim(token::Brace))?;
+            self.expect(&token::OpenDelim(Delimiter::Brace))?;
             let (mut inner_attrs, items, inner_span) =
-                self.parse_mod(&token::CloseDelim(token::Brace))?;
+                self.parse_mod(&token::CloseDelim(Delimiter::Brace))?;
             attrs.append(&mut inner_attrs);
             ModKind::Loaded(items, Inline::Yes, inner_span)
         };
@@ -324,7 +324,7 @@ impl<'a> Parser<'a> {
         let sp = self.prev_token.span.between(self.token.span);
         let full_sp = self.prev_token.span.to(self.token.span);
         let ident_sp = self.token.span;
-        if self.look_ahead(1, |t| *t == token::OpenDelim(token::Brace)) {
+        if self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Brace)) {
             // possible public struct definition where `struct` was forgotten
             let ident = self.parse_ident().unwrap();
             let msg = format!("add `struct` here to parse `{ident}` as a public struct");
@@ -332,20 +332,20 @@ impl<'a> Parser<'a> {
             err.span_suggestion_short(
                 sp,
                 &msg,
-                " struct ".into(),
+                " struct ",
                 Applicability::MaybeIncorrect, // speculative
             );
             Err(err)
-        } else if self.look_ahead(1, |t| *t == token::OpenDelim(token::Paren)) {
+        } else if self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Parenthesis)) {
             let ident = self.parse_ident().unwrap();
             self.bump(); // `(`
             let kw_name = self.recover_first_param();
-            self.consume_block(token::Paren, ConsumeClosingDelim::Yes);
+            self.consume_block(Delimiter::Parenthesis, ConsumeClosingDelim::Yes);
             let (kw, kw_name, ambiguous) = if self.check(&token::RArrow) {
-                self.eat_to_tokens(&[&token::OpenDelim(token::Brace)]);
+                self.eat_to_tokens(&[&token::OpenDelim(Delimiter::Brace)]);
                 self.bump(); // `{`
                 ("fn", kw_name, false)
-            } else if self.check(&token::OpenDelim(token::Brace)) {
+            } else if self.check(&token::OpenDelim(Delimiter::Brace)) {
                 self.bump(); // `{`
                 ("fn", kw_name, false)
             } else if self.check(&token::Colon) {
@@ -358,7 +358,7 @@ impl<'a> Parser<'a> {
             let msg = format!("missing `{kw}` for {kw_name} definition");
             let mut err = self.struct_span_err(sp, &msg);
             if !ambiguous {
-                self.consume_block(token::Brace, ConsumeClosingDelim::Yes);
+                self.consume_block(Delimiter::Brace, ConsumeClosingDelim::Yes);
                 let suggestion =
                     format!("add `{kw}` here to parse `{ident}` as a public {kw_name}");
                 err.span_suggestion_short(
@@ -386,9 +386,9 @@ impl<'a> Parser<'a> {
             let ident = self.parse_ident().unwrap();
             self.eat_to_tokens(&[&token::Gt]);
             self.bump(); // `>`
-            let (kw, kw_name, ambiguous) = if self.eat(&token::OpenDelim(token::Paren)) {
+            let (kw, kw_name, ambiguous) = if self.eat(&token::OpenDelim(Delimiter::Parenthesis)) {
                 ("fn", self.recover_first_param(), false)
-            } else if self.check(&token::OpenDelim(token::Brace)) {
+            } else if self.check(&token::OpenDelim(Delimiter::Brace)) {
                 ("struct", "struct", false)
             } else {
                 ("fn` or `struct", "function or struct", true)
@@ -532,13 +532,13 @@ impl<'a> Parser<'a> {
                 .span_suggestion(
                     span,
                     "add a trait here",
-                    " Trait ".into(),
+                    " Trait ",
                     Applicability::HasPlaceholders,
                 )
                 .span_suggestion(
                     span.to(self.token.span),
                     "for an inherent impl, drop this `for`",
-                    "".into(),
+                    "",
                     Applicability::MaybeIncorrect,
                 )
                 .emit();
@@ -630,11 +630,11 @@ impl<'a> Parser<'a> {
         mut parse_item: impl FnMut(&mut Parser<'a>) -> PResult<'a, Option<Option<T>>>,
     ) -> PResult<'a, Vec<T>> {
         let open_brace_span = self.token.span;
-        self.expect(&token::OpenDelim(token::Brace))?;
+        self.expect(&token::OpenDelim(Delimiter::Brace))?;
         attrs.append(&mut self.parse_inner_attributes()?);
 
         let mut items = Vec::new();
-        while !self.eat(&token::CloseDelim(token::Brace)) {
+        while !self.eat(&token::CloseDelim(Delimiter::Brace)) {
             if self.recover_doc_comment_before_brace() {
                 continue;
             }
@@ -642,7 +642,7 @@ impl<'a> Parser<'a> {
                 Ok(None) => {
                     // We have to bail or we'll potentially never make progress.
                     let non_item_span = self.token.span;
-                    self.consume_block(token::Brace, ConsumeClosingDelim::Yes);
+                    self.consume_block(Delimiter::Brace, ConsumeClosingDelim::Yes);
                     self.struct_span_err(non_item_span, "non-item in item list")
                         .span_label(open_brace_span, "item list starts here")
                         .span_label(non_item_span, "non-item starts here")
@@ -652,7 +652,7 @@ impl<'a> Parser<'a> {
                 }
                 Ok(Some(item)) => items.extend(item),
                 Err(mut err) => {
-                    self.consume_block(token::Brace, ConsumeClosingDelim::Yes);
+                    self.consume_block(Delimiter::Brace, ConsumeClosingDelim::Yes);
                     err.span_label(open_brace_span, "while parsing this item list starting here")
                         .span_label(self.prev_token.span, "the item list ends here")
                         .emit();
@@ -666,7 +666,7 @@ impl<'a> Parser<'a> {
     /// Recover on a doc comment before `}`.
     fn recover_doc_comment_before_brace(&mut self) -> bool {
         if let token::DocComment(..) = self.token.kind {
-            if self.look_ahead(1, |tok| tok == &token::CloseDelim(token::Brace)) {
+            if self.look_ahead(1, |tok| tok == &token::CloseDelim(Delimiter::Brace)) {
                 struct_span_err!(
                     self.diagnostic(),
                     self.token.span,
@@ -866,7 +866,7 @@ impl<'a> Parser<'a> {
         let lo = self.token.span;
 
         let mut prefix = ast::Path { segments: Vec::new(), span: lo.shrink_to_lo(), tokens: None };
-        let kind = if self.check(&token::OpenDelim(token::Brace))
+        let kind = if self.check(&token::OpenDelim(Delimiter::Brace))
             || self.check(&token::BinOp(token::Star))
             || self.is_import_coupler()
         {
@@ -908,7 +908,7 @@ impl<'a> Parser<'a> {
     /// USE_TREE_LIST = Ø | (USE_TREE `,`)* USE_TREE [`,`]
     /// ```
     fn parse_use_tree_list(&mut self) -> PResult<'a, Vec<(UseTree, ast::NodeId)>> {
-        self.parse_delim_comma_seq(token::Brace, |p| Ok((p.parse_use_tree()?, DUMMY_NODE_ID)))
+        self.parse_delim_comma_seq(Delimiter::Brace, |p| Ok((p.parse_use_tree()?, DUMMY_NODE_ID)))
             .map(|(r, _)| r)
     }
 
@@ -1077,7 +1077,7 @@ impl<'a> Parser<'a> {
             && self.is_keyword_ahead(1, &[kw::Extern])
             && self.look_ahead(
                 2 + self.look_ahead(2, |t| t.can_begin_literal_maybe_minus() as usize),
-                |t| t.kind == token::OpenDelim(token::Brace),
+                |t| t.kind == token::OpenDelim(Delimiter::Brace),
             )
     }
 
@@ -1204,8 +1204,9 @@ impl<'a> Parser<'a> {
         let mut generics = self.parse_generics()?;
         generics.where_clause = self.parse_where_clause()?;
 
-        let (variants, _) =
-            self.parse_delim_comma_seq(token::Brace, |p| p.parse_enum_variant()).map_err(|e| {
+        let (variants, _) = self
+            .parse_delim_comma_seq(Delimiter::Brace, |p| p.parse_enum_variant())
+            .map_err(|e| {
                 self.recover_stmt();
                 e
             })?;
@@ -1228,11 +1229,11 @@ impl<'a> Parser<'a> {
                 }
                 let ident = this.parse_field_ident("enum", vlo)?;
 
-                let struct_def = if this.check(&token::OpenDelim(token::Brace)) {
+                let struct_def = if this.check(&token::OpenDelim(Delimiter::Brace)) {
                     // Parse a struct variant.
                     let (fields, recovered) = this.parse_record_struct_body("struct", false)?;
                     VariantData::Struct(fields, recovered)
-                } else if this.check(&token::OpenDelim(token::Paren)) {
+                } else if this.check(&token::OpenDelim(Delimiter::Parenthesis)) {
                     VariantData::Tuple(this.parse_tuple_struct_body()?, DUMMY_NODE_ID)
                 } else {
                     VariantData::Unit(DUMMY_NODE_ID)
@@ -1292,12 +1293,12 @@ impl<'a> Parser<'a> {
         } else if self.eat(&token::Semi) {
             VariantData::Unit(DUMMY_NODE_ID)
         // Record-style struct definition
-        } else if self.token == token::OpenDelim(token::Brace) {
+        } else if self.token == token::OpenDelim(Delimiter::Brace) {
             let (fields, recovered) =
                 self.parse_record_struct_body("struct", generics.where_clause.has_where_token)?;
             VariantData::Struct(fields, recovered)
         // Tuple-style struct definition with optional where-clause.
-        } else if self.token == token::OpenDelim(token::Paren) {
+        } else if self.token == token::OpenDelim(Delimiter::Parenthesis) {
             let body = VariantData::Tuple(self.parse_tuple_struct_body()?, DUMMY_NODE_ID);
             generics.where_clause = self.parse_where_clause()?;
             self.expect_semi()?;
@@ -1326,7 +1327,7 @@ impl<'a> Parser<'a> {
             let (fields, recovered) =
                 self.parse_record_struct_body("union", generics.where_clause.has_where_token)?;
             VariantData::Struct(fields, recovered)
-        } else if self.token == token::OpenDelim(token::Brace) {
+        } else if self.token == token::OpenDelim(Delimiter::Brace) {
             let (fields, recovered) =
                 self.parse_record_struct_body("union", generics.where_clause.has_where_token)?;
             VariantData::Struct(fields, recovered)
@@ -1348,10 +1349,10 @@ impl<'a> Parser<'a> {
     ) -> PResult<'a, (Vec<FieldDef>, /* recovered */ bool)> {
         let mut fields = Vec::new();
         let mut recovered = false;
-        if self.eat(&token::OpenDelim(token::Brace)) {
-            while self.token != token::CloseDelim(token::Brace) {
+        if self.eat(&token::OpenDelim(Delimiter::Brace)) {
+            while self.token != token::CloseDelim(Delimiter::Brace) {
                 let field = self.parse_field_def(adt_ty).map_err(|e| {
-                    self.consume_block(token::Brace, ConsumeClosingDelim::No);
+                    self.consume_block(Delimiter::Brace, ConsumeClosingDelim::No);
                     recovered = true;
                     e
                 });
@@ -1363,7 +1364,7 @@ impl<'a> Parser<'a> {
                     }
                 }
             }
-            self.eat(&token::CloseDelim(token::Brace));
+            self.eat(&token::CloseDelim(Delimiter::Brace));
         } else {
             let token_str = super::token_descr(&self.token);
             let msg = &format!(
@@ -1439,7 +1440,7 @@ impl<'a> Parser<'a> {
             token::Comma => {
                 self.bump();
             }
-            token::CloseDelim(token::Brace) => {}
+            token::CloseDelim(Delimiter::Brace) => {}
             token::DocComment(..) => {
                 let previous_span = self.prev_token.span;
                 let mut err = self.span_err(self.token.span, Error::UselessDocComment);
@@ -1450,7 +1451,7 @@ impl<'a> Parser<'a> {
                 if !seen_comma && comma_after_doc_seen {
                     seen_comma = true;
                 }
-                if comma_after_doc_seen || self.token == token::CloseDelim(token::Brace) {
+                if comma_after_doc_seen || self.token == token::CloseDelim(Delimiter::Brace) {
                     err.emit();
                 } else {
                     if !seen_comma {
@@ -1458,7 +1459,7 @@ impl<'a> Parser<'a> {
                         err.span_suggestion(
                             sp,
                             "missing comma here",
-                            ",".into(),
+                            ",",
                             Applicability::MachineApplicable,
                         );
                     }
@@ -1478,7 +1479,7 @@ impl<'a> Parser<'a> {
                     if let Some(last_segment) = segments.last() {
                         recovered = self.check_trailing_angle_brackets(
                             last_segment,
-                            &[&token::Comma, &token::CloseDelim(token::Brace)],
+                            &[&token::Comma, &token::CloseDelim(Delimiter::Brace)],
                         );
                         if recovered {
                             // Handle a case like `Vec<u8>>,` where we can continue parsing fields
@@ -1497,7 +1498,7 @@ impl<'a> Parser<'a> {
                     err.span_suggestion(
                         sp,
                         "try adding a comma",
-                        ",".into(),
+                        ",",
                         Applicability::MachineApplicable,
                     );
                     err.emit();
@@ -1636,12 +1637,12 @@ impl<'a> Parser<'a> {
     /// ```
     fn parse_item_decl_macro(&mut self, lo: Span) -> PResult<'a, ItemInfo> {
         let ident = self.parse_ident()?;
-        let body = if self.check(&token::OpenDelim(token::Brace)) {
+        let body = if self.check(&token::OpenDelim(Delimiter::Brace)) {
             self.parse_mac_args()? // `MacBody`
-        } else if self.check(&token::OpenDelim(token::Paren)) {
+        } else if self.check(&token::OpenDelim(Delimiter::Parenthesis)) {
             let params = self.parse_token_tree(); // `MacParams`
             let pspan = params.span();
-            if !self.check(&token::OpenDelim(token::Brace)) {
+            if !self.check(&token::OpenDelim(Delimiter::Brace)) {
                 return self.unexpected();
             }
             let body = self.parse_token_tree(); // `MacBody`
@@ -1924,7 +1925,7 @@ impl<'a> Parser<'a> {
             self.expect_semi()?;
             *sig_hi = self.prev_token.span;
             (Vec::new(), None)
-        } else if self.check(&token::OpenDelim(token::Brace)) || self.token.is_whole_block() {
+        } else if self.check(&token::OpenDelim(Delimiter::Brace)) || self.token.is_whole_block() {
             self.parse_inner_attrs_and_block().map(|(attrs, body)| (attrs, Some(body)))?
         } else if self.token.kind == token::Eq {
             // Recover `fn foo() = $expr;`.
@@ -1943,12 +1944,12 @@ impl<'a> Parser<'a> {
             (Vec::new(), Some(self.mk_block_err(span)))
         } else {
             let expected = if req_body {
-                &[token::OpenDelim(token::Brace)][..]
+                &[token::OpenDelim(Delimiter::Brace)][..]
             } else {
-                &[token::Semi, token::OpenDelim(token::Brace)]
+                &[token::Semi, token::OpenDelim(Delimiter::Brace)]
             };
             if let Err(mut err) = self.expected_one_of_not_found(&[], &expected) {
-                if self.token.kind == token::CloseDelim(token::Brace) {
+                if self.token.kind == token::CloseDelim(Delimiter::Brace) {
                     // The enclosing `mod`, `trait` or `impl` is being closed, so keep the `fn` in
                     // the AST for typechecking.
                     err.span_label(ident.span, "while parsing this `fn`");
@@ -2164,7 +2165,7 @@ impl<'a> Parser<'a> {
                 e.emit();
                 let lo = p.prev_token.span;
                 // Skip every token until next possible arg or end.
-                p.eat_to_tokens(&[&token::Comma, &token::CloseDelim(token::Paren)]);
+                p.eat_to_tokens(&[&token::Comma, &token::CloseDelim(Delimiter::Parenthesis)]);
                 // Create a placeholder argument for proper arg count (issue #34264).
                 Ok(dummy_arg(Ident::new(kw::Empty, lo.to(p.prev_token.span))))
             });
@@ -2220,7 +2221,7 @@ impl<'a> Parser<'a> {
                 let mut ty = this.parse_ty_for_param();
                 if ty.is_ok()
                     && this.token != token::Comma
-                    && this.token != token::CloseDelim(token::Paren)
+                    && this.token != token::CloseDelim(Delimiter::Parenthesis)
                 {
                     // This wasn't actually a type, but a pattern looking like a type,
                     // so we are going to rollback and re-parse for recovery.
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index cb6be8f412c..cd61584a876 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -19,7 +19,7 @@ pub use pat::{CommaRecoveryMode, RecoverColon, RecoverComma};
 pub use path::PathStyle;
 
 use rustc_ast::ptr::P;
-use rustc_ast::token::{self, DelimToken, Nonterminal, Token, TokenKind};
+use rustc_ast::token::{self, Delimiter, Nonterminal, Token, TokenKind};
 use rustc_ast::tokenstream::AttributesData;
 use rustc_ast::tokenstream::{self, DelimSpan, Spacing};
 use rustc_ast::tokenstream::{TokenStream, TokenTree};
@@ -123,8 +123,8 @@ pub struct Parser<'a> {
     pub capture_cfg: bool,
     restrictions: Restrictions,
     expected_tokens: Vec<TokenType>,
-    // Important: This must only be advanced from `next_tok`
-    // to ensure that `token_cursor.num_next_calls` is updated properly
+    // Important: This must only be advanced from `bump` to ensure that
+    // `token_cursor.num_next_calls` is updated properly.
     token_cursor: TokenCursor,
     desugar_doc_comments: bool,
     /// This field is used to keep track of how many left angle brackets we have seen. This is
@@ -150,6 +150,11 @@ pub struct Parser<'a> {
     pub current_closure: Option<ClosureSpans>,
 }
 
+// This type is used a lot, e.g. it's cloned when matching many declarative macro rules. Make sure
+// it doesn't unintentionally get bigger.
+#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+rustc_data_structures::static_assert_size!(Parser<'_>, 328);
+
 /// Stores span information about a closure.
 #[derive(Clone)]
 pub struct ClosureSpans {
@@ -203,12 +208,15 @@ impl<'a> Drop for Parser<'a> {
 
 #[derive(Clone)]
 struct TokenCursor {
+    // The current (innermost) frame. `frame` and `stack` could be combined,
+    // but it's faster to have them separately to access `frame` directly
+    // rather than via something like `stack.last().unwrap()` or
+    // `stack[stack.len() - 1]`.
     frame: TokenCursorFrame,
+    // Additional frames that enclose `frame`.
     stack: Vec<TokenCursorFrame>,
     desugar_doc_comments: bool,
-    // Counts the number of calls to `{,inlined_}next` or
-    // `{,inlined_}next_desugared`, depending on whether
-    // `desugar_doc_comments` is set.
+    // Counts the number of calls to `{,inlined_}next`.
     num_next_calls: usize,
     // During parsing, we may sometimes need to 'unglue' a
     // glued token into two component tokens
@@ -236,75 +244,60 @@ struct TokenCursor {
 
 #[derive(Clone)]
 struct TokenCursorFrame {
-    delim: token::DelimToken,
-    span: DelimSpan,
-    open_delim: bool,
+    delim_sp: Option<(Delimiter, DelimSpan)>,
     tree_cursor: tokenstream::Cursor,
-    close_delim: bool,
 }
 
 impl TokenCursorFrame {
-    fn new(span: DelimSpan, delim: DelimToken, tts: TokenStream) -> Self {
-        TokenCursorFrame {
-            delim,
-            span,
-            open_delim: false,
-            tree_cursor: tts.into_trees(),
-            close_delim: false,
-        }
+    fn new(delim_sp: Option<(Delimiter, DelimSpan)>, tts: TokenStream) -> Self {
+        TokenCursorFrame { delim_sp, tree_cursor: tts.into_trees() }
     }
 }
 
 impl TokenCursor {
-    fn next(&mut self) -> (Token, Spacing) {
-        self.inlined_next()
+    fn next(&mut self, desugar_doc_comments: bool) -> (Token, Spacing) {
+        self.inlined_next(desugar_doc_comments)
     }
 
     /// This always-inlined version should only be used on hot code paths.
     #[inline(always)]
-    fn inlined_next(&mut self) -> (Token, Spacing) {
+    fn inlined_next(&mut self, desugar_doc_comments: bool) -> (Token, Spacing) {
         loop {
-            let (tree, spacing) = if !self.frame.open_delim {
-                self.frame.open_delim = true;
-                TokenTree::open_tt(self.frame.span, self.frame.delim).into()
-            } else if let Some(tree) = self.frame.tree_cursor.next_with_spacing() {
-                tree
-            } else if !self.frame.close_delim {
-                self.frame.close_delim = true;
-                TokenTree::close_tt(self.frame.span, self.frame.delim).into()
+            // FIXME: we currently don't return `Delimiter` open/close delims. To fix #67062 we will
+            // need to, whereupon the `delim != Delimiter::Invisible` conditions below can be
+            // removed.
+            if let Some((tree, spacing)) = self.frame.tree_cursor.next_with_spacing_ref() {
+                match tree {
+                    &TokenTree::Token(ref token) => match (desugar_doc_comments, token) {
+                        (true, &Token { kind: token::DocComment(_, attr_style, data), span }) => {
+                            return self.desugar(attr_style, data, span);
+                        }
+                        _ => return (token.clone(), *spacing),
+                    },
+                    &TokenTree::Delimited(sp, delim, ref tts) => {
+                        // Set `open_delim` to true here because we deal with it immediately.
+                        let frame = TokenCursorFrame::new(Some((delim, sp)), tts.clone());
+                        self.stack.push(mem::replace(&mut self.frame, frame));
+                        if delim != Delimiter::Invisible {
+                            return (Token::new(token::OpenDelim(delim), sp.open), Spacing::Alone);
+                        }
+                        // No open delimeter to return; continue on to the next iteration.
+                    }
+                };
             } else if let Some(frame) = self.stack.pop() {
+                if let Some((delim, span)) = self.frame.delim_sp && delim != Delimiter::Invisible {
+                    self.frame = frame;
+                    return (Token::new(token::CloseDelim(delim), span.close), Spacing::Alone);
+                }
                 self.frame = frame;
-                continue;
+                // No close delimiter to return; continue on to the next iteration.
             } else {
-                (TokenTree::Token(Token::new(token::Eof, DUMMY_SP)), Spacing::Alone)
-            };
-
-            match tree {
-                TokenTree::Token(token) => {
-                    return (token, spacing);
-                }
-                TokenTree::Delimited(sp, delim, tts) => {
-                    let frame = TokenCursorFrame::new(sp, delim, tts);
-                    self.stack.push(mem::replace(&mut self.frame, frame));
-                }
+                return (Token::new(token::Eof, DUMMY_SP), Spacing::Alone);
             }
         }
     }
 
-    fn next_desugared(&mut self) -> (Token, Spacing) {
-        self.inlined_next_desugared()
-    }
-
-    /// This always-inlined version should only be used on hot code paths.
-    #[inline(always)]
-    fn inlined_next_desugared(&mut self) -> (Token, Spacing) {
-        let (data, attr_style, sp) = match self.inlined_next() {
-            (Token { kind: token::DocComment(_, attr_style, data), span }, _) => {
-                (data, attr_style, span)
-            }
-            tok => return tok,
-        };
-
+    fn desugar(&mut self, attr_style: AttrStyle, data: Symbol, span: Span) -> (Token, Spacing) {
         // Searches for the occurrences of `"#*` and returns the minimum number of `#`s
         // required to wrap the text.
         let mut num_of_hashes = 0;
@@ -318,14 +311,14 @@ impl TokenCursor {
             num_of_hashes = cmp::max(num_of_hashes, count);
         }
 
-        let delim_span = DelimSpan::from_single(sp);
+        let delim_span = DelimSpan::from_single(span);
         let body = TokenTree::Delimited(
             delim_span,
-            token::Bracket,
+            Delimiter::Bracket,
             [
-                TokenTree::token(token::Ident(sym::doc, false), sp),
-                TokenTree::token(token::Eq, sp),
-                TokenTree::token(TokenKind::lit(token::StrRaw(num_of_hashes), data, None), sp),
+                TokenTree::token(token::Ident(sym::doc, false), span),
+                TokenTree::token(token::Eq, span),
+                TokenTree::token(TokenKind::lit(token::StrRaw(num_of_hashes), data, None), span),
             ]
             .iter()
             .cloned()
@@ -335,15 +328,14 @@ impl TokenCursor {
         self.stack.push(mem::replace(
             &mut self.frame,
             TokenCursorFrame::new(
-                delim_span,
-                token::NoDelim,
+                None,
                 if attr_style == AttrStyle::Inner {
-                    [TokenTree::token(token::Pound, sp), TokenTree::token(token::Not, sp), body]
+                    [TokenTree::token(token::Pound, span), TokenTree::token(token::Not, span), body]
                         .iter()
                         .cloned()
                         .collect::<TokenStream>()
                 } else {
-                    [TokenTree::token(token::Pound, sp), body]
+                    [TokenTree::token(token::Pound, span), body]
                         .iter()
                         .cloned()
                         .collect::<TokenStream>()
@@ -351,7 +343,7 @@ impl TokenCursor {
             ),
         ));
 
-        self.next()
+        self.next(/* desugar_doc_comments */ false)
     }
 }
 
@@ -436,10 +428,6 @@ impl<'a> Parser<'a> {
         desugar_doc_comments: bool,
         subparser_name: Option<&'static str>,
     ) -> Self {
-        let mut start_frame = TokenCursorFrame::new(DelimSpan::dummy(), token::NoDelim, tokens);
-        start_frame.open_delim = true;
-        start_frame.close_delim = true;
-
         let mut parser = Parser {
             sess,
             token: Token::dummy(),
@@ -449,7 +437,7 @@ impl<'a> Parser<'a> {
             restrictions: Restrictions::empty(),
             expected_tokens: Vec::new(),
             token_cursor: TokenCursor {
-                frame: start_frame,
+                frame: TokenCursorFrame::new(None, tokens),
                 stack: Vec::new(),
                 num_next_calls: 0,
                 desugar_doc_comments,
@@ -476,33 +464,6 @@ impl<'a> Parser<'a> {
         parser
     }
 
-    #[inline]
-    fn next_tok(&mut self, fallback_span: Span) -> (Token, Spacing) {
-        loop {
-            let (mut next, spacing) = if self.desugar_doc_comments {
-                self.token_cursor.inlined_next_desugared()
-            } else {
-                self.token_cursor.inlined_next()
-            };
-            self.token_cursor.num_next_calls += 1;
-            // We've retrieved an token from the underlying
-            // cursor, so we no longer need to worry about
-            // an unglued token. See `break_and_eat` for more details
-            self.token_cursor.break_last_token = false;
-            if next.span.is_dummy() {
-                // Tweak the location for better diagnostics, but keep syntactic context intact.
-                next.span = fallback_span.with_ctxt(next.span.ctxt());
-            }
-            if matches!(
-                next.kind,
-                token::OpenDelim(token::NoDelim) | token::CloseDelim(token::NoDelim)
-            ) {
-                continue;
-            }
-            return (next, spacing);
-        }
-    }
-
     pub fn unexpected<T>(&mut self) -> PResult<'a, T> {
         match self.expect_one_of(&[], &[]) {
             Err(e) => Err(e),
@@ -665,7 +626,7 @@ impl<'a> Parser<'a> {
         self.is_keyword_ahead(dist, &[kw::Const])
             && self.look_ahead(dist + 1, |t| match t.kind {
                 token::Interpolated(ref nt) => matches!(**nt, token::NtBlock(..)),
-                token::OpenDelim(DelimToken::Brace) => true,
+                token::OpenDelim(Delimiter::Brace) => true,
                 _ => false,
             })
     }
@@ -697,7 +658,7 @@ impl<'a> Parser<'a> {
                 //
                 // If we consume any additional tokens, then this token
                 // is not needed (we'll capture the entire 'glued' token),
-                // and `next_tok` will set this field to `None`
+                // and `bump` will set this field to `None`
                 self.token_cursor.break_last_token = true;
                 // Use the spacing of the glued token as the spacing
                 // of the unglued second token.
@@ -841,7 +802,7 @@ impl<'a> Parser<'a> {
                                     .span_suggestion_verbose(
                                         self.prev_token.span.shrink_to_hi().until(self.token.span),
                                         &msg,
-                                        " @ ".to_string(),
+                                        " @ ",
                                         Applicability::MaybeIncorrect,
                                     )
                                     .emit();
@@ -857,7 +818,7 @@ impl<'a> Parser<'a> {
                                         .span_suggestion_short(
                                             sp,
                                             &format!("missing `{}`", token_str),
-                                            token_str.into(),
+                                            token_str,
                                             Applicability::MaybeIncorrect,
                                         )
                                         .emit();
@@ -993,7 +954,7 @@ impl<'a> Parser<'a> {
 
     fn parse_delim_comma_seq<T>(
         &mut self,
-        delim: DelimToken,
+        delim: Delimiter,
         f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>,
     ) -> PResult<'a, (Vec<T>, bool)> {
         self.parse_unspanned_seq(
@@ -1008,7 +969,7 @@ impl<'a> Parser<'a> {
         &mut self,
         f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>,
     ) -> PResult<'a, (Vec<T>, bool)> {
-        self.parse_delim_comma_seq(token::Paren, f)
+        self.parse_delim_comma_seq(Delimiter::Parenthesis, f)
     }
 
     /// Advance the parser by one token using provided token as the next one.
@@ -1019,12 +980,6 @@ impl<'a> Parser<'a> {
     /// This always-inlined version should only be used on hot code paths.
     #[inline(always)]
     fn inlined_bump_with(&mut self, (next_token, next_spacing): (Token, Spacing)) {
-        // Bumping after EOF is a bad sign, usually an infinite loop.
-        if self.prev_token.kind == TokenKind::Eof {
-            let msg = "attempted to bump the parser past EOF (may be stuck in a loop)";
-            self.span_bug(self.token.span, msg);
-        }
-
         // Update the current and previous tokens.
         self.prev_token = mem::replace(&mut self.token, next_token);
         self.token_spacing = next_spacing;
@@ -1035,8 +990,24 @@ impl<'a> Parser<'a> {
 
     /// Advance the parser by one token.
     pub fn bump(&mut self) {
-        let next_token = self.next_tok(self.token.span);
-        self.inlined_bump_with(next_token);
+        // Note: destructuring here would give nicer code, but it was found in #96210 to be slower
+        // than `.0`/`.1` access.
+        let mut next = self.token_cursor.inlined_next(self.desugar_doc_comments);
+        self.token_cursor.num_next_calls += 1;
+        // We've retrieved an token from the underlying
+        // cursor, so we no longer need to worry about
+        // an unglued token. See `break_and_eat` for more details
+        self.token_cursor.break_last_token = false;
+        if next.0.span.is_dummy() {
+            // Tweak the location for better diagnostics, but keep syntactic context intact.
+            let fallback_span = self.token.span;
+            next.0.span = fallback_span.with_ctxt(next.0.span.ctxt());
+        }
+        debug_assert!(!matches!(
+            next.0.kind,
+            token::OpenDelim(Delimiter::Invisible) | token::CloseDelim(Delimiter::Invisible)
+        ));
+        self.inlined_bump_with(next)
     }
 
     /// Look-ahead `dist` tokens of `self.token` and get access to that token there.
@@ -1047,10 +1018,10 @@ impl<'a> Parser<'a> {
         }
 
         let frame = &self.token_cursor.frame;
-        if frame.delim != DelimToken::NoDelim {
+        if let Some((delim, span)) = frame.delim_sp && delim != Delimiter::Invisible {
             let all_normal = (0..dist).all(|i| {
                 let token = frame.tree_cursor.look_ahead(i);
-                !matches!(token, Some(TokenTree::Delimited(_, DelimToken::NoDelim, _)))
+                !matches!(token, Some(TokenTree::Delimited(_, Delimiter::Invisible, _)))
             });
             if all_normal {
                 return match frame.tree_cursor.look_ahead(dist - 1) {
@@ -1060,7 +1031,7 @@ impl<'a> Parser<'a> {
                             looker(&Token::new(token::OpenDelim(*delim), dspan.open))
                         }
                     },
-                    None => looker(&Token::new(token::CloseDelim(frame.delim), frame.span.close)),
+                    None => looker(&Token::new(token::CloseDelim(delim), span.close)),
                 };
             }
         }
@@ -1069,10 +1040,10 @@ impl<'a> Parser<'a> {
         let mut i = 0;
         let mut token = Token::dummy();
         while i < dist {
-            token = cursor.next().0;
+            token = cursor.next(/* desugar_doc_comments */ false).0;
             if matches!(
                 token.kind,
-                token::OpenDelim(token::NoDelim) | token::CloseDelim(token::NoDelim)
+                token::OpenDelim(Delimiter::Invisible) | token::CloseDelim(Delimiter::Invisible)
             ) {
                 continue;
             }
@@ -1108,7 +1079,7 @@ impl<'a> Parser<'a> {
     /// Parses constness: `const` or nothing.
     fn parse_constness(&mut self) -> Const {
         // Avoid const blocks to be parsed as const items
-        if self.look_ahead(1, |t| t != &token::OpenDelim(DelimToken::Brace))
+        if self.look_ahead(1, |t| t != &token::OpenDelim(Delimiter::Brace))
             && self.eat_keyword(kw::Const)
         {
             Const::Yes(self.prev_token.uninterpolated_span())
@@ -1171,9 +1142,9 @@ impl<'a> Parser<'a> {
 
     fn parse_mac_args_common(&mut self, delimited_only: bool) -> PResult<'a, MacArgs> {
         Ok(
-            if self.check(&token::OpenDelim(DelimToken::Paren))
-                || self.check(&token::OpenDelim(DelimToken::Bracket))
-                || self.check(&token::OpenDelim(DelimToken::Brace))
+            if self.check(&token::OpenDelim(Delimiter::Parenthesis))
+                || self.check(&token::OpenDelim(Delimiter::Bracket))
+                || self.check(&token::OpenDelim(Delimiter::Brace))
             {
                 match self.parse_token_tree() {
                     TokenTree::Delimited(dspan, delim, tokens) =>
@@ -1217,24 +1188,27 @@ impl<'a> Parser<'a> {
     pub(crate) fn parse_token_tree(&mut self) -> TokenTree {
         match self.token.kind {
             token::OpenDelim(..) => {
-                let depth = self.token_cursor.stack.len();
-
-                // We keep advancing the token cursor until we hit
-                // the matching `CloseDelim` token.
-                while !(depth == self.token_cursor.stack.len()
-                    && matches!(self.token.kind, token::CloseDelim(_)))
-                {
+                // Grab the tokens from this frame.
+                let frame = &self.token_cursor.frame;
+                let stream = frame.tree_cursor.stream.clone();
+                let (delim, span) = frame.delim_sp.unwrap();
+
+                // Advance the token cursor through the entire delimited
+                // sequence. After getting the `OpenDelim` we are *within* the
+                // delimited sequence, i.e. at depth `d`. After getting the
+                // matching `CloseDelim` we are *after* the delimited sequence,
+                // i.e. at depth `d - 1`.
+                let target_depth = self.token_cursor.stack.len() - 1;
+                loop {
                     // Advance one token at a time, so `TokenCursor::next()`
                     // can capture these tokens if necessary.
                     self.bump();
+                    if self.token_cursor.stack.len() == target_depth {
+                        debug_assert!(matches!(self.token.kind, token::CloseDelim(_)));
+                        break;
+                    }
                 }
-                // We are still inside the frame corresponding
-                // to the delimited stream we captured, so grab
-                // the tokens from this frame.
-                let frame = &self.token_cursor.frame;
-                let stream = frame.tree_cursor.stream.clone();
-                let span = frame.span;
-                let delim = frame.delim;
+
                 // Consume close delimiter
                 self.bump();
                 TokenTree::Delimited(span, delim, stream)
@@ -1314,7 +1288,7 @@ impl<'a> Parser<'a> {
         }
         let lo = self.prev_token.span;
 
-        if self.check(&token::OpenDelim(token::Paren)) {
+        if self.check(&token::OpenDelim(Delimiter::Parenthesis)) {
             // We don't `self.bump()` the `(` yet because this might be a struct definition where
             // `()` or a tuple might be allowed. For example, `struct Struct(pub (), pub (usize));`.
             // Because of this, we only `bump` the `(` if we're assured it is appropriate to do so
@@ -1325,7 +1299,7 @@ impl<'a> Parser<'a> {
                 // Parse `pub(crate)`.
                 self.bump(); // `(`
                 self.bump(); // `crate`
-                self.expect(&token::CloseDelim(token::Paren))?; // `)`
+                self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; // `)`
                 let vis = VisibilityKind::Crate(CrateSugar::PubCrate);
                 return Ok(Visibility {
                     span: lo.to(self.prev_token.span),
@@ -1337,20 +1311,20 @@ impl<'a> Parser<'a> {
                 self.bump(); // `(`
                 self.bump(); // `in`
                 let path = self.parse_path(PathStyle::Mod)?; // `path`
-                self.expect(&token::CloseDelim(token::Paren))?; // `)`
+                self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; // `)`
                 let vis = VisibilityKind::Restricted { path: P(path), id: ast::DUMMY_NODE_ID };
                 return Ok(Visibility {
                     span: lo.to(self.prev_token.span),
                     kind: vis,
                     tokens: None,
                 });
-            } else if self.look_ahead(2, |t| t == &token::CloseDelim(token::Paren))
+            } else if self.look_ahead(2, |t| t == &token::CloseDelim(Delimiter::Parenthesis))
                 && self.is_keyword_ahead(1, &[kw::Super, kw::SelfLower])
             {
                 // Parse `pub(self)` or `pub(super)`.
                 self.bump(); // `(`
                 let path = self.parse_path(PathStyle::Mod)?; // `super`/`self`
-                self.expect(&token::CloseDelim(token::Paren))?; // `)`
+                self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; // `)`
                 let vis = VisibilityKind::Restricted { path: P(path), id: ast::DUMMY_NODE_ID };
                 return Ok(Visibility {
                     span: lo.to(self.prev_token.span),
@@ -1372,7 +1346,7 @@ impl<'a> Parser<'a> {
     fn recover_incorrect_vis_restriction(&mut self) -> PResult<'a, ()> {
         self.bump(); // `(`
         let path = self.parse_path(PathStyle::Mod)?;
-        self.expect(&token::CloseDelim(token::Paren))?; // `)`
+        self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; // `)`
 
         let msg = "incorrect visibility restriction";
         let suggestion = r##"some possible visibility restrictions are:
@@ -1439,7 +1413,7 @@ impl<'a> Parser<'a> {
     fn is_import_coupler(&mut self) -> bool {
         self.check(&token::ModSep)
             && self.look_ahead(1, |t| {
-                *t == token::OpenDelim(token::Brace) || *t == token::BinOp(token::Star)
+                *t == token::OpenDelim(Delimiter::Brace) || *t == token::BinOp(token::Star)
             })
     }
 
diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs
index b45bca3d2e0..6974f318f94 100644
--- a/compiler/rustc_parse/src/parser/nonterminal.rs
+++ b/compiler/rustc_parse/src/parser/nonterminal.rs
@@ -1,5 +1,5 @@
 use rustc_ast::ptr::P;
-use rustc_ast::token::{self, NonterminalKind, Token};
+use rustc_ast::token::{self, Delimiter, NonterminalKind, Token};
 use rustc_ast::AstLike;
 use rustc_ast_pretty::pprust;
 use rustc_errors::PResult;
@@ -11,8 +11,10 @@ use crate::parser::{FollowedByType, ForceCollect, NtOrTt, Parser, PathStyle};
 impl<'a> Parser<'a> {
     /// Checks whether a non-terminal may begin with a particular token.
     ///
-    /// Returning `false` is a *stability guarantee* that such a matcher will *never* begin with that
-    /// token. Be conservative (return true) if not sure.
+    /// Returning `false` is a *stability guarantee* that such a matcher will *never* begin with
+    /// that token. Be conservative (return true) if not sure. Inlined because it has a single call
+    /// site.
+    #[inline]
     pub fn nonterminal_may_begin_with(kind: NonterminalKind, token: &Token) -> bool {
         /// Checks whether the non-terminal may contain a single (non-keyword) identifier.
         fn may_be_ident(nt: &token::Nonterminal) -> bool {
@@ -41,7 +43,7 @@ impl<'a> Parser<'a> {
                 _ => token.can_begin_type(),
             },
             NonterminalKind::Block => match token.kind {
-                token::OpenDelim(token::Brace) => true,
+                token::OpenDelim(Delimiter::Brace) => true,
                 token::Interpolated(ref nt) => !matches!(
                     **nt,
                     token::NtItem(_)
@@ -65,8 +67,8 @@ impl<'a> Parser<'a> {
             NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr { .. } => {
                 match token.kind {
                 token::Ident(..) |                  // box, ref, mut, and other identifiers (can stricten)
-                token::OpenDelim(token::Paren) |    // tuple pattern
-                token::OpenDelim(token::Bracket) |  // slice pattern
+                token::OpenDelim(Delimiter::Parenthesis) |    // tuple pattern
+                token::OpenDelim(Delimiter::Bracket) |  // slice pattern
                 token::BinOp(token::And) |          // reference
                 token::BinOp(token::Minus) |        // negative literal
                 token::AndAnd |                     // double reference
@@ -95,7 +97,9 @@ impl<'a> Parser<'a> {
         }
     }
 
-    /// Parse a non-terminal (e.g. MBE `:pat` or `:ident`).
+    /// Parse a non-terminal (e.g. MBE `:pat` or `:ident`). Inlined because there is only one call
+    /// site.
+    #[inline]
     pub fn parse_nonterminal(&mut self, kind: NonterminalKind) -> PResult<'a, NtOrTt> {
         // Any `Nonterminal` which stores its tokens (currently `NtItem` and `NtExpr`)
         // needs to have them force-captured here.
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index 67bbbf24936..8019c5fb67c 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -2,7 +2,7 @@ use super::{ForceCollect, Parser, PathStyle, TrailingToken};
 use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
 use rustc_ast::mut_visit::{noop_visit_pat, MutVisitor};
 use rustc_ast::ptr::P;
-use rustc_ast::token;
+use rustc_ast::token::{self, Delimiter};
 use rustc_ast::{
     self as ast, AttrVec, Attribute, BindingMode, Expr, ExprKind, MacCall, Mutability, Pat,
     PatField, PatKind, Path, QSelf, RangeEnd, RangeSyntax,
@@ -260,9 +260,9 @@ impl<'a> Parser<'a> {
                 | token::Semi // e.g. `let a |;`.
                 | token::Colon // e.g. `let a | :`.
                 | token::Comma // e.g. `let (a |,)`.
-                | token::CloseDelim(token::Bracket) // e.g. `let [a | ]`.
-                | token::CloseDelim(token::Paren) // e.g. `let (a | )`.
-                | token::CloseDelim(token::Brace) // e.g. `let A { f: a | }`.
+                | token::CloseDelim(Delimiter::Bracket) // e.g. `let [a | ]`.
+                | token::CloseDelim(Delimiter::Parenthesis) // e.g. `let (a | )`.
+                | token::CloseDelim(Delimiter::Brace) // e.g. `let A { f: a | }`.
             )
         });
         match (is_end_ahead, &self.token.kind) {
@@ -323,11 +323,11 @@ impl<'a> Parser<'a> {
 
         let pat = if self.check(&token::BinOp(token::And)) || self.token.kind == token::AndAnd {
             self.parse_pat_deref(expected)?
-        } else if self.check(&token::OpenDelim(token::Paren)) {
+        } else if self.check(&token::OpenDelim(Delimiter::Parenthesis)) {
             self.parse_pat_tuple_or_parens()?
-        } else if self.check(&token::OpenDelim(token::Bracket)) {
+        } else if self.check(&token::OpenDelim(Delimiter::Bracket)) {
             // Parse `[pat, pat,...]` as a slice pattern.
-            let (pats, _) = self.parse_delim_comma_seq(token::Bracket, |p| {
+            let (pats, _) = self.parse_delim_comma_seq(Delimiter::Bracket, |p| {
                 p.parse_pat_allow_top_alt(
                     None,
                     RecoverComma::No,
@@ -389,9 +389,9 @@ impl<'a> Parser<'a> {
             } else if let Some(form) = self.parse_range_end() {
                 let begin = self.mk_expr(span, ExprKind::Path(qself, path), AttrVec::new());
                 self.parse_pat_range_begin_with(begin, form)?
-            } else if self.check(&token::OpenDelim(token::Brace)) {
+            } else if self.check(&token::OpenDelim(Delimiter::Brace)) {
                 self.parse_pat_struct(qself, path)?
-            } else if self.check(&token::OpenDelim(token::Paren)) {
+            } else if self.check(&token::OpenDelim(Delimiter::Parenthesis)) {
                 self.parse_pat_tuple_struct(qself, path)?
             } else {
                 PatKind::Path(qself, path)
@@ -606,7 +606,7 @@ impl<'a> Parser<'a> {
             .span_suggestion(
                 mutref_span,
                 "try switching the order",
-                "ref mut".into(),
+                "ref mut",
                 Applicability::MachineApplicable,
             )
             .emit();
@@ -845,8 +845,8 @@ impl<'a> Parser<'a> {
         // Avoid `in`. Due to recovery in the list parser this messes with `for ( $pat in $expr )`.
         && !self.token.is_keyword(kw::In)
         // Try to do something more complex?
-        && self.look_ahead(1, |t| !matches!(t.kind, token::OpenDelim(token::Paren) // A tuple struct pattern.
-            | token::OpenDelim(token::Brace) // A struct pattern.
+        && self.look_ahead(1, |t| !matches!(t.kind, token::OpenDelim(Delimiter::Parenthesis) // A tuple struct pattern.
+            | token::OpenDelim(Delimiter::Brace) // A struct pattern.
             | token::DotDotDot | token::DotDotEq | token::DotDot // A range pattern.
             | token::ModSep // A tuple / struct variant pattern.
             | token::Not)) // A macro expanding to a pattern.
@@ -868,7 +868,7 @@ impl<'a> Parser<'a> {
         // This shortly leads to a parse error. Note that if there is no explicit
         // binding mode then we do not end up here, because the lookahead
         // will direct us over to `parse_enum_variant()`.
-        if self.token == token::OpenDelim(token::Paren) {
+        if self.token == token::OpenDelim(Delimiter::Parenthesis) {
             return Err(self
                 .struct_span_err(self.prev_token.span, "expected identifier, found enum pattern"));
         }
@@ -917,7 +917,7 @@ impl<'a> Parser<'a> {
         let mut delayed_err: Option<DiagnosticBuilder<'a, ErrorGuaranteed>> = None;
         let mut etc_span = None;
 
-        while self.token != token::CloseDelim(token::Brace) {
+        while self.token != token::CloseDelim(Delimiter::Brace) {
             let attrs = match self.parse_outer_attributes() {
                 Ok(attrs) => attrs,
                 Err(err) => {
@@ -946,7 +946,7 @@ impl<'a> Parser<'a> {
                 self.recover_one_fewer_dotdot();
                 self.bump(); // `..` || `...`
 
-                if self.token == token::CloseDelim(token::Brace) {
+                if self.token == token::CloseDelim(Delimiter::Brace) {
                     etc_span = Some(etc_sp);
                     break;
                 }
@@ -970,7 +970,7 @@ impl<'a> Parser<'a> {
                 }
 
                 etc_span = Some(etc_sp.until(self.token.span));
-                if self.token == token::CloseDelim(token::Brace) {
+                if self.token == token::CloseDelim(Delimiter::Brace) {
                     // If the struct looks otherwise well formed, recover and continue.
                     if let Some(sp) = comma_sp {
                         err.span_suggestion_short(
diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs
index b9e3adaac03..5c6fb376cd4 100644
--- a/compiler/rustc_parse/src/parser/path.rs
+++ b/compiler/rustc_parse/src/parser/path.rs
@@ -2,7 +2,7 @@ use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
 use super::{Parser, Restrictions, TokenType};
 use crate::maybe_whole;
 use rustc_ast::ptr::P;
-use rustc_ast::token::{self, Token};
+use rustc_ast::token::{self, Delimiter, Token};
 use rustc_ast::{
     self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AssocConstraint,
     AssocConstraintKind, BlockCheckMode, GenericArg, GenericArgs, Generics, ParenthesizedArgs,
@@ -236,14 +236,14 @@ impl<'a> Parser<'a> {
                 token.kind,
                 token::Lt
                     | token::BinOp(token::Shl)
-                    | token::OpenDelim(token::Paren)
+                    | token::OpenDelim(Delimiter::Parenthesis)
                     | token::LArrow
             )
         };
         let check_args_start = |this: &mut Self| {
             this.expected_tokens.extend_from_slice(&[
                 TokenType::Token(token::Lt),
-                TokenType::Token(token::OpenDelim(token::Paren)),
+                TokenType::Token(token::OpenDelim(Delimiter::Parenthesis)),
             ]);
             is_args_start(&this.token)
         };
@@ -639,7 +639,7 @@ impl<'a> Parser<'a> {
     /// the caller.
     pub(super) fn parse_const_arg(&mut self) -> PResult<'a, AnonConst> {
         // Parse const argument.
-        let value = if let token::OpenDelim(token::Brace) = self.token.kind {
+        let value = if let token::OpenDelim(Delimiter::Brace) = self.token.kind {
             self.parse_block_expr(
                 None,
                 self.token.span,
@@ -667,7 +667,8 @@ impl<'a> Parser<'a> {
             GenericArg::Const(self.parse_const_arg()?)
         } else if self.check_type() {
             // Parse type argument.
-            let is_const_fn = self.look_ahead(1, |t| t.kind == token::OpenDelim(token::Paren));
+            let is_const_fn =
+                self.look_ahead(1, |t| t.kind == token::OpenDelim(Delimiter::Parenthesis));
             let mut snapshot = self.create_snapshot_for_diagnostic();
             match self.parse_ty() {
                 Ok(ty) => GenericArg::Type(ty),
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index 5b7ae5f7a7b..ac693597662 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -11,7 +11,7 @@ use crate::maybe_whole;
 
 use rustc_ast as ast;
 use rustc_ast::ptr::P;
-use rustc_ast::token::{self, TokenKind};
+use rustc_ast::token::{self, Delimiter, TokenKind};
 use rustc_ast::util::classify;
 use rustc_ast::{
     AstLike, AttrStyle, AttrVec, Attribute, LocalKind, MacCall, MacCallStmt, MacStmtStyle,
@@ -92,7 +92,7 @@ impl<'a> Parser<'a> {
             // Do not attempt to parse an expression if we're done here.
             self.error_outer_attrs(&attrs.take_for_recovery());
             self.mk_stmt(lo, StmtKind::Empty)
-        } else if self.token != token::CloseDelim(token::Brace) {
+        } else if self.token != token::CloseDelim(Delimiter::Brace) {
             // Remainder are line-expr stmts.
             let e = if force_collect == ForceCollect::Yes {
                 self.collect_tokens_no_attrs(|this| {
@@ -131,7 +131,7 @@ impl<'a> Parser<'a> {
                 }
             }
 
-            let expr = if this.eat(&token::OpenDelim(token::Brace)) {
+            let expr = if this.eat(&token::OpenDelim(Delimiter::Brace)) {
                 this.parse_struct_expr(None, path, AttrVec::new(), true)?
             } else {
                 let hi = this.prev_token.span;
@@ -164,25 +164,29 @@ impl<'a> Parser<'a> {
         let delim = args.delim();
         let hi = self.prev_token.span;
 
-        let style =
-            if delim == token::Brace { MacStmtStyle::Braces } else { MacStmtStyle::NoBraces };
+        let style = match delim {
+            Some(Delimiter::Brace) => MacStmtStyle::Braces,
+            Some(_) => MacStmtStyle::NoBraces,
+            None => unreachable!(),
+        };
 
         let mac = MacCall { path, args, prior_type_ascription: self.last_type_ascription };
 
-        let kind =
-            if (delim == token::Brace && self.token != token::Dot && self.token != token::Question)
-                || self.token == token::Semi
-                || self.token == token::Eof
-            {
-                StmtKind::MacCall(P(MacCallStmt { mac, style, attrs, tokens: None }))
-            } else {
-                // Since none of the above applied, this is an expression statement macro.
-                let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac), AttrVec::new());
-                let e = self.maybe_recover_from_bad_qpath(e, true)?;
-                let e = self.parse_dot_or_call_expr_with(e, lo, attrs.into())?;
-                let e = self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))?;
-                StmtKind::Expr(e)
-            };
+        let kind = if (style == MacStmtStyle::Braces
+            && self.token != token::Dot
+            && self.token != token::Question)
+            || self.token == token::Semi
+            || self.token == token::Eof
+        {
+            StmtKind::MacCall(P(MacCallStmt { mac, style, attrs, tokens: None }))
+        } else {
+            // Since none of the above applied, this is an expression statement macro.
+            let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac), AttrVec::new());
+            let e = self.maybe_recover_from_bad_qpath(e, true)?;
+            let e = self.parse_dot_or_call_expr_with(e, lo, attrs.into())?;
+            let e = self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))?;
+            StmtKind::Expr(e)
+        };
         Ok(self.mk_stmt(lo.to(hi), kind))
     }
 
@@ -430,7 +434,7 @@ impl<'a> Parser<'a> {
             // If the next token is an open brace (e.g., `if a b {`), the place-
             // inside-a-block suggestion would be more likely wrong than right.
             Ok(Some(_))
-                if self.look_ahead(1, |t| t == &token::OpenDelim(token::Brace))
+                if self.look_ahead(1, |t| t == &token::OpenDelim(Delimiter::Brace))
                     || do_not_suggest_help => {}
             // Do not suggest `if foo println!("") {;}` (as would be seen in test for #46836).
             Ok(Some(Stmt { kind: StmtKind::Empty, .. })) => {}
@@ -484,7 +488,7 @@ impl<'a> Parser<'a> {
         maybe_whole!(self, NtBlock, |x| (Vec::new(), x));
 
         self.maybe_recover_unexpected_block_label();
-        if !self.eat(&token::OpenDelim(token::Brace)) {
+        if !self.eat(&token::OpenDelim(Delimiter::Brace)) {
             return self.error_block_no_opening_brace();
         }
 
@@ -505,7 +509,7 @@ impl<'a> Parser<'a> {
         recover: AttemptLocalParseRecovery,
     ) -> PResult<'a, P<Block>> {
         let mut stmts = vec![];
-        while !self.eat(&token::CloseDelim(token::Brace)) {
+        while !self.eat(&token::CloseDelim(Delimiter::Brace)) {
             if self.token == token::Eof {
                 break;
             }
@@ -549,7 +553,7 @@ impl<'a> Parser<'a> {
             {
                 // Just check for errors and recover; do not eat semicolon yet.
                 if let Err(mut e) =
-                    self.expect_one_of(&[], &[token::Semi, token::CloseDelim(token::Brace)])
+                    self.expect_one_of(&[], &[token::Semi, token::CloseDelim(Delimiter::Brace)])
                 {
                     if let TokenKind::DocComment(..) = self.token.kind {
                         if let Ok(snippet) = self.span_to_snippet(self.token.span) {
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index bb387064e27..9e771a8af1a 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -3,7 +3,7 @@ use super::{Parser, PathStyle, TokenType};
 use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
 
 use rustc_ast::ptr::P;
-use rustc_ast::token::{self, Token, TokenKind};
+use rustc_ast::token::{self, Delimiter, Token, TokenKind};
 use rustc_ast::{
     self as ast, BareFnTy, FnRetTy, GenericBound, GenericBounds, GenericParam, Generics, Lifetime,
     MacCall, MutTy, Mutability, PolyTraitRef, TraitBoundModifier, TraitObjectSyntax, Ty, TyKind,
@@ -249,14 +249,14 @@ impl<'a> Parser<'a> {
 
         let lo = self.token.span;
         let mut impl_dyn_multi = false;
-        let kind = if self.check(&token::OpenDelim(token::Paren)) {
+        let kind = if self.check(&token::OpenDelim(Delimiter::Parenthesis)) {
             self.parse_ty_tuple_or_parens(lo, allow_plus)?
         } else if self.eat(&token::Not) {
             // Never type `!`
             TyKind::Never
         } else if self.eat(&token::BinOp(token::Star)) {
             self.parse_ty_ptr()?
-        } else if self.eat(&token::OpenDelim(token::Bracket)) {
+        } else if self.eat(&token::OpenDelim(Delimiter::Bracket)) {
             self.parse_array_or_slice_ty()?
         } else if self.check(&token::BinOp(token::And)) || self.check(&token::AndAnd) {
             // Reference
@@ -409,7 +409,7 @@ impl<'a> Parser<'a> {
         let elt_ty = match self.parse_ty() {
             Ok(ty) => ty,
             Err(mut err)
-                if self.look_ahead(1, |t| t.kind == token::CloseDelim(token::Bracket))
+                if self.look_ahead(1, |t| t.kind == token::CloseDelim(Delimiter::Bracket))
                     | self.look_ahead(1, |t| t.kind == token::Semi) =>
             {
                 // Recover from `[LIT; EXPR]` and `[LIT]`
@@ -422,14 +422,14 @@ impl<'a> Parser<'a> {
 
         let ty = if self.eat(&token::Semi) {
             let mut length = self.parse_anon_const_expr()?;
-            if let Err(e) = self.expect(&token::CloseDelim(token::Bracket)) {
+            if let Err(e) = self.expect(&token::CloseDelim(Delimiter::Bracket)) {
                 // Try to recover from `X<Y, ...>` when `X::<Y, ...>` works
                 self.check_mistyped_turbofish_with_multiple_type_params(e, &mut length.value)?;
-                self.expect(&token::CloseDelim(token::Bracket))?;
+                self.expect(&token::CloseDelim(Delimiter::Bracket))?;
             }
             TyKind::Array(elt_ty, length)
         } else {
-            self.expect(&token::CloseDelim(token::Bracket))?;
+            self.expect(&token::CloseDelim(Delimiter::Bracket))?;
             TyKind::Slice(elt_ty)
         };
 
@@ -492,9 +492,9 @@ impl<'a> Parser<'a> {
     // Parses the `typeof(EXPR)`.
     // To avoid ambiguity, the type is surrounded by parentheses.
     fn parse_typeof_ty(&mut self) -> PResult<'a, TyKind> {
-        self.expect(&token::OpenDelim(token::Paren))?;
+        self.expect(&token::OpenDelim(Delimiter::Parenthesis))?;
         let expr = self.parse_anon_const_expr()?;
-        self.expect(&token::CloseDelim(token::Paren))?;
+        self.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
         Ok(TyKind::Typeof(expr))
     }
 
@@ -672,7 +672,7 @@ impl<'a> Parser<'a> {
         || self.check(&token::Question)
         || self.check(&token::Tilde)
         || self.check_keyword(kw::For)
-        || self.check(&token::OpenDelim(token::Paren))
+        || self.check(&token::OpenDelim(Delimiter::Parenthesis))
     }
 
     fn error_negative_bounds(
@@ -713,7 +713,7 @@ impl<'a> Parser<'a> {
     fn parse_generic_bound(&mut self) -> PResult<'a, Result<GenericBound, Span>> {
         let anchor_lo = self.prev_token.span;
         let lo = self.token.span;
-        let has_parens = self.eat(&token::OpenDelim(token::Paren));
+        let has_parens = self.eat(&token::OpenDelim(Delimiter::Parenthesis));
         let inner_lo = self.token.span;
         let is_negative = self.eat(&token::Not);
 
@@ -766,7 +766,7 @@ impl<'a> Parser<'a> {
     /// Recover on `('lifetime)` with `(` already eaten.
     fn recover_paren_lifetime(&mut self, lo: Span, inner_lo: Span) -> PResult<'a, ()> {
         let inner_span = inner_lo.to(self.prev_token.span);
-        self.expect(&token::CloseDelim(token::Paren))?;
+        self.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
         let mut err = self.struct_span_err(
             lo.to(self.prev_token.span),
             "parenthesized lifetime bounds are not supported",
@@ -829,7 +829,7 @@ impl<'a> Parser<'a> {
                 // suggestion is given.
                 let bounds = vec![];
                 self.parse_remaining_bounds(bounds, true)?;
-                self.expect(&token::CloseDelim(token::Paren))?;
+                self.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
                 let sp = vec![lo, self.prev_token.span];
                 let sugg: Vec<_> = sp.iter().map(|sp| (*sp, String::new())).collect();
                 self.struct_span_err(sp, "incorrect braces around trait bounds")
@@ -840,7 +840,7 @@ impl<'a> Parser<'a> {
                     )
                     .emit();
             } else {
-                self.expect(&token::CloseDelim(token::Paren))?;
+                self.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
             }
         }
 
diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs
index 261ea0b4deb..3cd18390285 100644
--- a/compiler/rustc_passes/src/dead.rs
+++ b/compiler/rustc_passes/src/dead.rs
@@ -45,8 +45,6 @@ struct MarkSymbolVisitor<'tcx> {
     live_symbols: FxHashSet<LocalDefId>,
     repr_has_repr_c: bool,
     in_pat: bool,
-    inherited_pub_visibility: bool,
-    pub_visibility: bool,
     ignore_variant_stack: Vec<DefId>,
     // maps from tuple struct constructors to tuple struct items
     struct_constructors: FxHashMap<LocalDefId, LocalDefId>,
@@ -284,33 +282,23 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
         }
 
         let had_repr_c = self.repr_has_repr_c;
-        let had_inherited_pub_visibility = self.inherited_pub_visibility;
-        let had_pub_visibility = self.pub_visibility;
         self.repr_has_repr_c = false;
-        self.inherited_pub_visibility = false;
-        self.pub_visibility = false;
         match node {
-            Node::Item(item) => {
-                self.pub_visibility = item.vis.node.is_pub();
+            Node::Item(item) => match item.kind {
+                hir::ItemKind::Struct(..) | hir::ItemKind::Union(..) => {
+                    let def = self.tcx.adt_def(item.def_id);
+                    self.repr_has_repr_c = def.repr().c();
 
-                match item.kind {
-                    hir::ItemKind::Struct(..) | hir::ItemKind::Union(..) => {
-                        let def = self.tcx.adt_def(item.def_id);
-                        self.repr_has_repr_c = def.repr().c();
-
-                        intravisit::walk_item(self, &item);
-                    }
-                    hir::ItemKind::Enum(..) => {
-                        self.inherited_pub_visibility = self.pub_visibility;
-
-                        intravisit::walk_item(self, &item);
-                    }
-                    hir::ItemKind::ForeignMod { .. } => {}
-                    _ => {
-                        intravisit::walk_item(self, &item);
-                    }
+                    intravisit::walk_item(self, &item);
                 }
-            }
+                hir::ItemKind::Enum(..) => {
+                    intravisit::walk_item(self, &item);
+                }
+                hir::ItemKind::ForeignMod { .. } => {}
+                _ => {
+                    intravisit::walk_item(self, &item);
+                }
+            },
             Node::TraitItem(trait_item) => {
                 intravisit::walk_trait_item(self, trait_item);
             }
@@ -322,8 +310,6 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
             }
             _ => {}
         }
-        self.pub_visibility = had_pub_visibility;
-        self.inherited_pub_visibility = had_inherited_pub_visibility;
         self.repr_has_repr_c = had_repr_c;
     }
 
@@ -354,14 +340,19 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
         _: hir::HirId,
         _: rustc_span::Span,
     ) {
+        let tcx = self.tcx;
         let has_repr_c = self.repr_has_repr_c;
-        let inherited_pub_visibility = self.inherited_pub_visibility;
-        let pub_visibility = self.pub_visibility;
-        let live_fields = def.fields().iter().filter(|f| {
-            has_repr_c || (pub_visibility && (inherited_pub_visibility || f.vis.node.is_pub()))
+        let live_fields = def.fields().iter().filter_map(|f| {
+            let def_id = tcx.hir().local_def_id(f.hir_id);
+            if has_repr_c {
+                return Some(def_id);
+            }
+            if !tcx.visibility(f.hir_id.owner).is_public() {
+                return None;
+            }
+            if tcx.visibility(def_id).is_public() { Some(def_id) } else { None }
         });
-        let hir = self.tcx.hir();
-        self.live_symbols.extend(live_fields.map(|f| hir.local_def_id(f.hir_id)));
+        self.live_symbols.extend(live_fields);
 
         intravisit::walk_struct_def(self, def);
     }
@@ -521,7 +512,7 @@ impl<'v, 'tcx> ItemLikeVisitor<'v> for LifeSeeder<'tcx> {
                 if of_trait.is_some() {
                     self.worklist.push(item.def_id);
                 }
-                for impl_item_ref in items {
+                for impl_item_ref in *items {
                     let impl_item = self.tcx.hir().impl_item(impl_item_ref.id);
                     if of_trait.is_some()
                         || has_allow_dead_code_or_lang_attr(self.tcx, impl_item.hir_id())
@@ -602,8 +593,6 @@ fn live_symbols_and_ignored_derived_traits<'tcx>(
         live_symbols: Default::default(),
         repr_has_repr_c: false,
         in_pat: false,
-        inherited_pub_visibility: false,
-        pub_visibility: false,
         ignore_variant_stack: vec![],
         struct_constructors,
         ignored_derived_traits: FxHashMap::default(),
diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs
index 6cf1aa480d2..237a8abfabe 100644
--- a/compiler/rustc_passes/src/hir_stats.rs
+++ b/compiler/rustc_passes/src/hir_stats.rs
@@ -3,6 +3,7 @@
 // completely accurate (some things might be counted twice, others missed).
 
 use rustc_ast::visit as ast_visit;
+use rustc_ast::visit::BoundKind;
 use rustc_ast::{self as ast, AttrId, NodeId};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_hir as hir;
@@ -302,7 +303,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
         ast_visit::walk_assoc_item(self, item, ctxt);
     }
 
-    fn visit_param_bound(&mut self, bounds: &'v ast::GenericBound) {
+    fn visit_param_bound(&mut self, bounds: &'v ast::GenericBound, _ctxt: BoundKind) {
         self.record("GenericBound", Id::None, bounds);
         ast_visit::walk_param_bound(self, bounds)
     }
diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs
index b520e5d04ea..b65e3342613 100644
--- a/compiler/rustc_passes/src/reachable.rs
+++ b/compiler/rustc_passes/src/reachable.rs
@@ -333,6 +333,11 @@ impl CollectPrivateImplItemsVisitor<'_, '_> {
         let codegen_attrs = self.tcx.codegen_fn_attrs(def_id);
         if codegen_attrs.contains_extern_indicator()
             || codegen_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
+            // FIXME(nbdd0121): `#[used]` are marked as reachable here so it's picked up by
+            // `linked_symbols` in cg_ssa. They won't be exported in binary or cdylib due to their
+            // `SymbolExportLevel::Rust` export level but may end up being exported in dylibs.
+            || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED)
+            || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)
         {
             self.worklist.push(def_id);
         }
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index 35a858cb86c..10dc587be6e 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -737,7 +737,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
                     }
                 }
 
-                for impl_item_ref in items {
+                for impl_item_ref in *items {
                     let impl_item = self.tcx.associated_item(impl_item_ref.id.def_id);
 
                     if let Some(def_id) = impl_item.trait_item_def_id {
diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs
index 0ce97de4134..619e0d0341f 100644
--- a/compiler/rustc_privacy/src/lib.rs
+++ b/compiler/rustc_privacy/src/lib.rs
@@ -296,28 +296,6 @@ fn min(vis1: ty::Visibility, vis2: ty::Visibility, tcx: TyCtxt<'_>) -> ty::Visib
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-/// Visitor used to determine if pub(restricted) is used anywhere in the crate.
-///
-/// This is done so that `private_in_public` warnings can be turned into hard errors
-/// in crates that have been updated to use pub(restricted).
-////////////////////////////////////////////////////////////////////////////////
-struct PubRestrictedVisitor<'tcx> {
-    tcx: TyCtxt<'tcx>,
-    has_pub_restricted: bool,
-}
-
-impl<'tcx> Visitor<'tcx> for PubRestrictedVisitor<'tcx> {
-    type NestedFilter = nested_filter::All;
-
-    fn nested_visit_map(&mut self) -> Self::Map {
-        self.tcx.hir()
-    }
-    fn visit_vis(&mut self, vis: &'tcx hir::Visibility<'tcx>) {
-        self.has_pub_restricted = self.has_pub_restricted || vis.node.is_pub_restricted();
-    }
-}
-
-////////////////////////////////////////////////////////////////////////////////
 /// Visitor used to determine impl visibility and reachability.
 ////////////////////////////////////////////////////////////////////////////////
 
@@ -682,7 +660,9 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> {
                     self.update_with_hir_id(ctor_hir_id, item_level);
                 }
                 for field in def.fields() {
-                    if field.vis.node.is_pub() {
+                    let def_id = self.tcx.hir().local_def_id(field.hir_id);
+                    let vis = self.tcx.visibility(def_id);
+                    if vis.is_public() {
                         self.update_with_hir_id(field.hir_id, item_level);
                     }
                 }
@@ -1361,7 +1341,7 @@ impl<'a, 'tcx> ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> {
             // .. and it corresponds to a private type in the AST (this returns
             // `None` for type parameters).
             match self.tcx.hir().find(self.tcx.hir().local_def_id_to_hir_id(did)) {
-                Some(Node::Item(item)) => !item.vis.node.is_pub(),
+                Some(Node::Item(_)) => !self.tcx.visibility(did).is_public(),
                 Some(_) | None => false,
             }
         } else {
@@ -1383,8 +1363,8 @@ impl<'a, 'tcx> ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> {
         }
     }
 
-    fn item_is_public(&self, def_id: LocalDefId, vis: &hir::Visibility<'_>) -> bool {
-        self.access_levels.is_reachable(def_id) || vis.node.is_pub()
+    fn item_is_public(&self, def_id: LocalDefId) -> bool {
+        self.access_levels.is_reachable(def_id) || self.tcx.visibility(def_id).is_public()
     }
 }
 
@@ -1519,8 +1499,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> {
                                 let impl_item = self.tcx.hir().impl_item(impl_item_ref.id);
                                 match impl_item.kind {
                                     hir::ImplItemKind::Const(..) | hir::ImplItemKind::Fn(..)
-                                        if self
-                                            .item_is_public(impl_item.def_id, &impl_item.vis) =>
+                                        if self.item_is_public(impl_item.def_id) =>
                                     {
                                         intravisit::walk_impl_item(self, impl_item)
                                     }
@@ -1591,7 +1570,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> {
             hir::ItemKind::TyAlias(..) => return,
 
             // Not at all public, so we don't care.
-            _ if !self.item_is_public(item.def_id, &item.vis) => {
+            _ if !self.item_is_public(item.def_id) => {
                 return;
             }
 
@@ -1606,12 +1585,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> {
     }
 
     fn visit_generics(&mut self, generics: &'tcx hir::Generics<'tcx>) {
-        for param in generics.params {
-            for bound in param.bounds {
-                self.check_generic_bound(bound);
-            }
-        }
-        for predicate in generics.where_clause.predicates {
+        for predicate in generics.predicates {
             match predicate {
                 hir::WherePredicate::BoundPredicate(bound_pred) => {
                     for bound in bound_pred.bounds.iter() {
@@ -1655,7 +1629,9 @@ impl<'a, 'tcx> Visitor<'tcx> for ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> {
     }
 
     fn visit_field_def(&mut self, s: &'tcx hir::FieldDef<'tcx>) {
-        if s.vis.node.is_pub() || self.in_variant {
+        let def_id = self.tcx.hir().local_def_id(s.hir_id);
+        let vis = self.tcx.visibility(def_id);
+        if vis.is_public() || self.in_variant {
             intravisit::walk_field_def(self, s);
         }
     }
@@ -1680,7 +1656,6 @@ struct SearchInterfaceForPrivateItemsVisitor<'tcx> {
     item_def_id: LocalDefId,
     /// The visitor checks that each component type is at least this visible.
     required_visibility: ty::Visibility,
-    has_pub_restricted: bool,
     has_old_errors: bool,
     in_assoc_ty: bool,
 }
@@ -1769,7 +1744,10 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> {
             };
             let make_msg = || format!("{} {} `{}` in public interface", vis_descr, kind, descr);
             let span = self.tcx.def_span(self.item_def_id.to_def_id());
-            if self.has_pub_restricted || self.has_old_errors || self.in_assoc_ty {
+            if self.has_old_errors
+                || self.in_assoc_ty
+                || self.tcx.resolutions(()).has_pub_restricted
+            {
                 let mut err = if kind == "trait" {
                     struct_span_err!(self.tcx.sess, span, E0445, "{}", make_msg())
                 } else {
@@ -1828,7 +1806,6 @@ impl<'tcx> DefIdVisitor<'tcx> for SearchInterfaceForPrivateItemsVisitor<'tcx> {
 
 struct PrivateItemsInPublicInterfacesVisitor<'tcx> {
     tcx: TyCtxt<'tcx>,
-    has_pub_restricted: bool,
     old_error_set_ancestry: LocalDefIdSet,
 }
 
@@ -1842,7 +1819,6 @@ impl<'tcx> PrivateItemsInPublicInterfacesVisitor<'tcx> {
             tcx: self.tcx,
             item_def_id: def_id,
             required_visibility,
-            has_pub_restricted: self.has_pub_restricted,
             has_old_errors: self.old_error_set_ancestry.contains(&def_id),
             in_assoc_ty: false,
         }
@@ -1994,19 +1970,16 @@ fn visibility(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Visibility {
             match tcx.hir().get(hir_id) {
                 // Unique types created for closures participate in type privacy checking.
                 // They have visibilities inherited from the module they are defined in.
-                Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(..), .. }) => {
-                    ty::Visibility::Restricted(tcx.parent_module(hir_id).to_def_id())
-                }
-                // - AST lowering may clone `use` items and the clones don't
+                Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(..), .. })
+                // - AST lowering creates dummy `use` items which don't
                 //   get their entries in the resolver's visibility table.
                 // - AST lowering also creates opaque type items with inherited visibilities.
                 //   Visibility on them should have no effect, but to avoid the visibility
                 //   query failing on some items, we provide it for opaque types as well.
-                Node::Item(hir::Item {
-                    vis,
-                    kind: hir::ItemKind::Use(..) | hir::ItemKind::OpaqueTy(..),
+                | Node::Item(hir::Item {
+                    kind: hir::ItemKind::Use(_, hir::UseKind::ListStem) | hir::ItemKind::OpaqueTy(..),
                     ..
-                }) => ty::Visibility::from_hir(vis, hir_id, tcx),
+                }) => ty::Visibility::Restricted(tcx.parent_module(hir_id).to_def_id()),
                 // Visibilities of trait impl items are inherited from their traits
                 // and are not filled in resolve.
                 Node::ImplItem(impl_item) => {
@@ -2083,12 +2056,6 @@ fn check_private_in_public(tcx: TyCtxt<'_>, (): ()) {
     };
     tcx.hir().walk_toplevel_module(&mut visitor);
 
-    let has_pub_restricted = {
-        let mut pub_restricted_visitor = PubRestrictedVisitor { tcx, has_pub_restricted: false };
-        tcx.hir().walk_toplevel_module(&mut pub_restricted_visitor);
-        pub_restricted_visitor.has_pub_restricted
-    };
-
     let mut old_error_set_ancestry = HirIdSet::default();
     for mut id in visitor.old_error_set.iter().copied() {
         loop {
@@ -2106,7 +2073,6 @@ fn check_private_in_public(tcx: TyCtxt<'_>, (): ()) {
     // Check for private types and traits in public interfaces.
     let mut visitor = PrivateItemsInPublicInterfacesVisitor {
         tcx,
-        has_pub_restricted,
         // Only definition IDs are ever searched in `old_error_set_ancestry`,
         // so we can filter away all non-definition IDs at this point.
         old_error_set_ancestry: old_error_set_ancestry
diff --git a/compiler/rustc_query_impl/src/keys.rs b/compiler/rustc_query_impl/src/keys.rs
index f1f83a7299c..3f0f856b5dd 100644
--- a/compiler/rustc_query_impl/src/keys.rs
+++ b/compiler/rustc_query_impl/src/keys.rs
@@ -502,3 +502,14 @@ impl<'tcx> Key for (ty::Instance<'tcx>, &'tcx ty::List<Ty<'tcx>>) {
         self.0.default_span(tcx)
     }
 }
+
+impl<'tcx> Key for (Ty<'tcx>, ty::ValTree<'tcx>) {
+    #[inline(always)]
+    fn query_crate_is_local(&self) -> bool {
+        true
+    }
+
+    fn default_span(&self, _: TyCtxt<'_>) -> Span {
+        DUMMY_SP
+    }
+}
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index d97d9199b77..43c1062f32e 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -265,6 +265,8 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
                 })
             }
             ast::VisibilityKind::Restricted { ref path, id, .. } => {
+                // Make `PRIVATE_IN_PUBLIC` lint a hard error.
+                self.r.has_pub_restricted = true;
                 // For visibilities we are not ready to provide correct implementation of "uniform
                 // paths" right now, so on 2018 edition we only allow module-relative paths for now.
                 // On 2015 edition visibilities are resolved as crate-relative by default,
@@ -296,7 +298,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
                     &segments,
                     Some(TypeNS),
                     parent_scope,
-                    if finalize { Finalize::SimplePath(id, path.span) } else { Finalize::No },
+                    finalize.then(|| Finalize::new(id, path.span)),
                     None,
                 ) {
                     PathResult::Module(ModuleOrUniformRoot::Module(module)) => {
@@ -458,6 +460,14 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
                 let mut source = module_path.pop().unwrap();
                 let mut type_ns_only = false;
 
+                self.r.visibilities.insert(self.r.local_def_id(id), vis);
+                if id1 != ast::DUMMY_NODE_ID {
+                    self.r.visibilities.insert(self.r.local_def_id(id1), vis);
+                }
+                if id2 != ast::DUMMY_NODE_ID {
+                    self.r.visibilities.insert(self.r.local_def_id(id2), vis);
+                }
+
                 if nested {
                     // Correctly handle `self`
                     if source.ident.name == kw::SelfLower {
@@ -580,6 +590,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
                     is_prelude: self.r.session.contains_name(&item.attrs, sym::prelude_import),
                     max_vis: Cell::new(ty::Visibility::Invisible),
                 };
+                self.r.visibilities.insert(self.r.local_def_id(id), vis);
                 self.add_import(prefix, kind, use_tree.span, id, item, root_span, item.id, vis);
             }
             ast::UseTreeKind::Nested(ref items) => {
@@ -840,7 +851,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
                 .span_suggestion(
                     item.span,
                     "rename the `self` crate to be able to import it",
-                    "extern crate self as name;".into(),
+                    "extern crate self as name;",
                     Applicability::HasPlaceholders,
                 )
                 .emit();
@@ -1256,13 +1267,15 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
                 self.insert_unused_macro(ident, def_id, item.id);
             }
             self.r.visibilities.insert(def_id, vis);
-            self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Binding(
+            let scope = self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Binding(
                 self.r.arenas.alloc_macro_rules_binding(MacroRulesBinding {
                     parent_macro_rules_scope: parent_scope.macro_rules,
                     binding,
                     ident,
                 }),
-            ))
+            ));
+            self.r.macro_rules_scopes.insert(def_id, scope);
+            scope
         } else {
             let module = parent_scope.module;
             let vis = match item.kind {
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index 899980a4c08..7d40ecb18b7 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -123,7 +123,7 @@ impl<'a> Resolver<'a> {
             let (span, found_use) = if let Some(def_id) = def_id.as_local() {
                 UsePlacementFinder::check(krate, self.def_id_to_node_id[def_id])
             } else {
-                (None, false)
+                (None, FoundUse::No)
             };
             if !candidates.is_empty() {
                 show_candidates(
@@ -132,8 +132,9 @@ impl<'a> Resolver<'a> {
                     &mut err,
                     span,
                     &candidates,
-                    instead,
+                    if instead { Instead::Yes } else { Instead::No },
                     found_use,
+                    IsPattern::No,
                 );
             } else if let Some((span, msg, sugg, appl)) = suggestion {
                 err.span_suggestion(span, msg, sugg, appl);
@@ -416,15 +417,12 @@ impl<'a> Resolver<'a> {
 
     crate fn lint_if_path_starts_with_module(
         &mut self,
-        finalize: Finalize,
+        finalize: Option<Finalize>,
         path: &[Segment],
         second_binding: Option<&NameBinding<'_>>,
     ) {
-        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 Some(Finalize { node_id, root_span, .. }) = finalize else {
+            return;
         };
 
         let first_name = match path.get(0) {
@@ -462,11 +460,11 @@ impl<'a> Resolver<'a> {
             }
         }
 
-        let diag = BuiltinLintDiagnostics::AbsPathWithModule(diag_span);
+        let diag = BuiltinLintDiagnostics::AbsPathWithModule(root_span);
         self.lint_buffer.buffer_lint_with_diagnostic(
             ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE,
-            diag_id,
-            diag_span,
+            node_id,
+            root_span,
             "absolute paths must start with `self`, `super`, \
              `crate`, or an external crate name in the 2018 edition",
             diag,
@@ -493,14 +491,14 @@ impl<'a> Resolver<'a> {
     ///
     /// This takes the error provided, combines it with the span and any additional spans inside the
     /// error and emits it.
-    crate fn report_error(&self, span: Span, resolution_error: ResolutionError<'_>) {
+    crate fn report_error(&mut self, span: Span, resolution_error: ResolutionError<'a>) {
         self.into_struct_error(span, resolution_error).emit();
     }
 
     crate fn into_struct_error(
-        &self,
+        &mut self,
         span: Span,
-        resolution_error: ResolutionError<'_>,
+        resolution_error: ResolutionError<'a>,
     ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
         match resolution_error {
             ResolutionError::GenericParamsFromOuterFunction(outer_res, has_generic_params) => {
@@ -650,7 +648,7 @@ impl<'a> Resolver<'a> {
                 }
                 err
             }
-            ResolutionError::VariableNotBoundInPattern(binding_error) => {
+            ResolutionError::VariableNotBoundInPattern(binding_error, parent_scope) => {
                 let BindingError { name, target, origin, could_be_path } = binding_error;
 
                 let target_sp = target.iter().copied().collect::<Vec<_>>();
@@ -670,13 +668,41 @@ impl<'a> Resolver<'a> {
                 for sp in origin_sp {
                     err.span_label(sp, "variable not in all patterns");
                 }
-                if *could_be_path {
-                    let help_msg = format!(
-                        "if you meant to match on a variant or a `const` item, consider \
-                         making the path in the pattern qualified: `?::{}`",
-                        name,
+                if could_be_path {
+                    let import_suggestions = self.lookup_import_candidates(
+                        Ident::with_dummy_span(name),
+                        Namespace::ValueNS,
+                        &parent_scope,
+                        &|res: Res| match res {
+                            Res::Def(
+                                DefKind::Ctor(CtorOf::Variant, CtorKind::Const)
+                                | DefKind::Ctor(CtorOf::Struct, CtorKind::Const)
+                                | DefKind::Const
+                                | DefKind::AssocConst,
+                                _,
+                            ) => true,
+                            _ => false,
+                        },
+                    );
+
+                    if import_suggestions.is_empty() {
+                        let help_msg = format!(
+                            "if you meant to match on a variant or a `const` item, consider \
+                             making the path in the pattern qualified: `path::to::ModOrType::{}`",
+                            name,
+                        );
+                        err.span_help(span, &help_msg);
+                    }
+                    show_candidates(
+                        &self.definitions,
+                        self.session,
+                        &mut err,
+                        Some(span),
+                        &import_suggestions,
+                        Instead::No,
+                        FoundUse::Yes,
+                        IsPattern::Yes,
                     );
-                    err.span_help(span, &help_msg);
                 }
                 err
             }
@@ -1014,7 +1040,7 @@ impl<'a> Resolver<'a> {
             }
             ResolutionError::InvalidAsmSym => {
                 let mut err = self.session.struct_span_err(span, "invalid `sym` operand");
-                err.span_label(span, &format!("is a local variable"));
+                err.span_label(span, "is a local variable");
                 err.help("`sym` operands must refer to either a function or a static");
                 err
             }
@@ -1022,7 +1048,7 @@ impl<'a> Resolver<'a> {
     }
 
     crate fn report_vis_error(
-        &self,
+        &mut self,
         vis_resolution_error: VisResolutionError<'_>,
     ) -> ErrorGuaranteed {
         match vis_resolution_error {
@@ -1453,8 +1479,9 @@ impl<'a> Resolver<'a> {
             err,
             None,
             &import_suggestions,
-            false,
-            true,
+            Instead::No,
+            FoundUse::Yes,
+            IsPattern::No,
         );
 
         if macro_kind == MacroKind::Derive && (ident.name == sym::Send || ident.name == sym::Sync) {
@@ -1473,7 +1500,6 @@ impl<'a> Resolver<'a> {
                 &parent_scope,
                 None,
                 false,
-                false,
                 None,
             ) {
                 let desc = match binding.res() {
@@ -1781,7 +1807,7 @@ impl<'a> Resolver<'a> {
         opt_ns: Option<Namespace>, // `None` indicates a module path in import
         parent_scope: &ParentScope<'a>,
         ribs: Option<&PerNS<Vec<Rib<'a>>>>,
-        unusable_binding: Option<&'a NameBinding<'a>>,
+        ignore_binding: Option<&'a NameBinding<'a>>,
         module: Option<ModuleOrUniformRoot<'a>>,
         i: usize,
         ident: Ident,
@@ -1833,8 +1859,7 @@ impl<'a> Resolver<'a> {
                         ns_to_try,
                         parent_scope,
                         None,
-                        false,
-                        unusable_binding,
+                        ignore_binding,
                     ).ok()
                 } else if let Some(ribs) = ribs
                     && let Some(TypeNS | ValueNS) = opt_ns
@@ -1843,9 +1868,9 @@ impl<'a> Resolver<'a> {
                         ident,
                         ns_to_try,
                         parent_scope,
-                        Finalize::No,
+                        None,
                         &ribs[ns_to_try],
-                        unusable_binding,
+                        ignore_binding,
                     ) {
                         // we found a locally-imported or available item/module
                         Some(LexicalScopeBinding::Item(binding)) => Some(binding),
@@ -1859,8 +1884,7 @@ impl<'a> Resolver<'a> {
                         parent_scope,
                         None,
                         false,
-                        false,
-                        unusable_binding,
+                        ignore_binding,
                     ).ok()
                 };
                 if let Some(binding) = binding {
@@ -1891,9 +1915,9 @@ impl<'a> Resolver<'a> {
                     ident,
                     ValueNS,
                     parent_scope,
-                    Finalize::No,
+                    None,
                     &ribs[ValueNS],
-                    unusable_binding,
+                    ignore_binding,
                 )
             } else {
                 None
@@ -2390,6 +2414,27 @@ fn find_span_immediately_after_crate_name(
     (next_left_bracket == after_second_colon, from_second_colon)
 }
 
+/// A suggestion has already been emitted, change the wording slightly to clarify that both are
+/// independent options.
+enum Instead {
+    Yes,
+    No,
+}
+
+/// Whether an existing place with an `use` item was found.
+enum FoundUse {
+    Yes,
+    No,
+}
+
+/// Whether a binding is part of a pattern or an expression. Used for diagnostics.
+enum IsPattern {
+    /// The binding is part of a pattern
+    Yes,
+    /// The binding is part of an expression
+    No,
+}
+
 /// When an entity with a given name is not available in scope, we search for
 /// entities with that name in all crates. This method allows outputting the
 /// results of this search in a programmer-friendly way
@@ -2400,8 +2445,9 @@ fn show_candidates(
     // This is `None` if all placement locations are inside expansions
     use_placement_span: Option<Span>,
     candidates: &[ImportSuggestion],
-    instead: bool,
-    found_use: bool,
+    instead: Instead,
+    found_use: FoundUse,
+    is_pattern: IsPattern,
 ) {
     if candidates.is_empty() {
         return;
@@ -2428,24 +2474,38 @@ fn show_candidates(
     }
 
     if !accessible_path_strings.is_empty() {
-        let (determiner, kind) = if accessible_path_strings.len() == 1 {
-            ("this", accessible_path_strings[0].1)
+        let (determiner, kind, name) = if accessible_path_strings.len() == 1 {
+            ("this", accessible_path_strings[0].1, format!(" `{}`", accessible_path_strings[0].0))
         } else {
-            ("one of these", "items")
+            ("one of these", "items", String::new())
         };
 
-        let instead = if instead { " instead" } else { "" };
-        let mut msg = format!("consider importing {} {}{}", determiner, kind, instead);
+        let instead = if let Instead::Yes = instead { " instead" } else { "" };
+        let mut msg = if let IsPattern::Yes = is_pattern {
+            format!(
+                "if you meant to match on {}{}{}, use the full path in the pattern",
+                kind, instead, name
+            )
+        } else {
+            format!("consider importing {} {}{}", determiner, kind, instead)
+        };
 
         for note in accessible_path_strings.iter().flat_map(|cand| cand.3.as_ref()) {
             err.note(note);
         }
 
-        if let Some(span) = use_placement_span {
+        if let (IsPattern::Yes, Some(span)) = (is_pattern, use_placement_span) {
+            err.span_suggestions(
+                span,
+                &msg,
+                accessible_path_strings.into_iter().map(|a| a.0),
+                Applicability::MaybeIncorrect,
+            );
+        } else if let Some(span) = use_placement_span {
             for candidate in &mut accessible_path_strings {
                 // produce an additional newline to separate the new use statement
                 // from the directly following item.
-                let additional_newline = if found_use { "" } else { "\n" };
+                let additional_newline = if let FoundUse::Yes = found_use { "" } else { "\n" };
                 candidate.0 = format!("use {};\n{}", &candidate.0, additional_newline);
             }
 
@@ -2453,7 +2513,7 @@ fn show_candidates(
                 span,
                 &msg,
                 accessible_path_strings.into_iter().map(|a| a.0),
-                Applicability::Unspecified,
+                Applicability::MaybeIncorrect,
             );
         } else {
             msg.push(':');
@@ -2468,9 +2528,17 @@ fn show_candidates(
     } else {
         assert!(!inaccessible_path_strings.is_empty());
 
+        let prefix =
+            if let IsPattern::Yes = is_pattern { "you might have meant to match on " } else { "" };
         if inaccessible_path_strings.len() == 1 {
             let (name, descr, def_id, note) = &inaccessible_path_strings[0];
-            let msg = format!("{} `{}` exists but is inaccessible", descr, name);
+            let msg = format!(
+                "{}{} `{}`{} exists but is inaccessible",
+                prefix,
+                descr,
+                name,
+                if let IsPattern::Yes = is_pattern { ", which" } else { "" }
+            );
 
             if let Some(local_def_id) = def_id.and_then(|did| did.as_local()) {
                 let span = definitions.def_span(local_def_id);
@@ -2496,7 +2564,7 @@ fn show_candidates(
                 "item".to_string()
             };
 
-            let mut msg = format!("these {}s exist but are inaccessible", descr);
+            let mut msg = format!("{}these {}s exist but are inaccessible", prefix, descr);
             let mut has_colon = false;
 
             let mut spans = Vec::new();
@@ -2537,14 +2605,14 @@ struct UsePlacementFinder {
 }
 
 impl UsePlacementFinder {
-    fn check(krate: &Crate, target_module: NodeId) -> (Option<Span>, bool) {
+    fn check(krate: &Crate, target_module: NodeId) -> (Option<Span>, FoundUse) {
         let mut finder =
             UsePlacementFinder { target_module, first_legal_span: None, first_use_span: None };
         finder.visit_crate(krate);
         if let Some(use_span) = finder.first_use_span {
-            (Some(use_span), true)
+            (Some(use_span), FoundUse::Yes)
         } else {
-            (finder.first_legal_span, false)
+            (finder.first_legal_span, FoundUse::No)
         }
     }
 }
diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs
index 84fe0ec83d2..18ce359524d 100644
--- a/compiler/rustc_resolve/src/ident.rs
+++ b/compiler/rustc_resolve/src/ident.rs
@@ -279,9 +279,9 @@ impl<'a> Resolver<'a> {
         mut ident: Ident,
         ns: Namespace,
         parent_scope: &ParentScope<'a>,
-        finalize_full: Finalize,
+        finalize: Option<Finalize>,
         ribs: &[Rib<'a>],
-        unusable_binding: Option<&'a NameBinding<'a>>,
+        ignore_binding: Option<&'a NameBinding<'a>>,
     ) -> Option<LexicalScopeBinding<'a>> {
         assert!(ns == TypeNS || ns == ValueNS);
         let orig_ident = ident;
@@ -302,7 +302,6 @@ impl<'a> Resolver<'a> {
         let normalized_ident = Ident { span: normalized_span, ..ident };
 
         // Walk backwards up the ribs in scope.
-        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);
@@ -316,7 +315,7 @@ impl<'a> Resolver<'a> {
                     i,
                     rib_ident,
                     *res,
-                    finalize,
+                    finalize.map(|finalize| finalize.path_span),
                     *original_rib_ident_def,
                     ribs,
                 )));
@@ -344,8 +343,7 @@ impl<'a> Resolver<'a> {
                 ns,
                 parent_scope,
                 finalize,
-                false,
-                unusable_binding,
+                ignore_binding,
             );
             if let Ok(binding) = item {
                 // The ident resolves to an item.
@@ -354,12 +352,11 @@ impl<'a> Resolver<'a> {
         }
         self.early_resolve_ident_in_lexical_scope(
             orig_ident,
-            ScopeSet::Late(ns, module, finalize_full.node_id()),
+            ScopeSet::Late(ns, module, finalize.map(|finalize| finalize.node_id)),
             parent_scope,
             finalize,
             finalize.is_some(),
-            false,
-            unusable_binding,
+            ignore_binding,
         )
         .ok()
         .map(LexicalScopeBinding::Item)
@@ -376,10 +373,9 @@ impl<'a> Resolver<'a> {
         orig_ident: Ident,
         scope_set: ScopeSet<'a>,
         parent_scope: &ParentScope<'a>,
-        finalize: Option<Span>,
+        finalize: Option<Finalize>,
         force: bool,
-        last_import_segment: bool,
-        unusable_binding: Option<&'a NameBinding<'a>>,
+        ignore_binding: Option<&'a NameBinding<'a>>,
     ) -> Result<&'a NameBinding<'a>, Determinacy> {
         bitflags::bitflags! {
             struct Flags: u8 {
@@ -499,8 +495,7 @@ impl<'a> Resolver<'a> {
                             ns,
                             parent_scope,
                             finalize,
-                            last_import_segment,
-                            unusable_binding,
+                            ignore_binding,
                         );
                         match binding {
                             Ok(binding) => Ok((binding, Flags::MODULE | Flags::MISC_SUGGEST_CRATE)),
@@ -522,8 +517,7 @@ impl<'a> Resolver<'a> {
                             adjusted_parent_scope,
                             !matches!(scope_set, ScopeSet::Late(..)),
                             finalize,
-                            last_import_segment,
-                            unusable_binding,
+                            ignore_binding,
                         );
                         match binding {
                             Ok(binding) => {
@@ -608,8 +602,7 @@ impl<'a> Resolver<'a> {
                                 ns,
                                 parent_scope,
                                 None,
-                                last_import_segment,
-                                unusable_binding,
+                                ignore_binding,
                             ) {
                                 if use_prelude || this.is_builtin_macro(binding.res()) {
                                     result = Ok((binding, Flags::MISC_FROM_PRELUDE));
@@ -731,7 +724,7 @@ impl<'a> Resolver<'a> {
         ns: Namespace,
         parent_scope: &ParentScope<'a>,
     ) -> Result<&'a NameBinding<'a>, Determinacy> {
-        self.resolve_ident_in_module_ext(module, ident, ns, parent_scope, None, false, None)
+        self.resolve_ident_in_module_ext(module, ident, ns, parent_scope, None, None)
             .map_err(|(determinacy, _)| determinacy)
     }
 
@@ -742,23 +735,11 @@ impl<'a> Resolver<'a> {
         ident: Ident,
         ns: Namespace,
         parent_scope: &ParentScope<'a>,
-        finalize: Option<Span>,
-        // We are resolving a last import segment during import validation.
-        last_import_segment: bool,
-        // This binding should be ignored during in-module resolution, so that we don't get
-        // "self-confirming" import resolutions during import validation.
-        unusable_binding: Option<&'a NameBinding<'a>>,
+        finalize: Option<Finalize>,
+        ignore_binding: Option<&'a NameBinding<'a>>,
     ) -> Result<&'a NameBinding<'a>, Determinacy> {
-        self.resolve_ident_in_module_ext(
-            module,
-            ident,
-            ns,
-            parent_scope,
-            finalize,
-            last_import_segment,
-            unusable_binding,
-        )
-        .map_err(|(determinacy, _)| determinacy)
+        self.resolve_ident_in_module_ext(module, ident, ns, parent_scope, finalize, ignore_binding)
+            .map_err(|(determinacy, _)| determinacy)
     }
 
     #[tracing::instrument(level = "debug", skip(self))]
@@ -768,9 +749,8 @@ impl<'a> Resolver<'a> {
         mut ident: Ident,
         ns: Namespace,
         parent_scope: &ParentScope<'a>,
-        finalize: Option<Span>,
-        last_import_segment: bool,
-        unusable_binding: Option<&'a NameBinding<'a>>,
+        finalize: Option<Finalize>,
+        ignore_binding: Option<&'a NameBinding<'a>>,
     ) -> Result<&'a NameBinding<'a>, (Determinacy, Weak)> {
         let tmp_parent_scope;
         let mut adjusted_parent_scope = parent_scope;
@@ -796,8 +776,7 @@ impl<'a> Resolver<'a> {
             adjusted_parent_scope,
             false,
             finalize,
-            last_import_segment,
-            unusable_binding,
+            ignore_binding,
         )
     }
 
@@ -808,9 +787,8 @@ impl<'a> Resolver<'a> {
         ident: Ident,
         ns: Namespace,
         parent_scope: &ParentScope<'a>,
-        finalize: Option<Span>,
-        last_import_segment: bool,
-        unusable_binding: Option<&'a NameBinding<'a>>,
+        finalize: Option<Finalize>,
+        ignore_binding: Option<&'a NameBinding<'a>>,
     ) -> Result<&'a NameBinding<'a>, Determinacy> {
         self.resolve_ident_in_module_unadjusted_ext(
             module,
@@ -819,8 +797,7 @@ impl<'a> Resolver<'a> {
             parent_scope,
             false,
             finalize,
-            last_import_segment,
-            unusable_binding,
+            ignore_binding,
         )
         .map_err(|(determinacy, _)| determinacy)
     }
@@ -835,9 +812,10 @@ impl<'a> Resolver<'a> {
         ns: Namespace,
         parent_scope: &ParentScope<'a>,
         restricted_shadowing: bool,
-        finalize: Option<Span>,
-        last_import_segment: bool,
-        unusable_binding: Option<&'a NameBinding<'a>>,
+        finalize: Option<Finalize>,
+        // This binding should be ignored during in-module resolution, so that we don't get
+        // "self-confirming" import resolutions during import validation and checking.
+        ignore_binding: Option<&'a NameBinding<'a>>,
     ) -> Result<&'a NameBinding<'a>, (Determinacy, Weak)> {
         let module = match module {
             ModuleOrUniformRoot::Module(module) => module,
@@ -849,8 +827,7 @@ impl<'a> Resolver<'a> {
                     parent_scope,
                     finalize,
                     finalize.is_some(),
-                    last_import_segment,
-                    unusable_binding,
+                    ignore_binding,
                 );
                 return binding.map_err(|determinacy| (determinacy, Weak::No));
             }
@@ -890,8 +867,7 @@ impl<'a> Resolver<'a> {
                     parent_scope,
                     finalize,
                     finalize.is_some(),
-                    last_import_segment,
-                    unusable_binding,
+                    ignore_binding,
                 );
                 return binding.map_err(|determinacy| (determinacy, Weak::No));
             }
@@ -901,19 +877,15 @@ 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(path_span) = finalize {
+        if let Some(Finalize { path_span, report_private, .. }) = finalize {
             // If the primary binding is unusable, search further and return the shadowed glob
             // binding if it exists. What we really want here is having two separate scopes in
             // a module - one for non-globs and one for globs, but until that's done use this
             // hack to avoid inconsistent resolution ICEs during import validation.
             let binding = [resolution.binding, resolution.shadowed_glob]
                 .into_iter()
-                .filter_map(|binding| match (binding, unusable_binding) {
-                    (Some(binding), Some(unusable_binding))
-                        if ptr::eq(binding, unusable_binding) =>
-                    {
-                        None
-                    }
+                .filter_map(|binding| match (binding, ignore_binding) {
+                    (Some(binding), Some(ignored)) if ptr::eq(binding, ignored) => None,
                     _ => binding,
                 })
                 .next();
@@ -922,14 +894,14 @@ impl<'a> Resolver<'a> {
             };
 
             if !self.is_accessible_from(binding.vis, parent_scope.module) {
-                if last_import_segment {
-                    return Err((Determined, Weak::No));
-                } else {
+                if report_private {
                     self.privacy_errors.push(PrivacyError {
                         ident,
                         binding,
                         dedup_span: path_span,
                     });
+                } else {
+                    return Err((Determined, Weak::No));
                 }
             }
 
@@ -960,10 +932,8 @@ impl<'a> Resolver<'a> {
         }
 
         let check_usable = |this: &mut Self, binding: &'a NameBinding<'a>| {
-            if let Some(unusable_binding) = unusable_binding {
-                if ptr::eq(binding, unusable_binding) {
-                    return Err((Determined, Weak::No));
-                }
+            if let Some(ignored) = ignore_binding && ptr::eq(binding, ignored) {
+                return Err((Determined, Weak::No));
             }
             let usable = this.is_accessible_from(binding.vis, parent_scope.module);
             if usable { Ok(binding) } else { Err((Determined, Weak::No)) }
@@ -996,8 +966,7 @@ impl<'a> Resolver<'a> {
                 ns,
                 &single_import.parent_scope,
                 None,
-                last_import_segment,
-                unusable_binding,
+                ignore_binding,
             ) {
                 Err(Determined) => continue,
                 Ok(binding)
@@ -1073,8 +1042,7 @@ impl<'a> Resolver<'a> {
                 ns,
                 adjusted_parent_scope,
                 None,
-                last_import_segment,
-                unusable_binding,
+                ignore_binding,
             );
 
             match result {
@@ -1371,7 +1339,7 @@ impl<'a> Resolver<'a> {
         opt_ns: Option<Namespace>, // `None` indicates a module path in import
         parent_scope: &ParentScope<'a>,
     ) -> PathResult<'a> {
-        self.resolve_path_with_ribs(path, opt_ns, parent_scope, Finalize::No, None, None)
+        self.resolve_path_with_ribs(path, opt_ns, parent_scope, None, None, None)
     }
 
     #[tracing::instrument(level = "debug", skip(self))]
@@ -1380,10 +1348,10 @@ impl<'a> Resolver<'a> {
         path: &[Segment],
         opt_ns: Option<Namespace>, // `None` indicates a module path in import
         parent_scope: &ParentScope<'a>,
-        finalize: Finalize,
-        unusable_binding: Option<&'a NameBinding<'a>>,
+        finalize: Option<Finalize>,
+        ignore_binding: Option<&'a NameBinding<'a>>,
     ) -> PathResult<'a> {
-        self.resolve_path_with_ribs(path, opt_ns, parent_scope, finalize, None, unusable_binding)
+        self.resolve_path_with_ribs(path, opt_ns, parent_scope, finalize, None, ignore_binding)
     }
 
     crate fn resolve_path_with_ribs(
@@ -1391,13 +1359,12 @@ impl<'a> Resolver<'a> {
         path: &[Segment],
         opt_ns: Option<Namespace>, // `None` indicates a module path in import
         parent_scope: &ParentScope<'a>,
-        finalize_full: Finalize,
+        finalize: Option<Finalize>,
         ribs: Option<&PerNS<Vec<Rib<'a>>>>,
-        unusable_binding: Option<&'a NameBinding<'a>>,
+        ignore_binding: Option<&'a NameBinding<'a>>,
     ) -> PathResult<'a> {
-        debug!("resolve_path(path={:?}, opt_ns={:?}, finalize={:?})", path, opt_ns, finalize_full);
+        debug!("resolve_path(path={:?}, opt_ns={:?}, finalize={:?})", path, opt_ns, finalize);
 
-        let finalize = finalize_full.path_span();
         let mut module = None;
         let mut allow_super = true;
         let mut second_binding = None;
@@ -1497,8 +1464,7 @@ impl<'a> Resolver<'a> {
                         ns,
                         parent_scope,
                         finalize,
-                        false,
-                        unusable_binding,
+                        ignore_binding,
                     )
                 } else if let Some(ribs) = ribs
                     && let Some(TypeNS | ValueNS) = opt_ns
@@ -1507,9 +1473,9 @@ impl<'a> Resolver<'a> {
                         ident,
                         ns,
                         parent_scope,
-                        finalize_full,
+                        finalize,
                         &ribs[ns],
-                        unusable_binding,
+                        ignore_binding,
                     ) {
                         // we found a locally-imported or available item/module
                         Some(LexicalScopeBinding::Item(binding)) => Ok(binding),
@@ -1525,8 +1491,7 @@ impl<'a> Resolver<'a> {
                         parent_scope,
                         finalize,
                         finalize.is_some(),
-                        false,
-                        unusable_binding,
+                        ignore_binding,
                     )
                 };
                 FindBindingResult::Binding(binding)
@@ -1566,7 +1531,7 @@ 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(finalize_full, path, second_binding);
+                        self.lint_if_path_starts_with_module(finalize, path, second_binding);
                         return PathResult::NonModule(PartialRes::with_unresolved_segments(
                             res,
                             path.len() - i - 1,
@@ -1599,7 +1564,7 @@ impl<'a> Resolver<'a> {
                             opt_ns,
                             parent_scope,
                             ribs,
-                            unusable_binding,
+                            ignore_binding,
                             module,
                             i,
                             ident,
@@ -1609,7 +1574,7 @@ impl<'a> Resolver<'a> {
             }
         }
 
-        self.lint_if_path_starts_with_module(finalize_full, path, second_binding);
+        self.lint_if_path_starts_with_module(finalize, path, second_binding);
 
         PathResult::Module(match module {
             Some(module) => module,
diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs
index 01dc727737a..ef06ec356bd 100644
--- a/compiler/rustc_resolve/src/imports.rs
+++ b/compiler/rustc_resolve/src/imports.rs
@@ -545,7 +545,6 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
                         ns,
                         &import.parent_scope,
                         None,
-                        false,
                         None,
                     );
                     import.vis.set(orig_vis);
@@ -589,22 +588,18 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
     /// consolidate multiple unresolved import errors into a single diagnostic.
     fn finalize_import(&mut self, import: &'b Import<'b>) -> Option<UnresolvedImportError> {
         let orig_vis = import.vis.replace(ty::Visibility::Invisible);
-        let unusable_binding = match &import.kind {
+        let ignore_binding = match &import.kind {
             ImportKind::Single { target_bindings, .. } => target_bindings[TypeNS].get(),
             _ => None,
         };
         let prev_ambiguity_errors_len = self.r.ambiguity_errors.len();
-        let finalize = Finalize::UsePath {
-            root_id: import.root_id,
-            root_span: import.root_span,
-            path_span: import.span,
-        };
+        let finalize = Finalize::with_root_span(import.root_id, import.span, import.root_span);
         let path_res = self.r.resolve_path(
             &import.module_path,
             None,
             &import.parent_scope,
-            finalize,
-            unusable_binding,
+            Some(finalize),
+            ignore_binding,
         );
         let no_ambiguity = self.r.ambiguity_errors.len() == prev_ambiguity_errors_len;
         import.vis.set(orig_vis);
@@ -685,7 +680,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(finalize, &full_path, None);
+                    self.r.lint_if_path_starts_with_module(Some(finalize), &full_path, None);
                 }
 
                 if let ModuleOrUniformRoot::Module(module) = module {
@@ -720,8 +715,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
                     ident,
                     ns,
                     &import.parent_scope,
-                    Some(import.span),
-                    true,
+                    Some(Finalize { report_private: false, ..finalize }),
                     target_bindings[ns].get(),
                 );
                 import.vis.set(orig_vis);
@@ -781,8 +775,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
                         ident,
                         ns,
                         &import.parent_scope,
-                        Some(import.span),
-                        false,
+                        Some(finalize),
                         None,
                     );
                     if binding.is_ok() {
@@ -948,7 +941,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(finalize, &full_path, Some(binding));
+                    this.lint_if_path_starts_with_module(Some(finalize), &full_path, Some(binding));
                 }
             });
         }
@@ -1003,7 +996,6 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
                     &import.parent_scope,
                     None,
                     false,
-                    false,
                     target_bindings[ns].get(),
                 ) {
                     Ok(other_binding) => {
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 591bad70840..ca89f610322 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -12,15 +12,17 @@ use crate::{Module, ModuleOrUniformRoot, NameBinding, ParentScope, PathResult};
 use crate::{ResolutionError, Resolver, Segment, UseError};
 
 use rustc_ast::ptr::P;
-use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor};
+use rustc_ast::visit::{self, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor};
 use rustc_ast::*;
-use rustc_ast_lowering::ResolverAstLowering;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_ast_lowering::{LifetimeRes, ResolverAstLowering};
+use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
 use rustc_errors::DiagnosticId;
 use rustc_hir::def::Namespace::{self, *};
 use rustc_hir::def::{self, CtorKind, DefKind, PartialRes, PerNS};
 use rustc_hir::def_id::{DefId, CRATE_DEF_ID};
+use rustc_hir::definitions::DefPathData;
 use rustc_hir::{PrimTy, TraitCandidate};
+use rustc_index::vec::Idx;
 use rustc_middle::ty::DefIdTree;
 use rustc_middle::{bug, span_bug};
 use rustc_session::lint;
@@ -195,7 +197,17 @@ enum LifetimeRibKind {
     Item,
 
     /// This rib declares generic parameters.
-    Generics { span: Span, kind: LifetimeBinderKind },
+    Generics { parent: NodeId, span: Span, kind: LifetimeBinderKind },
+
+    /// FIXME(const_generics): This patches over an ICE caused by non-'static lifetimes in const
+    /// generics. We are disallowing this until we can decide on how we want to handle non-'static
+    /// lifetimes in const generics. See issue #74052 for discussion.
+    ConstGeneric,
+
+    /// Non-static lifetimes are prohibited in anonymous constants under `min_const_generics`.
+    /// This function will emit an error if `generic_const_exprs` is not enabled, the body identified by
+    /// `body_id` is an anonymous constant and `lifetime_ref` is non-static.
+    AnonConst,
 
     /// For **Modern** cases, create a new anonymous region parameter
     /// and reference that.
@@ -204,7 +216,7 @@ enum LifetimeRibKind {
     /// `resolve_lifetime` code.
     ///
     /// For **Deprecated** cases, report an error.
-    AnonymousCreateParameter,
+    AnonymousCreateParameter(NodeId),
 
     /// Give a hard error when either `&` or `'_` is written. Used to
     /// rule out things like `where T: Foo<'_>`. Does not imply an
@@ -212,7 +224,7 @@ enum LifetimeRibKind {
     AnonymousReportError,
 
     /// Pass responsibility to `resolve_lifetime` code for all cases.
-    AnonymousPassThrough,
+    AnonymousPassThrough(NodeId),
 }
 
 #[derive(Copy, Clone, Debug)]
@@ -242,7 +254,8 @@ impl LifetimeBinderKind {
 #[derive(Debug)]
 struct LifetimeRib {
     kind: LifetimeRibKind,
-    bindings: IdentMap<()>,
+    // We need to preserve insertion order for async fns.
+    bindings: FxIndexMap<Ident, (NodeId, LifetimeRes)>,
 }
 
 impl LifetimeRib {
@@ -524,7 +537,9 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
     }
     fn visit_anon_const(&mut self, constant: &'ast AnonConst) {
         // We deal with repeat expressions explicitly in `resolve_expr`.
-        self.resolve_anon_const(constant, IsRepeatExpr::No);
+        self.with_lifetime_rib(LifetimeRibKind::AnonConst, |this| {
+            this.resolve_anon_const(constant, IsRepeatExpr::No);
+        })
     }
     fn visit_expr(&mut self, expr: &'ast Expr) {
         self.resolve_expr(expr, None);
@@ -563,7 +578,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
                     .resolve_ident_in_lexical_scope(
                         self_ty,
                         TypeNS,
-                        Finalize::SimplePath(ty.id, ty.span),
+                        Some(Finalize::new(ty.id, ty.span)),
                         None,
                     )
                     .map_or(Res::Err, |d| d.res());
@@ -581,12 +596,19 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
                 self.with_generic_param_rib(
                     &bare_fn.generic_params,
                     NormalRibKind,
-                    LifetimeRibKind::Generics { kind: LifetimeBinderKind::BareFnType, span },
+                    LifetimeRibKind::Generics {
+                        parent: ty.id,
+                        kind: LifetimeBinderKind::BareFnType,
+                        span,
+                    },
                     |this| {
-                        this.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough, |this| {
-                            this.visit_generic_param_vec(&bare_fn.generic_params, false);
-                            visit::walk_fn_decl(this, &bare_fn.decl);
-                        });
+                        this.with_lifetime_rib(
+                            LifetimeRibKind::AnonymousPassThrough(ty.id),
+                            |this| {
+                                this.visit_generic_param_vec(&bare_fn.generic_params, false);
+                                visit::walk_fn_decl(this, &bare_fn.decl);
+                            },
+                        );
                     },
                 );
                 self.diagnostic_metadata.current_trait_object = prev;
@@ -604,7 +626,11 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
         self.with_generic_param_rib(
             &tref.bound_generic_params,
             NormalRibKind,
-            LifetimeRibKind::Generics { kind: LifetimeBinderKind::PolyTrait, span },
+            LifetimeRibKind::Generics {
+                parent: tref.trait_ref.ref_id,
+                kind: LifetimeBinderKind::PolyTrait,
+                span,
+            },
             |this| {
                 this.visit_generic_param_vec(&tref.bound_generic_params, false);
                 this.smart_resolve_path(
@@ -625,6 +651,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
                         &generics.params,
                         ItemRibKind(HasGenericParams::Yes),
                         LifetimeRibKind::Generics {
+                            parent: foreign_item.id,
                             kind: LifetimeBinderKind::Item,
                             span: generics.span,
                         },
@@ -638,6 +665,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
                         &generics.params,
                         ItemRibKind(HasGenericParams::Yes),
                         LifetimeRibKind::Generics {
+                            parent: foreign_item.id,
                             kind: LifetimeBinderKind::Function,
                             span: generics.span,
                         },
@@ -655,13 +683,13 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
             }
         }
     }
-    fn visit_fn(&mut self, fn_kind: FnKind<'ast>, sp: Span, _: NodeId) {
+    fn visit_fn(&mut self, fn_kind: FnKind<'ast>, sp: Span, fn_id: NodeId) {
         let rib_kind = match fn_kind {
             // Bail if the function is foreign, and thus cannot validly have
             // a body, or if there's no body for some other reason.
             FnKind::Fn(FnCtxt::Foreign, _, sig, _, generics, _)
             | FnKind::Fn(_, _, sig, _, generics, None) => {
-                self.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough, |this| {
+                self.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough(fn_id), |this| {
                     // We don't need to deal with patterns in parameters, because
                     // they are not possible for foreign or bodiless functions.
                     this.visit_fn_header(&sig.header);
@@ -691,20 +719,50 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
                     this.visit_generics(generics);
                 }
 
-                if async_node_id.is_some() {
+                if let Some(async_node_id) = async_node_id {
                     // In `async fn`, argument-position elided lifetimes
                     // must be transformed into fresh generic parameters so that
                     // they can be applied to the opaque `impl Trait` return type.
-                    this.with_lifetime_rib(LifetimeRibKind::AnonymousCreateParameter, |this| {
-                        // Add each argument to the rib.
-                        this.resolve_params(&declaration.inputs)
-                    });
+                    this.with_lifetime_rib(
+                        LifetimeRibKind::AnonymousCreateParameter(fn_id),
+                        |this| {
+                            // Add each argument to the rib.
+                            this.resolve_params(&declaration.inputs)
+                        },
+                    );
 
-                    this.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough, |this| {
-                        visit::walk_fn_ret_ty(this, &declaration.output)
-                    });
+                    // Construct the list of in-scope lifetime parameters for async lowering.
+                    // We include all lifetime parameters, either named or "Fresh".
+                    // The order of those parameters does not matter, as long as it is
+                    // deterministic.
+                    let mut extra_lifetime_params =
+                        this.r.extra_lifetime_params_map.get(&fn_id).cloned().unwrap_or_default();
+                    for rib in this.lifetime_ribs.iter().rev() {
+                        extra_lifetime_params.extend(
+                            rib.bindings
+                                .iter()
+                                .map(|(&ident, &(node_id, res))| (ident, node_id, res)),
+                        );
+                        match rib.kind {
+                            LifetimeRibKind::Item => break,
+                            LifetimeRibKind::AnonymousCreateParameter(id) => {
+                                if let Some(earlier_fresh) =
+                                    this.r.extra_lifetime_params_map.get(&id)
+                                {
+                                    extra_lifetime_params.extend(earlier_fresh);
+                                }
+                            }
+                            _ => {}
+                        }
+                    }
+                    this.r.extra_lifetime_params_map.insert(async_node_id, extra_lifetime_params);
+
+                    this.with_lifetime_rib(
+                        LifetimeRibKind::AnonymousPassThrough(async_node_id),
+                        |this| visit::walk_fn_ret_ty(this, &declaration.output),
+                    );
                 } else {
-                    this.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough, |this| {
+                    this.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough(fn_id), |this| {
                         // Add each argument to the rib.
                         this.resolve_params(&declaration.inputs);
 
@@ -716,13 +774,12 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
                 // Be sure not to set this until the function signature has been resolved.
                 let previous_state = replace(&mut this.in_func_body, true);
                 // Resolve the function body, potentially inside the body of an async closure
-                this.with_lifetime_rib(
-                    LifetimeRibKind::AnonymousPassThrough,
-                    |this| match fn_kind {
+                this.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough(fn_id), |this| {
+                    match fn_kind {
                         FnKind::Fn(.., body) => walk_list!(this, visit_block, body),
                         FnKind::Closure(_, body) => this.visit_expr(body),
-                    },
-                );
+                    }
+                });
 
                 debug!("(resolving function) leaving function");
                 this.in_func_body = previous_state;
@@ -801,10 +858,10 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
         if let Some(ref args) = path_segment.args {
             match &**args {
                 GenericArgs::AngleBracketed(..) => visit::walk_generic_args(self, path_span, args),
-                GenericArgs::Parenthesized(..) => self
-                    .with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough, |this| {
-                        visit::walk_generic_args(this, path_span, args)
-                    }),
+                GenericArgs::Parenthesized(..) => self.with_lifetime_rib(
+                    LifetimeRibKind::AnonymousPassThrough(path_segment.id),
+                    |this| visit::walk_generic_args(this, path_span, args),
+                ),
             }
         }
     }
@@ -830,12 +887,16 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
                 this.with_generic_param_rib(
                     &bound_generic_params,
                     NormalRibKind,
-                    LifetimeRibKind::Generics { kind: LifetimeBinderKind::WhereBound, span },
+                    LifetimeRibKind::Generics {
+                        parent: bounded_ty.id,
+                        kind: LifetimeBinderKind::WhereBound,
+                        span,
+                    },
                     |this| {
                         this.visit_generic_param_vec(&bound_generic_params, false);
                         this.visit_ty(bounded_ty);
                         for bound in bounds {
-                            this.visit_param_bound(bound)
+                            this.visit_param_bound(bound, BoundKind::Bound)
                         }
                     },
                 );
@@ -897,7 +958,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
             ident,
             ns,
             &self.parent_scope,
-            Finalize::No,
+            None,
             &self.ribs[ns],
             None,
         )
@@ -907,8 +968,8 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
         &mut self,
         ident: Ident,
         ns: Namespace,
-        finalize: Finalize,
-        unusable_binding: Option<&'a NameBinding<'a>>,
+        finalize: Option<Finalize>,
+        ignore_binding: Option<&'a NameBinding<'a>>,
     ) -> Option<LexicalScopeBinding<'a>> {
         self.r.resolve_ident_in_lexical_scope(
             ident,
@@ -916,7 +977,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
             &self.parent_scope,
             finalize,
             &self.ribs[ns],
-            unusable_binding,
+            ignore_binding,
         )
     }
 
@@ -924,7 +985,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
         &mut self,
         path: &[Segment],
         opt_ns: Option<Namespace>, // `None` indicates a module path in import
-        finalize: Finalize,
+        finalize: Option<Finalize>,
     ) -> PathResult<'a> {
         self.r.resolve_path_with_ribs(
             path,
@@ -1026,12 +1087,12 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                 match param.kind {
                     GenericParamKind::Lifetime => {
                         for bound in &param.bounds {
-                            this.visit_param_bound(bound);
+                            this.visit_param_bound(bound, BoundKind::Bound);
                         }
                     }
                     GenericParamKind::Type { ref default } => {
                         for bound in &param.bounds {
-                            this.visit_param_bound(bound);
+                            this.visit_param_bound(bound, BoundKind::Bound);
                         }
 
                         if let Some(ref ty) = default {
@@ -1053,14 +1114,18 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
 
                         this.ribs[TypeNS].push(Rib::new(ConstParamTyRibKind));
                         this.ribs[ValueNS].push(Rib::new(ConstParamTyRibKind));
-                        this.visit_ty(ty);
+                        this.with_lifetime_rib(LifetimeRibKind::ConstGeneric, |this| {
+                            this.visit_ty(ty)
+                        });
                         this.ribs[TypeNS].pop().unwrap();
                         this.ribs[ValueNS].pop().unwrap();
 
                         if let Some(ref expr) = default {
                             this.ribs[TypeNS].push(forward_ty_ban_rib);
                             this.ribs[ValueNS].push(forward_const_ban_rib);
-                            this.visit_anon_const(expr);
+                            this.with_lifetime_rib(LifetimeRibKind::ConstGeneric, |this| {
+                                this.resolve_anon_const(expr, IsRepeatExpr::No)
+                            });
                             forward_const_ban_rib = this.ribs[ValueNS].pop().unwrap();
                             forward_ty_ban_rib = this.ribs[TypeNS].pop().unwrap();
                         }
@@ -1092,6 +1157,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
         let ident = lifetime.ident;
 
         if ident.name == kw::StaticLifetime {
+            self.record_lifetime_res(lifetime.id, LifetimeRes::Static);
             return;
         }
 
@@ -1103,12 +1169,24 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
         for i in &mut indices {
             let rib = &self.lifetime_ribs[i];
             let normalized_ident = ident.normalize_to_macros_2_0();
-            if let Some(_) = rib.bindings.get_key_value(&normalized_ident) {
+            if let Some(&(_, region)) = rib.bindings.get(&normalized_ident) {
+                self.record_lifetime_res(lifetime.id, region);
                 return;
             }
 
-            if let LifetimeRibKind::Item = rib.kind {
-                break;
+            match rib.kind {
+                LifetimeRibKind::Item => break,
+                LifetimeRibKind::ConstGeneric => {
+                    self.emit_non_static_lt_in_const_generic_error(lifetime);
+                    self.r.lifetimes_res_map.insert(lifetime.id, LifetimeRes::Error);
+                    return;
+                }
+                LifetimeRibKind::AnonConst => {
+                    self.maybe_emit_forbidden_non_static_lifetime_error(lifetime);
+                    self.r.lifetimes_res_map.insert(lifetime.id, LifetimeRes::Error);
+                    return;
+                }
+                _ => {}
             }
         }
 
@@ -1123,6 +1201,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
         }
 
         self.emit_undeclared_lifetime_error(lifetime, outer_res);
+        self.record_lifetime_res(lifetime.id, LifetimeRes::Error);
     }
 
     #[tracing::instrument(level = "debug", skip(self))]
@@ -1132,6 +1211,10 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
         for i in (0..self.lifetime_ribs.len()).rev() {
             let rib = &mut self.lifetime_ribs[i];
             match rib.kind {
+                LifetimeRibKind::AnonymousCreateParameter(item_node_id) => {
+                    self.create_fresh_lifetime(lifetime.id, lifetime.ident, item_node_id);
+                    return;
+                }
                 LifetimeRibKind::AnonymousReportError => {
                     let (msg, note) = if elided {
                         (
@@ -1151,35 +1234,73 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                     .span_label(lifetime.ident.span, note)
                     .emit();
 
+                    self.record_lifetime_res(lifetime.id, LifetimeRes::Error);
+                    return;
+                }
+                LifetimeRibKind::AnonymousPassThrough(node_id) => {
+                    self.record_lifetime_res(
+                        lifetime.id,
+                        LifetimeRes::Anonymous { binder: node_id, elided },
+                    );
                     return;
                 }
-                LifetimeRibKind::AnonymousCreateParameter
-                | LifetimeRibKind::AnonymousPassThrough
-                | LifetimeRibKind::Item => return,
+                LifetimeRibKind::Item => break,
                 _ => {}
             }
         }
+        // This resolution is wrong, it passes the work to HIR lifetime resolution.
+        // We cannot use `LifetimeRes::Error` because we do not emit a diagnostic.
+        self.record_lifetime_res(
+            lifetime.id,
+            LifetimeRes::Anonymous { binder: DUMMY_NODE_ID, elided },
+        );
     }
 
     #[tracing::instrument(level = "debug", skip(self))]
     fn resolve_elided_lifetime(&mut self, anchor_id: NodeId, span: Span) {
         let id = self.r.next_node_id();
+        self.record_lifetime_res(
+            anchor_id,
+            LifetimeRes::ElidedAnchor { start: id, end: NodeId::from_u32(id.as_u32() + 1) },
+        );
+
         let lt = Lifetime { id, ident: Ident::new(kw::UnderscoreLifetime, span) };
         self.resolve_anonymous_lifetime(&lt, true);
     }
 
     #[tracing::instrument(level = "debug", skip(self))]
+    fn create_fresh_lifetime(&mut self, id: NodeId, ident: Ident, item_node_id: NodeId) {
+        debug_assert_eq!(ident.name, kw::UnderscoreLifetime);
+        debug!(?ident.span);
+        let item_def_id = self.r.local_def_id(item_node_id);
+        let def_node_id = self.r.next_node_id();
+        let def_id = self.r.create_def(
+            item_def_id,
+            def_node_id,
+            DefPathData::LifetimeNs(kw::UnderscoreLifetime),
+            self.parent_scope.expansion.to_expn_id(),
+            ident.span,
+        );
+        debug!(?def_id);
+
+        let region = LifetimeRes::Fresh { param: def_id, binder: item_node_id };
+        self.record_lifetime_res(id, region);
+        self.r.extra_lifetime_params_map.entry(item_node_id).or_insert_with(Vec::new).push((
+            ident,
+            def_node_id,
+            region,
+        ));
+    }
+
+    #[tracing::instrument(level = "debug", skip(self))]
     fn resolve_elided_lifetimes_in_path(
         &mut self,
         path_id: NodeId,
         partial_res: PartialRes,
         path: &[Segment],
         source: PathSource<'_>,
-        finalize: Finalize,
+        path_span: Span,
     ) {
-        let Some(path_span) = finalize.path_span() else {
-            return;
-        };
         let proj_start = path.len() - partial_res.unresolved_segments();
         for (i, segment) in path.iter().enumerate() {
             if segment.has_lifetime_args {
@@ -1222,7 +1343,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                 | PathSource::Struct
                 | PathSource::TupleStruct(..) => false,
             };
-            let mut error = false;
+            let mut res = LifetimeRes::Error;
             for rib in self.lifetime_ribs.iter().rev() {
                 match rib.kind {
                     // In create-parameter mode we error here because we don't want to support
@@ -1231,8 +1352,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                     //
                     //     impl Foo for std::cell::Ref<u32> // note lack of '_
                     //     async fn foo(_: std::cell::Ref<u32>) { ... }
-                    LifetimeRibKind::AnonymousCreateParameter => {
-                        error = true;
+                    LifetimeRibKind::AnonymousCreateParameter(_) => {
                         break;
                     }
                     // `PassThrough` is the normal case.
@@ -1241,13 +1361,31 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                     // `PathSegment`, for which there is no associated `'_` or `&T` with no explicit
                     // lifetime. Instead, we simply create an implicit lifetime, which will be checked
                     // later, at which point a suitable error will be emitted.
-                    LifetimeRibKind::AnonymousPassThrough
-                    | LifetimeRibKind::AnonymousReportError
-                    | LifetimeRibKind::Item => break,
+                    LifetimeRibKind::AnonymousPassThrough(binder) => {
+                        res = LifetimeRes::Anonymous { binder, elided: true };
+                        break;
+                    }
+                    LifetimeRibKind::AnonymousReportError | LifetimeRibKind::Item => {
+                        // FIXME(cjgillot) This resolution is wrong, but this does not matter
+                        // since these cases are erroneous anyway.  Lifetime resolution should
+                        // emit a "missing lifetime specifier" diagnostic.
+                        res = LifetimeRes::Anonymous { binder: DUMMY_NODE_ID, elided: true };
+                        break;
+                    }
                     _ => {}
                 }
             }
 
+            let node_ids = self.r.next_node_ids(expected_lifetimes);
+            self.record_lifetime_res(
+                segment_id,
+                LifetimeRes::ElidedAnchor { start: node_ids.start, end: node_ids.end },
+            );
+            for i in 0..expected_lifetimes {
+                let id = node_ids.start.plus(i);
+                self.record_lifetime_res(id, res);
+            }
+
             if !missing {
                 continue;
             }
@@ -1261,7 +1399,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                 // originating from macros, since the segment's span might be from a macro arg.
                 segment.ident.span.find_ancestor_inside(path_span).unwrap_or(path_span)
             };
-            if error {
+            if let LifetimeRes::Error = res {
                 let sess = self.r.session;
                 let mut err = rustc_errors::struct_span_err!(
                     sess,
@@ -1296,9 +1434,19 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
         }
     }
 
+    #[tracing::instrument(level = "debug", skip(self))]
+    fn record_lifetime_res(&mut self, id: NodeId, res: LifetimeRes) {
+        if let Some(prev_res) = self.r.lifetimes_res_map.insert(id, res) {
+            panic!(
+                "lifetime {:?} resolved multiple times ({:?} before, {:?} now)",
+                id, prev_res, res
+            )
+        }
+    }
+
     /// Searches the current set of local scopes for labels. Returns the `NodeId` of the resolved
     /// label and reports an error if the label is not found or is unreachable.
-    fn resolve_label(&self, mut label: Ident) -> Option<NodeId> {
+    fn resolve_label(&mut self, mut label: Ident) -> Option<NodeId> {
         let mut suggestion = None;
 
         // Preserve the original span so that errors contain "in this macro invocation"
@@ -1318,6 +1466,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
 
             let ident = label.normalize_to_macro_rules();
             if let Some((ident, id)) = rib.bindings.get_key_value(&ident) {
+                let definition_span = ident.span;
                 return if self.is_label_valid_from_rib(i) {
                     Some(*id)
                 } else {
@@ -1325,7 +1474,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                         original_span,
                         ResolutionError::UnreachableLabel {
                             name: label.name,
-                            definition_span: ident.span,
+                            definition_span,
                             suggestion,
                         },
                     );
@@ -1379,7 +1528,11 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
             this.with_generic_param_rib(
                 &generics.params,
                 ItemRibKind(HasGenericParams::Yes),
-                LifetimeRibKind::Generics { kind: LifetimeBinderKind::Item, span: generics.span },
+                LifetimeRibKind::Generics {
+                    parent: item.id,
+                    kind: LifetimeBinderKind::Item,
+                    span: generics.span,
+                },
                 |this| {
                     let item_def_id = this.r.local_def_id(item.id).to_def_id();
                     this.with_self_rib(
@@ -1420,8 +1573,8 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                         report_error(self, ns);
                     }
                     Some(LexicalScopeBinding::Item(binding)) => {
-                        if let Some(LexicalScopeBinding::Res(..)) = self
-                            .resolve_ident_in_lexical_scope(ident, ns, Finalize::No, Some(binding))
+                        if let Some(LexicalScopeBinding::Res(..)) =
+                            self.resolve_ident_in_lexical_scope(ident, ns, None, Some(binding))
                         {
                             report_error(self, ns);
                         }
@@ -1446,6 +1599,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                     &generics.params,
                     ItemRibKind(HasGenericParams::Yes),
                     LifetimeRibKind::Generics {
+                        parent: item.id,
                         kind: LifetimeBinderKind::Item,
                         span: generics.span,
                     },
@@ -1458,6 +1612,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                     &generics.params,
                     ItemRibKind(HasGenericParams::Yes),
                     LifetimeRibKind::Generics {
+                        parent: item.id,
                         kind: LifetimeBinderKind::Function,
                         span: generics.span,
                     },
@@ -1487,6 +1642,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                     &generics.params,
                     ItemRibKind(HasGenericParams::Yes),
                     LifetimeRibKind::Generics {
+                        parent: item.id,
                         kind: LifetimeBinderKind::Item,
                         span: generics.span,
                     },
@@ -1496,7 +1652,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                             Res::SelfTy { trait_: Some(local_def_id), alias_to: None },
                             |this| {
                                 this.visit_generics(generics);
-                                walk_list!(this, visit_param_bound, bounds);
+                                walk_list!(this, visit_param_bound, bounds, BoundKind::SuperTraits);
 
                                 let walk_assoc_item =
                                     |this: &mut Self,
@@ -1506,7 +1662,11 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                                         this.with_generic_param_rib(
                                             &generics.params,
                                             AssocItemRibKind,
-                                            LifetimeRibKind::Generics { span: generics.span, kind },
+                                            LifetimeRibKind::Generics {
+                                                parent: item.id,
+                                                span: generics.span,
+                                                kind,
+                                            },
                                             |this| {
                                                 visit::walk_assoc_item(this, item, AssocCtxt::Trait)
                                             },
@@ -1571,6 +1731,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                     &generics.params,
                     ItemRibKind(HasGenericParams::Yes),
                     LifetimeRibKind::Generics {
+                        parent: item.id,
                         kind: LifetimeBinderKind::Item,
                         span: generics.span,
                     },
@@ -1580,7 +1741,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                             Res::SelfTy { trait_: Some(local_def_id), alias_to: None },
                             |this| {
                                 this.visit_generics(generics);
-                                walk_list!(this, visit_param_bound, bounds);
+                                walk_list!(this, visit_param_bound, bounds, BoundKind::Bound);
                             },
                         );
                     },
@@ -1696,7 +1857,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                     "invalid lifetime parameter name: `{}`",
                     param.ident,
                 )
-                .span_label(param.ident.span, format!("'static is a reserved lifetime name"))
+                .span_label(param.ident.span, "'static is a reserved lifetime name")
                 .emit();
                 continue;
             }
@@ -1708,7 +1869,10 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                 GenericParamKind::Type { .. } => (&mut function_type_rib, DefKind::TyParam),
                 GenericParamKind::Const { .. } => (&mut function_value_rib, DefKind::ConstParam),
                 GenericParamKind::Lifetime => {
-                    function_lifetime_rib.bindings.insert(ident, ());
+                    let LifetimeRibKind::Generics { parent, .. } = lifetime_kind else { panic!() };
+                    let res = LifetimeRes::Param { param: def_id, binder: parent };
+                    self.record_lifetime_res(param.id, res);
+                    function_lifetime_rib.bindings.insert(ident, (param.id, res));
                     continue;
                 }
             };
@@ -1812,7 +1976,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                 None,
                 &path,
                 PathSource::Trait(AliasPossibility::No),
-                Finalize::SimplePath(trait_ref.ref_id, trait_ref.path.span),
+                Finalize::new(trait_ref.ref_id, trait_ref.path.span),
             );
             if let Some(def_id) = res.base_res().opt_def_id() {
                 new_id = Some(def_id);
@@ -1849,10 +2013,10 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
     ) {
         debug!("resolve_implementation");
         // If applicable, create a rib for the type parameters.
-        self.with_generic_param_rib(&generics.params, ItemRibKind(HasGenericParams::Yes), LifetimeRibKind::Generics { span: generics.span, kind: LifetimeBinderKind::ImplBlock }, |this| {
+        self.with_generic_param_rib(&generics.params, ItemRibKind(HasGenericParams::Yes), LifetimeRibKind::Generics { span: generics.span, parent: item_id, kind: LifetimeBinderKind::ImplBlock }, |this| {
             // Dummy self type for better errors if `Self` is used in the trait path.
             this.with_self_rib(Res::SelfTy { trait_: None, alias_to: None }, |this| {
-                this.with_lifetime_rib(LifetimeRibKind::AnonymousCreateParameter, |this| {
+                this.with_lifetime_rib(LifetimeRibKind::AnonymousCreateParameter(item_id), |this| {
                     // Resolve the trait reference, if necessary.
                     this.with_optional_trait_ref(opt_trait_reference.as_ref(), |this, trait_id| {
                         let item_def_id = this.r.local_def_id(item_id);
@@ -1876,7 +2040,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                             this.visit_generics(generics);
 
                             // Resolve the items within the impl.
-                            this.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough,
+                            this.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough(item_id),
                                 |this| {
                                     this.with_current_self_type(self_type, |this| {
                                         this.with_self_rib_ns(ValueNS, Res::SelfCtor(item_def_id), |this| {
@@ -1921,7 +2085,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                                                         this.with_generic_param_rib(
                                                             &generics.params,
                                                             AssocItemRibKind,
-                                                            LifetimeRibKind::Generics { span: generics.span, kind: LifetimeBinderKind::Function },
+                                                            LifetimeRibKind::Generics { parent: item.id, span: generics.span, kind: LifetimeBinderKind::Function },
                                                             |this| {
                                                                 // If this is a trait impl, ensure the method
                                                                 // exists in trait
@@ -1950,7 +2114,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                                                         this.with_generic_param_rib(
                                                             &generics.params,
                                                             AssocItemRibKind,
-                                                            LifetimeRibKind::Generics { span: generics.span, kind: LifetimeBinderKind::Item },
+                                                            LifetimeRibKind::Generics { parent: item.id, span: generics.span, kind: LifetimeBinderKind::Item },
                                                             |this| {
                                                                 // If this is a trait impl, ensure the type
                                                                 // exists in trait
@@ -1996,7 +2160,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
         span: Span,
         err: F,
     ) where
-        F: FnOnce(Ident, &str, Option<Symbol>) -> ResolutionError<'_>,
+        F: FnOnce(Ident, String, Option<Symbol>) -> ResolutionError<'a>,
     {
         // If there is a TraitRef in scope for an impl, then the method must be in the trait.
         let Some((module, _)) = &self.current_trait_ref else { return; };
@@ -2020,7 +2184,8 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
             // We could not find the method: report an error.
             let candidate = self.find_similarly_named_assoc_item(ident.name, kind);
             let path = &self.current_trait_ref.as_ref().unwrap().1.path;
-            self.report_error(span, err(ident, &path_names_to_string(path), candidate));
+            let path_names = path_names_to_string(path);
+            self.report_error(span, err(ident, path_names, candidate));
             return;
         };
 
@@ -2044,13 +2209,14 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
             AssocItemKind::TyAlias(..) => (rustc_errors::error_code!(E0325), "type"),
             AssocItemKind::MacCall(..) => span_bug!(span, "unexpanded macro"),
         };
+        let trait_path = path_names_to_string(path);
         self.report_error(
             span,
             ResolutionError::TraitImplMismatch {
                 name: ident.name,
                 kind,
                 code,
-                trait_path: path_names_to_string(path),
+                trait_path,
                 trait_item_span: binding.span,
             },
         );
@@ -2165,16 +2331,16 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
         }
 
         // 3) Report all missing variables we found.
-        let mut missing_vars = missing_vars.iter_mut().collect::<Vec<_>>();
-        missing_vars.sort_by_key(|(sym, _err)| sym.as_str());
+        let mut missing_vars = missing_vars.into_iter().collect::<Vec<_>>();
+        missing_vars.sort_by_key(|&(sym, ref _err)| sym);
 
-        for (name, mut v) in missing_vars {
-            if inconsistent_vars.contains_key(name) {
+        for (name, mut v) in missing_vars.into_iter() {
+            if inconsistent_vars.contains_key(&name) {
                 v.could_be_path = false;
             }
             self.report_error(
                 *v.origin.iter().next().unwrap(),
-                ResolutionError::VariableNotBoundInPattern(v),
+                ResolutionError::VariableNotBoundInPattern(v, self.parent_scope),
             );
         }
 
@@ -2484,7 +2650,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
             qself,
             &Segment::from_path(path),
             source,
-            Finalize::SimplePath(id, path.span),
+            Finalize::new(id, path.span),
         );
     }
 
@@ -2503,8 +2669,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
         );
         let ns = source.namespace();
 
-        let (id, path_span) =
-            finalize.node_id_and_path_span().expect("unexpected speculative resolution");
+        let Finalize { node_id, path_span, .. } = finalize;
         let report_errors = |this: &mut Self, res: Option<Res>| {
             if this.should_report_errs() {
                 let (err, candidates) =
@@ -2618,7 +2783,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                 if ns == ValueNS {
                     let item_name = path.last().unwrap().ident;
                     let traits = self.traits_in_scope(item_name, ns);
-                    self.r.trait_map.insert(id, traits);
+                    self.r.trait_map.insert(node_id, traits);
                 }
 
                 if PrimTy::from_name(path[0].ident.name).is_some() {
@@ -2627,7 +2792,7 @@ 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), Finalize::No)
+                        self.resolve_path(&std_path, Some(ns), None)
                     {
                         // Check if we wrote `str::from_utf8` instead of `std::str::from_utf8`
                         let item_span =
@@ -2654,10 +2819,10 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
 
         if !matches!(source, PathSource::TraitItem(..)) {
             // Avoid recording definition of `A::B` in `<T as A>::B::C`.
-            self.r.record_partial_res(id, partial_res);
+            self.r.record_partial_res(node_id, partial_res);
+            self.resolve_elided_lifetimes_in_path(node_id, partial_res, path, source, path_span);
         }
 
-        self.resolve_elided_lifetimes_in_path(id, partial_res, path, source, finalize);
         partial_res
     }
 
@@ -2676,7 +2841,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
     /// A wrapper around [`Resolver::report_error`].
     ///
     /// This doesn't emit errors for function bodies if this is rustdoc.
-    fn report_error(&self, span: Span, resolution_error: ResolutionError<'_>) {
+    fn report_error(&mut self, span: Span, resolution_error: ResolutionError<'a>) {
         if self.should_report_errs() {
             self.r.report_error(span, resolution_error);
         }
@@ -2763,21 +2928,12 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
             // the trait (the slice upto and including
             // `qself.position`). And then we recursively resolve that,
             // but with `qself` set to `None`.
-            //
-            // However, setting `qself` to none (but not changing the
-            // span) loses the information about where this path
-            // *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 `Finalize::QPathTrait`).
             let ns = if qself.position + 1 == path.len() { ns } else { TypeNS };
             let partial_res = self.smart_resolve_path_fragment(
                 None,
                 &path[..=qself.position],
                 PathSource::TraitItem(ns),
-                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 }
-                }),
+                Finalize::with_root_span(finalize.node_id, finalize.path_span, qself.path_span),
             );
 
             // The remaining segments (the `C` in our example) will
@@ -2789,7 +2945,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
             )));
         }
 
-        let result = match self.resolve_path(&path, Some(ns), finalize) {
+        let result = match self.resolve_path(&path, Some(ns), Some(finalize)) {
             PathResult::NonModule(path_res) => path_res,
             PathResult::Module(ModuleOrUniformRoot::Module(module)) if !module.is_normal() => {
                 PartialRes::new(module.res().unwrap())
@@ -2827,10 +2983,9 @@ 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), Finalize::No) {
+                match self.resolve_path(&[*path.last().unwrap()], Some(ns), None) {
                     PathResult::NonModule(path_res) => path_res.base_res(),
                     PathResult::Module(ModuleOrUniformRoot::Module(module)) => {
                         module.res().unwrap()
@@ -2840,7 +2995,12 @@ 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, path_span, "unnecessary qualification")
+                self.r.lint_buffer.buffer_lint(
+                    lint,
+                    finalize.node_id,
+                    finalize.path_span,
+                    "unnecessary qualification",
+                )
             }
         }
 
@@ -2923,9 +3083,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
             is_repeat,
             constant.value.is_potential_trivial_const_param(),
             None,
-            |this| {
-                visit::walk_anon_const(this, constant);
-            },
+            |this| visit::walk_anon_const(this, constant),
         );
     }
 
@@ -3076,7 +3234,12 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
             }
             ExprKind::Repeat(ref elem, ref ct) => {
                 self.visit_expr(elem);
-                self.resolve_anon_const(ct, IsRepeatExpr::Yes);
+                self.with_lifetime_rib(LifetimeRibKind::AnonConst, |this| {
+                    this.resolve_anon_const(ct, IsRepeatExpr::Yes)
+                });
+            }
+            ExprKind::ConstBlock(ref ct) => {
+                self.resolve_anon_const(ct, IsRepeatExpr::No);
             }
             ExprKind::Index(ref elem, ref idx) => {
                 self.resolve_expr(elem, Some(expr));
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index d77cc917e2f..3076cc11317 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -3,7 +3,7 @@ use crate::late::lifetimes::{ElisionFailureInfo, LifetimeContext};
 use crate::late::{AliasPossibility, LateResolutionVisitor, RibKind};
 use crate::late::{LifetimeBinderKind, LifetimeRibKind};
 use crate::path_names_to_string;
-use crate::{Finalize, Module, ModuleKind, ModuleOrUniformRoot};
+use crate::{Module, ModuleKind, ModuleOrUniformRoot};
 use crate::{PathResult, PathSource, Segment};
 
 use rustc_ast::visit::FnKind;
@@ -86,7 +86,7 @@ impl ForLifetimeSpanType {
     }
 }
 
-impl<'tcx> Into<MissingLifetimeSpot<'tcx>> for &'tcx hir::Generics<'tcx> {
+impl<'tcx> Into<MissingLifetimeSpot<'tcx>> for &&'tcx hir::Generics<'tcx> {
     fn into(self) -> MissingLifetimeSpot<'tcx> {
         MissingLifetimeSpot::Generics(self)
     }
@@ -189,7 +189,7 @@ 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), Finalize::No) {
+                let mod_prefix = match self.resolve_path(mod_path, Some(TypeNS), None) {
                     PathResult::Module(ModuleOrUniformRoot::Module(module)) => module.res(),
                     _ => None,
                 }
@@ -648,7 +648,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
         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, Finalize::No)
+                self.resolve_path(mod_path, None, None)
             {
                 let resolutions = self.r.resolutions(module).borrow();
                 let targets: Vec<_> =
@@ -1183,9 +1183,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
         ident: Symbol,
         kind: &AssocItemKind,
     ) -> Option<Symbol> {
-        let Some((module, _)) = &self.current_trait_ref else {
-            return None;
-        };
+        let (module, _) = self.current_trait_ref.as_ref()?;
         if ident == kw::Underscore {
             // We do nothing for `_`.
             return None;
@@ -1364,7 +1362,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), Finalize::No)
+                self.resolve_path(mod_path, Some(TypeNS), None)
             {
                 self.r.add_module_candidates(module, &mut names, &filter_fn);
             }
@@ -1824,7 +1822,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
 
         for rib in self.lifetime_ribs.iter().rev() {
             match rib.kind {
-                LifetimeRibKind::Generics { span, kind } => {
+                LifetimeRibKind::Generics { parent: _, span, kind } => {
                     if !span.can_be_used_for_suggestions() && suggest_note {
                         suggest_note = false; // Avoid displaying the same help multiple times.
                         err.span_label(
@@ -1888,6 +1886,37 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
 
         err.emit();
     }
+
+    crate fn emit_non_static_lt_in_const_generic_error(&self, lifetime_ref: &ast::Lifetime) {
+        struct_span_err!(
+            self.r.session,
+            lifetime_ref.ident.span,
+            E0771,
+            "use of non-static lifetime `{}` in const generic",
+            lifetime_ref.ident
+        )
+        .note(
+            "for more information, see issue #74052 \
+            <https://github.com/rust-lang/rust/issues/74052>",
+        )
+        .emit();
+    }
+
+    /// Non-static lifetimes are prohibited in anonymous constants under `min_const_generics`.
+    /// This function will emit an error if `generic_const_exprs` is not enabled, the body identified by
+    /// `body_id` is an anonymous constant and `lifetime_ref` is non-static.
+    crate fn maybe_emit_forbidden_non_static_lifetime_error(&self, lifetime_ref: &ast::Lifetime) {
+        let feature_active = self.r.session.features_untracked().generic_const_exprs;
+        if !feature_active {
+            feature_err(
+                &self.r.session.parse_sess,
+                sym::generic_const_exprs,
+                lifetime_ref.ident.span,
+                "a non-static lifetime is not allowed in a `const`",
+            )
+            .emit();
+        }
+    }
 }
 
 impl<'tcx> LifetimeContext<'_, 'tcx> {
@@ -1984,24 +2013,6 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
         }
     }
 
-    // FIXME(const_generics): This patches over an ICE caused by non-'static lifetimes in const
-    // generics. We are disallowing this until we can decide on how we want to handle non-'static
-    // lifetimes in const generics. See issue #74052 for discussion.
-    crate fn emit_non_static_lt_in_const_generic_error(&self, lifetime_ref: &hir::Lifetime) {
-        let mut err = struct_span_err!(
-            self.tcx.sess,
-            lifetime_ref.span,
-            E0771,
-            "use of non-static lifetime `{}` in const generic",
-            lifetime_ref
-        );
-        err.note(
-            "for more information, see issue #74052 \
-            <https://github.com/rust-lang/rust/issues/74052>",
-        );
-        err.emit();
-    }
-
     crate fn is_trait_ref_fn_scope(&mut self, trait_ref: &'tcx hir::PolyTraitRef<'tcx>) -> bool {
         if let def::Res::Def(_, did) = trait_ref.trait_ref.path.res {
             if [
@@ -2403,32 +2414,4 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
             _ => unreachable!(),
         }
     }
-
-    /// Non-static lifetimes are prohibited in anonymous constants under `min_const_generics`.
-    /// This function will emit an error if `generic_const_exprs` is not enabled, the body identified by
-    /// `body_id` is an anonymous constant and `lifetime_ref` is non-static.
-    crate fn maybe_emit_forbidden_non_static_lifetime_error(
-        &self,
-        body_id: hir::BodyId,
-        lifetime_ref: &'tcx hir::Lifetime,
-    ) {
-        let is_anon_const = matches!(
-            self.tcx.def_kind(self.tcx.hir().body_owner_def_id(body_id)),
-            hir::def::DefKind::AnonConst
-        );
-        let is_allowed_lifetime = matches!(
-            lifetime_ref.name,
-            hir::LifetimeName::Implicit | hir::LifetimeName::Static | hir::LifetimeName::Underscore
-        );
-
-        if !self.tcx.lazy_normalization() && is_anon_const && !is_allowed_lifetime {
-            feature_err(
-                &self.tcx.sess.parse_sess,
-                sym::generic_const_exprs,
-                lifetime_ref.span,
-                "a non-static lifetime is not allowed in a `const`",
-            )
-            .emit();
-        }
-    }
 }
diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs
index d5f2e2db1e3..787536d2a38 100644
--- a/compiler/rustc_resolve/src/late/lifetimes.rs
+++ b/compiler/rustc_resolve/src/late/lifetimes.rs
@@ -164,8 +164,6 @@ crate struct LifetimeContext<'a, 'tcx> {
     map: &'a mut NamedRegionMap,
     scope: ScopeRef<'a>,
 
-    is_in_const_generic: bool,
-
     /// Indicates that we only care about the definition of a trait. This should
     /// be false if the `Item` we are resolving lifetimes for is not a trait or
     /// we eventually need lifetimes resolve for trait items.
@@ -452,7 +450,6 @@ fn do_resolve(
         tcx,
         map: &mut named_region_map,
         scope: ROOT_SCOPE,
-        is_in_const_generic: false,
         trait_definition_only,
         labels_in_fn: vec![],
         xcrate_object_lifetime_defaults: Default::default(),
@@ -685,8 +682,8 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
         hir_id: hir::HirId,
     ) {
         let name = match fk {
-            intravisit::FnKind::ItemFn(id, _, _, _) => id.name,
-            intravisit::FnKind::Method(id, _, _) => id.name,
+            intravisit::FnKind::ItemFn(id, _, _) => id.name,
+            intravisit::FnKind::Method(id, _) => id.name,
             intravisit::FnKind::Closure => sym::closure,
         };
         let name = name.as_str();
@@ -1266,10 +1263,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
             self.insert_lifetime(lifetime_ref, Region::Static);
             return;
         }
-        if self.is_in_const_generic && lifetime_ref.name != LifetimeName::Error {
-            self.emit_non_static_lt_in_const_generic_error(lifetime_ref);
-            return;
-        }
         self.resolve_lifetime_ref(lifetime_ref);
     }
 
@@ -1335,24 +1328,19 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                 match param.kind {
                     GenericParamKind::Lifetime { .. } => {}
                     GenericParamKind::Type { ref default, .. } => {
-                        walk_list!(this, visit_param_bound, param.bounds);
                         if let Some(ref ty) = default {
                             this.visit_ty(&ty);
                         }
                     }
                     GenericParamKind::Const { ref ty, default } => {
-                        let was_in_const_generic = this.is_in_const_generic;
-                        this.is_in_const_generic = true;
-                        walk_list!(this, visit_param_bound, param.bounds);
                         this.visit_ty(&ty);
                         if let Some(default) = default {
                             this.visit_body(this.tcx.hir().body(default.body));
                         }
-                        this.is_in_const_generic = was_in_const_generic;
                     }
                 }
             }
-            for predicate in generics.where_clause.predicates {
+            for predicate in generics.predicates {
                 match predicate {
                     &hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
                         ref bounded_ty,
@@ -1403,6 +1391,32 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     }) => {
                         this.visit_lifetime(lifetime);
                         walk_list!(this, visit_param_bound, bounds);
+
+                        if lifetime.name != hir::LifetimeName::Static {
+                            for bound in bounds {
+                                let hir::GenericBound::Outlives(ref lt) = bound else {
+                                    continue;
+                                };
+                                if lt.name != hir::LifetimeName::Static {
+                                    continue;
+                                }
+                                this.insert_lifetime(lt, Region::Static);
+                                this.tcx
+                                    .sess
+                                    .struct_span_warn(
+                                        lifetime.span,
+                                        &format!(
+                                            "unnecessary lifetime parameter `{}`",
+                                            lifetime.name.ident(),
+                                        ),
+                                    )
+                                    .help(&format!(
+                                        "you can use the `'static` lifetime directly, in place of `{}`",
+                                        lifetime.name.ident(),
+                                    ))
+                                    .emit();
+                            }
+                        }
                     }
                     &hir::WherePredicate::EqPredicate(hir::WhereEqPredicate {
                         ref lhs_ty,
@@ -1724,10 +1738,8 @@ fn object_lifetime_defaults_for_item<'tcx>(
         GenericParamKind::Type { .. } => {
             let mut set = Set1::Empty;
 
-            add_bounds(&mut set, &param.bounds);
-
             let param_def_id = tcx.hir().local_def_id(param.hir_id);
-            for predicate in generics.where_clause.predicates {
+            for predicate in generics.predicates {
                 // Look for `type: ...` where clauses.
                 let hir::WherePredicate::BoundPredicate(ref data) = *predicate else { continue };
 
@@ -1798,7 +1810,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
             tcx: *tcx,
             map,
             scope: &wrap_scope,
-            is_in_const_generic: self.is_in_const_generic,
             trait_definition_only: self.trait_definition_only,
             labels_in_fn,
             xcrate_object_lifetime_defaults,
@@ -2254,10 +2265,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
         let result = loop {
             match *scope {
                 Scope::Body { id, s } => {
-                    // Non-static lifetimes are prohibited in anonymous constants without
-                    // `generic_const_exprs`.
-                    self.maybe_emit_forbidden_non_static_lifetime_error(id, lifetime_ref);
-
                     outermost_body = Some(id);
                     scope = s;
                 }
@@ -3139,50 +3146,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
 
             // It is a soft error to shadow a lifetime within a parent scope.
             self.check_lifetime_param_for_shadowing(old_scope, &lifetime_i);
-
-            for bound in lifetime_i.bounds {
-                match bound {
-                    hir::GenericBound::Outlives(ref lt) => match lt.name {
-                        hir::LifetimeName::Underscore => {
-                            self.tcx.sess.delay_span_bug(
-                                lt.span,
-                                "use of `'_` in illegal place, but not caught by lowering",
-                            );
-                        }
-                        hir::LifetimeName::Static => {
-                            self.insert_lifetime(lt, Region::Static);
-                            self.tcx
-                                .sess
-                                .struct_span_warn(
-                                    lifetime_i.span.to(lt.span),
-                                    &format!(
-                                        "unnecessary lifetime parameter `{}`",
-                                        lifetime_i.name.ident(),
-                                    ),
-                                )
-                                .help(&format!(
-                                    "you can use the `'static` lifetime directly, in place of `{}`",
-                                    lifetime_i.name.ident(),
-                                ))
-                                .emit();
-                        }
-                        hir::LifetimeName::Param(_) | hir::LifetimeName::Implicit => {
-                            self.resolve_lifetime_ref(lt);
-                        }
-                        hir::LifetimeName::ImplicitObjectLifetimeDefault => {
-                            self.tcx.sess.delay_span_bug(
-                                lt.span,
-                                "lowering generated `ImplicitObjectLifetimeDefault` \
-                                 outside of an object type",
-                            );
-                        }
-                        hir::LifetimeName::Error => {
-                            // No need to do anything, error already reported.
-                        }
-                    },
-                    _ => bug!(),
-                }
-            }
         }
     }
 
@@ -3341,18 +3304,6 @@ fn insert_late_bound_lifetimes(
     // ignore binders here and scrape up all names we see.
     let mut appears_in_where_clause = AllCollector::default();
     appears_in_where_clause.visit_generics(generics);
-
-    for param in generics.params {
-        if let hir::GenericParamKind::Lifetime { .. } = param.kind {
-            if !param.bounds.is_empty() {
-                // `'a: 'b` means both `'a` and `'b` are referenced
-                appears_in_where_clause
-                    .regions
-                    .insert(hir::LifetimeName::Param(param.name.normalize_to_macros_2_0()));
-            }
-        }
-    }
-
     debug!(?appears_in_where_clause.regions);
 
     // Late bound regions are those that:
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 4dfb7aef86f..92e55a0ccb2 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -29,7 +29,7 @@ use rustc_arena::{DroplessArena, TypedArena};
 use rustc_ast::node_id::NodeMap;
 use rustc_ast::{self as ast, NodeId, CRATE_NODE_ID};
 use rustc_ast::{AngleBracketedArg, Crate, Expr, ExprKind, GenericArg, GenericArgs, LitKind, Path};
-use rustc_ast_lowering::ResolverAstLowering;
+use rustc_ast_lowering::{LifetimeRes, ResolverAstLowering};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
 use rustc_data_structures::intern::Interned;
 use rustc_data_structures::sync::Lrc;
@@ -143,9 +143,9 @@ enum ScopeSet<'a> {
 /// but not for late resolution yet.
 #[derive(Clone, Copy, Debug)]
 pub struct ParentScope<'a> {
-    module: Module<'a>,
+    pub module: Module<'a>,
     expansion: LocalExpnId,
-    macro_rules: MacroRulesScopeRef<'a>,
+    pub macro_rules: MacroRulesScopeRef<'a>,
     derives: &'a [ast::Path],
 }
 
@@ -201,13 +201,13 @@ enum ResolutionError<'a> {
     /// parameter list.
     NameAlreadyUsedInParameterList(Symbol, Span),
     /// Error E0407: method is not a member of trait.
-    MethodNotMemberOfTrait(Ident, &'a str, Option<Symbol>),
+    MethodNotMemberOfTrait(Ident, String, Option<Symbol>),
     /// Error E0437: type is not a member of trait.
-    TypeNotMemberOfTrait(Ident, &'a str, Option<Symbol>),
+    TypeNotMemberOfTrait(Ident, String, Option<Symbol>),
     /// Error E0438: const is not a member of trait.
-    ConstNotMemberOfTrait(Ident, &'a str, Option<Symbol>),
+    ConstNotMemberOfTrait(Ident, String, Option<Symbol>),
     /// Error E0408: variable `{}` is not bound in all patterns.
-    VariableNotBoundInPattern(&'a BindingError),
+    VariableNotBoundInPattern(BindingError, ParentScope<'a>),
     /// Error E0409: variable `{}` is bound in inconsistent ways within the same match arm.
     VariableBoundWithDifferentMode(Symbol, Span),
     /// Error E0415: identifier is bound more than once in this parameter list.
@@ -901,6 +901,10 @@ pub struct Resolver<'a> {
     import_res_map: NodeMap<PerNS<Option<Res>>>,
     /// Resolutions for labels (node IDs of their corresponding blocks or loops).
     label_res_map: NodeMap<NodeId>,
+    /// Resolutions for lifetimes.
+    lifetimes_res_map: NodeMap<LifetimeRes>,
+    /// Lifetime parameters that lowering will have to introduce.
+    extra_lifetime_params_map: NodeMap<Vec<(Ident, NodeId, LifetimeRes)>>,
 
     /// `CrateNum` resolutions of `extern crate` items.
     extern_crate_map: FxHashMap<LocalDefId, CrateNum>,
@@ -934,6 +938,7 @@ pub struct Resolver<'a> {
     glob_map: FxHashMap<LocalDefId, FxHashSet<Symbol>>,
     /// Visibilities in "lowered" form, for all entities that have them.
     visibilities: FxHashMap<LocalDefId, ty::Visibility>,
+    has_pub_restricted: bool,
     used_imports: FxHashSet<NodeId>,
     maybe_unused_trait_imports: FxHashSet<LocalDefId>,
     maybe_unused_extern_crates: Vec<(LocalDefId, Span)>,
@@ -985,6 +990,8 @@ pub struct Resolver<'a> {
     /// `macro_rules` scopes *produced* by expanding the macro invocations,
     /// include all the `macro_rules` items and other invocations generated by them.
     output_macro_rules_scopes: FxHashMap<LocalExpnId, MacroRulesScopeRef<'a>>,
+    /// `macro_rules` scopes produced by `macro_rules` item definitions.
+    macro_rules_scopes: FxHashMap<LocalDefId, MacroRulesScopeRef<'a>>,
     /// Helper attributes that are in scope for the given expansion.
     helper_attrs: FxHashMap<LocalExpnId, Vec<Ident>>,
     /// Ready or in-progress results of resolving paths inside the `#[derive(...)]` attribute
@@ -1153,6 +1160,14 @@ impl ResolverAstLowering for Resolver<'_> {
         self.label_res_map.get(&id).cloned()
     }
 
+    fn get_lifetime_res(&self, id: NodeId) -> Option<LifetimeRes> {
+        self.lifetimes_res_map.get(&id).copied()
+    }
+
+    fn take_extra_lifetime_params(&mut self, id: NodeId) -> Vec<(Ident, NodeId, LifetimeRes)> {
+        self.extra_lifetime_params_map.remove(&id).unwrap_or_default()
+    }
+
     fn create_stable_hashing_context(&self) -> StableHashingContext<'_> {
         StableHashingContext::new(self.session, &self.definitions, self.crate_loader.cstore())
     }
@@ -1301,6 +1316,8 @@ impl<'a> Resolver<'a> {
             partial_res_map: Default::default(),
             import_res_map: Default::default(),
             label_res_map: Default::default(),
+            lifetimes_res_map: Default::default(),
+            extra_lifetime_params_map: Default::default(),
             extern_crate_map: Default::default(),
             reexport_map: FxHashMap::default(),
             trait_map: NodeMap::default(),
@@ -1313,6 +1330,7 @@ impl<'a> Resolver<'a> {
 
             glob_map: Default::default(),
             visibilities,
+            has_pub_restricted: false,
             used_imports: FxHashSet::default(),
             maybe_unused_trait_imports: Default::default(),
             maybe_unused_extern_crates: Vec::new(),
@@ -1345,6 +1363,7 @@ impl<'a> Resolver<'a> {
             non_macro_attr: Lrc::new(SyntaxExtension::non_macro_attr(session.edition())),
             invocation_parent_scopes: Default::default(),
             output_macro_rules_scopes: Default::default(),
+            macro_rules_scopes: Default::default(),
             helper_attrs: Default::default(),
             derive_data: Default::default(),
             local_macro_def_scopes: FxHashMap::default(),
@@ -1423,6 +1442,7 @@ impl<'a> Resolver<'a> {
         let proc_macros = self.proc_macros.iter().map(|id| self.local_def_id(*id)).collect();
         let definitions = self.definitions;
         let visibilities = self.visibilities;
+        let has_pub_restricted = self.has_pub_restricted;
         let extern_crate_map = self.extern_crate_map;
         let reexport_map = self.reexport_map;
         let maybe_unused_trait_imports = self.maybe_unused_trait_imports;
@@ -1435,6 +1455,7 @@ impl<'a> Resolver<'a> {
             definitions,
             cstore: Box::new(self.crate_loader.into_cstore()),
             visibilities,
+            has_pub_restricted,
             access_levels,
             extern_crate_map,
             reexport_map,
@@ -1461,6 +1482,7 @@ impl<'a> Resolver<'a> {
             access_levels: self.access_levels.clone(),
             cstore: Box::new(self.cstore().clone()),
             visibilities: self.visibilities.clone(),
+            has_pub_restricted: self.has_pub_restricted,
             extern_crate_map: self.extern_crate_map.clone(),
             reexport_map: self.reexport_map.clone(),
             glob_map: self.glob_map.clone(),
@@ -1855,25 +1877,25 @@ impl<'a> Resolver<'a> {
         &mut self,
         path_str: &str,
         ns: Namespace,
-        mut module_id: DefId,
+        mut parent_scope: ParentScope<'a>,
     ) -> Option<Res> {
         let mut segments =
             Vec::from_iter(path_str.split("::").map(Ident::from_str).map(Segment::from_ident));
         if let Some(segment) = segments.first_mut() {
             if segment.ident.name == kw::Crate {
                 // FIXME: `resolve_path` always resolves `crate` to the current crate root, but
-                // rustdoc wants it to resolve to the `module_id`'s crate root. This trick of
+                // rustdoc wants it to resolve to the `parent_scope`'s crate root. This trick of
                 // replacing `crate` with `self` and changing the current module should achieve
                 // the same effect.
                 segment.ident.name = kw::SelfLower;
-                module_id = module_id.krate.as_def_id();
+                parent_scope.module =
+                    self.expect_module(parent_scope.module.def_id().krate.as_def_id());
             } else if segment.ident.name == kw::Empty {
                 segment.ident.name = kw::PathRoot;
             }
         }
 
-        let module = self.expect_module(module_id);
-        match self.maybe_resolve_path(&segments, Some(ns), &ParentScope::module(module, self)) {
+        match self.maybe_resolve_path(&segments, Some(ns), &parent_scope) {
             PathResult::Module(ModuleOrUniformRoot::Module(module)) => Some(module.res().unwrap()),
             PathResult::NonModule(path_res) if path_res.unresolved_segments() == 0 => {
                 Some(path_res.base_res())
@@ -1886,11 +1908,6 @@ impl<'a> Resolver<'a> {
     }
 
     // For rustdoc.
-    pub fn graph_root(&self) -> Module<'a> {
-        self.graph_root
-    }
-
-    // For rustdoc.
     pub fn take_all_macro_rules(&mut self) -> FxHashMap<Symbol, Res> {
         mem::take(&mut self.all_macro_rules)
     }
@@ -1905,6 +1922,11 @@ impl<'a> Resolver<'a> {
         }
     }
 
+    /// For rustdoc.
+    pub fn macro_rules_scope(&self, def_id: LocalDefId) -> MacroRulesScopeRef<'a> {
+        *self.macro_rules_scopes.get(&def_id).expect("not a `macro_rules` item")
+    }
+
     /// Retrieves the span of the given `DefId` if `DefId` is in the local crate.
     #[inline]
     pub fn opt_span(&self, def_id: DefId) -> Option<Span> {
@@ -2025,42 +2047,27 @@ fn module_to_string(module: Module<'_>) -> Option<String> {
 }
 
 #[derive(Copy, Clone, Debug)]
-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, 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, 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, path_span: Span },
+struct Finalize {
+    /// Node ID for linting.
+    node_id: NodeId,
+    /// Span of the whole path or some its characteristic fragment.
+    /// E.g. span of `b` in `foo::{a, b, c}`, or full span for regular paths.
+    path_span: Span,
+    /// Span of the path start, suitable for prepending something to to it.
+    /// E.g. span of `foo` in `foo::{a, b, c}`, or full span for regular paths.
+    root_span: Span,
+    /// Whether to report privacy errors or silently return "no resolution" for them,
+    /// similarly to speculative resolution.
+    report_private: bool,
 }
 
 impl Finalize {
-    fn node_id_and_path_span(&self) -> Option<(NodeId, Span)> {
-        match *self {
-            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 new(node_id: NodeId, path_span: Span) -> Finalize {
+        Finalize::with_root_span(node_id, path_span, path_span)
     }
 
-    fn path_span(&self) -> Option<Span> {
-        self.node_id_and_path_span().map(|(_, path_span)| path_span)
+    fn with_root_span(node_id: NodeId, path_span: Span, root_span: Span) -> Finalize {
+        Finalize { node_id, path_span, root_span, report_private: true }
     }
 }
 
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index 01f0b11f1ac..19a9c1b99fc 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -604,7 +604,6 @@ impl<'a> Resolver<'a> {
                 parent_scope,
                 None,
                 force,
-                false,
                 None,
             );
             if let Err(Determinacy::Undetermined) = binding {
@@ -673,7 +672,7 @@ impl<'a> Resolver<'a> {
                 &path,
                 Some(MacroNS),
                 &parent_scope,
-                Finalize::SimplePath(ast::CRATE_NODE_ID, path_span),
+                Some(Finalize::new(ast::CRATE_NODE_ID, path_span)),
                 None,
             ) {
                 PathResult::NonModule(path_res) if path_res.unresolved_segments() == 0 => {
@@ -708,9 +707,8 @@ impl<'a> Resolver<'a> {
                 ident,
                 ScopeSet::Macro(kind),
                 &parent_scope,
-                Some(ident.span),
+                Some(Finalize::new(ast::CRATE_NODE_ID, ident.span)),
                 true,
-                false,
                 None,
             ) {
                 Ok(binding) => {
@@ -751,9 +749,8 @@ impl<'a> Resolver<'a> {
                 ident,
                 ScopeSet::Macro(MacroKind::Attr),
                 &parent_scope,
-                Some(ident.span),
+                Some(Finalize::new(ast::CRATE_NODE_ID, ident.span)),
                 true,
-                false,
                 None,
             );
         }
diff --git a/compiler/rustc_save_analysis/src/dump_visitor.rs b/compiler/rustc_save_analysis/src/dump_visitor.rs
index 22d0a20395e..b4230a144f8 100644
--- a/compiler/rustc_save_analysis/src/dump_visitor.rs
+++ b/compiler/rustc_save_analysis/src/dump_visitor.rs
@@ -25,7 +25,6 @@ use rustc_middle::hir::nested_filter;
 use rustc_middle::span_bug;
 use rustc_middle::ty::{self, DefIdTree, TyCtxt};
 use rustc_session::config::Input;
-use rustc_span::source_map::respan;
 use rustc_span::symbol::Ident;
 use rustc_span::*;
 
@@ -57,20 +56,14 @@ macro_rules! down_cast_data {
 }
 
 macro_rules! access_from {
-    ($save_ctxt:expr, $item:expr, $id:expr) => {
+    ($save_ctxt:expr, $id:expr) => {
         Access {
-            public: $item.vis.node.is_pub(),
+            public: $save_ctxt.tcx.visibility($id).is_public(),
             reachable: $save_ctxt.access_levels.is_reachable($id),
         }
     };
 }
 
-macro_rules! access_from_vis {
-    ($save_ctxt:expr, $vis:expr, $id:expr) => {
-        Access { public: $vis.node.is_pub(), reachable: $save_ctxt.access_levels.is_reachable($id) }
-    };
-}
-
 pub struct DumpVisitor<'tcx> {
     pub save_ctxt: SaveContext<'tcx>,
     tcx: TyCtxt<'tcx>,
@@ -257,7 +250,6 @@ impl<'tcx> DumpVisitor<'tcx> {
         def_id: LocalDefId,
         ident: Ident,
         generics: &'tcx hir::Generics<'tcx>,
-        vis: &hir::Visibility<'tcx>,
         span: Span,
     ) {
         debug!("process_method: {:?}:{}", def_id, ident);
@@ -272,10 +264,10 @@ impl<'tcx> DumpVisitor<'tcx> {
                 v.process_generic_params(&generics, &method_data.qualname, hir_id);
 
                 method_data.value =
-                    fn_to_string(sig.decl, sig.header, Some(ident.name), generics, vis, &[], None);
+                    fn_to_string(sig.decl, sig.header, Some(ident.name), generics, &[], None);
                 method_data.sig = sig::method_signature(hir_id, ident, generics, sig, &v.save_ctxt);
 
-                v.dumper.dump_def(&access_from_vis!(v.save_ctxt, vis, def_id), method_data);
+                v.dumper.dump_def(&access_from!(v.save_ctxt, def_id), method_data);
             }
 
             // walk arg and return types
@@ -302,7 +294,7 @@ impl<'tcx> DumpVisitor<'tcx> {
         let field_data = self.save_ctxt.get_field_data(field, parent_id);
         if let Some(field_data) = field_data {
             self.dumper.dump_def(
-                &access_from!(self.save_ctxt, field, self.tcx.hir().local_def_id(field.hir_id)),
+                &access_from!(self.save_ctxt, self.tcx.hir().local_def_id(field.hir_id)),
                 field_data,
             );
         }
@@ -369,7 +361,7 @@ impl<'tcx> DumpVisitor<'tcx> {
                 v.process_formals(body.params, &fn_data.qualname);
                 v.process_generic_params(ty_params, &fn_data.qualname, item.hir_id());
 
-                v.dumper.dump_def(&access_from!(v.save_ctxt, item, item.def_id), fn_data);
+                v.dumper.dump_def(&access_from!(v.save_ctxt, item.def_id), fn_data);
             }
 
             for arg in decl.inputs {
@@ -393,7 +385,7 @@ impl<'tcx> DumpVisitor<'tcx> {
         self.nest_typeck_results(item.def_id, |v| {
             if let Some(var_data) = v.save_ctxt.get_item_data(item) {
                 down_cast_data!(var_data, DefData, item.span);
-                v.dumper.dump_def(&access_from!(v.save_ctxt, item, item.def_id), var_data);
+                v.dumper.dump_def(&access_from!(v.save_ctxt, item.def_id), var_data);
             }
             v.visit_ty(&typ);
             v.visit_expr(expr);
@@ -407,7 +399,6 @@ impl<'tcx> DumpVisitor<'tcx> {
         typ: &'tcx hir::Ty<'tcx>,
         expr: Option<&'tcx hir::Expr<'tcx>>,
         parent_id: DefId,
-        vis: &hir::Visibility<'tcx>,
         attrs: &'tcx [ast::Attribute],
     ) {
         let qualname = format!("::{}", self.tcx.def_path_str(def_id.to_def_id()));
@@ -418,7 +409,7 @@ impl<'tcx> DumpVisitor<'tcx> {
             let span = self.span_from_span(ident.span);
 
             self.dumper.dump_def(
-                &access_from_vis!(self.save_ctxt, vis, def_id),
+                &access_from!(self.save_ctxt, def_id),
                 Def {
                     kind: DefKind::Const,
                     id: id_from_hir_id(hir_id, &self.save_ctxt),
@@ -469,7 +460,11 @@ impl<'tcx> DumpVisitor<'tcx> {
                 let fields_str = fields
                     .iter()
                     .filter_map(|f| {
-                        if include_priv_fields || f.vis.node.is_pub() {
+                        if include_priv_fields {
+                            return Some(f.ident.to_string());
+                        }
+                        let def_id = self.save_ctxt.tcx.hir().local_def_id(f.hir_id);
+                        if self.save_ctxt.tcx.visibility(def_id).is_public() {
                             Some(f.ident.to_string())
                         } else {
                             None
@@ -487,7 +482,7 @@ impl<'tcx> DumpVisitor<'tcx> {
             let span = self.span_from_span(item.ident.span);
             let attrs = self.tcx.hir().attrs(item.hir_id());
             self.dumper.dump_def(
-                &access_from!(self.save_ctxt, item, item.def_id),
+                &access_from!(self.save_ctxt, item.def_id),
                 Def {
                     kind,
                     id: id_from_def_id(item.def_id.to_def_id()),
@@ -527,7 +522,7 @@ impl<'tcx> DumpVisitor<'tcx> {
         };
         down_cast_data!(enum_data, DefData, item.span);
 
-        let access = access_from!(self.save_ctxt, item, item.def_id);
+        let access = access_from!(self.save_ctxt, item.def_id);
 
         for variant in enum_definition.variants {
             let name = variant.ident.name.to_string();
@@ -662,7 +657,7 @@ impl<'tcx> DumpVisitor<'tcx> {
                 methods.iter().map(|i| id_from_def_id(i.id.def_id.to_def_id())).collect();
             let attrs = self.tcx.hir().attrs(item.hir_id());
             self.dumper.dump_def(
-                &access_from!(self.save_ctxt, item, item.def_id),
+                &access_from!(self.save_ctxt, item.def_id),
                 Def {
                     kind: DefKind::Trait,
                     id,
@@ -724,7 +719,7 @@ impl<'tcx> DumpVisitor<'tcx> {
     fn process_mod(&mut self, item: &'tcx hir::Item<'tcx>) {
         if let Some(mod_data) = self.save_ctxt.get_item_data(item) {
             down_cast_data!(mod_data, DefData, item.span);
-            self.dumper.dump_def(&access_from!(self.save_ctxt, item, item.def_id), mod_data);
+            self.dumper.dump_def(&access_from!(self.save_ctxt, item.def_id), mod_data);
         }
     }
 
@@ -979,11 +974,9 @@ impl<'tcx> DumpVisitor<'tcx> {
 
     fn process_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>, trait_id: DefId) {
         self.process_macro_use(trait_item.span);
-        let vis_span = trait_item.span.shrink_to_lo();
         match trait_item.kind {
             hir::TraitItemKind::Const(ref ty, body) => {
                 let body = body.map(|b| &self.tcx.hir().body(b).value);
-                let respan = respan(vis_span, hir::VisibilityKind::Public);
                 let attrs = self.tcx.hir().attrs(trait_item.hir_id());
                 self.process_assoc_const(
                     trait_item.def_id,
@@ -991,21 +984,18 @@ impl<'tcx> DumpVisitor<'tcx> {
                     &ty,
                     body,
                     trait_id,
-                    &respan,
                     attrs,
                 );
             }
             hir::TraitItemKind::Fn(ref sig, ref trait_fn) => {
                 let body =
                     if let hir::TraitFn::Provided(body) = trait_fn { Some(*body) } else { None };
-                let respan = respan(vis_span, hir::VisibilityKind::Public);
                 self.process_method(
                     sig,
                     body,
                     trait_item.def_id,
                     trait_item.ident,
                     &trait_item.generics,
-                    &respan,
                     trait_item.span,
                 );
             }
@@ -1064,7 +1054,6 @@ impl<'tcx> DumpVisitor<'tcx> {
                     &ty,
                     Some(&body.value),
                     impl_id,
-                    &impl_item.vis,
                     attrs,
                 );
             }
@@ -1075,7 +1064,6 @@ impl<'tcx> DumpVisitor<'tcx> {
                     impl_item.def_id,
                     impl_item.ident,
                     &impl_item.generics,
-                    &impl_item.vis,
                     impl_item.span,
                 );
             }
@@ -1147,7 +1135,7 @@ impl<'tcx> Visitor<'tcx> for DumpVisitor<'tcx> {
             hir::ItemKind::Use(path, hir::UseKind::Single) => {
                 let sub_span = path.segments.last().unwrap().ident.span;
                 if !self.span.filter_generated(sub_span) {
-                    let access = access_from!(self.save_ctxt, item, item.def_id);
+                    let access = access_from!(self.save_ctxt, item.def_id);
                     let ref_id = self.lookup_def_id(item.hir_id()).map(id_from_def_id);
                     let span = self.span_from_span(sub_span);
                     let parent =
@@ -1176,7 +1164,7 @@ impl<'tcx> Visitor<'tcx> for DumpVisitor<'tcx> {
                 // we don't want to track anyway, since it's probably macro-internal `use`
                 if let Some(sub_span) = self.span.sub_span_of_star(item.span) {
                     if !self.span.filter_generated(item.span) {
-                        let access = access_from!(self.save_ctxt, item, item.def_id);
+                        let access = access_from!(self.save_ctxt, item.def_id);
                         let span = self.span_from_span(sub_span);
                         let parent =
                             self.save_ctxt.tcx.parent(item.def_id.to_def_id()).map(id_from_def_id);
@@ -1249,7 +1237,7 @@ impl<'tcx> Visitor<'tcx> for DumpVisitor<'tcx> {
                     let attrs = self.tcx.hir().attrs(item.hir_id());
 
                     self.dumper.dump_def(
-                        &access_from!(self.save_ctxt, item, item.def_id),
+                        &access_from!(self.save_ctxt, item.def_id),
                         Def {
                             kind: DefKind::Type,
                             id,
@@ -1279,13 +1267,11 @@ impl<'tcx> Visitor<'tcx> for DumpVisitor<'tcx> {
             match param.kind {
                 hir::GenericParamKind::Lifetime { .. } => {}
                 hir::GenericParamKind::Type { ref default, .. } => {
-                    self.process_bounds(param.bounds);
                     if let Some(ref ty) = default {
                         self.visit_ty(ty);
                     }
                 }
                 hir::GenericParamKind::Const { ref ty, ref default } => {
-                    self.process_bounds(param.bounds);
                     self.visit_ty(ty);
                     if let Some(default) = default {
                         self.visit_anon_const(default);
@@ -1293,7 +1279,7 @@ impl<'tcx> Visitor<'tcx> for DumpVisitor<'tcx> {
                 }
             }
         }
-        for pred in generics.where_clause.predicates {
+        for pred in generics.predicates {
             if let hir::WherePredicate::BoundPredicate(ref wbp) = *pred {
                 self.process_bounds(wbp.bounds);
                 self.visit_ty(wbp.bounded_ty);
@@ -1443,7 +1429,7 @@ impl<'tcx> Visitor<'tcx> for DumpVisitor<'tcx> {
     }
 
     fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'tcx>) {
-        let access = access_from!(self.save_ctxt, item, item.def_id);
+        let access = access_from!(self.save_ctxt, item.def_id);
 
         match item.kind {
             hir::ForeignItemKind::Fn(decl, _, ref generics) => {
diff --git a/compiler/rustc_save_analysis/src/lib.rs b/compiler/rustc_save_analysis/src/lib.rs
index 102268c6ca3..582186cbd1f 100644
--- a/compiler/rustc_save_analysis/src/lib.rs
+++ b/compiler/rustc_save_analysis/src/lib.rs
@@ -27,7 +27,6 @@ use rustc_middle::{bug, span_bug};
 use rustc_session::config::{CrateType, Input, OutputType};
 use rustc_session::cstore::ExternCrate;
 use rustc_session::output::{filename_for_metadata, out_filename};
-use rustc_span::source_map::Spanned;
 use rustc_span::symbol::Ident;
 use rustc_span::*;
 
@@ -165,7 +164,6 @@ impl<'tcx> SaveContext<'tcx> {
                         },
                         Some(item.ident.name),
                         generics,
-                        &item.vis,
                         arg_names,
                         None,
                     ),
@@ -221,7 +219,6 @@ impl<'tcx> SaveContext<'tcx> {
                         sig.header,
                         Some(item.ident.name),
                         generics,
-                        &item.vis,
                         &[],
                         None,
                     ),
@@ -310,7 +307,7 @@ impl<'tcx> SaveContext<'tcx> {
                 let qualname = format!("::{}", self.tcx.def_path_str(def_id));
                 filter!(self.span_utils, item.ident.span);
                 let value =
-                    enum_def_to_string(def, generics, item.ident.name, item.span, &item.vis);
+                    enum_def_to_string(def, generics, item.ident.name, item.span);
                 Some(Data::DefData(Def {
                     kind: DefKind::Enum,
                     id: id_from_def_id(def_id),
@@ -595,11 +592,6 @@ impl<'tcx> SaveContext<'tcx> {
             Node::TraitRef(tr) => tr.path.res,
 
             Node::Item(&hir::Item { kind: hir::ItemKind::Use(path, _), .. }) => path.res,
-            Node::Visibility(&Spanned {
-                node: hir::VisibilityKind::Restricted { ref path, .. },
-                ..
-            }) => path.res,
-
             Node::PathSegment(seg) => match seg.res {
                 Some(res) if res != Res::Err => res,
                 _ => {
diff --git a/compiler/rustc_save_analysis/src/sig.rs b/compiler/rustc_save_analysis/src/sig.rs
index 8f50f445719..d1286c9b8b0 100644
--- a/compiler/rustc_save_analysis/src/sig.rs
+++ b/compiler/rustc_save_analysis/src/sig.rs
@@ -630,31 +630,6 @@ impl<'hir> Sig for hir::Generics<'hir> {
                     param_text.push_str(&id_to_string(&scx.tcx.hir(), default.hir_id));
                 }
             }
-            if !param.bounds.is_empty() {
-                param_text.push_str(": ");
-                match param.kind {
-                    hir::GenericParamKind::Lifetime { .. } => {
-                        let bounds = param
-                            .bounds
-                            .iter()
-                            .map(|bound| match bound {
-                                hir::GenericBound::Outlives(lt) => lt.name.ident().to_string(),
-                                _ => panic!(),
-                            })
-                            .collect::<Vec<_>>()
-                            .join(" + ");
-                        param_text.push_str(&bounds);
-                        // FIXME add lifetime bounds refs.
-                    }
-                    hir::GenericParamKind::Type { .. } => {
-                        param_text.push_str(&bounds_to_string(param.bounds));
-                        // FIXME descend properly into bounds.
-                    }
-                    hir::GenericParamKind::Const { .. } => {
-                        // Const generics cannot contain bounds.
-                    }
-                }
-            }
             text.push_str(&param_text);
             text.push(',');
         }
diff --git a/compiler/rustc_serialize/src/serialize.rs b/compiler/rustc_serialize/src/serialize.rs
index d5053034ed8..7d6b8c760ff 100644
--- a/compiler/rustc_serialize/src/serialize.rs
+++ b/compiler/rustc_serialize/src/serialize.rs
@@ -498,7 +498,6 @@ macro_rules! peel {
 /// Therefore, the recursion depth is the binary logarithm of the number of
 /// tokens to count, and the expanded tree is likewise very small.
 macro_rules! count {
-    ()                     => (0usize);
     ($one:tt)              => (1usize);
     ($($pairs:tt $_p:tt)*) => (count!($($pairs)*) << 1usize);
     ($odd:tt $($rest:tt)*) => (count!($($rest)*) | 1usize);
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 86a078f4a38..530c1a06f8f 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -3,14 +3,14 @@
 
 pub use crate::options::*;
 
-use crate::lint;
 use crate::search_paths::SearchPath;
 use crate::utils::{CanonicalizedPath, NativeLib, NativeLibKind};
 use crate::{early_error, early_warn, Session};
+use crate::{lint, HashStableContext};
 
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_data_structures::impl_stable_hash_via_hash;
 
+use rustc_data_structures::stable_hasher::ToStableHashKey;
 use rustc_target::abi::{Align, TargetDataLayout};
 use rustc_target::spec::{LinkerFlavor, SplitDebuginfo, Target, TargetTriple, TargetWarnings};
 use rustc_target::spec::{PanicStrategy, SanitizerSet, TARGETS};
@@ -78,7 +78,7 @@ pub enum CFProtection {
     Full,
 }
 
-#[derive(Clone, Copy, Debug, PartialEq, Hash)]
+#[derive(Clone, Copy, Debug, PartialEq, Hash, HashStable_Generic)]
 pub enum OptLevel {
     No,         // -O0
     Less,       // -O1
@@ -88,8 +88,6 @@ pub enum OptLevel {
     SizeMin,    // -Oz
 }
 
-impl_stable_hash_via_hash!(OptLevel);
-
 /// This is what the `LtoCli` values get mapped to after resolving defaults and
 /// and taking other command line options into account.
 ///
@@ -230,15 +228,13 @@ impl SwitchWithOptPath {
     }
 }
 
-#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, HashStable_Generic)]
 #[derive(Encodable, Decodable)]
 pub enum SymbolManglingVersion {
     Legacy,
     V0,
 }
 
-impl_stable_hash_via_hash!(SymbolManglingVersion);
-
 #[derive(Clone, Copy, Debug, PartialEq, Hash)]
 pub enum DebugInfo {
     None,
@@ -277,7 +273,7 @@ impl FromStr for SplitDwarfKind {
     }
 }
 
-#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
+#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord, HashStable_Generic)]
 #[derive(Encodable, Decodable)]
 pub enum OutputType {
     Bitcode,
@@ -290,7 +286,13 @@ pub enum OutputType {
     DepInfo,
 }
 
-impl_stable_hash_via_hash!(OutputType);
+impl<HCX: HashStableContext> ToStableHashKey<HCX> for OutputType {
+    type KeyType = Self;
+
+    fn to_stable_hash_key(&self, _: &HCX) -> Self::KeyType {
+        *self
+    }
+}
 
 impl OutputType {
     fn is_compatible_with_codegen_units_and_single_output_file(&self) -> bool {
@@ -396,7 +398,7 @@ pub enum TrimmedDefPaths {
 /// *Do not* switch `BTreeMap` out for an unsorted container type! That would break
 /// dependency tracking for command-line arguments. Also only hash keys, since tracking
 /// should only depend on the output types, not the paths they're written to.
-#[derive(Clone, Debug, Hash)]
+#[derive(Clone, Debug, Hash, HashStable_Generic)]
 pub struct OutputTypes(BTreeMap<OutputType, Option<PathBuf>>);
 
 impl OutputTypes {
@@ -472,6 +474,11 @@ pub struct ExternEntry {
     /// This can be disabled with the `noprelude` option like
     /// `--extern noprelude:name`.
     pub add_prelude: bool,
+    /// The extern entry shouldn't be considered for unused dependency warnings.
+    ///
+    /// `--extern nounused:std=/path/to/lib/libstd.rlib`. This is used to
+    /// suppress `unused-crate-dependencies` warnings.
+    pub nounused_dep: bool,
 }
 
 #[derive(Clone, Debug)]
@@ -510,7 +517,7 @@ impl Externs {
 
 impl ExternEntry {
     fn new(location: ExternLocation) -> ExternEntry {
-        ExternEntry { location, is_private_dep: false, add_prelude: false }
+        ExternEntry { location, is_private_dep: false, add_prelude: false, nounused_dep: false }
     }
 
     pub fn files(&self) -> Option<impl Iterator<Item = &CanonicalizedPath>> {
@@ -585,7 +592,7 @@ impl Input {
     }
 }
 
-#[derive(Clone, Hash, Debug)]
+#[derive(Clone, Hash, Debug, HashStable_Generic)]
 pub struct OutputFilenames {
     pub out_directory: PathBuf,
     filestem: String,
@@ -594,8 +601,6 @@ pub struct OutputFilenames {
     pub outputs: OutputTypes,
 }
 
-impl_stable_hash_via_hash!(OutputFilenames);
-
 pub const RLINK_EXT: &str = "rlink";
 pub const RUST_CGU_EXT: &str = "rcgu";
 pub const DWARF_OBJECT_EXT: &str = "dwo";
@@ -752,7 +757,7 @@ impl Default for Options {
             real_rust_source_base_dir: None,
             edition: DEFAULT_EDITION,
             json_artifact_notifications: false,
-            json_unused_externs: false,
+            json_unused_externs: JsonUnusedExterns::No,
             json_future_incompat: false,
             pretty: None,
             working_dir: RealFileName::LocalPath(std::env::current_dir().unwrap()),
@@ -808,15 +813,14 @@ impl DebuggingOptions {
 }
 
 // The type of entry function, so users can have their own entry functions
-#[derive(Copy, Clone, PartialEq, Hash, Debug)]
+#[derive(Copy, Clone, PartialEq, Hash, Debug, HashStable_Generic)]
 pub enum EntryFnType {
     Main,
     Start,
 }
 
-impl_stable_hash_via_hash!(EntryFnType);
-
 #[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug, Encodable, Decodable)]
+#[derive(HashStable_Generic)]
 pub enum CrateType {
     Executable,
     Dylib,
@@ -826,8 +830,6 @@ pub enum CrateType {
     ProcMacro,
 }
 
-impl_stable_hash_via_hash!(CrateType);
-
 impl CrateType {
     /// When generated, is this crate type an archive?
     pub fn is_archive(&self) -> bool {
@@ -1041,6 +1043,7 @@ impl CrateCheckConfig {
             sym::target_has_atomic_load_store,
             sym::target_has_atomic,
             sym::target_has_atomic_equal_alignment,
+            sym::target_feature,
             sym::panic,
             sym::sanitize,
             sym::debug_assertions,
@@ -1084,6 +1087,10 @@ impl CrateCheckConfig {
             .into_iter()
             .map(|sanitizer| Symbol::intern(sanitizer.as_str().unwrap()));
 
+        // Unknown possible values:
+        //  - `feature`
+        //  - `target_feature`
+
         // No-values
         for name in [
             sym::doc,
@@ -1491,10 +1498,37 @@ pub fn parse_color(matches: &getopts::Matches) -> ColorConfig {
 pub struct JsonConfig {
     pub json_rendered: HumanReadableErrorType,
     pub json_artifact_notifications: bool,
-    pub json_unused_externs: bool,
+    pub json_unused_externs: JsonUnusedExterns,
     pub json_future_incompat: bool,
 }
 
+/// Report unused externs in event stream
+#[derive(Copy, Clone)]
+pub enum JsonUnusedExterns {
+    /// Do not
+    No,
+    /// Report, but do not exit with failure status for deny/forbid
+    Silent,
+    /// Report, and also exit with failure status for deny/forbid
+    Loud,
+}
+
+impl JsonUnusedExterns {
+    pub fn is_enabled(&self) -> bool {
+        match self {
+            JsonUnusedExterns::No => false,
+            JsonUnusedExterns::Loud | JsonUnusedExterns::Silent => true,
+        }
+    }
+
+    pub fn is_loud(&self) -> bool {
+        match self {
+            JsonUnusedExterns::No | JsonUnusedExterns::Silent => false,
+            JsonUnusedExterns::Loud => true,
+        }
+    }
+}
+
 /// Parse the `--json` flag.
 ///
 /// The first value returned is how to render JSON diagnostics, and the second
@@ -1504,7 +1538,7 @@ pub fn parse_json(matches: &getopts::Matches) -> JsonConfig {
         HumanReadableErrorType::Default;
     let mut json_color = ColorConfig::Never;
     let mut json_artifact_notifications = false;
-    let mut json_unused_externs = false;
+    let mut json_unused_externs = JsonUnusedExterns::No;
     let mut json_future_incompat = false;
     for option in matches.opt_strs("json") {
         // For now conservatively forbid `--color` with `--json` since `--json`
@@ -1522,7 +1556,8 @@ pub fn parse_json(matches: &getopts::Matches) -> JsonConfig {
                 "diagnostic-short" => json_rendered = HumanReadableErrorType::Short,
                 "diagnostic-rendered-ansi" => json_color = ColorConfig::Always,
                 "artifacts" => json_artifact_notifications = true,
-                "unused-externs" => json_unused_externs = true,
+                "unused-externs" => json_unused_externs = JsonUnusedExterns::Loud,
+                "unused-externs-silent" => json_unused_externs = JsonUnusedExterns::Silent,
                 "future-incompat" => json_future_incompat = true,
                 s => early_error(
                     ErrorOutputType::default(),
@@ -2134,6 +2169,7 @@ pub fn parse_externs(
 
         let mut is_private_dep = false;
         let mut add_prelude = true;
+        let mut nounused_dep = false;
         if let Some(opts) = options {
             if !is_unstable_enabled {
                 early_error(
@@ -2155,6 +2191,7 @@ pub fn parse_externs(
                             );
                         }
                     }
+                    "nounused" => nounused_dep = true,
                     _ => early_error(error_format, &format!("unknown --extern option `{opt}`")),
                 }
             }
@@ -2163,6 +2200,8 @@ pub fn parse_externs(
         // Crates start out being not private, and go to being private `priv`
         // is specified.
         entry.is_private_dep |= is_private_dep;
+        // likewise `nounused`
+        entry.nounused_dep |= nounused_dep;
         // If any flag is missing `noprelude`, then add to the prelude.
         entry.add_prelude |= add_prelude;
     }
@@ -2218,7 +2257,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
 
     check_debug_option_stability(&debugging_opts, error_format, json_rendered);
 
-    if !debugging_opts.unstable_options && json_unused_externs {
+    if !debugging_opts.unstable_options && json_unused_externs.is_enabled() {
         early_error(
             error_format,
             "the `-Z unstable-options` flag must also be passed to enable \
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index df65409a8a0..14e918660dd 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -221,7 +221,7 @@ top_level_options!(
         json_artifact_notifications: bool [TRACKED],
 
         /// `true` if we're emitting a JSON blob containing the unused externs
-        json_unused_externs: bool [UNTRACKED],
+        json_unused_externs: JsonUnusedExterns [UNTRACKED],
 
         /// `true` if we're emitting a JSON job containing a future-incompat report for lints
         json_future_incompat: bool [TRACKED],
@@ -368,6 +368,8 @@ mod desc {
     pub const parse_opt_langid: &str = "a language identifier";
     pub const parse_opt_pathbuf: &str = "a path";
     pub const parse_list: &str = "a space-separated list of strings";
+    pub const parse_list_with_polarity: &str =
+        "a comma-separated list of strings, with elements beginning with + or -";
     pub const parse_opt_comma_list: &str = "a comma-separated list of strings";
     pub const parse_number: &str = "a number";
     pub const parse_opt_number: &str = parse_number;
@@ -529,6 +531,19 @@ mod parse {
         }
     }
 
+    crate fn parse_list_with_polarity(slot: &mut Vec<(String, bool)>, v: Option<&str>) -> bool {
+        match v {
+            Some(s) => {
+                for s in s.split(",") {
+                    let Some(pass_name) = s.strip_prefix(&['+', '-'][..]) else { return false };
+                    slot.push((pass_name.to_string(), &s[..1] == "+"));
+                }
+                true
+            }
+            None => false,
+        }
+    }
+
     crate fn parse_location_detail(ld: &mut LocationDetail, v: Option<&str>) -> bool {
         if let Some(v) = v {
             ld.line = false;
@@ -1318,6 +1333,10 @@ options! {
     mir_emit_retag: bool = (false, parse_bool, [TRACKED],
         "emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 \
         (default: no)"),
+    mir_enable_passes: Vec<(String, bool)> = (Vec::new(), parse_list_with_polarity, [TRACKED],
+        "use like `-Zmir-enable-passes=+DestProp,-InstCombine`. Forces the specified passes to be \
+        enabled, overriding all other checks. Passes that are not specified are enabled or \
+        disabled by other flags as usual."),
     mir_opt_level: Option<usize> = (None, parse_opt_number, [TRACKED],
         "MIR optimization level (0-4; default: 1 in non optimized builds and 2 in optimized builds)"),
     move_size_limit: Option<usize> = (None, parse_opt_number, [TRACKED],
diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs
index 6a36ae63c68..e933fe1cb24 100644
--- a/compiler/rustc_session/src/parse.rs
+++ b/compiler/rustc_session/src/parse.rs
@@ -3,13 +3,14 @@
 
 use crate::config::CheckCfg;
 use crate::lint::{BufferedEarlyLint, BuiltinLintDiagnostics, Lint, LintId};
+use crate::SessionDiagnostic;
 use rustc_ast::node_id::NodeId;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::sync::{Lock, Lrc};
 use rustc_errors::{emitter::SilentEmitter, ColorConfig, Handler};
 use rustc_errors::{
     error_code, fallback_fluent_bundle, Applicability, Diagnostic, DiagnosticBuilder,
-    ErrorGuaranteed, MultiSpan,
+    DiagnosticMessage, ErrorGuaranteed, MultiSpan,
 };
 use rustc_feature::{find_feature_issue, GateIssue, UnstableFeatures};
 use rustc_span::edition::Edition;
@@ -287,4 +288,23 @@ impl ParseSess {
     pub fn proc_macro_quoted_spans(&self) -> Vec<Span> {
         self.proc_macro_quoted_spans.lock().clone()
     }
+
+    pub fn emit_err<'a>(&'a self, err: impl SessionDiagnostic<'a>) -> ErrorGuaranteed {
+        err.into_diagnostic(self).emit()
+    }
+
+    pub fn emit_warning<'a>(&'a self, warning: impl SessionDiagnostic<'a, ()>) {
+        warning.into_diagnostic(self).emit()
+    }
+
+    pub fn struct_err(
+        &self,
+        msg: impl Into<DiagnosticMessage>,
+    ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
+        self.span_diagnostic.struct_err(msg)
+    }
+
+    pub fn struct_warn(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
+        self.span_diagnostic.struct_warn(msg)
+    }
 }
diff --git a/compiler/rustc_session/src/search_paths.rs b/compiler/rustc_session/src/search_paths.rs
index b6bde28233d..56a6b6f3b03 100644
--- a/compiler/rustc_session/src/search_paths.rs
+++ b/compiler/rustc_session/src/search_paths.rs
@@ -26,7 +26,7 @@ pub struct SearchPathFile {
     pub file_name_str: String,
 }
 
-#[derive(PartialEq, Clone, Copy, Debug, Hash, Eq, Encodable, Decodable)]
+#[derive(PartialEq, Clone, Copy, Debug, Hash, Eq, Encodable, Decodable, HashStable_Generic)]
 pub enum PathKind {
     Native,
     Crate,
@@ -36,8 +36,6 @@ pub enum PathKind {
     All,
 }
 
-rustc_data_structures::impl_stable_hash_via_hash!(PathKind);
-
 impl PathKind {
     pub fn matches(&self, kind: PathKind) -> bool {
         match (self, kind) {
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index b4548129689..e8279f6fed2 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -212,7 +212,7 @@ pub struct PerfStats {
 pub trait SessionDiagnostic<'a, T: EmissionGuarantee = ErrorGuaranteed> {
     /// Write out as a diagnostic out of `sess`.
     #[must_use]
-    fn into_diagnostic(self, sess: &'a Session) -> DiagnosticBuilder<'a, T>;
+    fn into_diagnostic(self, sess: &'a ParseSess) -> DiagnosticBuilder<'a, T>;
 }
 
 impl Session {
@@ -334,7 +334,7 @@ impl Session {
         &self,
         msg: impl Into<DiagnosticMessage>,
     ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
-        self.diagnostic().struct_err(msg)
+        self.parse_sess.struct_err(msg)
     }
     pub fn struct_err_with_code(
         &self,
@@ -414,10 +414,10 @@ impl Session {
         self.diagnostic().err(msg)
     }
     pub fn emit_err<'a>(&'a self, err: impl SessionDiagnostic<'a>) -> ErrorGuaranteed {
-        err.into_diagnostic(self).emit()
+        self.parse_sess.emit_err(err)
     }
     pub fn emit_warning<'a>(&'a self, warning: impl SessionDiagnostic<'a, ()>) {
-        warning.into_diagnostic(self).emit()
+        self.parse_sess.emit_warning(warning)
     }
     #[inline]
     pub fn err_count(&self) -> usize {
diff --git a/compiler/rustc_session/src/utils.rs b/compiler/rustc_session/src/utils.rs
index 9a286d94ab8..db755ccd1d5 100644
--- a/compiler/rustc_session/src/utils.rs
+++ b/compiler/rustc_session/src/utils.rs
@@ -1,6 +1,6 @@
 use crate::parse::ParseSess;
 use crate::session::Session;
-use rustc_ast::token::{self, DelimToken, Nonterminal, Token};
+use rustc_ast::token::{self, Delimiter, Nonterminal, Token};
 use rustc_ast::tokenstream::CanSynthesizeMissingTokens;
 use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree};
 use rustc_data_structures::profiling::VerboseTimingGuard;
@@ -18,6 +18,7 @@ impl Session {
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)]
+#[derive(HashStable_Generic)]
 pub enum NativeLibKind {
     /// Static library (e.g. `libfoo.a` on Linux or `foo.lib` on Windows/MSVC)
     Static {
@@ -57,9 +58,8 @@ impl NativeLibKind {
     }
 }
 
-rustc_data_structures::impl_stable_hash_via_hash!(NativeLibKind);
-
 #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)]
+#[derive(HashStable_Generic)]
 pub struct NativeLib {
     pub name: String,
     pub new_name: Option<String>,
@@ -73,8 +73,6 @@ impl NativeLib {
     }
 }
 
-rustc_data_structures::impl_stable_hash_via_hash!(NativeLib);
-
 /// A path that has been canonicalized along with its original, non-canonicalized form
 #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
 pub struct CanonicalizedPath {
@@ -139,7 +137,7 @@ impl<'a> FlattenNonterminals<'a> {
                 let tts = (self.nt_to_tokenstream)(&nt, self.parse_sess, self.synthesize_tokens);
                 TokenTree::Delimited(
                     DelimSpan::from_single(token.span),
-                    DelimToken::NoDelim,
+                    Delimiter::Invisible,
                     self.process_token_stream(tts),
                 )
                 .into()
diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs
index 3889639b50f..447b73fa3c3 100644
--- a/compiler/rustc_span/src/hygiene.rs
+++ b/compiler/rustc_span/src/hygiene.rs
@@ -1132,6 +1132,7 @@ pub enum DesugaringKind {
     CondTemporary,
     QuestionMark,
     TryBlock,
+    YeetExpr,
     /// Desugaring of an `impl Trait` in return type position
     /// to an `type Foo = impl Trait;` and replacing the
     /// `impl Trait` with `Foo`.
@@ -1152,6 +1153,7 @@ impl DesugaringKind {
             DesugaringKind::Await => "`await` expression",
             DesugaringKind::QuestionMark => "operator `?`",
             DesugaringKind::TryBlock => "`try` block",
+            DesugaringKind::YeetExpr => "`do yeet` expression",
             DesugaringKind::OpaqueTy => "`impl Trait`",
             DesugaringKind::ForLoop => "`for` loop",
             DesugaringKind::LetElse => "`let...else`",
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index b132c0a2132..f22faef2580 100644
--- a/compiler/rustc_span/src/lib.rs
+++ b/compiler/rustc_span/src/lib.rs
@@ -59,6 +59,8 @@ pub use symbol::{sym, Symbol};
 mod analyze_source_file;
 pub mod fatal_error;
 
+pub mod profiling;
+
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::sync::{Lock, Lrc};
 
@@ -1130,6 +1132,7 @@ impl ExternalSource {
 pub struct OffsetOverflowError;
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)]
+#[derive(HashStable_Generic)]
 pub enum SourceFileHashAlgorithm {
     Md5,
     Sha1,
@@ -1149,8 +1152,6 @@ impl FromStr for SourceFileHashAlgorithm {
     }
 }
 
-rustc_data_structures::impl_stable_hash_via_hash!(SourceFileHashAlgorithm);
-
 /// The hash of the on-disk source file used for debug info.
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
 #[derive(HashStable_Generic, Encodable, Decodable)]
diff --git a/compiler/rustc_span/src/profiling.rs b/compiler/rustc_span/src/profiling.rs
new file mode 100644
index 00000000000..f169007fab4
--- /dev/null
+++ b/compiler/rustc_span/src/profiling.rs
@@ -0,0 +1,35 @@
+use std::borrow::Borrow;
+
+use rustc_data_structures::profiling::EventArgRecorder;
+
+/// Extension trait for self-profiling purposes: allows to record spans within a generic activity's
+/// event arguments.
+pub trait SpannedEventArgRecorder {
+    /// Records the following event arguments within the current generic activity being profiled:
+    /// - the provided `event_arg`
+    /// - a string representation of the provided `span`
+    ///
+    /// Note: when self-profiling with costly event arguments, at least one argument
+    /// needs to be recorded. A panic will be triggered if that doesn't happen.
+    fn record_arg_with_span<A>(&mut self, event_arg: A, span: crate::Span)
+    where
+        A: Borrow<str> + Into<String>;
+}
+
+impl SpannedEventArgRecorder for EventArgRecorder<'_> {
+    fn record_arg_with_span<A>(&mut self, event_arg: A, span: crate::Span)
+    where
+        A: Borrow<str> + Into<String>,
+    {
+        self.record_arg(event_arg);
+
+        let span_arg = crate::with_session_globals(|session_globals| {
+            if let Some(source_map) = &*session_globals.source_map.borrow() {
+                source_map.span_to_embeddable_string(span)
+            } else {
+                format!("{:?}", span)
+            }
+        });
+        self.record_arg(span_arg);
+    }
+}
diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs
index 95177102dcf..460b5c18fc1 100644
--- a/compiler/rustc_span/src/source_map.rs
+++ b/compiler/rustc_span/src/source_map.rs
@@ -1102,7 +1102,19 @@ impl FilePathMapping {
         //       take precedence.
         for &(ref from, ref to) in self.mapping.iter().rev() {
             if let Ok(rest) = path.strip_prefix(from) {
-                return (to.join(rest), true);
+                let remapped = if rest.as_os_str().is_empty() {
+                    // This is subtle, joining an empty path onto e.g. `foo/bar` will
+                    // result in `foo/bar/`, that is, there'll be an additional directory
+                    // separator at the end. This can lead to duplicated directory separators
+                    // in remapped paths down the line.
+                    // So, if we have an exact match, we just return that without a call
+                    // to `Path::join()`.
+                    to.clone()
+                } else {
+                    to.join(rest)
+                };
+
+                return (remapped, true);
             }
         }
 
diff --git a/compiler/rustc_span/src/source_map/tests.rs b/compiler/rustc_span/src/source_map/tests.rs
index f13979941ab..481e015c66c 100644
--- a/compiler/rustc_span/src/source_map/tests.rs
+++ b/compiler/rustc_span/src/source_map/tests.rs
@@ -312,3 +312,83 @@ impl SourceMapExtension for SourceMap {
         }
     }
 }
+
+fn map_path_prefix(mapping: &FilePathMapping, path: &str) -> String {
+    // It's important that we convert to a string here because that's what
+    // later stages do too (e.g. in the backend), and comparing `Path` values
+    // won't catch some differences at the string level, e.g. "abc" and "abc/"
+    // compare as equal.
+    mapping.map_prefix(path.into()).0.to_string_lossy().to_string()
+}
+
+#[cfg(unix)]
+#[test]
+fn path_prefix_remapping() {
+    // Relative to relative
+    {
+        let mapping = &FilePathMapping::new(vec![("abc/def".into(), "foo".into())]);
+
+        assert_eq!(map_path_prefix(mapping, "abc/def/src/main.rs"), "foo/src/main.rs");
+        assert_eq!(map_path_prefix(mapping, "abc/def"), "foo");
+    }
+
+    // Relative to absolute
+    {
+        let mapping = &FilePathMapping::new(vec![("abc/def".into(), "/foo".into())]);
+
+        assert_eq!(map_path_prefix(mapping, "abc/def/src/main.rs"), "/foo/src/main.rs");
+        assert_eq!(map_path_prefix(mapping, "abc/def"), "/foo");
+    }
+
+    // Absolute to relative
+    {
+        let mapping = &FilePathMapping::new(vec![("/abc/def".into(), "foo".into())]);
+
+        assert_eq!(map_path_prefix(mapping, "/abc/def/src/main.rs"), "foo/src/main.rs");
+        assert_eq!(map_path_prefix(mapping, "/abc/def"), "foo");
+    }
+
+    // Absolute to absolute
+    {
+        let mapping = &FilePathMapping::new(vec![("/abc/def".into(), "/foo".into())]);
+
+        assert_eq!(map_path_prefix(mapping, "/abc/def/src/main.rs"), "/foo/src/main.rs");
+        assert_eq!(map_path_prefix(mapping, "/abc/def"), "/foo");
+    }
+}
+
+#[cfg(windows)]
+#[test]
+fn path_prefix_remapping_from_relative2() {
+    // Relative to relative
+    {
+        let mapping = &FilePathMapping::new(vec![("abc\\def".into(), "foo".into())]);
+
+        assert_eq!(map_path_prefix(mapping, "abc\\def\\src\\main.rs"), "foo\\src\\main.rs");
+        assert_eq!(map_path_prefix(mapping, "abc\\def"), "foo");
+    }
+
+    // Relative to absolute
+    {
+        let mapping = &FilePathMapping::new(vec![("abc\\def".into(), "X:\\foo".into())]);
+
+        assert_eq!(map_path_prefix(mapping, "abc\\def\\src\\main.rs"), "X:\\foo\\src\\main.rs");
+        assert_eq!(map_path_prefix(mapping, "abc\\def"), "X:\\foo");
+    }
+
+    // Absolute to relative
+    {
+        let mapping = &FilePathMapping::new(vec![("X:\\abc\\def".into(), "foo".into())]);
+
+        assert_eq!(map_path_prefix(mapping, "X:\\abc\\def\\src\\main.rs"), "foo\\src\\main.rs");
+        assert_eq!(map_path_prefix(mapping, "X:\\abc\\def"), "foo");
+    }
+
+    // Absolute to absolute
+    {
+        let mapping = &FilePathMapping::new(vec![("X:\\abc\\def".into(), "X:\\foo".into())]);
+
+        assert_eq!(map_path_prefix(mapping, "X:\\abc\\def\\src\\main.rs"), "X:\\foo\\src\\main.rs");
+        assert_eq!(map_path_prefix(mapping, "X:\\abc\\def"), "X:\\foo");
+    }
+}
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index d9bada29589..c1299c94c4b 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -101,6 +101,7 @@ symbols! {
         MacroRules:         "macro_rules",
         Raw:                "raw",
         Union:              "union",
+        Yeet:               "yeet",
     }
 
     // Pre-interned symbols that can be referred to with `rustc_span::sym::*`.
@@ -714,6 +715,7 @@ symbols! {
         from_residual,
         from_size_align_unchecked,
         from_usize,
+        from_yeet,
         fsub_fast,
         fundamental,
         future,
@@ -803,6 +805,7 @@ symbols! {
         keyword,
         kind,
         kreg,
+        kreg0,
         label,
         label_break_value,
         lang,
@@ -1533,6 +1536,8 @@ symbols! {
         x87_reg,
         xer,
         xmm_reg,
+        yeet_desugar_details,
+        yeet_expr,
         ymm_reg,
         zmm_reg,
     }
diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs
index ce564d1455b..afce10ff1cb 100644
--- a/compiler/rustc_target/src/abi/call/mod.rs
+++ b/compiler/rustc_target/src/abi/call/mod.rs
@@ -696,7 +696,13 @@ impl<'a, Ty> FnAbi<'a, Ty> {
             "sparc" => sparc::compute_abi_info(cx, self),
             "sparc64" => sparc64::compute_abi_info(cx, self),
             "nvptx" => nvptx::compute_abi_info(self),
-            "nvptx64" => nvptx64::compute_abi_info(self),
+            "nvptx64" => {
+                if cx.target_spec().adjust_abi(abi) == spec::abi::Abi::PtxKernel {
+                    nvptx64::compute_ptx_kernel_abi_info(cx, self)
+                } else {
+                    nvptx64::compute_abi_info(self)
+                }
+            }
             "hexagon" => hexagon::compute_abi_info(self),
             "riscv32" | "riscv64" => riscv::compute_abi_info(cx, self),
             "wasm32" | "wasm64" => {
diff --git a/compiler/rustc_target/src/abi/call/nvptx64.rs b/compiler/rustc_target/src/abi/call/nvptx64.rs
index 16f331b16d5..fc16f1c97a4 100644
--- a/compiler/rustc_target/src/abi/call/nvptx64.rs
+++ b/compiler/rustc_target/src/abi/call/nvptx64.rs
@@ -1,21 +1,35 @@
-// Reference: PTX Writer's Guide to Interoperability
-// https://docs.nvidia.com/cuda/ptx-writers-guide-to-interoperability
-
-use crate::abi::call::{ArgAbi, FnAbi};
+use crate::abi::call::{ArgAbi, FnAbi, PassMode, Reg, Size, Uniform};
+use crate::abi::{HasDataLayout, TyAbiInterface};
 
 fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) {
     if ret.layout.is_aggregate() && ret.layout.size.bits() > 64 {
         ret.make_indirect();
-    } else {
-        ret.extend_integer_width_to(64);
     }
 }
 
 fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) {
     if arg.layout.is_aggregate() && arg.layout.size.bits() > 64 {
         arg.make_indirect();
-    } else {
-        arg.extend_integer_width_to(64);
+    }
+}
+
+fn classify_arg_kernel<'a, Ty, C>(_cx: &C, arg: &mut ArgAbi<'a, Ty>)
+where
+    Ty: TyAbiInterface<'a, C> + Copy,
+    C: HasDataLayout,
+{
+    if matches!(arg.mode, PassMode::Pair(..)) && (arg.layout.is_adt() || arg.layout.is_tuple()) {
+        let align_bytes = arg.layout.align.abi.bytes();
+
+        let unit = match align_bytes {
+            1 => Reg::i8(),
+            2 => Reg::i16(),
+            4 => Reg::i32(),
+            8 => Reg::i64(),
+            16 => Reg::i128(),
+            _ => unreachable!("Align is given as power of 2 no larger than 16 bytes"),
+        };
+        arg.cast_to(Uniform { unit, total: Size::from_bytes(2 * align_bytes) });
     }
 }
 
@@ -31,3 +45,20 @@ pub fn compute_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) {
         classify_arg(arg);
     }
 }
+
+pub fn compute_ptx_kernel_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
+where
+    Ty: TyAbiInterface<'a, C> + Copy,
+    C: HasDataLayout,
+{
+    if !fn_abi.ret.layout.is_unit() && !fn_abi.ret.layout.is_never() {
+        panic!("Kernels should not return anything other than () or !");
+    }
+
+    for arg in &mut fn_abi.args {
+        if arg.is_ignore() {
+            continue;
+        }
+        classify_arg_kernel(cx, arg);
+    }
+}
diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs
index 169167f69bf..0e8fd9cc93f 100644
--- a/compiler/rustc_target/src/abi/mod.rs
+++ b/compiler/rustc_target/src/abi/mod.rs
@@ -1355,6 +1355,10 @@ pub trait TyAbiInterface<'a, C>: Sized {
         cx: &C,
         offset: Size,
     ) -> Option<PointeeInfo>;
+    fn is_adt(this: TyAndLayout<'a, Self>) -> bool;
+    fn is_never(this: TyAndLayout<'a, Self>) -> bool;
+    fn is_tuple(this: TyAndLayout<'a, Self>) -> bool;
+    fn is_unit(this: TyAndLayout<'a, Self>) -> bool;
 }
 
 impl<'a, Ty> TyAndLayout<'a, Ty> {
@@ -1396,6 +1400,34 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
             _ => false,
         }
     }
+
+    pub fn is_adt<C>(self) -> bool
+    where
+        Ty: TyAbiInterface<'a, C>,
+    {
+        Ty::is_adt(self)
+    }
+
+    pub fn is_never<C>(self) -> bool
+    where
+        Ty: TyAbiInterface<'a, C>,
+    {
+        Ty::is_never(self)
+    }
+
+    pub fn is_tuple<C>(self) -> bool
+    where
+        Ty: TyAbiInterface<'a, C>,
+    {
+        Ty::is_tuple(self)
+    }
+
+    pub fn is_unit<C>(self) -> bool
+    where
+        Ty: TyAbiInterface<'a, C>,
+    {
+        Ty::is_unit(self)
+    }
 }
 
 impl<'a, Ty> TyAndLayout<'a, Ty> {
diff --git a/compiler/rustc_target/src/asm/mips.rs b/compiler/rustc_target/src/asm/mips.rs
index b1e8737b52b..4e7c2eb1bf8 100644
--- a/compiler/rustc_target/src/asm/mips.rs
+++ b/compiler/rustc_target/src/asm/mips.rs
@@ -43,7 +43,8 @@ impl MipsInlineAsmRegClass {
     }
 }
 
-// The reserved registers are somewhat taken from <https://git.io/JUR1k#L150>.
+// The reserved registers are somewhat taken from
+// <https://github.com/llvm/llvm-project/blob/deb8f8bcf31540c657716ea5242183b0792702a1/llvm/lib/Target/Mips/MipsRegisterInfo.cpp#L150>.
 def_regs! {
     Mips MipsInlineAsmReg MipsInlineAsmRegClass {
         r2: reg = ["$2"],
diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs
index 5bc4b566daf..6bc807c7c44 100644
--- a/compiler/rustc_target/src/asm/mod.rs
+++ b/compiler/rustc_target/src/asm/mod.rs
@@ -893,7 +893,7 @@ impl InlineAsmClobberAbi {
 
                     xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7,
 
-                    k1, k2, k3, k4, k5, k6, k7,
+                    k0, k1, k2, k3, k4, k5, k6, k7,
 
                     mm0, mm1, mm2, mm3, mm4, mm5, mm6, mm7,
                     st0, st1, st2, st3, st4, st5, st6, st7,
@@ -908,7 +908,7 @@ impl InlineAsmClobberAbi {
                     zmm16, zmm17, zmm18, zmm19, zmm20, zmm21, zmm22, zmm23,
                     zmm24, zmm25, zmm26, zmm27, zmm28, zmm29, zmm30, zmm31,
 
-                    k1, k2, k3, k4, k5, k6, k7,
+                    k0, k1, k2, k3, k4, k5, k6, k7,
 
                     mm0, mm1, mm2, mm3, mm4, mm5, mm6, mm7,
                     st0, st1, st2, st3, st4, st5, st6, st7,
@@ -927,7 +927,7 @@ impl InlineAsmClobberAbi {
                     zmm16, zmm17, zmm18, zmm19, zmm20, zmm21, zmm22, zmm23,
                     zmm24, zmm25, zmm26, zmm27, zmm28, zmm29, zmm30, zmm31,
 
-                    k1, k2, k3, k4, k5, k6, k7,
+                    k0, k1, k2, k3, k4, k5, k6, k7,
 
                     mm0, mm1, mm2, mm3, mm4, mm5, mm6, mm7,
                     st0, st1, st2, st3, st4, st5, st6, st7,
diff --git a/compiler/rustc_target/src/asm/x86.rs b/compiler/rustc_target/src/asm/x86.rs
index 7c136a47548..854674c7f2f 100644
--- a/compiler/rustc_target/src/asm/x86.rs
+++ b/compiler/rustc_target/src/asm/x86.rs
@@ -14,6 +14,7 @@ def_reg_class! {
         ymm_reg,
         zmm_reg,
         kreg,
+        kreg0,
         mmx_reg,
         x87_reg,
     }
@@ -38,7 +39,7 @@ impl X86InlineAsmRegClass {
             }
             Self::reg_byte => &[],
             Self::xmm_reg | Self::ymm_reg | Self::zmm_reg => &['x', 'y', 'z'],
-            Self::kreg => &[],
+            Self::kreg | Self::kreg0 => &[],
             Self::mmx_reg | Self::x87_reg => &[],
         }
     }
@@ -77,7 +78,7 @@ impl X86InlineAsmRegClass {
                 256 => Some(('y', "ymm0")),
                 _ => Some(('x', "xmm0")),
             },
-            Self::kreg => None,
+            Self::kreg | Self::kreg0 => None,
             Self::mmx_reg | Self::x87_reg => None,
         }
     }
@@ -95,7 +96,7 @@ impl X86InlineAsmRegClass {
             Self::xmm_reg => Some(('x', "xmm0")),
             Self::ymm_reg => Some(('y', "ymm0")),
             Self::zmm_reg => Some(('z', "zmm0")),
-            Self::kreg => None,
+            Self::kreg | Self::kreg0 => None,
             Self::mmx_reg | Self::x87_reg => None,
         }
     }
@@ -132,6 +133,7 @@ impl X86InlineAsmRegClass {
                 avx512f: I8, I16;
                 avx512bw: I32, I64;
             },
+            Self::kreg0 => &[],
             Self::mmx_reg | Self::x87_reg => &[],
         }
     }
@@ -294,6 +296,7 @@ def_regs! {
         zmm29: zmm_reg = ["zmm29", "xmm29", "ymm29"] % x86_64_only,
         zmm30: zmm_reg = ["zmm30", "xmm30", "ymm30"] % x86_64_only,
         zmm31: zmm_reg = ["zmm31", "xmm31", "ymm31"] % x86_64_only,
+        k0: kreg0 = ["k0"],
         k1: kreg = ["k1"],
         k2: kreg = ["k2"],
         k3: kreg = ["k3"],
@@ -323,8 +326,6 @@ def_regs! {
             "the stack pointer cannot be used as an operand for inline asm",
         #error = ["ip", "eip", "rip"] =>
             "the instruction pointer cannot be used as an operand for inline asm",
-        #error = ["k0"] =>
-            "the k0 AVX mask register cannot be used as an operand for inline asm",
     }
 }
 
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index bd5b712c143..965a3c10983 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -2249,10 +2249,6 @@ impl ToJson for Target {
                 let name = (stringify!($attr)).replace("_", "-");
                 d.insert(name, self.$attr.to_json());
             }};
-            ($attr:ident, $key_name:expr) => {{
-                let name = $key_name;
-                d.insert(name.into(), self.$attr.to_json());
-            }};
         }
 
         macro_rules! target_option_val {
diff --git a/compiler/rustc_target/src/spec/wasm64_unknown_unknown.rs b/compiler/rustc_target/src/spec/wasm64_unknown_unknown.rs
index 609b7d42e43..6826c0ac62b 100644
--- a/compiler/rustc_target/src/spec/wasm64_unknown_unknown.rs
+++ b/compiler/rustc_target/src/spec/wasm64_unknown_unknown.rs
@@ -36,7 +36,7 @@ pub fn target() -> Target {
     Target {
         llvm_target: "wasm64-unknown-unknown".into(),
         pointer_width: 64,
-        data_layout: "e-m:e-p:64:64-i64:64-n32:64-S128-ni:1:10:20".into(),
+        data_layout: "e-m:e-p:64:64-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20".into(),
         arch: "wasm64".into(),
         options,
     }
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 50e4fafdd6c..7a3579eb1cc 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -1727,6 +1727,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
         } else if cat_a == cat_b {
             match (a.kind(), b.kind()) {
                 (ty::Adt(def_a, _), ty::Adt(def_b, _)) => def_a == def_b,
+                (ty::Foreign(def_a), ty::Foreign(def_b)) => def_a == def_b,
                 // Matching on references results in a lot of unhelpful
                 // suggestions, so let's just not do that for now.
                 //
@@ -2418,26 +2419,15 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
         };
         let sized_trait = self.tcx.lang_items().sized_trait();
         debug!("maybe_suggest_unsized_generics: generics.params={:?}", generics.params);
-        debug!("maybe_suggest_unsized_generics: generics.where_clause={:?}", generics.where_clause);
-        let param = generics.params.iter().filter(|param| param.span == span).find(|param| {
-            // Check that none of the explicit trait bounds is `Sized`. Assume that an explicit
-            // `Sized` bound is there intentionally and we don't need to suggest relaxing it.
-            param
-                .bounds
-                .iter()
-                .all(|bound| bound.trait_ref().and_then(|tr| tr.trait_def_id()) != sized_trait)
-        });
-        let Some(param) = param else {
+        debug!("maybe_suggest_unsized_generics: generics.predicates={:?}", generics.predicates);
+        let Some(param) = generics.params.iter().find(|param| param.span == span) else {
             return;
         };
-        let param_def_id = self.tcx.hir().local_def_id(param.hir_id).to_def_id();
-        let preds = generics.where_clause.predicates.iter();
-        let explicitly_sized = preds
-            .filter_map(|pred| match pred {
-                hir::WherePredicate::BoundPredicate(bp) => Some(bp),
-                _ => None,
-            })
-            .filter(|bp| bp.is_param_bound(param_def_id))
+        let param_def_id = self.tcx.hir().local_def_id(param.hir_id);
+        // Check that none of the explicit trait bounds is `Sized`. Assume that an explicit
+        // `Sized` bound is there intentionally and we don't need to suggest relaxing it.
+        let explicitly_sized = generics
+            .bounds_for_param(param_def_id)
             .flat_map(|bp| bp.bounds)
             .any(|bound| bound.trait_ref().and_then(|tr| tr.trait_def_id()) == sized_trait);
         if explicitly_sized {
@@ -2460,9 +2450,11 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
             _ => {}
         };
         // Didn't add an indirection suggestion, so add a general suggestion to relax `Sized`.
-        let (span, separator) = match param.bounds {
-            [] => (span.shrink_to_hi(), ":"),
-            [.., bound] => (bound.span().shrink_to_hi(), " +"),
+        let (span, separator) = if let Some(s) = generics.bounds_span_for_suggestions(param_def_id)
+        {
+            (s, " +")
+        } else {
+            (span.shrink_to_hi(), ":")
         };
         err.span_suggestion_verbose(
             span,
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
index 31b92d52beb..9e9c230aebb 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
@@ -217,22 +217,42 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                 flags.push((sym::_Self, Some(shortname.to_owned())));
             }
 
+            // Slices give us `[]`, `[{ty}]`
+            if let ty::Slice(aty) = self_ty.kind() {
+                flags.push((sym::_Self, Some("[]".to_string())));
+                if let Some(def) = aty.ty_adt_def() {
+                    // We also want to be able to select the slice's type's original
+                    // signature with no type arguments resolved
+                    let type_string = self.tcx.type_of(def.did()).to_string();
+                    flags.push((sym::_Self, Some(format!("[{type_string}]"))));
+                }
+                if aty.is_integral() {
+                    flags.push((sym::_Self, Some("[{integral}]".to_string())));
+                }
+            }
+
+            // Arrays give us `[]`, `[{ty}; _]` and `[{ty}; N]`
             if let ty::Array(aty, len) = self_ty.kind() {
-                flags.push((sym::_Self, Some("[]".to_owned())));
-                flags.push((sym::_Self, Some(format!("[{}]", aty))));
+                flags.push((sym::_Self, Some("[]".to_string())));
+                let len = len.val().try_to_value().and_then(|v| v.try_to_machine_usize(self.tcx));
+                flags.push((sym::_Self, Some(format!("[{}; _]", aty))));
+                if let Some(n) = len {
+                    flags.push((sym::_Self, Some(format!("[{}; {}]", aty, n))));
+                }
                 if let Some(def) = aty.ty_adt_def() {
                     // We also want to be able to select the array's type's original
                     // signature with no type arguments resolved
                     let type_string = self.tcx.type_of(def.did()).to_string();
-                    flags.push((sym::_Self, Some(format!("[{}]", type_string))));
-
-                    let len =
-                        len.val().try_to_value().and_then(|v| v.try_to_machine_usize(self.tcx));
-                    let string = match len {
-                        Some(n) => format!("[{}; {}]", type_string, n),
-                        None => format!("[{}; _]", type_string),
-                    };
-                    flags.push((sym::_Self, Some(string)));
+                    flags.push((sym::_Self, Some(format!("[{type_string}; _]"))));
+                    if let Some(n) = len {
+                        flags.push((sym::_Self, Some(format!("[{type_string}; {n}]"))));
+                    }
+                }
+                if aty.is_integral() {
+                    flags.push((sym::_Self, Some("[{integral}; _]".to_string())));
+                    if let Some(n) = len {
+                        flags.push((sym::_Self, Some(format!("[{{integral}}; {n}]"))));
+                    }
                 }
             }
             if let ty::Dynamic(traits, _) = self_ty.kind() {
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index 0c1ca65c48f..446b14a17ae 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -319,12 +319,8 @@ pub trait InferCtxtExt<'tcx> {
 
 fn predicate_constraint(generics: &hir::Generics<'_>, pred: String) -> (Span, String) {
     (
-        generics.where_clause.tail_span_for_suggestion(),
-        format!(
-            "{} {}",
-            if !generics.where_clause.predicates.is_empty() { "," } else { " where" },
-            pred,
-        ),
+        generics.tail_span_for_predicate_suggestion(),
+        format!("{} {}", if generics.has_where_clause { "," } else { " where" }, pred,),
     )
 }
 
@@ -346,7 +342,7 @@ fn suggest_restriction<'tcx>(
     //              -  ^^^^^^^^^ GenericBounds
     //              |
     //              &Ident
-    let span = generics.where_clause.span_for_predicates_or_empty_place();
+    let span = generics.span_for_predicates_or_empty_place();
     if span.from_expansion() || span.desugaring_kind().is_some() {
         return;
     }
@@ -396,21 +392,10 @@ fn suggest_restriction<'tcx>(
         let pred = trait_pred.to_predicate(tcx).to_string();
         let pred = pred.replace(&impl_trait_str, &type_param_name);
         let mut sugg = vec![
-            // Find the last of the generic parameters contained within the span of
-            // the generics
-            match generics
-                .params
-                .iter()
-                .map(|p| p.bounds_span_for_suggestions().unwrap_or(p.span.shrink_to_hi()))
-                .filter(|&span| generics.span.contains(span) && span.can_be_used_for_suggestions())
-                .max_by_key(|span| span.hi())
-            {
-                // `fn foo(t: impl Trait)`
-                //        ^ suggest `<T: Trait>` here
-                None => (generics.span, format!("<{}>", type_param)),
-                // `fn foo<A>(t: impl Trait)`
-                //        ^^^ suggest `<A, T: Trait>` here
-                Some(span) => (span, format!(", {}", type_param)),
+            if let Some(span) = generics.span_for_param_suggestion() {
+                (span, format!(", {}", type_param))
+            } else {
+                (generics.span, format!("<{}>", type_param))
             },
             // `fn foo(t: impl Trait)`
             //                       ^ suggest `where <T as Trait>::A: Bound`
@@ -2227,7 +2212,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
             }
             ObligationCauseCode::RepeatElementCopy { is_const_fn } => {
                 err.note(
-                    "the `Copy` trait is required because the repeated element will be copied",
+                    "the `Copy` trait is required because this value will be copied for each element of the array",
                 );
 
                 if is_const_fn {
diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs
index 84958136cac..b39310d1294 100644
--- a/compiler/rustc_trait_selection/src/traits/object_safety.rs
+++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs
@@ -221,7 +221,6 @@ fn get_sized_bounds(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span; 1]>
                 ..
             }) => Some(
                 generics
-                    .where_clause
                     .predicates
                     .iter()
                     .filter_map(|pred| {
@@ -399,8 +398,8 @@ fn virtual_call_violation_for_method<'tcx>(
         // We'll attempt to provide a structured suggestion for `Self: Sized`.
         let sugg =
             tcx.hir().get_if_local(method.def_id).as_ref().and_then(|node| node.generics()).map(
-                |generics| match generics.where_clause.predicates {
-                    [] => (" where Self: Sized", generics.where_clause.span),
+                |generics| match generics.predicates {
+                    [] => (" where Self: Sized", generics.where_clause_span),
                     [.., pred] => (", Self: Sized", pred.span().shrink_to_hi()),
                 },
             );
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index 8ba390c71db..c7a61cbe25a 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -1519,18 +1519,22 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
                 // Any type with multiple potential metadata types is therefore not eligible.
                 let self_ty = selcx.infcx().shallow_resolve(obligation.predicate.self_ty());
 
-                let tail = selcx.tcx().struct_tail_with_normalize(self_ty, |ty| {
-                    // We throw away any obligations we get from this, since we normalize
-                    // and confirm these obligations once again during confirmation
-                    normalize_with_depth(
-                        selcx,
-                        obligation.param_env,
-                        obligation.cause.clone(),
-                        obligation.recursion_depth + 1,
-                        ty,
-                    )
-                    .value
-                });
+                let tail = selcx.tcx().struct_tail_with_normalize(
+                    self_ty,
+                    |ty| {
+                        // We throw away any obligations we get from this, since we normalize
+                        // and confirm these obligations once again during confirmation
+                        normalize_with_depth(
+                            selcx,
+                            obligation.param_env,
+                            obligation.cause.clone(),
+                            obligation.recursion_depth + 1,
+                            ty,
+                        )
+                        .value
+                    },
+                    || {},
+                );
 
                 match tail.kind() {
                     ty::Bool
diff --git a/compiler/rustc_typeck/src/astconv/generics.rs b/compiler/rustc_typeck/src/astconv/generics.rs
index 5f5b81b8924..794e711b6c8 100644
--- a/compiler/rustc_typeck/src/astconv/generics.rs
+++ b/compiler/rustc_typeck/src/astconv/generics.rs
@@ -86,7 +86,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                         let param_type = tcx.infer_ctxt().enter(|infcx| {
                             infcx.resolve_numeric_literals_with_default(tcx.type_of(param.def_id))
                         });
-                        if param_type.is_suggestable() {
+                        if param_type.is_suggestable(tcx) {
                             err.span_suggestion(
                                 tcx.def_span(src_def_id),
                                 "consider changing this type parameter to be a `const` generic",
diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs
index 6bae0f2eac9..b8422ce3208 100644
--- a/compiler/rustc_typeck/src/astconv/mod.rs
+++ b/compiler/rustc_typeck/src/astconv/mod.rs
@@ -924,14 +924,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             let self_ty_def_id = tcx.hir().local_def_id(self_ty).to_def_id();
             for clause in where_clause {
                 if let hir::WherePredicate::BoundPredicate(pred) = clause {
-                    match pred.bounded_ty.kind {
-                        hir::TyKind::Path(hir::QPath::Resolved(_, path)) => match path.res {
-                            Res::Def(DefKind::TyParam, def_id) if def_id == self_ty_def_id => {}
-                            _ => continue,
-                        },
-                        _ => continue,
+                    if pred.is_param_bound(self_ty_def_id) {
+                        search_bounds(pred.bounds);
                     }
-                    search_bounds(pred.bounds);
                 }
             }
         }
@@ -2389,7 +2384,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     bf.unsafety,
                     bf.abi,
                     bf.decl,
-                    &hir::Generics::empty(),
                     None,
                     Some(ast_ty),
                 ))
@@ -2466,7 +2460,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     span,
                     ty,
                     opt_sugg: Some((span, Applicability::MachineApplicable))
-                        .filter(|_| ty.is_suggestable()),
+                        .filter(|_| ty.is_suggestable(tcx)),
                 });
 
                 ty
@@ -2551,8 +2545,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         unsafety: hir::Unsafety,
         abi: abi::Abi,
         decl: &hir::FnDecl<'_>,
-        generics: &hir::Generics<'_>,
-        ident_span: Option<Span>,
+        generics: Option<&hir::Generics<'_>>,
         hir_ty: Option<&hir::Ty<'_>>,
     ) -> ty::PolyFnSig<'tcx> {
         debug!("ty_of_fn");
@@ -2563,40 +2556,78 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
 
         // We proactively collect all the inferred type params to emit a single error per fn def.
         let mut visitor = HirPlaceholderCollector::default();
-        for ty in decl.inputs {
-            visitor.visit_ty(ty);
+        let mut infer_replacements = vec![];
+
+        if let Some(generics) = generics {
+            walk_generics(&mut visitor, generics);
         }
-        walk_generics(&mut visitor, generics);
 
-        let input_tys = decl.inputs.iter().map(|a| self.ty_of_arg(a, None));
+        let input_tys: Vec<_> = decl
+            .inputs
+            .iter()
+            .enumerate()
+            .map(|(i, a)| {
+                if let hir::TyKind::Infer = a.kind && !self.allow_ty_infer() {
+                    if let Some(suggested_ty) =
+                        self.suggest_trait_fn_ty_for_impl_fn_infer(hir_id, Some(i))
+                    {
+                        infer_replacements.push((a.span, suggested_ty.to_string()));
+                        return suggested_ty;
+                    }
+                }
+
+                // Only visit the type looking for `_` if we didn't fix the type above
+                visitor.visit_ty(a);
+                self.ty_of_arg(a, None)
+            })
+            .collect();
+
         let output_ty = match decl.output {
             hir::FnRetTy::Return(output) => {
-                visitor.visit_ty(output);
-                self.ast_ty_to_ty(output)
+                if let hir::TyKind::Infer = output.kind
+                    && !self.allow_ty_infer()
+                    && let Some(suggested_ty) =
+                        self.suggest_trait_fn_ty_for_impl_fn_infer(hir_id, None)
+                {
+                    infer_replacements.push((output.span, suggested_ty.to_string()));
+                    suggested_ty
+                } else {
+                    visitor.visit_ty(output);
+                    self.ast_ty_to_ty(output)
+                }
             }
             hir::FnRetTy::DefaultReturn(..) => tcx.mk_unit(),
         };
 
         debug!("ty_of_fn: output_ty={:?}", output_ty);
 
-        let fn_ty = tcx.mk_fn_sig(input_tys, output_ty, decl.c_variadic, unsafety, abi);
+        let fn_ty = tcx.mk_fn_sig(input_tys.into_iter(), output_ty, decl.c_variadic, unsafety, abi);
         let bare_fn_ty = ty::Binder::bind_with_vars(fn_ty, bound_vars);
 
-        if !self.allow_ty_infer() {
+        if !self.allow_ty_infer() && !(visitor.0.is_empty() && infer_replacements.is_empty()) {
             // We always collect the spans for placeholder types when evaluating `fn`s, but we
             // only want to emit an error complaining about them if infer types (`_`) are not
             // allowed. `allow_ty_infer` gates this behavior. We check for the presence of
             // `ident_span` to not emit an error twice when we have `fn foo(_: fn() -> _)`.
 
-            crate::collect::placeholder_type_error(
+            let mut diag = crate::collect::placeholder_type_error_diag(
                 tcx,
-                ident_span.map(|sp| sp.shrink_to_hi()),
-                generics.params,
+                generics,
                 visitor.0,
+                infer_replacements.iter().map(|(s, _)| *s).collect(),
                 true,
                 hir_ty,
                 "function",
             );
+
+            if !infer_replacements.is_empty() {
+                diag.multipart_suggestion(&format!(
+                    "try replacing `_` with the type{} in the corresponding trait method signature",
+                    rustc_errors::pluralize!(infer_replacements.len()),
+                ), infer_replacements, Applicability::MachineApplicable);
+            }
+
+            diag.emit();
         }
 
         // Find any late-bound regions declared in return type that do
@@ -2624,6 +2655,43 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         bare_fn_ty
     }
 
+    /// Given a fn_hir_id for a impl function, suggest the type that is found on the
+    /// corresponding function in the trait that the impl implements, if it exists.
+    /// If arg_idx is Some, then it corresponds to an input type index, otherwise it
+    /// corresponds to the return type.
+    fn suggest_trait_fn_ty_for_impl_fn_infer(
+        &self,
+        fn_hir_id: hir::HirId,
+        arg_idx: Option<usize>,
+    ) -> Option<Ty<'tcx>> {
+        let tcx = self.tcx();
+        let hir = tcx.hir();
+
+        let hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), ident, .. }) =
+            hir.get(fn_hir_id) else { return None };
+        let hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(i), .. }) =
+                hir.get(hir.get_parent_node(fn_hir_id)) else { bug!("ImplItem should have Impl parent") };
+
+        let trait_ref =
+            self.instantiate_mono_trait_ref(i.of_trait.as_ref()?, self.ast_ty_to_ty(i.self_ty));
+
+        let assoc = tcx.associated_items(trait_ref.def_id).find_by_name_and_kind(
+            tcx,
+            *ident,
+            ty::AssocKind::Fn,
+            trait_ref.def_id,
+        )?;
+
+        let fn_sig = tcx.fn_sig(assoc.def_id).subst(
+            tcx,
+            trait_ref.substs.extend_to(tcx, assoc.def_id, |param, _| tcx.mk_param_from_def(param)),
+        );
+
+        let ty = if let Some(arg_idx) = arg_idx { fn_sig.input(arg_idx) } else { fn_sig.output() };
+
+        Some(tcx.liberate_late_bound_regions(fn_hir_id.expect_owner().to_def_id(), ty))
+    }
+
     fn validate_late_bound_regions(
         &self,
         constrained_regions: FxHashSet<ty::BoundRegionKind>,
diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs
index 314236b1cdf..6a4c0c2091e 100644
--- a/compiler/rustc_typeck/src/check/check.rs
+++ b/compiler/rustc_typeck/src/check/check.rs
@@ -102,6 +102,12 @@ pub(super) fn check_fn<'a, 'tcx>(
             DUMMY_SP,
             param_env,
         ));
+    // HACK(oli-obk): we rewrite the declared return type, too, so that we don't end up inferring all
+    // unconstrained RPIT to have `()` as their hidden type. This would happen because further down we
+    // compare the ret_coercion with declared_ret_ty, and anything uninferred would be inferred to the
+    // opaque type itself. That again would cause writeback to assume we have a recursive call site
+    // and do the sadly stabilized fallback to `()`.
+    let declared_ret_ty = ret_ty;
     fcx.ret_coercion = Some(RefCell::new(CoerceMany::new(ret_ty)));
     fcx.ret_type_span = Some(decl.output.span());
 
diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs
index 4ab6f2cdafb..6d78a863d54 100644
--- a/compiler/rustc_typeck/src/check/compare_method.rs
+++ b/compiler/rustc_typeck/src/check/compare_method.rs
@@ -804,7 +804,8 @@ fn compare_synthetic_generics<'tcx>(
         iter::zip(impl_m_type_params, trait_m_type_params)
     {
         if impl_synthetic != trait_synthetic {
-            let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_def_id.expect_local());
+            let impl_def_id = impl_def_id.expect_local();
+            let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_def_id);
             let impl_span = tcx.hir().span(impl_hir_id);
             let trait_span = tcx.def_span(trait_def_id);
             let mut err = struct_span_err!(
@@ -868,14 +869,14 @@ fn compare_synthetic_generics<'tcx>(
                             hir::ImplItemKind::Fn(ref sig, _) => sig.decl.inputs,
                             _ => unreachable!(),
                         };
-                        struct Visitor(Option<Span>, hir::def_id::DefId);
+                        struct Visitor(Option<Span>, hir::def_id::LocalDefId);
                         impl<'v> intravisit::Visitor<'v> for Visitor {
                             fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
                                 intravisit::walk_ty(self, ty);
                                 if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) =
                                     ty.kind
                                     && let Res::Def(DefKind::TyParam, def_id) = path.res
-                                    && def_id == self.1
+                                    && def_id == self.1.to_def_id()
                                 {
                                     self.0 = Some(ty.span);
                                 }
@@ -887,17 +888,7 @@ fn compare_synthetic_generics<'tcx>(
                         }
                         let span = visitor.0?;
 
-                        let bounds =
-                            impl_m.generics.params.iter().find_map(|param| match param.kind {
-                                GenericParamKind::Lifetime { .. } => None,
-                                GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {
-                                    if param.hir_id == impl_hir_id {
-                                        Some(&param.bounds)
-                                    } else {
-                                        None
-                                    }
-                                }
-                            })?;
+                        let bounds = impl_m.generics.bounds_for_param(impl_def_id).next()?.bounds;
                         let bounds = bounds.first()?.span().to(bounds.last()?.span());
                         let bounds = tcx.sess.source_map().span_to_snippet(bounds).ok()?;
 
diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs
index e88082dbb97..a1e8d2040dd 100644
--- a/compiler/rustc_typeck/src/check/expr.rs
+++ b/compiler/rustc_typeck/src/check/expr.rs
@@ -21,11 +21,13 @@ use crate::errors::{
 };
 use crate::type_error_struct;
 
+use super::suggest_call_constructor;
 use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive};
 use rustc_ast as ast;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_errors::Diagnostic;
+use rustc_errors::EmissionGuarantee;
 use rustc_errors::ErrorGuaranteed;
 use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, DiagnosticId};
 use rustc_hir as hir;
@@ -78,10 +80,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // While we don't allow *arbitrary* coercions here, we *do* allow
         // coercions from ! to `expected`.
         if ty.is_never() {
-            assert!(
-                !self.typeck_results.borrow().adjustments().contains_key(expr.hir_id),
-                "expression with never type wound up being adjusted"
-            );
+            if let Some(adjustments) = self.typeck_results.borrow().adjustments().get(expr.hir_id) {
+                self.tcx().sess.delay_span_bug(
+                    expr.span,
+                    "expression with never type wound up being adjusted",
+                );
+                return if let [Adjustment { kind: Adjust::NeverToAny, target }] = &adjustments[..] {
+                    target.to_owned()
+                } else {
+                    self.tcx().ty_error()
+                };
+            }
+
             let adj_ty = self.next_ty_var(TypeVariableOrigin {
                 kind: TypeVariableOriginKind::AdjustmentType,
                 span: expr.span,
@@ -1282,9 +1292,49 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             return tcx.ty_error();
         }
 
+        self.check_repeat_element_needs_copy_bound(element, count, element_ty);
+
         tcx.mk_ty(ty::Array(t, count))
     }
 
+    fn check_repeat_element_needs_copy_bound(
+        &self,
+        element: &hir::Expr<'_>,
+        count: ty::Const<'tcx>,
+        element_ty: Ty<'tcx>,
+    ) {
+        let tcx = self.tcx;
+        // Actual constants as the repeat element get inserted repeatedly instead of getting copied via Copy.
+        match &element.kind {
+            hir::ExprKind::ConstBlock(..) => return,
+            hir::ExprKind::Path(qpath) => {
+                let res = self.typeck_results.borrow().qpath_res(qpath, element.hir_id);
+                if let Res::Def(DefKind::Const | DefKind::AssocConst | DefKind::AnonConst, _) = res
+                {
+                    return;
+                }
+            }
+            _ => {}
+        }
+        // If someone calls a const fn, they can extract that call out into a separate constant (or a const
+        // block in the future), so we check that to tell them that in the diagnostic. Does not affect typeck.
+        let is_const_fn = match element.kind {
+            hir::ExprKind::Call(func, _args) => match *self.node_ty(func.hir_id).kind() {
+                ty::FnDef(def_id, _) => tcx.is_const_fn(def_id),
+                _ => false,
+            },
+            _ => false,
+        };
+
+        // If the length is 0, we don't create any elements, so we don't copy any. If the length is 1, we
+        // don't copy that one element, we move it. Only check for Copy if the length is larger.
+        if count.try_eval_usize(tcx, self.param_env).map_or(true, |len| len > 1) {
+            let lang_item = self.tcx.require_lang_item(LangItem::Copy, None);
+            let code = traits::ObligationCauseCode::RepeatElementCopy { is_const_fn };
+            self.require_type_meets(element_ty, element.span, code, lang_item);
+        }
+    }
+
     fn check_expr_tuple(
         &self,
         elts: &'tcx [hir::Expr<'tcx>],
@@ -1978,6 +2028,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         self.tcx().ty_error()
     }
 
+    fn check_call_constructor<G: EmissionGuarantee>(
+        &self,
+        err: &mut DiagnosticBuilder<'_, G>,
+        base: &'tcx hir::Expr<'tcx>,
+        def_id: DefId,
+    ) {
+        let local_id = def_id.expect_local();
+        let hir_id = self.tcx.hir().local_def_id_to_hir_id(local_id);
+        let node = self.tcx.hir().get(hir_id);
+
+        if let Some(fields) = node.tuple_fields() {
+            let kind = match self.tcx.opt_def_kind(local_id) {
+                Some(DefKind::Ctor(of, _)) => of,
+                _ => return,
+            };
+
+            suggest_call_constructor(base.span, kind, fields.len(), err);
+        }
+    }
+
     fn suggest_await_on_field_access(
         &self,
         err: &mut Diagnostic,
@@ -2047,6 +2117,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             ty::Opaque(_, _) => {
                 self.suggest_await_on_field_access(&mut err, field, base, expr_t.peel_refs());
             }
+            ty::FnDef(def_id, _) => {
+                self.check_call_constructor(&mut err, base, def_id);
+            }
             _ => {}
         }
 
@@ -2277,14 +2350,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // try to add a suggestion in case the field is a nested field of a field of the Adt
         if let Some((fields, substs)) = self.get_field_candidates(span, expr_t) {
             for candidate_field in fields.iter() {
-                if let Some(field_path) = self.check_for_nested_field(
+                if let Some(mut field_path) = self.check_for_nested_field_satisfying(
                     span,
-                    field,
+                    &|candidate_field, _| candidate_field.ident(self.tcx()) == field,
                     candidate_field,
                     substs,
                     vec![],
                     self.tcx.parent_module(id).to_def_id(),
                 ) {
+                    // field_path includes `field` that we're looking for, so pop it.
+                    field_path.pop();
+
                     let field_path_str = field_path
                         .iter()
                         .map(|id| id.name.to_ident_string())
@@ -2304,7 +2380,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         err
     }
 
-    fn get_field_candidates(
+    crate fn get_field_candidates(
         &self,
         span: Span,
         base_t: Ty<'tcx>,
@@ -2329,49 +2405,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     /// This method is called after we have encountered a missing field error to recursively
     /// search for the field
-    fn check_for_nested_field(
+    crate fn check_for_nested_field_satisfying(
         &self,
         span: Span,
-        target_field: Ident,
+        matches: &impl Fn(&ty::FieldDef, Ty<'tcx>) -> bool,
         candidate_field: &ty::FieldDef,
         subst: SubstsRef<'tcx>,
         mut field_path: Vec<Ident>,
         id: DefId,
     ) -> Option<Vec<Ident>> {
         debug!(
-            "check_for_nested_field(span: {:?}, candidate_field: {:?}, field_path: {:?}",
+            "check_for_nested_field_satisfying(span: {:?}, candidate_field: {:?}, field_path: {:?}",
             span, candidate_field, field_path
         );
 
-        if candidate_field.ident(self.tcx) == target_field {
-            Some(field_path)
-        } else if field_path.len() > 3 {
+        if field_path.len() > 3 {
             // For compile-time reasons and to avoid infinite recursion we only check for fields
             // up to a depth of three
             None
         } else {
             // recursively search fields of `candidate_field` if it's a ty::Adt
-
             field_path.push(candidate_field.ident(self.tcx).normalize_to_macros_2_0());
             let field_ty = candidate_field.ty(self.tcx, subst);
             if let Some((nested_fields, subst)) = self.get_field_candidates(span, field_ty) {
                 for field in nested_fields.iter() {
-                    let accessible = field.vis.is_accessible_from(id, self.tcx);
-                    if accessible {
-                        let ident = field.ident(self.tcx).normalize_to_macros_2_0();
-                        if ident == target_field {
+                    if field.vis.is_accessible_from(id, self.tcx) {
+                        if matches(candidate_field, field_ty) {
                             return Some(field_path);
-                        }
-                        let field_path = field_path.clone();
-                        if let Some(path) = self.check_for_nested_field(
+                        } else if let Some(field_path) = self.check_for_nested_field_satisfying(
                             span,
-                            target_field,
+                            matches,
                             field,
                             subst,
-                            field_path,
+                            field_path.clone(),
                             id,
                         ) {
-                            return Some(path);
+                            return Some(field_path);
                         }
                     }
                 }
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
index 152be4bd538..8feb7170983 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
@@ -757,7 +757,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         formal_args: &[Ty<'tcx>],
     ) -> Option<Vec<Ty<'tcx>>> {
         let formal_ret = self.resolve_vars_with_obligations(formal_ret);
-        let Some(ret_ty) = expected_ret.only_has_type(self) else { return None };
+        let ret_ty = expected_ret.only_has_type(self)?;
 
         // HACK(oli-obk): This is a hack to keep RPIT and TAIT in sync wrt their behaviour.
         // Without it, the inference
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
index 80f6190732a..616aa11f00a 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
@@ -968,9 +968,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 SuggestionText::Remove(plural) => {
                     Some(format!("remove the extra argument{}", if plural { "s" } else { "" }))
                 }
-                SuggestionText::Swap => Some(format!("swap these arguments")),
-                SuggestionText::Reorder => Some(format!("reorder these arguments")),
-                SuggestionText::DidYouMean => Some(format!("did you mean")),
+                SuggestionText::Swap => Some("swap these arguments".to_string()),
+                SuggestionText::Reorder => Some("reorder these arguments".to_string()),
+                SuggestionText::DidYouMean => Some("did you mean".to_string()),
             };
             if let Some(suggestion_text) = suggestion_text {
                 let source_map = self.sess().source_map();
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
index 62518408b8b..681d1e37f86 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
@@ -1,5 +1,6 @@
 use super::FnCtxt;
 use crate::astconv::AstConv;
+use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel};
 
 use rustc_ast::util::parser::ExprPrecedence;
 use rustc_errors::{Applicability, Diagnostic, MultiSpan};
@@ -525,30 +526,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             self.resolve_numeric_literals_with_default(self.resolve_vars_if_possible(found));
         // Only suggest changing the return type for methods that
         // haven't set a return type at all (and aren't `fn main()` or an impl).
-        match (&fn_decl.output, found.is_suggestable(), can_suggest, expected.is_unit()) {
+        match (&fn_decl.output, found.is_suggestable(self.tcx), can_suggest, expected.is_unit()) {
             (&hir::FnRetTy::DefaultReturn(span), true, true, true) => {
-                err.span_suggestion(
-                    span,
-                    "try adding a return type",
-                    format!("-> {} ", found),
-                    Applicability::MachineApplicable,
-                );
+                err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found });
                 true
             }
             (&hir::FnRetTy::DefaultReturn(span), false, true, true) => {
                 // FIXME: if `found` could be `impl Iterator` or `impl Fn*`, we should suggest
                 // that.
-                err.span_suggestion(
-                    span,
-                    "a return type might be missing here",
-                    "-> _ ".to_string(),
-                    Applicability::HasPlaceholders,
-                );
+                err.subdiagnostic(AddReturnTypeSuggestion::MissingHere { span });
                 true
             }
             (&hir::FnRetTy::DefaultReturn(span), _, false, true) => {
                 // `fn main()` must return `()`, do not suggest changing return type
-                err.span_label(span, "expected `()` because of default return type");
+                err.subdiagnostic(ExpectedReturnTypeLabel::Unit { span });
                 true
             }
             // expectation was caused by something else, not the default return
@@ -557,16 +548,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // Only point to return type if the expected type is the return type, as if they
                 // are not, the expectation must have been caused by something else.
                 debug!("suggest_missing_return_type: return type {:?} node {:?}", ty, ty.kind);
-                let sp = ty.span;
+                let span = ty.span;
                 let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, ty);
                 debug!("suggest_missing_return_type: return type {:?}", ty);
                 debug!("suggest_missing_return_type: expected type {:?}", ty);
                 let bound_vars = self.tcx.late_bound_vars(fn_id);
                 let ty = Binder::bind_with_vars(ty, bound_vars);
-                let ty = self.normalize_associated_types_in(sp, ty);
+                let ty = self.normalize_associated_types_in(span, ty);
                 let ty = self.tcx.erase_late_bound_regions(ty);
                 if self.can_coerce(expected, ty) {
-                    err.span_label(sp, format!("expected `{}` because of return type", expected));
+                    err.subdiagnostic(ExpectedReturnTypeLabel::Other { span, expected });
                     self.try_suggest_return_impl_trait(err, expected, ty, fn_id);
                     return true;
                 }
@@ -608,17 +599,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             kind:
                 hir::ItemKind::Fn(
                     hir::FnSig { decl: hir::FnDecl { inputs: fn_parameters, output: fn_return, .. }, .. },
-                    hir::Generics { params, where_clause, .. },
+                    hir::Generics { params, predicates, .. },
                     _body_id,
                 ),
             ..
         })) = fn_node else { return };
 
-        let Some(expected_generic_param) = params.get(expected_ty_as_param.index as usize) else { return };
+        if params.get(expected_ty_as_param.index as usize).is_none() {
+            return;
+        };
 
         // get all where BoundPredicates here, because they are used in to cases below
-        let where_predicates = where_clause
-            .predicates
+        let where_predicates = predicates
             .iter()
             .filter_map(|p| match p {
                 WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
@@ -649,10 +641,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             where_predicates.iter().flatten().flat_map(|bounds| bounds.iter());
 
         // extract all bounds from the source code using their spans
-        let all_matching_bounds_strs = expected_generic_param
-            .bounds
-            .iter()
-            .chain(predicates_from_where)
+        let all_matching_bounds_strs = predicates_from_where
             .filter_map(|bound| match bound {
                 GenericBound::Trait(_, _) => {
                     self.tcx.sess.source_map().span_to_snippet(bound.span()).ok()
diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs
index 75a8ad8a159..721f251650f 100644
--- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs
+++ b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs
@@ -257,7 +257,6 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> {
                 | hir::Node::Ctor(..)
                 | hir::Node::Lifetime(..)
                 | hir::Node::GenericParam(..)
-                | hir::Node::Visibility(..)
                 | hir::Node::Crate(..)
                 | hir::Node::Infer(..) => bug!("Unsupported branch target: {:?}", node),
             }
diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs
index 2921176ca4b..640dccf66b0 100644
--- a/compiler/rustc_typeck/src/check/method/suggest.rs
+++ b/compiler/rustc_typeck/src/check/method/suggest.rs
@@ -8,6 +8,7 @@ use rustc_errors::{
     MultiSpan,
 };
 use rustc_hir as hir;
+use rustc_hir::def::DefKind;
 use rustc_hir::def_id::DefId;
 use rustc_hir::lang_items::LangItem;
 use rustc_hir::{ExprKind, Node, QPath};
@@ -28,8 +29,8 @@ use rustc_trait_selection::traits::{
 use std::cmp::Ordering;
 use std::iter;
 
-use super::probe::Mode;
-use super::{CandidateSource, MethodError, NoMatchData};
+use super::probe::{Mode, ProbeScope};
+use super::{super::suggest_call_constructor, CandidateSource, MethodError, NoMatchData};
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool {
@@ -271,205 +272,82 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         (None, true) => "variant",
                     }
                 };
-                // FIXME(eddyb) this indentation is probably unnecessary.
-                let mut err = {
-                    // Suggest clamping down the type if the method that is being attempted to
-                    // be used exists at all, and the type is an ambiguous numeric type
-                    // ({integer}/{float}).
-                    let mut candidates = all_traits(self.tcx)
-                        .into_iter()
-                        .filter_map(|info| self.associated_value(info.def_id, item_name));
-                    // There are methods that are defined on the primitive types and won't be
-                    // found when exploring `all_traits`, but we also need them to be accurate on
-                    // our suggestions (#47759).
-                    let found_assoc = |ty: Ty<'tcx>| {
-                        simplify_type(tcx, ty, TreatParams::AsPlaceholders)
-                            .and_then(|simp| {
-                                tcx.incoherent_impls(simp)
-                                    .iter()
-                                    .find_map(|&id| self.associated_value(id, item_name))
-                            })
-                            .is_some()
-                    };
-                    let found_candidate = candidates.next().is_some()
-                        || found_assoc(tcx.types.i8)
-                        || found_assoc(tcx.types.i16)
-                        || found_assoc(tcx.types.i32)
-                        || found_assoc(tcx.types.i64)
-                        || found_assoc(tcx.types.i128)
-                        || found_assoc(tcx.types.u8)
-                        || found_assoc(tcx.types.u16)
-                        || found_assoc(tcx.types.u32)
-                        || found_assoc(tcx.types.u64)
-                        || found_assoc(tcx.types.u128)
-                        || found_assoc(tcx.types.f32)
-                        || found_assoc(tcx.types.f32);
-                    if let (true, false, SelfSource::MethodCall(expr), true) = (
-                        actual.is_numeric(),
-                        actual.has_concrete_skeleton(),
-                        source,
-                        found_candidate,
-                    ) {
-                        let mut err = struct_span_err!(
-                            tcx.sess,
-                            span,
-                            E0689,
-                            "can't call {} `{}` on ambiguous numeric type `{}`",
-                            item_kind,
-                            item_name,
-                            ty_str
-                        );
-                        let concrete_type = if actual.is_integral() { "i32" } else { "f32" };
-                        match expr.kind {
-                            ExprKind::Lit(ref lit) => {
-                                // numeric literal
-                                let snippet = tcx
-                                    .sess
-                                    .source_map()
-                                    .span_to_snippet(lit.span)
-                                    .unwrap_or_else(|_| "<numeric literal>".to_owned());
-
-                                // If this is a floating point literal that ends with '.',
-                                // get rid of it to stop this from becoming a member access.
-                                let snippet = snippet.strip_suffix('.').unwrap_or(&snippet);
 
-                                err.span_suggestion(
-                                    lit.span,
-                                    &format!(
-                                        "you must specify a concrete type for this numeric value, \
-                                         like `{}`",
-                                        concrete_type
-                                    ),
-                                    format!("{snippet}_{concrete_type}"),
-                                    Applicability::MaybeIncorrect,
-                                );
-                            }
-                            ExprKind::Path(QPath::Resolved(_, path)) => {
-                                // local binding
-                                if let hir::def::Res::Local(hir_id) = path.res {
-                                    let span = tcx.hir().span(hir_id);
-                                    let snippet = tcx.sess.source_map().span_to_snippet(span);
-                                    let filename = tcx.sess.source_map().span_to_filename(span);
-
-                                    let parent_node =
-                                        self.tcx.hir().get(self.tcx.hir().get_parent_node(hir_id));
-                                    let msg = format!(
-                                        "you must specify a type for this binding, like `{}`",
-                                        concrete_type,
-                                    );
-
-                                    match (filename, parent_node, snippet) {
-                                        (
-                                            FileName::Real(_),
-                                            Node::Local(hir::Local {
-                                                source: hir::LocalSource::Normal,
-                                                ty,
-                                                ..
-                                            }),
-                                            Ok(ref snippet),
-                                        ) => {
-                                            err.span_suggestion(
-                                                // account for `let x: _ = 42;`
-                                                //                  ^^^^
-                                                span.to(ty
-                                                    .as_ref()
-                                                    .map(|ty| ty.span)
-                                                    .unwrap_or(span)),
-                                                &msg,
-                                                format!("{}: {}", snippet, concrete_type),
-                                                Applicability::MaybeIncorrect,
-                                            );
-                                        }
-                                        _ => {
-                                            err.span_label(span, msg);
-                                        }
-                                    }
-                                }
+                if self.suggest_constraining_numerical_ty(
+                    tcx, actual, source, span, item_kind, item_name, &ty_str,
+                ) {
+                    return None;
+                }
+
+                span = item_name.span;
+
+                // Don't show generic arguments when the method can't be found in any implementation (#81576).
+                let mut ty_str_reported = ty_str.clone();
+                if let ty::Adt(_, generics) = actual.kind() {
+                    if generics.len() > 0 {
+                        let mut autoderef = self.autoderef(span, actual);
+                        let candidate_found = autoderef.any(|(ty, _)| {
+                            if let ty::Adt(adt_deref, _) = ty.kind() {
+                                self.tcx
+                                    .inherent_impls(adt_deref.did())
+                                    .iter()
+                                    .filter_map(|def_id| self.associated_value(*def_id, item_name))
+                                    .count()
+                                    >= 1
+                            } else {
+                                false
                             }
-                            _ => {}
-                        }
-                        err.emit();
-                        return None;
-                    } else {
-                        span = item_name.span;
-
-                        // Don't show generic arguments when the method can't be found in any implementation (#81576).
-                        let mut ty_str_reported = ty_str.clone();
-                        if let ty::Adt(_, generics) = actual.kind() {
-                            if generics.len() > 0 {
-                                let mut autoderef = self.autoderef(span, actual);
-                                let candidate_found = autoderef.any(|(ty, _)| {
-                                    if let ty::Adt(adt_deref, _) = ty.kind() {
-                                        self.tcx
-                                            .inherent_impls(adt_deref.did())
-                                            .iter()
-                                            .filter_map(|def_id| {
-                                                self.associated_value(*def_id, item_name)
-                                            })
-                                            .count()
-                                            >= 1
-                                    } else {
-                                        false
-                                    }
-                                });
-                                let has_deref = autoderef.step_count() > 0;
-                                if !candidate_found
-                                    && !has_deref
-                                    && unsatisfied_predicates.is_empty()
-                                {
-                                    if let Some((path_string, _)) = ty_str.split_once('<') {
-                                        ty_str_reported = path_string.to_string();
-                                    }
-                                }
+                        });
+                        let has_deref = autoderef.step_count() > 0;
+                        if !candidate_found && !has_deref && unsatisfied_predicates.is_empty() {
+                            if let Some((path_string, _)) = ty_str.split_once('<') {
+                                ty_str_reported = path_string.to_string();
                             }
                         }
+                    }
+                }
 
-                        let mut err = struct_span_err!(
-                            tcx.sess,
-                            span,
-                            E0599,
-                            "no {} named `{}` found for {} `{}` in the current scope",
-                            item_kind,
-                            item_name,
-                            actual.prefix_string(self.tcx),
-                            ty_str_reported,
-                        );
-                        if let Mode::MethodCall = mode && let SelfSource::MethodCall(cal) = source {
-                            self.suggest_await_before_method(
-                                &mut err, item_name, actual, cal, span,
-                            );
-                        }
-                        if let Some(span) =
-                            tcx.resolutions(()).confused_type_with_std_module.get(&span)
-                        {
-                            if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(*span) {
-                                err.span_suggestion(
-                                    *span,
-                                    "you are looking for the module in `std`, \
+                let mut err = struct_span_err!(
+                    tcx.sess,
+                    span,
+                    E0599,
+                    "no {} named `{}` found for {} `{}` in the current scope",
+                    item_kind,
+                    item_name,
+                    actual.prefix_string(self.tcx),
+                    ty_str_reported,
+                );
+                if actual.references_error() {
+                    err.downgrade_to_delayed_bug();
+                }
+
+                if let Mode::MethodCall = mode && let SelfSource::MethodCall(cal) = source {
+                    self.suggest_await_before_method(
+                        &mut err, item_name, actual, cal, span,
+                    );
+                }
+                if let Some(span) = tcx.resolutions(()).confused_type_with_std_module.get(&span) {
+                    if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(*span) {
+                        err.span_suggestion(
+                            *span,
+                            "you are looking for the module in `std`, \
                                      not the primitive type",
-                                    format!("std::{}", snippet),
-                                    Applicability::MachineApplicable,
-                                );
-                            }
-                        }
-                        if let ty::RawPtr(_) = &actual.kind() {
-                            err.note(
-                                "try using `<*const T>::as_ref()` to get a reference to the \
+                            format!("std::{}", snippet),
+                            Applicability::MachineApplicable,
+                        );
+                    }
+                }
+                if let ty::RawPtr(_) = &actual.kind() {
+                    err.note(
+                        "try using `<*const T>::as_ref()` to get a reference to the \
                                       type behind the pointer: https://doc.rust-lang.org/std/\
                                       primitive.pointer.html#method.as_ref",
-                            );
-                            err.note(
-                                "using `<*const T>::as_ref()` on a pointer \
+                    );
+                    err.note(
+                        "using `<*const T>::as_ref()` on a pointer \
                                       which is unaligned or points to invalid \
                                       or uninitialized memory is undefined behavior",
-                            );
-                        }
-                        err
-                    }
-                };
-
-                if actual.references_error() {
-                    err.downgrade_to_delayed_bug();
+                    );
                 }
 
                 if let Some(def) = actual.ty_adt_def() {
@@ -488,19 +366,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 }
 
                 if self.is_fn_ty(rcvr_ty, span) {
-                    fn report_function<T: std::fmt::Display>(err: &mut Diagnostic, name: T) {
-                        err.note(
-                            &format!("`{}` is a function, perhaps you wish to call it", name,),
-                        );
-                    }
-
                     if let SelfSource::MethodCall(expr) = source {
-                        if let Ok(expr_string) = tcx.sess.source_map().span_to_snippet(expr.span) {
-                            report_function(&mut err, expr_string);
-                        } else if let ExprKind::Path(QPath::Resolved(_, path)) = expr.kind {
-                            if let Some(segment) = path.segments.last() {
-                                report_function(&mut err, segment.ident);
+                        let suggest = if let ty::FnDef(def_id, _) = rcvr_ty.kind() {
+                            let local_id = def_id.expect_local();
+                            let hir_id = tcx.hir().local_def_id_to_hir_id(local_id);
+                            let node = tcx.hir().get(hir_id);
+                            let fields = node.tuple_fields();
+
+                            if let Some(fields) = fields
+                                && let Some(DefKind::Ctor(of, _)) = self.tcx.opt_def_kind(local_id) {
+                                    Some((fields, of))
+                            } else {
+                                None
                             }
+                        } else {
+                            None
+                        };
+
+                        // If the function is a tuple constructor, we recommend that they call it
+                        if let Some((fields, kind)) = suggest {
+                            suggest_call_constructor(expr.span, kind, fields.len(), &mut err);
+                        } else {
+                            // General case
+                            err.span_label(
+                                expr.span,
+                                "this is a function, perhaps you wish to call it",
+                            );
                         }
                     }
                 }
@@ -642,12 +533,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                 };
                                 if let Some(hir::Node::Item(hir::Item { kind, .. })) = node {
                                     if let Some(g) = kind.generics() {
-                                        let key = match g.where_clause.predicates {
+                                        let key = match g.predicates {
                                             [.., pred] => (pred.span().shrink_to_hi(), false),
-                                            [] => (
-                                                g.where_clause.span_for_predicates_or_empty_place(),
-                                                true,
-                                            ),
+                                            [] => (g.span_for_predicates_or_empty_place(), true),
                                         };
                                         type_params
                                             .entry(key)
@@ -985,7 +873,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     }
                 }
 
-                let mut label_span_not_found = || {
+                let label_span_not_found = |err: &mut DiagnosticBuilder<'_, _>| {
                     if unsatisfied_predicates.is_empty() {
                         err.span_label(span, format!("{item_kind} not found in `{ty_str}`"));
                         let is_string_or_ref_str = match actual.kind() {
@@ -1071,62 +959,54 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // If the method name is the name of a field with a function or closure type,
                 // give a helping note that it has to be called as `(x.f)(...)`.
                 if let SelfSource::MethodCall(expr) = source {
-                    let field_receiver =
-                        self.autoderef(span, rcvr_ty).find_map(|(ty, _)| match ty.kind() {
-                            ty::Adt(def, substs) if !def.is_enum() => {
-                                let variant = &def.non_enum_variant();
-                                self.tcx.find_field_index(item_name, variant).map(|index| {
-                                    let field = &variant.fields[index];
-                                    let field_ty = field.ty(tcx, substs);
-                                    (field, field_ty)
-                                })
-                            }
-                            _ => None,
-                        });
+                    if !self.suggest_field_call(span, rcvr_ty, expr, item_name, &mut err)
+                        && lev_candidate.is_none()
+                        && !custom_span_label
+                    {
+                        label_span_not_found(&mut err);
+                    }
+                } else if !custom_span_label {
+                    label_span_not_found(&mut err);
+                }
 
-                    if let Some((field, field_ty)) = field_receiver {
-                        let scope = self.tcx.parent_module(self.body_id).to_def_id();
-                        let is_accessible = field.vis.is_accessible_from(scope, self.tcx);
+                if let SelfSource::MethodCall(expr) = source
+                    && let Some((fields, substs)) = self.get_field_candidates(span, actual)
+                {
+                    let call_expr =
+                        self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id));
+                    for candidate_field in fields.iter() {
+                        if let Some(field_path) = self.check_for_nested_field_satisfying(
+                            span,
+                            &|_, field_ty| {
+                                self.lookup_probe(
+                                    span,
+                                    item_name,
+                                    field_ty,
+                                    call_expr,
+                                    ProbeScope::AllTraits,
+                                )
+                                .is_ok()
+                            },
+                            candidate_field,
+                            substs,
+                            vec![],
+                            self.tcx.parent_module(expr.hir_id).to_def_id(),
+                        ) {
+                            let field_path_str = field_path
+                                .iter()
+                                .map(|id| id.name.to_ident_string())
+                                .collect::<Vec<String>>()
+                                .join(".");
+                            debug!("field_path_str: {:?}", field_path_str);
 
-                        if is_accessible {
-                            if self.is_fn_ty(field_ty, span) {
-                                let expr_span = expr.span.to(item_name.span);
-                                err.multipart_suggestion(
-                                    &format!(
-                                        "to call the function stored in `{}`, \
-                                         surround the field access with parentheses",
-                                        item_name,
-                                    ),
-                                    vec![
-                                        (expr_span.shrink_to_lo(), '('.to_string()),
-                                        (expr_span.shrink_to_hi(), ')'.to_string()),
-                                    ],
-                                    Applicability::MachineApplicable,
-                                );
-                            } else {
-                                let call_expr = self
-                                    .tcx
-                                    .hir()
-                                    .expect_expr(self.tcx.hir().get_parent_node(expr.hir_id));
-
-                                if let Some(span) = call_expr.span.trim_start(item_name.span) {
-                                    err.span_suggestion(
-                                        span,
-                                        "remove the arguments",
-                                        String::new(),
-                                        Applicability::MaybeIncorrect,
-                                    );
-                                }
-                            }
+                            err.span_suggestion_verbose(
+                                item_name.span.shrink_to_lo(),
+                                "one of the expressions' fields has a method of the same name",
+                                format!("{field_path_str}."),
+                                Applicability::MaybeIncorrect,
+                            );
                         }
-
-                        let field_kind = if is_accessible { "field" } else { "private field" };
-                        err.span_label(item_name.span, format!("{}, not a method", field_kind));
-                    } else if lev_candidate.is_none() && !custom_span_label {
-                        label_span_not_found();
                     }
-                } else if !custom_span_label {
-                    label_span_not_found();
                 }
 
                 bound_spans.sort();
@@ -1273,6 +1153,187 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         None
     }
 
+    fn suggest_field_call(
+        &self,
+        span: Span,
+        rcvr_ty: Ty<'tcx>,
+        expr: &hir::Expr<'_>,
+        item_name: Ident,
+        err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>,
+    ) -> bool {
+        let tcx = self.tcx;
+        let field_receiver = self.autoderef(span, rcvr_ty).find_map(|(ty, _)| match ty.kind() {
+            ty::Adt(def, substs) if !def.is_enum() => {
+                let variant = &def.non_enum_variant();
+                tcx.find_field_index(item_name, variant).map(|index| {
+                    let field = &variant.fields[index];
+                    let field_ty = field.ty(tcx, substs);
+                    (field, field_ty)
+                })
+            }
+            _ => None,
+        });
+        if let Some((field, field_ty)) = field_receiver {
+            let scope = tcx.parent_module(self.body_id).to_def_id();
+            let is_accessible = field.vis.is_accessible_from(scope, tcx);
+
+            if is_accessible {
+                if self.is_fn_ty(field_ty, span) {
+                    let expr_span = expr.span.to(item_name.span);
+                    err.multipart_suggestion(
+                        &format!(
+                            "to call the function stored in `{}`, \
+                                         surround the field access with parentheses",
+                            item_name,
+                        ),
+                        vec![
+                            (expr_span.shrink_to_lo(), '('.to_string()),
+                            (expr_span.shrink_to_hi(), ')'.to_string()),
+                        ],
+                        Applicability::MachineApplicable,
+                    );
+                } else {
+                    let call_expr = tcx.hir().expect_expr(tcx.hir().get_parent_node(expr.hir_id));
+
+                    if let Some(span) = call_expr.span.trim_start(item_name.span) {
+                        err.span_suggestion(
+                            span,
+                            "remove the arguments",
+                            String::new(),
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                }
+            }
+
+            let field_kind = if is_accessible { "field" } else { "private field" };
+            err.span_label(item_name.span, format!("{}, not a method", field_kind));
+            return true;
+        }
+        false
+    }
+
+    fn suggest_constraining_numerical_ty(
+        &self,
+        tcx: TyCtxt<'tcx>,
+        actual: Ty<'tcx>,
+        source: SelfSource<'_>,
+        span: Span,
+        item_kind: &str,
+        item_name: Ident,
+        ty_str: &str,
+    ) -> bool {
+        let found_candidate = all_traits(self.tcx)
+            .into_iter()
+            .any(|info| self.associated_value(info.def_id, item_name).is_some());
+        let found_assoc = |ty: Ty<'tcx>| {
+            simplify_type(tcx, ty, TreatParams::AsPlaceholders)
+                .and_then(|simp| {
+                    tcx.incoherent_impls(simp)
+                        .iter()
+                        .find_map(|&id| self.associated_value(id, item_name))
+                })
+                .is_some()
+        };
+        let found_candidate = found_candidate
+            || found_assoc(tcx.types.i8)
+            || found_assoc(tcx.types.i16)
+            || found_assoc(tcx.types.i32)
+            || found_assoc(tcx.types.i64)
+            || found_assoc(tcx.types.i128)
+            || found_assoc(tcx.types.u8)
+            || found_assoc(tcx.types.u16)
+            || found_assoc(tcx.types.u32)
+            || found_assoc(tcx.types.u64)
+            || found_assoc(tcx.types.u128)
+            || found_assoc(tcx.types.f32)
+            || found_assoc(tcx.types.f32);
+        if found_candidate
+            && actual.is_numeric()
+            && !actual.has_concrete_skeleton()
+            && let SelfSource::MethodCall(expr) = source
+        {
+            let mut err = struct_span_err!(
+                tcx.sess,
+                span,
+                E0689,
+                "can't call {} `{}` on ambiguous numeric type `{}`",
+                item_kind,
+                item_name,
+                ty_str
+            );
+            let concrete_type = if actual.is_integral() { "i32" } else { "f32" };
+            match expr.kind {
+                ExprKind::Lit(ref lit) => {
+                    // numeric literal
+                    let snippet = tcx
+                        .sess
+                        .source_map()
+                        .span_to_snippet(lit.span)
+                        .unwrap_or_else(|_| "<numeric literal>".to_owned());
+
+                    // If this is a floating point literal that ends with '.',
+                    // get rid of it to stop this from becoming a member access.
+                    let snippet = snippet.strip_suffix('.').unwrap_or(&snippet);
+
+                    err.span_suggestion(
+                        lit.span,
+                        &format!(
+                            "you must specify a concrete type for this numeric value, \
+                                         like `{}`",
+                            concrete_type
+                        ),
+                        format!("{snippet}_{concrete_type}"),
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+                ExprKind::Path(QPath::Resolved(_, path)) => {
+                    // local binding
+                    if let hir::def::Res::Local(hir_id) = path.res {
+                        let span = tcx.hir().span(hir_id);
+                        let snippet = tcx.sess.source_map().span_to_snippet(span);
+                        let filename = tcx.sess.source_map().span_to_filename(span);
+
+                        let parent_node =
+                            self.tcx.hir().get(self.tcx.hir().get_parent_node(hir_id));
+                        let msg = format!(
+                            "you must specify a type for this binding, like `{}`",
+                            concrete_type,
+                        );
+
+                        match (filename, parent_node, snippet) {
+                            (
+                                FileName::Real(_),
+                                Node::Local(hir::Local {
+                                    source: hir::LocalSource::Normal,
+                                    ty,
+                                    ..
+                                }),
+                                Ok(ref snippet),
+                            ) => {
+                                err.span_suggestion(
+                                    // account for `let x: _ = 42;`
+                                    //                  ^^^^
+                                    span.to(ty.as_ref().map(|ty| ty.span).unwrap_or(span)),
+                                    &msg,
+                                    format!("{}: {}", snippet, concrete_type),
+                                    Applicability::MaybeIncorrect,
+                                );
+                            }
+                            _ => {
+                                err.span_label(span, msg);
+                            }
+                        }
+                    }
+                }
+                _ => {}
+            }
+            err.emit();
+            return true;
+        }
+        false
+    }
+
     crate fn note_unmet_impls_on_type(
         &self,
         err: &mut Diagnostic,
@@ -1807,37 +1868,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     // instead we suggest `T: Foo + Bar` in that case.
                     match hir.get(id) {
                         Node::GenericParam(param) => {
-                            let mut impl_trait = false;
-                            let has_bounds =
-                                if let hir::GenericParamKind::Type { synthetic: true, .. } =
-                                    &param.kind
-                                {
-                                    // We've found `fn foo(x: impl Trait)` instead of
-                                    // `fn foo<T>(x: T)`. We want to suggest the correct
-                                    // `fn foo(x: impl Trait + TraitBound)` instead of
-                                    // `fn foo<T: TraitBound>(x: T)`. (#63706)
-                                    impl_trait = true;
-                                    param.bounds.get(1)
-                                } else {
-                                    param.bounds.get(0)
-                                };
-                            let sp = hir.span(id);
-                            let sp = if let Some(first_bound) = has_bounds {
-                                sp.until(first_bound.span())
-                            } else if let Some(colon_sp) =
-                                // If the generic param is declared with a colon but without bounds:
-                                // fn foo<T:>(t: T) { ... }
-                                param.colon_span_for_suggestions(
-                                    self.inh.tcx.sess.source_map(),
-                                )
+                            enum Introducer {
+                                Plus,
+                                Colon,
+                                Nothing,
+                            }
+                            let ast_generics = hir.get_generics(id.owner).unwrap();
+                            let (sp, mut introducer) = if let Some(span) =
+                                ast_generics.bounds_span_for_suggestions(def_id)
                             {
-                                sp.to(colon_sp)
+                                (span, Introducer::Plus)
+                            } else if let Some(colon_span) = param.colon_span {
+                                (colon_span.shrink_to_hi(), Introducer::Nothing)
                             } else {
-                                sp
+                                (param.span.shrink_to_hi(), Introducer::Colon)
                             };
-                            let trait_def_ids: FxHashSet<DefId> = param
-                                .bounds
-                                .iter()
+                            if matches!(
+                                param.kind,
+                                hir::GenericParamKind::Type { synthetic: true, .. },
+                            ) {
+                                introducer = Introducer::Plus
+                            }
+                            let trait_def_ids: FxHashSet<DefId> = ast_generics
+                                .bounds_for_param(def_id)
+                                .flat_map(|bp| bp.bounds.iter())
                                 .filter_map(|bound| bound.trait_ref()?.trait_def_id())
                                 .collect();
                             if !candidates.iter().any(|t| trait_def_ids.contains(&t.def_id)) {
@@ -1849,11 +1903,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                     )),
                                     candidates.iter().map(|t| {
                                         format!(
-                                            "{}{} {}{}",
-                                            param.name.ident(),
-                                            if impl_trait { " +" } else { ":" },
+                                            "{} {}",
+                                            match introducer {
+                                                Introducer::Plus => " +",
+                                                Introducer::Colon => ":",
+                                                Introducer::Nothing => "",
+                                            },
                                             self.tcx.def_path_str(t.def_id),
-                                            if has_bounds.is_some() { " + " } else { "" },
                                         )
                                     }),
                                     Applicability::MaybeIncorrect,
diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs
index 0d5e7b28a4e..76c955d6f69 100644
--- a/compiler/rustc_typeck/src/check/mod.rs
+++ b/compiler/rustc_typeck/src/check/mod.rs
@@ -98,12 +98,15 @@ pub use check::{check_item_type, check_wf_new};
 pub use diverges::Diverges;
 pub use expectation::Expectation;
 pub use fn_ctxt::*;
+use hir::def::CtorOf;
 pub use inherited::{Inherited, InheritedBuilder};
 
 use crate::astconv::AstConv;
 use crate::check::gather_locals::GatherLocalsVisitor;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_errors::{pluralize, struct_span_err, Applicability, MultiSpan};
+use rustc_errors::{
+    pluralize, struct_span_err, Applicability, DiagnosticBuilder, EmissionGuarantee, MultiSpan,
+};
 use rustc_hir as hir;
 use rustc_hir::def::Res;
 use rustc_hir::def_id::{DefId, LocalDefId};
@@ -370,16 +373,7 @@ fn typeck_with_fallback<'tcx>(
         let (fcx, wf_tys) = if let Some(hir::FnSig { header, decl, .. }) = fn_sig {
             let fn_sig = if crate::collect::get_infer_ret_ty(&decl.output).is_some() {
                 let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id);
-                <dyn AstConv<'_>>::ty_of_fn(
-                    &fcx,
-                    id,
-                    header.unsafety,
-                    header.abi,
-                    decl,
-                    &hir::Generics::empty(),
-                    None,
-                    None,
-                )
+                <dyn AstConv<'_>>::ty_of_fn(&fcx, id, header.unsafety, header.abi, decl, None, None)
             } else {
                 tcx.fn_sig(def_id)
             };
@@ -988,3 +982,36 @@ fn has_expected_num_generic_args<'tcx>(
         generics.count() == expected + if generics.has_self { 1 } else { 0 }
     })
 }
+
+/// Suggests calling the constructor of a tuple struct or enum variant
+///
+/// * `snippet` - The snippet of code that references the constructor
+/// * `span` - The span of the snippet
+/// * `params` - The number of parameters the constructor accepts
+/// * `err` - A mutable diagnostic builder to add the suggestion to
+fn suggest_call_constructor<G: EmissionGuarantee>(
+    span: Span,
+    kind: CtorOf,
+    params: usize,
+    err: &mut DiagnosticBuilder<'_, G>,
+) {
+    // Note: tuple-structs don't have named fields, so just use placeholders
+    let args = vec!["_"; params].join(", ");
+    let applicable = if params > 0 {
+        Applicability::HasPlaceholders
+    } else {
+        // When n = 0, it's an empty-tuple struct/enum variant
+        // so we trivially know how to construct it
+        Applicability::MachineApplicable
+    };
+    let kind = match kind {
+        CtorOf::Struct => "a struct",
+        CtorOf::Variant => "an enum variant",
+    };
+    err.span_label(span, &format!("this is the constructor of {kind}"));
+    err.multipart_suggestion(
+        "call the constructor",
+        vec![(span.shrink_to_lo(), "(".to_string()), (span.shrink_to_hi(), format!(")({args})"))],
+        applicable,
+    );
+}
diff --git a/compiler/rustc_typeck/src/check/op.rs b/compiler/rustc_typeck/src/check/op.rs
index 811833bca80..1ae53a77adc 100644
--- a/compiler/rustc_typeck/src/check/op.rs
+++ b/compiler/rustc_typeck/src/check/op.rs
@@ -11,13 +11,12 @@ use rustc_middle::ty::adjustment::{
 };
 use rustc_middle::ty::fold::TypeFolder;
 use rustc_middle::ty::TyKind::{Adt, Array, Char, FnDef, Never, Ref, Str, Tuple, Uint};
-use rustc_middle::ty::{
-    self, suggest_constraining_type_param, Ty, TyCtxt, TypeFoldable, TypeVisitor,
-};
+use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitor};
 use rustc_span::source_map::Spanned;
 use rustc_span::symbol::{sym, Ident};
 use rustc_span::Span;
 use rustc_trait_selection::infer::InferCtxtExt;
+use rustc_trait_selection::traits::error_reporting::suggestions::InferCtxtExt as _;
 use rustc_trait_selection::traits::{FulfillmentError, TraitEngine, TraitEngineExt};
 
 use std::ops::ControlFlow;
@@ -266,7 +265,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             Err(_) if lhs_ty.references_error() || rhs_ty.references_error() => self.tcx.ty_error(),
             Err(errors) => {
                 let source_map = self.tcx.sess.source_map();
-                let (mut err, missing_trait, use_output) = match is_assign {
+                let (mut err, missing_trait, _use_output) = match is_assign {
                     IsAssign::Yes => {
                         let mut err = struct_span_err!(
                             self.tcx.sess,
@@ -449,39 +448,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         // concatenation (e.g., "Hello " + "World!"). This means
                         // we don't want the note in the else clause to be emitted
                     } else if let [ty] = &visitor.0[..] {
-                        if let ty::Param(p) = *ty.kind() {
-                            // Check if the method would be found if the type param wasn't
-                            // involved. If so, it means that adding a trait bound to the param is
-                            // enough. Otherwise we do not give the suggestion.
-                            let mut eraser = TypeParamEraser(self, expr.span);
-                            let needs_bound = self
-                                .lookup_op_method(
-                                    eraser.fold_ty(lhs_ty),
-                                    Some(eraser.fold_ty(rhs_ty)),
-                                    Some(rhs_expr),
-                                    Op::Binary(op, is_assign),
-                                )
-                                .is_ok();
-                            if needs_bound {
-                                suggest_constraining_param(
-                                    self.tcx,
-                                    self.body_id,
+                        // Look for a TraitPredicate in the Fulfillment errors,
+                        // and use it to generate a suggestion.
+                        //
+                        // Note that lookup_op_method must be called again but
+                        // with a specific rhs_ty instead of a placeholder so
+                        // the resulting predicate generates a more specific
+                        // suggestion for the user.
+                        let errors = self
+                            .lookup_op_method(
+                                lhs_ty,
+                                Some(rhs_ty),
+                                Some(rhs_expr),
+                                Op::Binary(op, is_assign),
+                            )
+                            .unwrap_err();
+                        let predicates = errors
+                            .into_iter()
+                            .filter_map(|error| error.obligation.predicate.to_opt_poly_trait_pred())
+                            .collect::<Vec<_>>();
+                        if !predicates.is_empty() {
+                            for pred in predicates {
+                                self.infcx.suggest_restricting_param_bound(
                                     &mut err,
-                                    *ty,
-                                    rhs_ty,
-                                    missing_trait,
-                                    p,
-                                    use_output,
+                                    pred,
+                                    self.body_id,
                                 );
-                            } else if *ty != lhs_ty {
-                                // When we know that a missing bound is responsible, we don't show
-                                // this note as it is redundant.
-                                err.note(&format!(
-                                    "the trait `{missing_trait}` is not implemented for `{lhs_ty}`"
-                                ));
                             }
-                        } else {
-                            bug!("type param visitor stored a non type param: {:?}", ty.kind());
+                        } else if *ty != lhs_ty {
+                            // When we know that a missing bound is responsible, we don't show
+                            // this note as it is redundant.
+                            err.note(&format!(
+                                "the trait `{missing_trait}` is not implemented for `{lhs_ty}`"
+                            ));
                         }
                     }
                 }
@@ -671,24 +670,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         ex.span,
                         format!("cannot apply unary operator `{}`", op.as_str()),
                     );
-                    let missing_trait = match op {
-                        hir::UnOp::Deref => unreachable!("check unary op `-` or `!` only"),
-                        hir::UnOp::Not => "std::ops::Not",
-                        hir::UnOp::Neg => "std::ops::Neg",
-                    };
+
                     let mut visitor = TypeParamVisitor(vec![]);
                     visitor.visit_ty(operand_ty);
-                    if let [ty] = &visitor.0[..] && let ty::Param(p) = *operand_ty.kind() {
-                        suggest_constraining_param(
-                            self.tcx,
-                            self.body_id,
-                            &mut err,
-                            *ty,
-                            operand_ty,
-                            missing_trait,
-                            p,
-                            true,
-                        );
+                    if let [_] = &visitor.0[..] && let ty::Param(_) = *operand_ty.kind() {
+                        let predicates = errors
+                            .iter()
+                            .filter_map(|error| {
+                                error.obligation.predicate.clone().to_opt_poly_trait_pred()
+                            });
+                        for pred in predicates {
+                            self.infcx.suggest_restricting_param_bound(
+                                &mut err,
+                                pred,
+                                self.body_id,
+                            );
+                        }
                     }
 
                     let sp = self.tcx.sess.source_map().start_point(ex.span);
@@ -973,46 +970,6 @@ fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, op: hir::BinOp) -> bool
     }
 }
 
-fn suggest_constraining_param(
-    tcx: TyCtxt<'_>,
-    body_id: hir::HirId,
-    mut err: &mut Diagnostic,
-    lhs_ty: Ty<'_>,
-    rhs_ty: Ty<'_>,
-    missing_trait: &str,
-    p: ty::ParamTy,
-    set_output: bool,
-) {
-    let hir = tcx.hir();
-    let msg = &format!("`{lhs_ty}` might need a bound for `{missing_trait}`");
-    // Try to find the def-id and details for the parameter p. We have only the index,
-    // so we have to find the enclosing function's def-id, then look through its declared
-    // generic parameters to get the declaration.
-    let def_id = hir.body_owner_def_id(hir::BodyId { hir_id: body_id });
-    let generics = tcx.generics_of(def_id);
-    let param_def_id = generics.type_param(&p, tcx).def_id;
-    if let Some(generics) = param_def_id
-        .as_local()
-        .map(|id| hir.local_def_id_to_hir_id(id))
-        .and_then(|id| hir.find_by_def_id(hir.get_parent_item(id)))
-        .as_ref()
-        .and_then(|node| node.generics())
-    {
-        let output = if set_output { format!("<Output = {rhs_ty}>") } else { String::new() };
-        suggest_constraining_type_param(
-            tcx,
-            generics,
-            &mut err,
-            &lhs_ty.to_string(),
-            &format!("{missing_trait}{output}"),
-            None,
-        );
-    } else {
-        let span = tcx.def_span(param_def_id);
-        err.span_label(span, msg);
-    }
-}
-
 struct TypeParamVisitor<'tcx>(Vec<Ty<'tcx>>);
 
 impl<'tcx> TypeVisitor<'tcx> for TypeParamVisitor<'tcx> {
diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs
index cf0c5703cd0..356763fab5e 100644
--- a/compiler/rustc_typeck/src/check/pat.rs
+++ b/compiler/rustc_typeck/src/check/pat.rs
@@ -643,6 +643,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             });
             let pre = if in_match { "in the same arm, " } else { "" };
             err.note(&format!("{}a binding must have the same type in all alternatives", pre));
+            // FIXME: check if `var_ty` and `ty` can be made the same type by adding or removing
+            // `ref` or `&` to the pattern.
             err.emit();
         }
     }
diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs
index 4e3e32670e9..ec2b7c13ff3 100644
--- a/compiler/rustc_typeck/src/check/wfcheck.rs
+++ b/compiler/rustc_typeck/src/check/wfcheck.rs
@@ -420,15 +420,11 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
 
             let suggestion = format!(
                 "{} {}",
-                if !gat_item_hir.generics.where_clause.predicates.is_empty() {
-                    ","
-                } else {
-                    " where"
-                },
+                if !gat_item_hir.generics.predicates.is_empty() { "," } else { " where" },
                 unsatisfied_bounds.join(", "),
             );
             err.span_suggestion(
-                gat_item_hir.generics.where_clause.tail_span_for_suggestion(),
+                gat_item_hir.generics.tail_span_for_predicate_suggestion(),
                 &format!("add the required where clause{plural}"),
                 suggestion,
                 Applicability::MachineApplicable,
@@ -1733,7 +1729,6 @@ fn check_variances_for_type_defn<'tcx>(
     let explicitly_bounded_params = Lazy::new(|| {
         let icx = crate::collect::ItemCtxt::new(tcx, item.def_id.to_def_id());
         hir_generics
-            .where_clause
             .predicates
             .iter()
             .filter_map(|predicate| match predicate {
@@ -1760,8 +1755,7 @@ fn check_variances_for_type_defn<'tcx>(
         match param.name {
             hir::ParamName::Error => {}
             _ => {
-                let has_explicit_bounds =
-                    !param.bounds.is_empty() || explicitly_bounded_params.contains(&parameter);
+                let has_explicit_bounds = explicitly_bounded_params.contains(&parameter);
                 report_bivariance(tcx, param, has_explicit_bounds);
             }
         }
@@ -1819,13 +1813,12 @@ fn check_false_global_bounds(fcx: &FnCtxt<'_, '_>, mut span: Span, id: hir::HirI
 
             // only use the span of the predicate clause (#90869)
 
-            if let Some(hir::Generics { where_clause, .. }) =
+            if let Some(hir::Generics { predicates, .. }) =
                 hir_node.and_then(|node| node.generics())
             {
                 let obligation_span = obligation.cause.span(fcx.tcx);
 
-                span = where_clause
-                    .predicates
+                span = predicates
                     .iter()
                     // There seems to be no better way to find out which predicate we are in
                     .find(|pred| pred.span().contains(obligation_span))
diff --git a/compiler/rustc_typeck/src/check_unused.rs b/compiler/rustc_typeck/src/check_unused.rs
index 4f792fa25a1..d52886a09bd 100644
--- a/compiler/rustc_typeck/src/check_unused.rs
+++ b/compiler/rustc_typeck/src/check_unused.rs
@@ -18,8 +18,11 @@ pub fn check_crate(tcx: TyCtxt<'_>) {
 
     for id in tcx.hir().items() {
         if matches!(tcx.hir().def_kind(id.def_id), DefKind::Use) {
+            if tcx.visibility(id.def_id).is_public() {
+                continue;
+            }
             let item = tcx.hir().item(id);
-            if item.vis.node.is_pub() || item.span.is_dummy() {
+            if item.span.is_dummy() {
                 continue;
             }
             if let hir::ItemKind::Use(path, _) = item.kind {
@@ -176,7 +179,7 @@ fn unused_crates_lint(tcx: TyCtxt<'_>) {
                 Some(orig_name) => format!("use {} as {};", orig_name, item.ident.name),
                 None => format!("use {};", item.ident.name),
             };
-            let vis = tcx.sess.source_map().span_to_snippet(item.vis.span).unwrap_or_default();
+            let vis = tcx.sess.source_map().span_to_snippet(item.vis_span).unwrap_or_default();
             let add_vis = |to| if vis.is_empty() { to } else { format!("{} {}", vis, to) };
             lint.build("`extern crate` is not idiomatic in the new edition")
                 .span_suggestion_short(
diff --git a/compiler/rustc_typeck/src/coherence/orphan.rs b/compiler/rustc_typeck/src/coherence/orphan.rs
index 77a53744829..19e68f0b14f 100644
--- a/compiler/rustc_typeck/src/coherence/orphan.rs
+++ b/compiler/rustc_typeck/src/coherence/orphan.rs
@@ -50,6 +50,7 @@ fn orphan_check_impl(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGua
             tcx,
             sp,
             tr.path.span,
+            trait_ref.self_ty(),
             impl_.self_ty.span,
             &impl_.generics,
             err,
@@ -201,18 +202,23 @@ fn emit_orphan_check_error<'tcx>(
     tcx: TyCtxt<'tcx>,
     sp: Span,
     trait_span: Span,
+    self_ty: Ty<'tcx>,
     self_ty_span: Span,
     generics: &hir::Generics<'tcx>,
     err: traits::OrphanCheckErr<'tcx>,
 ) -> Result<!, ErrorGuaranteed> {
     Err(match err {
         traits::OrphanCheckErr::NonLocalInputType(tys) => {
+            let msg = match self_ty.kind() {
+                ty::Adt(..) => "can be implemented for types defined outside of the crate",
+                _ if self_ty.is_primitive() => "can be implemented for primitive types",
+                _ => "can be implemented for arbitrary types",
+            };
             let mut err = struct_span_err!(
                 tcx.sess,
                 sp,
                 E0117,
-                "only traits defined in the current crate can be implemented for \
-                        arbitrary types"
+                "only traits defined in the current crate {msg}"
             );
             err.span_label(sp, "impl doesn't use only types from inside the current crate");
             for (ty, is_target_ty) in &tys {
diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs
index 153ab8d95fd..f85735ec57b 100644
--- a/compiler/rustc_typeck/src/collect.rs
+++ b/compiler/rustc_typeck/src/collect.rs
@@ -1,4 +1,3 @@
-// ignore-tidy-filelength
 //! "Collection" is the process of determining the type and other external
 //! details of each item in Rust. Collection is specifically concerned
 //! with *inter-procedural* things -- for example, for a function
@@ -41,7 +40,7 @@ use rustc_middle::ty::subst::InternalSubsts;
 use rustc_middle::ty::util::Discr;
 use rustc_middle::ty::util::IntTypeExt;
 use rustc_middle::ty::{self, AdtKind, Const, DefIdTree, Ty, TyCtxt};
-use rustc_middle::ty::{ReprOptions, ToPredicate, TypeFoldable};
+use rustc_middle::ty::{ReprOptions, ToPredicate};
 use rustc_session::lint;
 use rustc_session::parse::feature_err;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
@@ -149,8 +148,7 @@ struct CollectItemTypesVisitor<'tcx> {
 /// all already existing generic type parameters to avoid suggesting a name that is already in use.
 crate fn placeholder_type_error<'tcx>(
     tcx: TyCtxt<'tcx>,
-    span: Option<Span>,
-    generics: &[hir::GenericParam<'_>],
+    generics: Option<&hir::Generics<'_>>,
     placeholder_types: Vec<Span>,
     suggest: bool,
     hir_ty: Option<&hir::Ty<'_>>,
@@ -160,29 +158,45 @@ crate fn placeholder_type_error<'tcx>(
         return;
     }
 
-    let type_name = generics.next_type_param_name(None);
+    placeholder_type_error_diag(tcx, generics, placeholder_types, vec![], suggest, hir_ty, kind)
+        .emit();
+}
+
+crate fn placeholder_type_error_diag<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    generics: Option<&hir::Generics<'_>>,
+    placeholder_types: Vec<Span>,
+    additional_spans: Vec<Span>,
+    suggest: bool,
+    hir_ty: Option<&hir::Ty<'_>>,
+    kind: &'static str,
+) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    if placeholder_types.is_empty() {
+        return bad_placeholder(tcx, additional_spans, kind);
+    }
+
+    let params = generics.map(|g| g.params).unwrap_or_default();
+    let type_name = params.next_type_param_name(None);
     let mut sugg: Vec<_> =
         placeholder_types.iter().map(|sp| (*sp, (*type_name).to_string())).collect();
 
-    if generics.is_empty() {
-        if let Some(span) = span {
-            sugg.push((span, format!("<{}>", type_name)));
+    if let Some(generics) = generics {
+        if let Some(arg) = params.iter().find(|arg| {
+            matches!(arg.name, hir::ParamName::Plain(Ident { name: kw::Underscore, .. }))
+        }) {
+            // Account for `_` already present in cases like `struct S<_>(_);` and suggest
+            // `struct S<T>(T);` instead of `struct S<_, T>(T);`.
+            sugg.push((arg.span, (*type_name).to_string()));
+        } else if let Some(span) = generics.span_for_param_suggestion() {
+            // Account for bounds, we want `fn foo<T: E, K>(_: K)` not `fn foo<T, K: E>(_: K)`.
+            sugg.push((span, format!(", {}", type_name)));
+        } else {
+            sugg.push((generics.span, format!("<{}>", type_name)));
         }
-    } else if let Some(arg) = generics
-        .iter()
-        .find(|arg| matches!(arg.name, hir::ParamName::Plain(Ident { name: kw::Underscore, .. })))
-    {
-        // Account for `_` already present in cases like `struct S<_>(_);` and suggest
-        // `struct S<T>(T);` instead of `struct S<_, T>(T);`.
-        sugg.push((arg.span, (*type_name).to_string()));
-    } else {
-        let last = generics.iter().last().unwrap();
-        // Account for bounds, we want `fn foo<T: E, K>(_: K)` not `fn foo<T, K: E>(_: K)`.
-        let span = last.bounds_span_for_suggestions().unwrap_or(last.span.shrink_to_hi());
-        sugg.push((span, format!(", {}", type_name)));
     }
 
-    let mut err = bad_placeholder(tcx, placeholder_types, kind);
+    let mut err =
+        bad_placeholder(tcx, placeholder_types.into_iter().chain(additional_spans).collect(), kind);
 
     // Suggest, but only if it is not a function in const or static
     if suggest {
@@ -218,7 +232,8 @@ crate fn placeholder_type_error<'tcx>(
             );
         }
     }
-    err.emit();
+
+    err
 }
 
 fn reject_placeholder_type_signatures_in_item<'tcx>(
@@ -241,15 +256,7 @@ fn reject_placeholder_type_signatures_in_item<'tcx>(
     let mut visitor = HirPlaceholderCollector::default();
     visitor.visit_item(item);
 
-    placeholder_type_error(
-        tcx,
-        Some(generics.span),
-        generics.params,
-        visitor.0,
-        suggest,
-        None,
-        item.kind.descr(),
-    );
+    placeholder_type_error(tcx, Some(generics), visitor.0, suggest, None, item.kind.descr());
 }
 
 impl<'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'tcx> {
@@ -651,27 +658,8 @@ impl<'tcx> ItemCtxt<'tcx> {
         only_self_bounds: OnlySelfBounds,
         assoc_name: Option<Ident>,
     ) -> Vec<(ty::Predicate<'tcx>, Span)> {
-        let from_ty_params = ast_generics
-            .params
-            .iter()
-            .filter_map(|param| match param.kind {
-                GenericParamKind::Type { .. } | GenericParamKind::Const { .. }
-                    if param.hir_id == param_id =>
-                {
-                    Some(&param.bounds)
-                }
-                _ => None,
-            })
-            .flat_map(|bounds| bounds.iter())
-            .filter(|b| match assoc_name {
-                Some(assoc_name) => self.bound_defines_assoc_item(b, assoc_name),
-                None => true,
-            })
-            .flat_map(|b| predicates_from_bound(self, ty, b, ty::List::empty()));
-
         let param_def_id = self.tcx.hir().local_def_id(param_id).to_def_id();
-        let from_where_clauses = ast_generics
-            .where_clause
+        ast_generics
             .predicates
             .iter()
             .filter_map(|wp| match *wp {
@@ -696,9 +684,8 @@ impl<'tcx> ItemCtxt<'tcx> {
                     })
                     .filter_map(move |b| bt.map(|bt| (bt, b, bvars)))
             })
-            .flat_map(|(bt, b, bvars)| predicates_from_bound(self, bt, b, bvars));
-
-        from_ty_params.chain(from_where_clauses).collect()
+            .flat_map(|(bt, b, bvars)| predicates_from_bound(self, bt, b, bvars))
+            .collect()
     }
 
     fn bound_defines_assoc_item(&self, b: &hir::GenericBound<'_>, assoc_name: Ident) -> bool {
@@ -744,7 +731,6 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) {
                         placeholder_type_error(
                             tcx,
                             None,
-                            &[],
                             visitor.0,
                             false,
                             None,
@@ -824,15 +810,7 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) {
                     if let hir::TyKind::TraitObject(..) = ty.kind {
                         let mut visitor = HirPlaceholderCollector::default();
                         visitor.visit_item(it);
-                        placeholder_type_error(
-                            tcx,
-                            None,
-                            &[],
-                            visitor.0,
-                            false,
-                            None,
-                            it.kind.descr(),
-                        );
+                        placeholder_type_error(tcx, None, visitor.0, false, None, it.kind.descr());
                     }
                 }
                 _ => (),
@@ -860,7 +838,7 @@ fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) {
             // Account for `const C: _;`.
             let mut visitor = HirPlaceholderCollector::default();
             visitor.visit_trait_item(trait_item);
-            placeholder_type_error(tcx, None, &[], visitor.0, false, None, "constant");
+            placeholder_type_error(tcx, None, visitor.0, false, None, "constant");
         }
 
         hir::TraitItemKind::Type(_, Some(_)) => {
@@ -869,7 +847,7 @@ fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) {
             // Account for `type T = _;`.
             let mut visitor = HirPlaceholderCollector::default();
             visitor.visit_trait_item(trait_item);
-            placeholder_type_error(tcx, None, &[], visitor.0, false, None, "associated type");
+            placeholder_type_error(tcx, None, visitor.0, false, None, "associated type");
         }
 
         hir::TraitItemKind::Type(_, None) => {
@@ -879,7 +857,7 @@ fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) {
             let mut visitor = HirPlaceholderCollector::default();
             visitor.visit_trait_item(trait_item);
 
-            placeholder_type_error(tcx, None, &[], visitor.0, false, None, "associated type");
+            placeholder_type_error(tcx, None, visitor.0, false, None, "associated type");
         }
     };
 
@@ -901,7 +879,7 @@ fn convert_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::ImplItemId) {
             let mut visitor = HirPlaceholderCollector::default();
             visitor.visit_impl_item(impl_item);
 
-            placeholder_type_error(tcx, None, &[], visitor.0, false, None, "associated type");
+            placeholder_type_error(tcx, None, visitor.0, false, None, "associated type");
         }
         hir::ImplItemKind::Const(..) => {}
     }
@@ -1864,69 +1842,35 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> {
     match tcx.hir().get(hir_id) {
         TraitItem(hir::TraitItem {
             kind: TraitItemKind::Fn(sig, TraitFn::Provided(_)),
-            ident,
             generics,
             ..
         })
-        | ImplItem(hir::ImplItem { kind: ImplItemKind::Fn(sig, _), ident, generics, .. })
-        | Item(hir::Item { kind: ItemKind::Fn(sig, generics, _), ident, .. }) => {
-            match get_infer_ret_ty(&sig.decl.output) {
-                Some(ty) => {
-                    let fn_sig = tcx.typeck(def_id).liberated_fn_sigs()[hir_id];
-                    // Typeck doesn't expect erased regions to be returned from `type_of`.
-                    let fn_sig = tcx.fold_regions(fn_sig, &mut false, |r, _| match *r {
-                        ty::ReErased => tcx.lifetimes.re_static,
-                        _ => r,
-                    });
-                    let fn_sig = ty::Binder::dummy(fn_sig);
-
-                    let mut visitor = HirPlaceholderCollector::default();
-                    visitor.visit_ty(ty);
-                    let mut diag = bad_placeholder(tcx, visitor.0, "return type");
-                    let ret_ty = fn_sig.skip_binder().output();
-                    if !ret_ty.references_error() {
-                        if !ret_ty.is_closure() {
-                            let ret_ty_str = match ret_ty.kind() {
-                                // Suggest a function pointer return type instead of a unique function definition
-                                // (e.g. `fn() -> i32` instead of `fn() -> i32 { f }`, the latter of which is invalid
-                                // syntax)
-                                ty::FnDef(..) => ret_ty.fn_sig(tcx).to_string(),
-                                _ => ret_ty.to_string(),
-                            };
-                            diag.span_suggestion(
-                                ty.span,
-                                "replace with the correct return type",
-                                ret_ty_str,
-                                Applicability::MaybeIncorrect,
-                            );
-                        } else {
-                            // We're dealing with a closure, so we should suggest using `impl Fn` or trait bounds
-                            // to prevent the user from getting a papercut while trying to use the unique closure
-                            // syntax (e.g. `[closure@src/lib.rs:2:5: 2:9]`).
-                            diag.help("consider using an `Fn`, `FnMut`, or `FnOnce` trait bound");
-                            diag.note("for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html");
-                        }
-                    }
-                    diag.emit();
+        | Item(hir::Item { kind: ItemKind::Fn(sig, generics, _), .. }) => {
+            infer_return_ty_for_fn_sig(tcx, sig, generics, def_id, &icx)
+        }
 
-                    fn_sig
-                }
-                None => <dyn AstConv<'_>>::ty_of_fn(
+        ImplItem(hir::ImplItem { kind: ImplItemKind::Fn(sig, _), generics, .. }) => {
+            // Do not try to inference the return type for a impl method coming from a trait
+            if let Item(hir::Item { kind: ItemKind::Impl(i), .. }) =
+                tcx.hir().get(tcx.hir().get_parent_node(hir_id))
+                && i.of_trait.is_some()
+            {
+                <dyn AstConv<'_>>::ty_of_fn(
                     &icx,
                     hir_id,
                     sig.header.unsafety,
                     sig.header.abi,
                     sig.decl,
-                    generics,
-                    Some(ident.span),
+                    Some(generics),
                     None,
-                ),
+                )
+            } else {
+                infer_return_ty_for_fn_sig(tcx, sig, generics, def_id, &icx)
             }
         }
 
         TraitItem(hir::TraitItem {
             kind: TraitItemKind::Fn(FnSig { header, decl, span: _ }, _),
-            ident,
             generics,
             ..
         }) => <dyn AstConv<'_>>::ty_of_fn(
@@ -1935,16 +1879,13 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> {
             header.unsafety,
             header.abi,
             decl,
-            generics,
-            Some(ident.span),
+            Some(generics),
             None,
         ),
 
-        ForeignItem(&hir::ForeignItem {
-            kind: ForeignItemKind::Fn(fn_decl, _, _), ident, ..
-        }) => {
+        ForeignItem(&hir::ForeignItem { kind: ForeignItemKind::Fn(fn_decl, _, _), .. }) => {
             let abi = tcx.hir().get_foreign_abi(hir_id);
-            compute_sig_of_foreign_fn_decl(tcx, def_id.to_def_id(), fn_decl, abi, ident)
+            compute_sig_of_foreign_fn_decl(tcx, def_id.to_def_id(), fn_decl, abi)
         }
 
         Ctor(data) | Variant(hir::Variant { data, .. }) if data.ctor_hir_id().is_some() => {
@@ -1982,6 +1923,69 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> {
     }
 }
 
+fn infer_return_ty_for_fn_sig<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    sig: &hir::FnSig<'_>,
+    generics: &hir::Generics<'_>,
+    def_id: LocalDefId,
+    icx: &ItemCtxt<'tcx>,
+) -> ty::PolyFnSig<'tcx> {
+    let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
+
+    match get_infer_ret_ty(&sig.decl.output) {
+        Some(ty) => {
+            let fn_sig = tcx.typeck(def_id).liberated_fn_sigs()[hir_id];
+            // Typeck doesn't expect erased regions to be returned from `type_of`.
+            let fn_sig = tcx.fold_regions(fn_sig, &mut false, |r, _| match *r {
+                ty::ReErased => tcx.lifetimes.re_static,
+                _ => r,
+            });
+            let fn_sig = ty::Binder::dummy(fn_sig);
+
+            let mut visitor = HirPlaceholderCollector::default();
+            visitor.visit_ty(ty);
+            let mut diag = bad_placeholder(tcx, visitor.0, "return type");
+            let ret_ty = fn_sig.skip_binder().output();
+            if ret_ty.is_suggestable(tcx) {
+                diag.span_suggestion(
+                    ty.span,
+                    "replace with the correct return type",
+                    ret_ty.to_string(),
+                    Applicability::MachineApplicable,
+                );
+            } else if matches!(ret_ty.kind(), ty::FnDef(..)) {
+                let fn_sig = ret_ty.fn_sig(tcx);
+                if fn_sig.skip_binder().inputs_and_output.iter().all(|t| t.is_suggestable(tcx)) {
+                    diag.span_suggestion(
+                        ty.span,
+                        "replace with the correct return type",
+                        fn_sig.to_string(),
+                        Applicability::MachineApplicable,
+                    );
+                }
+            } else if ret_ty.is_closure() {
+                // We're dealing with a closure, so we should suggest using `impl Fn` or trait bounds
+                // to prevent the user from getting a papercut while trying to use the unique closure
+                // syntax (e.g. `[closure@src/lib.rs:2:5: 2:9]`).
+                diag.help("consider using an `Fn`, `FnMut`, or `FnOnce` trait bound");
+                diag.note("for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html");
+            }
+            diag.emit();
+
+            fn_sig
+        }
+        None => <dyn AstConv<'_>>::ty_of_fn(
+            icx,
+            hir_id,
+            sig.header.unsafety,
+            sig.header.abi,
+            sig.decl,
+            Some(generics),
+            None,
+        ),
+    }
+}
+
 fn impl_trait_ref(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ty::TraitRef<'_>> {
     let icx = ItemCtxt::new(tcx, def_id);
     match tcx.hir().expect_item(def_id.expect_local()).kind {
@@ -2137,16 +2141,16 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP
 
     let icx = ItemCtxt::new(tcx, def_id);
 
-    const NO_GENERICS: &hir::Generics<'_> = &hir::Generics::empty();
+    const NO_GENERICS: &hir::Generics<'_> = hir::Generics::empty();
 
     // We use an `IndexSet` to preserves order of insertion.
     // Preserving the order of insertion is important here so as not to break UI tests.
     let mut predicates: FxIndexSet<(ty::Predicate<'_>, Span)> = FxIndexSet::default();
 
     let ast_generics = match node {
-        Node::TraitItem(item) => &item.generics,
+        Node::TraitItem(item) => item.generics,
 
-        Node::ImplItem(item) => &item.generics,
+        Node::ImplItem(item) => item.generics,
 
         Node::Item(item) => {
             match item.kind {
@@ -2160,15 +2164,15 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP
                 | ItemKind::TyAlias(_, ref generics)
                 | ItemKind::Enum(_, ref generics)
                 | ItemKind::Struct(_, ref generics)
-                | ItemKind::Union(_, ref generics) => generics,
+                | ItemKind::Union(_, ref generics) => *generics,
 
                 ItemKind::Trait(_, _, ref generics, ..) => {
                     is_trait = Some(ty::TraitRef::identity(tcx, def_id));
-                    generics
+                    *generics
                 }
                 ItemKind::TraitAlias(ref generics, _) => {
                     is_trait = Some(ty::TraitRef::identity(tcx, def_id));
-                    generics
+                    *generics
                 }
                 ItemKind::OpaqueTy(OpaqueTy {
                     origin: hir::OpaqueTyOrigin::AsyncFn(..) | hir::OpaqueTyOrigin::FnReturn(..),
@@ -2205,7 +2209,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP
 
         Node::ForeignItem(item) => match item.kind {
             ForeignItemKind::Static(..) => NO_GENERICS,
-            ForeignItemKind::Fn(_, _, ref generics) => generics,
+            ForeignItemKind::Fn(_, _, ref generics) => *generics,
             ForeignItemKind::Type => NO_GENERICS,
         },
 
@@ -2239,29 +2243,9 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP
     // Collect the region predicates that were declared inline as
     // well. In the case of parameters declared on a fn or method, we
     // have to be careful to only iterate over early-bound regions.
-    let mut index = parent_count + has_own_self as u32;
-    for param in early_bound_lifetimes_from_generics(tcx, hir_id.owner, ast_generics) {
-        let region = tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion {
-            def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(),
-            index,
-            name: param.name.ident().name,
-        }));
-        index += 1;
-
-        match param.kind {
-            GenericParamKind::Lifetime { .. } => {
-                param.bounds.iter().for_each(|bound| match bound {
-                    hir::GenericBound::Outlives(lt) => {
-                        let bound = <dyn AstConv<'_>>::ast_region_to_region(&icx, lt, None);
-                        let outlives = ty::Binder::dummy(ty::OutlivesPredicate(region, bound));
-                        predicates.insert((outlives.to_predicate(tcx), lt.span));
-                    }
-                    _ => bug!(),
-                });
-            }
-            _ => bug!(),
-        }
-    }
+    let mut index = parent_count
+        + has_own_self as u32
+        + early_bound_lifetimes_from_generics(tcx, hir_id.owner, ast_generics).count() as u32;
 
     // Collect the predicates that were written inline by the user on each
     // type parameter (e.g., `<T: Foo>`).
@@ -2274,28 +2258,26 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP
                 let param_ty = ty::ParamTy::new(index, name).to_ty(tcx);
                 index += 1;
 
-                let mut bounds = <dyn AstConv<'_>>::compute_bounds(&icx, param_ty, param.bounds);
+                let mut bounds = Bounds::default();
                 // Params are implicitly sized unless a `?Sized` bound is found
                 <dyn AstConv<'_>>::add_implicitly_sized(
                     &icx,
                     &mut bounds,
-                    param.bounds,
-                    Some((param.hir_id, ast_generics.where_clause.predicates)),
+                    &[],
+                    Some((param.hir_id, ast_generics.predicates)),
                     param.span,
                 );
                 predicates.extend(bounds.predicates(tcx, param_ty));
             }
             GenericParamKind::Const { .. } => {
                 // Bounds on const parameters are currently not possible.
-                debug_assert!(param.bounds.is_empty());
                 index += 1;
             }
         }
     }
 
     // Add in the bounds that appear in the where-clause.
-    let where_clause = &ast_generics.where_clause;
-    for predicate in where_clause.predicates {
+    for predicate in ast_generics.predicates {
         match predicate {
             hir::WherePredicate::BoundPredicate(bound_pred) => {
                 let ty = icx.to_ty(bound_pred.bounded_ty);
@@ -2553,7 +2535,6 @@ fn compute_sig_of_foreign_fn_decl<'tcx>(
     def_id: DefId,
     decl: &'tcx hir::FnDecl<'tcx>,
     abi: abi::Abi,
-    ident: Ident,
 ) -> ty::PolyFnSig<'tcx> {
     let unsafety = if abi == abi::Abi::RustIntrinsic {
         intrinsic_operation_unsafety(tcx.item_name(def_id))
@@ -2567,8 +2548,7 @@ fn compute_sig_of_foreign_fn_decl<'tcx>(
         unsafety,
         abi,
         decl,
-        &hir::Generics::empty(),
-        Some(ident.span),
+        None,
         None,
     );
 
diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs
index fa06ec09fce..495b8d3b4ee 100644
--- a/compiler/rustc_typeck/src/collect/type_of.rs
+++ b/compiler/rustc_typeck/src/collect/type_of.rs
@@ -337,8 +337,8 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
                         icx.to_ty(ty)
                     }
                 }
-                ItemKind::TyAlias(self_ty, _)
-                | ItemKind::Impl(hir::Impl { self_ty, .. }) => icx.to_ty(self_ty),
+                ItemKind::TyAlias(self_ty, _) => icx.to_ty(self_ty),
+                ItemKind::Impl(hir::Impl { self_ty, .. }) => icx.to_ty(*self_ty),
                 ItemKind::Fn(..) => {
                     let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id());
                     tcx.mk_fn_def(def_id.to_def_id(), substs)
diff --git a/compiler/rustc_typeck/src/errors.rs b/compiler/rustc_typeck/src/errors.rs
index 1088be5f566..3d2f93537e4 100644
--- a/compiler/rustc_typeck/src/errors.rs
+++ b/compiler/rustc_typeck/src/errors.rs
@@ -1,6 +1,6 @@
 //! Errors emitted by typeck.
 use rustc_errors::Applicability;
-use rustc_macros::SessionDiagnostic;
+use rustc_macros::{SessionDiagnostic, SessionSubdiagnostic};
 use rustc_middle::ty::Ty;
 use rustc_span::{symbol::Ident, Span, Symbol};
 
@@ -134,7 +134,7 @@ pub struct TypeofReservedKeywordUsed<'tcx> {
     #[primary_span]
     #[label]
     pub span: Span,
-    #[suggestion_verbose(message = "suggestion", code = "{ty}")]
+    #[suggestion_verbose(code = "{ty}")]
     pub opt_sugg: Option<(Span, Applicability)>,
 }
 
@@ -190,3 +190,41 @@ pub struct AddressOfTemporaryTaken {
     #[label]
     pub span: Span,
 }
+
+#[derive(SessionSubdiagnostic)]
+pub enum AddReturnTypeSuggestion<'tcx> {
+    #[suggestion(
+        slug = "typeck-add-return-type-add",
+        code = "-> {found} ",
+        applicability = "machine-applicable"
+    )]
+    Add {
+        #[primary_span]
+        span: Span,
+        found: Ty<'tcx>,
+    },
+    #[suggestion(
+        slug = "typeck-add-return-type-missing-here",
+        code = "-> _ ",
+        applicability = "has-placeholders"
+    )]
+    MissingHere {
+        #[primary_span]
+        span: Span,
+    },
+}
+
+#[derive(SessionSubdiagnostic)]
+pub enum ExpectedReturnTypeLabel<'tcx> {
+    #[label(slug = "typeck-expected-default-return-type")]
+    Unit {
+        #[primary_span]
+        span: Span,
+    },
+    #[label(slug = "typeck-expected-return-type")]
+    Other {
+        #[primary_span]
+        span: Span,
+        expected: Ty<'tcx>,
+    },
+}
diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs
index 9fb9652b849..03b3d68d59f 100644
--- a/compiler/rustc_typeck/src/lib.rs
+++ b/compiler/rustc_typeck/src/lib.rs
@@ -213,7 +213,7 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
         let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
         match tcx.hir().find(hir_id) {
             Some(Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, ref generics, _), .. })) => {
-                generics.where_clause.span()
+                generics.where_clause_span()
             }
             _ => {
                 span_bug!(tcx.def_span(def_id), "main has a non-function type");
@@ -408,7 +408,7 @@ fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: DefId) {
                         .emit();
                         error = true;
                     }
-                    if let Some(sp) = generics.where_clause.span() {
+                    if let Some(sp) = generics.where_clause_span() {
                         struct_span_err!(
                             tcx.sess,
                             sp,
diff --git a/config.toml.example b/config.toml.example
index 6e53d9b442f..dd886879b14 100644
--- a/config.toml.example
+++ b/config.toml.example
@@ -473,13 +473,23 @@ changelog-seen = 2
 # FIXME(#61117): Some tests fail when this option is enabled.
 #debuginfo-level-tests = 0
 
-# Whether to run `dsymutil` on Apple platforms to gather debug info into .dSYM
-# bundles. `dsymutil` adds time to builds for no clear benefit, and also makes
-# it more difficult for debuggers to find debug info. The compiler currently
-# defaults to running `dsymutil` to preserve its historical default, but when
-# compiling the compiler itself, we skip it by default since we know it's safe
-# to do so in that case.
-#run-dsymutil = false
+# Should rustc be build with split debuginfo? Default is platform dependent.
+# Valid values are the same as those accepted by `-C split-debuginfo`
+# (`off`/`unpacked`/`packed`).
+#
+# On Linux, split debuginfo is disabled by default.
+#
+# On Apple platforms, unpacked split debuginfo is used by default. Unpacked
+# debuginfo does not run `dsymutil`, which packages debuginfo from disparate
+# object files into a single `.dSYM` file. `dsymutil` adds time to builds for
+# no clear benefit, and also makes it more difficult for debuggers to find
+# debug info. The compiler currently defaults to running `dsymutil` to preserve
+# its historical default, but when compiling the compiler itself, we skip it by
+# default since we know it's safe to do so in that case.
+#
+# On Windows platforms, packed debuginfo is the only supported option,
+# producing a `.pdb` file.
+#split-debuginfo = if linux { off } else if windows { packed } else if apple { unpacked }
 
 # Whether or not `panic!`s generate backtraces (RUST_BACKTRACE)
 #backtrace = true
diff --git a/library/alloc/benches/vec_deque.rs b/library/alloc/benches/vec_deque.rs
index 404cfa6addb..6660380e4be 100644
--- a/library/alloc/benches/vec_deque.rs
+++ b/library/alloc/benches/vec_deque.rs
@@ -67,3 +67,27 @@ fn bench_from_array_1000(b: &mut Bencher) {
         black_box(deq);
     })
 }
+
+#[bench]
+fn bench_extend_bytes(b: &mut Bencher) {
+    let mut ring: VecDeque<u8> = VecDeque::with_capacity(1000);
+    let input: &[u8] = &[128; 512];
+
+    b.iter(|| {
+        ring.clear();
+        ring.extend(black_box(input));
+    });
+}
+
+#[bench]
+fn bench_extend_vec(b: &mut Bencher) {
+    let mut ring: VecDeque<u8> = VecDeque::with_capacity(1000);
+    let input = vec![128; 512];
+
+    b.iter(|| {
+        ring.clear();
+
+        let input = input.clone();
+        ring.extend(black_box(input));
+    });
+}
diff --git a/library/alloc/src/borrow.rs b/library/alloc/src/borrow.rs
index 8b13e36c4b3..cb4e438f8be 100644
--- a/library/alloc/src/borrow.rs
+++ b/library/alloc/src/borrow.rs
@@ -292,8 +292,7 @@ impl<B: ?Sized + ToOwned> Cow<'_, B> {
     ///
     /// # Examples
     ///
-    /// Calling `into_owned` on a `Cow::Borrowed` clones the underlying data
-    /// and becomes a `Cow::Owned`:
+    /// Calling `into_owned` on a `Cow::Borrowed` returns a clone of the borrowed data:
     ///
     /// ```
     /// use std::borrow::Cow;
@@ -307,7 +306,8 @@ impl<B: ?Sized + ToOwned> Cow<'_, B> {
     /// );
     /// ```
     ///
-    /// Calling `into_owned` on a `Cow::Owned` is a no-op:
+    /// Calling `into_owned` on a `Cow::Owned` returns the owned data. The data is moved out of the
+    /// `Cow` without being cloned.
     ///
     /// ```
     /// use std::borrow::Cow;
diff --git a/library/alloc/src/collections/binary_heap.rs b/library/alloc/src/collections/binary_heap.rs
index ef5bef0253a..c3c1d0c92a8 100644
--- a/library/alloc/src/collections/binary_heap.rs
+++ b/library/alloc/src/collections/binary_heap.rs
@@ -155,6 +155,9 @@ use crate::vec::{self, AsVecIntoIter, Vec};
 
 use super::SpecExtend;
 
+#[cfg(test)]
+mod tests;
+
 /// A priority queue implemented with a binary heap.
 ///
 /// This will be a max-heap.
diff --git a/library/alloc/tests/binary_heap.rs b/library/alloc/src/collections/binary_heap/tests.rs
index f32118bb563..7c758dbb3ab 100644
--- a/library/alloc/tests/binary_heap.rs
+++ b/library/alloc/src/collections/binary_heap/tests.rs
@@ -1,5 +1,5 @@
-use std::collections::binary_heap::{Drain, PeekMut};
-use std::collections::BinaryHeap;
+use super::*;
+use crate::boxed::Box;
 use std::iter::TrustedLen;
 use std::panic::{catch_unwind, AssertUnwindSafe};
 use std::sync::atomic::{AtomicU32, Ordering};
diff --git a/library/alloc/src/collections/linked_list.rs b/library/alloc/src/collections/linked_list.rs
index d81f24e7202..736b38370ab 100644
--- a/library/alloc/src/collections/linked_list.rs
+++ b/library/alloc/src/collections/linked_list.rs
@@ -645,7 +645,7 @@ impl<T> LinkedList<T> {
     /// Returns `true` if the `LinkedList` contains an element equal to the
     /// given value.
     ///
-    /// This operation should compute in *O*(*n*) time.
+    /// This operation should compute linearly in *O*(*n*) time.
     ///
     /// # Examples
     ///
@@ -1569,7 +1569,7 @@ impl<'a, T> CursorMut<'a, T> {
     /// Appends an element to the front of the cursor's parent list. The node
     /// that the cursor points to is unchanged, even if it is the "ghost" node.
     ///
-    /// This operation should compute in O(1) time.
+    /// This operation should compute in *O*(1) time.
     // `push_front` continues to point to "ghost" when it addes a node to mimic
     // the behavior of `insert_before` on an empty list.
     #[unstable(feature = "linked_list_cursors", issue = "58533")]
@@ -1584,7 +1584,7 @@ impl<'a, T> CursorMut<'a, T> {
     /// Appends an element to the back of the cursor's parent list. The node
     /// that the cursor points to is unchanged, even if it is the "ghost" node.
     ///
-    /// This operation should compute in O(1) time.
+    /// This operation should compute in *O*(1) time.
     #[unstable(feature = "linked_list_cursors", issue = "58533")]
     pub fn push_back(&mut self, elt: T) {
         // Safety: We know that `push_back` does not change the position in
@@ -1603,7 +1603,7 @@ impl<'a, T> CursorMut<'a, T> {
     /// unchanged, unless it was pointing to the front element. In that case, it
     /// points to the new front element.
     ///
-    /// This operation should compute in O(1) time.
+    /// This operation should compute in *O*(1) time.
     #[unstable(feature = "linked_list_cursors", issue = "58533")]
     pub fn pop_front(&mut self) -> Option<T> {
         // We can't check if current is empty, we must check the list directly.
@@ -1630,7 +1630,7 @@ impl<'a, T> CursorMut<'a, T> {
     /// unchanged, unless it was pointing to the back element. In that case, it
     /// points to the "ghost" element.
     ///
-    /// This operation should compute in O(1) time.
+    /// This operation should compute in *O*(1) time.
     #[unstable(feature = "linked_list_cursors", issue = "58533")]
     pub fn pop_back(&mut self) -> Option<T> {
         if self.list.is_empty() {
diff --git a/library/alloc/src/collections/linked_list/tests.rs b/library/alloc/src/collections/linked_list/tests.rs
index 5a65ed7a962..38c702aa387 100644
--- a/library/alloc/src/collections/linked_list/tests.rs
+++ b/library/alloc/src/collections/linked_list/tests.rs
@@ -1,10 +1,55 @@
 use super::*;
+use crate::vec::Vec;
 
+use std::panic::{catch_unwind, AssertUnwindSafe};
 use std::thread;
-use std::vec::Vec;
 
 use rand::{thread_rng, RngCore};
 
+#[test]
+fn test_basic() {
+    let mut m = LinkedList::<Box<_>>::new();
+    assert_eq!(m.pop_front(), None);
+    assert_eq!(m.pop_back(), None);
+    assert_eq!(m.pop_front(), None);
+    m.push_front(box 1);
+    assert_eq!(m.pop_front(), Some(box 1));
+    m.push_back(box 2);
+    m.push_back(box 3);
+    assert_eq!(m.len(), 2);
+    assert_eq!(m.pop_front(), Some(box 2));
+    assert_eq!(m.pop_front(), Some(box 3));
+    assert_eq!(m.len(), 0);
+    assert_eq!(m.pop_front(), None);
+    m.push_back(box 1);
+    m.push_back(box 3);
+    m.push_back(box 5);
+    m.push_back(box 7);
+    assert_eq!(m.pop_front(), Some(box 1));
+
+    let mut n = LinkedList::new();
+    n.push_front(2);
+    n.push_front(3);
+    {
+        assert_eq!(n.front().unwrap(), &3);
+        let x = n.front_mut().unwrap();
+        assert_eq!(*x, 3);
+        *x = 0;
+    }
+    {
+        assert_eq!(n.back().unwrap(), &2);
+        let y = n.back_mut().unwrap();
+        assert_eq!(*y, 2);
+        *y = 1;
+    }
+    assert_eq!(n.pop_front(), Some(0));
+    assert_eq!(n.pop_front(), Some(1));
+}
+
+fn generate_test() -> LinkedList<i32> {
+    list_from(&[0, 1, 2, 3, 4, 5, 6])
+}
+
 fn list_from<T: Clone>(v: &[T]) -> LinkedList<T> {
     v.iter().cloned().collect()
 }
@@ -111,6 +156,123 @@ fn test_append() {
 }
 
 #[test]
+fn test_iterator() {
+    let m = generate_test();
+    for (i, elt) in m.iter().enumerate() {
+        assert_eq!(i as i32, *elt);
+    }
+    let mut n = LinkedList::new();
+    assert_eq!(n.iter().next(), None);
+    n.push_front(4);
+    let mut it = n.iter();
+    assert_eq!(it.size_hint(), (1, Some(1)));
+    assert_eq!(it.next().unwrap(), &4);
+    assert_eq!(it.size_hint(), (0, Some(0)));
+    assert_eq!(it.next(), None);
+}
+
+#[test]
+fn test_iterator_clone() {
+    let mut n = LinkedList::new();
+    n.push_back(2);
+    n.push_back(3);
+    n.push_back(4);
+    let mut it = n.iter();
+    it.next();
+    let mut jt = it.clone();
+    assert_eq!(it.next(), jt.next());
+    assert_eq!(it.next_back(), jt.next_back());
+    assert_eq!(it.next(), jt.next());
+}
+
+#[test]
+fn test_iterator_double_end() {
+    let mut n = LinkedList::new();
+    assert_eq!(n.iter().next(), None);
+    n.push_front(4);
+    n.push_front(5);
+    n.push_front(6);
+    let mut it = n.iter();
+    assert_eq!(it.size_hint(), (3, Some(3)));
+    assert_eq!(it.next().unwrap(), &6);
+    assert_eq!(it.size_hint(), (2, Some(2)));
+    assert_eq!(it.next_back().unwrap(), &4);
+    assert_eq!(it.size_hint(), (1, Some(1)));
+    assert_eq!(it.next_back().unwrap(), &5);
+    assert_eq!(it.next_back(), None);
+    assert_eq!(it.next(), None);
+}
+
+#[test]
+fn test_rev_iter() {
+    let m = generate_test();
+    for (i, elt) in m.iter().rev().enumerate() {
+        assert_eq!((6 - i) as i32, *elt);
+    }
+    let mut n = LinkedList::new();
+    assert_eq!(n.iter().rev().next(), None);
+    n.push_front(4);
+    let mut it = n.iter().rev();
+    assert_eq!(it.size_hint(), (1, Some(1)));
+    assert_eq!(it.next().unwrap(), &4);
+    assert_eq!(it.size_hint(), (0, Some(0)));
+    assert_eq!(it.next(), None);
+}
+
+#[test]
+fn test_mut_iter() {
+    let mut m = generate_test();
+    let mut len = m.len();
+    for (i, elt) in m.iter_mut().enumerate() {
+        assert_eq!(i as i32, *elt);
+        len -= 1;
+    }
+    assert_eq!(len, 0);
+    let mut n = LinkedList::new();
+    assert!(n.iter_mut().next().is_none());
+    n.push_front(4);
+    n.push_back(5);
+    let mut it = n.iter_mut();
+    assert_eq!(it.size_hint(), (2, Some(2)));
+    assert!(it.next().is_some());
+    assert!(it.next().is_some());
+    assert_eq!(it.size_hint(), (0, Some(0)));
+    assert!(it.next().is_none());
+}
+
+#[test]
+fn test_iterator_mut_double_end() {
+    let mut n = LinkedList::new();
+    assert!(n.iter_mut().next_back().is_none());
+    n.push_front(4);
+    n.push_front(5);
+    n.push_front(6);
+    let mut it = n.iter_mut();
+    assert_eq!(it.size_hint(), (3, Some(3)));
+    assert_eq!(*it.next().unwrap(), 6);
+    assert_eq!(it.size_hint(), (2, Some(2)));
+    assert_eq!(*it.next_back().unwrap(), 4);
+    assert_eq!(it.size_hint(), (1, Some(1)));
+    assert_eq!(*it.next_back().unwrap(), 5);
+    assert!(it.next_back().is_none());
+    assert!(it.next().is_none());
+}
+
+#[test]
+fn test_mut_rev_iter() {
+    let mut m = generate_test();
+    for (i, elt) in m.iter_mut().rev().enumerate() {
+        assert_eq!((6 - i) as i32, *elt);
+    }
+    let mut n = LinkedList::new();
+    assert!(n.iter_mut().rev().next().is_none());
+    n.push_front(4);
+    let mut it = n.iter_mut().rev();
+    assert!(it.next().is_some());
+    assert!(it.next().is_none());
+}
+
+#[test]
 fn test_clone_from() {
     // Short cloned from long
     {
@@ -168,13 +330,60 @@ fn test_send() {
 }
 
 #[test]
-fn test_fuzz() {
-    for _ in 0..25 {
-        fuzz_test(3);
-        fuzz_test(16);
-        #[cfg(not(miri))] // Miri is too slow
-        fuzz_test(189);
-    }
+fn test_eq() {
+    let mut n = list_from(&[]);
+    let mut m = list_from(&[]);
+    assert!(n == m);
+    n.push_front(1);
+    assert!(n != m);
+    m.push_back(1);
+    assert!(n == m);
+
+    let n = list_from(&[2, 3, 4]);
+    let m = list_from(&[1, 2, 3]);
+    assert!(n != m);
+}
+
+#[test]
+fn test_ord() {
+    let n = list_from(&[]);
+    let m = list_from(&[1, 2, 3]);
+    assert!(n < m);
+    assert!(m > n);
+    assert!(n <= n);
+    assert!(n >= n);
+}
+
+#[test]
+fn test_ord_nan() {
+    let nan = 0.0f64 / 0.0;
+    let n = list_from(&[nan]);
+    let m = list_from(&[nan]);
+    assert!(!(n < m));
+    assert!(!(n > m));
+    assert!(!(n <= m));
+    assert!(!(n >= m));
+
+    let n = list_from(&[nan]);
+    let one = list_from(&[1.0f64]);
+    assert!(!(n < one));
+    assert!(!(n > one));
+    assert!(!(n <= one));
+    assert!(!(n >= one));
+
+    let u = list_from(&[1.0f64, 2.0, nan]);
+    let v = list_from(&[1.0f64, 2.0, 3.0]);
+    assert!(!(u < v));
+    assert!(!(u > v));
+    assert!(!(u <= v));
+    assert!(!(u >= v));
+
+    let s = list_from(&[1.0f64, 2.0, 4.0, 2.0]);
+    let t = list_from(&[1.0f64, 2.0, 3.0, 2.0]);
+    assert!(!(s < t));
+    assert!(s > one);
+    assert!(!(s <= one));
+    assert!(s >= one);
 }
 
 #[test]
@@ -215,6 +424,62 @@ fn test_split_off() {
     }
 }
 
+#[test]
+fn test_split_off_2() {
+    // singleton
+    {
+        let mut m = LinkedList::new();
+        m.push_back(1);
+
+        let p = m.split_off(0);
+        assert_eq!(m.len(), 0);
+        assert_eq!(p.len(), 1);
+        assert_eq!(p.back(), Some(&1));
+        assert_eq!(p.front(), Some(&1));
+    }
+
+    // not singleton, forwards
+    {
+        let u = vec![1, 2, 3, 4, 5];
+        let mut m = list_from(&u);
+        let mut n = m.split_off(2);
+        assert_eq!(m.len(), 2);
+        assert_eq!(n.len(), 3);
+        for elt in 1..3 {
+            assert_eq!(m.pop_front(), Some(elt));
+        }
+        for elt in 3..6 {
+            assert_eq!(n.pop_front(), Some(elt));
+        }
+    }
+    // not singleton, backwards
+    {
+        let u = vec![1, 2, 3, 4, 5];
+        let mut m = list_from(&u);
+        let mut n = m.split_off(4);
+        assert_eq!(m.len(), 4);
+        assert_eq!(n.len(), 1);
+        for elt in 1..5 {
+            assert_eq!(m.pop_front(), Some(elt));
+        }
+        for elt in 5..6 {
+            assert_eq!(n.pop_front(), Some(elt));
+        }
+    }
+
+    // no-op on the last index
+    {
+        let mut m = LinkedList::new();
+        m.push_back(1);
+
+        let p = m.split_off(1);
+        assert_eq!(m.len(), 1);
+        assert_eq!(p.len(), 0);
+        assert_eq!(m.back(), Some(&1));
+        assert_eq!(m.front(), Some(&1));
+    }
+}
+
 fn fuzz_test(sz: i32) {
     let mut m: LinkedList<_> = LinkedList::new();
     let mut v = vec![];
@@ -254,6 +519,25 @@ fn fuzz_test(sz: i32) {
 }
 
 #[test]
+fn test_fuzz() {
+    for _ in 0..25 {
+        fuzz_test(3);
+        fuzz_test(16);
+        #[cfg(not(miri))] // Miri is too slow
+        fuzz_test(189);
+    }
+}
+
+#[test]
+fn test_show() {
+    let list: LinkedList<_> = (0..10).collect();
+    assert_eq!(format!("{list:?}"), "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]");
+
+    let list: LinkedList<_> = ["just", "one", "test", "more"].into_iter().collect();
+    assert_eq!(format!("{list:?}"), "[\"just\", \"one\", \"test\", \"more\"]");
+}
+
+#[test]
 fn drain_filter_test() {
     let mut m: LinkedList<u32> = LinkedList::new();
     m.extend(&[1, 2, 3, 4, 5, 6]);
@@ -475,3 +759,398 @@ fn test_cursor_pop_front_back() {
     assert_eq!(c.current(), None);
     assert_eq!(c.index, 2);
 }
+
+#[test]
+fn test_extend_ref() {
+    let mut a = LinkedList::new();
+    a.push_back(1);
+
+    a.extend(&[2, 3, 4]);
+
+    assert_eq!(a.len(), 4);
+    assert_eq!(a, list_from(&[1, 2, 3, 4]));
+
+    let mut b = LinkedList::new();
+    b.push_back(5);
+    b.push_back(6);
+    a.extend(&b);
+
+    assert_eq!(a.len(), 6);
+    assert_eq!(a, list_from(&[1, 2, 3, 4, 5, 6]));
+}
+
+#[test]
+fn test_extend() {
+    let mut a = LinkedList::new();
+    a.push_back(1);
+    a.extend(vec![2, 3, 4]); // uses iterator
+
+    assert_eq!(a.len(), 4);
+    assert!(a.iter().eq(&[1, 2, 3, 4]));
+
+    let b: LinkedList<_> = [5, 6, 7].into_iter().collect();
+    a.extend(b); // specializes to `append`
+
+    assert_eq!(a.len(), 7);
+    assert!(a.iter().eq(&[1, 2, 3, 4, 5, 6, 7]));
+}
+
+#[test]
+fn test_contains() {
+    let mut l = LinkedList::new();
+    l.extend(&[2, 3, 4]);
+
+    assert!(l.contains(&3));
+    assert!(!l.contains(&1));
+
+    l.clear();
+
+    assert!(!l.contains(&3));
+}
+
+#[test]
+fn drain_filter_empty() {
+    let mut list: LinkedList<i32> = LinkedList::new();
+
+    {
+        let mut iter = list.drain_filter(|_| true);
+        assert_eq!(iter.size_hint(), (0, Some(0)));
+        assert_eq!(iter.next(), None);
+        assert_eq!(iter.size_hint(), (0, Some(0)));
+        assert_eq!(iter.next(), None);
+        assert_eq!(iter.size_hint(), (0, Some(0)));
+    }
+
+    assert_eq!(list.len(), 0);
+    assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![]);
+}
+
+#[test]
+fn drain_filter_zst() {
+    let mut list: LinkedList<_> = [(), (), (), (), ()].into_iter().collect();
+    let initial_len = list.len();
+    let mut count = 0;
+
+    {
+        let mut iter = list.drain_filter(|_| true);
+        assert_eq!(iter.size_hint(), (0, Some(initial_len)));
+        while let Some(_) = iter.next() {
+            count += 1;
+            assert_eq!(iter.size_hint(), (0, Some(initial_len - count)));
+        }
+        assert_eq!(iter.size_hint(), (0, Some(0)));
+        assert_eq!(iter.next(), None);
+        assert_eq!(iter.size_hint(), (0, Some(0)));
+    }
+
+    assert_eq!(count, initial_len);
+    assert_eq!(list.len(), 0);
+    assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![]);
+}
+
+#[test]
+fn drain_filter_false() {
+    let mut list: LinkedList<_> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect();
+
+    let initial_len = list.len();
+    let mut count = 0;
+
+    {
+        let mut iter = list.drain_filter(|_| false);
+        assert_eq!(iter.size_hint(), (0, Some(initial_len)));
+        for _ in iter.by_ref() {
+            count += 1;
+        }
+        assert_eq!(iter.size_hint(), (0, Some(0)));
+        assert_eq!(iter.next(), None);
+        assert_eq!(iter.size_hint(), (0, Some(0)));
+    }
+
+    assert_eq!(count, 0);
+    assert_eq!(list.len(), initial_len);
+    assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
+}
+
+#[test]
+fn drain_filter_true() {
+    let mut list: LinkedList<_> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect();
+
+    let initial_len = list.len();
+    let mut count = 0;
+
+    {
+        let mut iter = list.drain_filter(|_| true);
+        assert_eq!(iter.size_hint(), (0, Some(initial_len)));
+        while let Some(_) = iter.next() {
+            count += 1;
+            assert_eq!(iter.size_hint(), (0, Some(initial_len - count)));
+        }
+        assert_eq!(iter.size_hint(), (0, Some(0)));
+        assert_eq!(iter.next(), None);
+        assert_eq!(iter.size_hint(), (0, Some(0)));
+    }
+
+    assert_eq!(count, initial_len);
+    assert_eq!(list.len(), 0);
+    assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![]);
+}
+
+#[test]
+fn drain_filter_complex() {
+    {
+        //                [+xxx++++++xxxxx++++x+x++]
+        let mut list = [
+            1, 2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36, 37,
+            39,
+        ]
+        .into_iter()
+        .collect::<LinkedList<_>>();
+
+        let removed = list.drain_filter(|x| *x % 2 == 0).collect::<Vec<_>>();
+        assert_eq!(removed.len(), 10);
+        assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]);
+
+        assert_eq!(list.len(), 14);
+        assert_eq!(
+            list.into_iter().collect::<Vec<_>>(),
+            vec![1, 7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35, 37, 39]
+        );
+    }
+
+    {
+        // [xxx++++++xxxxx++++x+x++]
+        let mut list =
+            [2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36, 37, 39]
+                .into_iter()
+                .collect::<LinkedList<_>>();
+
+        let removed = list.drain_filter(|x| *x % 2 == 0).collect::<Vec<_>>();
+        assert_eq!(removed.len(), 10);
+        assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]);
+
+        assert_eq!(list.len(), 13);
+        assert_eq!(
+            list.into_iter().collect::<Vec<_>>(),
+            vec![7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35, 37, 39]
+        );
+    }
+
+    {
+        // [xxx++++++xxxxx++++x+x]
+        let mut list =
+            [2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36]
+                .into_iter()
+                .collect::<LinkedList<_>>();
+
+        let removed = list.drain_filter(|x| *x % 2 == 0).collect::<Vec<_>>();
+        assert_eq!(removed.len(), 10);
+        assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]);
+
+        assert_eq!(list.len(), 11);
+        assert_eq!(
+            list.into_iter().collect::<Vec<_>>(),
+            vec![7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35]
+        );
+    }
+
+    {
+        // [xxxxxxxxxx+++++++++++]
+        let mut list = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
+            .into_iter()
+            .collect::<LinkedList<_>>();
+
+        let removed = list.drain_filter(|x| *x % 2 == 0).collect::<Vec<_>>();
+        assert_eq!(removed.len(), 10);
+        assert_eq!(removed, vec![2, 4, 6, 8, 10, 12, 14, 16, 18, 20]);
+
+        assert_eq!(list.len(), 10);
+        assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![1, 3, 5, 7, 9, 11, 13, 15, 17, 19]);
+    }
+
+    {
+        // [+++++++++++xxxxxxxxxx]
+        let mut list = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
+            .into_iter()
+            .collect::<LinkedList<_>>();
+
+        let removed = list.drain_filter(|x| *x % 2 == 0).collect::<Vec<_>>();
+        assert_eq!(removed.len(), 10);
+        assert_eq!(removed, vec![2, 4, 6, 8, 10, 12, 14, 16, 18, 20]);
+
+        assert_eq!(list.len(), 10);
+        assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![1, 3, 5, 7, 9, 11, 13, 15, 17, 19]);
+    }
+}
+
+#[test]
+fn drain_filter_drop_panic_leak() {
+    static mut DROPS: i32 = 0;
+
+    struct D(bool);
+
+    impl Drop for D {
+        fn drop(&mut self) {
+            unsafe {
+                DROPS += 1;
+            }
+
+            if self.0 {
+                panic!("panic in `drop`");
+            }
+        }
+    }
+
+    let mut q = LinkedList::new();
+    q.push_back(D(false));
+    q.push_back(D(false));
+    q.push_back(D(false));
+    q.push_back(D(false));
+    q.push_back(D(false));
+    q.push_front(D(false));
+    q.push_front(D(true));
+    q.push_front(D(false));
+
+    catch_unwind(AssertUnwindSafe(|| drop(q.drain_filter(|_| true)))).ok();
+
+    assert_eq!(unsafe { DROPS }, 8);
+    assert!(q.is_empty());
+}
+
+#[test]
+fn drain_filter_pred_panic_leak() {
+    static mut DROPS: i32 = 0;
+
+    #[derive(Debug)]
+    struct D(u32);
+
+    impl Drop for D {
+        fn drop(&mut self) {
+            unsafe {
+                DROPS += 1;
+            }
+        }
+    }
+
+    let mut q = LinkedList::new();
+    q.push_back(D(3));
+    q.push_back(D(4));
+    q.push_back(D(5));
+    q.push_back(D(6));
+    q.push_back(D(7));
+    q.push_front(D(2));
+    q.push_front(D(1));
+    q.push_front(D(0));
+
+    catch_unwind(AssertUnwindSafe(|| {
+        drop(q.drain_filter(|item| if item.0 >= 2 { panic!() } else { true }))
+    }))
+    .ok();
+
+    assert_eq!(unsafe { DROPS }, 2); // 0 and 1
+    assert_eq!(q.len(), 6);
+}
+
+#[test]
+fn test_drop() {
+    static mut DROPS: i32 = 0;
+    struct Elem;
+    impl Drop for Elem {
+        fn drop(&mut self) {
+            unsafe {
+                DROPS += 1;
+            }
+        }
+    }
+
+    let mut ring = LinkedList::new();
+    ring.push_back(Elem);
+    ring.push_front(Elem);
+    ring.push_back(Elem);
+    ring.push_front(Elem);
+    drop(ring);
+
+    assert_eq!(unsafe { DROPS }, 4);
+}
+
+#[test]
+fn test_drop_with_pop() {
+    static mut DROPS: i32 = 0;
+    struct Elem;
+    impl Drop for Elem {
+        fn drop(&mut self) {
+            unsafe {
+                DROPS += 1;
+            }
+        }
+    }
+
+    let mut ring = LinkedList::new();
+    ring.push_back(Elem);
+    ring.push_front(Elem);
+    ring.push_back(Elem);
+    ring.push_front(Elem);
+
+    drop(ring.pop_back());
+    drop(ring.pop_front());
+    assert_eq!(unsafe { DROPS }, 2);
+
+    drop(ring);
+    assert_eq!(unsafe { DROPS }, 4);
+}
+
+#[test]
+fn test_drop_clear() {
+    static mut DROPS: i32 = 0;
+    struct Elem;
+    impl Drop for Elem {
+        fn drop(&mut self) {
+            unsafe {
+                DROPS += 1;
+            }
+        }
+    }
+
+    let mut ring = LinkedList::new();
+    ring.push_back(Elem);
+    ring.push_front(Elem);
+    ring.push_back(Elem);
+    ring.push_front(Elem);
+    ring.clear();
+    assert_eq!(unsafe { DROPS }, 4);
+
+    drop(ring);
+    assert_eq!(unsafe { DROPS }, 4);
+}
+
+#[test]
+fn test_drop_panic() {
+    static mut DROPS: i32 = 0;
+
+    struct D(bool);
+
+    impl Drop for D {
+        fn drop(&mut self) {
+            unsafe {
+                DROPS += 1;
+            }
+
+            if self.0 {
+                panic!("panic in `drop`");
+            }
+        }
+    }
+
+    let mut q = LinkedList::new();
+    q.push_back(D(false));
+    q.push_back(D(false));
+    q.push_back(D(false));
+    q.push_back(D(false));
+    q.push_back(D(false));
+    q.push_front(D(false));
+    q.push_front(D(false));
+    q.push_front(D(true));
+
+    catch_unwind(move || drop(q)).ok();
+
+    assert_eq!(unsafe { DROPS }, 8);
+}
diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs
index 488671d8d8d..5f1a6848ae6 100644
--- a/library/alloc/src/collections/vec_deque/mod.rs
+++ b/library/alloc/src/collections/vec_deque/mod.rs
@@ -54,6 +54,10 @@ use self::ring_slices::RingSlices;
 
 mod ring_slices;
 
+use self::spec_extend::SpecExtend;
+
+mod spec_extend;
+
 #[cfg(test)]
 mod tests;
 
@@ -1342,6 +1346,12 @@ impl<T, A: Allocator> VecDeque<T, A> {
     /// Returns `true` if the deque contains an element equal to the
     /// given value.
     ///
+    /// This operation is *O*(*n*).
+    ///
+    /// Note that if you have a sorted `VecDeque`, [`binary_search`] may be faster.
+    ///
+    /// [`binary_search`]: VecDeque::binary_search
+    ///
     /// # Examples
     ///
     /// ```
@@ -2560,7 +2570,8 @@ impl<T, A: Allocator> VecDeque<T, A> {
         }
     }
 
-    /// Binary searches the sorted deque for a given element.
+    /// Binary searches this `VecDeque` for a given element.
+    /// This behaves similarly to [`contains`] if this `VecDeque` is sorted.
     ///
     /// If the value is found then [`Result::Ok`] is returned, containing the
     /// index of the matching element. If there are multiple matches, then any
@@ -2570,6 +2581,7 @@ impl<T, A: Allocator> VecDeque<T, A> {
     ///
     /// See also [`binary_search_by`], [`binary_search_by_key`], and [`partition_point`].
     ///
+    /// [`contains`]: VecDeque::contains
     /// [`binary_search_by`]: VecDeque::binary_search_by
     /// [`binary_search_by_key`]: VecDeque::binary_search_by_key
     /// [`partition_point`]: VecDeque::partition_point
@@ -2614,7 +2626,8 @@ impl<T, A: Allocator> VecDeque<T, A> {
         self.binary_search_by(|e| e.cmp(x))
     }
 
-    /// Binary searches the sorted deque with a comparator function.
+    /// Binary searches this `VecDeque` with a comparator function.
+    /// This behaves similarly to [`contains`] if this `VecDeque` is sorted.
     ///
     /// The comparator function should implement an order consistent
     /// with the sort order of the deque, returning an order code that
@@ -2629,6 +2642,7 @@ impl<T, A: Allocator> VecDeque<T, A> {
     ///
     /// See also [`binary_search`], [`binary_search_by_key`], and [`partition_point`].
     ///
+    /// [`contains`]: VecDeque::contains
     /// [`binary_search`]: VecDeque::binary_search
     /// [`binary_search_by_key`]: VecDeque::binary_search_by_key
     /// [`partition_point`]: VecDeque::partition_point
@@ -2667,7 +2681,8 @@ impl<T, A: Allocator> VecDeque<T, A> {
         }
     }
 
-    /// Binary searches the sorted deque with a key extraction function.
+    /// Binary searches this `VecDeque` with a key extraction function.
+    /// This behaves similarly to [`contains`] if this `VecDeque` is sorted.
     ///
     /// Assumes that the deque is sorted by the key, for instance with
     /// [`make_contiguous().sort_by_key()`] using the same key extraction function.
@@ -2680,6 +2695,7 @@ impl<T, A: Allocator> VecDeque<T, A> {
     ///
     /// See also [`binary_search`], [`binary_search_by`], and [`partition_point`].
     ///
+    /// [`contains`]: VecDeque::contains
     /// [`make_contiguous().sort_by_key()`]: VecDeque::make_contiguous
     /// [`binary_search`]: VecDeque::binary_search
     /// [`binary_search_by`]: VecDeque::binary_search_by
@@ -2958,24 +2974,7 @@ impl<'a, T, A: Allocator> IntoIterator for &'a mut VecDeque<T, A> {
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T, A: Allocator> Extend<T> for VecDeque<T, A> {
     fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
-        // This function should be the moral equivalent of:
-        //
-        //      for item in iter.into_iter() {
-        //          self.push_back(item);
-        //      }
-        let mut iter = iter.into_iter();
-        while let Some(element) = iter.next() {
-            if self.len() == self.capacity() {
-                let (lower, _) = iter.size_hint();
-                self.reserve(lower.saturating_add(1));
-            }
-
-            let head = self.head;
-            self.head = self.wrap_add(self.head, 1);
-            unsafe {
-                self.buffer_write(head, element);
-            }
-        }
+        <Self as SpecExtend<T, I::IntoIter>>::spec_extend(self, iter.into_iter());
     }
 
     #[inline]
@@ -2992,7 +2991,7 @@ impl<T, A: Allocator> Extend<T> for VecDeque<T, A> {
 #[stable(feature = "extend_ref", since = "1.2.0")]
 impl<'a, T: 'a + Copy, A: Allocator> Extend<&'a T> for VecDeque<T, A> {
     fn extend<I: IntoIterator<Item = &'a T>>(&mut self, iter: I) {
-        self.extend(iter.into_iter().cloned());
+        self.spec_extend(iter.into_iter());
     }
 
     #[inline]
diff --git a/library/alloc/src/collections/vec_deque/spec_extend.rs b/library/alloc/src/collections/vec_deque/spec_extend.rs
new file mode 100644
index 00000000000..172f2e9068f
--- /dev/null
+++ b/library/alloc/src/collections/vec_deque/spec_extend.rs
@@ -0,0 +1,73 @@
+use crate::alloc::Allocator;
+use crate::vec;
+use core::slice;
+
+use super::VecDeque;
+
+// Specialization trait used for VecDeque::extend
+pub(super) trait SpecExtend<T, I> {
+    fn spec_extend(&mut self, iter: I);
+}
+
+impl<T, I, A: Allocator> SpecExtend<T, I> for VecDeque<T, A>
+where
+    I: Iterator<Item = T>,
+{
+    default fn spec_extend(&mut self, mut iter: I) {
+        // This function should be the moral equivalent of:
+        //
+        //      for item in iter {
+        //          self.push_back(item);
+        //      }
+        while let Some(element) = iter.next() {
+            if self.len() == self.capacity() {
+                let (lower, _) = iter.size_hint();
+                self.reserve(lower.saturating_add(1));
+            }
+
+            let head = self.head;
+            self.head = self.wrap_add(self.head, 1);
+            unsafe {
+                self.buffer_write(head, element);
+            }
+        }
+    }
+}
+
+impl<T, A: Allocator> SpecExtend<T, vec::IntoIter<T>> for VecDeque<T, A> {
+    fn spec_extend(&mut self, mut iterator: vec::IntoIter<T>) {
+        let slice = iterator.as_slice();
+        self.reserve(slice.len());
+
+        unsafe {
+            self.copy_slice(self.head, slice);
+            self.head = self.wrap_add(self.head, slice.len());
+        }
+        iterator.forget_remaining_elements();
+    }
+}
+
+impl<'a, T: 'a, I, A: Allocator> SpecExtend<&'a T, I> for VecDeque<T, A>
+where
+    I: Iterator<Item = &'a T>,
+    T: Copy,
+{
+    default fn spec_extend(&mut self, iterator: I) {
+        self.spec_extend(iterator.copied())
+    }
+}
+
+impl<'a, T: 'a, A: Allocator> SpecExtend<&'a T, slice::Iter<'a, T>> for VecDeque<T, A>
+where
+    T: Copy,
+{
+    fn spec_extend(&mut self, iterator: slice::Iter<'a, T>) {
+        let slice = iterator.as_slice();
+        self.reserve(slice.len());
+
+        unsafe {
+            self.copy_slice(self.head, slice);
+            self.head = self.wrap_add(self.head, slice.len());
+        }
+    }
+}
diff --git a/library/alloc/src/collections/vec_deque/tests.rs b/library/alloc/src/collections/vec_deque/tests.rs
index 2be83f68f01..f2869a54713 100644
--- a/library/alloc/src/collections/vec_deque/tests.rs
+++ b/library/alloc/src/collections/vec_deque/tests.rs
@@ -163,6 +163,300 @@ fn test_insert() {
 }
 
 #[test]
+fn test_get() {
+    let mut tester = VecDeque::new();
+    tester.push_back(1);
+    tester.push_back(2);
+    tester.push_back(3);
+
+    assert_eq!(tester.len(), 3);
+
+    assert_eq!(tester.get(1), Some(&2));
+    assert_eq!(tester.get(2), Some(&3));
+    assert_eq!(tester.get(0), Some(&1));
+    assert_eq!(tester.get(3), None);
+
+    tester.remove(0);
+
+    assert_eq!(tester.len(), 2);
+    assert_eq!(tester.get(0), Some(&2));
+    assert_eq!(tester.get(1), Some(&3));
+    assert_eq!(tester.get(2), None);
+}
+
+#[test]
+fn test_get_mut() {
+    let mut tester = VecDeque::new();
+    tester.push_back(1);
+    tester.push_back(2);
+    tester.push_back(3);
+
+    assert_eq!(tester.len(), 3);
+
+    if let Some(elem) = tester.get_mut(0) {
+        assert_eq!(*elem, 1);
+        *elem = 10;
+    }
+
+    if let Some(elem) = tester.get_mut(2) {
+        assert_eq!(*elem, 3);
+        *elem = 30;
+    }
+
+    assert_eq!(tester.get(0), Some(&10));
+    assert_eq!(tester.get(2), Some(&30));
+    assert_eq!(tester.get_mut(3), None);
+
+    tester.remove(2);
+
+    assert_eq!(tester.len(), 2);
+    assert_eq!(tester.get(0), Some(&10));
+    assert_eq!(tester.get(1), Some(&2));
+    assert_eq!(tester.get(2), None);
+}
+
+#[test]
+fn test_swap() {
+    let mut tester = VecDeque::new();
+    tester.push_back(1);
+    tester.push_back(2);
+    tester.push_back(3);
+
+    assert_eq!(tester, [1, 2, 3]);
+
+    tester.swap(0, 0);
+    assert_eq!(tester, [1, 2, 3]);
+    tester.swap(0, 1);
+    assert_eq!(tester, [2, 1, 3]);
+    tester.swap(2, 1);
+    assert_eq!(tester, [2, 3, 1]);
+    tester.swap(1, 2);
+    assert_eq!(tester, [2, 1, 3]);
+    tester.swap(0, 2);
+    assert_eq!(tester, [3, 1, 2]);
+    tester.swap(2, 2);
+    assert_eq!(tester, [3, 1, 2]);
+}
+
+#[test]
+#[should_panic = "assertion failed: j < self.len()"]
+fn test_swap_panic() {
+    let mut tester = VecDeque::new();
+    tester.push_back(1);
+    tester.push_back(2);
+    tester.push_back(3);
+    tester.swap(2, 3);
+}
+
+#[test]
+fn test_reserve_exact() {
+    let mut tester: VecDeque<i32> = VecDeque::with_capacity(1);
+    assert!(tester.capacity() == 1);
+    tester.reserve_exact(50);
+    assert!(tester.capacity() >= 51);
+    tester.reserve_exact(40);
+    assert!(tester.capacity() >= 51);
+    tester.reserve_exact(200);
+    assert!(tester.capacity() >= 200);
+}
+
+#[test]
+#[should_panic = "capacity overflow"]
+fn test_reserve_exact_panic() {
+    let mut tester: VecDeque<i32> = VecDeque::new();
+    tester.reserve_exact(usize::MAX);
+}
+
+#[test]
+fn test_try_reserve_exact() {
+    let mut tester: VecDeque<i32> = VecDeque::with_capacity(1);
+    assert!(tester.capacity() == 1);
+    assert_eq!(tester.try_reserve_exact(100), Ok(()));
+    assert!(tester.capacity() >= 100);
+    assert_eq!(tester.try_reserve_exact(50), Ok(()));
+    assert!(tester.capacity() >= 100);
+    assert_eq!(tester.try_reserve_exact(200), Ok(()));
+    assert!(tester.capacity() >= 200);
+    assert_eq!(tester.try_reserve_exact(0), Ok(()));
+    assert!(tester.capacity() >= 200);
+    assert!(tester.try_reserve_exact(usize::MAX).is_err());
+}
+
+#[test]
+fn test_try_reserve() {
+    let mut tester: VecDeque<i32> = VecDeque::with_capacity(1);
+    assert!(tester.capacity() == 1);
+    assert_eq!(tester.try_reserve(100), Ok(()));
+    assert!(tester.capacity() >= 100);
+    assert_eq!(tester.try_reserve(50), Ok(()));
+    assert!(tester.capacity() >= 100);
+    assert_eq!(tester.try_reserve(200), Ok(()));
+    assert!(tester.capacity() >= 200);
+    assert_eq!(tester.try_reserve(0), Ok(()));
+    assert!(tester.capacity() >= 200);
+    assert!(tester.try_reserve(usize::MAX).is_err());
+}
+
+#[test]
+fn test_contains() {
+    let mut tester = VecDeque::new();
+    tester.push_back(1);
+    tester.push_back(2);
+    tester.push_back(3);
+
+    assert!(tester.contains(&1));
+    assert!(tester.contains(&3));
+    assert!(!tester.contains(&0));
+    assert!(!tester.contains(&4));
+    tester.remove(0);
+    assert!(!tester.contains(&1));
+    assert!(tester.contains(&2));
+    assert!(tester.contains(&3));
+}
+
+#[test]
+fn test_rotate_left_right() {
+    let mut tester: VecDeque<_> = (1..=10).collect();
+
+    assert_eq!(tester.len(), 10);
+
+    tester.rotate_left(0);
+    assert_eq!(tester, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
+
+    tester.rotate_right(0);
+    assert_eq!(tester, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
+
+    tester.rotate_left(3);
+    assert_eq!(tester, [4, 5, 6, 7, 8, 9, 10, 1, 2, 3]);
+
+    tester.rotate_right(5);
+    assert_eq!(tester, [9, 10, 1, 2, 3, 4, 5, 6, 7, 8]);
+
+    tester.rotate_left(tester.len());
+    assert_eq!(tester, [9, 10, 1, 2, 3, 4, 5, 6, 7, 8]);
+
+    tester.rotate_right(tester.len());
+    assert_eq!(tester, [9, 10, 1, 2, 3, 4, 5, 6, 7, 8]);
+
+    tester.rotate_left(1);
+    assert_eq!(tester, [10, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
+}
+
+#[test]
+#[should_panic = "assertion failed: mid <= self.len()"]
+fn test_rotate_left_panic() {
+    let mut tester: VecDeque<_> = (1..=10).collect();
+    tester.rotate_left(tester.len() + 1);
+}
+
+#[test]
+#[should_panic = "assertion failed: k <= self.len()"]
+fn test_rotate_right_panic() {
+    let mut tester: VecDeque<_> = (1..=10).collect();
+    tester.rotate_right(tester.len() + 1);
+}
+
+#[test]
+fn test_binary_search() {
+    // If the givin VecDeque is not sorted, the returned result is unspecified and meaningless,
+    // as this method performs a binary search.
+
+    let tester: VecDeque<_> = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55].into();
+
+    assert_eq!(tester.binary_search(&0), Ok(0));
+    assert_eq!(tester.binary_search(&5), Ok(5));
+    assert_eq!(tester.binary_search(&55), Ok(10));
+    assert_eq!(tester.binary_search(&4), Err(5));
+    assert_eq!(tester.binary_search(&-1), Err(0));
+    assert!(matches!(tester.binary_search(&1), Ok(1..=2)));
+
+    let tester: VecDeque<_> = [1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3].into();
+    assert_eq!(tester.binary_search(&1), Ok(0));
+    assert!(matches!(tester.binary_search(&2), Ok(1..=4)));
+    assert!(matches!(tester.binary_search(&3), Ok(5..=13)));
+    assert_eq!(tester.binary_search(&-2), Err(0));
+    assert_eq!(tester.binary_search(&0), Err(0));
+    assert_eq!(tester.binary_search(&4), Err(14));
+    assert_eq!(tester.binary_search(&5), Err(14));
+}
+
+#[test]
+fn test_binary_search_by() {
+    // If the givin VecDeque is not sorted, the returned result is unspecified and meaningless,
+    // as this method performs a binary search.
+
+    let tester: VecDeque<_> = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55].into();
+
+    assert_eq!(tester.binary_search_by(|x| x.cmp(&0)), Ok(0));
+    assert_eq!(tester.binary_search_by(|x| x.cmp(&5)), Ok(5));
+    assert_eq!(tester.binary_search_by(|x| x.cmp(&55)), Ok(10));
+    assert_eq!(tester.binary_search_by(|x| x.cmp(&4)), Err(5));
+    assert_eq!(tester.binary_search_by(|x| x.cmp(&-1)), Err(0));
+    assert!(matches!(tester.binary_search_by(|x| x.cmp(&1)), Ok(1..=2)));
+}
+
+#[test]
+fn test_binary_search_key() {
+    // If the givin VecDeque is not sorted, the returned result is unspecified and meaningless,
+    // as this method performs a binary search.
+
+    let tester: VecDeque<_> = [
+        (-1, 0),
+        (2, 10),
+        (6, 5),
+        (7, 1),
+        (8, 10),
+        (10, 2),
+        (20, 3),
+        (24, 5),
+        (25, 18),
+        (28, 13),
+        (31, 21),
+        (32, 4),
+        (54, 25),
+    ]
+    .into();
+
+    assert_eq!(tester.binary_search_by_key(&-1, |&(a, _b)| a), Ok(0));
+    assert_eq!(tester.binary_search_by_key(&8, |&(a, _b)| a), Ok(4));
+    assert_eq!(tester.binary_search_by_key(&25, |&(a, _b)| a), Ok(8));
+    assert_eq!(tester.binary_search_by_key(&54, |&(a, _b)| a), Ok(12));
+    assert_eq!(tester.binary_search_by_key(&-2, |&(a, _b)| a), Err(0));
+    assert_eq!(tester.binary_search_by_key(&1, |&(a, _b)| a), Err(1));
+    assert_eq!(tester.binary_search_by_key(&4, |&(a, _b)| a), Err(2));
+    assert_eq!(tester.binary_search_by_key(&13, |&(a, _b)| a), Err(6));
+    assert_eq!(tester.binary_search_by_key(&55, |&(a, _b)| a), Err(13));
+    assert_eq!(tester.binary_search_by_key(&100, |&(a, _b)| a), Err(13));
+
+    let tester: VecDeque<_> = [
+        (0, 0),
+        (2, 1),
+        (6, 1),
+        (5, 1),
+        (3, 1),
+        (1, 2),
+        (2, 3),
+        (4, 5),
+        (5, 8),
+        (8, 13),
+        (1, 21),
+        (2, 34),
+        (4, 55),
+    ]
+    .into();
+
+    assert_eq!(tester.binary_search_by_key(&0, |&(_a, b)| b), Ok(0));
+    assert!(matches!(tester.binary_search_by_key(&1, |&(_a, b)| b), Ok(1..=4)));
+    assert_eq!(tester.binary_search_by_key(&8, |&(_a, b)| b), Ok(8));
+    assert_eq!(tester.binary_search_by_key(&13, |&(_a, b)| b), Ok(9));
+    assert_eq!(tester.binary_search_by_key(&55, |&(_a, b)| b), Ok(12));
+    assert_eq!(tester.binary_search_by_key(&-1, |&(_a, b)| b), Err(0));
+    assert_eq!(tester.binary_search_by_key(&4, |&(_a, b)| b), Err(7));
+    assert_eq!(tester.binary_search_by_key(&56, |&(_a, b)| b), Err(13));
+    assert_eq!(tester.binary_search_by_key(&100, |&(_a, b)| b), Err(13));
+}
+
+#[test]
 fn make_contiguous_big_tail() {
     let mut tester = VecDeque::with_capacity(15);
 
diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs
index a42f1c3b4bb..4bcc78ae0f4 100644
--- a/library/alloc/src/rc.rs
+++ b/library/alloc/src/rc.rs
@@ -1956,6 +1956,25 @@ where
     }
 }
 
+#[stable(feature = "shared_from_str", since = "1.62.0")]
+impl From<Rc<str>> for Rc<[u8]> {
+    /// Converts a reference-counted string slice into a byte slice.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// # use std::rc::Rc;
+    /// let string: Rc<str> = Rc::from("eggplant");
+    /// let bytes: Rc<[u8]> = Rc::from(string);
+    /// assert_eq!("eggplant".as_bytes(), bytes.as_ref());
+    /// ```
+    #[inline]
+    fn from(rc: Rc<str>) -> Self {
+        // SAFETY: `str` has the same layout as `[u8]`.
+        unsafe { Rc::from_raw(Rc::into_raw(rc) as *const [u8]) }
+    }
+}
+
 #[stable(feature = "boxed_slice_try_from", since = "1.43.0")]
 impl<T, const N: usize> TryFrom<Rc<[T]>> for Rc<[T; N]> {
     type Error = Rc<[T]>;
diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs
index a19999cd725..1e2caddcacb 100644
--- a/library/alloc/src/sync.rs
+++ b/library/alloc/src/sync.rs
@@ -2556,6 +2556,25 @@ where
     }
 }
 
+#[stable(feature = "shared_from_str", since = "1.62.0")]
+impl From<Arc<str>> for Arc<[u8]> {
+    /// Converts an atomically reference-counted string slice into a byte slice.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// # use std::sync::Arc;
+    /// let string: Arc<str> = Arc::from("eggplant");
+    /// let bytes: Arc<[u8]> = Arc::from(string);
+    /// assert_eq!("eggplant".as_bytes(), bytes.as_ref());
+    /// ```
+    #[inline]
+    fn from(rc: Arc<str>) -> Self {
+        // SAFETY: `str` has the same layout as `[u8]`.
+        unsafe { Arc::from_raw(Arc::into_raw(rc) as *const [u8]) }
+    }
+}
+
 #[stable(feature = "boxed_slice_try_from", since = "1.43.0")]
 impl<T, const N: usize> TryFrom<Arc<[T]>> for Arc<[T; N]> {
     type Error = Arc<[T]>;
diff --git a/library/alloc/src/vec/into_iter.rs b/library/alloc/src/vec/into_iter.rs
index 03c532bb697..8134eea570a 100644
--- a/library/alloc/src/vec/into_iter.rs
+++ b/library/alloc/src/vec/into_iter.rs
@@ -121,6 +121,11 @@ impl<T, A: Allocator> IntoIter<T, A> {
             ptr::drop_in_place(remaining);
         }
     }
+
+    /// Forgets to Drop the remaining elements while still allowing the backing allocation to be freed.
+    pub(crate) fn forget_remaining_elements(&mut self) {
+        self.ptr = self.end;
+    }
 }
 
 #[stable(feature = "vec_intoiter_as_ref", since = "1.46.0")]
diff --git a/library/alloc/src/vec/is_zero.rs b/library/alloc/src/vec/is_zero.rs
index 0efc4893c3c..868f2f1e323 100644
--- a/library/alloc/src/vec/is_zero.rs
+++ b/library/alloc/src/vec/is_zero.rs
@@ -2,7 +2,7 @@ use crate::boxed::Box;
 
 #[rustc_specialization_trait]
 pub(super) unsafe trait IsZero {
-    /// Whether this value is zero
+    /// Whether this value's representation is all zeros
     fn is_zero(&self) -> bool;
 }
 
@@ -49,6 +49,13 @@ unsafe impl<T> IsZero for *mut T {
     }
 }
 
+unsafe impl<T: IsZero, const N: usize> IsZero for [T; N] {
+    #[inline]
+    fn is_zero(&self) -> bool {
+        self.iter().all(IsZero::is_zero)
+    }
+}
+
 // `Option<&T>` and `Option<Box<T>>` are guaranteed to represent `None` as null.
 // For fat pointers, the bytes that would be the pointer metadata in the `Some`
 // variant are padding in the `None` variant, so ignoring them and
diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs
index 9bf42e779c9..3dc8a4fbba8 100644
--- a/library/alloc/src/vec/mod.rs
+++ b/library/alloc/src/vec/mod.rs
@@ -2989,48 +2989,6 @@ impl<T, const N: usize> From<[T; N]> for Vec<T> {
     }
 }
 
-#[cfg(not(no_global_oom_handling))]
-#[stable(feature = "vec_from_array_ref", since = "1.61.0")]
-impl<T: Clone, const N: usize> From<&[T; N]> for Vec<T> {
-    /// Allocate a `Vec<T>` and fill it by cloning `s`'s items.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// assert_eq!(Vec::from(b"raw"), vec![b'r', b'a', b'w']);
-    /// ```
-    #[cfg(not(test))]
-    fn from(s: &[T; N]) -> Vec<T> {
-        s.to_vec()
-    }
-
-    #[cfg(test)]
-    fn from(s: &[T; N]) -> Vec<T> {
-        crate::slice::to_vec(s, Global)
-    }
-}
-
-#[cfg(not(no_global_oom_handling))]
-#[stable(feature = "vec_from_array_ref", since = "1.61.0")]
-impl<T: Clone, const N: usize> From<&mut [T; N]> for Vec<T> {
-    /// Allocate a `Vec<T>` and fill it by cloning `s`'s items.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// assert_eq!(Vec::from(&mut [1, 2, 3]), vec![1, 2, 3]);
-    /// ```
-    #[cfg(not(test))]
-    fn from(s: &mut [T; N]) -> Vec<T> {
-        s.to_vec()
-    }
-
-    #[cfg(test)]
-    fn from(s: &mut [T; N]) -> Vec<T> {
-        crate::slice::to_vec(s, Global)
-    }
-}
-
 #[stable(feature = "vec_from_cow_slice", since = "1.14.0")]
 impl<'a, T> From<Cow<'a, [T]>> for Vec<T>
 where
diff --git a/library/alloc/src/vec/spec_extend.rs b/library/alloc/src/vec/spec_extend.rs
index c3b4534096d..506ee0ecfa2 100644
--- a/library/alloc/src/vec/spec_extend.rs
+++ b/library/alloc/src/vec/spec_extend.rs
@@ -62,7 +62,7 @@ impl<T, A: Allocator> SpecExtend<T, IntoIter<T>> for Vec<T, A> {
         unsafe {
             self.append_elements(iterator.as_slice() as _);
         }
-        iterator.ptr = iterator.end;
+        iterator.forget_remaining_elements();
     }
 }
 
diff --git a/library/alloc/tests/c_str.rs b/library/alloc/tests/c_str.rs
index 8fbb10e1d5c..4a581793956 100644
--- a/library/alloc/tests/c_str.rs
+++ b/library/alloc/tests/c_str.rs
@@ -1,5 +1,6 @@
 use std::borrow::Cow::{Borrowed, Owned};
-use std::ffi::{c_char, CStr};
+use std::ffi::CStr;
+use std::os::raw::c_char;
 
 #[test]
 fn to_str() {
diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs
index 8de159246c6..601a87aa4ac 100644
--- a/library/alloc/tests/lib.rs
+++ b/library/alloc/tests/lib.rs
@@ -47,7 +47,6 @@ use std::collections::hash_map::DefaultHasher;
 use std::hash::{Hash, Hasher};
 
 mod arc;
-mod binary_heap;
 mod borrow;
 mod boxed;
 mod btree_set_hash;
diff --git a/library/alloc/tests/linked_list.rs b/library/alloc/tests/linked_list.rs
index 66a9cca6644..65b09cb00c4 100644
--- a/library/alloc/tests/linked_list.rs
+++ b/library/alloc/tests/linked_list.rs
@@ -1,241 +1,4 @@
 use std::collections::LinkedList;
-use std::panic::{catch_unwind, AssertUnwindSafe};
-
-#[test]
-fn test_basic() {
-    let mut m = LinkedList::<Box<_>>::new();
-    assert_eq!(m.pop_front(), None);
-    assert_eq!(m.pop_back(), None);
-    assert_eq!(m.pop_front(), None);
-    m.push_front(box 1);
-    assert_eq!(m.pop_front(), Some(box 1));
-    m.push_back(box 2);
-    m.push_back(box 3);
-    assert_eq!(m.len(), 2);
-    assert_eq!(m.pop_front(), Some(box 2));
-    assert_eq!(m.pop_front(), Some(box 3));
-    assert_eq!(m.len(), 0);
-    assert_eq!(m.pop_front(), None);
-    m.push_back(box 1);
-    m.push_back(box 3);
-    m.push_back(box 5);
-    m.push_back(box 7);
-    assert_eq!(m.pop_front(), Some(box 1));
-
-    let mut n = LinkedList::new();
-    n.push_front(2);
-    n.push_front(3);
-    {
-        assert_eq!(n.front().unwrap(), &3);
-        let x = n.front_mut().unwrap();
-        assert_eq!(*x, 3);
-        *x = 0;
-    }
-    {
-        assert_eq!(n.back().unwrap(), &2);
-        let y = n.back_mut().unwrap();
-        assert_eq!(*y, 2);
-        *y = 1;
-    }
-    assert_eq!(n.pop_front(), Some(0));
-    assert_eq!(n.pop_front(), Some(1));
-}
-
-fn generate_test() -> LinkedList<i32> {
-    list_from(&[0, 1, 2, 3, 4, 5, 6])
-}
-
-fn list_from<T: Clone>(v: &[T]) -> LinkedList<T> {
-    v.iter().cloned().collect()
-}
-
-#[test]
-fn test_split_off() {
-    // singleton
-    {
-        let mut m = LinkedList::new();
-        m.push_back(1);
-
-        let p = m.split_off(0);
-        assert_eq!(m.len(), 0);
-        assert_eq!(p.len(), 1);
-        assert_eq!(p.back(), Some(&1));
-        assert_eq!(p.front(), Some(&1));
-    }
-
-    // not singleton, forwards
-    {
-        let u = vec![1, 2, 3, 4, 5];
-        let mut m = list_from(&u);
-        let mut n = m.split_off(2);
-        assert_eq!(m.len(), 2);
-        assert_eq!(n.len(), 3);
-        for elt in 1..3 {
-            assert_eq!(m.pop_front(), Some(elt));
-        }
-        for elt in 3..6 {
-            assert_eq!(n.pop_front(), Some(elt));
-        }
-    }
-    // not singleton, backwards
-    {
-        let u = vec![1, 2, 3, 4, 5];
-        let mut m = list_from(&u);
-        let mut n = m.split_off(4);
-        assert_eq!(m.len(), 4);
-        assert_eq!(n.len(), 1);
-        for elt in 1..5 {
-            assert_eq!(m.pop_front(), Some(elt));
-        }
-        for elt in 5..6 {
-            assert_eq!(n.pop_front(), Some(elt));
-        }
-    }
-
-    // no-op on the last index
-    {
-        let mut m = LinkedList::new();
-        m.push_back(1);
-
-        let p = m.split_off(1);
-        assert_eq!(m.len(), 1);
-        assert_eq!(p.len(), 0);
-        assert_eq!(m.back(), Some(&1));
-        assert_eq!(m.front(), Some(&1));
-    }
-}
-
-#[test]
-fn test_iterator() {
-    let m = generate_test();
-    for (i, elt) in m.iter().enumerate() {
-        assert_eq!(i as i32, *elt);
-    }
-    let mut n = LinkedList::new();
-    assert_eq!(n.iter().next(), None);
-    n.push_front(4);
-    let mut it = n.iter();
-    assert_eq!(it.size_hint(), (1, Some(1)));
-    assert_eq!(it.next().unwrap(), &4);
-    assert_eq!(it.size_hint(), (0, Some(0)));
-    assert_eq!(it.next(), None);
-}
-
-#[test]
-fn test_iterator_clone() {
-    let mut n = LinkedList::new();
-    n.push_back(2);
-    n.push_back(3);
-    n.push_back(4);
-    let mut it = n.iter();
-    it.next();
-    let mut jt = it.clone();
-    assert_eq!(it.next(), jt.next());
-    assert_eq!(it.next_back(), jt.next_back());
-    assert_eq!(it.next(), jt.next());
-}
-
-#[test]
-fn test_iterator_double_end() {
-    let mut n = LinkedList::new();
-    assert_eq!(n.iter().next(), None);
-    n.push_front(4);
-    n.push_front(5);
-    n.push_front(6);
-    let mut it = n.iter();
-    assert_eq!(it.size_hint(), (3, Some(3)));
-    assert_eq!(it.next().unwrap(), &6);
-    assert_eq!(it.size_hint(), (2, Some(2)));
-    assert_eq!(it.next_back().unwrap(), &4);
-    assert_eq!(it.size_hint(), (1, Some(1)));
-    assert_eq!(it.next_back().unwrap(), &5);
-    assert_eq!(it.next_back(), None);
-    assert_eq!(it.next(), None);
-}
-
-#[test]
-fn test_rev_iter() {
-    let m = generate_test();
-    for (i, elt) in m.iter().rev().enumerate() {
-        assert_eq!((6 - i) as i32, *elt);
-    }
-    let mut n = LinkedList::new();
-    assert_eq!(n.iter().rev().next(), None);
-    n.push_front(4);
-    let mut it = n.iter().rev();
-    assert_eq!(it.size_hint(), (1, Some(1)));
-    assert_eq!(it.next().unwrap(), &4);
-    assert_eq!(it.size_hint(), (0, Some(0)));
-    assert_eq!(it.next(), None);
-}
-
-#[test]
-fn test_mut_iter() {
-    let mut m = generate_test();
-    let mut len = m.len();
-    for (i, elt) in m.iter_mut().enumerate() {
-        assert_eq!(i as i32, *elt);
-        len -= 1;
-    }
-    assert_eq!(len, 0);
-    let mut n = LinkedList::new();
-    assert!(n.iter_mut().next().is_none());
-    n.push_front(4);
-    n.push_back(5);
-    let mut it = n.iter_mut();
-    assert_eq!(it.size_hint(), (2, Some(2)));
-    assert!(it.next().is_some());
-    assert!(it.next().is_some());
-    assert_eq!(it.size_hint(), (0, Some(0)));
-    assert!(it.next().is_none());
-}
-
-#[test]
-fn test_iterator_mut_double_end() {
-    let mut n = LinkedList::new();
-    assert!(n.iter_mut().next_back().is_none());
-    n.push_front(4);
-    n.push_front(5);
-    n.push_front(6);
-    let mut it = n.iter_mut();
-    assert_eq!(it.size_hint(), (3, Some(3)));
-    assert_eq!(*it.next().unwrap(), 6);
-    assert_eq!(it.size_hint(), (2, Some(2)));
-    assert_eq!(*it.next_back().unwrap(), 4);
-    assert_eq!(it.size_hint(), (1, Some(1)));
-    assert_eq!(*it.next_back().unwrap(), 5);
-    assert!(it.next_back().is_none());
-    assert!(it.next().is_none());
-}
-
-#[test]
-fn test_mut_rev_iter() {
-    let mut m = generate_test();
-    for (i, elt) in m.iter_mut().rev().enumerate() {
-        assert_eq!((6 - i) as i32, *elt);
-    }
-    let mut n = LinkedList::new();
-    assert!(n.iter_mut().rev().next().is_none());
-    n.push_front(4);
-    let mut it = n.iter_mut().rev();
-    assert!(it.next().is_some());
-    assert!(it.next().is_none());
-}
-
-#[test]
-fn test_eq() {
-    let mut n = list_from(&[]);
-    let mut m = list_from(&[]);
-    assert!(n == m);
-    n.push_front(1);
-    assert!(n != m);
-    m.push_back(1);
-    assert!(n == m);
-
-    let n = list_from(&[2, 3, 4]);
-    let m = list_from(&[1, 2, 3]);
-    assert!(n != m);
-}
 
 #[test]
 fn test_hash() {
@@ -256,449 +19,3 @@ fn test_hash() {
 
     assert!(hash(&x) == hash(&y));
 }
-
-#[test]
-fn test_ord() {
-    let n = list_from(&[]);
-    let m = list_from(&[1, 2, 3]);
-    assert!(n < m);
-    assert!(m > n);
-    assert!(n <= n);
-    assert!(n >= n);
-}
-
-#[test]
-fn test_ord_nan() {
-    let nan = 0.0f64 / 0.0;
-    let n = list_from(&[nan]);
-    let m = list_from(&[nan]);
-    assert!(!(n < m));
-    assert!(!(n > m));
-    assert!(!(n <= m));
-    assert!(!(n >= m));
-
-    let n = list_from(&[nan]);
-    let one = list_from(&[1.0f64]);
-    assert!(!(n < one));
-    assert!(!(n > one));
-    assert!(!(n <= one));
-    assert!(!(n >= one));
-
-    let u = list_from(&[1.0f64, 2.0, nan]);
-    let v = list_from(&[1.0f64, 2.0, 3.0]);
-    assert!(!(u < v));
-    assert!(!(u > v));
-    assert!(!(u <= v));
-    assert!(!(u >= v));
-
-    let s = list_from(&[1.0f64, 2.0, 4.0, 2.0]);
-    let t = list_from(&[1.0f64, 2.0, 3.0, 2.0]);
-    assert!(!(s < t));
-    assert!(s > one);
-    assert!(!(s <= one));
-    assert!(s >= one);
-}
-
-#[test]
-fn test_show() {
-    let list: LinkedList<_> = (0..10).collect();
-    assert_eq!(format!("{list:?}"), "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]");
-
-    let list: LinkedList<_> = ["just", "one", "test", "more"].into_iter().collect();
-    assert_eq!(format!("{list:?}"), "[\"just\", \"one\", \"test\", \"more\"]");
-}
-
-#[test]
-fn test_extend_ref() {
-    let mut a = LinkedList::new();
-    a.push_back(1);
-
-    a.extend(&[2, 3, 4]);
-
-    assert_eq!(a.len(), 4);
-    assert_eq!(a, list_from(&[1, 2, 3, 4]));
-
-    let mut b = LinkedList::new();
-    b.push_back(5);
-    b.push_back(6);
-    a.extend(&b);
-
-    assert_eq!(a.len(), 6);
-    assert_eq!(a, list_from(&[1, 2, 3, 4, 5, 6]));
-}
-
-#[test]
-fn test_extend() {
-    let mut a = LinkedList::new();
-    a.push_back(1);
-    a.extend(vec![2, 3, 4]); // uses iterator
-
-    assert_eq!(a.len(), 4);
-    assert!(a.iter().eq(&[1, 2, 3, 4]));
-
-    let b: LinkedList<_> = [5, 6, 7].into_iter().collect();
-    a.extend(b); // specializes to `append`
-
-    assert_eq!(a.len(), 7);
-    assert!(a.iter().eq(&[1, 2, 3, 4, 5, 6, 7]));
-}
-
-#[test]
-fn test_contains() {
-    let mut l = LinkedList::new();
-    l.extend(&[2, 3, 4]);
-
-    assert!(l.contains(&3));
-    assert!(!l.contains(&1));
-
-    l.clear();
-
-    assert!(!l.contains(&3));
-}
-
-#[test]
-fn drain_filter_empty() {
-    let mut list: LinkedList<i32> = LinkedList::new();
-
-    {
-        let mut iter = list.drain_filter(|_| true);
-        assert_eq!(iter.size_hint(), (0, Some(0)));
-        assert_eq!(iter.next(), None);
-        assert_eq!(iter.size_hint(), (0, Some(0)));
-        assert_eq!(iter.next(), None);
-        assert_eq!(iter.size_hint(), (0, Some(0)));
-    }
-
-    assert_eq!(list.len(), 0);
-    assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![]);
-}
-
-#[test]
-fn drain_filter_zst() {
-    let mut list: LinkedList<_> = [(), (), (), (), ()].into_iter().collect();
-    let initial_len = list.len();
-    let mut count = 0;
-
-    {
-        let mut iter = list.drain_filter(|_| true);
-        assert_eq!(iter.size_hint(), (0, Some(initial_len)));
-        while let Some(_) = iter.next() {
-            count += 1;
-            assert_eq!(iter.size_hint(), (0, Some(initial_len - count)));
-        }
-        assert_eq!(iter.size_hint(), (0, Some(0)));
-        assert_eq!(iter.next(), None);
-        assert_eq!(iter.size_hint(), (0, Some(0)));
-    }
-
-    assert_eq!(count, initial_len);
-    assert_eq!(list.len(), 0);
-    assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![]);
-}
-
-#[test]
-fn drain_filter_false() {
-    let mut list: LinkedList<_> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect();
-
-    let initial_len = list.len();
-    let mut count = 0;
-
-    {
-        let mut iter = list.drain_filter(|_| false);
-        assert_eq!(iter.size_hint(), (0, Some(initial_len)));
-        for _ in iter.by_ref() {
-            count += 1;
-        }
-        assert_eq!(iter.size_hint(), (0, Some(0)));
-        assert_eq!(iter.next(), None);
-        assert_eq!(iter.size_hint(), (0, Some(0)));
-    }
-
-    assert_eq!(count, 0);
-    assert_eq!(list.len(), initial_len);
-    assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
-}
-
-#[test]
-fn drain_filter_true() {
-    let mut list: LinkedList<_> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect();
-
-    let initial_len = list.len();
-    let mut count = 0;
-
-    {
-        let mut iter = list.drain_filter(|_| true);
-        assert_eq!(iter.size_hint(), (0, Some(initial_len)));
-        while let Some(_) = iter.next() {
-            count += 1;
-            assert_eq!(iter.size_hint(), (0, Some(initial_len - count)));
-        }
-        assert_eq!(iter.size_hint(), (0, Some(0)));
-        assert_eq!(iter.next(), None);
-        assert_eq!(iter.size_hint(), (0, Some(0)));
-    }
-
-    assert_eq!(count, initial_len);
-    assert_eq!(list.len(), 0);
-    assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![]);
-}
-
-#[test]
-fn drain_filter_complex() {
-    {
-        //                [+xxx++++++xxxxx++++x+x++]
-        let mut list = [
-            1, 2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36, 37,
-            39,
-        ]
-        .into_iter()
-        .collect::<LinkedList<_>>();
-
-        let removed = list.drain_filter(|x| *x % 2 == 0).collect::<Vec<_>>();
-        assert_eq!(removed.len(), 10);
-        assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]);
-
-        assert_eq!(list.len(), 14);
-        assert_eq!(
-            list.into_iter().collect::<Vec<_>>(),
-            vec![1, 7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35, 37, 39]
-        );
-    }
-
-    {
-        // [xxx++++++xxxxx++++x+x++]
-        let mut list =
-            [2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36, 37, 39]
-                .into_iter()
-                .collect::<LinkedList<_>>();
-
-        let removed = list.drain_filter(|x| *x % 2 == 0).collect::<Vec<_>>();
-        assert_eq!(removed.len(), 10);
-        assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]);
-
-        assert_eq!(list.len(), 13);
-        assert_eq!(
-            list.into_iter().collect::<Vec<_>>(),
-            vec![7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35, 37, 39]
-        );
-    }
-
-    {
-        // [xxx++++++xxxxx++++x+x]
-        let mut list =
-            [2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36]
-                .into_iter()
-                .collect::<LinkedList<_>>();
-
-        let removed = list.drain_filter(|x| *x % 2 == 0).collect::<Vec<_>>();
-        assert_eq!(removed.len(), 10);
-        assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]);
-
-        assert_eq!(list.len(), 11);
-        assert_eq!(
-            list.into_iter().collect::<Vec<_>>(),
-            vec![7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35]
-        );
-    }
-
-    {
-        // [xxxxxxxxxx+++++++++++]
-        let mut list = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
-            .into_iter()
-            .collect::<LinkedList<_>>();
-
-        let removed = list.drain_filter(|x| *x % 2 == 0).collect::<Vec<_>>();
-        assert_eq!(removed.len(), 10);
-        assert_eq!(removed, vec![2, 4, 6, 8, 10, 12, 14, 16, 18, 20]);
-
-        assert_eq!(list.len(), 10);
-        assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![1, 3, 5, 7, 9, 11, 13, 15, 17, 19]);
-    }
-
-    {
-        // [+++++++++++xxxxxxxxxx]
-        let mut list = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
-            .into_iter()
-            .collect::<LinkedList<_>>();
-
-        let removed = list.drain_filter(|x| *x % 2 == 0).collect::<Vec<_>>();
-        assert_eq!(removed.len(), 10);
-        assert_eq!(removed, vec![2, 4, 6, 8, 10, 12, 14, 16, 18, 20]);
-
-        assert_eq!(list.len(), 10);
-        assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![1, 3, 5, 7, 9, 11, 13, 15, 17, 19]);
-    }
-}
-
-#[test]
-fn drain_filter_drop_panic_leak() {
-    static mut DROPS: i32 = 0;
-
-    struct D(bool);
-
-    impl Drop for D {
-        fn drop(&mut self) {
-            unsafe {
-                DROPS += 1;
-            }
-
-            if self.0 {
-                panic!("panic in `drop`");
-            }
-        }
-    }
-
-    let mut q = LinkedList::new();
-    q.push_back(D(false));
-    q.push_back(D(false));
-    q.push_back(D(false));
-    q.push_back(D(false));
-    q.push_back(D(false));
-    q.push_front(D(false));
-    q.push_front(D(true));
-    q.push_front(D(false));
-
-    catch_unwind(AssertUnwindSafe(|| drop(q.drain_filter(|_| true)))).ok();
-
-    assert_eq!(unsafe { DROPS }, 8);
-    assert!(q.is_empty());
-}
-
-#[test]
-fn drain_filter_pred_panic_leak() {
-    static mut DROPS: i32 = 0;
-
-    #[derive(Debug)]
-    struct D(u32);
-
-    impl Drop for D {
-        fn drop(&mut self) {
-            unsafe {
-                DROPS += 1;
-            }
-        }
-    }
-
-    let mut q = LinkedList::new();
-    q.push_back(D(3));
-    q.push_back(D(4));
-    q.push_back(D(5));
-    q.push_back(D(6));
-    q.push_back(D(7));
-    q.push_front(D(2));
-    q.push_front(D(1));
-    q.push_front(D(0));
-
-    catch_unwind(AssertUnwindSafe(|| {
-        drop(q.drain_filter(|item| if item.0 >= 2 { panic!() } else { true }))
-    }))
-    .ok();
-
-    assert_eq!(unsafe { DROPS }, 2); // 0 and 1
-    assert_eq!(q.len(), 6);
-}
-
-#[test]
-fn test_drop() {
-    static mut DROPS: i32 = 0;
-    struct Elem;
-    impl Drop for Elem {
-        fn drop(&mut self) {
-            unsafe {
-                DROPS += 1;
-            }
-        }
-    }
-
-    let mut ring = LinkedList::new();
-    ring.push_back(Elem);
-    ring.push_front(Elem);
-    ring.push_back(Elem);
-    ring.push_front(Elem);
-    drop(ring);
-
-    assert_eq!(unsafe { DROPS }, 4);
-}
-
-#[test]
-fn test_drop_with_pop() {
-    static mut DROPS: i32 = 0;
-    struct Elem;
-    impl Drop for Elem {
-        fn drop(&mut self) {
-            unsafe {
-                DROPS += 1;
-            }
-        }
-    }
-
-    let mut ring = LinkedList::new();
-    ring.push_back(Elem);
-    ring.push_front(Elem);
-    ring.push_back(Elem);
-    ring.push_front(Elem);
-
-    drop(ring.pop_back());
-    drop(ring.pop_front());
-    assert_eq!(unsafe { DROPS }, 2);
-
-    drop(ring);
-    assert_eq!(unsafe { DROPS }, 4);
-}
-
-#[test]
-fn test_drop_clear() {
-    static mut DROPS: i32 = 0;
-    struct Elem;
-    impl Drop for Elem {
-        fn drop(&mut self) {
-            unsafe {
-                DROPS += 1;
-            }
-        }
-    }
-
-    let mut ring = LinkedList::new();
-    ring.push_back(Elem);
-    ring.push_front(Elem);
-    ring.push_back(Elem);
-    ring.push_front(Elem);
-    ring.clear();
-    assert_eq!(unsafe { DROPS }, 4);
-
-    drop(ring);
-    assert_eq!(unsafe { DROPS }, 4);
-}
-
-#[test]
-fn test_drop_panic() {
-    static mut DROPS: i32 = 0;
-
-    struct D(bool);
-
-    impl Drop for D {
-        fn drop(&mut self) {
-            unsafe {
-                DROPS += 1;
-            }
-
-            if self.0 {
-                panic!("panic in `drop`");
-            }
-        }
-    }
-
-    let mut q = LinkedList::new();
-    q.push_back(D(false));
-    q.push_back(D(false));
-    q.push_back(D(false));
-    q.push_back(D(false));
-    q.push_back(D(false));
-    q.push_front(D(false));
-    q.push_front(D(false));
-    q.push_front(D(true));
-
-    catch_unwind(move || drop(q)).ok();
-
-    assert_eq!(unsafe { DROPS }, 8);
-}
diff --git a/library/core/src/internal_macros.rs b/library/core/src/internal_macros.rs
index 417ed51c6b6..7ef78e0b48a 100644
--- a/library/core/src/internal_macros.rs
+++ b/library/core/src/internal_macros.rs
@@ -1,10 +1,6 @@
 // implements the unary operator "op &T"
 // based on "op T" where T is expected to be `Copy`able
 macro_rules! forward_ref_unop {
-    (impl $imp:ident, $method:ident for $t:ty) => {
-        forward_ref_unop!(impl $imp, $method for $t,
-                #[stable(feature = "rust1", since = "1.0.0")]);
-    };
     (impl const $imp:ident, $method:ident for $t:ty) => {
         forward_ref_unop!(impl const $imp, $method for $t,
                 #[stable(feature = "rust1", since = "1.0.0")]);
@@ -38,10 +34,6 @@ macro_rules! forward_ref_unop {
 // implements binary operators "&T op U", "T op &U", "&T op &U"
 // based on "T op U" where T and U are expected to be `Copy`able
 macro_rules! forward_ref_binop {
-    (impl $imp:ident, $method:ident for $t:ty, $u:ty) => {
-        forward_ref_binop!(impl $imp, $method for $t, $u,
-                #[stable(feature = "rust1", since = "1.0.0")]);
-    };
     (impl const $imp:ident, $method:ident for $t:ty, $u:ty) => {
         forward_ref_binop!(impl const $imp, $method for $t, $u,
                 #[stable(feature = "rust1", since = "1.0.0")]);
@@ -230,22 +222,6 @@ macro_rules! cfg_if {
         }
     };
 
-    // match if/else chains lacking a final `else`
-    (
-        if #[cfg( $i_meta:meta )] { $( $i_tokens:tt )* }
-        $(
-            else if #[cfg( $e_meta:meta )] { $( $e_tokens:tt )* }
-        )*
-    ) => {
-        cfg_if! {
-            @__items () ;
-            (( $i_meta ) ( $( $i_tokens )* )) ,
-            $(
-                (( $e_meta ) ( $( $e_tokens )* )) ,
-            )*
-        }
-    };
-
     // Internal and recursive macro to emit all the items
     //
     // Collects all the previous cfgs in a list at the beginning, so they can be
diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs
index 8e02ca84317..bddbe2b9b0d 100644
--- a/library/core/src/intrinsics.rs
+++ b/library/core/src/intrinsics.rs
@@ -1,7 +1,7 @@
 //! Compiler intrinsics.
 //!
-//! The corresponding definitions are in `compiler/rustc_codegen_llvm/src/intrinsic.rs`.
-//! The corresponding const implementations are in `compiler/rustc_mir/src/interpret/intrinsics.rs`
+//! The corresponding definitions are in <https://github.com/rust-lang/rust/blob/master/compiler/rustc_codegen_llvm/src/intrinsic.rs>.
+//! The corresponding const implementations are in <https://github.com/rust-lang/rust/blob/master/compiler/rustc_const_eval/src/interpret/intrinsics.rs>.
 //!
 //! # Const intrinsics
 //!
@@ -10,8 +10,8 @@
 //!
 //! In order to make an intrinsic usable at compile-time, one needs to copy the implementation
 //! from <https://github.com/rust-lang/miri/blob/master/src/shims/intrinsics.rs> to
-//! `compiler/rustc_mir/src/interpret/intrinsics.rs` and add a
-//! `#[rustc_const_unstable(feature = "foo", issue = "01234")]` to the intrinsic.
+//! <https://github.com/rust-lang/rust/blob/master/compiler/rustc_const_eval/src/interpret/intrinsics.rs> and add a
+//! `#[rustc_const_unstable(feature = "const_such_and_such", issue = "01234")]` to the intrinsic declaration.
 //!
 //! If an intrinsic is supposed to be used from a `const fn` with a `rustc_const_stable` attribute,
 //! the intrinsic's attribute must be `rustc_const_stable`, too. Such a change should not be done
diff --git a/library/core/src/iter/sources/repeat_with.rs b/library/core/src/iter/sources/repeat_with.rs
index 44bc6890c55..6f62662d880 100644
--- a/library/core/src/iter/sources/repeat_with.rs
+++ b/library/core/src/iter/sources/repeat_with.rs
@@ -27,7 +27,7 @@ use crate::iter::{FusedIterator, TrustedLen};
 /// use std::iter;
 ///
 /// // let's assume we have some value of a type that is not `Clone`
-/// // or which don't want to have in memory just yet because it is expensive:
+/// // or which we don't want to have in memory just yet because it is expensive:
 /// #[derive(PartialEq, Debug)]
 /// struct Expensive;
 ///
diff --git a/library/core/src/iter/traits/collect.rs b/library/core/src/iter/traits/collect.rs
index 7b75ab96ee7..12ca508bed2 100644
--- a/library/core/src/iter/traits/collect.rs
+++ b/library/core/src/iter/traits/collect.rs
@@ -96,30 +96,24 @@
 #[rustc_on_unimplemented(
     on(
         _Self = "[{A}]",
-        message = "a value of type `{Self}` cannot be built since `{Self}` has no definite size",
+        message = "a slice of type `{Self}` cannot be built since `{Self}` has no definite size",
         label = "try explicitly collecting into a `Vec<{A}>`",
     ),
     on(
-        all(
-            A = "{integer}",
-            any(
-                _Self = "[i8]",
-                _Self = "[i16]",
-                _Self = "[i32]",
-                _Self = "[i64]",
-                _Self = "[i128]",
-                _Self = "[isize]",
-                _Self = "[u8]",
-                _Self = "[u16]",
-                _Self = "[u32]",
-                _Self = "[u64]",
-                _Self = "[u128]",
-                _Self = "[usize]"
-            )
-        ),
-        message = "a value of type `{Self}` cannot be built since `{Self}` has no definite size",
+        all(A = "{integer}", any(_Self = "[{integral}]",)),
+        message = "a slice of type `{Self}` cannot be built since `{Self}` has no definite size",
         label = "try explicitly collecting into a `Vec<{A}>`",
     ),
+    on(
+        _Self = "[{A}; _]",
+        message = "an array of type `{Self}` cannot be built directly from an iterator",
+        label = "try collecting into a `Vec<{A}>`, then using `.try_into()`",
+    ),
+    on(
+        all(A = "{integer}", any(_Self = "[{integral}; _]",)),
+        message = "an array of type `{Self}` cannot be built directly from an iterator",
+        label = "try collecting into a `Vec<{A}>`, then using `.try_into()`",
+    ),
     message = "a value of type `{Self}` cannot be built from an iterator \
                over elements of type `{A}`",
     label = "value of type `{Self}` cannot be built from `std::iter::Iterator<Item={A}>`"
@@ -267,8 +261,9 @@ pub trait IntoIterator {
     fn into_iter(self) -> Self::IntoIter;
 }
 
+#[rustc_const_unstable(feature = "const_intoiterator_identity", issue = "90603")]
 #[stable(feature = "rust1", since = "1.0.0")]
-impl<I: Iterator> IntoIterator for I {
+impl<I: ~const Iterator> const IntoIterator for I {
     type Item = I::Item;
     type IntoIter = I;
 
diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs
index 83f33ca007a..d00056f0c32 100644
--- a/library/core/src/macros/mod.rs
+++ b/library/core/src/macros/mod.rs
@@ -640,6 +640,8 @@ macro_rules! unreachable {
 ///
 /// Like `panic!`, this macro has a second form for displaying custom values.
 ///
+/// [`todo!`]: crate::todo
+///
 /// # Examples
 ///
 /// Say we have a trait `Foo`:
diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs
index a983d0872bc..e1a46086af0 100644
--- a/library/core/src/num/f32.rs
+++ b/library/core/src/num/f32.rs
@@ -449,7 +449,8 @@ impl f32 {
     #[inline]
     #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
     pub(crate) const fn abs_private(self) -> f32 {
-        f32::from_bits(self.to_bits() & 0x7fff_ffff)
+        // SAFETY: This transmutation is fine. Probably. For the reasons std is using it.
+        unsafe { mem::transmute::<u32, f32>(mem::transmute::<f32, u32>(self) & 0x7fff_ffff) }
     }
 
     /// Returns `true` if this value is positive infinity or negative infinity, and
@@ -472,7 +473,10 @@ impl f32 {
     #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
     #[inline]
     pub const fn is_infinite(self) -> bool {
-        self.abs_private() == Self::INFINITY
+        // Getting clever with transmutation can result in incorrect answers on some FPUs
+        // FIXME: alter the Rust <-> Rust calling convention to prevent this problem.
+        // See https://github.com/rust-lang/rust/issues/72327
+        (self == f32::INFINITY) | (self == f32::NEG_INFINITY)
     }
 
     /// Returns `true` if this number is neither infinite nor `NaN`.
@@ -568,15 +572,76 @@ impl f32 {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
     pub const fn classify(self) -> FpCategory {
+        // A previous implementation tried to only use bitmask-based checks,
+        // using f32::to_bits to transmute the float to its bit repr and match on that.
+        // Unfortunately, floating point numbers can be much worse than that.
+        // This also needs to not result in recursive evaluations of f64::to_bits.
+        //
+        // On some processors, in some cases, LLVM will "helpfully" lower floating point ops,
+        // in spite of a request for them using f32 and f64, to things like x87 operations.
+        // These have an f64's mantissa, but can have a larger than normal exponent.
+        // FIXME(jubilee): Using x87 operations is never necessary in order to function
+        // on x86 processors for Rust-to-Rust calls, so this issue should not happen.
+        // Code generation should be adjusted to use non-C calling conventions, avoiding this.
+        //
+        if self.is_infinite() {
+            // Thus, a value may compare unequal to infinity, despite having a "full" exponent mask.
+            FpCategory::Infinite
+        } else if self.is_nan() {
+            // And it may not be NaN, as it can simply be an "overextended" finite value.
+            FpCategory::Nan
+        } else {
+            // However, std can't simply compare to zero to check for zero, either,
+            // as correctness requires avoiding equality tests that may be Subnormal == -0.0
+            // because it may be wrong under "denormals are zero" and "flush to zero" modes.
+            // Most of std's targets don't use those, but they are used for thumbv7neon.
+            // So, this does use bitpattern matching for the rest.
+
+            // SAFETY: f32 to u32 is fine. Usually.
+            // If classify has gotten this far, the value is definitely in one of these categories.
+            unsafe { f32::partial_classify(self) }
+        }
+    }
+
+    // This doesn't actually return a right answer for NaN on purpose,
+    // seeing as how it cannot correctly discern between a floating point NaN,
+    // and some normal floating point numbers truncated from an x87 FPU.
+    // FIXME(jubilee): This probably could at least answer things correctly for Infinity,
+    // like the f64 version does, but I need to run more checks on how things go on x86.
+    // I fear losing mantissa data that would have answered that differently.
+    //
+    // # Safety
+    // This requires making sure you call this function for values it answers correctly on,
+    // otherwise it returns a wrong answer. This is not important for memory safety per se,
+    // but getting floats correct is important for not accidentally leaking const eval
+    // runtime-deviating logic which may or may not be acceptable.
+    #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
+    const unsafe fn partial_classify(self) -> FpCategory {
         const EXP_MASK: u32 = 0x7f800000;
         const MAN_MASK: u32 = 0x007fffff;
 
-        let bits = self.to_bits();
-        match (bits & MAN_MASK, bits & EXP_MASK) {
+        // SAFETY: The caller is not asking questions for which this will tell lies.
+        let b = unsafe { mem::transmute::<f32, u32>(self) };
+        match (b & MAN_MASK, b & EXP_MASK) {
             (0, 0) => FpCategory::Zero,
             (_, 0) => FpCategory::Subnormal,
+            _ => FpCategory::Normal,
+        }
+    }
+
+    // This operates on bits, and only bits, so it can ignore concerns about weird FPUs.
+    // FIXME(jubilee): In a just world, this would be the entire impl for classify,
+    // plus a transmute. We do not live in a just world, but we can make it more so.
+    #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
+    const fn classify_bits(b: u32) -> FpCategory {
+        const EXP_MASK: u32 = 0x7f800000;
+        const MAN_MASK: u32 = 0x007fffff;
+
+        match (b & MAN_MASK, b & EXP_MASK) {
             (0, EXP_MASK) => FpCategory::Infinite,
             (_, EXP_MASK) => FpCategory::Nan,
+            (0, 0) => FpCategory::Zero,
+            (_, 0) => FpCategory::Subnormal,
             _ => FpCategory::Normal,
         }
     }
@@ -616,7 +681,8 @@ impl f32 {
     pub const fn is_sign_negative(self) -> bool {
         // IEEE754 says: isSignMinus(x) is true if and only if x has negative sign. isSignMinus
         // applies to zeros and NaNs as well.
-        self.to_bits() & 0x8000_0000 != 0
+        // SAFETY: This is just transmuting to get the sign bit, it's fine.
+        unsafe { mem::transmute::<f32, u32>(self) & 0x8000_0000 != 0 }
     }
 
     /// Takes the reciprocal (inverse) of a number, `1/x`.
@@ -831,8 +897,49 @@ impl f32 {
     #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
     #[inline]
     pub const fn to_bits(self) -> u32 {
-        // SAFETY: `u32` is a plain old datatype so we can always transmute to it
-        unsafe { mem::transmute(self) }
+        // SAFETY: `u32` is a plain old datatype so we can always transmute to it.
+        // ...sorta.
+        //
+        // It turns out that at runtime, it is possible for a floating point number
+        // to be subject to a floating point mode that alters nonzero subnormal numbers
+        // to zero on reads and writes, aka "denormals are zero" and "flush to zero".
+        // This is not a problem per se, but at least one tier2 platform for Rust
+        // actually exhibits this behavior by default.
+        //
+        // In addition, on x86 targets with SSE or SSE2 disabled and the x87 FPU enabled,
+        // i.e. not soft-float, the way Rust does parameter passing can actually alter
+        // a number that is "not infinity" to have the same exponent as infinity,
+        // in a slightly unpredictable manner.
+        //
+        // And, of course evaluating to a NaN value is fairly nondeterministic.
+        // More precisely: when NaN should be returned is knowable, but which NaN?
+        // So far that's defined by a combination of LLVM and the CPU, not Rust.
+        // This function, however, allows observing the bitstring of a NaN,
+        // thus introspection on CTFE.
+        //
+        // In order to preserve, at least for the moment, const-to-runtime equivalence,
+        // we reject any of these possible situations from happening.
+        #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
+        const fn ct_f32_to_u32(ct: f32) -> u32 {
+            match ct.classify() {
+                FpCategory::Nan => {
+                    panic!("const-eval error: cannot use f32::to_bits on a NaN")
+                }
+                FpCategory::Subnormal => {
+                    panic!("const-eval error: cannot use f32::to_bits on a subnormal number")
+                }
+                FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => {
+                    // SAFETY: We have a normal floating point number. Now we transmute, i.e. do a bitcopy.
+                    unsafe { mem::transmute::<f32, u32>(ct) }
+                }
+            }
+        }
+        // SAFETY: `u32` is a plain old datatype so we can always... uh...
+        // ...look, just pretend you forgot what you just read.
+        // Stability concerns.
+        let rt_f32_to_u32 = |rt| unsafe { mem::transmute::<f32, u32>(rt) };
+        // SAFETY: We use internal implementations that either always work or fail at compile time.
+        unsafe { intrinsics::const_eval_select((self,), ct_f32_to_u32, rt_f32_to_u32) }
     }
 
     /// Raw transmutation from `u32`.
@@ -876,9 +983,51 @@ impl f32 {
     #[must_use]
     #[inline]
     pub const fn from_bits(v: u32) -> Self {
-        // SAFETY: `u32` is a plain old datatype so we can always transmute from it
         // It turns out the safety issues with sNaN were overblown! Hooray!
-        unsafe { mem::transmute(v) }
+        // SAFETY: `u32` is a plain old datatype so we can always transmute from it
+        // ...sorta.
+        //
+        // It turns out that at runtime, it is possible for a floating point number
+        // to be subject to floating point modes that alter nonzero subnormal numbers
+        // to zero on reads and writes, aka "denormals are zero" and "flush to zero".
+        // This is not a problem usually, but at least one tier2 platform for Rust
+        // actually exhibits this behavior by default: thumbv7neon
+        // aka "the Neon FPU in AArch32 state"
+        //
+        // In addition, on x86 targets with SSE or SSE2 disabled and the x87 FPU enabled,
+        // i.e. not soft-float, the way Rust does parameter passing can actually alter
+        // a number that is "not infinity" to have the same exponent as infinity,
+        // in a slightly unpredictable manner.
+        //
+        // And, of course evaluating to a NaN value is fairly nondeterministic.
+        // More precisely: when NaN should be returned is knowable, but which NaN?
+        // So far that's defined by a combination of LLVM and the CPU, not Rust.
+        // This function, however, allows observing the bitstring of a NaN,
+        // thus introspection on CTFE.
+        //
+        // In order to preserve, at least for the moment, const-to-runtime equivalence,
+        // reject any of these possible situations from happening.
+        #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
+        const fn ct_u32_to_f32(ct: u32) -> f32 {
+            match f32::classify_bits(ct) {
+                FpCategory::Subnormal => {
+                    panic!("const-eval error: cannot use f32::from_bits on a subnormal number")
+                }
+                FpCategory::Nan => {
+                    panic!("const-eval error: cannot use f32::from_bits on NaN")
+                }
+                FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => {
+                    // SAFETY: It's not a frumious number
+                    unsafe { mem::transmute::<u32, f32>(ct) }
+                }
+            }
+        }
+        // SAFETY: `u32` is a plain old datatype so we can always... uh...
+        // ...look, just pretend you forgot what you just read.
+        // Stability concerns.
+        let rt_u32_to_f32 = |rt| unsafe { mem::transmute::<u32, f32>(rt) };
+        // SAFETY: We use internal implementations that either always work or fail at compile time.
+        unsafe { intrinsics::const_eval_select((v,), ct_u32_to_f32, rt_u32_to_f32) }
     }
 
     /// Return the memory representation of this floating point number as a byte array in
diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs
index 05598e5fe7b..b07f201ca4a 100644
--- a/library/core/src/num/f64.rs
+++ b/library/core/src/num/f64.rs
@@ -448,7 +448,10 @@ impl f64 {
     #[inline]
     #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
     pub(crate) const fn abs_private(self) -> f64 {
-        f64::from_bits(self.to_bits() & 0x7fff_ffff_ffff_ffff)
+        // SAFETY: This transmutation is fine. Probably. For the reasons std is using it.
+        unsafe {
+            mem::transmute::<u64, f64>(mem::transmute::<f64, u64>(self) & 0x7fff_ffff_ffff_ffff)
+        }
     }
 
     /// Returns `true` if this value is positive infinity or negative infinity, and
@@ -471,7 +474,10 @@ impl f64 {
     #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
     #[inline]
     pub const fn is_infinite(self) -> bool {
-        self.abs_private() == Self::INFINITY
+        // Getting clever with transmutation can result in incorrect answers on some FPUs
+        // FIXME: alter the Rust <-> Rust calling convention to prevent this problem.
+        // See https://github.com/rust-lang/rust/issues/72327
+        (self == f64::INFINITY) | (self == f64::NEG_INFINITY)
     }
 
     /// Returns `true` if this number is neither infinite nor `NaN`.
@@ -567,15 +573,67 @@ impl f64 {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
     pub const fn classify(self) -> FpCategory {
+        // A previous implementation tried to only use bitmask-based checks,
+        // using f64::to_bits to transmute the float to its bit repr and match on that.
+        // Unfortunately, floating point numbers can be much worse than that.
+        // This also needs to not result in recursive evaluations of f64::to_bits.
+        //
+        // On some processors, in some cases, LLVM will "helpfully" lower floating point ops,
+        // in spite of a request for them using f32 and f64, to things like x87 operations.
+        // These have an f64's mantissa, but can have a larger than normal exponent.
+        // FIXME(jubilee): Using x87 operations is never necessary in order to function
+        // on x86 processors for Rust-to-Rust calls, so this issue should not happen.
+        // Code generation should be adjusted to use non-C calling conventions, avoiding this.
+        //
+        // Thus, a value may compare unequal to infinity, despite having a "full" exponent mask.
+        // And it may not be NaN, as it can simply be an "overextended" finite value.
+        if self.is_nan() {
+            FpCategory::Nan
+        } else {
+            // However, std can't simply compare to zero to check for zero, either,
+            // as correctness requires avoiding equality tests that may be Subnormal == -0.0
+            // because it may be wrong under "denormals are zero" and "flush to zero" modes.
+            // Most of std's targets don't use those, but they are used for thumbv7neon.
+            // So, this does use bitpattern matching for the rest.
+
+            // SAFETY: f64 to u64 is fine. Usually.
+            // If control flow has gotten this far, the value is definitely in one of the categories
+            // that f64::partial_classify can correctly analyze.
+            unsafe { f64::partial_classify(self) }
+        }
+    }
+
+    // This doesn't actually return a right answer for NaN on purpose,
+    // seeing as how it cannot correctly discern between a floating point NaN,
+    // and some normal floating point numbers truncated from an x87 FPU.
+    #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
+    const unsafe fn partial_classify(self) -> FpCategory {
         const EXP_MASK: u64 = 0x7ff0000000000000;
         const MAN_MASK: u64 = 0x000fffffffffffff;
 
-        let bits = self.to_bits();
-        match (bits & MAN_MASK, bits & EXP_MASK) {
+        // SAFETY: The caller is not asking questions for which this will tell lies.
+        let b = unsafe { mem::transmute::<f64, u64>(self) };
+        match (b & MAN_MASK, b & EXP_MASK) {
+            (0, EXP_MASK) => FpCategory::Infinite,
             (0, 0) => FpCategory::Zero,
             (_, 0) => FpCategory::Subnormal,
+            _ => FpCategory::Normal,
+        }
+    }
+
+    // This operates on bits, and only bits, so it can ignore concerns about weird FPUs.
+    // FIXME(jubilee): In a just world, this would be the entire impl for classify,
+    // plus a transmute. We do not live in a just world, but we can make it more so.
+    #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
+    const fn classify_bits(b: u64) -> FpCategory {
+        const EXP_MASK: u64 = 0x7ff0000000000000;
+        const MAN_MASK: u64 = 0x000fffffffffffff;
+
+        match (b & MAN_MASK, b & EXP_MASK) {
             (0, EXP_MASK) => FpCategory::Infinite,
             (_, EXP_MASK) => FpCategory::Nan,
+            (0, 0) => FpCategory::Zero,
+            (_, 0) => FpCategory::Subnormal,
             _ => FpCategory::Normal,
         }
     }
@@ -622,7 +680,10 @@ impl f64 {
     #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
     #[inline]
     pub const fn is_sign_negative(self) -> bool {
-        self.to_bits() & 0x8000_0000_0000_0000 != 0
+        // IEEE754 says: isSignMinus(x) is true if and only if x has negative sign. isSignMinus
+        // applies to zeros and NaNs as well.
+        // SAFETY: This is just transmuting to get the sign bit, it's fine.
+        unsafe { mem::transmute::<f64, u64>(self) & 0x8000_0000_0000_0000 != 0 }
     }
 
     #[must_use]
@@ -847,8 +908,31 @@ impl f64 {
     #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
     #[inline]
     pub const fn to_bits(self) -> u64 {
-        // SAFETY: `u64` is a plain old datatype so we can always transmute to it
-        unsafe { mem::transmute(self) }
+        // SAFETY: `u64` is a plain old datatype so we can always transmute to it.
+        // ...sorta.
+        //
+        // See the SAFETY comment in f64::from_bits for more.
+        #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
+        const fn ct_f64_to_u64(ct: f64) -> u64 {
+            match ct.classify() {
+                FpCategory::Nan => {
+                    panic!("const-eval error: cannot use f64::to_bits on a NaN")
+                }
+                FpCategory::Subnormal => {
+                    panic!("const-eval error: cannot use f64::to_bits on a subnormal number")
+                }
+                FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => {
+                    // SAFETY: We have a normal floating point number. Now we transmute, i.e. do a bitcopy.
+                    unsafe { mem::transmute::<f64, u64>(ct) }
+                }
+            }
+        }
+        // SAFETY: `u64` is a plain old datatype so we can always... uh...
+        // ...look, just pretend you forgot what you just read.
+        // Stability concerns.
+        let rt_f64_to_u64 = |rt| unsafe { mem::transmute::<f64, u64>(rt) };
+        // SAFETY: We use internal implementations that either always work or fail at compile time.
+        unsafe { intrinsics::const_eval_select((self,), ct_f64_to_u64, rt_f64_to_u64) }
     }
 
     /// Raw transmutation from `u64`.
@@ -892,9 +976,56 @@ impl f64 {
     #[must_use]
     #[inline]
     pub const fn from_bits(v: u64) -> Self {
-        // SAFETY: `u64` is a plain old datatype so we can always transmute from it
         // It turns out the safety issues with sNaN were overblown! Hooray!
-        unsafe { mem::transmute(v) }
+        // SAFETY: `u64` is a plain old datatype so we can always transmute from it
+        // ...sorta.
+        //
+        // It turns out that at runtime, it is possible for a floating point number
+        // to be subject to floating point modes that alter nonzero subnormal numbers
+        // to zero on reads and writes, aka "denormals are zero" and "flush to zero".
+        // This is not a problem usually, but at least one tier2 platform for Rust
+        // actually exhibits an FTZ behavior by default: thumbv7neon
+        // aka "the Neon FPU in AArch32 state"
+        //
+        // Even with this, not all instructions exhibit the FTZ behaviors on thumbv7neon,
+        // so this should load the same bits if LLVM emits the "correct" instructions,
+        // but LLVM sometimes makes interesting choices about float optimization,
+        // and other FPUs may do similar. Thus, it is wise to indulge luxuriously in caution.
+        //
+        // In addition, on x86 targets with SSE or SSE2 disabled and the x87 FPU enabled,
+        // i.e. not soft-float, the way Rust does parameter passing can actually alter
+        // a number that is "not infinity" to have the same exponent as infinity,
+        // in a slightly unpredictable manner.
+        //
+        // And, of course evaluating to a NaN value is fairly nondeterministic.
+        // More precisely: when NaN should be returned is knowable, but which NaN?
+        // So far that's defined by a combination of LLVM and the CPU, not Rust.
+        // This function, however, allows observing the bitstring of a NaN,
+        // thus introspection on CTFE.
+        //
+        // In order to preserve, at least for the moment, const-to-runtime equivalence,
+        // reject any of these possible situations from happening.
+        #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
+        const fn ct_u64_to_f64(ct: u64) -> f64 {
+            match f64::classify_bits(ct) {
+                FpCategory::Subnormal => {
+                    panic!("const-eval error: cannot use f64::from_bits on a subnormal number")
+                }
+                FpCategory::Nan => {
+                    panic!("const-eval error: cannot use f64::from_bits on NaN")
+                }
+                FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => {
+                    // SAFETY: It's not a frumious number
+                    unsafe { mem::transmute::<u64, f64>(ct) }
+                }
+            }
+        }
+        // SAFETY: `u64` is a plain old datatype so we can always... uh...
+        // ...look, just pretend you forgot what you just read.
+        // Stability concerns.
+        let rt_u64_to_f64 = |rt| unsafe { mem::transmute::<u64, f64>(rt) };
+        // SAFETY: We use internal implementations that either always work or fail at compile time.
+        unsafe { intrinsics::const_eval_select((v,), ct_u64_to_f64, rt_u64_to_f64) }
     }
 
     /// Return the memory representation of this floating point number as a byte array in
diff --git a/library/core/src/ops/mod.rs b/library/core/src/ops/mod.rs
index 9d1e7e81b0e..31c1a1d099d 100644
--- a/library/core/src/ops/mod.rs
+++ b/library/core/src/ops/mod.rs
@@ -187,6 +187,9 @@ pub use self::range::OneSidedRange;
 #[unstable(feature = "try_trait_v2", issue = "84277")]
 pub use self::try_trait::{FromResidual, Try};
 
+#[unstable(feature = "try_trait_v2_yeet", issue = "96374")]
+pub use self::try_trait::Yeet;
+
 #[unstable(feature = "try_trait_v2_residual", issue = "91285")]
 pub use self::try_trait::Residual;
 
diff --git a/library/core/src/ops/try_trait.rs b/library/core/src/ops/try_trait.rs
index ba369e7f3aa..3eaee958b69 100644
--- a/library/core/src/ops/try_trait.rs
+++ b/library/core/src/ops/try_trait.rs
@@ -330,6 +330,22 @@ pub trait FromResidual<R = <Self as Try>::Residual> {
     fn from_residual(residual: R) -> Self;
 }
 
+#[cfg(not(bootstrap))]
+#[unstable(
+    feature = "yeet_desugar_details",
+    issue = "none",
+    reason = "just here to simplify the desugaring; will never be stabilized"
+)]
+#[inline]
+#[track_caller] // because `Result::from_residual` has it
+#[lang = "from_yeet"]
+pub fn from_yeet<T, Y>(yeeted: Y) -> T
+where
+    T: FromResidual<Yeet<Y>>,
+{
+    FromResidual::from_residual(Yeet(yeeted))
+}
+
 /// Allows retrieving the canonical type implementing [`Try`] that has this type
 /// as its residual and allows it to hold an `O` as its output.
 ///
@@ -395,3 +411,9 @@ impl<T> FromResidual for NeverShortCircuit<T> {
 impl<T> Residual<T> for NeverShortCircuitResidual {
     type TryType = NeverShortCircuit<T>;
 }
+
+/// Implement `FromResidual<Yeet<T>>` on your type to enable
+/// `do yeet expr` syntax in functions returning your type.
+#[unstable(feature = "try_trait_v2_yeet", issue = "96374")]
+#[derive(Debug)]
+pub struct Yeet<T>(pub T);
diff --git a/library/core/src/option.rs b/library/core/src/option.rs
index 91e4708f6a6..f339b076dd7 100644
--- a/library/core/src/option.rs
+++ b/library/core/src/option.rs
@@ -2287,6 +2287,14 @@ impl<T> const ops::FromResidual for Option<T> {
     }
 }
 
+#[unstable(feature = "try_trait_v2_yeet", issue = "96374")]
+impl<T> ops::FromResidual<ops::Yeet<()>> for Option<T> {
+    #[inline]
+    fn from_residual(ops::Yeet(()): ops::Yeet<()>) -> Self {
+        None
+    }
+}
+
 #[unstable(feature = "try_trait_v2_residual", issue = "91285")]
 impl<T> ops::Residual<T> for Option<convert::Infallible> {
     type TryType = Option<T>;
diff --git a/library/core/src/panic/location.rs b/library/core/src/panic/location.rs
index 714e9b73c78..8eefd9ff20d 100644
--- a/library/core/src/panic/location.rs
+++ b/library/core/src/panic/location.rs
@@ -83,6 +83,7 @@ impl<'a> Location<'a> {
     #[stable(feature = "track_caller", since = "1.46.0")]
     #[rustc_const_unstable(feature = "const_caller_location", issue = "76156")]
     #[track_caller]
+    #[inline]
     pub const fn caller() -> &'static Location<'static> {
         crate::intrinsics::caller_location()
     }
@@ -122,6 +123,7 @@ impl<'a> Location<'a> {
     /// ```
     #[must_use]
     #[stable(feature = "panic_hooks", since = "1.10.0")]
+    #[inline]
     pub fn file(&self) -> &str {
         self.file
     }
@@ -145,6 +147,7 @@ impl<'a> Location<'a> {
     /// ```
     #[must_use]
     #[stable(feature = "panic_hooks", since = "1.10.0")]
+    #[inline]
     pub fn line(&self) -> u32 {
         self.line
     }
@@ -168,6 +171,7 @@ impl<'a> Location<'a> {
     /// ```
     #[must_use]
     #[stable(feature = "panic_col", since = "1.25.0")]
+    #[inline]
     pub fn column(&self) -> u32 {
         self.col
     }
diff --git a/library/core/src/panic/unwind_safe.rs b/library/core/src/panic/unwind_safe.rs
index 95be879e319..f2948aac3c2 100644
--- a/library/core/src/panic/unwind_safe.rs
+++ b/library/core/src/panic/unwind_safe.rs
@@ -279,6 +279,13 @@ impl<T: fmt::Debug> fmt::Debug for AssertUnwindSafe<T> {
     }
 }
 
+#[stable(feature = "assertunwindsafe_default", since = "1.62.0")]
+impl<T: Default> Default for AssertUnwindSafe<T> {
+    fn default() -> Self {
+        Self(Default::default())
+    }
+}
+
 #[stable(feature = "futures_api", since = "1.36.0")]
 impl<F: Future> Future for AssertUnwindSafe<F> {
     type Output = F::Output;
diff --git a/library/core/src/result.rs b/library/core/src/result.rs
index b2b132300a2..5e5f8a5ab95 100644
--- a/library/core/src/result.rs
+++ b/library/core/src/result.rs
@@ -2107,6 +2107,14 @@ impl<T, E, F: ~const From<E>> const ops::FromResidual<Result<convert::Infallible
     }
 }
 
+#[unstable(feature = "try_trait_v2_yeet", issue = "96374")]
+impl<T, E, F: From<E>> ops::FromResidual<ops::Yeet<E>> for Result<T, F> {
+    #[inline]
+    fn from_residual(ops::Yeet(e): ops::Yeet<E>) -> Self {
+        Err(From::from(e))
+    }
+}
+
 #[unstable(feature = "try_trait_v2_residual", issue = "91285")]
 impl<T, E> ops::Residual<T> for Result<convert::Infallible, E> {
     type TryType = Result<T, E>;
diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs
index 2a4030de00b..a226dea54a4 100644
--- a/library/core/src/slice/mod.rs
+++ b/library/core/src/slice/mod.rs
@@ -2139,6 +2139,12 @@ impl<T> [T] {
 
     /// Returns `true` if the slice contains an element with the given value.
     ///
+    /// This operation is *O*(*n*).
+    ///
+    /// Note that if you have a sorted slice, [`binary_search`] may be faster.
+    ///
+    /// [`binary_search`]: slice::binary_search
+    ///
     /// # Examples
     ///
     /// ```
@@ -2298,7 +2304,8 @@ impl<T> [T] {
         None
     }
 
-    /// Binary searches this sorted slice for a given element.
+    /// Binary searches this slice for a given element.
+    /// This behaves similary to [`contains`] if this slice is sorted.
     ///
     /// If the value is found then [`Result::Ok`] is returned, containing the
     /// index of the matching element. If there are multiple matches, then any
@@ -2310,6 +2317,7 @@ impl<T> [T] {
     ///
     /// See also [`binary_search_by`], [`binary_search_by_key`], and [`partition_point`].
     ///
+    /// [`contains`]: slice::contains
     /// [`binary_search_by`]: slice::binary_search_by
     /// [`binary_search_by_key`]: slice::binary_search_by_key
     /// [`partition_point`]: slice::partition_point
@@ -2349,7 +2357,8 @@ impl<T> [T] {
         self.binary_search_by(|p| p.cmp(x))
     }
 
-    /// Binary searches this sorted slice with a comparator function.
+    /// Binary searches this slice with a comparator function.
+    /// This behaves similarly to [`contains`] if this slice is sorted.
     ///
     /// The comparator function should implement an order consistent
     /// with the sort order of the underlying slice, returning an
@@ -2366,6 +2375,7 @@ impl<T> [T] {
     ///
     /// See also [`binary_search`], [`binary_search_by_key`], and [`partition_point`].
     ///
+    /// [`contains`]: slice::contains
     /// [`binary_search`]: slice::binary_search
     /// [`binary_search_by_key`]: slice::binary_search_by_key
     /// [`partition_point`]: slice::partition_point
@@ -2424,7 +2434,8 @@ impl<T> [T] {
         Err(left)
     }
 
-    /// Binary searches this sorted slice with a key extraction function.
+    /// Binary searches this slice with a key extraction function.
+    /// This behaves similarly to [`contains`] if this slice is sorted.
     ///
     /// Assumes that the slice is sorted by the key, for instance with
     /// [`sort_by_key`] using the same key extraction function.
@@ -2439,6 +2450,7 @@ impl<T> [T] {
     ///
     /// See also [`binary_search`], [`binary_search_by`], and [`partition_point`].
     ///
+    /// [`contains`]: slice::contains
     /// [`sort_by_key`]: slice::sort_by_key
     /// [`binary_search`]: slice::binary_search
     /// [`binary_search_by`]: slice::binary_search_by
diff --git a/library/core/tests/num/ops.rs b/library/core/tests/num/ops.rs
index 9979cc8fde4..ae8b938250e 100644
--- a/library/core/tests/num/ops.rs
+++ b/library/core/tests/num/ops.rs
@@ -43,18 +43,6 @@ macro_rules! impls_defined {
 }
 
 macro_rules! test_op {
-    ($fn_name:ident, $op:ident::$method:ident($lhs:literal, $rhs:literal), $result:literal, $($t:ty),+) => {
-        #[test]
-        fn $fn_name() {
-            impls_defined!($op, $method($lhs, $rhs), $result, $($t),+);
-        }
-    };
-    ($fn_name:ident, $op:ident::$method:ident(&mut $lhs:literal, $rhs:literal), $result:literal, $($t:ty),+) => {
-        #[test]
-        fn $fn_name() {
-            impls_defined!($op, $method(&mut $lhs, $rhs), $result, $($t),+);
-        }
-    };
     ($fn_name:ident, $op:ident::$method:ident($lhs:literal), $result:literal, $($t:ty),+) => {
         #[test]
         fn $fn_name() {
diff --git a/library/proc_macro/Cargo.toml b/library/proc_macro/Cargo.toml
index db5e2e4e245..e54a50aa15c 100644
--- a/library/proc_macro/Cargo.toml
+++ b/library/proc_macro/Cargo.toml
@@ -5,3 +5,7 @@ edition = "2021"
 
 [dependencies]
 std = { path = "../std" }
+# Workaround: when documenting this crate rustdoc will try to load crate named
+# `core` when resolving doc links. Without this line a different `core` will be
+# loaded from sysroot causing duplicate lang items and other similar errors.
+core = { path = "../core" }
diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs
index 00b8bb1eb26..f1c5eaad868 100644
--- a/library/proc_macro/src/lib.rs
+++ b/library/proc_macro/src/lib.rs
@@ -704,10 +704,10 @@ pub enum Delimiter {
     #[stable(feature = "proc_macro_lib2", since = "1.29.0")]
     Bracket,
     /// `Ø ... Ø`
-    /// An implicit delimiter, that may, for example, appear around tokens coming from a
+    /// An invisible delimiter, that may, for example, appear around tokens coming from a
     /// "macro variable" `$var`. It is important to preserve operator priorities in cases like
     /// `$var * 3` where `$var` is `1 + 2`.
-    /// Implicit delimiters might not survive roundtrip of a token stream through a string.
+    /// Invisible delimiters might not survive roundtrip of a token stream through a string.
     #[stable(feature = "proc_macro_lib2", since = "1.29.0")]
     None,
 }
diff --git a/library/proc_macro/src/quote.rs b/library/proc_macro/src/quote.rs
index 1fd59889709..04fa696d5e6 100644
--- a/library/proc_macro/src/quote.rs
+++ b/library/proc_macro/src/quote.rs
@@ -12,7 +12,6 @@ macro_rules! quote_tt {
     ({$($t:tt)*}) => { Group::new(Delimiter::Brace, quote!($($t)*)) };
     (,) => { Punct::new(',', Spacing::Alone) };
     (.) => { Punct::new('.', Spacing::Alone) };
-    (:) => { Punct::new(':', Spacing::Alone) };
     (;) => { Punct::new(';', Spacing::Alone) };
     (!) => { Punct::new('!', Spacing::Alone) };
     (<) => { Punct::new('<', Spacing::Alone) };
diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs
index 6b63191eb58..0c638192264 100644
--- a/library/std/src/collections/hash/map.rs
+++ b/library/std/src/collections/hash/map.rs
@@ -136,7 +136,7 @@ use crate::sys;
 /// ]);
 /// ```
 ///
-/// `HashMap` implements an [`Entry API`](#method.entry), which allows
+/// `HashMap` implements an [`Entry` API](#method.entry), which allows
 /// for complex methods of getting, setting, updating and removing keys and
 /// their values:
 ///
diff --git a/library/std/src/ffi/mod.rs b/library/std/src/ffi/mod.rs
index 0141a2bccdf..10983a33232 100644
--- a/library/std/src/ffi/mod.rs
+++ b/library/std/src/ffi/mod.rs
@@ -171,15 +171,6 @@ pub use self::os_str::{OsStr, OsString};
 #[stable(feature = "core_c_void", since = "1.30.0")]
 pub use core::ffi::c_void;
 
-#[unstable(feature = "core_ffi_c", issue = "94501")]
-pub use core::ffi::{
-    c_char, c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint,
-    c_ulong, c_ulonglong, c_ushort,
-};
-
-#[unstable(feature = "c_size_t", issue = "88345")]
-pub use core::ffi::{c_ptrdiff_t, c_size_t, c_ssize_t};
-
 #[unstable(
     feature = "c_variadic",
     reason = "the `c_variadic` feature has not been properly tested on \
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index da7753216d0..97c30c42282 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -240,6 +240,7 @@
 #![feature(exhaustive_patterns)]
 #![feature(intra_doc_pointers)]
 #![feature(lang_items)]
+#![feature(let_chains)]
 #![feature(linkage)]
 #![feature(min_specialization)]
 #![feature(must_not_suspend)]
@@ -306,7 +307,6 @@
 // Only for re-exporting:
 #![feature(assert_matches)]
 #![feature(async_iterator)]
-#![feature(c_size_t)]
 #![feature(c_variadic)]
 #![feature(cfg_accessible)]
 #![feature(cfg_eval)]
diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs
index c597fb5df45..e512c0d81a0 100644
--- a/library/std/src/macros.rs
+++ b/library/std/src/macros.rs
@@ -31,6 +31,8 @@ macro_rules! panic {
 /// [`eprint!`] instead to print error and progress messages.
 ///
 /// [flush]: crate::io::Write::flush
+/// [`println!`]: crate::println
+/// [`eprint!`]: crate::eprint
 ///
 /// # Panics
 ///
@@ -77,6 +79,7 @@ macro_rules! print {
 /// [`eprintln!`] instead to print error and progress messages.
 ///
 /// [`std::fmt`]: crate::fmt
+/// [`eprintln!`]: crate::eprintln
 ///
 /// # Panics
 ///
@@ -99,9 +102,9 @@ macro_rules! println {
     () => {
         $crate::print!("\n")
     };
-    ($($arg:tt)*) => {
-        $crate::io::_print($crate::format_args_nl!($($arg)*))
-    };
+    ($($arg:tt)*) => {{
+        $crate::io::_print($crate::format_args_nl!($($arg)*));
+    }};
 }
 
 /// Prints to the standard error.
@@ -146,6 +149,7 @@ macro_rules! eprint {
 ///
 /// [`io::stderr`]: crate::io::stderr
 /// [`io::stdout`]: crate::io::stdout
+/// [`println!`]: crate::println
 ///
 /// # Panics
 ///
@@ -164,9 +168,9 @@ macro_rules! eprintln {
     () => {
         $crate::eprint!("\n")
     };
-    ($($arg:tt)*) => {
-        $crate::io::_eprint($crate::format_args_nl!($($arg)*))
-    };
+    ($($arg:tt)*) => {{
+        $crate::io::_eprint($crate::format_args_nl!($($arg)*));
+    }};
 }
 
 /// Prints and returns the value of a given expression for quick and dirty
diff --git a/library/std/src/net/parser.rs b/library/std/src/net/parser.rs
index 4e16a55edec..fb292ed29a1 100644
--- a/library/std/src/net/parser.rs
+++ b/library/std/src/net/parser.rs
@@ -59,12 +59,12 @@ impl<'a> Parser<'a> {
 
     /// Run a parser, but fail if the entire input wasn't consumed.
     /// Doesn't run atomically.
-    fn parse_with<T, F>(&mut self, inner: F) -> Result<T, AddrParseError>
+    fn parse_with<T, F>(&mut self, inner: F, kind: AddrKind) -> Result<T, AddrParseError>
     where
         F: FnOnce(&mut Parser<'_>) -> Option<T>,
     {
         let result = inner(self);
-        if self.state.is_empty() { result } else { None }.ok_or(AddrParseError(()))
+        if self.state.is_empty() { result } else { None }.ok_or(AddrParseError(kind))
     }
 
     /// Peek the next character from the input
@@ -278,7 +278,7 @@ impl<'a> Parser<'a> {
 impl FromStr for IpAddr {
     type Err = AddrParseError;
     fn from_str(s: &str) -> Result<IpAddr, AddrParseError> {
-        Parser::new(s).parse_with(|p| p.read_ip_addr())
+        Parser::new(s).parse_with(|p| p.read_ip_addr(), AddrKind::Ip)
     }
 }
 
@@ -288,9 +288,9 @@ impl FromStr for Ipv4Addr {
     fn from_str(s: &str) -> Result<Ipv4Addr, AddrParseError> {
         // don't try to parse if too long
         if s.len() > 15 {
-            Err(AddrParseError(()))
+            Err(AddrParseError(AddrKind::Ipv4))
         } else {
-            Parser::new(s).parse_with(|p| p.read_ipv4_addr())
+            Parser::new(s).parse_with(|p| p.read_ipv4_addr(), AddrKind::Ipv4)
         }
     }
 }
@@ -299,7 +299,7 @@ impl FromStr for Ipv4Addr {
 impl FromStr for Ipv6Addr {
     type Err = AddrParseError;
     fn from_str(s: &str) -> Result<Ipv6Addr, AddrParseError> {
-        Parser::new(s).parse_with(|p| p.read_ipv6_addr())
+        Parser::new(s).parse_with(|p| p.read_ipv6_addr(), AddrKind::Ipv6)
     }
 }
 
@@ -307,7 +307,7 @@ impl FromStr for Ipv6Addr {
 impl FromStr for SocketAddrV4 {
     type Err = AddrParseError;
     fn from_str(s: &str) -> Result<SocketAddrV4, AddrParseError> {
-        Parser::new(s).parse_with(|p| p.read_socket_addr_v4())
+        Parser::new(s).parse_with(|p| p.read_socket_addr_v4(), AddrKind::SocketV4)
     }
 }
 
@@ -315,7 +315,7 @@ impl FromStr for SocketAddrV4 {
 impl FromStr for SocketAddrV6 {
     type Err = AddrParseError;
     fn from_str(s: &str) -> Result<SocketAddrV6, AddrParseError> {
-        Parser::new(s).parse_with(|p| p.read_socket_addr_v6())
+        Parser::new(s).parse_with(|p| p.read_socket_addr_v6(), AddrKind::SocketV6)
     }
 }
 
@@ -323,10 +323,20 @@ impl FromStr for SocketAddrV6 {
 impl FromStr for SocketAddr {
     type Err = AddrParseError;
     fn from_str(s: &str) -> Result<SocketAddr, AddrParseError> {
-        Parser::new(s).parse_with(|p| p.read_socket_addr())
+        Parser::new(s).parse_with(|p| p.read_socket_addr(), AddrKind::Socket)
     }
 }
 
+#[derive(Debug, Clone, PartialEq, Eq)]
+enum AddrKind {
+    Ip,
+    Ipv4,
+    Ipv6,
+    Socket,
+    SocketV4,
+    SocketV6,
+}
+
 /// An error which can be returned when parsing an IP address or a socket address.
 ///
 /// This error is used as the error type for the [`FromStr`] implementation for
@@ -353,7 +363,7 @@ impl FromStr for SocketAddr {
 /// ```
 #[stable(feature = "rust1", since = "1.0.0")]
 #[derive(Debug, Clone, PartialEq, Eq)]
-pub struct AddrParseError(());
+pub struct AddrParseError(AddrKind);
 
 #[stable(feature = "addr_parse_error_error", since = "1.4.0")]
 impl fmt::Display for AddrParseError {
@@ -367,6 +377,13 @@ impl fmt::Display for AddrParseError {
 impl Error for AddrParseError {
     #[allow(deprecated)]
     fn description(&self) -> &str {
-        "invalid IP address syntax"
+        match self.0 {
+            AddrKind::Ip => "invalid IP address syntax",
+            AddrKind::Ipv4 => "invalid IPv4 address syntax",
+            AddrKind::Ipv6 => "invalid IPv6 address syntax",
+            AddrKind::Socket => "invalid socket address syntax",
+            AddrKind::SocketV4 => "invalid IPv4 socket address syntax",
+            AddrKind::SocketV6 => "invalid IPv6 socket address syntax",
+        }
     }
 }
diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs
index d95bc9b15c9..9a6778c0e86 100644
--- a/library/std/src/os/unix/process.rs
+++ b/library/std/src/os/unix/process.rs
@@ -24,8 +24,8 @@ pub trait CommandExt: Sealed {
     #[stable(feature = "rust1", since = "1.0.0")]
     fn uid(
         &mut self,
-        #[cfg(not(target_os = "vxworks"))] id: u32,
-        #[cfg(target_os = "vxworks")] id: u16,
+        #[cfg(not(any(target_os = "vxworks", target_os = "espidf")))] id: u32,
+        #[cfg(any(target_os = "vxworks", target_os = "espidf"))] id: u16,
     ) -> &mut process::Command;
 
     /// Similar to `uid`, but sets the group ID of the child process. This has
@@ -33,8 +33,8 @@ pub trait CommandExt: Sealed {
     #[stable(feature = "rust1", since = "1.0.0")]
     fn gid(
         &mut self,
-        #[cfg(not(target_os = "vxworks"))] id: u32,
-        #[cfg(target_os = "vxworks")] id: u16,
+        #[cfg(not(any(target_os = "vxworks", target_os = "espidf")))] id: u32,
+        #[cfg(any(target_os = "vxworks", target_os = "espidf"))] id: u16,
     ) -> &mut process::Command;
 
     /// Sets the supplementary group IDs for the calling process. Translates to
@@ -42,8 +42,8 @@ pub trait CommandExt: Sealed {
     #[unstable(feature = "setgroups", issue = "90747")]
     fn groups(
         &mut self,
-        #[cfg(not(target_os = "vxworks"))] groups: &[u32],
-        #[cfg(target_os = "vxworks")] groups: &[u16],
+        #[cfg(not(any(target_os = "vxworks", target_os = "espidf")))] groups: &[u32],
+        #[cfg(any(target_os = "vxworks", target_os = "espidf"))] groups: &[u16],
     ) -> &mut process::Command;
 
     /// Schedules a closure to be run just before the `exec` function is
@@ -160,8 +160,8 @@ pub trait CommandExt: Sealed {
 impl CommandExt for process::Command {
     fn uid(
         &mut self,
-        #[cfg(not(target_os = "vxworks"))] id: u32,
-        #[cfg(target_os = "vxworks")] id: u16,
+        #[cfg(not(any(target_os = "vxworks", target_os = "espidf")))] id: u32,
+        #[cfg(any(target_os = "vxworks", target_os = "espidf"))] id: u16,
     ) -> &mut process::Command {
         self.as_inner_mut().uid(id);
         self
@@ -169,8 +169,8 @@ impl CommandExt for process::Command {
 
     fn gid(
         &mut self,
-        #[cfg(not(target_os = "vxworks"))] id: u32,
-        #[cfg(target_os = "vxworks")] id: u16,
+        #[cfg(not(any(target_os = "vxworks", target_os = "espidf")))] id: u32,
+        #[cfg(any(target_os = "vxworks", target_os = "espidf"))] id: u16,
     ) -> &mut process::Command {
         self.as_inner_mut().gid(id);
         self
@@ -178,8 +178,8 @@ impl CommandExt for process::Command {
 
     fn groups(
         &mut self,
-        #[cfg(not(target_os = "vxworks"))] groups: &[u32],
-        #[cfg(target_os = "vxworks")] groups: &[u16],
+        #[cfg(not(any(target_os = "vxworks", target_os = "espidf")))] groups: &[u32],
+        #[cfg(any(target_os = "vxworks", target_os = "espidf"))] groups: &[u16],
     ) -> &mut process::Command {
         self.as_inner_mut().groups(groups);
         self
diff --git a/library/std/src/os/windows/io/handle.rs b/library/std/src/os/windows/io/handle.rs
index ee30cc8be6b..f3d7be3f95c 100644
--- a/library/std/src/os/windows/io/handle.rs
+++ b/library/std/src/os/windows/io/handle.rs
@@ -76,7 +76,7 @@ pub struct OwnedHandle {
 /// `NULL`. This ensures that such FFI calls cannot start using the handle without
 /// checking for `NULL` first.
 ///
-/// This type concerns any value other than `NULL` to be valid, including `INVALID_HANDLE_VALUE`.
+/// This type considers any value other than `NULL` to be valid, including `INVALID_HANDLE_VALUE`.
 /// This is because APIs that use `NULL` as their sentry value don't treat `INVALID_HANDLE_VALUE`
 /// as special.
 ///
@@ -96,7 +96,7 @@ pub struct HandleOrNull(OwnedHandle);
 /// `INVALID_HANDLE_VALUE`. This ensures that such FFI calls cannot start using the handle without
 /// checking for `INVALID_HANDLE_VALUE` first.
 ///
-/// This type concerns any value other than `INVALID_HANDLE_VALUE` to be valid, including `NULL`.
+/// This type considers any value other than `INVALID_HANDLE_VALUE` to be valid, including `NULL`.
 /// This is because APIs that use `INVALID_HANDLE_VALUE` as their sentry value may return `NULL`
 /// under `windows_subsystem = "windows"` or other situations where I/O devices are detached.
 ///
@@ -143,17 +143,17 @@ impl BorrowedHandle<'_> {
 }
 
 impl TryFrom<HandleOrNull> for OwnedHandle {
-    type Error = ();
+    type Error = NullHandleError;
 
     #[inline]
-    fn try_from(handle_or_null: HandleOrNull) -> Result<Self, ()> {
+    fn try_from(handle_or_null: HandleOrNull) -> Result<Self, NullHandleError> {
         let owned_handle = handle_or_null.0;
         if owned_handle.handle.is_null() {
             // Don't call `CloseHandle`; it'd be harmless, except that it could
             // overwrite the `GetLastError` error.
             forget(owned_handle);
 
-            Err(())
+            Err(NullHandleError(()))
         } else {
             Ok(owned_handle)
         }
@@ -198,26 +198,74 @@ impl OwnedHandle {
         })?;
         unsafe { Ok(Self::from_raw_handle(ret)) }
     }
+
+    /// Allow child processes to inherit the handle.
+    pub(crate) fn set_inheritable(&self) -> io::Result<()> {
+        cvt(unsafe {
+            c::SetHandleInformation(
+                self.as_raw_handle(),
+                c::HANDLE_FLAG_INHERIT,
+                c::HANDLE_FLAG_INHERIT,
+            )
+        })?;
+        Ok(())
+    }
 }
 
 impl TryFrom<HandleOrInvalid> for OwnedHandle {
-    type Error = ();
+    type Error = InvalidHandleError;
 
     #[inline]
-    fn try_from(handle_or_invalid: HandleOrInvalid) -> Result<Self, ()> {
+    fn try_from(handle_or_invalid: HandleOrInvalid) -> Result<Self, InvalidHandleError> {
         let owned_handle = handle_or_invalid.0;
         if owned_handle.handle == c::INVALID_HANDLE_VALUE {
             // Don't call `CloseHandle`; it'd be harmless, except that it could
             // overwrite the `GetLastError` error.
             forget(owned_handle);
 
-            Err(())
+            Err(InvalidHandleError(()))
         } else {
             Ok(owned_handle)
         }
     }
 }
 
+/// This is the error type used by [`HandleOrNull`] when attempting to convert
+/// into a handle, to indicate that the value is null.
+// The empty field prevents constructing this, and allows extending it in the future.
+#[unstable(feature = "io_safety", issue = "87074")]
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct NullHandleError(());
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl fmt::Display for NullHandleError {
+    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+        "A HandleOrNull could not be converted to a handle because it was null".fmt(fmt)
+    }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl crate::error::Error for NullHandleError {}
+
+/// This is the error type used by [`HandleOrInvalid`] when attempting to
+/// convert into a handle, to indicate that the value is
+/// `INVALID_HANDLE_VALUE`.
+// The empty field prevents constructing this, and allows extending it in the future.
+#[unstable(feature = "io_safety", issue = "87074")]
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct InvalidHandleError(());
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl fmt::Display for InvalidHandleError {
+    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+        "A HandleOrInvalid could not be converted to a handle because it was INVALID_HANDLE_VALUE"
+            .fmt(fmt)
+    }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl crate::error::Error for InvalidHandleError {}
+
 impl AsRawHandle for BorrowedHandle<'_> {
     #[inline]
     fn as_raw_handle(&self) -> RawHandle {
diff --git a/library/std/src/path.rs b/library/std/src/path.rs
index 8ecea8ce07f..c03d197e019 100644
--- a/library/std/src/path.rs
+++ b/library/std/src/path.rs
@@ -168,8 +168,8 @@ pub enum Prefix<'a> {
 
     /// Device namespace prefix, e.g., `\\.\COM42`.
     ///
-    /// Device namespace prefixes consist of `\\.\` immediately followed by the
-    /// device name.
+    /// Device namespace prefixes consist of `\\.\` (possibly using `/`
+    /// instead of `\`), immediately followed by the device name.
     #[stable(feature = "rust1", since = "1.0.0")]
     DeviceNS(#[stable(feature = "rust1", since = "1.0.0")] &'a OsStr),
 
@@ -193,7 +193,7 @@ impl<'a> Prefix<'a> {
     fn len(&self) -> usize {
         use self::Prefix::*;
         fn os_str_len(s: &OsStr) -> usize {
-            os_str_as_u8_slice(s).len()
+            s.bytes().len()
         }
         match *self {
             Verbatim(x) => 4 + os_str_len(x),
@@ -299,19 +299,17 @@ where
     }
 }
 
-// See note at the top of this module to understand why these are used:
-//
-// These casts are safe as OsStr is internally a wrapper around [u8] on all
-// platforms.
-//
-// Note that currently this relies on the special knowledge that libstd has;
-// these types are single-element structs but are not marked repr(transparent)
-// or repr(C) which would make these casts allowable outside std.
-fn os_str_as_u8_slice(s: &OsStr) -> &[u8] {
-    unsafe { &*(s as *const OsStr as *const [u8]) }
-}
 unsafe fn u8_slice_as_os_str(s: &[u8]) -> &OsStr {
-    // SAFETY: see the comment of `os_str_as_u8_slice`
+    // SAFETY: See note at the top of this module to understand why this and
+    // `OsStr::bytes` are used:
+    //
+    // This casts are safe as OsStr is internally a wrapper around [u8] on all
+    // platforms.
+    //
+    // Note that currently this relies on the special knowledge that libstd has;
+    // these types are single-element structs but are not marked
+    // repr(transparent) or repr(C) which would make these casts not allowable
+    // outside std.
     unsafe { &*(s as *const [u8] as *const OsStr) }
 }
 
@@ -332,7 +330,7 @@ fn has_physical_root(s: &[u8], prefix: Option<Prefix<'_>>) -> bool {
 
 // basic workhorse for splitting stem and extension
 fn rsplit_file_at_dot(file: &OsStr) -> (Option<&OsStr>, Option<&OsStr>) {
-    if os_str_as_u8_slice(file) == b".." {
+    if file.bytes() == b".." {
         return (Some(file), None);
     }
 
@@ -340,7 +338,7 @@ fn rsplit_file_at_dot(file: &OsStr) -> (Option<&OsStr>, Option<&OsStr>) {
     // and back. This is safe to do because (1) we only look at ASCII
     // contents of the encoding and (2) new &OsStr values are produced
     // only from ASCII-bounded slices of existing &OsStr values.
-    let mut iter = os_str_as_u8_slice(file).rsplitn(2, |b| *b == b'.');
+    let mut iter = file.bytes().rsplitn(2, |b| *b == b'.');
     let after = iter.next();
     let before = iter.next();
     if before == Some(b"") {
@@ -351,7 +349,7 @@ fn rsplit_file_at_dot(file: &OsStr) -> (Option<&OsStr>, Option<&OsStr>) {
 }
 
 fn split_file_at_dot(file: &OsStr) -> (&OsStr, Option<&OsStr>) {
-    let slice = os_str_as_u8_slice(file);
+    let slice = file.bytes();
     if slice == b".." {
         return (file, None);
     }
@@ -1445,17 +1443,17 @@ impl PathBuf {
     fn _set_extension(&mut self, extension: &OsStr) -> bool {
         let file_stem = match self.file_stem() {
             None => return false,
-            Some(f) => os_str_as_u8_slice(f),
+            Some(f) => f.bytes(),
         };
 
         // truncate until right after the file stem
         let end_file_stem = file_stem[file_stem.len()..].as_ptr().addr();
-        let start = os_str_as_u8_slice(&self.inner).as_ptr().addr();
+        let start = self.inner.bytes().as_ptr().addr();
         let v = self.as_mut_vec();
         v.truncate(end_file_stem.wrapping_sub(start));
 
         // add the new extension, if any
-        let new = os_str_as_u8_slice(extension);
+        let new = extension.bytes();
         if !new.is_empty() {
             v.reserve_exact(new.len() + 1);
             v.push(b'.');
@@ -1948,7 +1946,7 @@ impl Path {
     }
     // The following (private!) function reveals the byte encoding used for OsStr.
     fn as_u8_slice(&self) -> &[u8] {
-        os_str_as_u8_slice(&self.inner)
+        self.inner.bytes()
     }
 
     /// Directly wraps a string slice as a `Path` slice.
diff --git a/library/std/src/path/tests.rs b/library/std/src/path/tests.rs
index d1f59d2786e..0d8ea29c2be 100644
--- a/library/std/src/path/tests.rs
+++ b/library/std/src/path/tests.rs
@@ -971,15 +971,15 @@ pub fn test_decompositions_windows() {
     file_prefix: None
     );
 
-    t!("\\\\?\\C:/foo",
-    iter: ["\\\\?\\C:/foo"],
+    t!("\\\\?\\C:/foo/bar",
+    iter: ["\\\\?\\C:", "\\", "foo/bar"],
     has_root: true,
     is_absolute: true,
-    parent: None,
-    file_name: None,
-    file_stem: None,
+    parent: Some("\\\\?\\C:/"),
+    file_name: Some("foo/bar"),
+    file_stem: Some("foo/bar"),
     extension: None,
-    file_prefix: None
+    file_prefix: Some("foo/bar")
     );
 
     t!("\\\\.\\foo\\bar",
diff --git a/library/std/src/process/tests.rs b/library/std/src/process/tests.rs
index 4f779ab4e78..955ad68916c 100644
--- a/library/std/src/process/tests.rs
+++ b/library/std/src/process/tests.rs
@@ -435,3 +435,24 @@ fn run_bat_script() {
     assert!(output.status.success());
     assert_eq!(String::from_utf8_lossy(&output.stdout).trim(), "Hello, fellow Rustaceans!");
 }
+
+// See issue #95178
+#[test]
+#[cfg(windows)]
+fn run_canonical_bat_script() {
+    let tempdir = crate::sys_common::io::test::tmpdir();
+    let script_path = tempdir.join("hello.cmd");
+
+    crate::fs::write(&script_path, "@echo Hello, %~1!").unwrap();
+
+    // Try using a canonical path
+    let output = Command::new(&script_path.canonicalize().unwrap())
+        .arg("fellow Rustaceans")
+        .stdout(crate::process::Stdio::piped())
+        .spawn()
+        .unwrap()
+        .wait_with_output()
+        .unwrap();
+    assert!(output.status.success());
+    assert_eq!(String::from_utf8_lossy(&output.stdout).trim(), "Hello, fellow Rustaceans!");
+}
diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs
index 3de7c68a686..40a64585802 100644
--- a/library/std/src/sys/unix/fd.rs
+++ b/library/std/src/sys/unix/fd.rs
@@ -11,6 +11,21 @@ use crate::sys_common::{AsInner, FromInner, IntoInner};
 
 use libc::{c_int, c_void};
 
+#[cfg(any(
+    target_os = "android",
+    target_os = "linux",
+    target_os = "emscripten",
+    target_os = "l4re"
+))]
+use libc::off64_t;
+#[cfg(not(any(
+    target_os = "linux",
+    target_os = "emscripten",
+    target_os = "l4re",
+    target_os = "android"
+)))]
+use libc::off_t as off64_t;
+
 #[derive(Debug)]
 pub struct FileDesc(OwnedFd);
 
@@ -109,7 +124,7 @@ impl FileDesc {
                 self.as_raw_fd(),
                 buf.as_mut_ptr() as *mut c_void,
                 cmp::min(buf.len(), READ_LIMIT),
-                offset as i64,
+                offset as off64_t,
             ))
             .map(|n| n as usize)
         }
@@ -176,7 +191,7 @@ impl FileDesc {
                 self.as_raw_fd(),
                 buf.as_ptr() as *const c_void,
                 cmp::min(buf.len(), READ_LIMIT),
-                offset as i64,
+                offset as off64_t,
             ))
             .map(|n| n as usize)
         }
diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs
index 7181451de57..27fc7accdae 100644
--- a/library/std/src/sys/unix/fs.rs
+++ b/library/std/src/sys/unix/fs.rs
@@ -966,7 +966,7 @@ impl File {
             SeekFrom::End(off) => (libc::SEEK_END, off),
             SeekFrom::Current(off) => (libc::SEEK_CUR, off),
         };
-        let n = cvt(unsafe { lseek64(self.as_raw_fd(), pos, whence) })?;
+        let n = cvt(unsafe { lseek64(self.as_raw_fd(), pos as off64_t, whence) })?;
         Ok(n as u64)
     }
 
@@ -1647,8 +1647,9 @@ mod remove_dir_impl {
     fn remove_dir_all_recursive(parent_fd: Option<RawFd>, path: &CStr) -> io::Result<()> {
         // try opening as directory
         let fd = match openat_nofollow_dironly(parent_fd, &path) {
-            Err(err) if err.raw_os_error() == Some(libc::ENOTDIR) => {
+            Err(err) if matches!(err.raw_os_error(), Some(libc::ENOTDIR | libc::ELOOP)) => {
                 // not a directory - don't traverse further
+                // (for symlinks, older Linux kernels may return ELOOP instead of ENOTDIR)
                 return match parent_fd {
                     // unlink...
                     Some(parent_fd) => {
diff --git a/library/std/src/sys/unix/futex.rs b/library/std/src/sys/unix/futex.rs
index 62760373a6a..c12ee169e79 100644
--- a/library/std/src/sys/unix/futex.rs
+++ b/library/std/src/sys/unix/futex.rs
@@ -52,25 +52,6 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -
     }
 }
 
-#[cfg(target_os = "emscripten")]
-pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) {
-    extern "C" {
-        fn emscripten_futex_wait(
-            addr: *const AtomicU32,
-            val: libc::c_uint,
-            max_wait_ms: libc::c_double,
-        ) -> libc::c_int;
-    }
-
-    unsafe {
-        emscripten_futex_wait(
-            futex,
-            expected,
-            timeout.map_or(crate::f64::INFINITY, |d| d.as_secs_f64() * 1000.0),
-        );
-    }
-}
-
 /// Wake up one thread that's blocked on futex_wait on this futex.
 ///
 /// Returns true if this actually woke up such a thread,
@@ -101,10 +82,32 @@ pub fn futex_wake_all(futex: &AtomicU32) {
 }
 
 #[cfg(target_os = "emscripten")]
-pub fn futex_wake(futex: &AtomicU32) -> bool {
-    extern "C" {
-        fn emscripten_futex_wake(addr: *const AtomicU32, count: libc::c_int) -> libc::c_int;
+extern "C" {
+    fn emscripten_futex_wake(addr: *const AtomicU32, count: libc::c_int) -> libc::c_int;
+    fn emscripten_futex_wait(
+        addr: *const AtomicU32,
+        val: libc::c_uint,
+        max_wait_ms: libc::c_double,
+    ) -> libc::c_int;
+}
+
+#[cfg(target_os = "emscripten")]
+pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -> bool {
+    unsafe {
+        emscripten_futex_wait(
+            futex,
+            expected,
+            timeout.map_or(f64::INFINITY, |d| d.as_secs_f64() * 1000.0),
+        ) != -libc::ETIMEDOUT
     }
+}
 
+#[cfg(target_os = "emscripten")]
+pub fn futex_wake(futex: &AtomicU32) -> bool {
     unsafe { emscripten_futex_wake(futex, 1) > 0 }
 }
+
+#[cfg(target_os = "emscripten")]
+pub fn futex_wake_all(futex: &AtomicU32) {
+    unsafe { emscripten_futex_wake(futex, i32::MAX) };
+}
diff --git a/library/std/src/sys/unix/locks/mod.rs b/library/std/src/sys/unix/locks/mod.rs
index 17796f8894b..3e39c8b9b23 100644
--- a/library/std/src/sys/unix/locks/mod.rs
+++ b/library/std/src/sys/unix/locks/mod.rs
@@ -2,6 +2,7 @@ cfg_if::cfg_if! {
     if #[cfg(any(
         target_os = "linux",
         target_os = "android",
+        all(target_os = "emscripten", target_feature = "atomics"),
     ))] {
         mod futex;
         mod futex_rwlock;
diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs
index aedeb02e656..8e909aab7f0 100644
--- a/library/std/src/sys/unix/mod.rs
+++ b/library/std/src/sys/unix/mod.rs
@@ -39,6 +39,7 @@ pub mod stdio;
 pub mod thread;
 pub mod thread_local_dtor;
 pub mod thread_local_key;
+pub mod thread_parker;
 pub mod time;
 
 #[cfg(target_os = "espidf")]
diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs
index 1be733ba106..92bea9346d8 100644
--- a/library/std/src/sys/unix/os.rs
+++ b/library/std/src/sys/unix/os.rs
@@ -427,7 +427,7 @@ pub fn current_exe() -> io::Result<PathBuf> {
     crate::fs::read_to_string("sys:exe").map(PathBuf::from)
 }
 
-#[cfg(any(target_os = "fuchsia", target_os = "l4re"))]
+#[cfg(target_os = "l4re")]
 pub fn current_exe() -> io::Result<PathBuf> {
     use crate::io::ErrorKind;
     Err(io::const_io_error!(ErrorKind::Unsupported, "Not yet implemented!"))
@@ -451,6 +451,26 @@ pub fn current_exe() -> io::Result<PathBuf> {
     super::unsupported::unsupported()
 }
 
+#[cfg(target_os = "fuchsia")]
+pub fn current_exe() -> io::Result<PathBuf> {
+    use crate::io::ErrorKind;
+
+    #[cfg(test)]
+    use realstd::env;
+
+    #[cfg(not(test))]
+    use crate::env;
+
+    let exe_path = env::args().next().ok_or(io::const_io_error!(
+        ErrorKind::Uncategorized,
+        "an executable path was not found because no arguments were provided through argv"
+    ))?;
+    let path = PathBuf::from(exe_path);
+
+    // Prepend the current working directory to the path if it's not absolute.
+    if !path.is_absolute() { getcwd().map(|cwd| cwd.join(path)) } else { Ok(path) }
+}
+
 pub struct Env {
     iter: vec::IntoIter<(OsString, OsString)>,
 }
diff --git a/library/std/src/sys/unix/process/process_common.rs b/library/std/src/sys/unix/process/process_common.rs
index 27bee714f5b..bca1b65a7fc 100644
--- a/library/std/src/sys/unix/process/process_common.rs
+++ b/library/std/src/sys/unix/process/process_common.rs
@@ -35,7 +35,8 @@ cfg_if::cfg_if! {
 // Android with api less than 21 define sig* functions inline, so it is not
 // available for dynamic link. Implementing sigemptyset and sigaddset allow us
 // to support older Android version (independent of libc version).
-// The following implementations are based on https://git.io/vSkNf
+// The following implementations are based on
+// https://github.com/aosp-mirror/platform_bionic/blob/ad8dcd6023294b646e5a8288c0ed431b0845da49/libc/include/android/legacy_signal_inlines.h
 cfg_if::cfg_if! {
     if #[cfg(target_os = "android")] {
         pub unsafe fn sigemptyset(set: *mut libc::sigset_t) -> libc::c_int {
diff --git a/library/std/src/sys/unix/thread_parker.rs b/library/std/src/sys/unix/thread_parker.rs
new file mode 100644
index 00000000000..fd83f2f73d6
--- /dev/null
+++ b/library/std/src/sys/unix/thread_parker.rs
@@ -0,0 +1,265 @@
+//! Thread parking without `futex` using the `pthread` synchronization primitives.
+
+#![cfg(not(any(
+    target_os = "linux",
+    target_os = "android",
+    all(target_os = "emscripten", target_feature = "atomics")
+)))]
+
+use crate::cell::UnsafeCell;
+use crate::marker::PhantomPinned;
+use crate::pin::Pin;
+use crate::ptr::addr_of_mut;
+use crate::sync::atomic::AtomicUsize;
+use crate::sync::atomic::Ordering::SeqCst;
+use crate::time::Duration;
+
+const EMPTY: usize = 0;
+const PARKED: usize = 1;
+const NOTIFIED: usize = 2;
+
+unsafe fn lock(lock: *mut libc::pthread_mutex_t) {
+    let r = libc::pthread_mutex_lock(lock);
+    debug_assert_eq!(r, 0);
+}
+
+unsafe fn unlock(lock: *mut libc::pthread_mutex_t) {
+    let r = libc::pthread_mutex_unlock(lock);
+    debug_assert_eq!(r, 0);
+}
+
+unsafe fn notify_one(cond: *mut libc::pthread_cond_t) {
+    let r = libc::pthread_cond_signal(cond);
+    debug_assert_eq!(r, 0);
+}
+
+unsafe fn wait(cond: *mut libc::pthread_cond_t, lock: *mut libc::pthread_mutex_t) {
+    let r = libc::pthread_cond_wait(cond, lock);
+    debug_assert_eq!(r, 0);
+}
+
+const TIMESPEC_MAX: libc::timespec =
+    libc::timespec { tv_sec: <libc::time_t>::MAX, tv_nsec: 1_000_000_000 - 1 };
+
+unsafe fn wait_timeout(
+    cond: *mut libc::pthread_cond_t,
+    lock: *mut libc::pthread_mutex_t,
+    dur: Duration,
+) {
+    // Use the system clock on systems that do not support pthread_condattr_setclock.
+    // This unfortunately results in problems when the system time changes.
+    #[cfg(any(target_os = "macos", target_os = "ios", target_os = "espidf"))]
+    let (now, dur) = {
+        use super::time::SystemTime;
+        use crate::cmp::min;
+
+        // OSX implementation of `pthread_cond_timedwait` is buggy
+        // with super long durations. When duration is greater than
+        // 0x100_0000_0000_0000 seconds, `pthread_cond_timedwait`
+        // in macOS Sierra return error 316.
+        //
+        // This program demonstrates the issue:
+        // https://gist.github.com/stepancheg/198db4623a20aad2ad7cddb8fda4a63c
+        //
+        // To work around this issue, and possible bugs of other OSes, timeout
+        // is clamped to 1000 years, which is allowable per the API of `park_timeout`
+        // because of spurious wakeups.
+        let dur = min(dur, Duration::from_secs(1000 * 365 * 86400));
+        let now = SystemTime::now().t;
+        (now, dur)
+    };
+    // Use the monotonic clock on other systems.
+    #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "espidf")))]
+    let (now, dur) = {
+        use super::time::Timespec;
+
+        (Timespec::now(libc::CLOCK_MONOTONIC), dur)
+    };
+
+    let timeout = now.checked_add_duration(&dur).map(|t| t.t).unwrap_or(TIMESPEC_MAX);
+    let r = libc::pthread_cond_timedwait(cond, lock, &timeout);
+    debug_assert!(r == libc::ETIMEDOUT || r == 0);
+}
+
+pub struct Parker {
+    state: AtomicUsize,
+    lock: UnsafeCell<libc::pthread_mutex_t>,
+    cvar: UnsafeCell<libc::pthread_cond_t>,
+    // The `pthread` primitives require a stable address, so make this struct `!Unpin`.
+    _pinned: PhantomPinned,
+}
+
+impl Parker {
+    /// Construct the UNIX parker in-place.
+    ///
+    /// # Safety
+    /// The constructed parker must never be moved.
+    pub unsafe fn new(parker: *mut Parker) {
+        // Use the default mutex implementation to allow for simpler initialization.
+        // This could lead to undefined behaviour when deadlocking. This is avoided
+        // by not deadlocking. Note in particular the unlocking operation before any
+        // panic, as code after the panic could try to park again.
+        addr_of_mut!((*parker).state).write(AtomicUsize::new(EMPTY));
+        addr_of_mut!((*parker).lock).write(UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER));
+
+        cfg_if::cfg_if! {
+            if #[cfg(any(
+                target_os = "macos",
+                target_os = "ios",
+                target_os = "l4re",
+                target_os = "android",
+                target_os = "redox"
+            ))] {
+                addr_of_mut!((*parker).cvar).write(UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER));
+            } else if #[cfg(target_os = "espidf")] {
+                let r = libc::pthread_cond_init(addr_of_mut!((*parker).cvar).cast(), crate::ptr::null());
+                assert_eq!(r, 0);
+            } else {
+                use crate::mem::MaybeUninit;
+                let mut attr = MaybeUninit::<libc::pthread_condattr_t>::uninit();
+                let r = libc::pthread_condattr_init(attr.as_mut_ptr());
+                assert_eq!(r, 0);
+                let r = libc::pthread_condattr_setclock(attr.as_mut_ptr(), libc::CLOCK_MONOTONIC);
+                assert_eq!(r, 0);
+                let r = libc::pthread_cond_init(addr_of_mut!((*parker).cvar).cast(), attr.as_ptr());
+                assert_eq!(r, 0);
+                let r = libc::pthread_condattr_destroy(attr.as_mut_ptr());
+                assert_eq!(r, 0);
+            }
+        }
+    }
+
+    // This implementation doesn't require `unsafe`, but other implementations
+    // may assume this is only called by the thread that owns the Parker.
+    pub unsafe fn park(self: Pin<&Self>) {
+        // If we were previously notified then we consume this notification and
+        // return quickly.
+        if self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst).is_ok() {
+            return;
+        }
+
+        // Otherwise we need to coordinate going to sleep
+        lock(self.lock.get());
+        match self.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) {
+            Ok(_) => {}
+            Err(NOTIFIED) => {
+                // We must read here, even though we know it will be `NOTIFIED`.
+                // This is because `unpark` may have been called again since we read
+                // `NOTIFIED` in the `compare_exchange` above. We must perform an
+                // acquire operation that synchronizes with that `unpark` to observe
+                // any writes it made before the call to unpark. To do that we must
+                // read from the write it made to `state`.
+                let old = self.state.swap(EMPTY, SeqCst);
+
+                unlock(self.lock.get());
+
+                assert_eq!(old, NOTIFIED, "park state changed unexpectedly");
+                return;
+            } // should consume this notification, so prohibit spurious wakeups in next park.
+            Err(_) => {
+                unlock(self.lock.get());
+
+                panic!("inconsistent park state")
+            }
+        }
+
+        loop {
+            wait(self.cvar.get(), self.lock.get());
+
+            match self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst) {
+                Ok(_) => break, // got a notification
+                Err(_) => {}    // spurious wakeup, go back to sleep
+            }
+        }
+
+        unlock(self.lock.get());
+    }
+
+    // This implementation doesn't require `unsafe`, but other implementations
+    // may assume this is only called by the thread that owns the Parker. Use
+    // `Pin` to guarantee a stable address for the mutex and condition variable.
+    pub unsafe fn park_timeout(self: Pin<&Self>, dur: Duration) {
+        // Like `park` above we have a fast path for an already-notified thread, and
+        // afterwards we start coordinating for a sleep.
+        // return quickly.
+        if self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst).is_ok() {
+            return;
+        }
+
+        lock(self.lock.get());
+        match self.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) {
+            Ok(_) => {}
+            Err(NOTIFIED) => {
+                // We must read again here, see `park`.
+                let old = self.state.swap(EMPTY, SeqCst);
+                unlock(self.lock.get());
+
+                assert_eq!(old, NOTIFIED, "park state changed unexpectedly");
+                return;
+            } // should consume this notification, so prohibit spurious wakeups in next park.
+            Err(_) => {
+                unlock(self.lock.get());
+                panic!("inconsistent park_timeout state")
+            }
+        }
+
+        // Wait with a timeout, and if we spuriously wake up or otherwise wake up
+        // from a notification we just want to unconditionally set the state back to
+        // empty, either consuming a notification or un-flagging ourselves as
+        // parked.
+        wait_timeout(self.cvar.get(), self.lock.get(), dur);
+
+        match self.state.swap(EMPTY, SeqCst) {
+            NOTIFIED => unlock(self.lock.get()), // got a notification, hurray!
+            PARKED => unlock(self.lock.get()),   // no notification, alas
+            n => {
+                unlock(self.lock.get());
+                panic!("inconsistent park_timeout state: {n}")
+            }
+        }
+    }
+
+    pub fn unpark(self: Pin<&Self>) {
+        // To ensure the unparked thread will observe any writes we made
+        // before this call, we must perform a release operation that `park`
+        // can synchronize with. To do that we must write `NOTIFIED` even if
+        // `state` is already `NOTIFIED`. That is why this must be a swap
+        // rather than a compare-and-swap that returns if it reads `NOTIFIED`
+        // on failure.
+        match self.state.swap(NOTIFIED, SeqCst) {
+            EMPTY => return,    // no one was waiting
+            NOTIFIED => return, // already unparked
+            PARKED => {}        // gotta go wake someone up
+            _ => panic!("inconsistent state in unpark"),
+        }
+
+        // There is a period between when the parked thread sets `state` to
+        // `PARKED` (or last checked `state` in the case of a spurious wake
+        // up) and when it actually waits on `cvar`. If we were to notify
+        // during this period it would be ignored and then when the parked
+        // thread went to sleep it would never wake up. Fortunately, it has
+        // `lock` locked at this stage so we can acquire `lock` to wait until
+        // it is ready to receive the notification.
+        //
+        // Releasing `lock` before the call to `notify_one` means that when the
+        // parked thread wakes it doesn't get woken only to have to wait for us
+        // to release `lock`.
+        unsafe {
+            lock(self.lock.get());
+            unlock(self.lock.get());
+            notify_one(self.cvar.get());
+        }
+    }
+}
+
+impl Drop for Parker {
+    fn drop(&mut self) {
+        unsafe {
+            libc::pthread_cond_destroy(self.cvar.get_mut());
+            libc::pthread_mutex_destroy(self.lock.get_mut());
+        }
+    }
+}
+
+unsafe impl Sync for Parker {}
+unsafe impl Send for Parker {}
diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs
index 498c94d0cdc..d43ceec9c8a 100644
--- a/library/std/src/sys/unix/time.rs
+++ b/library/std/src/sys/unix/time.rs
@@ -132,7 +132,7 @@ mod inner {
 
     #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
     pub struct SystemTime {
-        t: Timespec,
+        pub(in crate::sys::unix) t: Timespec,
     }
 
     pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() };
@@ -279,7 +279,7 @@ mod inner {
 
     #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
     pub struct SystemTime {
-        t: Timespec,
+        pub(in crate::sys::unix) t: Timespec,
     }
 
     pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() };
diff --git a/library/std/src/sys/unix/weak.rs b/library/std/src/sys/unix/weak.rs
index da63c068384..e4ff21b25bd 100644
--- a/library/std/src/sys/unix/weak.rs
+++ b/library/std/src/sys/unix/weak.rs
@@ -25,7 +25,8 @@
 use crate::ffi::CStr;
 use crate::marker::PhantomData;
 use crate::mem;
-use crate::sync::atomic::{self, AtomicUsize, Ordering};
+use crate::ptr;
+use crate::sync::atomic::{self, AtomicPtr, Ordering};
 
 // We can use true weak linkage on ELF targets.
 #[cfg(not(any(target_os = "macos", target_os = "ios")))]
@@ -83,13 +84,13 @@ pub(crate) macro dlsym {
 }
 pub(crate) struct DlsymWeak<F> {
     name: &'static str,
-    addr: AtomicUsize,
+    func: AtomicPtr<libc::c_void>,
     _marker: PhantomData<F>,
 }
 
 impl<F> DlsymWeak<F> {
     pub(crate) const fn new(name: &'static str) -> Self {
-        DlsymWeak { name, addr: AtomicUsize::new(1), _marker: PhantomData }
+        DlsymWeak { name, func: AtomicPtr::new(ptr::invalid_mut(1)), _marker: PhantomData }
     }
 
     #[inline]
@@ -97,11 +98,11 @@ impl<F> DlsymWeak<F> {
         unsafe {
             // Relaxed is fine here because we fence before reading through the
             // pointer (see the comment below).
-            match self.addr.load(Ordering::Relaxed) {
-                1 => self.initialize(),
-                0 => None,
-                addr => {
-                    let func = mem::transmute_copy::<usize, F>(&addr);
+            match self.func.load(Ordering::Relaxed) {
+                func if func.addr() == 1 => self.initialize(),
+                func if func.is_null() => None,
+                func => {
+                    let func = mem::transmute_copy::<*mut libc::c_void, F>(&func);
                     // The caller is presumably going to read through this value
                     // (by calling the function we've dlsymed). This means we'd
                     // need to have loaded it with at least C11's consume
@@ -129,25 +130,22 @@ impl<F> DlsymWeak<F> {
     // Cold because it should only happen during first-time initialization.
     #[cold]
     unsafe fn initialize(&self) -> Option<F> {
-        assert_eq!(mem::size_of::<F>(), mem::size_of::<usize>());
+        assert_eq!(mem::size_of::<F>(), mem::size_of::<*mut libc::c_void>());
 
         let val = fetch(self.name);
         // This synchronizes with the acquire fence in `get`.
-        self.addr.store(val, Ordering::Release);
+        self.func.store(val, Ordering::Release);
 
-        match val {
-            0 => None,
-            addr => Some(mem::transmute_copy::<usize, F>(&addr)),
-        }
+        if val.is_null() { None } else { Some(mem::transmute_copy::<*mut libc::c_void, F>(&val)) }
     }
 }
 
-unsafe fn fetch(name: &str) -> usize {
+unsafe fn fetch(name: &str) -> *mut libc::c_void {
     let name = match CStr::from_bytes_with_nul(name.as_bytes()) {
         Ok(cstr) => cstr,
-        Err(..) => return 0,
+        Err(..) => return ptr::null_mut(),
     };
-    libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr()) as usize
+    libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr())
 }
 
 #[cfg(not(any(target_os = "linux", target_os = "android")))]
diff --git a/library/std/src/sys/wasm/atomics/condvar.rs b/library/std/src/sys/wasm/atomics/condvar.rs
deleted file mode 100644
index f06c07c5409..00000000000
--- a/library/std/src/sys/wasm/atomics/condvar.rs
+++ /dev/null
@@ -1,102 +0,0 @@
-use crate::arch::wasm32;
-use crate::cmp;
-use crate::mem;
-use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst};
-use crate::sys::locks::Mutex;
-use crate::time::Duration;
-
-pub struct Condvar {
-    cnt: AtomicUsize,
-}
-
-pub type MovableCondvar = Condvar;
-
-// Condition variables are implemented with a simple counter internally that is
-// likely to cause spurious wakeups. Blocking on a condition variable will first
-// read the value of the internal counter, unlock the given mutex, and then
-// block if and only if the counter's value is still the same. Notifying a
-// condition variable will modify the counter (add one for now) and then wake up
-// a thread waiting on the address of the counter.
-//
-// A thread waiting on the condition variable will as a result avoid going to
-// sleep if it's notified after the lock is unlocked but before it fully goes to
-// sleep. A sleeping thread is guaranteed to be woken up at some point as it can
-// only be woken up with a call to `wake`.
-//
-// Note that it's possible for 2 or more threads to be woken up by a call to
-// `notify_one` with this implementation. That can happen where the modification
-// of `cnt` causes any threads in the middle of `wait` to avoid going to sleep,
-// and the subsequent `wake` may wake up a thread that's actually blocking. We
-// consider this a spurious wakeup, though, which all users of condition
-// variables must already be prepared to handle. As a result, this source of
-// spurious wakeups is currently though to be ok, although it may be problematic
-// later on if it causes too many spurious wakeups.
-
-impl Condvar {
-    pub const fn new() -> Condvar {
-        Condvar { cnt: AtomicUsize::new(0) }
-    }
-
-    #[inline]
-    pub unsafe fn init(&mut self) {
-        // nothing to do
-    }
-
-    pub unsafe fn notify_one(&self) {
-        self.cnt.fetch_add(1, SeqCst);
-        // SAFETY: ptr() is always valid
-        unsafe {
-            wasm32::memory_atomic_notify(self.ptr(), 1);
-        }
-    }
-
-    #[inline]
-    pub unsafe fn notify_all(&self) {
-        self.cnt.fetch_add(1, SeqCst);
-        // SAFETY: ptr() is always valid
-        unsafe {
-            wasm32::memory_atomic_notify(self.ptr(), u32::MAX); // -1 == "wake everyone"
-        }
-    }
-
-    pub unsafe fn wait(&self, mutex: &Mutex) {
-        // "atomically block and unlock" implemented by loading our current
-        // counter's value, unlocking the mutex, and blocking if the counter
-        // still has the same value.
-        //
-        // Notifications happen by incrementing the counter and then waking a
-        // thread. Incrementing the counter after we unlock the mutex will
-        // prevent us from sleeping and otherwise the call to `wake` will
-        // wake us up once we're asleep.
-        let ticket = self.cnt.load(SeqCst) as i32;
-        mutex.unlock();
-        let val = wasm32::memory_atomic_wait32(self.ptr(), ticket, -1);
-        // 0 == woken, 1 == not equal to `ticket`, 2 == timeout (shouldn't happen)
-        debug_assert!(val == 0 || val == 1);
-        mutex.lock();
-    }
-
-    pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
-        let ticket = self.cnt.load(SeqCst) as i32;
-        mutex.unlock();
-        let nanos = dur.as_nanos();
-        let nanos = cmp::min(i64::MAX as u128, nanos);
-
-        // If the return value is 2 then a timeout happened, so we return
-        // `false` as we weren't actually notified.
-        let ret = wasm32::memory_atomic_wait32(self.ptr(), ticket, nanos as i64) != 2;
-        mutex.lock();
-        return ret;
-    }
-
-    #[inline]
-    pub unsafe fn destroy(&self) {
-        // nothing to do
-    }
-
-    #[inline]
-    fn ptr(&self) -> *mut i32 {
-        assert_eq!(mem::size_of::<usize>(), mem::size_of::<i32>());
-        self.cnt.as_mut_ptr() as *mut i32
-    }
-}
diff --git a/library/std/src/sys/wasm/atomics/futex.rs b/library/std/src/sys/wasm/atomics/futex.rs
index bbe9bd6951a..11413ba3bf5 100644
--- a/library/std/src/sys/wasm/atomics/futex.rs
+++ b/library/std/src/sys/wasm/atomics/futex.rs
@@ -3,19 +3,33 @@ use crate::convert::TryInto;
 use crate::sync::atomic::AtomicU32;
 use crate::time::Duration;
 
-pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) {
+/// Wait for a futex_wake operation to wake us.
+///
+/// Returns directly if the futex doesn't hold the expected value.
+///
+/// Returns false on timeout, and true in all other cases.
+pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -> bool {
     let timeout = timeout.and_then(|t| t.as_nanos().try_into().ok()).unwrap_or(-1);
     unsafe {
         wasm32::memory_atomic_wait32(
             futex as *const AtomicU32 as *mut i32,
             expected as i32,
             timeout,
-        );
+        ) < 2
     }
 }
 
-pub fn futex_wake(futex: &AtomicU32) {
+/// Wake up one thread that's blocked on futex_wait on this futex.
+///
+/// Returns true if this actually woke up such a thread,
+/// or false if no thread was waiting on this futex.
+pub fn futex_wake(futex: &AtomicU32) -> bool {
+    unsafe { wasm32::memory_atomic_notify(futex as *const AtomicU32 as *mut i32, 1) > 0 }
+}
+
+/// Wake up all threads that are waiting on futex_wait on this futex.
+pub fn futex_wake_all(futex: &AtomicU32) {
     unsafe {
-        wasm32::memory_atomic_notify(futex as *const AtomicU32 as *mut i32, 1);
+        wasm32::memory_atomic_notify(futex as *const AtomicU32 as *mut i32, i32::MAX as u32);
     }
 }
diff --git a/library/std/src/sys/wasm/atomics/mutex.rs b/library/std/src/sys/wasm/atomics/mutex.rs
deleted file mode 100644
index 1acc8392444..00000000000
--- a/library/std/src/sys/wasm/atomics/mutex.rs
+++ /dev/null
@@ -1,64 +0,0 @@
-use crate::arch::wasm32;
-use crate::mem;
-use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst};
-
-pub struct Mutex {
-    locked: AtomicUsize,
-}
-
-pub type MovableMutex = Mutex;
-
-// Mutexes have a pretty simple implementation where they contain an `i32`
-// internally that is 0 when unlocked and 1 when the mutex is locked.
-// Acquisition has a fast path where it attempts to cmpxchg the 0 to a 1, and
-// if it fails it then waits for a notification. Releasing a lock is then done
-// by swapping in 0 and then notifying any waiters, if present.
-
-impl Mutex {
-    pub const fn new() -> Mutex {
-        Mutex { locked: AtomicUsize::new(0) }
-    }
-
-    #[inline]
-    pub unsafe fn init(&mut self) {
-        // nothing to do
-    }
-
-    pub unsafe fn lock(&self) {
-        while !self.try_lock() {
-            // SAFETY: the caller must uphold the safety contract for `memory_atomic_wait32`.
-            let val = unsafe {
-                wasm32::memory_atomic_wait32(
-                    self.ptr(),
-                    1,  // we expect our mutex is locked
-                    -1, // wait infinitely
-                )
-            };
-            // we should have either woke up (0) or got a not-equal due to a
-            // race (1). We should never time out (2)
-            debug_assert!(val == 0 || val == 1);
-        }
-    }
-
-    pub unsafe fn unlock(&self) {
-        let prev = self.locked.swap(0, SeqCst);
-        debug_assert_eq!(prev, 1);
-        wasm32::memory_atomic_notify(self.ptr(), 1); // wake up one waiter, if any
-    }
-
-    #[inline]
-    pub unsafe fn try_lock(&self) -> bool {
-        self.locked.compare_exchange(0, 1, SeqCst, SeqCst).is_ok()
-    }
-
-    #[inline]
-    pub unsafe fn destroy(&self) {
-        // nothing to do
-    }
-
-    #[inline]
-    fn ptr(&self) -> *mut i32 {
-        assert_eq!(mem::size_of::<usize>(), mem::size_of::<i32>());
-        self.locked.as_mut_ptr() as *mut i32
-    }
-}
diff --git a/library/std/src/sys/wasm/atomics/rwlock.rs b/library/std/src/sys/wasm/atomics/rwlock.rs
deleted file mode 100644
index 690bb155e1a..00000000000
--- a/library/std/src/sys/wasm/atomics/rwlock.rs
+++ /dev/null
@@ -1,145 +0,0 @@
-use crate::cell::UnsafeCell;
-use crate::sys::locks::{Condvar, Mutex};
-
-pub struct RwLock {
-    lock: Mutex,
-    cond: Condvar,
-    state: UnsafeCell<State>,
-}
-
-pub type MovableRwLock = RwLock;
-
-enum State {
-    Unlocked,
-    Reading(usize),
-    Writing,
-}
-
-unsafe impl Send for RwLock {}
-unsafe impl Sync for RwLock {}
-
-// This rwlock implementation is a relatively simple implementation which has a
-// condition variable for readers/writers as well as a mutex protecting the
-// internal state of the lock. A current downside of the implementation is that
-// unlocking the lock will notify *all* waiters rather than just readers or just
-// writers. This can cause lots of "thundering stampede" problems. While
-// hopefully correct this implementation is very likely to want to be changed in
-// the future.
-
-impl RwLock {
-    pub const fn new() -> RwLock {
-        RwLock { lock: Mutex::new(), cond: Condvar::new(), state: UnsafeCell::new(State::Unlocked) }
-    }
-
-    #[inline]
-    pub unsafe fn read(&self) {
-        self.lock.lock();
-        while !(*self.state.get()).inc_readers() {
-            self.cond.wait(&self.lock);
-        }
-        self.lock.unlock();
-    }
-
-    #[inline]
-    pub unsafe fn try_read(&self) -> bool {
-        self.lock.lock();
-        let ok = (*self.state.get()).inc_readers();
-        self.lock.unlock();
-        return ok;
-    }
-
-    #[inline]
-    pub unsafe fn write(&self) {
-        self.lock.lock();
-        while !(*self.state.get()).inc_writers() {
-            self.cond.wait(&self.lock);
-        }
-        self.lock.unlock();
-    }
-
-    #[inline]
-    pub unsafe fn try_write(&self) -> bool {
-        self.lock.lock();
-        let ok = (*self.state.get()).inc_writers();
-        self.lock.unlock();
-        return ok;
-    }
-
-    #[inline]
-    pub unsafe fn read_unlock(&self) {
-        self.lock.lock();
-        let notify = (*self.state.get()).dec_readers();
-        self.lock.unlock();
-        if notify {
-            // FIXME: should only wake up one of these some of the time
-            self.cond.notify_all();
-        }
-    }
-
-    #[inline]
-    pub unsafe fn write_unlock(&self) {
-        self.lock.lock();
-        (*self.state.get()).dec_writers();
-        self.lock.unlock();
-        // FIXME: should only wake up one of these some of the time
-        self.cond.notify_all();
-    }
-
-    #[inline]
-    pub unsafe fn destroy(&self) {
-        self.lock.destroy();
-        self.cond.destroy();
-    }
-}
-
-impl State {
-    fn inc_readers(&mut self) -> bool {
-        match *self {
-            State::Unlocked => {
-                *self = State::Reading(1);
-                true
-            }
-            State::Reading(ref mut cnt) => {
-                *cnt += 1;
-                true
-            }
-            State::Writing => false,
-        }
-    }
-
-    fn inc_writers(&mut self) -> bool {
-        match *self {
-            State::Unlocked => {
-                *self = State::Writing;
-                true
-            }
-            State::Reading(_) | State::Writing => false,
-        }
-    }
-
-    fn dec_readers(&mut self) -> bool {
-        let zero = match *self {
-            State::Reading(ref mut cnt) => {
-                *cnt -= 1;
-                *cnt == 0
-            }
-            State::Unlocked | State::Writing => invalid(),
-        };
-        if zero {
-            *self = State::Unlocked;
-        }
-        zero
-    }
-
-    fn dec_writers(&mut self) {
-        match *self {
-            State::Writing => {}
-            State::Unlocked | State::Reading(_) => invalid(),
-        }
-        *self = State::Unlocked;
-    }
-}
-
-fn invalid() -> ! {
-    panic!("inconsistent rwlock");
-}
diff --git a/library/std/src/sys/wasm/atomics/thread.rs b/library/std/src/sys/wasm/atomics/thread.rs
index 16418a06226..714b7049227 100644
--- a/library/std/src/sys/wasm/atomics/thread.rs
+++ b/library/std/src/sys/wasm/atomics/thread.rs
@@ -53,37 +53,3 @@ pub mod guard {
         None
     }
 }
-
-// We currently just use our own thread-local to store our
-// current thread's ID, and then we lazily initialize it to something allocated
-// from a global counter.
-pub fn my_id() -> u32 {
-    use crate::sync::atomic::{AtomicU32, Ordering::SeqCst};
-
-    static NEXT_ID: AtomicU32 = AtomicU32::new(0);
-
-    #[thread_local]
-    static mut MY_ID: u32 = 0;
-
-    unsafe {
-        // If our thread ID isn't set yet then we need to allocate one. Do so
-        // with with a simple "atomically add to a global counter" strategy.
-        // This strategy doesn't handled what happens when the counter
-        // overflows, however, so just abort everything once the counter
-        // overflows and eventually we could have some sort of recycling scheme
-        // (or maybe this is all totally irrelevant by that point!). In any case
-        // though we're using a CAS loop instead of a `fetch_add` to ensure that
-        // the global counter never overflows.
-        if MY_ID == 0 {
-            let mut cur = NEXT_ID.load(SeqCst);
-            MY_ID = loop {
-                let next = cur.checked_add(1).unwrap_or_else(|| crate::process::abort());
-                match NEXT_ID.compare_exchange(cur, next, SeqCst, SeqCst) {
-                    Ok(_) => break next,
-                    Err(i) => cur = i,
-                }
-            };
-        }
-        MY_ID
-    }
-}
diff --git a/library/std/src/sys/wasm/mod.rs b/library/std/src/sys/wasm/mod.rs
index 9f6700caf14..9992e44b0e7 100644
--- a/library/std/src/sys/wasm/mod.rs
+++ b/library/std/src/sys/wasm/mod.rs
@@ -49,16 +49,13 @@ pub mod time;
 
 cfg_if::cfg_if! {
     if #[cfg(target_feature = "atomics")] {
-        #[path = "atomics/condvar.rs"]
-        mod condvar;
-        #[path = "atomics/mutex.rs"]
-        mod mutex;
-        #[path = "atomics/rwlock.rs"]
-        mod rwlock;
+        #[path = "../unix/locks"]
         pub mod locks {
-            pub use super::condvar::*;
-            pub use super::mutex::*;
-            pub use super::rwlock::*;
+            #![allow(unsafe_op_in_unsafe_fn)]
+            mod futex;
+            mod futex_rwlock;
+            pub use futex::{Mutex, MovableMutex, Condvar, MovableCondvar};
+            pub use futex_rwlock::{RwLock, MovableRwLock};
         }
         #[path = "atomics/futex.rs"]
         pub mod futex;
diff --git a/library/std/src/sys/windows/args.rs b/library/std/src/sys/windows/args.rs
index 3919025b080..c5918103fec 100644
--- a/library/std/src/sys/windows/args.rs
+++ b/library/std/src/sys/windows/args.rs
@@ -8,12 +8,14 @@ mod tests;
 
 use crate::ffi::OsString;
 use crate::fmt;
+use crate::io;
 use crate::marker::PhantomData;
 use crate::num::NonZeroU16;
 use crate::os::windows::prelude::*;
 use crate::path::PathBuf;
 use crate::ptr::NonNull;
 use crate::sys::c;
+use crate::sys::process::ensure_no_nuls;
 use crate::sys::windows::os::current_exe;
 use crate::vec;
 
@@ -234,3 +236,160 @@ impl Iterator for WStrUnits<'_> {
         }
     }
 }
+
+#[derive(Debug)]
+pub(crate) enum Arg {
+    /// Add quotes (if needed)
+    Regular(OsString),
+    /// Append raw string without quoting
+    Raw(OsString),
+}
+
+enum Quote {
+    // Every arg is quoted
+    Always,
+    // Whitespace and empty args are quoted
+    Auto,
+    // Arg appended without any changes (#29494)
+    Never,
+}
+
+pub(crate) fn append_arg(cmd: &mut Vec<u16>, arg: &Arg, force_quotes: bool) -> io::Result<()> {
+    let (arg, quote) = match arg {
+        Arg::Regular(arg) => (arg, if force_quotes { Quote::Always } else { Quote::Auto }),
+        Arg::Raw(arg) => (arg, Quote::Never),
+    };
+
+    // If an argument has 0 characters then we need to quote it to ensure
+    // that it actually gets passed through on the command line or otherwise
+    // it will be dropped entirely when parsed on the other end.
+    ensure_no_nuls(arg)?;
+    let arg_bytes = arg.bytes();
+    let (quote, escape) = match quote {
+        Quote::Always => (true, true),
+        Quote::Auto => {
+            (arg_bytes.iter().any(|c| *c == b' ' || *c == b'\t') || arg_bytes.is_empty(), true)
+        }
+        Quote::Never => (false, false),
+    };
+    if quote {
+        cmd.push('"' as u16);
+    }
+
+    let mut backslashes: usize = 0;
+    for x in arg.encode_wide() {
+        if escape {
+            if x == '\\' as u16 {
+                backslashes += 1;
+            } else {
+                if x == '"' as u16 {
+                    // Add n+1 backslashes to total 2n+1 before internal '"'.
+                    cmd.extend((0..=backslashes).map(|_| '\\' as u16));
+                }
+                backslashes = 0;
+            }
+        }
+        cmd.push(x);
+    }
+
+    if quote {
+        // Add n backslashes to total 2n before ending '"'.
+        cmd.extend((0..backslashes).map(|_| '\\' as u16));
+        cmd.push('"' as u16);
+    }
+    Ok(())
+}
+
+pub(crate) fn make_bat_command_line(
+    script: &[u16],
+    args: &[Arg],
+    force_quotes: bool,
+) -> io::Result<Vec<u16>> {
+    // Set the start of the command line to `cmd.exe /c "`
+    // It is necessary to surround the command in an extra pair of quotes,
+    // hence the trailing quote here. It will be closed after all arguments
+    // have been added.
+    let mut cmd: Vec<u16> = "cmd.exe /c \"".encode_utf16().collect();
+
+    // Push the script name surrounded by its quote pair.
+    cmd.push(b'"' as u16);
+    // Windows file names cannot contain a `"` character or end with `\\`.
+    // If the script name does then return an error.
+    if script.contains(&(b'"' as u16)) || script.last() == Some(&(b'\\' as u16)) {
+        return Err(io::const_io_error!(
+            io::ErrorKind::InvalidInput,
+            "Windows file names may not contain `\"` or end with `\\`"
+        ));
+    }
+    cmd.extend_from_slice(script.strip_suffix(&[0]).unwrap_or(script));
+    cmd.push(b'"' as u16);
+
+    // Append the arguments.
+    // FIXME: This needs tests to ensure that the arguments are properly
+    // reconstructed by the batch script by default.
+    for arg in args {
+        cmd.push(' ' as u16);
+        append_arg(&mut cmd, arg, force_quotes)?;
+    }
+
+    // Close the quote we left opened earlier.
+    cmd.push(b'"' as u16);
+
+    Ok(cmd)
+}
+
+/// Takes a path and tries to return a non-verbatim path.
+///
+/// This is necessary because cmd.exe does not support verbatim paths.
+pub(crate) fn to_user_path(mut path: Vec<u16>) -> io::Result<Vec<u16>> {
+    use crate::ptr;
+    use crate::sys::windows::fill_utf16_buf;
+
+    // UTF-16 encoded code points, used in parsing and building UTF-16 paths.
+    // All of these are in the ASCII range so they can be cast directly to `u16`.
+    const SEP: u16 = b'\\' as _;
+    const QUERY: u16 = b'?' as _;
+    const COLON: u16 = b':' as _;
+    const U: u16 = b'U' as _;
+    const N: u16 = b'N' as _;
+    const C: u16 = b'C' as _;
+
+    // Early return if the path is too long to remove the verbatim prefix.
+    const LEGACY_MAX_PATH: usize = 260;
+    if path.len() > LEGACY_MAX_PATH {
+        return Ok(path);
+    }
+
+    match &path[..] {
+        // `\\?\C:\...` => `C:\...`
+        [SEP, SEP, QUERY, SEP, _, COLON, SEP, ..] => unsafe {
+            let lpfilename = path[4..].as_ptr();
+            fill_utf16_buf(
+                |buffer, size| c::GetFullPathNameW(lpfilename, size, buffer, ptr::null_mut()),
+                |full_path: &[u16]| {
+                    if full_path == &path[4..path.len() - 1] { full_path.into() } else { path }
+                },
+            )
+        },
+        // `\\?\UNC\...` => `\\...`
+        [SEP, SEP, QUERY, SEP, U, N, C, SEP, ..] => unsafe {
+            // Change the `C` in `UNC\` to `\` so we can get a slice that starts with `\\`.
+            path[6] = b'\\' as u16;
+            let lpfilename = path[6..].as_ptr();
+            fill_utf16_buf(
+                |buffer, size| c::GetFullPathNameW(lpfilename, size, buffer, ptr::null_mut()),
+                |full_path: &[u16]| {
+                    if full_path == &path[6..path.len() - 1] {
+                        full_path.into()
+                    } else {
+                        // Restore the 'C' in "UNC".
+                        path[6] = b'C' as u16;
+                        path
+                    }
+                },
+            )
+        },
+        // For everything else, leave the path unchanged.
+        _ => Ok(path),
+    }
+}
diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs
index 5f14edaf067..0692da1d795 100644
--- a/library/std/src/sys/windows/c.rs
+++ b/library/std/src/sys/windows/c.rs
@@ -1022,6 +1022,12 @@ extern "system" {
         bWaitAll: BOOL,
         dwMilliseconds: DWORD,
     ) -> DWORD;
+    pub fn CreatePipe(
+        hReadPipe: *mut HANDLE,
+        hWritePipe: *mut HANDLE,
+        lpPipeAttributes: *const SECURITY_ATTRIBUTES,
+        nSize: DWORD,
+    ) -> BOOL;
     pub fn CreateNamedPipeW(
         lpName: LPCWSTR,
         dwOpenMode: DWORD,
diff --git a/library/std/src/sys/windows/handle.rs b/library/std/src/sys/windows/handle.rs
index ef9a8bd6900..3b609825a79 100644
--- a/library/std/src/sys/windows/handle.rs
+++ b/library/std/src/sys/windows/handle.rs
@@ -221,6 +221,10 @@ impl Handle {
         Ok(Self(self.0.duplicate(access, inherit, options)?))
     }
 
+    pub(crate) fn set_inheritable(&self) -> io::Result<()> {
+        self.0.set_inheritable()
+    }
+
     /// Performs a synchronous read.
     ///
     /// If the handle is opened for asynchronous I/O then this abort the process.
diff --git a/library/std/src/sys/windows/mod.rs b/library/std/src/sys/windows/mod.rs
index 31c7208bbf1..4e9d408291d 100644
--- a/library/std/src/sys/windows/mod.rs
+++ b/library/std/src/sys/windows/mod.rs
@@ -156,7 +156,13 @@ pub fn unrolled_find_u16s(needle: u16, haystack: &[u16]) -> Option<usize> {
 
 pub fn to_u16s<S: AsRef<OsStr>>(s: S) -> crate::io::Result<Vec<u16>> {
     fn inner(s: &OsStr) -> crate::io::Result<Vec<u16>> {
-        let mut maybe_result: Vec<u16> = s.encode_wide().collect();
+        // Most paths are ASCII, so reserve capacity for as much as there are bytes
+        // in the OsStr plus one for the null-terminating character. We are not
+        // wasting bytes here as paths created by this function are primarily used
+        // in an ephemeral fashion.
+        let mut maybe_result = Vec::with_capacity(s.len() + 1);
+        maybe_result.extend(s.encode_wide());
+
         if unrolled_find_u16s(0, &maybe_result).is_some() {
             return Err(crate::io::const_io_error!(
                 ErrorKind::InvalidInput,
@@ -190,6 +196,10 @@ where
 {
     // Start off with a stack buf but then spill over to the heap if we end up
     // needing more space.
+    //
+    // This initial size also works around `GetFullPathNameW` returning
+    // incorrect size hints for some short paths:
+    // https://github.com/dylni/normpath/issues/5
     let mut stack_buf = [0u16; 512];
     let mut heap_buf = Vec::new();
     unsafe {
diff --git a/library/std/src/sys/windows/path.rs b/library/std/src/sys/windows/path.rs
index e54fcaed495..a0f82207099 100644
--- a/library/std/src/sys/windows/path.rs
+++ b/library/std/src/sys/windows/path.rs
@@ -50,37 +50,101 @@ pub(crate) fn append_suffix(path: PathBuf, suffix: &OsStr) -> PathBuf {
     path.into()
 }
 
+struct PrefixParser<'a, const LEN: usize> {
+    path: &'a OsStr,
+    prefix: [u8; LEN],
+}
+
+impl<'a, const LEN: usize> PrefixParser<'a, LEN> {
+    #[inline]
+    fn get_prefix(path: &OsStr) -> [u8; LEN] {
+        let mut prefix = [0; LEN];
+        // SAFETY: Only ASCII characters are modified.
+        for (i, &ch) in path.bytes().iter().take(LEN).enumerate() {
+            prefix[i] = if ch == b'/' { b'\\' } else { ch };
+        }
+        prefix
+    }
+
+    fn new(path: &'a OsStr) -> Self {
+        Self { path, prefix: Self::get_prefix(path) }
+    }
+
+    fn as_slice(&self) -> PrefixParserSlice<'a, '_> {
+        PrefixParserSlice {
+            path: self.path,
+            prefix: &self.prefix[..LEN.min(self.path.len())],
+            index: 0,
+        }
+    }
+}
+
+struct PrefixParserSlice<'a, 'b> {
+    path: &'a OsStr,
+    prefix: &'b [u8],
+    index: usize,
+}
+
+impl<'a> PrefixParserSlice<'a, '_> {
+    fn strip_prefix(&self, prefix: &str) -> Option<Self> {
+        self.prefix[self.index..]
+            .starts_with(prefix.as_bytes())
+            .then(|| Self { index: self.index + prefix.len(), ..*self })
+    }
+
+    fn prefix_bytes(&self) -> &'a [u8] {
+        &self.path.bytes()[..self.index]
+    }
+
+    fn finish(self) -> &'a OsStr {
+        // SAFETY: The unsafety here stems from converting between &OsStr and
+        // &[u8] and back. This is safe to do because (1) we only look at ASCII
+        // contents of the encoding and (2) new &OsStr values are produced only
+        // from ASCII-bounded slices of existing &OsStr values.
+        unsafe { bytes_as_os_str(&self.path.bytes()[self.index..]) }
+    }
+}
+
 pub fn parse_prefix(path: &OsStr) -> Option<Prefix<'_>> {
     use Prefix::{DeviceNS, Disk, Verbatim, VerbatimDisk, VerbatimUNC, UNC};
 
-    if let Some(path) = strip_prefix(path, r"\\") {
+    let parser = PrefixParser::<8>::new(path);
+    let parser = parser.as_slice();
+    if let Some(parser) = parser.strip_prefix(r"\\") {
         // \\
-        if let Some(path) = strip_prefix(path, r"?\") {
+
+        // The meaning of verbatim paths can change when they use a different
+        // separator.
+        if let Some(parser) = parser.strip_prefix(r"?\") && !parser.prefix_bytes().iter().any(|&x| x == b'/') {
             // \\?\
-            if let Some(path) = strip_prefix(path, r"UNC\") {
+            if let Some(parser) = parser.strip_prefix(r"UNC\") {
                 // \\?\UNC\server\share
 
+                let path = parser.finish();
                 let (server, path) = parse_next_component(path, true);
                 let (share, _) = parse_next_component(path, true);
 
                 Some(VerbatimUNC(server, share))
             } else {
-                let (prefix, _) = parse_next_component(path, true);
+                let path = parser.finish();
 
                 // in verbatim paths only recognize an exact drive prefix
-                if let Some(drive) = parse_drive_exact(prefix) {
+                if let Some(drive) = parse_drive_exact(path) {
                     // \\?\C:
                     Some(VerbatimDisk(drive))
                 } else {
                     // \\?\prefix
+                    let (prefix, _) = parse_next_component(path, true);
                     Some(Verbatim(prefix))
                 }
             }
-        } else if let Some(path) = strip_prefix(path, r".\") {
+        } else if let Some(parser) = parser.strip_prefix(r".\") {
             // \\.\COM42
+            let path = parser.finish();
             let (prefix, _) = parse_next_component(path, false);
             Some(DeviceNS(prefix))
         } else {
+            let path = parser.finish();
             let (server, path) = parse_next_component(path, false);
             let (share, _) = parse_next_component(path, false);
 
@@ -102,31 +166,26 @@ pub fn parse_prefix(path: &OsStr) -> Option<Prefix<'_>> {
 }
 
 // Parses a drive prefix, e.g. "C:" and "C:\whatever"
-fn parse_drive(prefix: &OsStr) -> Option<u8> {
+fn parse_drive(path: &OsStr) -> Option<u8> {
     // In most DOS systems, it is not possible to have more than 26 drive letters.
     // See <https://en.wikipedia.org/wiki/Drive_letter_assignment#Common_assignments>.
     fn is_valid_drive_letter(drive: &u8) -> bool {
         drive.is_ascii_alphabetic()
     }
 
-    match prefix.bytes() {
+    match path.bytes() {
         [drive, b':', ..] if is_valid_drive_letter(drive) => Some(drive.to_ascii_uppercase()),
         _ => None,
     }
 }
 
 // Parses a drive prefix exactly, e.g. "C:"
-fn parse_drive_exact(prefix: &OsStr) -> Option<u8> {
+fn parse_drive_exact(path: &OsStr) -> Option<u8> {
     // only parse two bytes: the drive letter and the drive separator
-    if prefix.len() == 2 { parse_drive(prefix) } else { None }
-}
-
-fn strip_prefix<'a>(path: &'a OsStr, prefix: &str) -> Option<&'a OsStr> {
-    // `path` and `prefix` are valid wtf8 and utf8 encoded slices respectively, `path[prefix.len()]`
-    // is thus a code point boundary and `path[prefix.len()..]` is a valid wtf8 encoded slice.
-    match path.bytes().strip_prefix(prefix.as_bytes()) {
-        Some(path) => unsafe { Some(bytes_as_os_str(path)) },
-        None => None,
+    if path.bytes().get(2).map(|&x| is_sep_byte(x)).unwrap_or(true) {
+        parse_drive(path)
+    } else {
+        None
     }
 }
 
@@ -219,15 +278,7 @@ pub(crate) fn maybe_verbatim(path: &Path) -> io::Result<Vec<u16>> {
         // SAFETY: `fill_utf16_buf` ensures the `buffer` and `size` are valid.
         // `lpfilename` is a pointer to a null terminated string that is not
         // invalidated until after `GetFullPathNameW` returns successfully.
-        |buffer, size| unsafe {
-            // While the docs for `GetFullPathNameW` have the standard note
-            // about needing a `\\?\` path for a long lpfilename, this does not
-            // appear to be true in practice.
-            // See:
-            // https://stackoverflow.com/questions/38036943/getfullpathnamew-and-long-windows-file-paths
-            // https://googleprojectzero.blogspot.com/2016/02/the-definitive-guide-on-win32-to-nt.html
-            c::GetFullPathNameW(lpfilename, size, buffer, ptr::null_mut())
-        },
+        |buffer, size| unsafe { c::GetFullPathNameW(lpfilename, size, buffer, ptr::null_mut()) },
         |mut absolute| {
             path.clear();
 
@@ -263,9 +314,20 @@ pub(crate) fn maybe_verbatim(path: &Path) -> io::Result<Vec<u16>> {
 
 /// Make a Windows path absolute.
 pub(crate) fn absolute(path: &Path) -> io::Result<PathBuf> {
-    if path.as_os_str().bytes().starts_with(br"\\?\") {
-        return Ok(path.into());
+    let path = path.as_os_str();
+    let prefix = parse_prefix(path);
+    // Verbatim paths should not be modified.
+    if prefix.map(|x| x.is_verbatim()).unwrap_or(false) {
+        // NULs in verbatim paths are rejected for consistency.
+        if path.bytes().contains(&0) {
+            return Err(io::const_io_error!(
+                io::ErrorKind::InvalidInput,
+                "strings passed to WinAPI cannot contain NULs",
+            ));
+        }
+        return Ok(path.to_owned().into());
     }
+
     let path = to_u16s(path)?;
     let lpfilename = path.as_ptr();
     fill_utf16_buf(
diff --git a/library/std/src/sys/windows/path/tests.rs b/library/std/src/sys/windows/path/tests.rs
index 425c2011b32..8656b04e4f4 100644
--- a/library/std/src/sys/windows/path/tests.rs
+++ b/library/std/src/sys/windows/path/tests.rs
@@ -94,3 +94,23 @@ fn verbatim() {
     // A path that contains null is not a valid path.
     assert!(maybe_verbatim(Path::new("\0")).is_err());
 }
+
+fn parse_prefix(path: &str) -> Option<Prefix<'_>> {
+    super::parse_prefix(OsStr::new(path))
+}
+
+#[test]
+fn test_parse_prefix_verbatim() {
+    let prefix = Some(Prefix::VerbatimDisk(b'C'));
+    assert_eq!(prefix, parse_prefix(r"\\?\C:/windows/system32/notepad.exe"));
+    assert_eq!(prefix, parse_prefix(r"\\?\C:\windows\system32\notepad.exe"));
+}
+
+#[test]
+fn test_parse_prefix_verbatim_device() {
+    let prefix = Some(Prefix::UNC(OsStr::new("?"), OsStr::new("C:")));
+    assert_eq!(prefix, parse_prefix(r"//?/C:/windows/system32/notepad.exe"));
+    assert_eq!(prefix, parse_prefix(r"//?/C:\windows\system32\notepad.exe"));
+    assert_eq!(prefix, parse_prefix(r"/\?\C:\windows\system32\notepad.exe"));
+    assert_eq!(prefix, parse_prefix(r"\\?/C:\windows\system32\notepad.exe"));
+}
diff --git a/library/std/src/sys/windows/pipe.rs b/library/std/src/sys/windows/pipe.rs
index 013c776c476..928bf2439c3 100644
--- a/library/std/src/sys/windows/pipe.rs
+++ b/library/std/src/sys/windows/pipe.rs
@@ -18,13 +18,20 @@ use crate::sys_common::IntoInner;
 // Anonymous pipes
 ////////////////////////////////////////////////////////////////////////////////
 
-pub struct AnonPipe {
-    inner: Handle,
+// A 64kb pipe capacity is the same as a typical Linux default.
+const PIPE_BUFFER_CAPACITY: u32 = 64 * 1024;
+
+pub enum AnonPipe {
+    Sync(Handle),
+    Async(Handle),
 }
 
 impl IntoInner<Handle> for AnonPipe {
     fn into_inner(self) -> Handle {
-        self.inner
+        match self {
+            Self::Sync(handle) => handle,
+            Self::Async(handle) => handle,
+        }
     }
 }
 
@@ -32,6 +39,35 @@ pub struct Pipes {
     pub ours: AnonPipe,
     pub theirs: AnonPipe,
 }
+impl Pipes {
+    /// Create a new pair of pipes where both pipes are synchronous.
+    ///
+    /// These must not be used asynchronously.
+    pub fn new_synchronous(
+        ours_readable: bool,
+        their_handle_inheritable: bool,
+    ) -> io::Result<Self> {
+        unsafe {
+            // If `CreatePipe` succeeds, these will be our pipes.
+            let mut read = ptr::null_mut();
+            let mut write = ptr::null_mut();
+
+            if c::CreatePipe(&mut read, &mut write, ptr::null(), PIPE_BUFFER_CAPACITY) == 0 {
+                Err(io::Error::last_os_error())
+            } else {
+                let (ours, theirs) = if ours_readable { (read, write) } else { (write, read) };
+                let ours = Handle::from_raw_handle(ours);
+                let theirs = Handle::from_raw_handle(theirs);
+
+                if their_handle_inheritable {
+                    theirs.set_inheritable()?;
+                }
+
+                Ok(Pipes { ours: AnonPipe::Sync(ours), theirs: AnonPipe::Sync(theirs) })
+            }
+        }
+    }
+}
 
 /// Although this looks similar to `anon_pipe` in the Unix module it's actually
 /// subtly different. Here we'll return two pipes in the `Pipes` return value,
@@ -53,9 +89,6 @@ pub struct Pipes {
 /// with `OVERLAPPED` instances, but also works out ok if it's only ever used
 /// once at a time (which we do indeed guarantee).
 pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Result<Pipes> {
-    // A 64kb pipe capacity is the same as a typical Linux default.
-    const PIPE_BUFFER_CAPACITY: u32 = 64 * 1024;
-
     // Note that we specifically do *not* use `CreatePipe` here because
     // unfortunately the anonymous pipes returned do not support overlapped
     // operations. Instead, we create a "hopefully unique" name and create a
@@ -156,12 +189,9 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res
         };
         opts.security_attributes(&mut sa);
         let theirs = File::open(Path::new(&name), &opts)?;
-        let theirs = AnonPipe { inner: theirs.into_inner() };
+        let theirs = AnonPipe::Sync(theirs.into_inner());
 
-        Ok(Pipes {
-            ours: AnonPipe { inner: ours },
-            theirs: AnonPipe { inner: theirs.into_inner() },
-        })
+        Ok(Pipes { ours: AnonPipe::Async(ours), theirs })
     }
 }
 
@@ -171,12 +201,12 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res
 /// This is achieved by creating a new set of pipes and spawning a thread that
 /// relays messages between the source and the synchronous pipe.
 pub fn spawn_pipe_relay(
-    source: &AnonPipe,
+    source: &Handle,
     ours_readable: bool,
     their_handle_inheritable: bool,
 ) -> io::Result<AnonPipe> {
     // We need this handle to live for the lifetime of the thread spawned below.
-    let source = source.duplicate()?;
+    let source = AnonPipe::Async(source.duplicate(0, true, c::DUPLICATE_SAME_ACCESS)?);
 
     // create a new pair of anon pipes.
     let Pipes { theirs, ours } = anon_pipe(ours_readable, their_handle_inheritable)?;
@@ -227,19 +257,24 @@ type AlertableIoFn = unsafe extern "system" fn(
 
 impl AnonPipe {
     pub fn handle(&self) -> &Handle {
-        &self.inner
+        match self {
+            Self::Async(ref handle) => handle,
+            Self::Sync(ref handle) => handle,
+        }
     }
     pub fn into_handle(self) -> Handle {
-        self.inner
-    }
-    fn duplicate(&self) -> io::Result<Self> {
-        self.inner.duplicate(0, false, c::DUPLICATE_SAME_ACCESS).map(|inner| AnonPipe { inner })
+        self.into_inner()
     }
 
     pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
         let result = unsafe {
             let len = crate::cmp::min(buf.len(), c::DWORD::MAX as usize) as c::DWORD;
-            self.alertable_io_internal(c::ReadFileEx, buf.as_mut_ptr() as _, len)
+            match self {
+                Self::Sync(ref handle) => handle.read(buf),
+                Self::Async(_) => {
+                    self.alertable_io_internal(c::ReadFileEx, buf.as_mut_ptr() as _, len)
+                }
+            }
         };
 
         match result {
@@ -253,28 +288,33 @@ impl AnonPipe {
     }
 
     pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
-        self.inner.read_vectored(bufs)
+        io::default_read_vectored(|buf| self.read(buf), bufs)
     }
 
     #[inline]
     pub fn is_read_vectored(&self) -> bool {
-        self.inner.is_read_vectored()
+        false
     }
 
     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
         unsafe {
             let len = crate::cmp::min(buf.len(), c::DWORD::MAX as usize) as c::DWORD;
-            self.alertable_io_internal(c::WriteFileEx, buf.as_ptr() as _, len)
+            match self {
+                Self::Sync(ref handle) => handle.write(buf),
+                Self::Async(_) => {
+                    self.alertable_io_internal(c::WriteFileEx, buf.as_ptr() as _, len)
+                }
+            }
         }
     }
 
     pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
-        self.inner.write_vectored(bufs)
+        io::default_write_vectored(|buf| self.write(buf), bufs)
     }
 
     #[inline]
     pub fn is_write_vectored(&self) -> bool {
-        self.inner.is_write_vectored()
+        false
     }
 
     /// Synchronizes asynchronous reads or writes using our anonymous pipe.
@@ -346,7 +386,7 @@ impl AnonPipe {
 
         // Asynchronous read of the pipe.
         // If successful, `callback` will be called once it completes.
-        let result = io(self.inner.as_handle(), buf, len, &mut overlapped, callback);
+        let result = io(self.handle().as_handle(), buf, len, &mut overlapped, callback);
         if result == c::FALSE {
             // We can return here because the call failed.
             // After this we must not return until the I/O completes.
diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs
index a0c0f5dc3ec..cc29d1a72fb 100644
--- a/library/std/src/sys/windows/process.rs
+++ b/library/std/src/sys/windows/process.rs
@@ -17,17 +17,18 @@ use crate::os::windows::ffi::{OsStrExt, OsStringExt};
 use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle};
 use crate::path::{Path, PathBuf};
 use crate::ptr;
+use crate::sys::args::{self, Arg};
 use crate::sys::c;
 use crate::sys::c::NonZeroDWORD;
 use crate::sys::cvt;
 use crate::sys::fs::{File, OpenOptions};
 use crate::sys::handle::Handle;
 use crate::sys::path;
-use crate::sys::pipe::{self, AnonPipe};
+use crate::sys::pipe::{self, AnonPipe, Pipes};
 use crate::sys::stdio;
 use crate::sys_common::mutex::StaticMutex;
 use crate::sys_common::process::{CommandEnv, CommandEnvs};
-use crate::sys_common::{AsInner, IntoInner};
+use crate::sys_common::IntoInner;
 
 use libc::{c_void, EXIT_FAILURE, EXIT_SUCCESS};
 
@@ -147,7 +148,7 @@ impl AsRef<OsStr> for EnvKey {
     }
 }
 
-fn ensure_no_nuls<T: AsRef<OsStr>>(str: T) -> io::Result<T> {
+pub(crate) fn ensure_no_nuls<T: AsRef<OsStr>>(str: T) -> io::Result<T> {
     if str.as_ref().encode_wide().any(|b| b == 0) {
         Err(io::const_io_error!(ErrorKind::InvalidInput, "nul byte found in provided data"))
     } else {
@@ -172,7 +173,7 @@ pub enum Stdio {
     Inherit,
     Null,
     MakePipe,
-    Pipe(AnonPipe),
+    AsyncPipe(Handle),
     Handle(Handle),
 }
 
@@ -182,14 +183,6 @@ pub struct StdioPipes {
     pub stderr: Option<AnonPipe>,
 }
 
-#[derive(Debug)]
-enum Arg {
-    /// Add quotes (if needed)
-    Regular(OsString),
-    /// Append raw string without quoting
-    Raw(OsString),
-}
-
 impl Command {
     pub fn new(program: &OsStr) -> Command {
         Command {
@@ -275,8 +268,19 @@ impl Command {
             program.len().checked_sub(5).and_then(|i| program.get(i..)),
             Some([46, 98 | 66, 97 | 65, 116 | 84, 0] | [46, 99 | 67, 109 | 77, 100 | 68, 0])
         );
-        let mut cmd_str =
-            make_command_line(&program, &self.args, self.force_quotes_enabled, is_batch_file)?;
+        let (program, mut cmd_str) = if is_batch_file {
+            (
+                command_prompt()?,
+                args::make_bat_command_line(
+                    &args::to_user_path(program)?,
+                    &self.args,
+                    self.force_quotes_enabled,
+                )?,
+            )
+        } else {
+            let cmd_str = make_command_line(&self.program, &self.args, self.force_quotes_enabled)?;
+            (program, cmd_str)
+        };
         cmd_str.push(0); // add null terminator
 
         // stolen from the libuv code.
@@ -523,13 +527,33 @@ impl Stdio {
             },
 
             Stdio::MakePipe => {
-                let ours_readable = stdio_id != c::STD_INPUT_HANDLE;
-                let pipes = pipe::anon_pipe(ours_readable, true)?;
+                // Handles that are passed to a child process must be synchronous
+                // because they will be read synchronously (see #95759).
+                // Therefore we prefer to make both ends of a pipe synchronous
+                // just in case our end of the pipe is passed to another process.
+                //
+                // However, we may need to read from both the child's stdout and
+                // stderr simultaneously when waiting for output. This requires
+                // async reads so as to avoid blocking either pipe.
+                //
+                // The solution used here is to make handles synchronous
+                // except for our side of the stdout and sterr pipes.
+                // If our side of those pipes do end up being given to another
+                // process then we use a "pipe relay" to synchronize access
+                // (see `Stdio::AsyncPipe` below).
+                let pipes = if stdio_id == c::STD_INPUT_HANDLE {
+                    // For stdin both sides of the pipe are synchronous.
+                    Pipes::new_synchronous(false, true)?
+                } else {
+                    // For stdout/stderr our side of the pipe is async and their side is synchronous.
+                    pipe::anon_pipe(true, true)?
+                };
                 *pipe = Some(pipes.ours);
                 Ok(pipes.theirs.into_handle())
             }
 
-            Stdio::Pipe(ref source) => {
+            Stdio::AsyncPipe(ref source) => {
+                // We need to synchronize asynchronous pipes by using a pipe relay.
                 let ours_readable = stdio_id != c::STD_INPUT_HANDLE;
                 pipe::spawn_pipe_relay(source, ours_readable, true).map(AnonPipe::into_handle)
             }
@@ -558,7 +582,13 @@ impl Stdio {
 
 impl From<AnonPipe> for Stdio {
     fn from(pipe: AnonPipe) -> Stdio {
-        Stdio::Pipe(pipe)
+        // Note that it's very important we don't give async handles to child processes.
+        // Therefore if the pipe is asynchronous we must have a way to turn it synchronous.
+        // See #95759.
+        match pipe {
+            AnonPipe::Sync(handle) => Stdio::Handle(handle),
+            AnonPipe::Async(handle) => Stdio::AsyncPipe(handle),
+        }
     }
 }
 
@@ -730,96 +760,36 @@ fn zeroed_process_information() -> c::PROCESS_INFORMATION {
     }
 }
 
-enum Quote {
-    // Every arg is quoted
-    Always,
-    // Whitespace and empty args are quoted
-    Auto,
-    // Arg appended without any changes (#29494)
-    Never,
-}
-
 // Produces a wide string *without terminating null*; returns an error if
 // `prog` or any of the `args` contain a nul.
-fn make_command_line(
-    prog: &[u16],
-    args: &[Arg],
-    force_quotes: bool,
-    is_batch_file: bool,
-) -> io::Result<Vec<u16>> {
+fn make_command_line(argv0: &OsStr, args: &[Arg], force_quotes: bool) -> io::Result<Vec<u16>> {
     // Encode the command and arguments in a command line string such
     // that the spawned process may recover them using CommandLineToArgvW.
     let mut cmd: Vec<u16> = Vec::new();
 
-    // CreateFileW has special handling for .bat and .cmd files, which means we
-    // need to add an extra pair of quotes surrounding the whole command line
-    // so they are properly passed on to the script.
-    // See issue #91991.
-    if is_batch_file {
-        cmd.push(b'"' as u16);
-    }
-
     // Always quote the program name so CreateProcess to avoid ambiguity when
     // the child process parses its arguments.
     // Note that quotes aren't escaped here because they can't be used in arg0.
     // But that's ok because file paths can't contain quotes.
     cmd.push(b'"' as u16);
-    cmd.extend_from_slice(prog.strip_suffix(&[0]).unwrap_or(prog));
+    cmd.extend(argv0.encode_wide());
     cmd.push(b'"' as u16);
 
     for arg in args {
         cmd.push(' ' as u16);
-        let (arg, quote) = match arg {
-            Arg::Regular(arg) => (arg, if force_quotes { Quote::Always } else { Quote::Auto }),
-            Arg::Raw(arg) => (arg, Quote::Never),
-        };
-        append_arg(&mut cmd, arg, quote)?;
-    }
-    if is_batch_file {
-        cmd.push(b'"' as u16);
-    }
-    return Ok(cmd);
-
-    fn append_arg(cmd: &mut Vec<u16>, arg: &OsStr, quote: Quote) -> io::Result<()> {
-        // If an argument has 0 characters then we need to quote it to ensure
-        // that it actually gets passed through on the command line or otherwise
-        // it will be dropped entirely when parsed on the other end.
-        ensure_no_nuls(arg)?;
-        let arg_bytes = &arg.as_inner().inner.as_inner();
-        let (quote, escape) = match quote {
-            Quote::Always => (true, true),
-            Quote::Auto => {
-                (arg_bytes.iter().any(|c| *c == b' ' || *c == b'\t') || arg_bytes.is_empty(), true)
-            }
-            Quote::Never => (false, false),
-        };
-        if quote {
-            cmd.push('"' as u16);
-        }
-
-        let mut backslashes: usize = 0;
-        for x in arg.encode_wide() {
-            if escape {
-                if x == '\\' as u16 {
-                    backslashes += 1;
-                } else {
-                    if x == '"' as u16 {
-                        // Add n+1 backslashes to total 2n+1 before internal '"'.
-                        cmd.extend((0..=backslashes).map(|_| '\\' as u16));
-                    }
-                    backslashes = 0;
-                }
-            }
-            cmd.push(x);
-        }
-
-        if quote {
-            // Add n backslashes to total 2n before ending '"'.
-            cmd.extend((0..backslashes).map(|_| '\\' as u16));
-            cmd.push('"' as u16);
-        }
-        Ok(())
+        args::append_arg(&mut cmd, arg, force_quotes)?;
     }
+    Ok(cmd)
+}
+
+// Get `cmd.exe` for use with bat scripts, encoded as a UTF-16 string.
+fn command_prompt() -> io::Result<Vec<u16>> {
+    let mut system: Vec<u16> = super::fill_utf16_buf(
+        |buf, size| unsafe { c::GetSystemDirectoryW(buf, size) },
+        |buf| buf.into(),
+    )?;
+    system.extend("\\cmd.exe".encode_utf16().chain([0]));
+    Ok(system)
 }
 
 fn make_envp(maybe_env: Option<BTreeMap<EnvKey, OsString>>) -> io::Result<(*mut c_void, Vec<u16>)> {
diff --git a/library/std/src/sys/windows/process/tests.rs b/library/std/src/sys/windows/process/tests.rs
index cd535afb4a3..be3a0f4ed52 100644
--- a/library/std/src/sys/windows/process/tests.rs
+++ b/library/std/src/sys/windows/process/tests.rs
@@ -3,12 +3,11 @@ use super::Arg;
 use crate::env;
 use crate::ffi::{OsStr, OsString};
 use crate::process::Command;
-use crate::sys::to_u16s;
 
 #[test]
 fn test_raw_args() {
     let command_line = &make_command_line(
-        &to_u16s("quoted exe").unwrap(),
+        OsStr::new("quoted exe"),
         &[
             Arg::Regular(OsString::from("quote me")),
             Arg::Raw(OsString::from("quote me *not*")),
@@ -17,7 +16,6 @@ fn test_raw_args() {
             Arg::Regular(OsString::from("optional-quotes")),
         ],
         false,
-        false,
     )
     .unwrap();
     assert_eq!(
@@ -30,10 +28,9 @@ fn test_raw_args() {
 fn test_make_command_line() {
     fn test_wrapper(prog: &str, args: &[&str], force_quotes: bool) -> String {
         let command_line = &make_command_line(
-            &to_u16s(prog).unwrap(),
+            OsStr::new(prog),
             &args.iter().map(|a| Arg::Regular(OsString::from(a))).collect::<Vec<_>>(),
             force_quotes,
-            false,
         )
         .unwrap();
         String::from_utf16(command_line).unwrap()
diff --git a/library/std/src/sys/windows/thread_parker.rs b/library/std/src/sys/windows/thread_parker.rs
index 3497da51dee..51448344475 100644
--- a/library/std/src/sys/windows/thread_parker.rs
+++ b/library/std/src/sys/windows/thread_parker.rs
@@ -58,6 +58,7 @@
 // [4]: Windows Internals, Part 1, ISBN 9780735671300
 
 use crate::convert::TryFrom;
+use crate::pin::Pin;
 use crate::ptr;
 use crate::sync::atomic::{
     AtomicI8, AtomicPtr,
@@ -95,13 +96,16 @@ const NOTIFIED: i8 = 1;
 // Ordering::Release when writing NOTIFIED (the 'token') in unpark(), and using
 // Ordering::Acquire when reading this state in park() after waking up.
 impl Parker {
-    pub fn new() -> Self {
-        Self { state: AtomicI8::new(EMPTY) }
+    /// Construct the Windows parker. The UNIX parker implementation
+    /// requires this to happen in-place.
+    pub unsafe fn new(parker: *mut Parker) {
+        parker.write(Self { state: AtomicI8::new(EMPTY) });
     }
 
     // Assumes this is only called by the thread that owns the Parker,
-    // which means that `self.state != PARKED`.
-    pub unsafe fn park(&self) {
+    // which means that `self.state != PARKED`. This implementation doesn't require `Pin`,
+    // but other implementations do.
+    pub unsafe fn park(self: Pin<&Self>) {
         // Change NOTIFIED=>EMPTY or EMPTY=>PARKED, and directly return in the
         // first case.
         if self.state.fetch_sub(1, Acquire) == NOTIFIED {
@@ -132,8 +136,9 @@ impl Parker {
     }
 
     // Assumes this is only called by the thread that owns the Parker,
-    // which means that `self.state != PARKED`.
-    pub unsafe fn park_timeout(&self, timeout: Duration) {
+    // which means that `self.state != PARKED`. This implementation doesn't require `Pin`,
+    // but other implementations do.
+    pub unsafe fn park_timeout(self: Pin<&Self>, timeout: Duration) {
         // Change NOTIFIED=>EMPTY or EMPTY=>PARKED, and directly return in the
         // first case.
         if self.state.fetch_sub(1, Acquire) == NOTIFIED {
@@ -184,7 +189,8 @@ impl Parker {
         }
     }
 
-    pub fn unpark(&self) {
+    // This implementation doesn't require `Pin`, but other implementations do.
+    pub fn unpark(self: Pin<&Self>) {
         // Change PARKED=>NOTIFIED, EMPTY=>NOTIFIED, or NOTIFIED=>NOTIFIED, and
         // wake the thread in the first case.
         //
diff --git a/library/std/src/sys_common/thread_parker/futex.rs b/library/std/src/sys_common/thread_parker/futex.rs
index fbf6231ff4a..d9e2f39e345 100644
--- a/library/std/src/sys_common/thread_parker/futex.rs
+++ b/library/std/src/sys_common/thread_parker/futex.rs
@@ -1,3 +1,4 @@
+use crate::pin::Pin;
 use crate::sync::atomic::AtomicU32;
 use crate::sync::atomic::Ordering::{Acquire, Release};
 use crate::sys::futex::{futex_wait, futex_wake};
@@ -32,14 +33,15 @@ pub struct Parker {
 // Ordering::Release when writing NOTIFIED (the 'token') in unpark(), and using
 // Ordering::Acquire when checking for this state in park().
 impl Parker {
-    #[inline]
-    pub const fn new() -> Self {
-        Parker { state: AtomicU32::new(EMPTY) }
+    /// Construct the futex parker. The UNIX parker implementation
+    /// requires this to happen in-place.
+    pub unsafe fn new(parker: *mut Parker) {
+        parker.write(Self { state: AtomicU32::new(EMPTY) });
     }
 
     // Assumes this is only called by the thread that owns the Parker,
     // which means that `self.state != PARKED`.
-    pub unsafe fn park(&self) {
+    pub unsafe fn park(self: Pin<&Self>) {
         // Change NOTIFIED=>EMPTY or EMPTY=>PARKED, and directly return in the
         // first case.
         if self.state.fetch_sub(1, Acquire) == NOTIFIED {
@@ -58,8 +60,9 @@ impl Parker {
     }
 
     // Assumes this is only called by the thread that owns the Parker,
-    // which means that `self.state != PARKED`.
-    pub unsafe fn park_timeout(&self, timeout: Duration) {
+    // which means that `self.state != PARKED`. This implementation doesn't
+    // require `Pin`, but other implementations do.
+    pub unsafe fn park_timeout(self: Pin<&Self>, timeout: Duration) {
         // Change NOTIFIED=>EMPTY or EMPTY=>PARKED, and directly return in the
         // first case.
         if self.state.fetch_sub(1, Acquire) == NOTIFIED {
@@ -78,8 +81,9 @@ impl Parker {
         }
     }
 
+    // This implementation doesn't require `Pin`, but other implementations do.
     #[inline]
-    pub fn unpark(&self) {
+    pub fn unpark(self: Pin<&Self>) {
         // Change PARKED=>NOTIFIED, EMPTY=>NOTIFIED, or NOTIFIED=>NOTIFIED, and
         // wake the thread in the first case.
         //
diff --git a/library/std/src/sys_common/thread_parker/generic.rs b/library/std/src/sys_common/thread_parker/generic.rs
index ffb61200e15..f3d8b34d3fd 100644
--- a/library/std/src/sys_common/thread_parker/generic.rs
+++ b/library/std/src/sys_common/thread_parker/generic.rs
@@ -1,5 +1,6 @@
 //! Parker implementation based on a Mutex and Condvar.
 
+use crate::pin::Pin;
 use crate::sync::atomic::AtomicUsize;
 use crate::sync::atomic::Ordering::SeqCst;
 use crate::sync::{Condvar, Mutex};
@@ -16,13 +17,18 @@ pub struct Parker {
 }
 
 impl Parker {
-    pub fn new() -> Self {
-        Parker { state: AtomicUsize::new(EMPTY), lock: Mutex::new(()), cvar: Condvar::new() }
+    /// Construct the generic parker. The UNIX parker implementation
+    /// requires this to happen in-place.
+    pub unsafe fn new(parker: *mut Parker) {
+        parker.write(Parker {
+            state: AtomicUsize::new(EMPTY),
+            lock: Mutex::new(()),
+            cvar: Condvar::new(),
+        });
     }
 
-    // This implementation doesn't require `unsafe`, but other implementations
-    // may assume this is only called by the thread that owns the Parker.
-    pub unsafe fn park(&self) {
+    // This implementation doesn't require `unsafe` and `Pin`, but other implementations do.
+    pub unsafe fn park(self: Pin<&Self>) {
         // If we were previously notified then we consume this notification and
         // return quickly.
         if self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst).is_ok() {
@@ -55,9 +61,8 @@ impl Parker {
         }
     }
 
-    // This implementation doesn't require `unsafe`, but other implementations
-    // may assume this is only called by the thread that owns the Parker.
-    pub unsafe fn park_timeout(&self, dur: Duration) {
+    // This implementation doesn't require `unsafe` and `Pin`, but other implementations do.
+    pub unsafe fn park_timeout(self: Pin<&Self>, dur: Duration) {
         // Like `park` above we have a fast path for an already-notified thread, and
         // afterwards we start coordinating for a sleep.
         // return quickly.
@@ -88,7 +93,8 @@ impl Parker {
         }
     }
 
-    pub fn unpark(&self) {
+    // This implementation doesn't require `Pin`, but other implementations do.
+    pub fn unpark(self: Pin<&Self>) {
         // To ensure the unparked thread will observe any writes we made
         // before this call, we must perform a release operation that `park`
         // can synchronize with. To do that we must write `NOTIFIED` even if
diff --git a/library/std/src/sys_common/thread_parker/mod.rs b/library/std/src/sys_common/thread_parker/mod.rs
index ba896bd676b..ea0204cd357 100644
--- a/library/std/src/sys_common/thread_parker/mod.rs
+++ b/library/std/src/sys_common/thread_parker/mod.rs
@@ -8,6 +8,8 @@ cfg_if::cfg_if! {
         pub use futex::Parker;
     } else if #[cfg(windows)] {
         pub use crate::sys::thread_parker::Parker;
+    } else if #[cfg(target_family = "unix")] {
+        pub use crate::sys::thread_parker::Parker;
     } else {
         mod generic;
         pub use generic::Parker;
diff --git a/library/std/src/sys_common/wtf8.rs b/library/std/src/sys_common/wtf8.rs
index 10ef6662115..7d66973bed6 100644
--- a/library/std/src/sys_common/wtf8.rs
+++ b/library/std/src/sys_common/wtf8.rs
@@ -25,7 +25,7 @@ use crate::char;
 use crate::collections::TryReserveError;
 use crate::fmt;
 use crate::hash::{Hash, Hasher};
-use crate::iter::FromIterator;
+use crate::iter::{FromIterator, FusedIterator};
 use crate::mem;
 use crate::ops;
 use crate::rc::Rc;
@@ -899,6 +899,9 @@ impl<'a> Iterator for EncodeWide<'a> {
     }
 }
 
+#[stable(feature = "encode_wide_fused_iterator", since = "1.62.0")]
+impl FusedIterator for EncodeWide<'_> {}
+
 impl Hash for CodePoint {
     #[inline]
     fn hash<H: Hasher>(&self, state: &mut H) {
diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs
index 4ab8fb2e905..f4750cdf764 100644
--- a/library/std/src/thread/local.rs
+++ b/library/std/src/thread/local.rs
@@ -21,6 +21,8 @@ use crate::fmt;
 /// The [`with`] method yields a reference to the contained value which cannot be
 /// sent across threads or escape the given closure.
 ///
+/// [`thread_local!`]: crate::thread_local
+///
 /// # Initialization and Destruction
 ///
 /// Initialization is dynamically performed on the first call to [`with`]
diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs
index 5309dc47ac4..b4b1037a3cd 100644
--- a/library/std/src/thread/mod.rs
+++ b/library/std/src/thread/mod.rs
@@ -146,6 +146,7 @@
 //! [`Cell`]: crate::cell::Cell
 //! [`RefCell`]: crate::cell::RefCell
 //! [`with`]: LocalKey::with
+//! [`thread_local!`]: crate::thread_local
 
 #![stable(feature = "rust1", since = "1.0.0")]
 #![deny(unsafe_op_in_unsafe_fn)]
@@ -163,6 +164,8 @@ use crate::num::NonZeroU64;
 use crate::num::NonZeroUsize;
 use crate::panic;
 use crate::panicking;
+use crate::pin::Pin;
+use crate::ptr::addr_of_mut;
 use crate::str;
 use crate::sync::Arc;
 use crate::sys::thread as imp;
@@ -923,7 +926,7 @@ pub fn sleep(dur: Duration) {
 pub fn park() {
     // SAFETY: park_timeout is called on the parker owned by this thread.
     unsafe {
-        current().inner.parker.park();
+        current().inner.as_ref().parker().park();
     }
 }
 
@@ -987,7 +990,7 @@ pub fn park_timeout_ms(ms: u32) {
 pub fn park_timeout(dur: Duration) {
     // SAFETY: park_timeout is called on the parker owned by this thread.
     unsafe {
-        current().inner.parker.park_timeout(dur);
+        current().inner.as_ref().parker().park_timeout(dur);
     }
 }
 
@@ -1073,6 +1076,12 @@ struct Inner {
     parker: Parker,
 }
 
+impl Inner {
+    fn parker(self: Pin<&Self>) -> Pin<&Parker> {
+        unsafe { Pin::map_unchecked(self, |inner| &inner.parker) }
+    }
+}
+
 #[derive(Clone)]
 #[stable(feature = "rust1", since = "1.0.0")]
 /// A handle to a thread.
@@ -1094,14 +1103,28 @@ struct Inner {
 ///
 /// [`thread::current`]: current
 pub struct Thread {
-    inner: Arc<Inner>,
+    inner: Pin<Arc<Inner>>,
 }
 
 impl Thread {
     // Used only internally to construct a thread object without spawning
     // Panics if the name contains nuls.
     pub(crate) fn new(name: Option<CString>) -> Thread {
-        Thread { inner: Arc::new(Inner { name, id: ThreadId::new(), parker: Parker::new() }) }
+        // We have to use `unsafe` here to constuct the `Parker` in-place,
+        // which is required for the UNIX implementation.
+        //
+        // SAFETY: We pin the Arc immediately after creation, so its address never
+        // changes.
+        let inner = unsafe {
+            let mut arc = Arc::<Inner>::new_uninit();
+            let ptr = Arc::get_mut_unchecked(&mut arc).as_mut_ptr();
+            addr_of_mut!((*ptr).name).write(name);
+            addr_of_mut!((*ptr).id).write(ThreadId::new());
+            Parker::new(addr_of_mut!((*ptr).parker));
+            Pin::new_unchecked(arc.assume_init())
+        };
+
+        Thread { inner }
     }
 
     /// Atomically makes the handle's token available if it is not already.
@@ -1137,7 +1160,7 @@ impl Thread {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[inline]
     pub fn unpark(&self) {
-        self.inner.parker.unpark();
+        self.inner.as_ref().parker().unpark();
     }
 
     /// Gets the thread's unique identifier.
diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml
index 4c32547f059..dea8d998bde 100644
--- a/src/bootstrap/Cargo.toml
+++ b/src/bootstrap/Cargo.toml
@@ -42,10 +42,12 @@ cc = "1.0.69"
 libc = "0.2"
 serde = { version = "1.0.8", features = ["derive"] }
 serde_json = "1.0.2"
+tar = "0.4"
 toml = "0.5"
 ignore = "0.4.10"
 opener = "0.5"
 once_cell = "1.7.2"
+xz2 = "0.1"
 
 [target.'cfg(windows)'.dependencies.winapi]
 version = "0.3"
diff --git a/src/bootstrap/bin/rustdoc.rs b/src/bootstrap/bin/rustdoc.rs
index 0349277a36b..5f85fc5aa59 100644
--- a/src/bootstrap/bin/rustdoc.rs
+++ b/src/bootstrap/bin/rustdoc.rs
@@ -31,8 +31,8 @@ fn main() {
 
     let mut cmd = Command::new(rustdoc);
 
-    // I am not actually sure why it's necessary to pass the sysroot for `--test`,
-    // but `test --doc --stage 0` is broken without it :(
+    // cfg(bootstrap)
+    // NOTE: the `--test` special-casing can be removed when https://github.com/rust-lang/cargo/pull/10594 lands on beta.
     if target.is_some() || args.iter().any(|x| x == "--test") {
         // The stage0 compiler has a special sysroot distinct from what we
         // actually downloaded, so we just always pass the `--sysroot` option,
@@ -65,13 +65,6 @@ fn main() {
         }
     }
 
-    // Needed to be able to run all rustdoc tests.
-    if let Some(ref x) = env::var_os("RUSTDOC_RESOURCE_SUFFIX") {
-        // This "unstable-options" can be removed when `--resource-suffix` is stabilized
-        cmd.arg("-Z").arg("unstable-options");
-        cmd.arg("--resource-suffix").arg(x);
-    }
-
     if verbose > 1 {
         eprintln!(
             "rustdoc command: {:?}={:?} {:?}",
diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py
index ac1c47524fd..e38a574ca23 100644
--- a/src/bootstrap/bootstrap.py
+++ b/src/bootstrap/bootstrap.py
@@ -500,81 +500,6 @@ class RustBuild(object):
                 with output(self.rustfmt_stamp()) as rustfmt_stamp:
                     rustfmt_stamp.write(self.stage0_rustfmt.channel())
 
-        # Avoid downloading LLVM twice (once for stage0 and once for the master rustc)
-        if self.downloading_llvm() and stage0:
-            # We want the most recent LLVM submodule update to avoid downloading
-            # LLVM more often than necessary.
-            #
-            # This git command finds that commit SHA, looking for bors-authored
-            # commits that modified src/llvm-project or other relevant version
-            # stamp files.
-            #
-            # This works even in a repository that has not yet initialized
-            # submodules.
-            top_level = subprocess.check_output([
-                "git", "rev-parse", "--show-toplevel",
-            ]).decode(sys.getdefaultencoding()).strip()
-            llvm_sha = subprocess.check_output([
-                "git", "rev-list", "--author=bors@rust-lang.org", "-n1",
-                "--first-parent", "HEAD",
-                "--",
-                "{}/src/llvm-project".format(top_level),
-                "{}/src/bootstrap/download-ci-llvm-stamp".format(top_level),
-                # the LLVM shared object file is named `LLVM-12-rust-{version}-nightly`
-                "{}/src/version".format(top_level)
-            ]).decode(sys.getdefaultencoding()).strip()
-            llvm_assertions = self.get_toml('assertions', 'llvm') == 'true'
-            llvm_root = self.llvm_root()
-            llvm_lib = os.path.join(llvm_root, "lib")
-            if self.program_out_of_date(self.llvm_stamp(), llvm_sha + str(llvm_assertions)):
-                self._download_ci_llvm(llvm_sha, llvm_assertions)
-                for binary in ["llvm-config", "FileCheck"]:
-                    self.fix_bin_or_dylib(os.path.join(llvm_root, "bin", binary))
-                for lib in os.listdir(llvm_lib):
-                    if lib.endswith(".so"):
-                        self.fix_bin_or_dylib(os.path.join(llvm_lib, lib))
-                with output(self.llvm_stamp()) as llvm_stamp:
-                    llvm_stamp.write(llvm_sha + str(llvm_assertions))
-
-    def downloading_llvm(self):
-        opt = self.get_toml('download-ci-llvm', 'llvm')
-        # This is currently all tier 1 targets and tier 2 targets with host tools
-        # (since others may not have CI artifacts)
-        # https://doc.rust-lang.org/rustc/platform-support.html#tier-1
-        supported_platforms = [
-            # tier 1
-            "aarch64-unknown-linux-gnu",
-            "i686-pc-windows-gnu",
-            "i686-pc-windows-msvc",
-            "i686-unknown-linux-gnu",
-            "x86_64-unknown-linux-gnu",
-            "x86_64-apple-darwin",
-            "x86_64-pc-windows-gnu",
-            "x86_64-pc-windows-msvc",
-            # tier 2 with host tools
-            "aarch64-apple-darwin",
-            "aarch64-pc-windows-msvc",
-            "aarch64-unknown-linux-musl",
-            "arm-unknown-linux-gnueabi",
-            "arm-unknown-linux-gnueabihf",
-            "armv7-unknown-linux-gnueabihf",
-            "mips-unknown-linux-gnu",
-            "mips64-unknown-linux-gnuabi64",
-            "mips64el-unknown-linux-gnuabi64",
-            "mipsel-unknown-linux-gnu",
-            "powerpc-unknown-linux-gnu",
-            "powerpc64-unknown-linux-gnu",
-            "powerpc64le-unknown-linux-gnu",
-            "riscv64gc-unknown-linux-gnu",
-            "s390x-unknown-linux-gnu",
-            "x86_64-unknown-freebsd",
-            "x86_64-unknown-illumos",
-            "x86_64-unknown-linux-musl",
-            "x86_64-unknown-netbsd",
-        ]
-        return opt == "true" \
-            or (opt == "if-available" and self.build in supported_platforms)
-
     def _download_component_helper(
         self, filename, pattern, tarball_suffix, stage0=True, key=None
     ):
@@ -606,53 +531,6 @@ class RustBuild(object):
             )
         unpack(tarball, tarball_suffix, self.bin_root(stage0), match=pattern, verbose=self.verbose)
 
-    def _download_ci_llvm(self, llvm_sha, llvm_assertions):
-        if not llvm_sha:
-            print("error: could not find commit hash for downloading LLVM")
-            print("help: maybe your repository history is too shallow?")
-            print("help: consider disabling `download-ci-llvm`")
-            print("help: or fetch enough history to include one upstream commit")
-            exit(1)
-        cache_prefix = "llvm-{}-{}".format(llvm_sha, llvm_assertions)
-        cache_dst = os.path.join(self.build_dir, "cache")
-        rustc_cache = os.path.join(cache_dst, cache_prefix)
-        if not os.path.exists(rustc_cache):
-            os.makedirs(rustc_cache)
-
-        base = "https://ci-artifacts.rust-lang.org"
-        url = "rustc-builds/{}".format(llvm_sha)
-        if llvm_assertions:
-            url = url.replace('rustc-builds', 'rustc-builds-alt')
-        # ci-artifacts are only stored as .xz, not .gz
-        if not support_xz():
-            print("error: XZ support is required to download LLVM")
-            print("help: consider disabling `download-ci-llvm` or using python3")
-            exit(1)
-        tarball_suffix = '.tar.xz'
-        filename = "rust-dev-nightly-" + self.build + tarball_suffix
-        tarball = os.path.join(rustc_cache, filename)
-        if not os.path.exists(tarball):
-            help_on_error = "error: failed to download llvm from ci"
-            help_on_error += "\nhelp: old builds get deleted after a certain time"
-            help_on_error += "\nhelp: if trying to compile an old commit of rustc,"
-            help_on_error += " disable `download-ci-llvm` in config.toml:"
-            help_on_error += "\n"
-            help_on_error += "\n[llvm]"
-            help_on_error += "\ndownload-ci-llvm = false"
-            help_on_error += "\n"
-            get(
-                base,
-                "{}/{}".format(url, filename),
-                tarball,
-                self.checksums_sha256,
-                verbose=self.verbose,
-                do_verify=False,
-                help_on_error=help_on_error,
-            )
-        unpack(tarball, tarball_suffix, self.llvm_root(),
-                match="rust-dev",
-                verbose=self.verbose)
-
     def fix_bin_or_dylib(self, fname):
         """Modifies the interpreter section of 'fname' to fix the dynamic linker,
         or the RPATH section, to fix the dynamic library search path
@@ -816,17 +694,6 @@ class RustBuild(object):
         """
         return os.path.join(self.bin_root(True), '.rustfmt-stamp')
 
-    def llvm_stamp(self):
-        """Return the path for .llvm-stamp
-
-        >>> rb = RustBuild()
-        >>> rb.build_dir = "build"
-        >>> rb.llvm_stamp() == os.path.join("build", "ci-llvm", ".llvm-stamp")
-        True
-        """
-        return os.path.join(self.llvm_root(), '.llvm-stamp')
-
-
     def program_out_of_date(self, stamp_path, key):
         """Check if the given program stamp is out of date"""
         if not os.path.exists(stamp_path) or self.clean:
@@ -856,22 +723,6 @@ class RustBuild(object):
             subdir = "ci-rustc"
         return os.path.join(self.build_dir, self.build, subdir)
 
-    def llvm_root(self):
-        """Return the CI LLVM root directory
-
-        >>> rb = RustBuild()
-        >>> rb.build_dir = "build"
-        >>> rb.llvm_root() == os.path.join("build", "ci-llvm")
-        True
-
-        When the 'build' property is given should be a nested directory:
-
-        >>> rb.build = "devel"
-        >>> rb.llvm_root() == os.path.join("build", "devel", "ci-llvm")
-        True
-        """
-        return os.path.join(self.build_dir, self.build, "ci-llvm")
-
     def get_toml(self, key, section=None):
         """Returns the value of the given key in config.toml, otherwise returns None
 
diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs
index d688f798956..dd45bd3a213 100644
--- a/src/bootstrap/builder.rs
+++ b/src/bootstrap/builder.rs
@@ -1,9 +1,9 @@
-use std::any::Any;
+use std::any::{type_name, Any};
 use std::cell::{Cell, RefCell};
 use std::collections::BTreeSet;
 use std::env;
 use std::ffi::OsStr;
-use std::fmt::Debug;
+use std::fmt::{Debug, Write};
 use std::fs;
 use std::hash::Hash;
 use std::ops::Deref;
@@ -12,9 +12,8 @@ use std::process::Command;
 use std::time::{Duration, Instant};
 
 use crate::cache::{Cache, Interned, INTERNER};
-use crate::check;
 use crate::compile;
-use crate::config::TargetSelection;
+use crate::config::{SplitDebuginfo, TargetSelection};
 use crate::dist;
 use crate::doc;
 use crate::flags::{Color, Subcommand};
@@ -25,6 +24,7 @@ use crate::test;
 use crate::tool::{self, SourceType};
 use crate::util::{self, add_dylib_path, add_link_lib_path, exe, libdir, output, t};
 use crate::EXTRA_CHECK_CFGS;
+use crate::{check, Config};
 use crate::{Build, CLang, DocTests, GitRepo, Mode};
 
 pub use crate::Compiler;
@@ -125,7 +125,8 @@ impl TaskPath {
                     if found_kind.is_empty() {
                         panic!("empty kind in task path {}", path.display());
                     }
-                    kind = Some(Kind::parse(found_kind));
+                    kind = Kind::parse(found_kind);
+                    assert!(kind.is_some());
                     path = Path::new(found_prefix).join(components.as_path());
                 }
             }
@@ -388,11 +389,13 @@ impl<'a> ShouldRun<'a> {
             paths
                 .iter()
                 .map(|p| {
-                    assert!(
-                        self.builder.src.join(p).exists(),
-                        "`should_run.paths` should correspond to real on-disk paths - use `alias` if there is no relevant path: {}",
-                        p
-                    );
+                    // FIXME(#96188): make sure this is actually a path.
+                    // This currently breaks for paths within submodules.
+                    //assert!(
+                    //    self.builder.src.join(p).exists(),
+                    //    "`should_run.paths` should correspond to real on-disk paths - use `alias` if there is no relevant path: {}",
+                    //    p
+                    //);
                     TaskPath { path: p.into(), kind: Some(self.kind) }
                 })
                 .collect(),
@@ -429,43 +432,53 @@ pub enum Kind {
     Check,
     Clippy,
     Fix,
+    Format,
     Test,
     Bench,
-    Dist,
     Doc,
+    Clean,
+    Dist,
     Install,
     Run,
+    Setup,
 }
 
 impl Kind {
-    fn parse(string: &str) -> Kind {
-        match string {
-            "build" => Kind::Build,
-            "check" => Kind::Check,
+    pub fn parse(string: &str) -> Option<Kind> {
+        // these strings, including the one-letter aliases, must match the x.py help text
+        Some(match string {
+            "build" | "b" => Kind::Build,
+            "check" | "c" => Kind::Check,
             "clippy" => Kind::Clippy,
             "fix" => Kind::Fix,
-            "test" => Kind::Test,
+            "fmt" => Kind::Format,
+            "test" | "t" => Kind::Test,
             "bench" => Kind::Bench,
+            "doc" | "d" => Kind::Doc,
+            "clean" => Kind::Clean,
             "dist" => Kind::Dist,
-            "doc" => Kind::Doc,
             "install" => Kind::Install,
-            "run" => Kind::Run,
-            other => panic!("unknown kind: {}", other),
-        }
+            "run" | "r" => Kind::Run,
+            "setup" => Kind::Setup,
+            _ => return None,
+        })
     }
 
-    fn as_str(&self) -> &'static str {
+    pub fn as_str(&self) -> &'static str {
         match self {
             Kind::Build => "build",
             Kind::Check => "check",
             Kind::Clippy => "clippy",
             Kind::Fix => "fix",
+            Kind::Format => "fmt",
             Kind::Test => "test",
             Kind::Bench => "bench",
-            Kind::Dist => "dist",
             Kind::Doc => "doc",
+            Kind::Clean => "clean",
+            Kind::Dist => "dist",
             Kind::Install => "install",
             Kind::Run => "run",
+            Kind::Setup => "setup",
         }
     }
 }
@@ -509,7 +522,7 @@ impl<'a> Builder<'a> {
                 native::Lld,
                 native::CrtBeginEnd
             ),
-            Kind::Check | Kind::Clippy { .. } | Kind::Fix => describe!(
+            Kind::Check => describe!(
                 check::Std,
                 check::Rustc,
                 check::Rustdoc,
@@ -639,32 +652,29 @@ impl<'a> Builder<'a> {
                 install::Rustc
             ),
             Kind::Run => describe!(run::ExpandYamlAnchors, run::BuildManifest, run::BumpStage0),
+            // These commands either don't use paths, or they're special-cased in Build::build()
+            Kind::Clean | Kind::Clippy | Kind::Fix | Kind::Format | Kind::Setup => vec![],
         }
     }
 
-    pub fn get_help(build: &Build, subcommand: &str) -> Option<String> {
-        let kind = match subcommand {
-            "build" | "b" => Kind::Build,
-            "doc" | "d" => Kind::Doc,
-            "test" | "t" => Kind::Test,
-            "bench" => Kind::Bench,
-            "dist" => Kind::Dist,
-            "install" => Kind::Install,
-            _ => return None,
-        };
+    pub fn get_help(build: &Build, kind: Kind) -> Option<String> {
+        let step_descriptions = Builder::get_step_descriptions(kind);
+        if step_descriptions.is_empty() {
+            return None;
+        }
 
         let builder = Self::new_internal(build, kind, vec![]);
         let builder = &builder;
         // The "build" kind here is just a placeholder, it will be replaced with something else in
         // the following statement.
         let mut should_run = ShouldRun::new(builder, Kind::Build);
-        for desc in Builder::get_step_descriptions(builder.kind) {
+        for desc in step_descriptions {
             should_run.kind = desc.kind;
             should_run = (desc.should_run)(should_run);
         }
         let mut help = String::from("Available paths:\n");
         let mut add_path = |path: &Path| {
-            help.push_str(&format!("    ./x.py {} {}\n", subcommand, path.display()));
+            t!(write!(help, "    ./x.py {} {}\n", kind.as_str(), path.display()));
         };
         for pathset in should_run.paths {
             match pathset {
@@ -950,6 +960,11 @@ impl<'a> Builder<'a> {
         None
     }
 
+    /// Convenience wrapper to allow `builder.llvm_link_shared()` instead of `builder.config.llvm_link_shared(&builder)`.
+    pub(crate) fn llvm_link_shared(&self) -> bool {
+        Config::llvm_link_shared(self)
+    }
+
     /// Prepares an invocation of `cargo` to be run.
     ///
     /// This will create a `Command` that represents a pending execution of
@@ -1388,17 +1403,17 @@ impl<'a> Builder<'a> {
             },
         );
 
-        // `dsymutil` adds time to builds on Apple platforms for no clear benefit, and also makes
-        // it more difficult for debuggers to find debug info. The compiler currently defaults to
-        // running `dsymutil` to preserve its historical default, but when compiling the compiler
-        // itself, we skip it by default since we know it's safe to do so in that case.
-        // See https://github.com/rust-lang/rust/issues/79361 for more info on this flag.
-        if target.contains("apple") {
-            if self.config.rust_run_dsymutil {
-                rustflags.arg("-Csplit-debuginfo=packed");
-            } else {
-                rustflags.arg("-Csplit-debuginfo=unpacked");
+        // FIXME(davidtwco): #[cfg(not(bootstrap))] - #95612 needs to be in the bootstrap compiler
+        // for this conditional to be removed.
+        if !target.contains("windows") || compiler.stage >= 1 {
+            if target.contains("linux") || target.contains("windows") {
+                rustflags.arg("-Zunstable-options");
             }
+            match self.config.rust_split_debuginfo {
+                SplitDebuginfo::Packed => rustflags.arg("-Csplit-debuginfo=packed"),
+                SplitDebuginfo::Unpacked => rustflags.arg("-Csplit-debuginfo=unpacked"),
+                SplitDebuginfo::Off => rustflags.arg("-Csplit-debuginfo=off"),
+            };
         }
 
         if self.config.cmd.bless() {
@@ -1753,7 +1768,16 @@ impl<'a> Builder<'a> {
         };
 
         if self.config.print_step_timings && !self.config.dry_run {
-            println!("[TIMING] {:?} -- {}.{:03}", step, dur.as_secs(), dur.subsec_millis());
+            let step_string = format!("{:?}", step);
+            let brace_index = step_string.find("{").unwrap_or(0);
+            let type_string = type_name::<S>();
+            println!(
+                "[TIMING] {} {} -- {}.{:03}",
+                &type_string.strip_prefix("bootstrap::").unwrap_or(type_string),
+                &step_string[brace_index..],
+                dur.as_secs(),
+                dur.subsec_millis()
+            );
         }
 
         {
diff --git a/src/bootstrap/builder/tests.rs b/src/bootstrap/builder/tests.rs
index a59f72ed968..3b6cd7564f0 100644
--- a/src/bootstrap/builder/tests.rs
+++ b/src/bootstrap/builder/tests.rs
@@ -7,6 +7,7 @@ fn configure(cmd: &str, host: &[&str], target: &[&str]) -> Config {
     // don't save toolstates
     config.save_toolstates = None;
     config.dry_run = true;
+    config.submodules = Some(false);
     config.ninja_in_file = false;
     // try to avoid spurious failures in dist where we create/delete each others file
     // HACK: rather than pull in `tempdir`, use the one that cargo has conveniently created for us
@@ -25,36 +26,74 @@ fn first<A, B>(v: Vec<(A, B)>) -> Vec<A> {
     v.into_iter().map(|(a, _)| a).collect::<Vec<_>>()
 }
 
+fn run_build(paths: &[PathBuf], config: Config) -> Cache {
+    let kind = config.cmd.kind();
+    let build = Build::new(config);
+    let builder = Builder::new(&build);
+    builder.run_step_descriptions(&Builder::get_step_descriptions(kind), paths);
+    builder.cache
+}
+
+#[test]
+fn test_exclude() {
+    let mut config = configure("test", &["A"], &["A"]);
+    config.exclude = vec![TaskPath::parse("src/tools/tidy")];
+    let cache = run_build(&[], config);
+
+    // Ensure we have really excluded tidy
+    assert!(!cache.contains::<test::Tidy>());
+
+    // Ensure other tests are not affected.
+    assert!(cache.contains::<test::RustdocUi>());
+}
+
+#[test]
+fn test_exclude_kind() {
+    let path = PathBuf::from("src/tools/cargotest");
+    let exclude = TaskPath::parse("test::src/tools/cargotest");
+    assert_eq!(exclude, TaskPath { kind: Some(Kind::Test), path: path.clone() });
+
+    let mut config = configure("test", &["A"], &["A"]);
+    // Ensure our test is valid, and `test::Cargotest` would be run without the exclude.
+    assert!(run_build(&[path.clone()], config.clone()).contains::<test::Cargotest>());
+    // Ensure tests for cargotest are skipped.
+    config.exclude = vec![exclude.clone()];
+    assert!(!run_build(&[path.clone()], config).contains::<test::Cargotest>());
+
+    // Ensure builds for cargotest are not skipped.
+    let mut config = configure("build", &["A"], &["A"]);
+    config.exclude = vec![exclude];
+    assert!(run_build(&[path], config).contains::<tool::CargoTest>());
+}
+
 mod defaults {
-    use super::{configure, first};
+    use super::{configure, first, run_build};
     use crate::builder::*;
     use crate::Config;
     use pretty_assertions::assert_eq;
 
     #[test]
     fn build_default() {
-        let build = Build::new(configure("build", &["A"], &["A"]));
-        let mut builder = Builder::new(&build);
-        builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Build), &[]);
+        let mut cache = run_build(&[], configure("build", &["A"], &["A"]));
 
         let a = TargetSelection::from_user("A");
         assert_eq!(
-            first(builder.cache.all::<compile::Std>()),
+            first(cache.all::<compile::Std>()),
             &[
                 compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a },
                 compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
             ]
         );
-        assert!(!builder.cache.all::<compile::Assemble>().is_empty());
+        assert!(!cache.all::<compile::Assemble>().is_empty());
         // Make sure rustdoc is only built once.
         assert_eq!(
-            first(builder.cache.all::<tool::Rustdoc>()),
+            first(cache.all::<tool::Rustdoc>()),
             // Recall that rustdoc stages are off-by-one
             // - this is the compiler it's _linked_ to, not built with.
             &[tool::Rustdoc { compiler: Compiler { host: a, stage: 1 } }],
         );
         assert_eq!(
-            first(builder.cache.all::<compile::Rustc>()),
+            first(cache.all::<compile::Rustc>()),
             &[compile::Rustc { compiler: Compiler { host: a, stage: 0 }, target: a },]
         );
     }
@@ -62,31 +101,27 @@ mod defaults {
     #[test]
     fn build_stage_0() {
         let config = Config { stage: 0, ..configure("build", &["A"], &["A"]) };
-        let build = Build::new(config);
-        let mut builder = Builder::new(&build);
-        builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Build), &[]);
+        let mut cache = run_build(&[], config);
 
         let a = TargetSelection::from_user("A");
         assert_eq!(
-            first(builder.cache.all::<compile::Std>()),
+            first(cache.all::<compile::Std>()),
             &[compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a },]
         );
-        assert!(!builder.cache.all::<compile::Assemble>().is_empty());
+        assert!(!cache.all::<compile::Assemble>().is_empty());
         assert_eq!(
-            first(builder.cache.all::<tool::Rustdoc>()),
+            first(cache.all::<tool::Rustdoc>()),
             // This is the beta rustdoc.
             // Add an assert here to make sure this is the only rustdoc built.
             &[tool::Rustdoc { compiler: Compiler { host: a, stage: 0 } }],
         );
-        assert!(builder.cache.all::<compile::Rustc>().is_empty());
+        assert!(cache.all::<compile::Rustc>().is_empty());
     }
 
     #[test]
     fn build_cross_compile() {
         let config = Config { stage: 1, ..configure("build", &["A", "B"], &["A", "B"]) };
-        let build = Build::new(config);
-        let mut builder = Builder::new(&build);
-        builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Build), &[]);
+        let mut cache = run_build(&[], config);
 
         let a = TargetSelection::from_user("A");
         let b = TargetSelection::from_user("B");
@@ -97,7 +132,7 @@ mod defaults {
         // (since we're producing stage 1 libraries/binaries).  But currently
         // rustbuild is just a bit buggy here; this should be fixed though.
         assert_eq!(
-            first(builder.cache.all::<compile::Std>()),
+            first(cache.all::<compile::Std>()),
             &[
                 compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a },
                 compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
@@ -106,7 +141,7 @@ mod defaults {
             ]
         );
         assert_eq!(
-            first(builder.cache.all::<compile::Assemble>()),
+            first(cache.all::<compile::Assemble>()),
             &[
                 compile::Assemble { target_compiler: Compiler { host: a, stage: 0 } },
                 compile::Assemble { target_compiler: Compiler { host: a, stage: 1 } },
@@ -114,14 +149,14 @@ mod defaults {
             ]
         );
         assert_eq!(
-            first(builder.cache.all::<tool::Rustdoc>()),
+            first(cache.all::<tool::Rustdoc>()),
             &[
                 tool::Rustdoc { compiler: Compiler { host: a, stage: 1 } },
                 tool::Rustdoc { compiler: Compiler { host: b, stage: 1 } },
             ],
         );
         assert_eq!(
-            first(builder.cache.all::<compile::Rustc>()),
+            first(cache.all::<compile::Rustc>()),
             &[
                 compile::Rustc { compiler: Compiler { host: a, stage: 0 }, target: a },
                 compile::Rustc { compiler: Compiler { host: a, stage: 0 }, target: b },
@@ -134,33 +169,28 @@ mod defaults {
         let mut config = configure("doc", &["A"], &["A"]);
         config.compiler_docs = true;
         config.cmd = Subcommand::Doc { paths: Vec::new(), open: false };
-        let build = Build::new(config);
-        let mut builder = Builder::new(&build);
-        builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Doc), &[]);
+        let mut cache = run_build(&[], config);
         let a = TargetSelection::from_user("A");
 
         // error_index_generator uses stage 0 to share rustdoc artifacts with the
         // rustdoc tool.
+        assert_eq!(first(cache.all::<doc::ErrorIndex>()), &[doc::ErrorIndex { target: a },]);
         assert_eq!(
-            first(builder.cache.all::<doc::ErrorIndex>()),
-            &[doc::ErrorIndex { target: a },]
-        );
-        assert_eq!(
-            first(builder.cache.all::<tool::ErrorIndex>()),
+            first(cache.all::<tool::ErrorIndex>()),
             &[tool::ErrorIndex { compiler: Compiler { host: a, stage: 0 } }]
         );
         // docs should be built with the beta compiler, not with the stage0 artifacts.
         // recall that rustdoc is off-by-one: `stage` is the compiler rustdoc is _linked_ to,
         // not the one it was built by.
         assert_eq!(
-            first(builder.cache.all::<tool::Rustdoc>()),
+            first(cache.all::<tool::Rustdoc>()),
             &[tool::Rustdoc { compiler: Compiler { host: a, stage: 0 } },]
         );
     }
 }
 
 mod dist {
-    use super::{first, Config};
+    use super::{first, run_build, Config};
     use crate::builder::*;
     use pretty_assertions::assert_eq;
 
@@ -170,94 +200,88 @@ mod dist {
 
     #[test]
     fn dist_baseline() {
-        let build = Build::new(configure(&["A"], &["A"]));
-        let mut builder = Builder::new(&build);
-        builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
+        let mut cache = run_build(&[], configure(&["A"], &["A"]));
 
         let a = TargetSelection::from_user("A");
 
-        assert_eq!(first(builder.cache.all::<dist::Docs>()), &[dist::Docs { host: a },]);
-        assert_eq!(first(builder.cache.all::<dist::Mingw>()), &[dist::Mingw { host: a },]);
+        assert_eq!(first(cache.all::<dist::Docs>()), &[dist::Docs { host: a },]);
+        assert_eq!(first(cache.all::<dist::Mingw>()), &[dist::Mingw { host: a },]);
         assert_eq!(
-            first(builder.cache.all::<dist::Rustc>()),
+            first(cache.all::<dist::Rustc>()),
             &[dist::Rustc { compiler: Compiler { host: a, stage: 2 } },]
         );
         assert_eq!(
-            first(builder.cache.all::<dist::Std>()),
+            first(cache.all::<dist::Std>()),
             &[dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a },]
         );
-        assert_eq!(first(builder.cache.all::<dist::Src>()), &[dist::Src]);
+        assert_eq!(first(cache.all::<dist::Src>()), &[dist::Src]);
         // Make sure rustdoc is only built once.
         assert_eq!(
-            first(builder.cache.all::<tool::Rustdoc>()),
+            first(cache.all::<tool::Rustdoc>()),
             &[tool::Rustdoc { compiler: Compiler { host: a, stage: 2 } },]
         );
     }
 
     #[test]
     fn dist_with_targets() {
-        let build = Build::new(configure(&["A"], &["A", "B"]));
-        let mut builder = Builder::new(&build);
-        builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
+        let mut cache = run_build(&[], configure(&["A"], &["A", "B"]));
 
         let a = TargetSelection::from_user("A");
         let b = TargetSelection::from_user("B");
 
         assert_eq!(
-            first(builder.cache.all::<dist::Docs>()),
+            first(cache.all::<dist::Docs>()),
             &[dist::Docs { host: a }, dist::Docs { host: b },]
         );
         assert_eq!(
-            first(builder.cache.all::<dist::Mingw>()),
+            first(cache.all::<dist::Mingw>()),
             &[dist::Mingw { host: a }, dist::Mingw { host: b },]
         );
         assert_eq!(
-            first(builder.cache.all::<dist::Rustc>()),
+            first(cache.all::<dist::Rustc>()),
             &[dist::Rustc { compiler: Compiler { host: a, stage: 2 } },]
         );
         assert_eq!(
-            first(builder.cache.all::<dist::Std>()),
+            first(cache.all::<dist::Std>()),
             &[
                 dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
                 dist::Std { compiler: Compiler { host: a, stage: 2 }, target: b },
             ]
         );
-        assert_eq!(first(builder.cache.all::<dist::Src>()), &[dist::Src]);
+        assert_eq!(first(cache.all::<dist::Src>()), &[dist::Src]);
     }
 
     #[test]
     fn dist_with_hosts() {
-        let build = Build::new(configure(&["A", "B"], &["A", "B"]));
-        let mut builder = Builder::new(&build);
-        builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
+        let mut cache = run_build(&[], configure(&["A", "B"], &["A", "B"]));
 
         let a = TargetSelection::from_user("A");
         let b = TargetSelection::from_user("B");
 
         assert_eq!(
-            first(builder.cache.all::<dist::Docs>()),
+            first(cache.all::<dist::Docs>()),
             &[dist::Docs { host: a }, dist::Docs { host: b },]
         );
         assert_eq!(
-            first(builder.cache.all::<dist::Mingw>()),
+            first(cache.all::<dist::Mingw>()),
             &[dist::Mingw { host: a }, dist::Mingw { host: b },]
         );
         assert_eq!(
-            first(builder.cache.all::<dist::Rustc>()),
+            first(cache.all::<dist::Rustc>()),
             &[
                 dist::Rustc { compiler: Compiler { host: a, stage: 2 } },
                 dist::Rustc { compiler: Compiler { host: b, stage: 2 } },
             ]
         );
         assert_eq!(
-            first(builder.cache.all::<dist::Std>()),
+            first(cache.all::<dist::Std>()),
             &[
                 dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
                 dist::Std { compiler: Compiler { host: a, stage: 1 }, target: b },
             ]
         );
         assert_eq!(
-            first(builder.cache.all::<compile::Std>()),
+            first(cache.all::<compile::Std>()),
             &[
                 compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a },
                 compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
@@ -266,26 +290,25 @@ mod dist {
                 compile::Std { compiler: Compiler { host: a, stage: 2 }, target: b },
             ],
         );
-        assert_eq!(first(builder.cache.all::<dist::Src>()), &[dist::Src]);
+        assert_eq!(first(cache.all::<dist::Src>()), &[dist::Src]);
     }
 
     #[test]
     fn dist_only_cross_host() {
         let a = TargetSelection::from_user("A");
         let b = TargetSelection::from_user("B");
-        let mut build = Build::new(configure(&["A", "B"], &["A", "B"]));
-        build.config.docs = false;
-        build.config.extended = true;
-        build.hosts = vec![b];
-        let mut builder = Builder::new(&build);
-        builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
+        let mut config = configure(&["A", "B"], &["A", "B"]);
+        config.docs = false;
+        config.extended = true;
+        config.hosts = vec![b];
+        let mut cache = run_build(&[], config);
 
         assert_eq!(
-            first(builder.cache.all::<dist::Rustc>()),
+            first(cache.all::<dist::Rustc>()),
             &[dist::Rustc { compiler: Compiler { host: b, stage: 2 } },]
         );
         assert_eq!(
-            first(builder.cache.all::<compile::Rustc>()),
+            first(cache.all::<compile::Rustc>()),
             &[
                 compile::Rustc { compiler: Compiler { host: a, stage: 0 }, target: a },
                 compile::Rustc { compiler: Compiler { host: a, stage: 1 }, target: b },
@@ -295,92 +318,86 @@ mod dist {
 
     #[test]
     fn dist_with_targets_and_hosts() {
-        let build = Build::new(configure(&["A", "B"], &["A", "B", "C"]));
-        let mut builder = Builder::new(&build);
-        builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
+        let mut cache = run_build(&[], configure(&["A", "B"], &["A", "B", "C"]));
 
         let a = TargetSelection::from_user("A");
         let b = TargetSelection::from_user("B");
         let c = TargetSelection::from_user("C");
 
         assert_eq!(
-            first(builder.cache.all::<dist::Docs>()),
+            first(cache.all::<dist::Docs>()),
             &[dist::Docs { host: a }, dist::Docs { host: b }, dist::Docs { host: c },]
         );
         assert_eq!(
-            first(builder.cache.all::<dist::Mingw>()),
+            first(cache.all::<dist::Mingw>()),
             &[dist::Mingw { host: a }, dist::Mingw { host: b }, dist::Mingw { host: c },]
         );
         assert_eq!(
-            first(builder.cache.all::<dist::Rustc>()),
+            first(cache.all::<dist::Rustc>()),
             &[
                 dist::Rustc { compiler: Compiler { host: a, stage: 2 } },
                 dist::Rustc { compiler: Compiler { host: b, stage: 2 } },
             ]
         );
         assert_eq!(
-            first(builder.cache.all::<dist::Std>()),
+            first(cache.all::<dist::Std>()),
             &[
                 dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
                 dist::Std { compiler: Compiler { host: a, stage: 1 }, target: b },
                 dist::Std { compiler: Compiler { host: a, stage: 2 }, target: c },
             ]
         );
-        assert_eq!(first(builder.cache.all::<dist::Src>()), &[dist::Src]);
+        assert_eq!(first(cache.all::<dist::Src>()), &[dist::Src]);
     }
 
     #[test]
     fn dist_with_empty_host() {
         let config = configure(&[], &["C"]);
-        let build = Build::new(config);
-        let mut builder = Builder::new(&build);
-        builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
+        let mut cache = run_build(&[], config);
 
         let a = TargetSelection::from_user("A");
         let c = TargetSelection::from_user("C");
 
-        assert_eq!(first(builder.cache.all::<dist::Docs>()), &[dist::Docs { host: c },]);
-        assert_eq!(first(builder.cache.all::<dist::Mingw>()), &[dist::Mingw { host: c },]);
+        assert_eq!(first(cache.all::<dist::Docs>()), &[dist::Docs { host: c },]);
+        assert_eq!(first(cache.all::<dist::Mingw>()), &[dist::Mingw { host: c },]);
         assert_eq!(
-            first(builder.cache.all::<dist::Std>()),
+            first(cache.all::<dist::Std>()),
             &[dist::Std { compiler: Compiler { host: a, stage: 2 }, target: c },]
         );
     }
 
     #[test]
     fn dist_with_same_targets_and_hosts() {
-        let build = Build::new(configure(&["A", "B"], &["A", "B"]));
-        let mut builder = Builder::new(&build);
-        builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
+        let mut cache = run_build(&[], configure(&["A", "B"], &["A", "B"]));
 
         let a = TargetSelection::from_user("A");
         let b = TargetSelection::from_user("B");
 
         assert_eq!(
-            first(builder.cache.all::<dist::Docs>()),
+            first(cache.all::<dist::Docs>()),
             &[dist::Docs { host: a }, dist::Docs { host: b },]
         );
         assert_eq!(
-            first(builder.cache.all::<dist::Mingw>()),
+            first(cache.all::<dist::Mingw>()),
             &[dist::Mingw { host: a }, dist::Mingw { host: b },]
         );
         assert_eq!(
-            first(builder.cache.all::<dist::Rustc>()),
+            first(cache.all::<dist::Rustc>()),
             &[
                 dist::Rustc { compiler: Compiler { host: a, stage: 2 } },
                 dist::Rustc { compiler: Compiler { host: b, stage: 2 } },
             ]
         );
         assert_eq!(
-            first(builder.cache.all::<dist::Std>()),
+            first(cache.all::<dist::Std>()),
             &[
                 dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
                 dist::Std { compiler: Compiler { host: a, stage: 1 }, target: b },
             ]
         );
-        assert_eq!(first(builder.cache.all::<dist::Src>()), &[dist::Src]);
+        assert_eq!(first(cache.all::<dist::Src>()), &[dist::Src]);
         assert_eq!(
-            first(builder.cache.all::<compile::Std>()),
+            first(cache.all::<compile::Std>()),
             &[
                 compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a },
                 compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
@@ -390,7 +407,7 @@ mod dist {
             ]
         );
         assert_eq!(
-            first(builder.cache.all::<compile::Assemble>()),
+            first(cache.all::<compile::Assemble>()),
             &[
                 compile::Assemble { target_compiler: Compiler { host: a, stage: 0 } },
                 compile::Assemble { target_compiler: Compiler { host: a, stage: 1 } },
@@ -515,35 +532,6 @@ mod dist {
     }
 
     #[test]
-    fn test_exclude() {
-        let mut config = configure(&["A"], &["A"]);
-        config.exclude = vec![TaskPath::parse("src/tools/tidy")];
-        config.cmd = Subcommand::Test {
-            paths: Vec::new(),
-            test_args: Vec::new(),
-            rustc_args: Vec::new(),
-            fail_fast: true,
-            doc_tests: DocTests::No,
-            bless: false,
-            force_rerun: false,
-            compare_mode: None,
-            rustfix_coverage: false,
-            pass: None,
-            run: None,
-        };
-
-        let build = Build::new(config);
-        let builder = Builder::new(&build);
-        builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Test), &[]);
-
-        // Ensure we have really excluded tidy
-        assert!(!builder.cache.contains::<test::Tidy>());
-
-        // Ensure other tests are not affected.
-        assert!(builder.cache.contains::<test::RustdocUi>());
-    }
-
-    #[test]
     fn doc_ci() {
         let mut config = configure(&["A"], &["A"]);
         config.compiler_docs = true;
diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs
index 432a6c34ed5..731ebc41bb9 100644
--- a/src/bootstrap/check.rs
+++ b/src/bootstrap/check.rs
@@ -64,7 +64,7 @@ impl Step for Std {
     const DEFAULT: bool = true;
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
-        run.all_krates("test")
+        run.all_krates("test").path("library")
     }
 
     fn make_run(run: RunConfig<'_>) {
@@ -162,7 +162,7 @@ impl Step for Rustc {
     const DEFAULT: bool = true;
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
-        run.all_krates("rustc-main")
+        run.all_krates("rustc-main").path("compiler")
     }
 
     fn make_run(run: RunConfig<'_>) {
diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs
index 45991381dc0..53933e4cd7d 100644
--- a/src/bootstrap/compile.rs
+++ b/src/bootstrap/compile.rs
@@ -43,7 +43,7 @@ impl Step for Std {
         // When downloading stage1, the standard library has already been copied to the sysroot, so
         // there's no need to rebuild it.
         let download_rustc = run.builder.config.download_rustc;
-        run.all_krates("test").default_condition(!download_rustc)
+        run.all_krates("test").path("library").default_condition(!download_rustc)
     }
 
     fn make_run(run: RunConfig<'_>) {
@@ -737,7 +737,7 @@ pub fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetS
             );
             cargo.env("LLVM_STATIC_STDCPP", file);
         }
-        if builder.config.llvm_link_shared {
+        if builder.llvm_link_shared() {
             cargo.env("LLVM_LINK_SHARED", "1");
         }
         if builder.config.llvm_use_libcxx {
@@ -1047,7 +1047,7 @@ impl Step for Assemble {
     const ONLY_HOSTS: bool = true;
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
-        run.path("compiler/rustc")
+        run.path("compiler/rustc").path("compiler")
     }
 
     fn make_run(run: RunConfig<'_>) {
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs
index d7c29f6900a..e39c9fa1c5a 100644
--- a/src/bootstrap/config.rs
+++ b/src/bootstrap/config.rs
@@ -3,6 +3,7 @@
 //! This module implements parsing `config.toml` configuration files to tweak
 //! how the build runs.
 
+use std::cell::Cell;
 use std::cmp;
 use std::collections::{HashMap, HashSet};
 use std::env;
@@ -11,7 +12,7 @@ use std::fs;
 use std::path::{Path, PathBuf};
 use std::str::FromStr;
 
-use crate::builder::TaskPath;
+use crate::builder::{Builder, TaskPath};
 use crate::cache::{Interned, INTERNER};
 use crate::channel::GitInfo;
 pub use crate::flags::Subcommand;
@@ -41,6 +42,7 @@ macro_rules! check_ci_llvm {
 /// each field, see the corresponding fields in
 /// `config.toml.example`.
 #[derive(Default)]
+#[cfg_attr(test, derive(Clone))]
 pub struct Config {
     pub changelog_seen: Option<usize>,
     pub ccache: Option<String>,
@@ -68,13 +70,14 @@ pub struct Config {
     pub test_compare_mode: bool,
     pub llvm_libunwind: LlvmLibunwind,
     pub color: Color,
+    pub patch_binaries_for_nix: bool,
 
     pub on_fail: Option<String>,
     pub stage: u32,
     pub keep_stage: Vec<u32>,
     pub keep_stage_std: Vec<u32>,
     pub src: PathBuf,
-    // defaults to `config.toml`
+    /// defaults to `config.toml`
     pub config: PathBuf,
     pub jobs: Option<u32>,
     pub cmd: Subcommand,
@@ -95,7 +98,11 @@ pub struct Config {
     pub llvm_release_debuginfo: bool,
     pub llvm_version_check: bool,
     pub llvm_static_stdcpp: bool,
-    pub llvm_link_shared: bool,
+    /// `None` if `llvm_from_ci` is true and we haven't yet downloaded llvm.
+    #[cfg(not(test))]
+    llvm_link_shared: Cell<Option<bool>>,
+    #[cfg(test)]
+    pub llvm_link_shared: Cell<Option<bool>>,
     pub llvm_clang_cl: Option<String>,
     pub llvm_targets: Option<String>,
     pub llvm_experimental_targets: Option<String>,
@@ -130,7 +137,7 @@ pub struct Config {
     pub rust_debuginfo_level_std: u32,
     pub rust_debuginfo_level_tools: u32,
     pub rust_debuginfo_level_tests: u32,
-    pub rust_run_dsymutil: bool,
+    pub rust_split_debuginfo: SplitDebuginfo,
     pub rust_rpath: bool,
     pub rustc_parallel: bool,
     pub rustc_default_linker: Option<String>,
@@ -221,6 +228,46 @@ impl FromStr for LlvmLibunwind {
     }
 }
 
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum SplitDebuginfo {
+    Packed,
+    Unpacked,
+    Off,
+}
+
+impl Default for SplitDebuginfo {
+    fn default() -> Self {
+        SplitDebuginfo::Off
+    }
+}
+
+impl std::str::FromStr for SplitDebuginfo {
+    type Err = ();
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match s {
+            "packed" => Ok(SplitDebuginfo::Packed),
+            "unpacked" => Ok(SplitDebuginfo::Unpacked),
+            "off" => Ok(SplitDebuginfo::Off),
+            _ => Err(()),
+        }
+    }
+}
+
+impl SplitDebuginfo {
+    /// Returns the default `-Csplit-debuginfo` value for the current target. See the comment for
+    /// `rust.split-debuginfo` in `config.toml.example`.
+    fn default_for_platform(target: &str) -> Self {
+        if target.contains("apple") {
+            SplitDebuginfo::Unpacked
+        } else if target.contains("windows") {
+            SplitDebuginfo::Packed
+        } else {
+            SplitDebuginfo::Off
+        }
+    }
+}
+
 #[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub struct TargetSelection {
     pub triple: Interned<String>,
@@ -290,6 +337,7 @@ impl PartialEq<&str> for TargetSelection {
 
 /// Per-target configuration stored in the global configuration structure.
 #[derive(Default)]
+#[cfg_attr(test, derive(Clone))]
 pub struct Target {
     /// Some(path to llvm-config) if using an external LLVM.
     pub llvm_config: Option<PathBuf>,
@@ -586,6 +634,7 @@ define_config! {
         debuginfo_level_std: Option<u32> = "debuginfo-level-std",
         debuginfo_level_tools: Option<u32> = "debuginfo-level-tools",
         debuginfo_level_tests: Option<u32> = "debuginfo-level-tests",
+        split_debuginfo: Option<String> = "split-debuginfo",
         run_dsymutil: Option<bool> = "run-dsymutil",
         backtrace: Option<bool> = "backtrace",
         incremental: Option<bool> = "incremental",
@@ -815,6 +864,7 @@ impl Config {
         set(&mut config.local_rebuild, build.local_rebuild);
         set(&mut config.print_step_timings, build.print_step_timings);
         set(&mut config.print_step_rusage, build.print_step_rusage);
+        set(&mut config.patch_binaries_for_nix, build.patch_binaries_for_nix);
 
         config.verbose = cmp::max(config.verbose, flags.verbose);
 
@@ -870,7 +920,9 @@ impl Config {
             set(&mut config.llvm_release_debuginfo, llvm.release_debuginfo);
             set(&mut config.llvm_version_check, llvm.version_check);
             set(&mut config.llvm_static_stdcpp, llvm.static_libstdcpp);
-            set(&mut config.llvm_link_shared, llvm.link_shared);
+            if let Some(v) = llvm.link_shared {
+                config.llvm_link_shared.set(Some(v));
+            }
             config.llvm_targets = llvm.targets.clone();
             config.llvm_experimental_targets = llvm.experimental_targets.clone();
             config.llvm_link_jobs = llvm.link_jobs;
@@ -940,6 +992,7 @@ impl Config {
                 check_ci_llvm!(llvm.optimize);
                 check_ci_llvm!(llvm.thin_lto);
                 check_ci_llvm!(llvm.release_debuginfo);
+                // CI-built LLVM can be either dynamic or static. We won't know until we download it.
                 check_ci_llvm!(llvm.link_shared);
                 check_ci_llvm!(llvm.static_libstdcpp);
                 check_ci_llvm!(llvm.targets);
@@ -957,26 +1010,14 @@ impl Config {
                 check_ci_llvm!(llvm.clang);
                 check_ci_llvm!(llvm.build_config);
                 check_ci_llvm!(llvm.plugins);
-
-                // CI-built LLVM can be either dynamic or static.
-                let ci_llvm = config.out.join(&*config.build.triple).join("ci-llvm");
-                config.llvm_link_shared = if config.dry_run {
-                    // just assume dynamic for now
-                    true
-                } else {
-                    let link_type = t!(
-                        std::fs::read_to_string(ci_llvm.join("link-type.txt")),
-                        format!("CI llvm missing: {}", ci_llvm.display())
-                    );
-                    link_type == "dynamic"
-                };
             }
 
+            // NOTE: can never be hit when downloading from CI, since we call `check_ci_llvm!(thin_lto)` above.
             if config.llvm_thin_lto && llvm.link_shared.is_none() {
                 // If we're building with ThinLTO on, by default we want to link
                 // to LLVM shared, to avoid re-doing ThinLTO (which happens in
                 // the link step) with each stage.
-                config.llvm_link_shared = true;
+                config.llvm_link_shared.set(Some(true));
             }
         }
 
@@ -992,7 +1033,12 @@ impl Config {
             debuginfo_level_std = rust.debuginfo_level_std;
             debuginfo_level_tools = rust.debuginfo_level_tools;
             debuginfo_level_tests = rust.debuginfo_level_tests;
-            config.rust_run_dsymutil = rust.run_dsymutil.unwrap_or(false);
+            config.rust_split_debuginfo = rust
+                .split_debuginfo
+                .as_deref()
+                .map(SplitDebuginfo::from_str)
+                .map(|v| v.expect("invalid value for rust.split_debuginfo"))
+                .unwrap_or(SplitDebuginfo::default_for_platform(&config.build.triple));
             optimize = rust.optimize;
             ignore_git = rust.ignore_git;
             config.rust_new_symbol_mangling = rust.new_symbol_mangling;
@@ -1226,6 +1272,42 @@ impl Config {
         }
     }
 
+    /// The absolute path to the downloaded LLVM artifacts.
+    pub(crate) fn ci_llvm_root(&self) -> PathBuf {
+        assert!(self.llvm_from_ci);
+        self.out.join(&*self.build.triple).join("ci-llvm")
+    }
+
+    /// Determine whether llvm should be linked dynamically.
+    ///
+    /// If `false`, llvm should be linked statically.
+    /// This is computed on demand since LLVM might have to first be downloaded from CI.
+    pub(crate) fn llvm_link_shared(builder: &Builder<'_>) -> bool {
+        let mut opt = builder.config.llvm_link_shared.get();
+        if opt.is_none() && builder.config.dry_run {
+            // just assume static for now - dynamic linking isn't supported on all platforms
+            return false;
+        }
+
+        let llvm_link_shared = *opt.get_or_insert_with(|| {
+            if builder.config.llvm_from_ci {
+                crate::native::maybe_download_ci_llvm(builder);
+                let ci_llvm = builder.config.ci_llvm_root();
+                let link_type = t!(
+                    std::fs::read_to_string(ci_llvm.join("link-type.txt")),
+                    format!("CI llvm missing: {}", ci_llvm.display())
+                );
+                link_type == "dynamic"
+            } else {
+                // unclear how thought-through this default is, but it maintains compatibility with
+                // previous behavior
+                false
+            }
+        });
+        builder.config.llvm_link_shared.set(opt);
+        llvm_link_shared
+    }
+
     pub fn verbose(&self) -> bool {
         self.verbose > 0
     }
diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs
index e3287e35227..5d812e8b332 100644
--- a/src/bootstrap/dist.rs
+++ b/src/bootstrap/dist.rs
@@ -1904,7 +1904,7 @@ fn maybe_install_llvm(builder: &Builder<'_>, target: TargetSelection, dst_libdir
     // clear why this is the case, though. llvm-config will emit the versioned
     // paths and we don't want those in the sysroot (as we're expecting
     // unversioned paths).
-    if target.contains("apple-darwin") && builder.config.llvm_link_shared {
+    if target.contains("apple-darwin") && builder.llvm_link_shared() {
         let src_libdir = builder.llvm_out(target).join("lib");
         let llvm_dylib_path = src_libdir.join("libLLVM.dylib");
         if llvm_dylib_path.exists() {
@@ -1939,7 +1939,7 @@ pub fn maybe_install_llvm_target(builder: &Builder<'_>, target: TargetSelection,
     // We do not need to copy LLVM files into the sysroot if it is not
     // dynamically linked; it is already included into librustc_llvm
     // statically.
-    if builder.config.llvm_link_shared {
+    if builder.llvm_link_shared() {
         maybe_install_llvm(builder, target, &dst_libdir);
     }
 }
@@ -1951,7 +1951,7 @@ pub fn maybe_install_llvm_runtime(builder: &Builder<'_>, target: TargetSelection
     // We do not need to copy LLVM files into the sysroot if it is not
     // dynamically linked; it is already included into librustc_llvm
     // statically.
-    if builder.config.llvm_link_shared {
+    if builder.llvm_link_shared() {
         maybe_install_llvm(builder, target, &dst_libdir);
     }
 }
@@ -2077,7 +2077,7 @@ impl Step for RustDev {
         // compiler libraries.
         let dst_libdir = tarball.image_dir().join("lib");
         maybe_install_llvm(builder, target, &dst_libdir);
-        let link_type = if builder.config.llvm_link_shared { "dynamic" } else { "static" };
+        let link_type = if builder.llvm_link_shared() { "dynamic" } else { "static" };
         t!(std::fs::write(tarball.image_dir().join("link-type.txt"), link_type), dst_libdir);
 
         Some(tarball.generate())
diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs
index a2802f76008..fcef784d2d1 100644
--- a/src/bootstrap/doc.rs
+++ b/src/bootstrap/doc.rs
@@ -416,7 +416,7 @@ impl Step for Std {
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
         let builder = run.builder;
-        run.all_krates("test").default_condition(builder.config.docs)
+        run.all_krates("test").path("library").default_condition(builder.config.docs)
     }
 
     fn make_run(run: RunConfig<'_>) {
@@ -477,11 +477,14 @@ impl Step for Std {
             .iter()
             .map(components_simplified)
             .filter_map(|path| {
-                if path.get(0) == Some(&"library") {
+                if path.len() >= 2 && path.get(0) == Some(&"library") {
+                    // single crate
                     Some(path[1].to_owned())
                 } else if !path.is_empty() {
+                    // ??
                     Some(path[0].to_owned())
                 } else {
+                    // all library crates
                     None
                 }
             })
diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs
index 1a4e6a96888..58571ea129c 100644
--- a/src/bootstrap/flags.rs
+++ b/src/bootstrap/flags.rs
@@ -8,12 +8,13 @@ use std::process;
 
 use getopts::Options;
 
-use crate::builder::Builder;
+use crate::builder::{Builder, Kind};
 use crate::config::{Config, TargetSelection};
 use crate::setup::Profile;
 use crate::util::t;
 use crate::{Build, DocTests};
 
+#[derive(Copy, Clone)]
 pub enum Color {
     Always,
     Never,
@@ -79,6 +80,7 @@ pub struct Flags {
     pub llvm_profile_generate: bool,
 }
 
+#[cfg_attr(test, derive(Clone))]
 pub enum Subcommand {
     Build {
         paths: Vec<PathBuf>,
@@ -243,27 +245,7 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`",
         // the subcommand. Therefore we must manually identify the subcommand first, so that we can
         // complete the definition of the options.  Then we can use the getopt::Matches object from
         // there on out.
-        let subcommand = args.iter().find(|&s| {
-            (s == "build")
-                || (s == "b")
-                || (s == "check")
-                || (s == "c")
-                || (s == "clippy")
-                || (s == "fix")
-                || (s == "fmt")
-                || (s == "test")
-                || (s == "t")
-                || (s == "bench")
-                || (s == "doc")
-                || (s == "d")
-                || (s == "clean")
-                || (s == "dist")
-                || (s == "install")
-                || (s == "run")
-                || (s == "r")
-                || (s == "setup")
-        });
-        let subcommand = match subcommand {
+        let subcommand = match args.iter().find_map(|s| Kind::parse(&s)) {
             Some(s) => s,
             None => {
                 // No or an invalid subcommand -- show the general usage and subcommand help
@@ -276,8 +258,8 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`",
         };
 
         // Some subcommands get extra options
-        match subcommand.as_str() {
-            "test" | "t" => {
+        match subcommand {
+            Kind::Test => {
                 opts.optflag("", "no-fail-fast", "Run all tests regardless of failure");
                 opts.optmulti(
                     "",
@@ -316,22 +298,22 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`",
                         `/<build_base>/rustfix_missing_coverage.txt`",
                 );
             }
-            "check" | "c" => {
+            Kind::Check => {
                 opts.optflag("", "all-targets", "Check all targets");
             }
-            "bench" => {
+            Kind::Bench => {
                 opts.optmulti("", "test-args", "extra arguments", "ARGS");
             }
-            "clippy" => {
+            Kind::Clippy => {
                 opts.optflag("", "fix", "automatically apply lint suggestions");
             }
-            "doc" | "d" => {
+            Kind::Doc => {
                 opts.optflag("", "open", "open the docs in a browser");
             }
-            "clean" => {
+            Kind::Clean => {
                 opts.optflag("", "all", "clean all build artifacts");
             }
-            "fmt" => {
+            Kind::Format => {
                 opts.optflag("", "check", "check formatting instead of applying.");
             }
             _ => {}
@@ -339,25 +321,22 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`",
 
         // fn usage()
         let usage = |exit_code: i32, opts: &Options, verbose: bool, subcommand_help: &str| -> ! {
-            let mut extra_help = String::new();
-
-            // All subcommands except `clean` can have an optional "Available paths" section
-            if verbose {
-                let config = Config::parse(&["build".to_string()]);
-                let build = Build::new(config);
-
-                let maybe_rules_help = Builder::get_help(&build, subcommand.as_str());
-                extra_help.push_str(maybe_rules_help.unwrap_or_default().as_str());
-            } else if !(subcommand.as_str() == "clean" || subcommand.as_str() == "fmt") {
-                extra_help.push_str(
-                    format!("Run `./x.py {} -h -v` to see a list of available paths.", subcommand)
-                        .as_str(),
-                );
-            }
+            let config = Config::parse(&["build".to_string()]);
+            let build = Build::new(config);
+            let paths = Builder::get_help(&build, subcommand);
 
             println!("{}", opts.usage(subcommand_help));
-            if !extra_help.is_empty() {
-                println!("{}", extra_help);
+            if let Some(s) = paths {
+                if verbose {
+                    println!("{}", s);
+                } else {
+                    println!(
+                        "Run `./x.py {} -h -v` to see a list of available paths.",
+                        subcommand.as_str()
+                    );
+                }
+            } else if verbose {
+                panic!("No paths available for subcommand `{}`", subcommand.as_str());
             }
             process::exit(exit_code);
         };
@@ -375,7 +354,7 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`",
         //            ^-- option  ^     ^- actual subcommand
         //                        \_ arg to option could be mistaken as subcommand
         let mut pass_sanity_check = true;
-        match matches.free.get(0) {
+        match matches.free.get(0).and_then(|s| Kind::parse(&s)) {
             Some(check_subcommand) => {
                 if check_subcommand != subcommand {
                     pass_sanity_check = false;
@@ -394,8 +373,8 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`",
             process::exit(1);
         }
         // Extra help text for some commands
-        match subcommand.as_str() {
-            "build" | "b" => {
+        match subcommand {
+            Kind::Build => {
                 subcommand_help.push_str(
                     "\n
 Arguments:
@@ -415,7 +394,7 @@ Arguments:
         ./x.py build ",
                 );
             }
-            "check" | "c" => {
+            Kind::Check => {
                 subcommand_help.push_str(
                     "\n
 Arguments:
@@ -427,7 +406,7 @@ Arguments:
     If no arguments are passed then many artifacts are checked.",
                 );
             }
-            "clippy" => {
+            Kind::Clippy => {
                 subcommand_help.push_str(
                     "\n
 Arguments:
@@ -438,7 +417,7 @@ Arguments:
         ./x.py clippy library/core library/proc_macro",
                 );
             }
-            "fix" => {
+            Kind::Fix => {
                 subcommand_help.push_str(
                     "\n
 Arguments:
@@ -449,7 +428,7 @@ Arguments:
         ./x.py fix library/core library/proc_macro",
                 );
             }
-            "fmt" => {
+            Kind::Format => {
                 subcommand_help.push_str(
                     "\n
 Arguments:
@@ -460,7 +439,7 @@ Arguments:
         ./x.py fmt --check",
                 );
             }
-            "test" | "t" => {
+            Kind::Test => {
                 subcommand_help.push_str(
                     "\n
 Arguments:
@@ -488,7 +467,7 @@ Arguments:
         ./x.py test --stage 1",
                 );
             }
-            "doc" | "d" => {
+            Kind::Doc => {
                 subcommand_help.push_str(
                     "\n
 Arguments:
@@ -506,7 +485,7 @@ Arguments:
         ./x.py doc --stage 1",
                 );
             }
-            "run" | "r" => {
+            Kind::Run => {
                 subcommand_help.push_str(
                     "\n
 Arguments:
@@ -518,7 +497,7 @@ Arguments:
     At least a tool needs to be called.",
                 );
             }
-            "setup" => {
+            Kind::Setup => {
                 subcommand_help.push_str(&format!(
                     "\n
 x.py setup creates a `config.toml` which changes the defaults for x.py itself.
@@ -535,7 +514,7 @@ Arguments:
                     Profile::all_for_help("        ").trim_end()
                 ));
             }
-            _ => {}
+            Kind::Bench | Kind::Clean | Kind::Dist | Kind::Install => {}
         };
         // Get any optional paths which occur after the subcommand
         let mut paths = matches.free[1..].iter().map(|p| p.into()).collect::<Vec<PathBuf>>();
@@ -547,9 +526,9 @@ Arguments:
             usage(0, &opts, verbose, &subcommand_help);
         }
 
-        let cmd = match subcommand.as_str() {
-            "build" | "b" => Subcommand::Build { paths },
-            "check" | "c" => {
+        let cmd = match subcommand {
+            Kind::Build => Subcommand::Build { paths },
+            Kind::Check => {
                 if matches.opt_present("all-targets") {
                     eprintln!(
                         "Warning: --all-targets is now on by default and does not need to be passed explicitly."
@@ -557,9 +536,9 @@ Arguments:
                 }
                 Subcommand::Check { paths }
             }
-            "clippy" => Subcommand::Clippy { paths, fix: matches.opt_present("fix") },
-            "fix" => Subcommand::Fix { paths },
-            "test" | "t" => Subcommand::Test {
+            Kind::Clippy => Subcommand::Clippy { paths, fix: matches.opt_present("fix") },
+            Kind::Fix => Subcommand::Fix { paths },
+            Kind::Test => Subcommand::Test {
                 paths,
                 bless: matches.opt_present("bless"),
                 force_rerun: matches.opt_present("force-rerun"),
@@ -578,9 +557,9 @@ Arguments:
                     DocTests::Yes
                 },
             },
-            "bench" => Subcommand::Bench { paths, test_args: matches.opt_strs("test-args") },
-            "doc" | "d" => Subcommand::Doc { paths, open: matches.opt_present("open") },
-            "clean" => {
+            Kind::Bench => Subcommand::Bench { paths, test_args: matches.opt_strs("test-args") },
+            Kind::Doc => Subcommand::Doc { paths, open: matches.opt_present("open") },
+            Kind::Clean => {
                 if !paths.is_empty() {
                     println!("\nclean does not take a path argument\n");
                     usage(1, &opts, verbose, &subcommand_help);
@@ -588,17 +567,17 @@ Arguments:
 
                 Subcommand::Clean { all: matches.opt_present("all") }
             }
-            "fmt" => Subcommand::Format { check: matches.opt_present("check"), paths },
-            "dist" => Subcommand::Dist { paths },
-            "install" => Subcommand::Install { paths },
-            "run" | "r" => {
+            Kind::Format => Subcommand::Format { check: matches.opt_present("check"), paths },
+            Kind::Dist => Subcommand::Dist { paths },
+            Kind::Install => Subcommand::Install { paths },
+            Kind::Run => {
                 if paths.is_empty() {
                     println!("\nrun requires at least a path!\n");
                     usage(1, &opts, verbose, &subcommand_help);
                 }
                 Subcommand::Run { paths }
             }
-            "setup" => {
+            Kind::Setup => {
                 let profile = if paths.len() > 1 {
                     println!("\nat most one profile can be passed to setup\n");
                     usage(1, &opts, verbose, &subcommand_help)
@@ -618,9 +597,6 @@ Arguments:
                 };
                 Subcommand::Setup { profile }
             }
-            _ => {
-                usage(1, &opts, verbose, &subcommand_help);
-            }
         };
 
         if let Subcommand::Check { .. } = &cmd {
@@ -694,6 +670,24 @@ Arguments:
 }
 
 impl Subcommand {
+    pub fn kind(&self) -> Kind {
+        match self {
+            Subcommand::Bench { .. } => Kind::Bench,
+            Subcommand::Build { .. } => Kind::Build,
+            Subcommand::Check { .. } => Kind::Check,
+            Subcommand::Clippy { .. } => Kind::Clippy,
+            Subcommand::Doc { .. } => Kind::Doc,
+            Subcommand::Fix { .. } => Kind::Fix,
+            Subcommand::Format { .. } => Kind::Format,
+            Subcommand::Test { .. } => Kind::Test,
+            Subcommand::Clean { .. } => Kind::Clean,
+            Subcommand::Dist { .. } => Kind::Dist,
+            Subcommand::Install { .. } => Kind::Install,
+            Subcommand::Run { .. } => Kind::Run,
+            Subcommand::Setup { .. } => Kind::Setup,
+        }
+    }
+
     pub fn test_args(&self) -> Vec<&str> {
         match *self {
             Subcommand::Test { ref test_args, .. } | Subcommand::Bench { ref test_args, .. } => {
diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs
index 59102ad9f50..b4b973b4247 100644
--- a/src/bootstrap/lib.rs
+++ b/src/bootstrap/lib.rs
@@ -1391,6 +1391,16 @@ impl Build {
         paths
     }
 
+    /// Create a temporary directory in `out` and return its path.
+    ///
+    /// NOTE: this temporary directory is shared between all steps;
+    /// if you need an empty directory, create a new subdirectory inside it.
+    fn tempdir(&self) -> PathBuf {
+        let tmp = self.out.join("tmp");
+        t!(fs::create_dir_all(&tmp));
+        tmp
+    }
+
     /// Copies a file from `src` to `dst`
     pub fn copy(&self, src: &Path, dst: &Path) {
         if self.config.dry_run {
diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs
index 73fb2dad1e3..64e25f803b2 100644
--- a/src/bootstrap/native.rs
+++ b/src/bootstrap/native.rs
@@ -12,9 +12,12 @@ use std::env;
 use std::env::consts::EXE_EXTENSION;
 use std::ffi::{OsStr, OsString};
 use std::fs::{self, File};
-use std::io;
+use std::io::{self, BufRead, BufReader, ErrorKind};
 use std::path::{Path, PathBuf};
-use std::process::Command;
+use std::process::{Command, Stdio};
+
+use once_cell::sync::OnceCell;
+use xz2::bufread::XzDecoder;
 
 use crate::builder::{Builder, RunConfig, ShouldRun, Step};
 use crate::config::TargetSelection;
@@ -62,6 +65,8 @@ pub fn prebuilt_llvm_config(
     builder: &Builder<'_>,
     target: TargetSelection,
 ) -> Result<PathBuf, Meta> {
+    maybe_download_ci_llvm(builder);
+
     // If we're using a custom LLVM bail out here, but we can only use a
     // custom LLVM for the build triple.
     if let Some(config) = builder.config.target_config.get(&target) {
@@ -111,6 +116,285 @@ pub fn prebuilt_llvm_config(
     Err(Meta { stamp, build_llvm_config, out_dir, root: root.into() })
 }
 
+pub(crate) fn maybe_download_ci_llvm(builder: &Builder<'_>) {
+    let config = &builder.config;
+    if !config.llvm_from_ci {
+        return;
+    }
+    let mut rev_list = Command::new("git");
+    rev_list.args(&[
+        PathBuf::from("rev-list"),
+        "--author=bors@rust-lang.org".into(),
+        "-n1".into(),
+        "--first-parent".into(),
+        "HEAD".into(),
+        "--".into(),
+        builder.src.join("src/llvm-project"),
+        builder.src.join("src/bootstrap/download-ci-llvm-stamp"),
+        // the LLVM shared object file is named `LLVM-12-rust-{version}-nightly`
+        builder.src.join("src/version"),
+    ]);
+    let llvm_sha = output(&mut rev_list);
+    let llvm_sha = llvm_sha.trim();
+
+    if llvm_sha == "" {
+        println!("error: could not find commit hash for downloading LLVM");
+        println!("help: maybe your repository history is too shallow?");
+        println!("help: consider disabling `download-ci-llvm`");
+        println!("help: or fetch enough history to include one upstream commit");
+        panic!();
+    }
+
+    let llvm_root = config.ci_llvm_root();
+    let llvm_stamp = llvm_root.join(".llvm-stamp");
+    let key = format!("{}{}", llvm_sha, config.llvm_assertions);
+    if program_out_of_date(&llvm_stamp, &key) && !config.dry_run {
+        download_ci_llvm(builder, &llvm_sha);
+        for binary in ["llvm-config", "FileCheck"] {
+            fix_bin_or_dylib(builder, &llvm_root.join("bin").join(binary));
+        }
+        let llvm_lib = llvm_root.join("lib");
+        for entry in t!(fs::read_dir(&llvm_lib)) {
+            let lib = t!(entry).path();
+            if lib.ends_with(".so") {
+                fix_bin_or_dylib(builder, &lib);
+            }
+        }
+        t!(fs::write(llvm_stamp, key));
+    }
+}
+
+fn download_ci_llvm(builder: &Builder<'_>, llvm_sha: &str) {
+    let llvm_assertions = builder.config.llvm_assertions;
+
+    let cache_prefix = format!("llvm-{}-{}", llvm_sha, llvm_assertions);
+    let cache_dst = builder.out.join("cache");
+    let rustc_cache = cache_dst.join(cache_prefix);
+    if !rustc_cache.exists() {
+        t!(fs::create_dir_all(&rustc_cache));
+    }
+    let base = "https://ci-artifacts.rust-lang.org";
+    let url = if llvm_assertions {
+        format!("rustc-builds-alt/{}", llvm_sha)
+    } else {
+        format!("rustc-builds/{}", llvm_sha)
+    };
+    let filename = format!("rust-dev-nightly-{}.tar.xz", builder.build.build.triple);
+    let tarball = rustc_cache.join(&filename);
+    if !tarball.exists() {
+        download_component(builder, base, &format!("{}/{}", url, filename), &tarball);
+    }
+    let llvm_root = builder.config.ci_llvm_root();
+    unpack(builder, &tarball, &llvm_root);
+}
+
+/// Modifies the interpreter section of 'fname' to fix the dynamic linker,
+/// or the RPATH section, to fix the dynamic library search path
+///
+/// This is only required on NixOS and uses the PatchELF utility to
+/// change the interpreter/RPATH of ELF executables.
+///
+/// Please see https://nixos.org/patchelf.html for more information
+fn fix_bin_or_dylib(builder: &Builder<'_>, fname: &Path) {
+    // FIXME: cache NixOS detection?
+    match Command::new("uname").arg("-s").stderr(Stdio::inherit()).output() {
+        Err(_) => return,
+        Ok(output) if !output.status.success() => return,
+        Ok(output) => {
+            let mut s = output.stdout;
+            if s.last() == Some(&b'\n') {
+                s.pop();
+            }
+            if s != b"Linux" {
+                return;
+            }
+        }
+    }
+
+    // If the user has asked binaries to be patched for Nix, then
+    // don't check for NixOS or `/lib`, just continue to the patching.
+    // FIXME: shouldn't this take precedence over the `uname` check above?
+    if !builder.config.patch_binaries_for_nix {
+        // Use `/etc/os-release` instead of `/etc/NIXOS`.
+        // The latter one does not exist on NixOS when using tmpfs as root.
+        const NIX_IDS: &[&str] = &["ID=nixos", "ID='nixos'", "ID=\"nixos\""];
+        let os_release = match File::open("/etc/os-release") {
+            Err(e) if e.kind() == ErrorKind::NotFound => return,
+            Err(e) => panic!("failed to access /etc/os-release: {}", e),
+            Ok(f) => f,
+        };
+        if !BufReader::new(os_release).lines().any(|l| NIX_IDS.contains(&t!(l).trim())) {
+            return;
+        }
+        if Path::new("/lib").exists() {
+            return;
+        }
+    }
+
+    // At this point we're pretty sure the user is running NixOS or using Nix
+    println!("info: you seem to be using Nix. Attempting to patch {}", fname.display());
+
+    // Only build `.nix-deps` once.
+    static NIX_DEPS_DIR: OnceCell<PathBuf> = OnceCell::new();
+    let mut nix_build_succeeded = true;
+    let nix_deps_dir = NIX_DEPS_DIR.get_or_init(|| {
+        // Run `nix-build` to "build" each dependency (which will likely reuse
+        // the existing `/nix/store` copy, or at most download a pre-built copy).
+        //
+        // Importantly, we create a gc-root called `.nix-deps` in the `build/`
+        // directory, but still reference the actual `/nix/store` path in the rpath
+        // as it makes it significantly more robust against changes to the location of
+        // the `.nix-deps` location.
+        //
+        // bintools: Needed for the path of `ld-linux.so` (via `nix-support/dynamic-linker`).
+        // zlib: Needed as a system dependency of `libLLVM-*.so`.
+        // patchelf: Needed for patching ELF binaries (see doc comment above).
+        let nix_deps_dir = builder.out.join(".nix-deps");
+        const NIX_EXPR: &str = "
+        with (import <nixpkgs> {});
+        symlinkJoin {
+            name = \"rust-stage0-dependencies\";
+            paths = [
+                zlib
+                patchelf
+                stdenv.cc.bintools
+            ];
+        }
+        ";
+        nix_build_succeeded = builder.try_run(Command::new("nix-build").args(&[
+            Path::new("-E"),
+            Path::new(NIX_EXPR),
+            Path::new("-o"),
+            &nix_deps_dir,
+        ]));
+        nix_deps_dir
+    });
+    if !nix_build_succeeded {
+        return;
+    }
+
+    let mut patchelf = Command::new(nix_deps_dir.join("bin/patchelf"));
+    let rpath_entries = {
+        // ORIGIN is a relative default, all binary and dynamic libraries we ship
+        // appear to have this (even when `../lib` is redundant).
+        // NOTE: there are only two paths here, delimited by a `:`
+        let mut entries = OsString::from("$ORIGIN/../lib:");
+        entries.push(t!(fs::canonicalize(nix_deps_dir)));
+        entries.push("/lib");
+        entries
+    };
+    patchelf.args(&[OsString::from("--set-rpath"), rpath_entries]);
+    if !fname.ends_with(".so") {
+        // Finally, set the corret .interp for binaries
+        let dynamic_linker_path = nix_deps_dir.join("nix-support/dynamic-linker");
+        // FIXME: can we support utf8 here? `args` doesn't accept Vec<u8>, only OsString ...
+        let dynamic_linker = t!(String::from_utf8(t!(fs::read(dynamic_linker_path))));
+        patchelf.args(&["--set-interpreter", dynamic_linker.trim_end()]);
+    }
+
+    builder.try_run(patchelf.arg(fname));
+}
+
+fn download_component(builder: &Builder<'_>, base: &str, url: &str, dest_path: &Path) {
+    // Use a temporary file in case we crash while downloading, to avoid a corrupt download in cache/.
+    let tempfile = builder.tempdir().join(dest_path.file_name().unwrap());
+    // FIXME: support `do_verify` (only really needed for nightly rustfmt)
+    // FIXME: support non-utf8 paths?
+    download_with_retries(builder, tempfile.to_str().unwrap(), &format!("{}/{}", base, url));
+    t!(std::fs::rename(&tempfile, dest_path));
+}
+
+fn download_with_retries(builder: &Builder<'_>, tempfile: &str, url: &str) {
+    println!("downloading {}", url);
+
+    // FIXME: check if curl is installed instead of skipping straight to powershell
+    if builder.build.build.contains("windows-msvc") {
+        for _ in 0..3 {
+            if builder.try_run(Command::new("PowerShell.exe").args(&[
+                "/nologo",
+                "-Command",
+                "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;",
+                &format!(
+                    "(New-Object System.Net.WebClient).DownloadFile('{}', '{}')",
+                    url, tempfile
+                ),
+            ])) {
+                return;
+            }
+            println!("\nspurious failure, trying again");
+        }
+    } else {
+        builder.run(Command::new("curl").args(&[
+            "-#",
+            "-y",
+            "30",
+            "-Y",
+            "10", // timeout if speed is < 10 bytes/sec for > 30 seconds
+            "--connect-timeout",
+            "30", // timeout if cannot connect within 30 seconds
+            "--retry",
+            "3",
+            "-Sf",
+            "-o",
+            tempfile,
+            url,
+        ]));
+    }
+}
+
+fn unpack(builder: &Builder<'_>, tarball: &Path, dst: &Path) {
+    println!("extracting {} to {}", tarball.display(), dst.display());
+    if !dst.exists() {
+        t!(fs::create_dir_all(dst));
+    }
+
+    // FIXME: will need to be a parameter once `download-rustc` is moved to rustbuild
+    const MATCH: &str = "rust-dev";
+
+    // `tarball` ends with `.tar.xz`; strip that suffix
+    // example: `rust-dev-nightly-x86_64-unknown-linux-gnu`
+    let uncompressed_filename =
+        Path::new(tarball.file_name().expect("missing tarball filename")).file_stem().unwrap();
+    let directory_prefix = Path::new(Path::new(uncompressed_filename).file_stem().unwrap());
+
+    // decompress the file
+    let data = t!(File::open(tarball));
+    let decompressor = XzDecoder::new(BufReader::new(data));
+
+    let mut tar = tar::Archive::new(decompressor);
+    for member in t!(tar.entries()) {
+        let mut member = t!(member);
+        let original_path = t!(member.path()).into_owned();
+        // skip the top-level directory
+        if original_path == directory_prefix {
+            continue;
+        }
+        let mut short_path = t!(original_path.strip_prefix(directory_prefix));
+        if !short_path.starts_with(MATCH) {
+            continue;
+        }
+        short_path = t!(short_path.strip_prefix(MATCH));
+        let dst_path = dst.join(short_path);
+        builder.verbose(&format!("extracting {} to {}", original_path.display(), dst.display()));
+        if !t!(member.unpack_in(dst)) {
+            panic!("path traversal attack ??");
+        }
+        let src_path = dst.join(original_path);
+        if src_path.is_dir() && dst_path.exists() {
+            continue;
+        }
+        t!(fs::rename(src_path, dst_path));
+    }
+    t!(fs::remove_dir_all(dst.join(directory_prefix)));
+}
+
+fn program_out_of_date(stamp: &Path, key: &str) -> bool {
+    if !stamp.exists() {
+        return true;
+    }
+    t!(fs::read_to_string(stamp)) != key
+}
+
 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
 pub struct Llvm {
     pub target: TargetSelection,
@@ -153,7 +437,7 @@ impl Step for Llvm {
             };
 
         builder.update_submodule(&Path::new("src").join("llvm-project"));
-        if builder.config.llvm_link_shared
+        if builder.llvm_link_shared()
             && (target.contains("windows") || target.contains("apple-darwin"))
         {
             panic!("shared linking to LLVM is not currently supported on {}", target.triple);
@@ -255,7 +539,7 @@ impl Step for Llvm {
         //
         // If we're not linking rustc to a dynamic LLVM, though, then don't link
         // tools to it.
-        if builder.llvm_link_tools_dynamically(target) && builder.config.llvm_link_shared {
+        if builder.llvm_link_tools_dynamically(target) && builder.llvm_link_shared() {
             cfg.define("LLVM_LINK_LLVM_DYLIB", "ON");
         }
 
diff --git a/src/bootstrap/tarball.rs b/src/bootstrap/tarball.rs
index c743c5188e7..689b4819cdd 100644
--- a/src/bootstrap/tarball.rs
+++ b/src/bootstrap/tarball.rs
@@ -262,11 +262,13 @@ impl<'a> Tarball<'a> {
         t!(std::fs::rename(&self.image_dir, &dest));
 
         self.run(|this, cmd| {
+            let distdir = crate::dist::distdir(this.builder);
+            t!(std::fs::create_dir_all(&distdir));
             cmd.arg("tarball")
                 .arg("--input")
                 .arg(&dest)
                 .arg("--output")
-                .arg(crate::dist::distdir(this.builder).join(this.package_name()));
+                .arg(distdir.join(this.package_name()));
         })
     }
 
diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs
index da468909811..1193546992c 100644
--- a/src/bootstrap/test.rs
+++ b/src/bootstrap/test.rs
@@ -1400,9 +1400,7 @@ note: if you're sure you want to do this, please open an issue as to why. In the
         targetflags.extend(builder.lld_flags(target));
         cmd.arg("--target-rustcflags").arg(targetflags.join(" "));
 
-        cmd.arg("--docck-python").arg(builder.python());
-
-        cmd.arg("--lldb-python").arg(builder.python());
+        cmd.arg("--python").arg(builder.python());
 
         if let Some(ref gdb) = builder.config.gdb {
             cmd.arg("--gdb").arg(gdb);
@@ -1577,9 +1575,7 @@ note: if you're sure you want to do this, please open an issue as to why. In the
             cmd.env("RUSTC_PROFILER_SUPPORT", "1");
         }
 
-        let tmp = builder.out.join("tmp");
-        std::fs::create_dir_all(&tmp).unwrap();
-        cmd.env("RUST_TEST_TMPDIR", tmp);
+        cmd.env("RUST_TEST_TMPDIR", builder.tempdir());
 
         cmd.arg("--adb-path").arg("adb");
         cmd.arg("--adb-test-dir").arg(ADB_TEST_DIR);
@@ -2259,14 +2255,13 @@ impl Step for RemoteCopyLibs {
         builder.ensure(compile::Std { compiler, target });
 
         builder.info(&format!("REMOTE copy libs to emulator ({})", target));
-        t!(fs::create_dir_all(builder.out.join("tmp")));
 
         let server = builder.ensure(tool::RemoteTestServer { compiler, target });
 
         // Spawn the emulator and wait for it to come online
         let tool = builder.tool_exe(Tool::RemoteTestClient);
         let mut cmd = Command::new(&tool);
-        cmd.arg("spawn-emulator").arg(target.triple).arg(&server).arg(builder.out.join("tmp"));
+        cmd.arg("spawn-emulator").arg(target.triple).arg(&server).arg(builder.tempdir());
         if let Some(rootfs) = builder.qemu_rootfs(target) {
             cmd.arg(rootfs);
         }
@@ -2300,7 +2295,7 @@ impl Step for Distcheck {
     /// Runs "distcheck", a 'make check' from a tarball
     fn run(self, builder: &Builder<'_>) {
         builder.info("Distcheck");
-        let dir = builder.out.join("tmp").join("distcheck");
+        let dir = builder.tempdir().join("distcheck");
         let _ = fs::remove_dir_all(&dir);
         t!(fs::create_dir_all(&dir));
 
@@ -2326,7 +2321,7 @@ impl Step for Distcheck {
 
         // Now make sure that rust-src has all of libstd's dependencies
         builder.info("Distcheck rust-src");
-        let dir = builder.out.join("tmp").join("distcheck-src");
+        let dir = builder.tempdir().join("distcheck-src");
         let _ = fs::remove_dir_all(&dir);
         t!(fs::create_dir_all(&dir));
 
diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh b/src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh
index 562be752f84..495c539d069 100755
--- a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh
+++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh
@@ -4,7 +4,7 @@ set -ex
 
 source shared.sh
 
-LLVM=llvmorg-13.0.0
+LLVM=llvmorg-14.0.2
 
 mkdir llvm-project
 cd llvm-project
@@ -30,7 +30,12 @@ hide_output \
       -DCMAKE_BUILD_TYPE=Release \
       -DCMAKE_INSTALL_PREFIX=/rustroot \
       -DCOMPILER_RT_BUILD_SANITIZERS=OFF \
+      -DCOMPILER_RT_BUILD_XRAY=OFF \
+      -DCOMPILER_RT_BUILD_MEMPROF=OFF \
       -DLLVM_TARGETS_TO_BUILD=X86 \
+      -DLLVM_INCLUDE_BENCHMARKS=OFF \
+      -DLLVM_INCLUDE_TESTS=OFF \
+      -DLLVM_INCLUDE_EXAMPLES=OFF \
       -DLLVM_ENABLE_PROJECTS="clang;lld;compiler-rt" \
       -DC_INCLUDE_DIRS="$INC"
 
diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-gcc.sh b/src/ci/docker/host-x86_64/dist-x86_64-linux/build-gcc.sh
index 3a03eb2bdc8..7992ec3b991 100755
--- a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-gcc.sh
+++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/build-gcc.sh
@@ -3,7 +3,7 @@ set -ex
 
 source shared.sh
 
-GCC=5.5.0
+GCC=7.5.0
 
 curl https://ftp.gnu.org/gnu/gcc/gcc-$GCC/gcc-$GCC.tar.xz | xzcat | tar xf -
 cd gcc-$GCC
diff --git a/src/doc/book b/src/doc/book
-Subproject 765318b844569a642ceef7bf1adab9639cbf6af
+Subproject de0dbffc5812fd885700874e8d258dd334733ac
diff --git a/src/doc/embedded-book b/src/doc/embedded-book
-Subproject a6de8b6e3ea5d4f0de8b7b9a7e5c1405dc2c2dd
+Subproject f7cefbb995eec8c6148f213235e9e2e03268e77
diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example
-Subproject c2a98d9fc5d29c481d42052fbeccfde15ed0311
+Subproject 44a80e8d8bfc5881c9bd69a2cb3a570776ee418
diff --git a/src/doc/rustc-dev-guide b/src/doc/rustc-dev-guide
-Subproject eeb5a83c15b6ae60df3e4f19207376b22c6fbc4
+Subproject 043e60f4f191651e9f8bf52fa32df14defbb23d
diff --git a/src/doc/unstable-book/src/compiler-flags/cf-protection.md b/src/doc/unstable-book/src/compiler-flags/cf-protection.md
index cc580ca9b42..ab698c82ba9 100644
--- a/src/doc/unstable-book/src/compiler-flags/cf-protection.md
+++ b/src/doc/unstable-book/src/compiler-flags/cf-protection.md
@@ -17,7 +17,7 @@ standard library does not ship with CET enabled by default, so you may need to r
 modules with a `cargo` command like:
 
 ```sh
-$ RUSTFLAGS="-Z cf-protection=full" RUSTC="rustc-custom" cargo +nightly build -Z build-std --target x86_64-unknown-linux-gnu
+$ RUSTFLAGS="-Z cf-protection=full" cargo +nightly build -Z build-std --target x86_64-unknown-linux-gnu
 ```
 
 ### Detection
diff --git a/src/doc/unstable-book/src/language-features/yeet-expr.md b/src/doc/unstable-book/src/language-features/yeet-expr.md
new file mode 100644
index 00000000000..bc1ba4c916b
--- /dev/null
+++ b/src/doc/unstable-book/src/language-features/yeet-expr.md
@@ -0,0 +1,26 @@
+# `yeet_expr`
+
+The tracking issue for this feature is: [#96373]
+
+[#96373]: https://github.com/rust-lang/rust/issues/96373
+
+------------------------
+
+The `yeet_expr` feature adds support for `do yeet` expressions,
+which can be used to early-exit from a function or `try` block.
+
+These are highly experimental, thus the placeholder syntax.
+
+```rust,edition2021
+#![feature(yeet_expr)]
+
+fn foo() -> Result<String, i32> {
+    do yeet 4;
+}
+assert_eq!(foo(), Err(4));
+
+fn bar() -> Option<String> {
+    do yeet;
+}
+assert_eq!(bar(), None);
+```
diff --git a/src/etc/check_missing_items.py b/src/etc/check_missing_items.py
index 89696f39262..343dd0387f4 100644
--- a/src/etc/check_missing_items.py
+++ b/src/etc/check_missing_items.py
@@ -49,8 +49,6 @@ def check_generic_param(param):
         ty = param["kind"]["type"]
         if ty["default"]:
             check_type(ty["default"])
-        for bound in ty["bounds"]:
-            check_generic_bound(bound)
     elif "const" in param["kind"]:
         check_type(param["kind"]["const"])
 
diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml
index cc5c583bea8..21efd040663 100644
--- a/src/librustdoc/Cargo.toml
+++ b/src/librustdoc/Cargo.toml
@@ -22,6 +22,7 @@ regex = "1"
 rustdoc-json-types = { path = "../rustdoc-json-types" }
 tracing = "0.1"
 tracing-tree = "0.2.0"
+once_cell = "1.10.0"
 
 [dependencies.tracing-subscriber]
 version = "0.3.3"
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index a070cef2272..d458deddae3 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -238,9 +238,12 @@ impl Clean<Option<Lifetime>> for ty::Region<'_> {
     }
 }
 
-impl Clean<WherePredicate> for hir::WherePredicate<'_> {
-    fn clean(&self, cx: &mut DocContext<'_>) -> WherePredicate {
-        match *self {
+impl Clean<Option<WherePredicate>> for hir::WherePredicate<'_> {
+    fn clean(&self, cx: &mut DocContext<'_>) -> Option<WherePredicate> {
+        if !self.in_where_clause() {
+            return None;
+        }
+        Some(match *self {
             hir::WherePredicate::BoundPredicate(ref wbp) => {
                 let bound_params = wbp
                     .bound_generic_params
@@ -250,11 +253,7 @@ impl Clean<WherePredicate> for hir::WherePredicate<'_> {
                         // Higher-ranked lifetimes can't have bounds.
                         assert_matches!(
                             param,
-                            hir::GenericParam {
-                                kind: hir::GenericParamKind::Lifetime { .. },
-                                bounds: [],
-                                ..
-                            }
+                            hir::GenericParam { kind: hir::GenericParamKind::Lifetime { .. }, .. }
                         );
                         Lifetime(param.name.ident().name)
                     })
@@ -275,7 +274,7 @@ impl Clean<WherePredicate> for hir::WherePredicate<'_> {
                 lhs: wrp.lhs_ty.clean(cx),
                 rhs: wrp.rhs_ty.clean(cx).into(),
             },
-        }
+        })
     }
 }
 
@@ -456,44 +455,75 @@ impl Clean<GenericParamDef> for ty::GenericParamDef {
     }
 }
 
-impl Clean<GenericParamDef> for hir::GenericParam<'_> {
-    fn clean(&self, cx: &mut DocContext<'_>) -> GenericParamDef {
-        let (name, kind) = match self.kind {
-            hir::GenericParamKind::Lifetime { .. } => {
-                let outlives = self
-                    .bounds
+fn clean_generic_param(
+    cx: &mut DocContext<'_>,
+    generics: Option<&hir::Generics<'_>>,
+    param: &hir::GenericParam<'_>,
+) -> GenericParamDef {
+    let (name, kind) = match param.kind {
+        hir::GenericParamKind::Lifetime { .. } => {
+            let outlives = if let Some(generics) = generics {
+                generics
+                    .predicates
                     .iter()
+                    .flat_map(|pred| {
+                        match pred {
+                            hir::WherePredicate::RegionPredicate(rp)
+                                if rp.lifetime.name == hir::LifetimeName::Param(param.name)
+                                    && !rp.in_where_clause =>
+                            {
+                                rp.bounds
+                            }
+                            _ => &[],
+                        }
+                        .iter()
+                    })
                     .map(|bound| match bound {
                         hir::GenericBound::Outlives(lt) => lt.clean(cx),
                         _ => panic!(),
                     })
-                    .collect();
-                (self.name.ident().name, GenericParamDefKind::Lifetime { outlives })
-            }
-            hir::GenericParamKind::Type { ref default, synthetic } => (
-                self.name.ident().name,
+                    .collect()
+            } else {
+                Vec::new()
+            };
+            (param.name.ident().name, GenericParamDefKind::Lifetime { outlives })
+        }
+        hir::GenericParamKind::Type { ref default, synthetic } => {
+            let did = cx.tcx.hir().local_def_id(param.hir_id);
+            let bounds = if let Some(generics) = generics {
+                generics
+                    .bounds_for_param(did)
+                    .filter(|bp| !bp.in_where_clause)
+                    .flat_map(|bp| bp.bounds)
+                    .filter_map(|x| x.clean(cx))
+                    .collect()
+            } else {
+                Vec::new()
+            };
+            (
+                param.name.ident().name,
                 GenericParamDefKind::Type {
-                    did: cx.tcx.hir().local_def_id(self.hir_id).to_def_id(),
-                    bounds: self.bounds.iter().filter_map(|x| x.clean(cx)).collect(),
+                    did: did.to_def_id(),
+                    bounds,
                     default: default.map(|t| t.clean(cx)).map(Box::new),
                     synthetic,
                 },
-            ),
-            hir::GenericParamKind::Const { ref ty, default } => (
-                self.name.ident().name,
-                GenericParamDefKind::Const {
-                    did: cx.tcx.hir().local_def_id(self.hir_id).to_def_id(),
-                    ty: Box::new(ty.clean(cx)),
-                    default: default.map(|ct| {
-                        let def_id = cx.tcx.hir().local_def_id(ct.hir_id);
-                        Box::new(ty::Const::from_anon_const(cx.tcx, def_id).to_string())
-                    }),
-                },
-            ),
-        };
+            )
+        }
+        hir::GenericParamKind::Const { ref ty, default } => (
+            param.name.ident().name,
+            GenericParamDefKind::Const {
+                did: cx.tcx.hir().local_def_id(param.hir_id).to_def_id(),
+                ty: Box::new(ty.clean(cx)),
+                default: default.map(|ct| {
+                    let def_id = cx.tcx.hir().local_def_id(ct.hir_id);
+                    Box::new(ty::Const::from_anon_const(cx.tcx, def_id).to_string())
+                }),
+            },
+        ),
+    };
 
-        GenericParamDef { name, kind }
-    }
+    GenericParamDef { name, kind }
 }
 
 impl Clean<Generics> for hir::Generics<'_> {
@@ -524,7 +554,7 @@ impl Clean<Generics> for hir::Generics<'_> {
             .iter()
             .filter(|param| is_impl_trait(param))
             .map(|param| {
-                let param: GenericParamDef = param.clean(cx);
+                let param = clean_generic_param(cx, Some(self), param);
                 match param.kind {
                     GenericParamDefKind::Lifetime { .. } => unreachable!(),
                     GenericParamDefKind::Type { did, ref bounds, .. } => {
@@ -538,14 +568,14 @@ impl Clean<Generics> for hir::Generics<'_> {
 
         let mut params = Vec::with_capacity(self.params.len());
         for p in self.params.iter().filter(|p| !is_impl_trait(p) && !is_elided_lifetime(p)) {
-            let p = p.clean(cx);
+            let p = clean_generic_param(cx, Some(self), p);
             params.push(p);
         }
         params.extend(impl_trait_params);
 
         let mut generics = Generics {
             params,
-            where_predicates: self.where_clause.predicates.iter().map(|x| x.clean(cx)).collect(),
+            where_predicates: self.predicates.iter().filter_map(|x| x.clean(cx)).collect(),
         };
 
         // Some duplicates are generated for ?Sized bounds between type params and where
@@ -954,7 +984,11 @@ impl Clean<PolyTrait> for hir::PolyTraitRef<'_> {
     fn clean(&self, cx: &mut DocContext<'_>) -> PolyTrait {
         PolyTrait {
             trait_: self.trait_ref.clean(cx),
-            generic_params: self.bound_generic_params.iter().map(|x| x.clean(cx)).collect(),
+            generic_params: self
+                .bound_generic_params
+                .iter()
+                .map(|x| clean_generic_param(cx, None, x))
+                .collect(),
         }
     }
 }
@@ -1305,7 +1339,7 @@ fn clean_qpath(hir_ty: &hir::Ty<'_>, cx: &mut DocContext<'_>) -> Type {
 fn maybe_expand_private_type_alias(cx: &mut DocContext<'_>, path: &hir::Path<'_>) -> Option<Type> {
     let Res::Def(DefKind::TyAlias, def_id) = path.res else { return None };
     // Substitute private type aliases
-    let Some(def_id) = def_id.as_local() else { return None };
+    let def_id = def_id.as_local()?;
     let alias = if !cx.cache.access_levels.is_exported(def_id.to_def_id()) {
         &cx.tcx.hir().expect_item(def_id).kind
     } else {
@@ -1823,7 +1857,8 @@ impl Clean<BareFunctionDecl> for hir::BareFnTy<'_> {
     fn clean(&self, cx: &mut DocContext<'_>) -> BareFunctionDecl {
         let (generic_params, decl) = enter_impl_trait(cx, |cx| {
             // NOTE: generics must be cleaned before args
-            let generic_params = self.generic_params.iter().map(|x| x.clean(cx)).collect();
+            let generic_params =
+                self.generic_params.iter().map(|x| clean_generic_param(cx, None, x)).collect();
             let args = clean_args_from_types_and_names(cx, self.decl.inputs, self.param_names);
             let decl = clean_fn_decl_with_args(cx, self.decl, args);
             (generic_params, decl)
diff --git a/src/librustdoc/clean/render_macro_matchers.rs b/src/librustdoc/clean/render_macro_matchers.rs
index 58ca8869ea9..a9c0fe6f079 100644
--- a/src/librustdoc/clean/render_macro_matchers.rs
+++ b/src/librustdoc/clean/render_macro_matchers.rs
@@ -1,4 +1,4 @@
-use rustc_ast::token::{self, BinOpToken, DelimToken};
+use rustc_ast::token::{self, BinOpToken, Delimiter};
 use rustc_ast::tokenstream::{TokenStream, TokenTree};
 use rustc_ast_pretty::pprust::state::State as Printer;
 use rustc_ast_pretty::pprust::PrintState;
@@ -104,11 +104,11 @@ fn print_tt(printer: &mut Printer<'_>, tt: &TokenTree) {
             let open_delim = printer.token_kind_to_string(&token::OpenDelim(*delim));
             printer.word(open_delim);
             if !tts.is_empty() {
-                if *delim == DelimToken::Brace {
+                if *delim == Delimiter::Brace {
                     printer.space();
                 }
                 print_tts(printer, tts);
-                if *delim == DelimToken::Brace {
+                if *delim == Delimiter::Brace {
                     printer.space();
                 }
             }
@@ -162,9 +162,9 @@ fn print_tts(printer: &mut Printer<'_>, tts: &TokenStream) {
                 (_, _) => (true, Other),
             },
             TokenTree::Delimited(_, delim, _) => match (state, delim) {
-                (Dollar, DelimToken::Paren) => (false, DollarParen),
-                (Pound | PoundBang, DelimToken::Bracket) => (false, Other),
-                (Ident, DelimToken::Paren | DelimToken::Bracket) => (false, Other),
+                (Dollar, Delimiter::Parenthesis) => (false, DollarParen),
+                (Pound | PoundBang, Delimiter::Bracket) => (false, Other),
+                (Ident, Delimiter::Parenthesis | Delimiter::Bracket) => (false, Other),
                 (_, _) => (true, Other),
             },
         };
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index e30bc6e0a97..2b65b8f910c 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -1,13 +1,11 @@
 use std::cell::RefCell;
 use std::default::Default;
-use std::fmt;
 use std::hash::Hash;
-use std::iter;
 use std::lazy::SyncOnceCell as OnceCell;
 use std::path::PathBuf;
 use std::rc::Rc;
 use std::sync::Arc;
-use std::vec;
+use std::{cmp, fmt, iter};
 
 use arrayvec::ArrayVec;
 
@@ -55,6 +53,9 @@ crate use self::Type::{
 };
 crate use self::Visibility::{Inherited, Public};
 
+#[cfg(test)]
+mod tests;
+
 crate type ItemIdSet = FxHashSet<ItemId>;
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
@@ -1028,6 +1029,86 @@ crate fn collapse_doc_fragments(doc_strings: &[DocFragment]) -> String {
     acc
 }
 
+/// Removes excess indentation on comments in order for the Markdown
+/// to be parsed correctly. This is necessary because the convention for
+/// writing documentation is to provide a space between the /// or //! marker
+/// and the doc text, but Markdown is whitespace-sensitive. For example,
+/// a block of text with four-space indentation is parsed as a code block,
+/// so if we didn't unindent comments, these list items
+///
+/// /// A list:
+/// ///
+/// ///    - Foo
+/// ///    - Bar
+///
+/// would be parsed as if they were in a code block, which is likely not what the user intended.
+fn unindent_doc_fragments(docs: &mut Vec<DocFragment>) {
+    // `add` is used in case the most common sugared doc syntax is used ("/// "). The other
+    // fragments kind's lines are never starting with a whitespace unless they are using some
+    // markdown formatting requiring it. Therefore, if the doc block have a mix between the two,
+    // we need to take into account the fact that the minimum indent minus one (to take this
+    // whitespace into account).
+    //
+    // For example:
+    //
+    // /// hello!
+    // #[doc = "another"]
+    //
+    // In this case, you want "hello! another" and not "hello!  another".
+    let add = if docs.windows(2).any(|arr| arr[0].kind != arr[1].kind)
+        && docs.iter().any(|d| d.kind == DocFragmentKind::SugaredDoc)
+    {
+        // In case we have a mix of sugared doc comments and "raw" ones, we want the sugared one to
+        // "decide" how much the minimum indent will be.
+        1
+    } else {
+        0
+    };
+
+    // `min_indent` is used to know how much whitespaces from the start of each lines must be
+    // removed. Example:
+    //
+    // ///     hello!
+    // #[doc = "another"]
+    //
+    // In here, the `min_indent` is 1 (because non-sugared fragment are always counted with minimum
+    // 1 whitespace), meaning that "hello!" will be considered a codeblock because it starts with 4
+    // (5 - 1) whitespaces.
+    let Some(min_indent) = docs
+        .iter()
+        .map(|fragment| {
+            fragment.doc.as_str().lines().fold(usize::MAX, |min_indent, line| {
+                if line.chars().all(|c| c.is_whitespace()) {
+                    min_indent
+                } else {
+                    // Compare against either space or tab, ignoring whether they are
+                    // mixed or not.
+                    let whitespace = line.chars().take_while(|c| *c == ' ' || *c == '\t').count();
+                    cmp::min(min_indent, whitespace)
+                        + if fragment.kind == DocFragmentKind::SugaredDoc { 0 } else { add }
+                }
+            })
+        })
+        .min()
+    else {
+        return;
+    };
+
+    for fragment in docs {
+        if fragment.doc == kw::Empty {
+            continue;
+        }
+
+        let min_indent = if fragment.kind != DocFragmentKind::SugaredDoc && min_indent > 0 {
+            min_indent - add
+        } else {
+            min_indent
+        };
+
+        fragment.indent = min_indent;
+    }
+}
+
 /// A link that has not yet been rendered.
 ///
 /// This link will be turned into a rendered link by [`Item::links`].
@@ -1089,35 +1170,37 @@ impl Attributes {
         attrs: &[ast::Attribute],
         additional_attrs: Option<(&[ast::Attribute], DefId)>,
     ) -> Attributes {
-        let mut doc_strings: Vec<DocFragment> = vec![];
-        let clean_attr = |(attr, parent_module): (&ast::Attribute, Option<DefId>)| {
-            if let Some((value, kind)) = attr.doc_str_and_comment_kind() {
-                trace!("got doc_str={:?}", value);
-                let value = beautify_doc_string(value, kind);
+        // Additional documentation should be shown before the original documentation.
+        let attrs1 = additional_attrs
+            .into_iter()
+            .flat_map(|(attrs, def_id)| attrs.iter().map(move |attr| (attr, Some(def_id))));
+        let attrs2 = attrs.iter().map(|attr| (attr, None));
+        Attributes::from_ast_iter(attrs1.chain(attrs2), false)
+    }
+
+    crate fn from_ast_iter<'a>(
+        attrs: impl Iterator<Item = (&'a ast::Attribute, Option<DefId>)>,
+        doc_only: bool,
+    ) -> Attributes {
+        let mut doc_strings = Vec::new();
+        let mut other_attrs = Vec::new();
+        for (attr, parent_module) in attrs {
+            if let Some((doc_str, comment_kind)) = attr.doc_str_and_comment_kind() {
+                trace!("got doc_str={doc_str:?}");
+                let doc = beautify_doc_string(doc_str, comment_kind);
                 let kind = if attr.is_doc_comment() {
                     DocFragmentKind::SugaredDoc
                 } else {
                     DocFragmentKind::RawDoc
                 };
-
-                let frag =
-                    DocFragment { span: attr.span, doc: value, kind, parent_module, indent: 0 };
-
-                doc_strings.push(frag);
-
-                None
-            } else {
-                Some(attr.clone())
+                let fragment = DocFragment { span: attr.span, doc, kind, parent_module, indent: 0 };
+                doc_strings.push(fragment);
+            } else if !doc_only {
+                other_attrs.push(attr.clone());
             }
-        };
+        }
 
-        // Additional documentation should be shown before the original documentation
-        let other_attrs = additional_attrs
-            .into_iter()
-            .flat_map(|(attrs, id)| attrs.iter().map(move |attr| (attr, Some(id))))
-            .chain(attrs.iter().map(|attr| (attr, None)))
-            .filter_map(clean_attr)
-            .collect();
+        unindent_doc_fragments(&mut doc_strings);
 
         Attributes { doc_strings, other_attrs }
     }
@@ -1138,23 +1221,17 @@ impl Attributes {
     }
 
     /// Return the doc-comments on this item, grouped by the module they came from.
-    ///
     /// The module can be different if this is a re-export with added documentation.
-    crate fn collapsed_doc_value_by_module_level(&self) -> FxHashMap<Option<DefId>, String> {
-        let mut ret = FxHashMap::default();
-        if self.doc_strings.len() == 0 {
-            return ret;
-        }
-        let last_index = self.doc_strings.len() - 1;
-
-        for (i, new_frag) in self.doc_strings.iter().enumerate() {
-            let out = ret.entry(new_frag.parent_module).or_default();
-            add_doc_fragment(out, new_frag);
-            if i == last_index {
-                out.pop();
-            }
+    ///
+    /// The last newline is not trimmed so the produced strings are reusable between
+    /// early and late doc link resolution regardless of their position.
+    crate fn prepare_to_doc_link_resolution(&self) -> FxHashMap<Option<DefId>, String> {
+        let mut res = FxHashMap::default();
+        for fragment in &self.doc_strings {
+            let out_str = res.entry(fragment.parent_module).or_default();
+            add_doc_fragment(out_str, fragment);
         }
-        ret
+        res
     }
 
     /// Finds all `doc` attributes as NameValues and returns their corresponding values, joined
diff --git a/src/librustdoc/passes/unindent_comments/tests.rs b/src/librustdoc/clean/types/tests.rs
index baff839cdc8..71eddf4348f 100644
--- a/src/librustdoc/passes/unindent_comments/tests.rs
+++ b/src/librustdoc/clean/types/tests.rs
@@ -20,7 +20,7 @@ fn create_doc_fragment(s: &str) -> Vec<DocFragment> {
 fn run_test(input: &str, expected: &str) {
     create_default_session_globals_then(|| {
         let mut s = create_doc_fragment(input);
-        unindent_fragments(&mut s);
+        unindent_doc_fragments(&mut s);
         assert_eq!(collapse_doc_fragments(&s), expected);
     });
 }
diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs
index cee3dcb416f..1ff2c8191e5 100644
--- a/src/librustdoc/config.rs
+++ b/src/librustdoc/config.rs
@@ -10,7 +10,9 @@ use rustc_session::config::{
     self, parse_crate_types_from_list, parse_externs, parse_target_triple, CrateType,
 };
 use rustc_session::config::{get_cmd_lint_options, nightly_options};
-use rustc_session::config::{CodegenOptions, DebuggingOptions, ErrorOutputType, Externs};
+use rustc_session::config::{
+    CodegenOptions, DebuggingOptions, ErrorOutputType, Externs, JsonUnusedExterns,
+};
 use rustc_session::getopts;
 use rustc_session::lint::Level;
 use rustc_session::search_paths::SearchPath;
@@ -147,7 +149,7 @@ crate struct Options {
     /// documentation.
     crate run_check: bool,
     /// Whether doctests should emit unused externs
-    crate json_unused_externs: bool,
+    crate json_unused_externs: JsonUnusedExterns,
     /// Whether to skip capturing stdout and stderr of tests.
     crate nocapture: bool,
 
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index b9e20c41b68..1db6064551c 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -4,7 +4,7 @@ use rustc_data_structures::sync::{self, Lrc};
 use rustc_errors::emitter::{Emitter, EmitterWriter};
 use rustc_errors::json::JsonEmitter;
 use rustc_feature::UnstableFeatures;
-use rustc_hir::def::Res;
+use rustc_hir::def::{Namespace, Res};
 use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId};
 use rustc_hir::intravisit::{self, Visitor};
 use rustc_hir::{HirId, Path, TraitCandidate};
@@ -29,11 +29,14 @@ use crate::clean::inline::build_external_trait;
 use crate::clean::{self, ItemId, TraitWithExtraInfo};
 use crate::config::{Options as RustdocOptions, OutputFormat, RenderOptions};
 use crate::formats::cache::Cache;
+use crate::passes::collect_intra_doc_links::PreprocessedMarkdownLink;
 use crate::passes::{self, Condition::*};
 
 crate use rustc_session::config::{DebuggingOptions, Input, Options};
 
 crate struct ResolverCaches {
+    crate markdown_links: Option<FxHashMap<String, Vec<PreprocessedMarkdownLink>>>,
+    crate doc_link_resolutions: FxHashMap<(Symbol, Namespace, DefId), Option<Res<NodeId>>>,
     /// Traits in scope for a given module.
     /// See `collect_intra_doc_links::traits_implemented_by` for more details.
     crate traits_in_scope: DefIdMap<Vec<TraitCandidate>>,
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index 61826844574..82e367427ef 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -168,7 +168,7 @@ crate fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> {
 
     // Collect and warn about unused externs, but only if we've gotten
     // reports for each doctest
-    if json_unused_externs {
+    if json_unused_externs.is_enabled() {
         let unused_extern_reports: Vec<_> =
             std::mem::take(&mut unused_extern_reports.lock().unwrap());
         if unused_extern_reports.len() == compiling_test_count {
@@ -337,7 +337,7 @@ fn run_test(
     if lang_string.test_harness {
         compiler.arg("--test");
     }
-    if rustdoc_options.json_unused_externs && !lang_string.compile_fail {
+    if rustdoc_options.json_unused_externs.is_enabled() && !lang_string.compile_fail {
         compiler.arg("--error-format=json");
         compiler.arg("--json").arg("unused-externs");
         compiler.arg("-Z").arg("unstable-options");
@@ -1174,8 +1174,6 @@ impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> {
         nested: F,
     ) {
         let ast_attrs = self.tcx.hir().attrs(hir_id);
-        let mut attrs = Attributes::from_ast(ast_attrs, None);
-
         if let Some(ref cfg) = ast_attrs.cfg(self.tcx, &FxHashSet::default()) {
             if !cfg.matches(&self.sess.parse_sess, Some(self.sess.features_untracked())) {
                 return;
@@ -1187,9 +1185,9 @@ impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> {
             self.collector.names.push(name);
         }
 
-        attrs.unindent_doc_comments();
         // The collapse-docs pass won't combine sugared/raw doc attributes, or included files with
         // anything else, this will combine them for us.
+        let attrs = Attributes::from_ast(ast_attrs, None);
         if let Some(doc) = attrs.collapsed_doc_value() {
             // Use the outermost invocation, so that doctest names come from where the docs were written.
             let span = ast_attrs
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 6954e2363f5..118807a8286 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -18,6 +18,7 @@ use rustc_hir::def_id::DefId;
 use rustc_middle::ty;
 use rustc_middle::ty::DefIdTree;
 use rustc_middle::ty::TyCtxt;
+use rustc_span::symbol::kw;
 use rustc_span::{sym, Symbol};
 use rustc_target::spec::abi::Abi;
 
@@ -267,6 +268,8 @@ crate fn print_where_clause<'a, 'tcx: 'a>(
     indent: usize,
     end_newline: bool,
 ) -> impl fmt::Display + 'a + Captures<'tcx> {
+    use fmt::Write;
+
     display_fn(move |f| {
         let mut where_predicates = gens.where_predicates.iter().filter(|pred| {
             !matches!(pred, clean::WherePredicate::BoundPredicate { bounds, .. } if bounds.is_empty())
@@ -280,56 +283,44 @@ crate fn print_where_clause<'a, 'tcx: 'a>(
 
                 match pred {
                     clean::WherePredicate::BoundPredicate { ty, bounds, bound_params } => {
-                        let bounds = bounds;
-                        let for_prefix = if bound_params.is_empty() {
-                            String::new()
-                        } else if f.alternate() {
-                            format!(
-                                "for&lt;{:#}&gt; ",
-                                comma_sep(bound_params.iter().map(|lt| lt.print()), true)
-                            )
-                        } else {
-                            format!(
-                                "for&lt;{}&gt; ",
-                                comma_sep(bound_params.iter().map(|lt| lt.print()), true)
-                            )
-                        };
+                        let ty_cx = ty.print(cx);
+                        let generic_bounds = print_generic_bounds(bounds, cx);
 
-                        if f.alternate() {
-                            write!(
-                                f,
-                                "{}{:#}: {:#}",
-                                for_prefix,
-                                ty.print(cx),
-                                print_generic_bounds(bounds, cx)
-                            )
+                        if bound_params.is_empty() {
+                            if f.alternate() {
+                                write!(f, "{ty_cx:#}: {generic_bounds:#}")
+                            } else {
+                                write!(f, "{ty_cx}: {generic_bounds}")
+                            }
                         } else {
-                            write!(
-                                f,
-                                "{}{}: {}",
-                                for_prefix,
-                                ty.print(cx),
-                                print_generic_bounds(bounds, cx)
-                            )
+                            if f.alternate() {
+                                write!(
+                                    f,
+                                    "for<{:#}> {ty_cx:#}: {generic_bounds:#}",
+                                    comma_sep(bound_params.iter().map(|lt| lt.print()), true)
+                                )
+                            } else {
+                                write!(
+                                    f,
+                                    "for&lt;{}&gt; {ty_cx}: {generic_bounds}",
+                                    comma_sep(bound_params.iter().map(|lt| lt.print()), true)
+                                )
+                            }
                         }
                     }
                     clean::WherePredicate::RegionPredicate { lifetime, bounds } => {
-                        write!(
-                            f,
-                            "{}: {}",
-                            lifetime.print(),
-                            bounds
-                                .iter()
-                                .map(|b| b.print(cx).to_string())
-                                .collect::<Vec<_>>()
-                                .join(" + ")
-                        )
+                        let mut bounds_display = String::new();
+                        for bound in bounds.iter().map(|b| b.print(cx)) {
+                            write!(bounds_display, "{bound} + ")?;
+                        }
+                        bounds_display.truncate(bounds_display.len() - " + ".len());
+                        write!(f, "{}: {bounds_display}", lifetime.print())
                     }
                     clean::WherePredicate::EqPredicate { lhs, rhs } => {
                         if f.alternate() {
-                            write!(f, "{:#} == {:#}", lhs.print(cx), rhs.print(cx),)
+                            write!(f, "{:#} == {:#}", lhs.print(cx), rhs.print(cx))
                         } else {
-                            write!(f, "{} == {}", lhs.print(cx), rhs.print(cx),)
+                            write!(f, "{} == {}", lhs.print(cx), rhs.print(cx))
                         }
                     }
                 }
@@ -340,40 +331,43 @@ crate fn print_where_clause<'a, 'tcx: 'a>(
             return Ok(());
         }
 
-        let mut clause = String::new();
-
-        if f.alternate() {
-            clause.push_str(" where");
-        } else {
+        let where_preds = comma_sep(where_predicates, false);
+        let clause = if f.alternate() {
             if end_newline {
-                clause.push_str(" <span class=\"where fmt-newline\">where");
+                // add a space so stripping <br> tags and breaking spaces still renders properly
+                format!(" where{where_preds}, ")
             } else {
-                clause.push_str(" <span class=\"where\">where");
+                format!(" where{where_preds}")
             }
-        }
-
-        clause.push_str(&comma_sep(where_predicates, false).to_string());
-
-        if end_newline {
-            clause.push(',');
-            // add a space so stripping <br> tags and breaking spaces still renders properly
-            if f.alternate() {
-                clause.push(' ');
-            } else {
-                clause.push_str("&nbsp;");
+        } else {
+            let mut br_with_padding = String::with_capacity(6 * indent + 28);
+            br_with_padding.push_str("<br>");
+            for _ in 0..indent + 4 {
+                br_with_padding.push_str("&nbsp;");
             }
-        }
+            let where_preds = where_preds.to_string().replace("<br>", &br_with_padding);
 
-        if !f.alternate() {
-            clause.push_str("</span>");
-            let padding = "&nbsp;".repeat(indent + 4);
-            clause = clause.replace("<br>", &format!("<br>{}", padding));
-            clause.insert_str(0, &"&nbsp;".repeat(indent.saturating_sub(1)));
-            if !end_newline {
-                clause.insert_str(0, "<br>");
+            if end_newline {
+                let mut clause = "&nbsp;".repeat(indent.saturating_sub(1));
+                // add a space so stripping <br> tags and breaking spaces still renders properly
+                write!(
+                    clause,
+                    " <span class=\"where fmt-newline\">where{where_preds},&nbsp;</span>"
+                )?;
+                clause
+            } else {
+                // insert a <br> tag after a single space but before multiple spaces at the start
+                if indent == 0 {
+                    format!(" <br><span class=\"where\">where{where_preds}</span>")
+                } else {
+                    let mut clause = br_with_padding;
+                    clause.truncate(clause.len() - 5 * "&nbsp;".len());
+                    write!(clause, " <span class=\"where\">where{where_preds}</span>")?;
+                    clause
+                }
             }
-        }
-        write!(f, "{}", clause)
+        };
+        write!(f, "{clause}")
     })
 }
 
@@ -686,7 +680,7 @@ fn resolved_path<'cx>(
 
     if print_all {
         for seg in &path.segments[..path.segments.len() - 1] {
-            write!(w, "{}::", seg.name)?;
+            write!(w, "{}::", if seg.name == kw::PathRoot { "" } else { seg.name.as_str() })?;
         }
     }
     if w.alternate() {
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 1ebb41b5933..9e76af98298 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -32,6 +32,7 @@ use rustc_middle::ty::TyCtxt;
 use rustc_span::edition::Edition;
 use rustc_span::Span;
 
+use once_cell::sync::Lazy;
 use std::borrow::Cow;
 use std::cell::RefCell;
 use std::collections::VecDeque;
@@ -1255,7 +1256,7 @@ crate struct MarkdownLink {
     pub range: Range<usize>,
 }
 
-crate fn markdown_links(md: &str) -> Vec<MarkdownLink> {
+crate fn markdown_links<R>(md: &str, filter_map: impl Fn(MarkdownLink) -> Option<R>) -> Vec<R> {
     if md.is_empty() {
         return vec![];
     }
@@ -1295,11 +1296,12 @@ crate fn markdown_links(md: &str) -> Vec<MarkdownLink> {
 
     let mut push = |link: BrokenLink<'_>| {
         let span = span_for_link(&link.reference, link.span);
-        links.borrow_mut().push(MarkdownLink {
+        filter_map(MarkdownLink {
             kind: LinkType::ShortcutUnknown,
             link: link.reference.to_string(),
             range: span,
-        });
+        })
+        .map(|link| links.borrow_mut().push(link));
         None
     };
     let p = Parser::new_with_broken_link_callback(md, main_body_opts(), Some(&mut push))
@@ -1311,10 +1313,23 @@ crate fn markdown_links(md: &str) -> Vec<MarkdownLink> {
     let iter = Footnotes::new(HeadingLinks::new(p, None, &mut ids, HeadingOffset::H1));
 
     for ev in iter {
-        if let Event::Start(Tag::Link(kind, dest, _)) = ev.0 {
+        if let Event::Start(Tag::Link(
+            // `<>` links cannot be intra-doc links so we skip them.
+            kind @ (LinkType::Inline
+            | LinkType::Reference
+            | LinkType::ReferenceUnknown
+            | LinkType::Collapsed
+            | LinkType::CollapsedUnknown
+            | LinkType::Shortcut
+            | LinkType::ShortcutUnknown),
+            dest,
+            _,
+        )) = ev.0
+        {
             debug!("found link: {dest}");
             let span = span_for_link(&dest, ev.1);
-            links.borrow_mut().push(MarkdownLink { kind, link: dest.into_string(), range: span });
+            filter_map(MarkdownLink { kind, link: dest.into_string(), range: span })
+                .map(|link| links.borrow_mut().push(link));
         }
     }
 
@@ -1415,62 +1430,65 @@ crate fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_>) -> Vec<RustCodeB
 
 #[derive(Clone, Default, Debug)]
 pub struct IdMap {
-    map: FxHashMap<String, usize>,
+    map: FxHashMap<Cow<'static, str>, usize>,
 }
 
-fn init_id_map() -> FxHashMap<String, usize> {
+// The map is pre-initialized and cloned each time to avoid reinitializing it repeatedly.
+static DEFAULT_ID_MAP: Lazy<FxHashMap<Cow<'static, str>, usize>> = Lazy::new(|| init_id_map());
+
+fn init_id_map() -> FxHashMap<Cow<'static, str>, usize> {
     let mut map = FxHashMap::default();
     // This is the list of IDs used in Javascript.
-    map.insert("help".to_owned(), 1);
+    map.insert("help".into(), 1);
     // This is the list of IDs used in HTML generated in Rust (including the ones
     // used in tera template files).
-    map.insert("mainThemeStyle".to_owned(), 1);
-    map.insert("themeStyle".to_owned(), 1);
-    map.insert("theme-picker".to_owned(), 1);
-    map.insert("theme-choices".to_owned(), 1);
-    map.insert("settings-menu".to_owned(), 1);
-    map.insert("help-button".to_owned(), 1);
-    map.insert("main-content".to_owned(), 1);
-    map.insert("search".to_owned(), 1);
-    map.insert("crate-search".to_owned(), 1);
-    map.insert("render-detail".to_owned(), 1);
-    map.insert("toggle-all-docs".to_owned(), 1);
-    map.insert("all-types".to_owned(), 1);
-    map.insert("default-settings".to_owned(), 1);
-    map.insert("rustdoc-vars".to_owned(), 1);
-    map.insert("sidebar-vars".to_owned(), 1);
-    map.insert("copy-path".to_owned(), 1);
-    map.insert("TOC".to_owned(), 1);
+    map.insert("mainThemeStyle".into(), 1);
+    map.insert("themeStyle".into(), 1);
+    map.insert("theme-picker".into(), 1);
+    map.insert("theme-choices".into(), 1);
+    map.insert("settings-menu".into(), 1);
+    map.insert("help-button".into(), 1);
+    map.insert("main-content".into(), 1);
+    map.insert("search".into(), 1);
+    map.insert("crate-search".into(), 1);
+    map.insert("render-detail".into(), 1);
+    map.insert("toggle-all-docs".into(), 1);
+    map.insert("all-types".into(), 1);
+    map.insert("default-settings".into(), 1);
+    map.insert("rustdoc-vars".into(), 1);
+    map.insert("sidebar-vars".into(), 1);
+    map.insert("copy-path".into(), 1);
+    map.insert("TOC".into(), 1);
     // This is the list of IDs used by rustdoc sections (but still generated by
     // rustdoc).
-    map.insert("fields".to_owned(), 1);
-    map.insert("variants".to_owned(), 1);
-    map.insert("implementors-list".to_owned(), 1);
-    map.insert("synthetic-implementors-list".to_owned(), 1);
-    map.insert("foreign-impls".to_owned(), 1);
-    map.insert("implementations".to_owned(), 1);
-    map.insert("trait-implementations".to_owned(), 1);
-    map.insert("synthetic-implementations".to_owned(), 1);
-    map.insert("blanket-implementations".to_owned(), 1);
-    map.insert("required-associated-types".to_owned(), 1);
-    map.insert("provided-associated-types".to_owned(), 1);
-    map.insert("provided-associated-consts".to_owned(), 1);
-    map.insert("required-associated-consts".to_owned(), 1);
-    map.insert("required-methods".to_owned(), 1);
-    map.insert("provided-methods".to_owned(), 1);
-    map.insert("implementors".to_owned(), 1);
-    map.insert("synthetic-implementors".to_owned(), 1);
-    map.insert("implementations-list".to_owned(), 1);
-    map.insert("trait-implementations-list".to_owned(), 1);
-    map.insert("synthetic-implementations-list".to_owned(), 1);
-    map.insert("blanket-implementations-list".to_owned(), 1);
-    map.insert("deref-methods".to_owned(), 1);
+    map.insert("fields".into(), 1);
+    map.insert("variants".into(), 1);
+    map.insert("implementors-list".into(), 1);
+    map.insert("synthetic-implementors-list".into(), 1);
+    map.insert("foreign-impls".into(), 1);
+    map.insert("implementations".into(), 1);
+    map.insert("trait-implementations".into(), 1);
+    map.insert("synthetic-implementations".into(), 1);
+    map.insert("blanket-implementations".into(), 1);
+    map.insert("required-associated-types".into(), 1);
+    map.insert("provided-associated-types".into(), 1);
+    map.insert("provided-associated-consts".into(), 1);
+    map.insert("required-associated-consts".into(), 1);
+    map.insert("required-methods".into(), 1);
+    map.insert("provided-methods".into(), 1);
+    map.insert("implementors".into(), 1);
+    map.insert("synthetic-implementors".into(), 1);
+    map.insert("implementations-list".into(), 1);
+    map.insert("trait-implementations-list".into(), 1);
+    map.insert("synthetic-implementations-list".into(), 1);
+    map.insert("blanket-implementations-list".into(), 1);
+    map.insert("deref-methods".into(), 1);
     map
 }
 
 impl IdMap {
     pub fn new() -> Self {
-        IdMap { map: init_id_map() }
+        IdMap { map: DEFAULT_ID_MAP.clone() }
     }
 
     crate fn derive<S: AsRef<str> + ToString>(&mut self, candidate: S) -> String {
@@ -1483,7 +1501,7 @@ impl IdMap {
             }
         };
 
-        self.map.insert(id.clone(), 1);
+        self.map.insert(id.clone().into(), 1);
         id
     }
 }
diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs
index 06c63ec97d7..b5502309560 100644
--- a/src/librustdoc/html/render/span_map.rs
+++ b/src/librustdoc/html/render/span_map.rs
@@ -5,7 +5,7 @@ use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::{self, Visitor};
-use rustc_hir::{ExprKind, GenericParam, GenericParamKind, HirId, Mod, Node};
+use rustc_hir::{ExprKind, GenericParam, HirId, Mod, Node};
 use rustc_middle::hir::nested_filter;
 use rustc_middle::ty::TyCtxt;
 use rustc_span::Span;
@@ -100,16 +100,7 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
         self.tcx.hir()
     }
 
-    fn visit_generic_param(&mut self, p: &'tcx GenericParam<'tcx>) {
-        if !matches!(p.kind, GenericParamKind::Type { .. }) {
-            return;
-        }
-        for bound in p.bounds {
-            if let Some(trait_ref) = bound.trait_ref() {
-                self.handle_path(trait_ref.path, None);
-            }
-        }
-    }
+    fn visit_generic_param(&mut self, _: &'tcx GenericParam<'tcx>) {}
 
     fn visit_path(&mut self, path: &'tcx rustc_hir::Path<'tcx>, _id: HirId) {
         self.handle_path(path, None);
diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs
index 371d0e84087..7c202e471ad 100644
--- a/src/librustdoc/html/render/write_shared.rs
+++ b/src/librustdoc/html/render/write_shared.rs
@@ -21,27 +21,18 @@ use crate::{try_err, try_none};
 
 static FILES_UNVERSIONED: Lazy<FxHashMap<&str, &[u8]>> = Lazy::new(|| {
     map! {
-        "FiraSans-Regular.woff2" => static_files::fira_sans::REGULAR2,
-        "FiraSans-Medium.woff2" => static_files::fira_sans::MEDIUM2,
-        "FiraSans-Regular.woff" => static_files::fira_sans::REGULAR,
-        "FiraSans-Medium.woff" => static_files::fira_sans::MEDIUM,
+        "FiraSans-Regular.woff2" => static_files::fira_sans::REGULAR,
+        "FiraSans-Medium.woff2" => static_files::fira_sans::MEDIUM,
         "FiraSans-LICENSE.txt" => static_files::fira_sans::LICENSE,
-        "SourceSerif4-Regular.ttf.woff2" => static_files::source_serif_4::REGULAR2,
-        "SourceSerif4-Bold.ttf.woff2" => static_files::source_serif_4::BOLD2,
-        "SourceSerif4-It.ttf.woff2" => static_files::source_serif_4::ITALIC2,
-        "SourceSerif4-Regular.ttf.woff" => static_files::source_serif_4::REGULAR,
-        "SourceSerif4-Bold.ttf.woff" => static_files::source_serif_4::BOLD,
-        "SourceSerif4-It.ttf.woff" => static_files::source_serif_4::ITALIC,
+        "SourceSerif4-Regular.ttf.woff2" => static_files::source_serif_4::REGULAR,
+        "SourceSerif4-Bold.ttf.woff2" => static_files::source_serif_4::BOLD,
+        "SourceSerif4-It.ttf.woff2" => static_files::source_serif_4::ITALIC,
         "SourceSerif4-LICENSE.md" => static_files::source_serif_4::LICENSE,
-        "SourceCodePro-Regular.ttf.woff2" => static_files::source_code_pro::REGULAR2,
-        "SourceCodePro-Semibold.ttf.woff2" => static_files::source_code_pro::SEMIBOLD2,
-        "SourceCodePro-It.ttf.woff2" => static_files::source_code_pro::ITALIC2,
-        "SourceCodePro-Regular.ttf.woff" => static_files::source_code_pro::REGULAR,
-        "SourceCodePro-Semibold.ttf.woff" => static_files::source_code_pro::SEMIBOLD,
-        "SourceCodePro-It.ttf.woff" => static_files::source_code_pro::ITALIC,
+        "SourceCodePro-Regular.ttf.woff2" => static_files::source_code_pro::REGULAR,
+        "SourceCodePro-Semibold.ttf.woff2" => static_files::source_code_pro::SEMIBOLD,
+        "SourceCodePro-It.ttf.woff2" => static_files::source_code_pro::ITALIC,
         "SourceCodePro-LICENSE.txt" => static_files::source_code_pro::LICENSE,
-        "NanumBarunGothic.ttf.woff2" => static_files::nanum_barun_gothic::REGULAR2,
-        "NanumBarunGothic.ttf.woff" => static_files::nanum_barun_gothic::REGULAR,
+        "NanumBarunGothic.ttf.woff2" => static_files::nanum_barun_gothic::REGULAR,
         "NanumBarunGothic-LICENSE.txt" => static_files::nanum_barun_gothic::LICENSE,
         "LICENSE-MIT.txt" => static_files::LICENSE_MIT,
         "LICENSE-APACHE.txt" => static_files::LICENSE_APACHE,
diff --git a/src/librustdoc/html/static/COPYRIGHT.txt b/src/librustdoc/html/static/COPYRIGHT.txt
index c2629a83f70..34e48134cc3 100644
--- a/src/librustdoc/html/static/COPYRIGHT.txt
+++ b/src/librustdoc/html/static/COPYRIGHT.txt
@@ -2,8 +2,7 @@ These documentation pages include resources by third parties. This copyright
 file applies only to those resources. The following third party resources are
 included, and carry their own copyright notices and license terms:
 
-* Fira Sans (FiraSans-Regular.woff2, FiraSans-Medium.woff2,
-    FiraSans-Regular.woff, FiraSans-Medium.woff):
+* Fira Sans (FiraSans-Regular.woff2, FiraSans-Medium.woff2):
 
     Copyright (c) 2014, Mozilla Foundation https://mozilla.org/
     with Reserved Font Name Fira Sans.
@@ -25,9 +24,7 @@ included, and carry their own copyright notices and license terms:
     Licensed under the MIT license (see LICENSE-MIT.txt).
 
 * Source Code Pro (SourceCodePro-Regular.ttf.woff2,
-    SourceCodePro-Semibold.ttf.woff2, SourceCodePro-It.ttf.woff2,
-    SourceCodePro-Regular.ttf.woff, SourceCodePro-Semibold.ttf.woff,
-    SourceCodePro-It.ttf.woff):
+    SourceCodePro-Semibold.ttf.woff2, SourceCodePro-It.ttf.woff2):
 
     Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/),
     with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark
@@ -37,8 +34,7 @@ included, and carry their own copyright notices and license terms:
     See SourceCodePro-LICENSE.txt.
 
 * Source Serif 4 (SourceSerif4-Regular.ttf.woff2, SourceSerif4-Bold.ttf.woff2,
-    SourceSerif4-It.ttf.woff2, SourceSerif4-Regular.ttf.woff,
-    SourceSerif4-Bold.ttf.woff, SourceSerif4-It.ttf.woff):
+    SourceSerif4-It.ttf.woff2):
 
     Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name
     'Source'. All Rights Reserved. Source is a trademark of Adobe in the United
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index 48cb0a46ad6..81c12be8e83 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -4,8 +4,7 @@
 	font-style: normal;
 	font-weight: 400;
 	src: local('Fira Sans'),
-		url("FiraSans-Regular.woff2") format("woff2"),
-		url("FiraSans-Regular.woff") format('woff');
+		url("FiraSans-Regular.woff2") format("woff2");
 	font-display: swap;
 }
 @font-face {
@@ -13,8 +12,7 @@
 	font-style: normal;
 	font-weight: 500;
 	src: local('Fira Sans Medium'),
-		url("FiraSans-Medium.woff2") format("woff2"),
-		url("FiraSans-Medium.woff") format('woff');
+		url("FiraSans-Medium.woff2") format("woff2");
 	font-display: swap;
 }
 
@@ -24,8 +22,7 @@
 	font-style: normal;
 	font-weight: 400;
 	src: local('Source Serif 4'),
-		url("SourceSerif4-Regular.ttf.woff2") format("woff2"),
-		url("SourceSerif4-Regular.ttf.woff") format("woff");
+		url("SourceSerif4-Regular.ttf.woff2") format("woff2");
 	font-display: swap;
 }
 @font-face {
@@ -33,8 +30,7 @@
 	font-style: italic;
 	font-weight: 400;
 	src: local('Source Serif 4 Italic'),
-		url("SourceSerif4-It.ttf.woff2") format("woff2"),
-		url("SourceSerif4-It.ttf.woff") format("woff");
+		url("SourceSerif4-It.ttf.woff2") format("woff2");
 	font-display: swap;
 }
 @font-face {
@@ -42,8 +38,7 @@
 	font-style: normal;
 	font-weight: 700;
 	src: local('Source Serif 4 Bold'),
-		url("SourceSerif4-Bold.ttf.woff2") format("woff2"),
-		url("SourceSerif4-Bold.ttf.woff") format("woff");
+		url("SourceSerif4-Bold.ttf.woff2") format("woff2");
 	font-display: swap;
 }
 
@@ -54,32 +49,28 @@
 	font-weight: 400;
 	/* Avoid using locally installed font because bad versions are in circulation:
 	 * see https://github.com/rust-lang/rust/issues/24355 */
-	src: url("SourceCodePro-Regular.ttf.woff2") format("woff2"),
-		url("SourceCodePro-Regular.ttf.woff") format("woff");
+	src: url("SourceCodePro-Regular.ttf.woff2") format("woff2");
 	font-display: swap;
 }
 @font-face {
 	font-family: 'Source Code Pro';
 	font-style: italic;
 	font-weight: 400;
-	src: url("SourceCodePro-It.ttf.woff2") format("woff2"),
-		url("SourceCodePro-It.ttf.woff") format("woff");
+	src: url("SourceCodePro-It.ttf.woff2") format("woff2");
 	font-display: swap;
 }
 @font-face {
 	font-family: 'Source Code Pro';
 	font-style: normal;
 	font-weight: 600;
-	src: url("SourceCodePro-Semibold.ttf.woff2") format("woff2"),
-		url("SourceCodePro-Semibold.ttf.woff") format("woff");
+	src: url("SourceCodePro-Semibold.ttf.woff2") format("woff2");
 	font-display: swap;
 }
 
 /* Avoid using legacy CJK serif fonts in Windows like Batang. */
 @font-face {
 	font-family: 'NanumBarunGothic';
-	src: url("NanumBarunGothic.ttf.woff2") format("woff2"),
-		url("NanumBarunGothic.ttf.woff") format("woff");
+	src: url("NanumBarunGothic.ttf.woff2") format("woff2");
 	font-display: swap;
 	unicode-range: U+AC00-D7AF, U+1100-11FF, U+3130-318F, U+A960-A97F, U+D7B0-D7FF;
 }
diff --git a/src/librustdoc/html/static/fonts/FiraSans-Medium.woff b/src/librustdoc/html/static/fonts/FiraSans-Medium.woff
deleted file mode 100644
index 7d742c5fb7d..00000000000
--- a/src/librustdoc/html/static/fonts/FiraSans-Medium.woff
+++ /dev/null
Binary files differdiff --git a/src/librustdoc/html/static/fonts/FiraSans-Regular.woff b/src/librustdoc/html/static/fonts/FiraSans-Regular.woff
deleted file mode 100644
index d8e0363f4e1..00000000000
--- a/src/librustdoc/html/static/fonts/FiraSans-Regular.woff
+++ /dev/null
Binary files differdiff --git a/src/librustdoc/html/static/fonts/NanumBarunGothic.ttf.woff b/src/librustdoc/html/static/fonts/NanumBarunGothic.ttf.woff
deleted file mode 100644
index fb063e8fb7d..00000000000
--- a/src/librustdoc/html/static/fonts/NanumBarunGothic.ttf.woff
+++ /dev/null
Binary files differdiff --git a/src/librustdoc/html/static/fonts/SourceCodePro-It.ttf.woff b/src/librustdoc/html/static/fonts/SourceCodePro-It.ttf.woff
deleted file mode 100644
index 8d68f2febdd..00000000000
--- a/src/librustdoc/html/static/fonts/SourceCodePro-It.ttf.woff
+++ /dev/null
Binary files differdiff --git a/src/librustdoc/html/static/fonts/SourceCodePro-Regular.ttf.woff b/src/librustdoc/html/static/fonts/SourceCodePro-Regular.ttf.woff
deleted file mode 100644
index 7be076e1fca..00000000000
--- a/src/librustdoc/html/static/fonts/SourceCodePro-Regular.ttf.woff
+++ /dev/null
Binary files differdiff --git a/src/librustdoc/html/static/fonts/SourceCodePro-Semibold.ttf.woff b/src/librustdoc/html/static/fonts/SourceCodePro-Semibold.ttf.woff
deleted file mode 100644
index 61bc67b8025..00000000000
--- a/src/librustdoc/html/static/fonts/SourceCodePro-Semibold.ttf.woff
+++ /dev/null
Binary files differdiff --git a/src/librustdoc/html/static/fonts/SourceSerif4-Bold.ttf.woff b/src/librustdoc/html/static/fonts/SourceSerif4-Bold.ttf.woff
deleted file mode 100644
index 8ad41888e6e..00000000000
--- a/src/librustdoc/html/static/fonts/SourceSerif4-Bold.ttf.woff
+++ /dev/null
Binary files differdiff --git a/src/librustdoc/html/static/fonts/SourceSerif4-It.ttf.woff b/src/librustdoc/html/static/fonts/SourceSerif4-It.ttf.woff
deleted file mode 100644
index 2a34b5c42a8..00000000000
--- a/src/librustdoc/html/static/fonts/SourceSerif4-It.ttf.woff
+++ /dev/null
Binary files differdiff --git a/src/librustdoc/html/static/fonts/SourceSerif4-Regular.ttf.woff b/src/librustdoc/html/static/fonts/SourceSerif4-Regular.ttf.woff
deleted file mode 100644
index 45a5521ab0c..00000000000
--- a/src/librustdoc/html/static/fonts/SourceSerif4-Regular.ttf.woff
+++ /dev/null
Binary files differdiff --git a/src/librustdoc/html/static/js/externs.js b/src/librustdoc/html/static/js/externs.js
index 629f90728d2..de881dbd081 100644
--- a/src/librustdoc/html/static/js/externs.js
+++ b/src/librustdoc/html/static/js/externs.js
@@ -1,20 +1,47 @@
 // This file contains type definitions that are processed by the Closure Compiler but are
 // not put into the JavaScript we include as part of the documentation. It is used for
 // type checking. See README.md in this directory for more info.
+/* eslint-env es6 */
+/* eslint no-var: "error" */
+/* eslint prefer-const: "error" */
 
 /* eslint-disable */
-var searchState;
+let searchState;
 function initSearch(searchIndex){}
 
 /**
  * @typedef {{
- *   raw: string,
- *   query: string,
- *   type: string,
- *   id: string,
+ *     name: string,
+ *     fullPath: Array<string>,
+ *     pathWithoutLast: Array<string>,
+ *     pathLast: string,
+ *     generics: Array<QueryElement>,
  * }}
  */
-var ParsedQuery;
+let QueryElement;
+
+/**
+ * @typedef {{
+ *      pos: number,
+ *      totalElems: number,
+ *      typeFilter: (null|string),
+ *      userQuery: string,
+ * }}
+ */
+let ParserState;
+
+/**
+ * @typedef {{
+ *     original: string,
+ *     userQuery: string,
+ *     typeFilter: number,
+ *     elems: Array<QueryElement>,
+ *     args: Array<QueryElement>,
+ *     returned: Array<QueryElement>,
+ *     foundElems: number,
+ * }}
+ */
+let ParsedQuery;
 
 /**
  * @typedef {{
@@ -29,4 +56,31 @@ var ParsedQuery;
  *    type: (Array<?>|null)
  * }}
  */
-var Row;
+let Row;
+
+/**
+ * @typedef {{
+ *    in_args: Array<Object>,
+ *    returned: Array<Object>,
+ *    others: Array<Object>,
+ *    query: ParsedQuery,
+ * }}
+ */
+let ResultsTable;
+
+/**
+ * @typedef {{
+ *     desc: string,
+ *     displayPath: string,
+ *     fullPath: string,
+ *     href: string,
+ *     id: number,
+ *     lev: number,
+ *     name: string,
+ *     normalizedName: string,
+ *     parent: (Object|undefined),
+ *     path: string,
+ *     ty: number,
+ * }}
+ */
+let Results;
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index 90592335d5d..9e5de9a843a 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -1,3 +1,6 @@
+/* eslint-env es6 */
+/* eslint no-var: "error" */
+/* eslint prefer-const: "error" */
 // Local js definitions:
 /* global addClass, getSettingValue, hasClass, searchState */
 /* global onEach, onEachLazy, removeClass */
@@ -11,7 +14,7 @@ if (!String.prototype.startsWith) {
 }
 if (!String.prototype.endsWith) {
     String.prototype.endsWith = function(suffix, length) {
-        var l = length || this.length;
+        const l = length || this.length;
         return this.indexOf(suffix, l - suffix.length) !== -1;
     };
 }
@@ -40,7 +43,7 @@ if (!DOMTokenList.prototype.remove) {
 // Get a value from the rustdoc-vars div, which is used to convey data from
 // Rust to the JS. If there is no such element, return null.
 function getVar(name) {
-    var el = document.getElementById("rustdoc-vars");
+    const el = document.getElementById("rustdoc-vars");
     if (el) {
         return el.attributes["data-" + name].value;
     } else {
@@ -59,7 +62,7 @@ function resourcePath(basename, extension) {
     window.currentCrate = getVar("current-crate");
     window.searchJS =  resourcePath("search", ".js");
     window.searchIndexJS = resourcePath("search-index", ".js");
-    var sidebarVars = document.getElementById("sidebar-vars");
+    const sidebarVars = document.getElementById("sidebar-vars");
     if (sidebarVars) {
         window.sidebarCurrent = {
             name: sidebarVars.attributes["data-name"].value,
@@ -68,8 +71,8 @@ function resourcePath(basename, extension) {
         };
         // FIXME: It would be nicer to generate this text content directly in HTML,
         // but with the current code it's hard to get the right information in the right place.
-        var mobileLocationTitle = document.querySelector(".mobile-topbar h2.location");
-        var locationTitle = document.querySelector(".sidebar h2.location");
+        const mobileLocationTitle = document.querySelector(".mobile-topbar h2.location");
+        const locationTitle = document.querySelector(".sidebar h2.location");
         if (mobileLocationTitle && locationTitle) {
             mobileLocationTitle.innerHTML = locationTitle.innerHTML;
         }
@@ -91,16 +94,16 @@ function getVirtualKey(ev) {
         return ev.key;
     }
 
-    var c = ev.charCode || ev.keyCode;
+    const c = ev.charCode || ev.keyCode;
     if (c == 27) {
         return "Escape";
     }
     return String.fromCharCode(c);
 }
 
-var THEME_PICKER_ELEMENT_ID = "theme-picker";
-var THEMES_ELEMENT_ID = "theme-choices";
-var MAIN_ID = "main-content";
+const THEME_PICKER_ELEMENT_ID = "theme-picker";
+const THEMES_ELEMENT_ID = "theme-choices";
+const MAIN_ID = "main-content";
 
 function getThemesElement() {
     return document.getElementById(THEMES_ELEMENT_ID);
@@ -116,8 +119,8 @@ function getNakedUrl() {
 }
 
 function showThemeButtonState() {
-    var themePicker = getThemePickerElement();
-    var themeChoices = getThemesElement();
+    const themePicker = getThemePickerElement();
+    const themeChoices = getThemesElement();
 
     themeChoices.style.display = "block";
     themePicker.style.borderBottomRightRadius = "0";
@@ -125,8 +128,8 @@ function showThemeButtonState() {
 }
 
 function hideThemeButtonState() {
-    var themePicker = getThemePickerElement();
-    var themeChoices = getThemesElement();
+    const themePicker = getThemePickerElement();
+    const themeChoices = getThemesElement();
 
     themeChoices.style.display = "none";
     themePicker.style.borderBottomRightRadius = "3px";
@@ -138,9 +141,9 @@ function hideThemeButtonState() {
     if (!document.location.href.startsWith("file:///")) {
         return;
     }
-    var themeChoices = getThemesElement();
-    var themePicker = getThemePickerElement();
-    var availableThemes = getVar("themes").split(",");
+    const themeChoices = getThemesElement();
+    const themePicker = getThemePickerElement();
+    const availableThemes = getVar("themes").split(",");
 
     removeClass(themeChoices.parentElement, "hidden");
 
@@ -153,8 +156,8 @@ function hideThemeButtonState() {
     }
 
     function handleThemeButtonsBlur(e) {
-        var active = document.activeElement;
-        var related = e.relatedTarget;
+        const active = document.activeElement;
+        const related = e.relatedTarget;
 
         if (active.id !== THEME_PICKER_ELEMENT_ID &&
             (!active.parentNode || active.parentNode.id !== THEMES_ELEMENT_ID) &&
@@ -168,7 +171,7 @@ function hideThemeButtonState() {
     themePicker.onclick = switchThemeButtonState;
     themePicker.onblur = handleThemeButtonsBlur;
     availableThemes.forEach(function(item) {
-        var but = document.createElement("button");
+        const but = document.createElement("button");
         but.textContent = item;
         but.onclick = function() {
             switchTheme(window.currentTheme, window.mainTheme, item, true);
@@ -236,10 +239,10 @@ function hideThemeButtonState() {
             }
         },
         getQueryStringParams: function() {
-            var params = {};
+            const params = {};
             window.location.search.substring(1).split("&").
                 map(function(s) {
-                    var pair = s.split("=");
+                    const pair = s.split("=");
                     params[decodeURIComponent(pair[0])] =
                         typeof pair[1] === "undefined" ? null : decodeURIComponent(pair[1]);
                 });
@@ -249,17 +252,17 @@ function hideThemeButtonState() {
             return window.history && typeof window.history.pushState === "function";
         },
         setup: function() {
-            var search_input = searchState.input;
+            const search_input = searchState.input;
             if (!searchState.input) {
                 return;
             }
             function loadScript(url) {
-                var script = document.createElement('script');
+                const script = document.createElement('script');
                 script.src = url;
                 document.head.append(script);
             }
 
-            var searchLoaded = false;
+            let searchLoaded = false;
             function loadSearch() {
                 if (!searchLoaded) {
                     searchLoaded = true;
@@ -278,9 +281,9 @@ function hideThemeButtonState() {
                 loadSearch();
             }
 
-            var params = searchState.getQueryStringParams();
+            const params = searchState.getQueryStringParams();
             if (params.search !== undefined) {
-                var search = searchState.outputElement();
+                const search = searchState.outputElement();
                 search.innerHTML = "<h3 class=\"search-loading\">" +
                     searchState.loadingText + "</h3>";
                 searchState.showResults(search);
@@ -291,7 +294,7 @@ function hideThemeButtonState() {
 
     function getPageId() {
         if (window.location.hash) {
-            var tmp = window.location.hash.replace(/^#/, "");
+            const tmp = window.location.hash.replace(/^#/, "");
             if (tmp.length > 0) {
                 return tmp;
             }
@@ -299,18 +302,18 @@ function hideThemeButtonState() {
         return null;
     }
 
-    var toggleAllDocsId = "toggle-all-docs";
-    var main = document.getElementById(MAIN_ID);
-    var savedHash = "";
+    const toggleAllDocsId = "toggle-all-docs";
+    const main = document.getElementById(MAIN_ID);
+    let savedHash = "";
 
     function handleHashes(ev) {
-        var elem;
-        var search = searchState.outputElement();
+        let elem;
+        const search = searchState.outputElement();
         if (ev !== null && search && !hasClass(search, "hidden") && ev.newURL) {
             // This block occurs when clicking on an element in the navbar while
             // in a search.
             searchState.hideResults(search);
-            var hash = ev.newURL.slice(ev.newURL.indexOf("#") + 1);
+            const hash = ev.newURL.slice(ev.newURL.indexOf("#") + 1);
             if (searchState.browserSupportsHistoryApi()) {
                 // `window.location.search`` contains all the query parameters, not just `search`.
                 history.replaceState(null, "",
@@ -333,7 +336,7 @@ function hideThemeButtonState() {
 
     function onHashChange(ev) {
         // If we're in mobile mode, we should hide the sidebar in any case.
-        var sidebar = document.getElementsByClassName("sidebar")[0];
+        const sidebar = document.getElementsByClassName("sidebar")[0];
         removeClass(sidebar, "shown");
         handleHashes(ev);
     }
@@ -386,8 +389,8 @@ function hideThemeButtonState() {
     }
 
     function handleEscape(ev) {
-        var help = getHelpElement(false);
-        var search = searchState.outputElement();
+        const help = getHelpElement(false);
+        const search = searchState.outputElement();
         if (help && !hasClass(help, "hidden")) {
             displayHelp(false, ev, help);
         } else if (search && !hasClass(search, "hidden")) {
@@ -399,13 +402,15 @@ function hideThemeButtonState() {
         hideThemeButtonState();
     }
 
-    var disableShortcuts = getSettingValue("disable-shortcuts") === "true";
+    const disableShortcuts = getSettingValue("disable-shortcuts") === "true";
     function handleShortcut(ev) {
         // Don't interfere with browser shortcuts
         if (ev.ctrlKey || ev.altKey || ev.metaKey || disableShortcuts) {
             return;
         }
 
+        let themePicker;
+
         if (document.activeElement.tagName === "INPUT") {
             switch (getVirtualKey(ev)) {
             case "Escape":
@@ -439,7 +444,7 @@ function hideThemeButtonState() {
             case "T":
                 displayHelp(false, ev);
                 ev.preventDefault();
-                var themePicker = getThemePickerElement();
+                themePicker = getThemePickerElement();
                 themePicker.click();
                 themePicker.focus();
                 break;
@@ -453,8 +458,8 @@ function hideThemeButtonState() {
     }
 
     function handleThemeKeyDown(ev) {
-        var active = document.activeElement;
-        var themes = getThemesElement();
+        const active = document.activeElement;
+        const themes = getThemesElement();
         switch (getVirtualKey(ev)) {
         case "ArrowUp":
             ev.preventDefault();
@@ -499,36 +504,11 @@ function hideThemeButtonState() {
     document.addEventListener("keypress", handleShortcut);
     document.addEventListener("keydown", handleShortcut);
 
-    (function() {
-        var x = document.getElementsByClassName("version-selector");
-        if (x.length > 0) {
-            x[0].onchange = function() {
-                var i, match,
-                    url = document.location.href,
-                    stripped = "",
-                    len = window.rootPath.match(/\.\.\//g).length + 1;
-
-                for (i = 0; i < len; ++i) {
-                    match = url.match(/\/[^/]*$/);
-                    if (i < len - 1) {
-                        stripped = match[0] + stripped;
-                    }
-                    url = url.substring(0, url.length - match[0].length);
-                }
-
-                var selectedVersion = document.getElementsByClassName("version-selector")[0].value;
-                url += "/" + selectedVersion + stripped;
-
-                document.location.href = url;
-            };
-        }
-    }());
-
     // delayed sidebar rendering.
     window.initSidebarItems = function(items) {
-        var sidebar = document.getElementsByClassName("sidebar-elems")[0];
-        var others;
-        var current = window.sidebarCurrent;
+        const sidebar = document.getElementsByClassName("sidebar-elems")[0];
+        let others;
+        const current = window.sidebarCurrent;
 
         function addSidebarCrates(crates) {
             if (!hasClass(document.body, "crate")) {
@@ -536,23 +516,23 @@ function hideThemeButtonState() {
                 return;
             }
             // Draw a convenient sidebar of known crates if we have a listing
-            var div = document.createElement("div");
+            const div = document.createElement("div");
             div.className = "block crate";
             div.innerHTML = "<h3>Crates</h3>";
-            var ul = document.createElement("ul");
+            const ul = document.createElement("ul");
             div.appendChild(ul);
 
-            for (var i = 0; i < crates.length; ++i) {
-                var klass = "crate";
-                if (window.rootPath !== "./" && crates[i] === window.currentCrate) {
+            for (const crate of crates) {
+                let klass = "crate";
+                if (window.rootPath !== "./" && crate === window.currentCrate) {
                     klass += " current";
                 }
-                var link = document.createElement("a");
-                link.href = window.rootPath + crates[i] + "/index.html";
+                const link = document.createElement("a");
+                link.href = window.rootPath + crate + "/index.html";
                 link.className = klass;
-                link.textContent = crates[i];
+                link.textContent = crate;
 
-                var li = document.createElement("li");
+                const li = document.createElement("li");
                 li.appendChild(link);
                 ul.appendChild(li);
             }
@@ -568,39 +548,38 @@ function hideThemeButtonState() {
          *                          "Modules", or "Macros".
          */
         function block(shortty, id, longty) {
-            var filtered = items[shortty];
+            const filtered = items[shortty];
             if (!filtered) {
                 return;
             }
 
-            var div = document.createElement("div");
+            const div = document.createElement("div");
             div.className = "block " + shortty;
-            var h3 = document.createElement("h3");
+            const h3 = document.createElement("h3");
             h3.innerHTML = `<a href="index.html#${id}">${longty}</a>`;
             div.appendChild(h3);
-            var ul = document.createElement("ul");
+            const ul = document.createElement("ul");
 
-            for (var i = 0, len = filtered.length; i < len; ++i) {
-                var item = filtered[i];
-                var name = item[0];
-                var desc = item[1]; // can be null
+            for (const item of filtered) {
+                const name = item[0];
+                const desc = item[1]; // can be null
 
-                var klass = shortty;
+                let klass = shortty;
                 if (name === current.name && shortty === current.ty) {
                     klass += " current";
                 }
-                var path;
+                let path;
                 if (shortty === "mod") {
                     path = name + "/index.html";
                 } else {
                     path = shortty + "." + name + ".html";
                 }
-                var link = document.createElement("a");
+                const link = document.createElement("a");
                 link.href = current.relpath + path;
                 link.title = desc;
                 link.className = klass;
                 link.textContent = name;
-                var li = document.createElement("li");
+                const li = document.createElement("li");
                 li.appendChild(link);
                 ul.appendChild(li);
             }
@@ -613,7 +592,7 @@ function hideThemeButtonState() {
             others.className = "others";
             sidebar.appendChild(others);
 
-            var isModule = hasClass(document.body, "mod");
+            const isModule = hasClass(document.body, "mod");
             if (!isModule) {
                 block("primitive", "primitives", "Primitive Types");
                 block("mod", "modules", "Modules");
@@ -638,8 +617,9 @@ function hideThemeButtonState() {
     };
 
     window.register_implementors = function(imp) {
-        var implementors = document.getElementById("implementors-list");
-        var synthetic_implementors = document.getElementById("synthetic-implementors-list");
+        const implementors = document.getElementById("implementors-list");
+        const synthetic_implementors = document.getElementById("synthetic-implementors-list");
+        const inlined_types = new Set();
 
         if (synthetic_implementors) {
             // This `inlined_types` variable is used to avoid having the same implementation
@@ -647,9 +627,8 @@ function hideThemeButtonState() {
             //
             // By the way, this is only used by and useful for traits implemented automatically
             // (like "Send" and "Sync").
-            var inlined_types = new Set();
             onEachLazy(synthetic_implementors.getElementsByClassName("impl"), function(el) {
-                var aliases = el.getAttribute("data-aliases");
+                const aliases = el.getAttribute("data-aliases");
                 if (!aliases) {
                     return;
                 }
@@ -659,48 +638,48 @@ function hideThemeButtonState() {
             });
         }
 
-        var currentNbImpls = implementors.getElementsByClassName("impl").length;
-        var traitName = document.querySelector("h1.fqn > .in-band > .trait").textContent;
-        var baseIdName = "impl-" + traitName + "-";
-        var libs = Object.getOwnPropertyNames(imp);
-        for (var i = 0, llength = libs.length; i < llength; ++i) {
-            if (libs[i] === window.currentCrate) { continue; }
-            var structs = imp[libs[i]];
+        let currentNbImpls = implementors.getElementsByClassName("impl").length;
+        const traitName = document.querySelector("h1.fqn > .in-band > .trait").textContent;
+        const baseIdName = "impl-" + traitName + "-";
+        const libs = Object.getOwnPropertyNames(imp);
+        for (const lib of libs) {
+            if (lib === window.currentCrate) {
+                continue;
+            }
+            const structs = imp[lib];
 
             struct_loop:
-            for (var j = 0, slength = structs.length; j < slength; ++j) {
-                var struct = structs[j];
-
-                var list = struct.synthetic ? synthetic_implementors : implementors;
+            for (const struct of structs) {
+                const list = struct.synthetic ? synthetic_implementors : implementors;
 
                 if (struct.synthetic) {
-                    for (var k = 0, stlength = struct.types.length; k < stlength; k++) {
-                        if (inlined_types.has(struct.types[k])) {
+                    for (const struct_type of struct.types) {
+                        if (inlined_types.has(struct_type)) {
                             continue struct_loop;
                         }
-                        inlined_types.add(struct.types[k]);
+                        inlined_types.add(struct_type);
                     }
                 }
 
-                var code = document.createElement("h3");
+                const code = document.createElement("h3");
                 code.innerHTML = struct.text;
                 addClass(code, "code-header");
                 addClass(code, "in-band");
 
                 onEachLazy(code.getElementsByTagName("a"), function(elem) {
-                    var href = elem.getAttribute("href");
+                    const href = elem.getAttribute("href");
 
                     if (href && href.indexOf("http") !== 0) {
                         elem.setAttribute("href", window.rootPath + href);
                     }
                 });
 
-                var currentId = baseIdName + currentNbImpls;
-                var anchor = document.createElement("a");
+                const currentId = baseIdName + currentNbImpls;
+                const anchor = document.createElement("a");
                 anchor.href = "#" + currentId;
                 addClass(anchor, "anchor");
 
-                var display = document.createElement("div");
+                const display = document.createElement("div");
                 display.id = currentId;
                 addClass(display, "impl");
                 display.appendChild(anchor);
@@ -725,11 +704,11 @@ function hideThemeButtonState() {
     }
 
     function toggleAllDocs() {
-        var innerToggle = document.getElementById(toggleAllDocsId);
+        const innerToggle = document.getElementById(toggleAllDocsId);
         if (!innerToggle) {
             return;
         }
-        var sectionIsCollapsed = false;
+        let sectionIsCollapsed = false;
         if (hasClass(innerToggle, "will-expand")) {
             removeClass(innerToggle, "will-expand");
             onEachLazy(document.getElementsByClassName("rustdoc-toggle"), function(e) {
@@ -759,17 +738,17 @@ function hideThemeButtonState() {
     }
 
     (function() {
-        var toggles = document.getElementById(toggleAllDocsId);
+        const toggles = document.getElementById(toggleAllDocsId);
         if (toggles) {
             toggles.onclick = toggleAllDocs;
         }
 
-        var hideMethodDocs = getSettingValue("auto-hide-method-docs") === "true";
-        var hideImplementations = getSettingValue("auto-hide-trait-implementations") === "true";
-        var hideLargeItemContents = getSettingValue("auto-hide-large-items") !== "false";
+        const hideMethodDocs = getSettingValue("auto-hide-method-docs") === "true";
+        const hideImplementations = getSettingValue("auto-hide-trait-implementations") === "true";
+        const hideLargeItemContents = getSettingValue("auto-hide-large-items") !== "false";
 
         function setImplementorsTogglesOpen(id, open) {
-            var list = document.getElementById(id);
+            const list = document.getElementById(id);
             if (list !== null) {
                 onEachLazy(list.getElementsByClassName("implementors-toggle"), function(e) {
                     e.open = open;
@@ -792,7 +771,7 @@ function hideThemeButtonState() {
 
         });
 
-        var pageId = getPageId();
+        const pageId = getPageId();
         if (pageId !== null) {
             expandSection(pageId);
         }
@@ -800,15 +779,15 @@ function hideThemeButtonState() {
 
     (function() {
         // To avoid checking on "rustdoc-line-numbers" value on every loop...
-        var lineNumbersFunc = function() {};
+        let lineNumbersFunc = function() {};
         if (getSettingValue("line-numbers") === "true") {
             lineNumbersFunc = function(x) {
-                var count = x.textContent.split("\n").length;
-                var elems = [];
-                for (var i = 0; i < count; ++i) {
+                const count = x.textContent.split("\n").length;
+                const elems = [];
+                for (let i = 0; i < count; ++i) {
                     elems.push(i + 1);
                 }
-                var node = document.createElement("pre");
+                const node = document.createElement("pre");
                 addClass(node, "line-number");
                 node.innerHTML = elems.join("\n");
                 x.parentNode.insertBefore(node, x);
@@ -835,12 +814,12 @@ function hideThemeButtonState() {
     }());
 
     function hideSidebar() {
-        var sidebar = document.getElementsByClassName("sidebar")[0];
+        const sidebar = document.getElementsByClassName("sidebar")[0];
         removeClass(sidebar, "shown");
     }
 
     function handleClick(id, f) {
-        var elem = document.getElementById(id);
+        const elem = document.getElementById(id);
         if (elem) {
             elem.addEventListener("click", f);
         }
@@ -879,10 +858,10 @@ function hideThemeButtonState() {
         };
     });
 
-    var sidebar_menu_toggle = document.getElementsByClassName("sidebar-menu-toggle")[0];
+    const sidebar_menu_toggle = document.getElementsByClassName("sidebar-menu-toggle")[0];
     if (sidebar_menu_toggle) {
         sidebar_menu_toggle.addEventListener("click", function() {
-            var sidebar = document.getElementsByClassName("sidebar")[0];
+            const sidebar = document.getElementsByClassName("sidebar")[0];
             if (!hasClass(sidebar, "shown")) {
                 addClass(sidebar, "shown");
             } else {
@@ -891,8 +870,8 @@ function hideThemeButtonState() {
         });
     }
 
-    var buildHelperPopup = function() {
-        var popup = document.createElement("aside");
+    let buildHelperPopup = function() {
+        const popup = document.createElement("aside");
         addClass(popup, "hidden");
         popup.id = "help";
 
@@ -903,13 +882,13 @@ function hideThemeButtonState() {
             }
         });
 
-        var book_info = document.createElement("span");
+        const book_info = document.createElement("span");
         book_info.className = "top";
         book_info.innerHTML = "You can find more information in \
             <a href=\"https://doc.rust-lang.org/rustdoc/\">the rustdoc book</a>.";
 
-        var container = document.createElement("div");
-        var shortcuts = [
+        const container = document.createElement("div");
+        const shortcuts = [
             ["?", "Show this help dialog"],
             ["S", "Focus the search field"],
             ["T", "Focus the theme picker menu"],
@@ -927,11 +906,11 @@ function hideThemeButtonState() {
                     })
                     .join("") + "</dt><dd>" + x[1] + "</dd>";
         }).join("");
-        var div_shortcuts = document.createElement("div");
+        const div_shortcuts = document.createElement("div");
         addClass(div_shortcuts, "shortcuts");
         div_shortcuts.innerHTML = "<h2>Keyboard Shortcuts</h2><dl>" + shortcuts + "</dl></div>";
 
-        var infos = [
+        const infos = [
             "Prefix searches with a type followed by a colon (e.g., <code>fn:</code>) to \
              restrict the search to a given item kind.",
             "Accepted kinds are: <code>fn</code>, <code>mod</code>, <code>struct</code>, \
@@ -947,7 +926,7 @@ function hideThemeButtonState() {
         ].map(function(x) {
             return "<p>" + x + "</p>";
         }).join("");
-        var div_infos = document.createElement("div");
+        const div_infos = document.createElement("div");
         addClass(div_infos, "infos");
         div_infos.innerHTML = "<h2>Search Tricks</h2>" + infos;
 
@@ -955,9 +934,9 @@ function hideThemeButtonState() {
         container.appendChild(div_shortcuts);
         container.appendChild(div_infos);
 
-        var rustdoc_version = document.createElement("span");
+        const rustdoc_version = document.createElement("span");
         rustdoc_version.className = "bottom";
-        var rustdoc_version_code = document.createElement("code");
+        const rustdoc_version_code = document.createElement("code");
         rustdoc_version_code.innerText = "rustdoc " + getVar("rustdoc-version");
         rustdoc_version.appendChild(rustdoc_version_code);
 
@@ -975,11 +954,11 @@ function hideThemeButtonState() {
 }());
 
 (function () {
-    var reset_button_timeout = null;
+    let reset_button_timeout = null;
 
     window.copy_path = function(but) {
-        var parent = but.parentElement;
-        var path = [];
+        const parent = but.parentElement;
+        const path = [];
 
         onEach(parent.childNodes, function(child) {
             if (child.tagName === 'A') {
@@ -987,7 +966,7 @@ function hideThemeButtonState() {
             }
         });
 
-        var el = document.createElement('textarea');
+        const el = document.createElement('textarea');
         el.value = path.join('::');
         el.setAttribute('readonly', '');
         // To not make it appear on the screen.
@@ -1002,7 +981,7 @@ function hideThemeButtonState() {
         // There is always one children, but multiple childNodes.
         but.children[0].style.display = 'none';
 
-        var tmp;
+        let tmp;
         if (but.childNodes.length < 2) {
             tmp = document.createTextNode('✓');
             but.appendChild(tmp);
diff --git a/src/librustdoc/html/static/js/scrape-examples.js b/src/librustdoc/html/static/js/scrape-examples.js
index a28fb461729..865ed7190f3 100644
--- a/src/librustdoc/html/static/js/scrape-examples.js
+++ b/src/librustdoc/html/static/js/scrape-examples.js
@@ -1,4 +1,7 @@
-/* global addClass, hasClass, removeClass, onEach */
+/* eslint-env es6 */
+/* eslint no-var: "error" */
+/* eslint prefer-const: "error" */
+/* global addClass, hasClass, removeClass, onEachLazy */
 
 (function () {
     // Number of lines shown when code viewer is not expanded
@@ -6,19 +9,19 @@
 
     // Scroll code block to the given code location
     function scrollToLoc(elt, loc) {
-        var lines = elt.querySelector('.line-numbers');
-        var scrollOffset;
+        const lines = elt.querySelector('.line-numbers');
+        let scrollOffset;
 
         // If the block is greater than the size of the viewer,
         // then scroll to the top of the block. Otherwise scroll
         // to the middle of the block.
         if (loc[1] - loc[0] > MAX_LINES) {
-            var line = Math.max(0, loc[0] - 1);
+            const line = Math.max(0, loc[0] - 1);
             scrollOffset = lines.children[line].offsetTop;
         } else {
-            var wrapper = elt.querySelector(".code-wrapper");
-            var halfHeight = wrapper.offsetHeight / 2;
-            var offsetMid = (lines.children[loc[0]].offsetTop
+            const wrapper = elt.querySelector(".code-wrapper");
+            const halfHeight = wrapper.offsetHeight / 2;
+            const offsetMid = (lines.children[loc[0]].offsetTop
                              + lines.children[loc[1]].offsetTop) / 2;
             scrollOffset = offsetMid - halfHeight;
         }
@@ -28,21 +31,21 @@
     }
 
     function updateScrapedExample(example) {
-        var locs = JSON.parse(example.attributes.getNamedItem("data-locs").textContent);
-        var locIndex = 0;
-        var highlights = example.querySelectorAll('.highlight');
-        var link = example.querySelector('.scraped-example-title a');
+        const locs = JSON.parse(example.attributes.getNamedItem("data-locs").textContent);
+        let locIndex = 0;
+        const highlights = Array.prototype.slice.call(example.querySelectorAll('.highlight'));
+        const link = example.querySelector('.scraped-example-title a');
 
         if (locs.length > 1) {
             // Toggle through list of examples in a given file
-            var onChangeLoc = function(changeIndex) {
+            const onChangeLoc = function(changeIndex) {
                 removeClass(highlights[locIndex], 'focus');
                 changeIndex();
                 scrollToLoc(example, locs[locIndex][0]);
                 addClass(highlights[locIndex], 'focus');
 
-                var url = locs[locIndex][1];
-                var title = locs[locIndex][2];
+                const url = locs[locIndex][1];
+                const title = locs[locIndex][2];
 
                 link.href = url;
                 link.innerHTML = title;
@@ -63,7 +66,7 @@
                 });
         }
 
-        var expandButton = example.querySelector('.expand');
+        const expandButton = example.querySelector('.expand');
         if (expandButton) {
             expandButton.addEventListener('click', function () {
                 if (hasClass(example, "expanded")) {
@@ -79,24 +82,24 @@
         scrollToLoc(example, locs[0][0]);
     }
 
-    var firstExamples = document.querySelectorAll('.scraped-example-list > .scraped-example');
-    onEach(firstExamples, updateScrapedExample);
-    onEach(document.querySelectorAll('.more-examples-toggle'), function(toggle) {
+    const firstExamples = document.querySelectorAll('.scraped-example-list > .scraped-example');
+    onEachLazy(firstExamples, updateScrapedExample);
+    onEachLazy(document.querySelectorAll('.more-examples-toggle'), function(toggle) {
         // Allow users to click the left border of the <details> section to close it,
         // since the section can be large and finding the [+] button is annoying.
-        toggle.querySelectorAll('.toggle-line, .hide-more').forEach(button => {
+        onEachLazy(toggle.querySelectorAll('.toggle-line, .hide-more'), button => {
             button.addEventListener('click', function() {
                 toggle.open = false;
             });
         });
 
-        var moreExamples = toggle.querySelectorAll('.scraped-example');
+        const moreExamples = toggle.querySelectorAll('.scraped-example');
         toggle.querySelector('summary').addEventListener('click', function() {
             // Wrapping in setTimeout ensures the update happens after the elements are actually
             // visible. This is necessary since updateScrapedExample calls scrollToLoc which
             // depends on offsetHeight, a property that requires an element to be visible to
             // compute correctly.
-            setTimeout(function() { onEach(moreExamples, updateScrapedExample); });
+            setTimeout(function() { onEachLazy(moreExamples, updateScrapedExample); });
         }, {once: true});
     });
 })();
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index ab52304491a..a6f7dd74af6 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -1,10 +1,13 @@
+/* eslint-env es6 */
+/* eslint no-var: "error" */
+/* eslint prefer-const: "error" */
 /* global addClass, getNakedUrl, getSettingValue, hasOwnPropertyRustdoc, initSearch, onEach */
 /* global onEachLazy, removeClass, searchState, hasClass */
 
 (function() {
 // This mapping table should match the discriminants of
 // `rustdoc::formats::item_type::ItemType` type in Rust.
-var itemTypes = [
+const itemTypes = [
     "mod",
     "externcrate",
     "import",
@@ -34,15 +37,15 @@ var itemTypes = [
 ];
 
 // used for special search precedence
-var TY_PRIMITIVE = itemTypes.indexOf("primitive");
-var TY_KEYWORD = itemTypes.indexOf("keyword");
+const TY_PRIMITIVE = itemTypes.indexOf("primitive");
+const TY_KEYWORD = itemTypes.indexOf("keyword");
 
 // In the search display, allows to switch between tabs.
 function printTab(nb) {
     if (nb === 0 || nb === 1 || nb === 2) {
         searchState.currentTab = nb;
     }
-    var nb_copy = nb;
+    let nb_copy = nb;
     onEachLazy(document.getElementById("titles").childNodes, function(elem) {
         if (nb_copy === 0) {
             addClass(elem, "selected");
@@ -61,15 +64,6 @@ function printTab(nb) {
     });
 }
 
-function removeEmptyStringsFromArray(x) {
-    for (var i = 0, len = x.length; i < len; ++i) {
-        if (x[i] === "") {
-            x.splice(i, 1);
-            i -= 1;
-        }
-    }
-}
-
 /**
  * A function to compute the Levenshtein distance between two strings
  * Licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported
@@ -77,14 +71,15 @@ function removeEmptyStringsFromArray(x) {
  * This code is an unmodified version of the code written by Marco de Wit
  * and was found at https://stackoverflow.com/a/18514751/745719
  */
-var levenshtein_row2 = [];
+const levenshtein_row2 = [];
 function levenshtein(s1, s2) {
     if (s1 === s2) {
         return 0;
     }
-    var s1_len = s1.length, s2_len = s2.length;
+    const s1_len = s1.length, s2_len = s2.length;
     if (s1_len && s2_len) {
-        var i1 = 0, i2 = 0, a, b, c, c2, row = levenshtein_row2;
+        let i1 = 0, i2 = 0, a, b, c, c2;
+        const row = levenshtein_row2;
         while (i1 < s1_len) {
             row[i1] = ++i1;
         }
@@ -106,24 +101,20 @@ function levenshtein(s1, s2) {
 }
 
 window.initSearch = function(rawSearchIndex) {
-    var MAX_LEV_DISTANCE = 3;
-    var MAX_RESULTS = 200;
-    var GENERICS_DATA = 2;
-    var NAME = 0;
-    var INPUTS_DATA = 0;
-    var OUTPUT_DATA = 1;
-    var NO_TYPE_FILTER = -1;
+    const MAX_LEV_DISTANCE = 3;
+    const MAX_RESULTS = 200;
+    const GENERICS_DATA = 2;
+    const NAME = 0;
+    const INPUTS_DATA = 0;
+    const OUTPUT_DATA = 1;
+    const NO_TYPE_FILTER = -1;
     /**
      *  @type {Array<Row>}
      */
-    var searchIndex;
-    /**
-     *  @type {Array<string>}
-     */
-    var searchWords;
-    var currentResults;
-    var ALIASES = {};
-    var params = searchState.getQueryStringParams();
+    let searchIndex;
+    let currentResults;
+    const ALIASES = {};
+    const params = searchState.getQueryStringParams();
 
     // Populate search bar with query string search term when provided,
     // but only if the input bar is empty. This avoid the obnoxious issue
@@ -133,15 +124,451 @@ window.initSearch = function(rawSearchIndex) {
         searchState.input.value = params.search || "";
     }
 
+    function isWhitespace(c) {
+        return " \t\n\r".indexOf(c) !== -1;
+    }
+
+    function isSpecialStartCharacter(c) {
+        return "<\"".indexOf(c) !== -1;
+    }
+
+    function isEndCharacter(c) {
+        return ",>-".indexOf(c) !== -1;
+    }
+
+    function isStopCharacter(c) {
+        return isWhitespace(c) || isEndCharacter(c);
+    }
+
+    function isErrorCharacter(c) {
+        return "()".indexOf(c) !== -1;
+    }
+
+    function itemTypeFromName(typename) {
+        for (let i = 0, len = itemTypes.length; i < len; ++i) {
+            if (itemTypes[i] === typename) {
+                return i;
+            }
+        }
+
+        throw new Error("Unknown type filter `" + typename + "`");
+    }
+
+    /**
+     * If we encounter a `"`, then we try to extract the string from it until we find another `"`.
+     *
+     * This function will throw an error in the following cases:
+     * * There is already another string element.
+     * * We are parsing a generic argument.
+     * * There is more than one element.
+     * * There is no closing `"`.
+     *
+     * @param {ParsedQuery} query
+     * @param {ParserState} parserState
+     * @param {boolean} isInGenerics
+     */
+    function getStringElem(query, parserState, isInGenerics) {
+        if (isInGenerics) {
+            throw new Error("`\"` cannot be used in generics");
+        } else if (query.literalSearch) {
+            throw new Error("Cannot have more than one literal search element");
+        } else if (parserState.totalElems - parserState.genericsElems > 0) {
+            throw new Error("Cannot use literal search when there is more than one element");
+        }
+        parserState.pos += 1;
+        const start = parserState.pos;
+        const end = getIdentEndPosition(parserState);
+        if (parserState.pos >= parserState.length) {
+            throw new Error("Unclosed `\"`");
+        } else if (parserState.userQuery[end] !== "\"") {
+            throw new Error(`Unexpected \`${parserState.userQuery[end]}\` in a string element`);
+        } else if (start === end) {
+            throw new Error("Cannot have empty string element");
+        }
+        // To skip the quote at the end.
+        parserState.pos += 1;
+        query.literalSearch = true;
+    }
+
+    /**
+     * Returns `true` if the current parser position is starting with "::".
+     *
+     * @param {ParserState} parserState
+     *
+     * @return {boolean}
+     */
+    function isPathStart(parserState) {
+        return parserState.userQuery.slice(parserState.pos, parserState.pos + 2) == '::';
+    }
+
+    /**
+     * Returns `true` if the current parser position is starting with "->".
+     *
+     * @param {ParserState} parserState
+     *
+     * @return {boolean}
+     */
+    function isReturnArrow(parserState) {
+        return parserState.userQuery.slice(parserState.pos, parserState.pos + 2) == '->';
+    }
+
+    /**
+     * Returns `true` if the given `c` character is valid for an ident.
+     *
+     * @param {string} c
+     *
+     * @return {boolean}
+     */
+    function isIdentCharacter(c) {
+        return (
+            c === '_' ||
+            (c >= '0' && c <= '9') ||
+            (c >= 'a' && c <= 'z') ||
+            (c >= 'A' && c <= 'Z'));
+    }
+
+    /**
+     * Returns `true` if the given `c` character is a separator.
+     *
+     * @param {string} c
+     *
+     * @return {boolean}
+     */
+    function isSeparatorCharacter(c) {
+        return c === "," || isWhitespaceCharacter(c);
+    }
+
+    /**
+     * Returns `true` if the given `c` character is a whitespace.
+     *
+     * @param {string} c
+     *
+     * @return {boolean}
+     */
+    function isWhitespaceCharacter(c) {
+        return c === " " || c === "\t";
+    }
+
+    /**
+     * @param {ParsedQuery} query
+     * @param {ParserState} parserState
+     * @param {string} name                  - Name of the query element.
+     * @param {Array<QueryElement>} generics - List of generics of this query element.
+     *
+     * @return {QueryElement}                - The newly created `QueryElement`.
+     */
+    function createQueryElement(query, parserState, name, generics, isInGenerics) {
+        if (name === '*' || (name.length === 0 && generics.length === 0)) {
+            return;
+        }
+        if (query.literalSearch && parserState.totalElems - parserState.genericsElems > 0) {
+            throw new Error("You cannot have more than one element if you use quotes");
+        }
+        const pathSegments = name.split("::");
+        if (pathSegments.length > 1) {
+            for (let i = 0, len = pathSegments.length; i < len; ++i) {
+                const pathSegment = pathSegments[i];
+
+                if (pathSegment.length === 0) {
+                    if (i === 0) {
+                        throw new Error("Paths cannot start with `::`");
+                    } else if (i + 1 === len) {
+                        throw new Error("Paths cannot end with `::`");
+                    }
+                    throw new Error("Unexpected `::::`");
+                }
+            }
+        }
+        // In case we only have something like `<p>`, there is no name.
+        if (pathSegments.length === 0 || (pathSegments.length === 1 && pathSegments[0] === "")) {
+            throw new Error("Found generics without a path");
+        }
+        parserState.totalElems += 1;
+        if (isInGenerics) {
+            parserState.genericsElems += 1;
+        }
+        return {
+            name: name,
+            fullPath: pathSegments,
+            pathWithoutLast: pathSegments.slice(0, pathSegments.length - 1),
+            pathLast: pathSegments[pathSegments.length - 1],
+            generics: generics,
+        };
+    }
+
+    /**
+     * This function goes through all characters until it reaches an invalid ident character or the
+     * end of the query. It returns the position of the last character of the ident.
+     *
+     * @param {ParserState} parserState
+     *
+     * @return {integer}
+     */
+    function getIdentEndPosition(parserState) {
+        let end = parserState.pos;
+        let foundExclamation = false;
+        while (parserState.pos < parserState.length) {
+            const c = parserState.userQuery[parserState.pos];
+            if (!isIdentCharacter(c)) {
+                if (c === "!") {
+                    if (foundExclamation) {
+                        throw new Error("Cannot have more than one `!` in an ident");
+                    } else if (parserState.pos + 1 < parserState.length &&
+                        isIdentCharacter(parserState.userQuery[parserState.pos + 1]))
+                    {
+                        throw new Error("`!` can only be at the end of an ident");
+                    }
+                    foundExclamation = true;
+                } else if (isErrorCharacter(c)) {
+                    throw new Error(`Unexpected \`${c}\``);
+                } else if (
+                    isStopCharacter(c) ||
+                    isSpecialStartCharacter(c) ||
+                    isSeparatorCharacter(c))
+                {
+                    break;
+                }
+                // If we allow paths ("str::string" for example).
+                else if (c === ":") {
+                    if (!isPathStart(parserState)) {
+                        break;
+                    }
+                    // Skip current ":".
+                    parserState.pos += 1;
+                    foundExclamation = false;
+                } else {
+                    throw new Error(`Unexpected \`${c}\``);
+                }
+            }
+            parserState.pos += 1;
+            end = parserState.pos;
+        }
+        return end;
+    }
+
+    /**
+     * @param {ParsedQuery} query
+     * @param {ParserState} parserState
+     * @param {Array<QueryElement>} elems - This is where the new {QueryElement} will be added.
+     * @param {boolean} isInGenerics
+     */
+    function getNextElem(query, parserState, elems, isInGenerics) {
+        const generics = [];
+
+        let start = parserState.pos;
+        let end;
+        // We handle the strings on their own mostly to make code easier to follow.
+        if (parserState.userQuery[parserState.pos] === "\"") {
+            start += 1;
+            getStringElem(query, parserState, isInGenerics);
+            end = parserState.pos - 1;
+        } else {
+            end = getIdentEndPosition(parserState);
+        }
+        if (parserState.pos < parserState.length &&
+            parserState.userQuery[parserState.pos] === "<")
+        {
+            if (isInGenerics) {
+                throw new Error("Unexpected `<` after `<`");
+            } else if (start >= end) {
+                throw new Error("Found generics without a path");
+            }
+            parserState.pos += 1;
+            getItemsBefore(query, parserState, generics, ">");
+        }
+        if (start >= end && generics.length === 0) {
+            return;
+        }
+        elems.push(
+            createQueryElement(
+                query,
+                parserState,
+                parserState.userQuery.slice(start, end),
+                generics,
+                isInGenerics
+            )
+        );
+    }
+
+    /**
+     * This function parses the next query element until it finds `endChar`, calling `getNextElem`
+     * to collect each element.
+     *
+     * If there is no `endChar`, this function will implicitly stop at the end without raising an
+     * error.
+     *
+     * @param {ParsedQuery} query
+     * @param {ParserState} parserState
+     * @param {Array<QueryElement>} elems - This is where the new {QueryElement} will be added.
+     * @param {string} endChar            - This function will stop when it'll encounter this
+     *                                      character.
+     */
+    function getItemsBefore(query, parserState, elems, endChar) {
+        let foundStopChar = true;
+
+        while (parserState.pos < parserState.length) {
+            const c = parserState.userQuery[parserState.pos];
+            if (c === endChar) {
+                break;
+            } else if (isSeparatorCharacter(c)) {
+                parserState.pos += 1;
+                foundStopChar = true;
+                continue;
+            } else if (c === ":" && isPathStart(parserState)) {
+                throw new Error("Unexpected `::`: paths cannot start with `::`");
+            } else if (c === ":" || isEndCharacter(c)) {
+                let extra = "";
+                if (endChar === ">") {
+                    extra = "`<`";
+                } else if (endChar === "") {
+                    extra = "`->`";
+                }
+                throw new Error("Unexpected `" + c + "` after " + extra);
+            }
+            if (!foundStopChar) {
+                if (endChar !== "") {
+                    throw new Error(`Expected \`,\`, \` \` or \`${endChar}\`, found \`${c}\``);
+                }
+                throw new Error(`Expected \`,\` or \` \`, found \`${c}\``);
+            }
+            const posBefore = parserState.pos;
+            getNextElem(query, parserState, elems, endChar === ">");
+            // This case can be encountered if `getNextElem` encounted a "stop character" right from
+            // the start. For example if you have `,,` or `<>`. In this case, we simply move up the
+            // current position to continue the parsing.
+            if (posBefore === parserState.pos) {
+                parserState.pos += 1;
+            }
+            foundStopChar = false;
+        }
+        // We are either at the end of the string or on the `endChar`` character, let's move forward
+        // in any case.
+        parserState.pos += 1;
+    }
+
+    /**
+     * Checks that the type filter doesn't have unwanted characters like `<>` (which are ignored
+     * if empty).
+     *
+     * @param {ParserState} parserState
+     */
+    function checkExtraTypeFilterCharacters(parserState) {
+        const query = parserState.userQuery;
+
+        for (let pos = 0; pos < parserState.pos; ++pos) {
+            if (!isIdentCharacter(query[pos]) && !isWhitespaceCharacter(query[pos])) {
+                throw new Error(`Unexpected \`${query[pos]}\` in type filter`);
+            }
+        }
+    }
+
+    /**
+     * Parses the provided `query` input to fill `parserState`. If it encounters an error while
+     * parsing `query`, it'll throw an error.
+     *
+     * @param {ParsedQuery} query
+     * @param {ParserState} parserState
+     */
+    function parseInput(query, parserState) {
+        let c, before;
+        let foundStopChar = true;
+
+        while (parserState.pos < parserState.length) {
+            c = parserState.userQuery[parserState.pos];
+            if (isStopCharacter(c)) {
+                foundStopChar = true;
+                if (isSeparatorCharacter(c)) {
+                    parserState.pos += 1;
+                    continue;
+                } else if (c === "-" || c === ">") {
+                    if (isReturnArrow(parserState)) {
+                        break;
+                    }
+                    throw new Error(`Unexpected \`${c}\` (did you mean \`->\`?)`);
+                }
+                throw new Error(`Unexpected \`${c}\``);
+            } else if (c === ":" && !isPathStart(parserState)) {
+                if (parserState.typeFilter !== null) {
+                    throw new Error("Unexpected `:`");
+                }
+                if (query.elems.length === 0) {
+                    throw new Error("Expected type filter before `:`");
+                } else if (query.elems.length !== 1 || parserState.totalElems !== 1) {
+                    throw new Error("Unexpected `:`");
+                } else if (query.literalSearch) {
+                    throw new Error("You cannot use quotes on type filter");
+                }
+                checkExtraTypeFilterCharacters(parserState);
+                // The type filter doesn't count as an element since it's a modifier.
+                parserState.typeFilter = query.elems.pop().name;
+                parserState.pos += 1;
+                parserState.totalElems = 0;
+                query.literalSearch = false;
+                foundStopChar = true;
+                continue;
+            }
+            if (!foundStopChar) {
+                if (parserState.typeFilter !== null) {
+                    throw new Error(`Expected \`,\`, \` \` or \`->\`, found \`${c}\``);
+                }
+                throw new Error(`Expected \`,\`, \` \`, \`:\` or \`->\`, found \`${c}\``);
+            }
+            before = query.elems.length;
+            getNextElem(query, parserState, query.elems, false);
+            if (query.elems.length === before) {
+                // Nothing was added, weird... Let's increase the position to not remain stuck.
+                parserState.pos += 1;
+            }
+            foundStopChar = false;
+        }
+        while (parserState.pos < parserState.length) {
+            c = parserState.userQuery[parserState.pos];
+            if (isReturnArrow(parserState)) {
+                parserState.pos += 2;
+                // Get returned elements.
+                getItemsBefore(query, parserState, query.returned, "");
+                // Nothing can come afterward!
+                if (query.returned.length === 0) {
+                    throw new Error("Expected at least one item after `->`");
+                }
+                break;
+            } else {
+                parserState.pos += 1;
+            }
+        }
+    }
+
+    /**
+     * Takes the user search input and returns an empty `ParsedQuery`.
+     *
+     * @param {string} userQuery
+     *
+     * @return {ParsedQuery}
+     */
+    function newParsedQuery(userQuery) {
+        return {
+            original: userQuery,
+            userQuery: userQuery.toLowerCase(),
+            typeFilter: NO_TYPE_FILTER,
+            elems: [],
+            returned: [],
+            // Total number of "top" elements (does not include generics).
+            foundElems: 0,
+            literalSearch: false,
+            error: null,
+        };
+    }
+
     /**
      * Build an URL with search parameters.
      *
      * @param {string} search            - The current search being performed.
      * @param {string|null} filterCrates - The current filtering crate (if any).
+     *
      * @return {string}
      */
     function buildUrl(search, filterCrates) {
-        var extra = "?search=" + encodeURIComponent(search);
+        let extra = "?search=" + encodeURIComponent(search);
 
         if (filterCrates !== null) {
             extra += "&filter-crate=" + encodeURIComponent(filterCrates);
@@ -155,7 +582,7 @@ window.initSearch = function(rawSearchIndex) {
      * @return {string|null}
      */
     function getFilterCrates() {
-        var elem = document.getElementById("crate-search");
+        const elem = document.getElementById("crate-search");
 
         if (elem &&
             elem.value !== "All crates" &&
@@ -167,45 +594,149 @@ window.initSearch = function(rawSearchIndex) {
     }
 
     /**
-     * Executes the query and returns a list of results for each results tab.
-     * @param  {Object}        query          - The user query
-     * @param  {Array<string>} searchWords    - The list of search words to query against
-     * @param  {string}        [filterCrates] - Crate to search in
-     * @return {{
-     *   in_args: Array<?>,
-     *   returned: Array<?>,
-     *   others: Array<?>
-     * }}
+     * Parses the query.
+     *
+     * The supported syntax by this parser is as follow:
+     *
+     * ident = *(ALPHA / DIGIT / "_") [!]
+     * path = ident *(DOUBLE-COLON ident)
+     * arg = path [generics]
+     * arg-without-generic = path
+     * type-sep = COMMA/WS *(COMMA/WS)
+     * nonempty-arg-list = *(type-sep) arg *(type-sep arg) *(type-sep)
+     * nonempty-arg-list-without-generics = *(type-sep) arg-without-generic
+     *                                      *(type-sep arg-without-generic) *(type-sep)
+     * generics = OPEN-ANGLE-BRACKET [ nonempty-arg-list-without-generics ] *(type-sep)
+     *            CLOSE-ANGLE-BRACKET/EOF
+     * return-args = RETURN-ARROW *(type-sep) nonempty-arg-list
+     *
+     * exact-search = [type-filter *WS COLON] [ RETURN-ARROW ] *WS QUOTE ident QUOTE [ generics ]
+     * type-search = [type-filter *WS COLON] [ nonempty-arg-list ] [ return-args ]
+     *
+     * query = *WS (exact-search / type-search) *WS
+     *
+     * type-filter = (
+     *     "mod" /
+     *     "externcrate" /
+     *     "import" /
+     *     "struct" /
+     *     "enum" /
+     *     "fn" /
+     *     "type" /
+     *     "static" /
+     *     "trait" /
+     *     "impl" /
+     *     "tymethod" /
+     *     "method" /
+     *     "structfield" /
+     *     "variant" /
+     *     "macro" /
+     *     "primitive" /
+     *     "associatedtype" /
+     *     "constant" /
+     *     "associatedconstant" /
+     *     "union" /
+     *     "foreigntype" /
+     *     "keyword" /
+     *     "existential" /
+     *     "attr" /
+     *     "derive" /
+     *     "traitalias")
+     *
+     * OPEN-ANGLE-BRACKET = "<"
+     * CLOSE-ANGLE-BRACKET = ">"
+     * COLON = ":"
+     * DOUBLE-COLON = "::"
+     * QUOTE = %x22
+     * COMMA = ","
+     * RETURN-ARROW = "->"
+     *
+     * ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
+     * DIGIT = %x30-39
+     * WS = %x09 / " "
+     *
+     * @param  {string} val     - The user query
+     *
+     * @return {ParsedQuery}    - The parsed query
      */
-    function execQuery(query, searchWords, filterCrates) {
-        function itemTypeFromName(typename) {
-            for (var i = 0, len = itemTypes.length; i < len; ++i) {
-                if (itemTypes[i] === typename) {
-                    return i;
+    function parseQuery(userQuery) {
+        userQuery = userQuery.trim();
+        const parserState = {
+            length: userQuery.length,
+            pos: 0,
+            // Total number of elements (includes generics).
+            totalElems: 0,
+            genericsElems: 0,
+            typeFilter: null,
+            userQuery: userQuery.toLowerCase(),
+        };
+        let query = newParsedQuery(userQuery);
+
+        try {
+            parseInput(query, parserState);
+            if (parserState.typeFilter !== null) {
+                let typeFilter = parserState.typeFilter;
+                if (typeFilter === "const") {
+                    typeFilter = "constant";
                 }
+                query.typeFilter = itemTypeFromName(typeFilter);
             }
-            return NO_TYPE_FILTER;
+        } catch (err) {
+            query = newParsedQuery(userQuery);
+            query.error = err.message;
+            query.typeFilter = -1;
+            return query;
         }
 
-        var valLower = query.query.toLowerCase(),
-            val = valLower,
-            typeFilter = itemTypeFromName(query.type),
-            results = {}, results_in_args = {}, results_returned = {},
-            split = valLower.split("::");
+        if (!query.literalSearch) {
+            // If there is more than one element in the query, we switch to literalSearch in any
+            // case.
+            query.literalSearch = parserState.totalElems > 1;
+        }
+        query.foundElems = query.elems.length + query.returned.length;
+        return query;
+    }
+
+    /**
+     * Creates the query results.
+     *
+     * @param {Array<Result>} results_in_args
+     * @param {Array<Result>} results_returned
+     * @param {Array<Result>} results_in_args
+     * @param {ParsedQuery} parsedQuery
+     *
+     * @return {ResultsTable}
+     */
+    function createQueryResults(results_in_args, results_returned, results_others, parsedQuery) {
+        return {
+            "in_args": results_in_args,
+            "returned": results_returned,
+            "others": results_others,
+            "query": parsedQuery,
+        };
+    }
 
-        removeEmptyStringsFromArray(split);
+    /**
+     * Executes the parsed query and builds a {ResultsTable}.
+     *
+     * @param  {ParsedQuery} parsedQuery - The parsed user query
+     * @param  {Object} searchWords      - The list of search words to query against
+     * @param  {Object} [filterCrates]   - Crate to search in if defined
+     *
+     * @return {ResultsTable}
+     */
+    function execQuery(parsedQuery, searchWords, filterCrates) {
+        const results_others = {}, results_in_args = {}, results_returned = {};
 
         function transformResults(results) {
-            var duplicates = {};
-            var out = [];
-
-            for (var i = 0, len = results.length; i < len; ++i) {
-                var result = results[i];
+            const duplicates = {};
+            const out = [];
 
+            for (const result of results) {
                 if (result.id > -1) {
-                    var obj = searchIndex[result.id];
+                    const obj = searchIndex[result.id];
                     obj.lev = result.lev;
-                    var res = buildHrefAndPath(obj);
+                    const res = buildHrefAndPath(obj);
                     obj.displayPath = pathSplitter(res[0]);
                     obj.fullPath = obj.displayPath + obj.name;
                     // To be sure than it some items aren't considered as duplicate.
@@ -227,10 +758,11 @@ window.initSearch = function(rawSearchIndex) {
         }
 
         function sortResults(results, isType) {
-            var ar = [];
-            for (var entry in results) {
+            const userQuery = parsedQuery.userQuery;
+            const ar = [];
+            for (const entry in results) {
                 if (hasOwnPropertyRustdoc(results, entry)) {
-                    var result = results[entry];
+                    const result = results[entry];
                     result.word = searchWords[result.id];
                     result.item = searchIndex[result.id] || {};
                     ar.push(result);
@@ -243,11 +775,11 @@ window.initSearch = function(rawSearchIndex) {
             }
 
             results.sort(function(aaa, bbb) {
-                var a, b;
+                let a, b;
 
                 // sort by exact match with regard to the last word (mismatch goes later)
-                a = (aaa.word !== val);
-                b = (bbb.word !== val);
+                a = (aaa.word !== userQuery);
+                b = (bbb.word !== userQuery);
                 if (a !== b) { return a - b; }
 
                 // Sort by non levenshtein results and then levenshtein results by the distance
@@ -309,270 +841,273 @@ window.initSearch = function(rawSearchIndex) {
                 return 0;
             });
 
-            for (var i = 0, len = results.length; i < len; ++i) {
-                result = results[i];
+            let nameSplit = null;
+            if (parsedQuery.elems.length === 1) {
+                const hasPath = typeof parsedQuery.elems[0].path === "undefined";
+                nameSplit = hasPath ? null : parsedQuery.elems[0].path;
+            }
 
+            for (const result of results) {
                 // this validation does not make sense when searching by types
                 if (result.dontValidate) {
                     continue;
                 }
-                var name = result.item.name.toLowerCase(),
+                const name = result.item.name.toLowerCase(),
                     path = result.item.path.toLowerCase(),
                     parent = result.item.parent;
 
-                if (!isType && !validateResult(name, path, split, parent)) {
+                if (!isType && !validateResult(name, path, nameSplit, parent)) {
                     result.id = -1;
                 }
             }
             return transformResults(results);
         }
 
-        function extractGenerics(val) {
-            val = val.toLowerCase();
-            if (val.indexOf("<") !== -1) {
-                var values = val.substring(val.indexOf("<") + 1, val.lastIndexOf(">"));
-                return {
-                    name: val.substring(0, val.indexOf("<")),
-                    generics: values.split(/\s*,\s*/),
-                };
+        /**
+         * This function checks if the object (`row`) generics match the given type (`elem`)
+         * generics. If there are no generics on `row`, `defaultLev` is returned.
+         *
+         * @param {Row} row            - The object to check.
+         * @param {QueryElement} elem  - The element from the parsed query.
+         * @param {integer} defaultLev - This is the value to return in case there are no generics.
+         *
+         * @return {integer}           - Returns the best match (if any) or `MAX_LEV_DISTANCE + 1`.
+         */
+        function checkGenerics(row, elem, defaultLev) {
+            if (row.length <= GENERICS_DATA || row[GENERICS_DATA].length === 0) {
+                return elem.generics.length === 0 ? defaultLev : MAX_LEV_DISTANCE + 1;
+            } else if (row[GENERICS_DATA].length > 0 && row[GENERICS_DATA][0][NAME] === "") {
+                if (row.length > GENERICS_DATA) {
+                    return checkGenerics(row[GENERICS_DATA][0], elem, defaultLev);
+                }
+                return elem.generics.length === 0 ? defaultLev : MAX_LEV_DISTANCE + 1;
             }
-            return {
-                name: val,
-                generics: [],
-            };
-        }
-
-        function checkGenerics(obj, val) {
             // The names match, but we need to be sure that all generics kinda
             // match as well.
-            var tmp_lev, elem_name;
-            if (val.generics.length > 0) {
-                if (obj.length > GENERICS_DATA &&
-                      obj[GENERICS_DATA].length >= val.generics.length) {
-                    var elems = Object.create(null);
-                    var elength = obj[GENERICS_DATA].length;
-                    for (var x = 0; x < elength; ++x) {
-                        if (!elems[obj[GENERICS_DATA][x][NAME]]) {
-                            elems[obj[GENERICS_DATA][x][NAME]] = 0;
+            let elem_name;
+            if (elem.generics.length > 0 && row[GENERICS_DATA].length >= elem.generics.length) {
+                const elems = Object.create(null);
+                for (const entry of row[GENERICS_DATA]) {
+                    elem_name = entry[NAME];
+                    if (elem_name === "") {
+                        // Pure generic, needs to check into it.
+                        if (checkGenerics(entry, elem, MAX_LEV_DISTANCE + 1) !== 0) {
+                            return MAX_LEV_DISTANCE + 1;
                         }
-                        elems[obj[GENERICS_DATA][x][NAME]] += 1;
+                        continue;
+                    }
+                    if (elems[elem_name] === undefined) {
+                        elems[elem_name] = 0;
                     }
-                    var total = 0;
-                    var done = 0;
-                    // We need to find the type that matches the most to remove it in order
-                    // to move forward.
-                    var vlength = val.generics.length;
-                    for (x = 0; x < vlength; ++x) {
-                        var lev = MAX_LEV_DISTANCE + 1;
-                        var firstGeneric = val.generics[x];
-                        var match = null;
-                        if (elems[firstGeneric]) {
-                            match = firstGeneric;
-                            lev = 0;
-                        } else {
-                            for (elem_name in elems) {
-                                tmp_lev = levenshtein(elem_name, firstGeneric);
-                                if (tmp_lev < lev) {
-                                    lev = tmp_lev;
-                                    match = elem_name;
-                                }
+                    elems[elem_name] += 1;
+                }
+                // We need to find the type that matches the most to remove it in order
+                // to move forward.
+                for (const generic of elem.generics) {
+                    let match = null;
+                    if (elems[generic.name]) {
+                        match = generic.name;
+                    } else {
+                        for (elem_name in elems) {
+                            if (!hasOwnPropertyRustdoc(elems, elem_name)) {
+                                continue;
                             }
-                        }
-                        if (match !== null) {
-                            elems[match] -= 1;
-                            if (elems[match] == 0) {
-                                delete elems[match];
+                            if (elem_name === generic) {
+                                match = elem_name;
+                                break;
                             }
-                            total += lev;
-                            done += 1;
-                        } else {
-                            return MAX_LEV_DISTANCE + 1;
                         }
                     }
-                    return Math.ceil(total / done);
+                    if (match === null) {
+                        return MAX_LEV_DISTANCE + 1;
+                    }
+                    elems[match] -= 1;
+                    if (elems[match] === 0) {
+                        delete elems[match];
+                    }
                 }
+                return 0;
             }
             return MAX_LEV_DISTANCE + 1;
         }
 
         /**
-          * This function checks if the object (`obj`) matches the given type (`val`) and its
+          * This function checks if the object (`row`) matches the given type (`elem`) and its
           * generics (if any).
           *
-          * @param {Object} obj
-          * @param {string} val
+          * @param {Row} row
+          * @param {QueryElement} elem    - The element from the parsed query.
+          *
+          * @return {integer} - Returns a Levenshtein distance to the best match.
+          */
+        function checkIfInGenerics(row, elem) {
+            let lev = MAX_LEV_DISTANCE + 1;
+            for (const entry of row[GENERICS_DATA]) {
+                lev = Math.min(checkType(entry, elem, true), lev);
+                if (lev === 0) {
+                    break;
+                }
+            }
+            return lev;
+        }
+
+        /**
+          * This function checks if the object (`row`) matches the given type (`elem`) and its
+          * generics (if any).
+          *
+          * @param {Row} row
+          * @param {QueryElement} elem      - The element from the parsed query.
           * @param {boolean} literalSearch
           *
           * @return {integer} - Returns a Levenshtein distance to the best match. If there is
           *                     no match, returns `MAX_LEV_DISTANCE + 1`.
           */
-        function checkType(obj, val, literalSearch) {
-            var lev_distance = MAX_LEV_DISTANCE + 1;
-            var tmp_lev = MAX_LEV_DISTANCE + 1;
-            var len, x, firstGeneric;
-            if (obj[NAME] === val.name) {
-                if (literalSearch) {
-                    if (val.generics && val.generics.length !== 0) {
-                        if (obj.length > GENERICS_DATA &&
-                             obj[GENERICS_DATA].length > 0) {
-                            var elems = Object.create(null);
-                            len = obj[GENERICS_DATA].length;
-                            for (x = 0; x < len; ++x) {
-                                if (!elems[obj[GENERICS_DATA][x][NAME]]) {
-                                    elems[obj[GENERICS_DATA][x][NAME]] = 0;
-                                }
-                                elems[obj[GENERICS_DATA][x][NAME]] += 1;
-                            }
+        function checkType(row, elem, literalSearch) {
+            if (row[NAME].length === 0) {
+                // This is a pure "generic" search, no need to run other checks.
+                if (row.length > GENERICS_DATA) {
+                    return checkIfInGenerics(row, elem);
+                }
+                return MAX_LEV_DISTANCE + 1;
+            }
 
-                            len = val.generics.length;
-                            for (x = 0; x < len; ++x) {
-                                firstGeneric = val.generics[x];
-                                if (elems[firstGeneric]) {
-                                    elems[firstGeneric] -= 1;
-                                } else {
-                                    // Something wasn't found and this is a literal search so
-                                    // abort and return a "failing" distance.
-                                    return MAX_LEV_DISTANCE + 1;
-                                }
-                            }
-                            // Everything was found, success!
+            let lev = levenshtein(row[NAME], elem.name);
+            if (literalSearch) {
+                if (lev !== 0) {
+                    // The name didn't match, let's try to check if the generics do.
+                    if (elem.generics.length === 0) {
+                        const checkGeneric = (row.length > GENERICS_DATA &&
+                            row[GENERICS_DATA].length > 0);
+                        if (checkGeneric && row[GENERICS_DATA].findIndex(function(tmp_elem) {
+                            return tmp_elem[NAME] === elem.name;
+                        }) !== -1) {
                             return 0;
                         }
-                        return MAX_LEV_DISTANCE + 1;
                     }
-                    return 0;
-                } else {
-                    // If the type has generics but don't match, then it won't return at this point.
-                    // Otherwise, `checkGenerics` will return 0 and it'll return.
-                    if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length !== 0) {
-                        tmp_lev = checkGenerics(obj, val);
-                        if (tmp_lev <= MAX_LEV_DISTANCE) {
-                            return tmp_lev;
-                        }
-                    }
-                }
-            } else if (literalSearch) {
-                var found = false;
-                if ((!val.generics || val.generics.length === 0) &&
-                      obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) {
-                    found = obj[GENERICS_DATA].some(
-                        function(gen) {
-                            return gen[NAME] === val.name;
-                        });
-                }
-                return found ? 0 : MAX_LEV_DISTANCE + 1;
-            }
-            lev_distance = Math.min(levenshtein(obj[NAME], val.name), lev_distance);
-            if (lev_distance <= MAX_LEV_DISTANCE) {
-                // The generics didn't match but the name kinda did so we give it
-                // a levenshtein distance value that isn't *this* good so it goes
-                // into the search results but not too high.
-                lev_distance = Math.ceil((checkGenerics(obj, val) + lev_distance) / 2);
-            }
-            if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) {
-                // We can check if the type we're looking for is inside the generics!
-                var olength = obj[GENERICS_DATA].length;
-                for (x = 0; x < olength; ++x) {
-                    tmp_lev = Math.min(levenshtein(obj[GENERICS_DATA][x][NAME], val.name), tmp_lev);
+                    return MAX_LEV_DISTANCE + 1;
+                } else if (elem.generics.length > 0) {
+                    return checkGenerics(row, elem, MAX_LEV_DISTANCE + 1);
                 }
-                if (tmp_lev !== 0) {
-                    // If we didn't find a good enough result, we go check inside the generics of
-                    // the generics.
-                    for (x = 0; x < olength && tmp_lev !== 0; ++x) {
-                        tmp_lev = Math.min(
-                            checkType(obj[GENERICS_DATA][x], val, literalSearch),
-                            tmp_lev
-                        );
+                return 0;
+            } else if (row.length > GENERICS_DATA) {
+                if (elem.generics.length === 0) {
+                    if (lev === 0) {
+                        return 0;
+                    }
+                    // The name didn't match so we now check if the type we're looking for is inside
+                    // the generics!
+                    lev = checkIfInGenerics(row, elem);
+                    // Now whatever happens, the returned distance is "less good" so we should mark
+                    // it as such, and so we add 0.5 to the distance to make it "less good".
+                    return lev + 0.5;
+                } else if (lev > MAX_LEV_DISTANCE) {
+                    // So our item's name doesn't match at all and has generics.
+                    //
+                    // Maybe it's present in a sub generic? For example "f<A<B<C>>>()", if we're
+                    // looking for "B<C>", we'll need to go down.
+                    return checkIfInGenerics(row, elem);
+                } else {
+                    // At this point, the name kinda match and we have generics to check, so
+                    // let's go!
+                    const tmp_lev = checkGenerics(row, elem, lev);
+                    if (tmp_lev > MAX_LEV_DISTANCE) {
+                        return MAX_LEV_DISTANCE + 1;
                     }
+                    // We compute the median value of both checks and return it.
+                    return (tmp_lev + lev) / 2;
                 }
+            } else if (elem.generics.length > 0) {
+                // In this case, we were expecting generics but there isn't so we simply reject this
+                // one.
+                return MAX_LEV_DISTANCE + 1;
             }
-            // Now whatever happens, the returned distance is "less good" so we should mark it
-            // as such, and so we add 1 to the distance to make it "less good".
-            return Math.min(lev_distance, tmp_lev) + 1;
+            // No generics on our query or on the target type so we can return without doing
+            // anything else.
+            return lev;
         }
 
         /**
-         * This function checks if the object (`obj`) has an argument with the given type (`val`).
+         * This function checks if the object (`row`) has an argument with the given type (`elem`).
          *
-         * @param {Object} obj
-         * @param {string} val
-         * @param {boolean} literalSearch
+         * @param {Row} row
+         * @param {QueryElement} elem    - The element from the parsed query.
          * @param {integer} typeFilter
          *
          * @return {integer} - Returns a Levenshtein distance to the best match. If there is no
          *                      match, returns `MAX_LEV_DISTANCE + 1`.
          */
-        function findArg(obj, val, literalSearch, typeFilter) {
-            var lev_distance = MAX_LEV_DISTANCE + 1;
-
-            if (obj && obj.type && obj.type[INPUTS_DATA] && obj.type[INPUTS_DATA].length > 0) {
-                var length = obj.type[INPUTS_DATA].length;
-                for (var i = 0; i < length; i++) {
-                    var tmp = obj.type[INPUTS_DATA][i];
-                    if (!typePassesFilter(typeFilter, tmp[1])) {
+        function findArg(row, elem, typeFilter) {
+            let lev = MAX_LEV_DISTANCE + 1;
+
+            if (row && row.type && row.type[INPUTS_DATA] && row.type[INPUTS_DATA].length > 0) {
+                for (const input of row.type[INPUTS_DATA]) {
+                    if (!typePassesFilter(typeFilter, input[1])) {
                         continue;
                     }
-                    tmp = checkType(tmp, val, literalSearch);
-                    if (tmp === 0) {
+                    lev = Math.min(lev, checkType(input, elem, parsedQuery.literalSearch));
+                    if (lev === 0) {
                         return 0;
-                    } else if (literalSearch) {
-                        continue;
                     }
-                    lev_distance = Math.min(tmp, lev_distance);
                 }
             }
-            return literalSearch ? MAX_LEV_DISTANCE + 1 : lev_distance;
+            return parsedQuery.literalSearch ? MAX_LEV_DISTANCE + 1 : lev;
         }
 
-        function checkReturned(obj, val, literalSearch, typeFilter) {
-            var lev_distance = MAX_LEV_DISTANCE + 1;
+        /**
+         * This function checks if the object (`row`) returns the given type (`elem`).
+         *
+         * @param {Row} row
+         * @param {QueryElement} elem   - The element from the parsed query.
+         * @param {integer} typeFilter
+         *
+         * @return {integer} - Returns a Levenshtein distance to the best match. If there is no
+         *                      match, returns `MAX_LEV_DISTANCE + 1`.
+         */
+        function checkReturned(row, elem, typeFilter) {
+            let lev = MAX_LEV_DISTANCE + 1;
 
-            if (obj && obj.type && obj.type.length > OUTPUT_DATA) {
-                var ret = obj.type[OUTPUT_DATA];
+            if (row && row.type && row.type.length > OUTPUT_DATA) {
+                let ret = row.type[OUTPUT_DATA];
                 if (typeof ret[0] === "string") {
                     ret = [ret];
                 }
-                for (var x = 0, len = ret.length; x < len; ++x) {
-                    var tmp = ret[x];
-                    if (!typePassesFilter(typeFilter, tmp[1])) {
+                for (const ret_ty of ret) {
+                    if (!typePassesFilter(typeFilter, ret_ty[1])) {
                         continue;
                     }
-                    tmp = checkType(tmp, val, literalSearch);
-                    if (tmp === 0) {
+                    lev = Math.min(lev, checkType(ret_ty, elem, parsedQuery.literalSearch));
+                    if (lev === 0) {
                         return 0;
-                    } else if (literalSearch) {
-                        continue;
                     }
-                    lev_distance = Math.min(tmp, lev_distance);
                 }
             }
-            return literalSearch ? MAX_LEV_DISTANCE + 1 : lev_distance;
+            return parsedQuery.literalSearch ? MAX_LEV_DISTANCE + 1 : lev;
         }
 
         function checkPath(contains, lastElem, ty) {
             if (contains.length === 0) {
                 return 0;
             }
-            var ret_lev = MAX_LEV_DISTANCE + 1;
-            var path = ty.path.split("::");
+            let ret_lev = MAX_LEV_DISTANCE + 1;
+            const path = ty.path.split("::");
 
             if (ty.parent && ty.parent.name) {
                 path.push(ty.parent.name.toLowerCase());
             }
 
-            var length = path.length;
-            var clength = contains.length;
+            const length = path.length;
+            const clength = contains.length;
             if (clength > length) {
                 return MAX_LEV_DISTANCE + 1;
             }
-            for (var i = 0; i < length; ++i) {
+            for (let i = 0; i < length; ++i) {
                 if (i + clength > length) {
                     break;
                 }
-                var lev_total = 0;
-                var aborted = false;
-                for (var x = 0; x < clength; ++x) {
-                    var lev = levenshtein(path[i + x], contains[x]);
+                let lev_total = 0;
+                let aborted = false;
+                for (let x = 0; x < clength; ++x) {
+                    const lev = levenshtein(path[i + x], contains[x]);
                     if (lev > MAX_LEV_DISTANCE) {
                         aborted = true;
                         break;
@@ -591,7 +1126,7 @@ window.initSearch = function(rawSearchIndex) {
             if (filter <= NO_TYPE_FILTER || filter === type) return true;
 
             // Match related items
-            var name = itemTypes[type];
+            const name = itemTypes[type];
             switch (itemTypes[filter]) {
                 case "constant":
                     return name === "associatedconstant";
@@ -621,32 +1156,31 @@ window.initSearch = function(rawSearchIndex) {
         }
 
         function handleAliases(ret, query, filterCrates) {
+            const lowerQuery = query.toLowerCase();
             // We separate aliases and crate aliases because we want to have current crate
             // aliases to be before the others in the displayed results.
-            var aliases = [];
-            var crateAliases = [];
+            const aliases = [];
+            const crateAliases = [];
             if (filterCrates !== null) {
-                if (ALIASES[filterCrates] && ALIASES[filterCrates][query.search]) {
-                    var query_aliases = ALIASES[filterCrates][query.search];
-                    var len = query_aliases.length;
-                    for (var i = 0; i < len; ++i) {
-                        aliases.push(createAliasFromItem(searchIndex[query_aliases[i]]));
+                if (ALIASES[filterCrates] && ALIASES[filterCrates][lowerQuery]) {
+                    const query_aliases = ALIASES[filterCrates][lowerQuery];
+                    for (const alias of query_aliases) {
+                        aliases.push(createAliasFromItem(searchIndex[alias]));
                     }
                 }
             } else {
                 Object.keys(ALIASES).forEach(function(crate) {
-                    if (ALIASES[crate][query.search]) {
-                        var pushTo = crate === window.currentCrate ? crateAliases : aliases;
-                        var query_aliases = ALIASES[crate][query.search];
-                        var len = query_aliases.length;
-                        for (var i = 0; i < len; ++i) {
-                            pushTo.push(createAliasFromItem(searchIndex[query_aliases[i]]));
+                    if (ALIASES[crate][lowerQuery]) {
+                        const pushTo = crate === window.currentCrate ? crateAliases : aliases;
+                        const query_aliases = ALIASES[crate][lowerQuery];
+                        for (const alias of query_aliases) {
+                            pushTo.push(createAliasFromItem(searchIndex[alias]));
                         }
                     }
                 });
             }
 
-            var sortFunc = function(aaa, bbb) {
+            const sortFunc = function(aaa, bbb) {
                 if (aaa.path < bbb.path) {
                     return 1;
                 } else if (aaa.path === bbb.path) {
@@ -657,9 +1191,9 @@ window.initSearch = function(rawSearchIndex) {
             crateAliases.sort(sortFunc);
             aliases.sort(sortFunc);
 
-            var pushFunc = function(alias) {
-                alias.alias = query.raw;
-                var res = buildHrefAndPath(alias);
+            const pushFunc = function(alias) {
+                alias.alias = query;
+                const res = buildHrefAndPath(alias);
                 alias.displayPath = pathSplitter(res[0]);
                 alias.fullPath = alias.displayPath + alias.name;
                 alias.href = res[1];
@@ -674,208 +1208,235 @@ window.initSearch = function(rawSearchIndex) {
         }
 
         /**
-         * This function adds the given result into the provided `res` map if it matches the
+         * This function adds the given result into the provided `results` map if it matches the
          * following condition:
          *
-         * * If it is a "literal search" (`isExact`), then `lev` must be 0.
+         * * If it is a "literal search" (`parsedQuery.literalSearch`), then `lev` must be 0.
          * * If it is not a "literal search", `lev` must be <= `MAX_LEV_DISTANCE`.
          *
-         * The `res` map contains information which will be used to sort the search results:
+         * The `results` map contains information which will be used to sort the search results:
          *
-         * * `fullId` is a `string`` used as the key of the object we use for the `res` map.
+         * * `fullId` is a `string`` used as the key of the object we use for the `results` map.
          * * `id` is the index in both `searchWords` and `searchIndex` arrays for this element.
          * * `index` is an `integer`` used to sort by the position of the word in the item's name.
          * * `lev` is the main metric used to sort the search results.
          *
-         * @param {boolean} isExact
-         * @param {Object} res
+         * @param {Results} results
          * @param {string} fullId
          * @param {integer} id
          * @param {integer} index
          * @param {integer} lev
          */
-        function addIntoResults(isExact, res, fullId, id, index, lev) {
-            if (lev === 0 || (!isExact && lev <= MAX_LEV_DISTANCE)) {
-                if (res[fullId] !== undefined) {
-                    var result = res[fullId];
+        function addIntoResults(results, fullId, id, index, lev) {
+            if (lev === 0 || (!parsedQuery.literalSearch && lev <= MAX_LEV_DISTANCE)) {
+                if (results[fullId] !== undefined) {
+                    const result = results[fullId];
                     if (result.dontValidate || result.lev <= lev) {
                         return;
                     }
                 }
-                res[fullId] = {
+                results[fullId] = {
                     id: id,
                     index: index,
-                    dontValidate: isExact,
+                    dontValidate: parsedQuery.literalSearch,
                     lev: lev,
                 };
             }
         }
 
-        // quoted values mean literal search
-        var nSearchWords = searchWords.length;
-        var i, it;
-        var ty;
-        var fullId;
-        var returned;
-        var in_args;
-        var len;
-        if ((val.charAt(0) === "\"" || val.charAt(0) === "'") &&
-            val.charAt(val.length - 1) === val.charAt(0))
-        {
-            val = extractGenerics(val.substr(1, val.length - 2));
-            for (i = 0; i < nSearchWords; ++i) {
-                if (filterCrates !== null && searchIndex[i].crate !== filterCrates) {
-                    continue;
+        /**
+         * This function is called in case the query is only one element (with or without generics).
+         * This element will be compared to arguments' and returned values' items and also to items.
+         *
+         * Other important thing to note: since there is only one element, we use levenshtein
+         * distance for name comparisons.
+         *
+         * @param {Row} row
+         * @param {integer} pos              - Position in the `searchIndex`.
+         * @param {QueryElement} elem        - The element from the parsed query.
+         * @param {Results} results_others   - Unqualified results (not in arguments nor in
+         *                                     returned values).
+         * @param {Results} results_in_args  - Matching arguments results.
+         * @param {Results} results_returned - Matching returned arguments results.
+         */
+        function handleSingleArg(
+            row,
+            pos,
+            elem,
+            results_others,
+            results_in_args,
+            results_returned
+        ) {
+            if (!row || (filterCrates !== null && row.crate !== filterCrates)) {
+                return;
+            }
+            let lev, lev_add = 0, index = -1;
+            const fullId = row.id;
+
+            const in_args = findArg(row, elem, parsedQuery.typeFilter);
+            const returned = checkReturned(row, elem, parsedQuery.typeFilter);
+
+            addIntoResults(results_in_args, fullId, pos, index, in_args);
+            addIntoResults(results_returned, fullId, pos, index, returned);
+
+            if (!typePassesFilter(parsedQuery.typeFilter, row.ty)) {
+                return;
+            }
+            const searchWord = searchWords[pos];
+
+            if (parsedQuery.literalSearch) {
+                if (searchWord === elem.name) {
+                    addIntoResults(results_others, fullId, pos, -1, 0);
+                }
+                return;
+            }
+
+            // No need to check anything else if it's a "pure" generics search.
+            if (elem.name.length === 0) {
+                if (row.type !== null) {
+                    lev = checkGenerics(row.type, elem, MAX_LEV_DISTANCE + 1);
+                    addIntoResults(results_others, fullId, pos, index, lev);
                 }
-                in_args = findArg(searchIndex[i], val, true, typeFilter);
-                returned = checkReturned(searchIndex[i], val, true, typeFilter);
-                ty = searchIndex[i];
-                fullId = ty.id;
-
-                if (searchWords[i] === val.name
-                    && typePassesFilter(typeFilter, searchIndex[i].ty)) {
-                    addIntoResults(true, results, fullId, i, -1, 0);
+                return;
+            }
+
+            if (elem.fullPath.length > 1) {
+                lev = checkPath(elem.pathWithoutLast, elem.pathLast, row);
+                if (lev > MAX_LEV_DISTANCE || (parsedQuery.literalSearch && lev !== 0)) {
+                    return;
+                } else if (lev > 0) {
+                    lev_add = lev / 10;
                 }
-                addIntoResults(true, results_in_args, fullId, i, -1, in_args);
-                addIntoResults(true, results_returned, fullId, i, -1, returned);
-            }
-            query.inputs = [val];
-            query.output = val;
-            query.search = val;
-        // searching by type
-        } else if (val.search("->") > -1) {
-            var trimmer = function(s) { return s.trim(); };
-            var parts = val.split("->").map(trimmer);
-            var input = parts[0];
-            // sort inputs so that order does not matter
-            var inputs = input.split(",").map(trimmer).sort();
-            for (i = 0, len = inputs.length; i < len; ++i) {
-                inputs[i] = extractGenerics(inputs[i]);
-            }
-            var output = extractGenerics(parts[1]);
-
-            for (i = 0; i < nSearchWords; ++i) {
-                if (filterCrates !== null && searchIndex[i].crate !== filterCrates) {
-                    continue;
+            }
+
+            if (searchWord.indexOf(elem.pathLast) > -1 ||
+                row.normalizedName.indexOf(elem.pathLast) > -1)
+            {
+                // filter type: ... queries
+                if (!results_others[fullId] !== undefined) {
+                    index = row.normalizedName.indexOf(elem.pathLast);
                 }
-                var type = searchIndex[i].type;
-                ty = searchIndex[i];
-                if (!type) {
-                    continue;
+            }
+            lev = levenshtein(searchWord, elem.pathLast);
+            lev += lev_add;
+            if (lev > 0 && elem.pathLast.length > 2 && searchWord.indexOf(elem.pathLast) > -1)
+            {
+                if (elem.pathLast.length < 6) {
+                    lev = 1;
+                } else {
+                    lev = 0;
                 }
-                fullId = ty.id;
+            }
+            if (lev > MAX_LEV_DISTANCE) {
+                return;
+            } else if (index !== -1 && elem.fullPath.length < 2) {
+                lev -= 1;
+            }
+            if (lev < 0) {
+                lev = 0;
+            }
+            addIntoResults(results_others, fullId, pos, index, lev);
+        }
 
-                returned = checkReturned(ty, output, true, NO_TYPE_FILTER);
-                if (output.name === "*" || returned === 0) {
-                    in_args = false;
-                    var is_module = false;
+        /**
+         * This function is called in case the query has more than one element. In this case, it'll
+         * try to match the items which validates all the elements. For `aa -> bb` will look for
+         * functions which have a parameter `aa` and has `bb` in its returned values.
+         *
+         * @param {Row} row
+         * @param {integer} pos      - Position in the `searchIndex`.
+         * @param {Object} results
+         */
+        function handleArgs(row, pos, results) {
+            if (!row || (filterCrates !== null && row.crate !== filterCrates)) {
+                return;
+            }
 
-                    if (input === "*") {
-                        is_module = true;
+            let totalLev = 0;
+            let nbLev = 0;
+
+            // If the result is too "bad", we return false and it ends this search.
+            function checkArgs(elems, callback) {
+                for (const elem of elems) {
+                    // There is more than one parameter to the query so all checks should be "exact"
+                    const lev = callback(row, elem, NO_TYPE_FILTER);
+                    if (lev <= 1) {
+                        nbLev += 1;
+                        totalLev += lev;
                     } else {
-                        var firstNonZeroDistance = 0;
-                        for (it = 0, len = inputs.length; it < len; it++) {
-                            var distance = checkType(type, inputs[it], true);
-                            if (distance > 0) {
-                                firstNonZeroDistance = distance;
-                                break;
-                            }
-                        }
-                        in_args = firstNonZeroDistance;
-                    }
-                    addIntoResults(true, results_in_args, fullId, i, -1, in_args);
-                    addIntoResults(true, results_returned, fullId, i, -1, returned);
-                    if (is_module) {
-                        addIntoResults(true, results, fullId, i, -1, 0);
+                        return false;
                     }
                 }
+                return true;
+            }
+            if (!checkArgs(parsedQuery.elems, findArg)) {
+                return;
+            }
+            if (!checkArgs(parsedQuery.returned, checkReturned)) {
+                return;
             }
-            query.inputs = inputs.map(function(input) {
-                return input.name;
-            });
-            query.output = output.name;
-        } else {
-            query.inputs = [val];
-            query.output = val;
-            query.search = val;
-            // gather matching search results up to a certain maximum
-            val = val.replace(/_/g, "");
-
-            var valGenerics = extractGenerics(val);
-
-            var paths = valLower.split("::");
-            removeEmptyStringsFromArray(paths);
-            val = paths[paths.length - 1];
-            var contains = paths.slice(0, paths.length > 1 ? paths.length - 1 : 1);
-
-            var lev, j;
-            for (j = 0; j < nSearchWords; ++j) {
-                ty = searchIndex[j];
-                if (!ty || (filterCrates !== null && ty.crate !== filterCrates)) {
-                    continue;
-                }
-                var lev_add = 0;
-                if (paths.length > 1) {
-                    lev = checkPath(contains, paths[paths.length - 1], ty);
-                    if (lev > MAX_LEV_DISTANCE) {
-                        continue;
-                    } else if (lev > 0) {
-                        lev_add = lev / 10;
-                    }
-                }
 
-                returned = MAX_LEV_DISTANCE + 1;
-                in_args = MAX_LEV_DISTANCE + 1;
-                var index = -1;
-                // we want lev results to go lower than others
-                lev = MAX_LEV_DISTANCE + 1;
-                fullId = ty.id;
+            if (nbLev === 0) {
+                return;
+            }
+            const lev = Math.round(totalLev / nbLev);
+            addIntoResults(results, row.id, pos, 0, lev);
+        }
 
-                if (searchWords[j].indexOf(split[i]) > -1 ||
-                    searchWords[j].indexOf(val) > -1 ||
-                    ty.normalizedName.indexOf(val) > -1)
-                {
-                    // filter type: ... queries
-                    if (typePassesFilter(typeFilter, ty.ty) && results[fullId] === undefined) {
-                        index = ty.normalizedName.indexOf(val);
+        function innerRunQuery() {
+            let elem, i, nSearchWords, in_returned, row;
+
+            if (parsedQuery.foundElems === 1) {
+                if (parsedQuery.elems.length === 1) {
+                    elem = parsedQuery.elems[0];
+                    for (i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) {
+                        // It means we want to check for this element everywhere (in names, args and
+                        // returned).
+                        handleSingleArg(
+                            searchIndex[i],
+                            i,
+                            elem,
+                            results_others,
+                            results_in_args,
+                            results_returned
+                        );
                     }
-                }
-                if ((lev = levenshtein(searchWords[j], val)) <= MAX_LEV_DISTANCE) {
-                    if (typePassesFilter(typeFilter, ty.ty)) {
-                        lev += 1;
-                    } else {
-                        lev = MAX_LEV_DISTANCE + 1;
+                } else if (parsedQuery.returned.length === 1) {
+                    // We received one returned argument to check, so looking into returned values.
+                    elem = parsedQuery.returned[0];
+                    for (i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) {
+                        row = searchIndex[i];
+                        in_returned = checkReturned(row, elem, parsedQuery.typeFilter);
+                        addIntoResults(results_returned, row.id, i, -1, in_returned);
                     }
                 }
-                in_args = findArg(ty, valGenerics, false, typeFilter);
-                returned = checkReturned(ty, valGenerics, false, typeFilter);
-
-                lev += lev_add;
-                if (lev > 0 && val.length > 3 && searchWords[j].indexOf(val) > -1) {
-                    if (val.length < 6) {
-                        lev -= 1;
-                    } else {
-                        lev = 0;
-                    }
+            } else if (parsedQuery.foundElems > 0) {
+                let container = results_others;
+                // In the special case where only a "returned" information is available, we want to
+                // put the information into the "results_returned" dict.
+                if (parsedQuery.returned.length !== 0 && parsedQuery.elems.length === 0) {
+                    container = results_returned;
                 }
-                addIntoResults(false, results_in_args, fullId, j, index, in_args);
-                addIntoResults(false, results_returned, fullId, j, index, returned);
-                if (typePassesFilter(typeFilter, ty.ty) &&
-                        (index !== -1 || lev <= MAX_LEV_DISTANCE)) {
-                    if (index !== -1 && paths.length < 2) {
-                        lev = 0;
-                    }
-                    addIntoResults(false, results, fullId, j, index, lev);
+                for (i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) {
+                    handleArgs(searchIndex[i], i, container);
                 }
             }
         }
 
-        var ret = {
-            "in_args": sortResults(results_in_args, true),
-            "returned": sortResults(results_returned, true),
-            "others": sortResults(results, false),
-        };
-        handleAliases(ret, query, filterCrates);
+        if (parsedQuery.error === null) {
+            innerRunQuery();
+        }
+
+        const ret = createQueryResults(
+            sortResults(results_in_args, true),
+            sortResults(results_returned, true),
+            sortResults(results_others, false),
+            parsedQuery);
+        handleAliases(ret, parsedQuery.original.replace(/"/g, ""), filterCrates);
+        if (parsedQuery.error !== null && ret.others.length !== 0) {
+            // It means some doc aliases were found so let's "remove" the error!
+            ret.query.error = null;
+        }
         return ret;
     }
 
@@ -892,53 +1453,33 @@ window.initSearch = function(rawSearchIndex) {
      * @param  {string} path   - The path of the result
      * @param  {string} keys   - The keys to be used (["file", "open"])
      * @param  {Object} parent - The parent of the result
+     *
      * @return {boolean}       - Whether the result is valid or not
      */
     function validateResult(name, path, keys, parent) {
-        for (var i = 0, len = keys.length; i < len; ++i) {
+        if (!keys || !keys.length) {
+            return true;
+        }
+        for (const key of keys) {
             // each check is for validation so we negate the conditions and invalidate
             if (!(
                 // check for an exact name match
-                name.indexOf(keys[i]) > -1 ||
+                name.indexOf(key) > -1 ||
                 // then an exact path match
-                path.indexOf(keys[i]) > -1 ||
+                path.indexOf(key) > -1 ||
                 // next if there is a parent, check for exact parent match
                 (parent !== undefined && parent.name !== undefined &&
-                    parent.name.toLowerCase().indexOf(keys[i]) > -1) ||
+                    parent.name.toLowerCase().indexOf(key) > -1) ||
                 // lastly check to see if the name was a levenshtein match
-                levenshtein(name, keys[i]) <= MAX_LEV_DISTANCE)) {
+                levenshtein(name, key) <= MAX_LEV_DISTANCE)) {
                 return false;
             }
         }
         return true;
     }
 
-    /**
-     * Parse a string into a query object.
-     *
-     * @param {string} raw - The text that the user typed.
-     * @returns {ParsedQuery}
-     */
-    function getQuery(raw) {
-        var matches, type = "", query;
-        query = raw;
-
-        matches = query.match(/^(fn|mod|struct|enum|trait|type|const|macro)\s*:\s*/i);
-        if (matches) {
-            type = matches[1].replace(/^const$/, "constant");
-            query = query.substring(matches[0].length);
-        }
-
-        return {
-            raw: raw,
-            query: query,
-            type: type,
-            id: query + type
-        };
-    }
-
     function nextTab(direction) {
-        var next = (searchState.currentTab + direction + 3) % searchState.focusedByTab.length;
+        const next = (searchState.currentTab + direction + 3) % searchState.focusedByTab.length;
         searchState.focusedByTab[searchState.currentTab] = document.activeElement;
         printTab(next);
         focusSearchResult();
@@ -947,7 +1488,7 @@ window.initSearch = function(rawSearchIndex) {
     // Focus the first search result on the active tab, or the result that
     // was focused last time this tab was active.
     function focusSearchResult() {
-        var target = searchState.focusedByTab[searchState.currentTab] ||
+        const target = searchState.focusedByTab[searchState.currentTab] ||
             document.querySelectorAll(".search-results.active a").item(0) ||
             document.querySelectorAll("#titles > button").item(searchState.currentTab);
         if (target) {
@@ -956,11 +1497,11 @@ window.initSearch = function(rawSearchIndex) {
     }
 
     function buildHrefAndPath(item) {
-        var displayPath;
-        var href;
-        var type = itemTypes[item.ty];
-        var name = item.name;
-        var path = item.path;
+        let displayPath;
+        let href;
+        const type = itemTypes[item.ty];
+        const name = item.name;
+        let path = item.path;
 
         if (type === "mod") {
             displayPath = path + "::";
@@ -974,19 +1515,19 @@ window.initSearch = function(rawSearchIndex) {
             displayPath = "";
             href = window.rootPath + name + "/index.html";
         } else if (item.parent !== undefined) {
-            var myparent = item.parent;
-            var anchor = "#" + type + "." + name;
-            var parentType = itemTypes[myparent.ty];
-            var pageType = parentType;
-            var pageName = myparent.name;
+            const myparent = item.parent;
+            let anchor = "#" + type + "." + name;
+            const parentType = itemTypes[myparent.ty];
+            let pageType = parentType;
+            let pageName = myparent.name;
 
             if (parentType === "primitive") {
                 displayPath = myparent.name + "::";
             } else if (type === "structfield" && parentType === "variant") {
                 // Structfields belonging to variants are special: the
                 // final path element is the enum name.
-                var enumNameIdx = item.path.lastIndexOf("::");
-                var enumName = item.path.substr(enumNameIdx + 2);
+                const enumNameIdx = item.path.lastIndexOf("::");
+                const enumName = item.path.substr(enumNameIdx + 2);
                 path = item.path.substr(0, enumNameIdx);
                 displayPath = path + "::" + enumName + "::" + myparent.name + "::";
                 anchor = "#variant." + myparent.name + ".field." + name;
@@ -1008,13 +1549,13 @@ window.initSearch = function(rawSearchIndex) {
     }
 
     function escape(content) {
-        var h1 = document.createElement("h1");
+        const h1 = document.createElement("h1");
         h1.textContent = content;
         return h1.innerHTML;
     }
 
     function pathSplitter(path) {
-        var tmp = "<span>" + path.replace(/::/g, "::</span><span>");
+        const tmp = "<span>" + path.replace(/::/g, "::</span><span>");
         if (tmp.endsWith("<span>")) {
             return tmp.slice(0, tmp.length - 6);
         }
@@ -1028,42 +1569,42 @@ window.initSearch = function(rawSearchIndex) {
      * @param {boolean}     display - True if this is the active tab
      */
     function addTab(array, query, display) {
-        var extraClass = "";
+        let extraClass = "";
         if (display === true) {
             extraClass = " active";
         }
 
-        var output = document.createElement("div");
-        var length = 0;
+        const output = document.createElement("div");
+        let length = 0;
         if (array.length > 0) {
             output.className = "search-results " + extraClass;
 
             array.forEach(function(item) {
-                var name = item.name;
-                var type = itemTypes[item.ty];
+                const name = item.name;
+                const type = itemTypes[item.ty];
 
                 length += 1;
 
-                var extra = "";
+                let extra = "";
                 if (type === "primitive") {
                     extra = " <i>(primitive type)</i>";
                 } else if (type === "keyword") {
                     extra = " <i>(keyword)</i>";
                 }
 
-                var link = document.createElement("a");
+                const link = document.createElement("a");
                 link.className = "result-" + type;
                 link.href = item.href;
 
-                var wrapper = document.createElement("div");
-                var resultName = document.createElement("div");
+                const wrapper = document.createElement("div");
+                const resultName = document.createElement("div");
                 resultName.className = "result-name";
 
                 if (item.is_alias) {
-                    var alias = document.createElement("span");
+                    const alias = document.createElement("span");
                     alias.className = "alias";
 
-                    var bold = document.createElement("b");
+                    const bold = document.createElement("b");
                     bold.innerText = item.alias;
                     alias.appendChild(bold);
 
@@ -1078,9 +1619,9 @@ window.initSearch = function(rawSearchIndex) {
                     item.displayPath + "<span class=\"" + type + "\">" + name + extra + "</span>");
                 wrapper.appendChild(resultName);
 
-                var description = document.createElement("div");
+                const description = document.createElement("div");
                 description.className = "desc";
-                var spanDesc = document.createElement("span");
+                const spanDesc = document.createElement("span");
                 spanDesc.insertAdjacentHTML("beforeend", item.desc);
 
                 description.appendChild(spanDesc);
@@ -1088,11 +1629,11 @@ window.initSearch = function(rawSearchIndex) {
                 link.appendChild(wrapper);
                 output.appendChild(link);
             });
-        } else {
+        } else if (query.error === null) {
             output.className = "search-failed" + extraClass;
             output.innerHTML = "No results :(<br/>" +
                 "Try on <a href=\"https://duckduckgo.com/?q=" +
-                encodeURIComponent("rust " + query.query) +
+                encodeURIComponent("rust " + query.userQuery) +
                 "\">DuckDuckGo</a>?<br/><br/>" +
                 "Or try looking in one of these:<ul><li>The <a " +
                 "href=\"https://doc.rust-lang.org/reference/index.html\">Rust Reference</a> " +
@@ -1115,8 +1656,13 @@ window.initSearch = function(rawSearchIndex) {
         return "<button>" + text + " <div class=\"count\">(" + nbElems + ")</div></button>";
     }
 
+    /**
+     * @param {ResultsTable} results
+     * @param {boolean} go_to_first
+     * @param {string} filterCrates
+     */
     function showResults(results, go_to_first, filterCrates) {
-        var search = searchState.outputElement();
+        const search = searchState.outputElement();
         if (go_to_first || (results.others.length === 1
             && getSettingValue("go-to-only-result") === "true"
             // By default, the search DOM element is "empty" (meaning it has no children not
@@ -1124,7 +1670,7 @@ window.initSearch = function(rawSearchIndex) {
             // ESC or empty the search input (which also "cancels" the search).
             && (!search.firstChild || search.firstChild.innerText !== searchState.loadingText)))
         {
-            var elem = document.createElement("a");
+            const elem = document.createElement("a");
             elem.href = results.others[0].href;
             removeClass(elem, "active");
             // For firefox, we need the element to be in the DOM so it can be clicked.
@@ -1132,18 +1678,20 @@ window.initSearch = function(rawSearchIndex) {
             elem.click();
             return;
         }
-        var query = getQuery(searchState.input.value);
+        if (results.query === undefined) {
+            results.query = parseQuery(searchState.input.value);
+        }
 
-        currentResults = query.id;
+        currentResults = results.query.userQuery;
 
-        var ret_others = addTab(results.others, query, true);
-        var ret_in_args = addTab(results.in_args, query, false);
-        var ret_returned = addTab(results.returned, query, false);
+        const ret_others = addTab(results.others, results.query, true);
+        const ret_in_args = addTab(results.in_args, results.query, false);
+        const ret_returned = addTab(results.returned, results.query, false);
 
         // Navigate to the relevant tab if the current tab is empty, like in case users search
         // for "-> String". If they had selected another tab previously, they have to click on
         // it again.
-        var currentTab = searchState.currentTab;
+        let currentTab = searchState.currentTab;
         if ((currentTab === 0 && ret_others[1] === 0) ||
                 (currentTab === 1 && ret_in_args[1] === 0) ||
                 (currentTab === 2 && ret_returned[1] === 0)) {
@@ -1159,29 +1707,37 @@ window.initSearch = function(rawSearchIndex) {
         let crates = "";
         if (window.ALL_CRATES.length > 1) {
             crates = ` in <select id="crate-search"><option value="All crates">All crates</option>`;
-            for (let c of window.ALL_CRATES) {
+            for (const c of window.ALL_CRATES) {
                 crates += `<option value="${c}" ${c == filterCrates && "selected"}>${c}</option>`;
             }
             crates += `</select>`;
         }
-        var output = `<div id="search-settings">
-            <h1 class="search-results-title">Results for ${escape(query.query)} ` +
-            (query.type ? " (type: " + escape(query.type) + ")" : "") + "</h1>" +
-            crates +
-            `</div><div id="titles">` +
+
+        let typeFilter = "";
+        if (results.query.typeFilter !== NO_TYPE_FILTER) {
+            typeFilter = " (type: " + escape(itemTypes[results.query.typeFilter]) + ")";
+        }
+
+        let output = `<div id="search-settings">` +
+            `<h1 class="search-results-title">Results for ${escape(results.query.userQuery)}` +
+            `${typeFilter}</h1> in ${crates} </div>`;
+        if (results.query.error !== null) {
+            output += `<h3>Query parser error: "${results.query.error}".</h3>`;
+        }
+        output += `<div id="titles">` +
             makeTabHeader(0, "In Names", ret_others[1]) +
             makeTabHeader(1, "In Parameters", ret_in_args[1]) +
             makeTabHeader(2, "In Return Types", ret_returned[1]) +
             "</div>";
 
-        var resultsElem = document.createElement("div");
+        const resultsElem = document.createElement("div");
         resultsElem.id = "results";
         resultsElem.appendChild(ret_others[0]);
         resultsElem.appendChild(ret_in_args[0]);
         resultsElem.appendChild(ret_returned[0]);
 
         search.innerHTML = output;
-        let crateSearch = document.getElementById("crate-search");
+        const crateSearch = document.getElementById("crate-search");
         if (crateSearch) {
             crateSearch.addEventListener("input", updateCrate);
         }
@@ -1189,35 +1745,13 @@ window.initSearch = function(rawSearchIndex) {
         // Reset focused elements.
         searchState.focusedByTab = [null, null, null];
         searchState.showResults(search);
-        var elems = document.getElementById("titles").childNodes;
+        const elems = document.getElementById("titles").childNodes;
         elems[0].onclick = function() { printTab(0); };
         elems[1].onclick = function() { printTab(1); };
         elems[2].onclick = function() { printTab(2); };
         printTab(currentTab);
     }
 
-    function execSearch(query, searchWords, filterCrates) {
-        query = query.raw.trim();
-        var results = {
-            "in_args": [],
-            "returned": [],
-            "others": [],
-        };
-
-        if (query.length !== 0) {
-            var tmp = execQuery(getQuery(query), searchWords, filterCrates);
-
-            results.in_args.push(tmp.in_args);
-            results.returned.push(tmp.returned);
-            results.others.push(tmp.others);
-        }
-        return {
-            "in_args": results.in_args[0],
-            "returned": results.returned[0],
-            "others": results.others[0],
-        };
-    }
-
     /**
      * Perform a search based on the current state of the search input element
      * and display the results.
@@ -1225,24 +1759,21 @@ window.initSearch = function(rawSearchIndex) {
      * @param {boolean} [forced]
      */
     function search(e, forced) {
-        var params = searchState.getQueryStringParams();
-        var query = getQuery(searchState.input.value.trim());
+        const params = searchState.getQueryStringParams();
+        const query = parseQuery(searchState.input.value.trim());
 
         if (e) {
             e.preventDefault();
         }
 
-        if (query.query.length === 0) {
-            return;
-        }
-        if (!forced && query.id === currentResults) {
-            if (query.query.length > 0) {
+        if (!forced && query.userQuery === currentResults) {
+            if (query.userQuery.length > 0) {
                 putBackSearch();
             }
             return;
         }
 
-        var filterCrates = getFilterCrates();
+        let filterCrates = getFilterCrates();
 
         // In case we have no information about the saved crate and there is a URL query parameter,
         // we override it with the URL query parameter.
@@ -1251,13 +1782,12 @@ window.initSearch = function(rawSearchIndex) {
         }
 
         // Update document title to maintain a meaningful browser history
-        searchState.title = "Results for " + query.query + " - Rust";
+        searchState.title = "Results for " + query.original + " - Rust";
 
         // Because searching is incremental by character, only the most
         // recent search query is added to the browser history.
         if (searchState.browserSupportsHistoryApi()) {
-            var newURL = buildUrl(query.raw, filterCrates);
-
+            const newURL = buildUrl(query.original, filterCrates);
             if (!history.state && !params.search) {
                 history.pushState(null, "", newURL);
             } else {
@@ -1265,8 +1795,10 @@ window.initSearch = function(rawSearchIndex) {
             }
         }
 
-        showResults(execSearch(query, searchWords, filterCrates),
-            params["go_to_first"], filterCrates);
+        showResults(
+            execQuery(query, searchWords, filterCrates),
+            params.go_to_first,
+            filterCrates);
     }
 
     function buildIndex(rawSearchIndex) {
@@ -1274,17 +1806,17 @@ window.initSearch = function(rawSearchIndex) {
         /**
          * @type {Array<string>}
          */
-        var searchWords = [];
-        var i, word;
-        var currentIndex = 0;
-        var id = 0;
+        const searchWords = [];
+        let i, word;
+        let currentIndex = 0;
+        let id = 0;
 
-        for (var crate in rawSearchIndex) {
+        for (const crate in rawSearchIndex) {
             if (!hasOwnPropertyRustdoc(rawSearchIndex, crate)) {
                 continue;
             }
 
-            var crateSize = 0;
+            let crateSize = 0;
 
             /**
              * The raw search data for a given crate. `n`, `t`, `d`, and `q`, `i`, and `f`
@@ -1316,13 +1848,13 @@ window.initSearch = function(rawSearchIndex) {
              *   p: Array<Object>,
              * }}
              */
-            var crateCorpus = rawSearchIndex[crate];
+            const crateCorpus = rawSearchIndex[crate];
 
             searchWords.push(crate);
             // This object should have exactly the same set of fields as the "row"
             // object defined below. Your JavaScript runtime will thank you.
             // https://mathiasbynens.be/notes/shapes-ics
-            var crateRow = {
+            const crateRow = {
                 crate: crate,
                 ty: 1, // == ExternCrate
                 name: crate,
@@ -1338,26 +1870,26 @@ window.initSearch = function(rawSearchIndex) {
             currentIndex += 1;
 
             // an array of (Number) item types
-            var itemTypes = crateCorpus.t;
+            const itemTypes = crateCorpus.t;
             // an array of (String) item names
-            var itemNames = crateCorpus.n;
+            const itemNames = crateCorpus.n;
             // an array of (String) full paths (or empty string for previous path)
-            var itemPaths = crateCorpus.q;
+            const itemPaths = crateCorpus.q;
             // an array of (String) descriptions
-            var itemDescs = crateCorpus.d;
+            const itemDescs = crateCorpus.d;
             // an array of (Number) the parent path index + 1 to `paths`, or 0 if none
-            var itemParentIdxs = crateCorpus.i;
+            const itemParentIdxs = crateCorpus.i;
             // an array of (Object | null) the type of the function, if any
-            var itemFunctionSearchTypes = crateCorpus.f;
+            const itemFunctionSearchTypes = crateCorpus.f;
             // an array of [(Number) item type,
             //              (String) name]
-            var paths = crateCorpus.p;
+            const paths = crateCorpus.p;
             // an array of [(String) alias name
             //             [Number] index to items]
-            var aliases = crateCorpus.a;
+            const aliases = crateCorpus.a;
 
             // convert `rawPaths` entries into object form
-            var len = paths.length;
+            let len = paths.length;
             for (i = 0; i < len; ++i) {
                 paths[i] = {ty: paths[i][0], name: paths[i][1]};
             }
@@ -1370,7 +1902,7 @@ window.initSearch = function(rawSearchIndex) {
             // all other search operations have access to this cached data for
             // faster analysis operations
             len = itemTypes.length;
-            var lastPath = "";
+            let lastPath = "";
             for (i = 0; i < len; ++i) {
                 // This object should have exactly the same set of fields as the "crateRow"
                 // object defined above.
@@ -1381,7 +1913,7 @@ window.initSearch = function(rawSearchIndex) {
                     word = "";
                     searchWords.push("");
                 }
-                var row = {
+                const row = {
                     crate: crate,
                     ty: itemTypes[i],
                     name: itemNames[i],
@@ -1400,8 +1932,7 @@ window.initSearch = function(rawSearchIndex) {
 
             if (aliases) {
                 ALIASES[crate] = {};
-                var j, local_aliases;
-                for (var alias_name in aliases) {
+                for (const alias_name in aliases) {
                     if (!hasOwnPropertyRustdoc(aliases, alias_name)) {
                         continue;
                     }
@@ -1409,9 +1940,8 @@ window.initSearch = function(rawSearchIndex) {
                     if (!hasOwnPropertyRustdoc(ALIASES[crate], alias_name)) {
                         ALIASES[crate][alias_name] = [];
                     }
-                    local_aliases = aliases[alias_name];
-                    for (j = 0, len = local_aliases.length; j < len; ++j) {
-                        ALIASES[crate][alias_name].push(local_aliases[j] + currentIndex);
+                    for (const local_alias of aliases[alias_name]) {
+                        ALIASES[crate][alias_name].push(local_alias + currentIndex);
                     }
                 }
             }
@@ -1431,11 +1961,11 @@ window.initSearch = function(rawSearchIndex) {
     }
 
     function putBackSearch() {
-        var search_input = searchState.input;
+        const search_input = searchState.input;
         if (!searchState.input) {
             return;
         }
-        var search = searchState.outputElement();
+        const search = searchState.outputElement();
         if (search_input.value !== "" && hasClass(search, "hidden")) {
             searchState.showResults(search);
             if (searchState.browserSupportsHistoryApi()) {
@@ -1447,7 +1977,7 @@ window.initSearch = function(rawSearchIndex) {
     }
 
     function registerSearchEvents() {
-        var searchAfter500ms = function() {
+        const searchAfter500ms = function() {
             searchState.clearInputTimeout();
             if (searchState.input.value.length === 0) {
                 if (searchState.browserSupportsHistoryApi()) {
@@ -1485,7 +2015,7 @@ window.initSearch = function(rawSearchIndex) {
             // up and down arrow select next/previous search result, or the
             // search box if we're already at the top.
             if (e.which === 38) { // up
-                var previous = document.activeElement.previousElementSibling;
+                const previous = document.activeElement.previousElementSibling;
                 if (previous) {
                     previous.focus();
                 } else {
@@ -1493,11 +2023,11 @@ window.initSearch = function(rawSearchIndex) {
                 }
                 e.preventDefault();
             } else if (e.which === 40) { // down
-                var next = document.activeElement.nextElementSibling;
+                const next = document.activeElement.nextElementSibling;
                 if (next) {
                     next.focus();
                 }
-                var rect = document.activeElement.getBoundingClientRect();
+                const rect = document.activeElement.getBoundingClientRect();
                 if (window.innerHeight - rect.bottom < rect.height) {
                     window.scrollBy(0, rect.height);
                 }
@@ -1530,10 +2060,10 @@ window.initSearch = function(rawSearchIndex) {
         // history.
         if (searchState.browserSupportsHistoryApi()) {
             // Store the previous <title> so we can revert back to it later.
-            var previousTitle = document.title;
+            const previousTitle = document.title;
 
             window.addEventListener("popstate", function(e) {
-                var params = searchState.getQueryStringParams();
+                const params = searchState.getQueryStringParams();
                 // Revert to the previous title manually since the History
                 // API ignores the title parameter.
                 document.title = previousTitle;
@@ -1569,7 +2099,7 @@ window.initSearch = function(rawSearchIndex) {
         // that try to sync state between the URL and the search input. To work around it,
         // do a small amount of re-init on page show.
         window.onpageshow = function(){
-            var qSearch = searchState.getQueryStringParams().search;
+            const qSearch = searchState.getQueryStringParams().search;
             if (searchState.input.value === "" && qSearch) {
                 searchState.input.value = qSearch;
             }
@@ -1580,8 +2110,8 @@ window.initSearch = function(rawSearchIndex) {
     function updateCrate(ev) {
         if (ev.target.value === "All crates") {
             // If we don't remove it from the URL, it'll be picked up again by the search.
-            var params = searchState.getQueryStringParams();
-            var query = searchState.input.value.trim();
+            const params = searchState.getQueryStringParams();
+            const query = searchState.input.value.trim();
             if (!history.state && !params.search) {
                 history.pushState(null, "", buildUrl(query, null));
             } else {
@@ -1595,7 +2125,10 @@ window.initSearch = function(rawSearchIndex) {
         search(undefined, true);
     }
 
-    searchWords = buildIndex(rawSearchIndex);
+    /**
+     *  @type {Array<string>}
+     */
+    const searchWords = buildIndex(rawSearchIndex);
     registerSearchEvents();
 
     function runSearchIfNeeded() {
diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js
index 139fa5c9a11..7bc6f6cfe04 100644
--- a/src/librustdoc/html/static/js/settings.js
+++ b/src/librustdoc/html/static/js/settings.js
@@ -1,3 +1,6 @@
+/* eslint-env es6 */
+/* eslint no-var: "error" */
+/* eslint prefer-const: "error" */
 // Local js definitions:
 /* global getSettingValue, getVirtualKey, onEachLazy, updateLocalStorage, updateSystemTheme */
 /* global addClass, removeClass */
@@ -55,9 +58,9 @@
     function setEvents() {
         updateLightAndDark();
         onEachLazy(document.getElementsByClassName("slider"), function(elem) {
-            var toggle = elem.previousElementSibling;
-            var settingId = toggle.id;
-            var settingValue = getSettingValue(settingId);
+            const toggle = elem.previousElementSibling;
+            const settingId = toggle.id;
+            const settingValue = getSettingValue(settingId);
             if (settingValue !== null) {
                 toggle.checked = settingValue === "true";
             }
@@ -68,9 +71,9 @@
             toggle.onkeyrelease = handleKey;
         });
         onEachLazy(document.getElementsByClassName("select-wrapper"), function(elem) {
-            var select = elem.getElementsByTagName("select")[0];
-            var settingId = select.id;
-            var settingValue = getSettingValue(settingId);
+            const select = elem.getElementsByTagName("select")[0];
+            const settingId = select.id;
+            const settingValue = getSettingValue(settingId);
             if (settingValue !== null) {
                 select.value = settingValue;
             }
diff --git a/src/librustdoc/html/static/js/source-script.js b/src/librustdoc/html/static/js/source-script.js
index aa77e62ba5a..c48a847665e 100644
--- a/src/librustdoc/html/static/js/source-script.js
+++ b/src/librustdoc/html/static/js/source-script.js
@@ -1,3 +1,7 @@
+/* eslint-env es6 */
+/* eslint no-var: "error" */
+/* eslint prefer-const: "error" */
+
 // From rust:
 /* global search, sourcesIndex */
 
@@ -7,15 +11,15 @@
 (function() {
 
 function getCurrentFilePath() {
-    var parts = window.location.pathname.split("/");
-    var rootPathParts = window.rootPath.split("/");
+    const parts = window.location.pathname.split("/");
+    const rootPathParts = window.rootPath.split("/");
 
-    for (var i = 0, len = rootPathParts.length; i < len; ++i) {
-        if (rootPathParts[i] === "..") {
+    for (const rootPathPart of rootPathParts) {
+        if (rootPathPart === "..") {
             parts.pop();
         }
     }
-    var file = window.location.pathname.substring(parts.join("/").length);
+    let file = window.location.pathname.substring(parts.join("/").length);
     if (file.startsWith("/")) {
         file = file.substring(1);
     }
@@ -23,7 +27,7 @@ function getCurrentFilePath() {
 }
 
 function createDirEntry(elem, parent, fullPath, currentFile, hasFoundFile) {
-    var name = document.createElement("div");
+    const name = document.createElement("div");
     name.className = "name";
 
     fullPath += elem["name"] + "/";
@@ -37,16 +41,13 @@ function createDirEntry(elem, parent, fullPath, currentFile, hasFoundFile) {
     };
     name.innerText = elem["name"];
 
-    var i, len;
-
-    var children = document.createElement("div");
+    const children = document.createElement("div");
     children.className = "children";
-    var folders = document.createElement("div");
+    const folders = document.createElement("div");
     folders.className = "folders";
     if (elem.dirs) {
-        for (i = 0, len = elem.dirs.length; i < len; ++i) {
-            if (createDirEntry(elem.dirs[i], folders, fullPath, currentFile,
-                               hasFoundFile)) {
+        for (const dir of elem.dirs) {
+            if (createDirEntry(dir, folders, fullPath, currentFile, hasFoundFile)) {
                 addClass(name, "expand");
                 hasFoundFile = true;
             }
@@ -54,14 +55,14 @@ function createDirEntry(elem, parent, fullPath, currentFile, hasFoundFile) {
     }
     children.appendChild(folders);
 
-    var files = document.createElement("div");
+    const files = document.createElement("div");
     files.className = "files";
     if (elem.files) {
-        for (i = 0, len = elem.files.length; i < len; ++i) {
-            var file = document.createElement("a");
-            file.innerText = elem.files[i];
-            file.href = window.rootPath + "src/" + fullPath + elem.files[i] + ".html";
-            if (!hasFoundFile && currentFile === fullPath + elem.files[i]) {
+        for (const file_text of elem.files) {
+            const file = document.createElement("a");
+            file.innerText = file_text;
+            file.href = window.rootPath + "src/" + fullPath + file_text + ".html";
+            if (!hasFoundFile && currentFile === fullPath + file_text) {
                 file.className = "selected";
                 addClass(name, "expand");
                 hasFoundFile = true;
@@ -77,8 +78,8 @@ function createDirEntry(elem, parent, fullPath, currentFile, hasFoundFile) {
 }
 
 function toggleSidebar() {
-    var sidebar = document.querySelector("nav.sidebar");
-    var child = this.children[0];
+    const sidebar = document.querySelector("nav.sidebar");
+    const child = this.children[0];
     if (child.innerText === ">") {
         sidebar.classList.add("expanded");
         child.innerText = "<";
@@ -91,11 +92,11 @@ function toggleSidebar() {
 }
 
 function createSidebarToggle() {
-    var sidebarToggle = document.createElement("div");
+    const sidebarToggle = document.createElement("div");
     sidebarToggle.id = "sidebar-toggle";
     sidebarToggle.onclick = toggleSidebar;
 
-    var inner = document.createElement("div");
+    const inner = document.createElement("div");
 
     if (getCurrentValue("source-sidebar-show") === "true") {
         inner.innerText = "<";
@@ -113,12 +114,12 @@ function createSourceSidebar() {
     if (!window.rootPath.endsWith("/")) {
         window.rootPath += "/";
     }
-    var container = document.querySelector("nav.sidebar");
+    const container = document.querySelector("nav.sidebar");
 
-    var sidebarToggle = createSidebarToggle();
+    const sidebarToggle = createSidebarToggle();
     container.insertBefore(sidebarToggle, container.firstChild);
 
-    var sidebar = document.createElement("div");
+    const sidebar = document.createElement("div");
     sidebar.id = "source-sidebar";
     if (getCurrentValue("source-sidebar-show") !== "true") {
         container.classList.remove("expanded");
@@ -126,10 +127,10 @@ function createSourceSidebar() {
         container.classList.add("expanded");
     }
 
-    var currentFile = getCurrentFilePath();
-    var hasFoundFile = false;
+    const currentFile = getCurrentFilePath();
+    let hasFoundFile = false;
 
-    var title = document.createElement("div");
+    const title = document.createElement("div");
     title.className = "title";
     title.innerText = "Files";
     sidebar.appendChild(title);
@@ -141,13 +142,13 @@ function createSourceSidebar() {
 
     container.appendChild(sidebar);
     // Focus on the current file in the source files sidebar.
-    var selected_elem = sidebar.getElementsByClassName("selected")[0];
+    const selected_elem = sidebar.getElementsByClassName("selected")[0];
     if (typeof selected_elem !== "undefined") {
         selected_elem.focus();
     }
 }
 
-var lineNumbersRegex = /^#?(\d+)(?:-(\d+))?$/;
+const lineNumbersRegex = /^#?(\d+)(?:-(\d+))?$/;
 
 function highlightSourceLines(match) {
     if (typeof match === "undefined") {
@@ -156,21 +157,21 @@ function highlightSourceLines(match) {
     if (!match) {
         return;
     }
-    var from = parseInt(match[1], 10);
-    var to = from;
+    let from = parseInt(match[1], 10);
+    let to = from;
     if (typeof match[2] !== "undefined") {
         to = parseInt(match[2], 10);
     }
     if (to < from) {
-        var tmp = to;
+        const tmp = to;
         to = from;
         from = tmp;
     }
-    var elem = document.getElementById(from);
+    let elem = document.getElementById(from);
     if (!elem) {
         return;
     }
-    var x = document.getElementById(from);
+    const x = document.getElementById(from);
     if (x) {
         x.scrollIntoView();
     }
@@ -179,7 +180,7 @@ function highlightSourceLines(match) {
             removeClass(i_e, "line-highlighted");
         });
     });
-    for (var i = from; i <= to; ++i) {
+    for (let i = from; i <= to; ++i) {
         elem = document.getElementById(i);
         if (!elem) {
             break;
@@ -188,11 +189,11 @@ function highlightSourceLines(match) {
     }
 }
 
-var handleSourceHighlight = (function() {
-    var prev_line_id = 0;
+const handleSourceHighlight = (function() {
+    let prev_line_id = 0;
 
-    var set_fragment = function(name) {
-        var x = window.scrollX,
+    const set_fragment = function(name) {
+        const x = window.scrollX,
             y = window.scrollY;
         if (searchState.browserSupportsHistoryApi()) {
             history.replaceState(null, null, "#" + name);
@@ -205,13 +206,13 @@ var handleSourceHighlight = (function() {
     };
 
     return function(ev) {
-        var cur_line_id = parseInt(ev.target.id, 10);
+        let cur_line_id = parseInt(ev.target.id, 10);
         ev.preventDefault();
 
         if (ev.shiftKey && prev_line_id) {
             // Swap selection if needed
             if (prev_line_id > cur_line_id) {
-                var tmp = prev_line_id;
+                const tmp = prev_line_id;
                 prev_line_id = cur_line_id;
                 cur_line_id = tmp;
             }
@@ -226,7 +227,7 @@ var handleSourceHighlight = (function() {
 }());
 
 window.addEventListener("hashchange", function() {
-    var match = window.location.hash.match(lineNumbersRegex);
+    const match = window.location.hash.match(lineNumbersRegex);
     if (match) {
         return highlightSourceLines(match);
     }
diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js
index ccf3d0a581a..ae670ed9894 100644
--- a/src/librustdoc/html/static/js/storage.js
+++ b/src/librustdoc/html/static/js/storage.js
@@ -1,13 +1,17 @@
-var darkThemes = ["dark", "ayu"];
+/* eslint-env es6 */
+/* eslint no-var: "error" */
+/* eslint prefer-const: "error" */
+
+const darkThemes = ["dark", "ayu"];
 window.currentTheme = document.getElementById("themeStyle");
 window.mainTheme = document.getElementById("mainThemeStyle");
 
-var settingsDataset = (function () {
-    var settingsElement = document.getElementById("default-settings");
+const settingsDataset = (function () {
+    const settingsElement = document.getElementById("default-settings");
     if (settingsElement === null) {
         return null;
     }
-    var dataset = settingsElement.dataset;
+    const dataset = settingsElement.dataset;
     if (dataset === undefined) {
         return null;
     }
@@ -15,14 +19,14 @@ var settingsDataset = (function () {
 })();
 
 function getSettingValue(settingName) {
-    var current = getCurrentValue(settingName);
+    const current = getCurrentValue(settingName);
     if (current !== null) {
         return current;
     }
     if (settingsDataset !== null) {
         // See the comment for `default_settings.into_iter()` etc. in
         // `Options::from_matches` in `librustdoc/config.rs`.
-        var def = settingsDataset[settingName.replace(/-/g,'_')];
+        const def = settingsDataset[settingName.replace(/-/g,'_')];
         if (def !== undefined) {
             return def;
         }
@@ -30,9 +34,9 @@ function getSettingValue(settingName) {
     return null;
 }
 
-var localStoredTheme = getSettingValue("theme");
+const localStoredTheme = getSettingValue("theme");
 
-var savedHref = [];
+const savedHref = [];
 
 // eslint-disable-next-line no-unused-vars
 function hasClass(elem, className) {
@@ -63,17 +67,16 @@ function removeClass(elem, className) {
  */
 function onEach(arr, func, reversed) {
     if (arr && arr.length > 0 && func) {
-        var length = arr.length;
-        var i;
         if (reversed) {
-            for (i = length - 1; i >= 0; --i) {
+            const length = arr.length;
+            for (let i = length - 1; i >= 0; --i) {
                 if (func(arr[i])) {
                     return true;
                 }
             }
         } else {
-            for (i = 0; i < length; ++i) {
-                if (func(arr[i])) {
+            for (const elem of arr) {
+                if (func(elem)) {
                     return true;
                 }
             }
@@ -121,7 +124,7 @@ function getCurrentValue(name) {
 }
 
 function switchTheme(styleElem, mainStyleElem, newTheme, saveTheme) {
-    var newHref = mainStyleElem.href.replace(
+    const newHref = mainStyleElem.href.replace(
         /\/rustdoc([^/]*)\.css/, "/" + newTheme + "$1" + ".css");
 
     // If this new value comes from a system setting or from the previously
@@ -134,7 +137,7 @@ function switchTheme(styleElem, mainStyleElem, newTheme, saveTheme) {
         return;
     }
 
-    var found = false;
+    let found = false;
     if (savedHref.length === 0) {
         onEachLazy(document.getElementsByTagName("link"), function(el) {
             savedHref.push(el.href);
@@ -161,17 +164,17 @@ function useSystemTheme(value) {
     updateLocalStorage("use-system-theme", value);
 
     // update the toggle if we're on the settings page
-    var toggle = document.getElementById("use-system-theme");
+    const toggle = document.getElementById("use-system-theme");
     if (toggle && toggle instanceof HTMLInputElement) {
         toggle.checked = value;
     }
 }
 
-var updateSystemTheme = (function() {
+const updateSystemTheme = (function() {
     if (!window.matchMedia) {
         // fallback to the CSS computed value
         return function() {
-            var cssTheme = getComputedStyle(document.documentElement)
+            const cssTheme = getComputedStyle(document.documentElement)
                 .getPropertyValue('content');
 
             switchTheme(
@@ -184,16 +187,16 @@ var updateSystemTheme = (function() {
     }
 
     // only listen to (prefers-color-scheme: dark) because light is the default
-    var mql = window.matchMedia("(prefers-color-scheme: dark)");
+    const mql = window.matchMedia("(prefers-color-scheme: dark)");
 
     function handlePreferenceChange(mql) {
-        let use = function(theme) {
+        const use = function(theme) {
             switchTheme(window.currentTheme, window.mainTheme, theme, true);
         };
         // maybe the user has disabled the setting in the meantime!
         if (getSettingValue("use-system-theme") !== "false") {
-            var lightTheme = getSettingValue("preferred-light-theme") || "light";
-            var darkTheme = getSettingValue("preferred-dark-theme") || "dark";
+            const lightTheme = getSettingValue("preferred-light-theme") || "light";
+            const darkTheme = getSettingValue("preferred-dark-theme") || "dark";
 
             if (mql.matches) {
                 use(darkTheme);
diff --git a/src/librustdoc/html/static_files.rs b/src/librustdoc/html/static_files.rs
index 1837e4a3b65..bec5c083fed 100644
--- a/src/librustdoc/html/static_files.rs
+++ b/src/librustdoc/html/static_files.rs
@@ -92,17 +92,11 @@ crate mod themes {
 
 /// Files related to the Fira Sans font.
 crate mod fira_sans {
-    /// The file `FiraSans-Regular.woff`, the Regular variant of the Fira Sans font.
-    crate static REGULAR: &[u8] = include_bytes!("static/fonts/FiraSans-Regular.woff");
-
     /// The file `FiraSans-Regular.woff2`, the Regular variant of the Fira Sans font in woff2.
-    crate static REGULAR2: &[u8] = include_bytes!("static/fonts/FiraSans-Regular.woff2");
-
-    /// The file `FiraSans-Medium.woff`, the Medium variant of the Fira Sans font.
-    crate static MEDIUM: &[u8] = include_bytes!("static/fonts/FiraSans-Medium.woff");
+    crate static REGULAR: &[u8] = include_bytes!("static/fonts/FiraSans-Regular.woff2");
 
     /// The file `FiraSans-Medium.woff2`, the Medium variant of the Fira Sans font in woff2.
-    crate static MEDIUM2: &[u8] = include_bytes!("static/fonts/FiraSans-Medium.woff2");
+    crate static MEDIUM: &[u8] = include_bytes!("static/fonts/FiraSans-Medium.woff2");
 
     /// The file `FiraSans-LICENSE.txt`, the license text for the Fira Sans font.
     crate static LICENSE: &[u8] = include_bytes!("static/fonts/FiraSans-LICENSE.txt");
@@ -110,26 +104,17 @@ crate mod fira_sans {
 
 /// Files related to the Source Serif 4 font.
 crate mod source_serif_4 {
-    /// The file `SourceSerif4-Regular.ttf.woff`, the Regular variant of the Source Serif 4 font.
-    crate static REGULAR: &[u8] = include_bytes!("static/fonts/SourceSerif4-Regular.ttf.woff");
-
     /// The file `SourceSerif4-Regular.ttf.woff2`, the Regular variant of the Source Serif 4 font in
     /// woff2.
-    crate static REGULAR2: &[u8] = include_bytes!("static/fonts/SourceSerif4-Regular.ttf.woff2");
-
-    /// The file `SourceSerif4-Bold.ttf.woff`, the Bold variant of the Source Serif 4 font.
-    crate static BOLD: &[u8] = include_bytes!("static/fonts/SourceSerif4-Bold.ttf.woff");
+    crate static REGULAR: &[u8] = include_bytes!("static/fonts/SourceSerif4-Regular.ttf.woff2");
 
     /// The file `SourceSerif4-Bold.ttf.woff2`, the Bold variant of the Source Serif 4 font in
     /// woff2.
-    crate static BOLD2: &[u8] = include_bytes!("static/fonts/SourceSerif4-Bold.ttf.woff2");
-
-    /// The file `SourceSerif4-It.ttf.woff`, the Italic variant of the Source Serif 4 font.
-    crate static ITALIC: &[u8] = include_bytes!("static/fonts/SourceSerif4-It.ttf.woff");
+    crate static BOLD: &[u8] = include_bytes!("static/fonts/SourceSerif4-Bold.ttf.woff2");
 
     /// The file `SourceSerif4-It.ttf.woff2`, the Italic variant of the Source Serif 4 font in
     /// woff2.
-    crate static ITALIC2: &[u8] = include_bytes!("static/fonts/SourceSerif4-It.ttf.woff2");
+    crate static ITALIC: &[u8] = include_bytes!("static/fonts/SourceSerif4-It.ttf.woff2");
 
     /// The file `SourceSerif4-LICENSE.txt`, the license text for the Source Serif 4 font.
     crate static LICENSE: &[u8] = include_bytes!("static/fonts/SourceSerif4-LICENSE.md");
@@ -137,27 +122,17 @@ crate mod source_serif_4 {
 
 /// Files related to the Source Code Pro font.
 crate mod source_code_pro {
-    /// The file `SourceCodePro-Regular.ttf.woff`, the Regular variant of the Source Code Pro font.
-    crate static REGULAR: &[u8] = include_bytes!("static/fonts/SourceCodePro-Regular.ttf.woff");
-
     /// The file `SourceCodePro-Regular.ttf.woff2`, the Regular variant of the Source Code Pro font
     /// in woff2.
-    crate static REGULAR2: &[u8] = include_bytes!("static/fonts/SourceCodePro-Regular.ttf.woff2");
-
-    /// The file `SourceCodePro-Semibold.ttf.woff`, the Semibold variant of the Source Code Pro
-    /// font.
-    crate static SEMIBOLD: &[u8] = include_bytes!("static/fonts/SourceCodePro-Semibold.ttf.woff");
+    crate static REGULAR: &[u8] = include_bytes!("static/fonts/SourceCodePro-Regular.ttf.woff2");
 
     /// The file `SourceCodePro-Semibold.ttf.woff2`, the Semibold variant of the Source Code Pro
     /// font in woff2.
-    crate static SEMIBOLD2: &[u8] = include_bytes!("static/fonts/SourceCodePro-Semibold.ttf.woff2");
-
-    /// The file `SourceCodePro-It.ttf.woff`, the Italic variant of the Source Code Pro font.
-    crate static ITALIC: &[u8] = include_bytes!("static/fonts/SourceCodePro-It.ttf.woff");
+    crate static SEMIBOLD: &[u8] = include_bytes!("static/fonts/SourceCodePro-Semibold.ttf.woff2");
 
     /// The file `SourceCodePro-It.ttf.woff2`, the Italic variant of the Source Code Pro font in
     /// woff2.
-    crate static ITALIC2: &[u8] = include_bytes!("static/fonts/SourceCodePro-It.ttf.woff2");
+    crate static ITALIC: &[u8] = include_bytes!("static/fonts/SourceCodePro-It.ttf.woff2");
 
     /// The file `SourceCodePro-LICENSE.txt`, the license text of the Source Code Pro font.
     crate static LICENSE: &[u8] = include_bytes!("static/fonts/SourceCodePro-LICENSE.txt");
@@ -176,19 +151,11 @@ crate mod source_code_pro {
 /// ```sh
 /// pyftsubset NanumBarunGothic.ttf \
 /// --unicodes=U+AC00-D7AF,U+1100-11FF,U+3130-318F,U+A960-A97F,U+D7B0-D7FF \
-/// --output-file=NanumBarunGothic.ttf.woff --flavor=woff
-/// ```
-/// ```sh
-/// pyftsubset NanumBarunGothic.ttf \
-/// --unicodes=U+AC00-D7AF,U+1100-11FF,U+3130-318F,U+A960-A97F,U+D7B0-D7FF \
 /// --output-file=NanumBarunGothic.ttf.woff2 --flavor=woff2
 /// ```
 crate mod nanum_barun_gothic {
-    /// The file `NanumBarunGothic.ttf.woff`, the Regular variant of the Nanum Barun Gothic font.
-    crate static REGULAR: &[u8] = include_bytes!("static/fonts/NanumBarunGothic.ttf.woff");
-
     /// The file `NanumBarunGothic.ttf.woff2`, the Regular variant of the Nanum Barun Gothic font.
-    crate static REGULAR2: &[u8] = include_bytes!("static/fonts/NanumBarunGothic.ttf.woff2");
+    crate static REGULAR: &[u8] = include_bytes!("static/fonts/NanumBarunGothic.ttf.woff2");
 
     /// The file `NanumBarunGothic-LICENSE.txt`, the license text of the Nanum Barun Gothic font.
     crate static LICENSE: &[u8] = include_bytes!("static/fonts/NanumBarunGothic-LICENSE.txt");
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index 0fcfabba4c0..cd196a01847 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -795,6 +795,7 @@ fn main_options(options: config::Options) -> MainResult {
                 let resolver_caches = resolver.borrow_mut().access(|resolver| {
                     collect_intra_doc_links::early_resolve_intra_doc_links(
                         resolver,
+                        sess,
                         krate,
                         externs,
                         document_private,
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index c48f8bd0c7c..84512fab269 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -3,6 +3,7 @@
 //! [RFC 1946]: https://github.com/rust-lang/rfcs/blob/master/text/1946-intra-rustdoc-links.md
 
 use pulldown_cmark::LinkType;
+use rustc_ast::util::comments::may_have_doc_links;
 use rustc_data_structures::{fx::FxHashMap, intern::Interned, stable_set::FxHashSet};
 use rustc_errors::{Applicability, Diagnostic};
 use rustc_hir::def::Namespace::*;
@@ -11,6 +12,7 @@ use rustc_hir::def_id::{DefId, CRATE_DEF_ID};
 use rustc_hir::Mutability;
 use rustc_middle::ty::{DefIdTree, Ty, TyCtxt};
 use rustc_middle::{bug, span_bug, ty};
+use rustc_resolve::ParentScope;
 use rustc_session::lint::Lint;
 use rustc_span::hygiene::MacroKind;
 use rustc_span::symbol::{sym, Ident, Symbol};
@@ -159,7 +161,7 @@ impl TryFrom<ResolveRes> for Res {
 }
 
 /// A link failed to resolve.
-#[derive(Debug)]
+#[derive(Clone, Debug)]
 enum ResolutionFailure<'a> {
     /// This resolved, but with the wrong namespace.
     WrongNamespace {
@@ -199,7 +201,7 @@ enum ResolutionFailure<'a> {
     Dummy,
 }
 
-#[derive(Debug)]
+#[derive(Clone, Debug)]
 enum MalformedGenerics {
     /// This link has unbalanced angle brackets.
     ///
@@ -252,6 +254,7 @@ impl ResolutionFailure<'_> {
     }
 }
 
+#[derive(Clone, Copy)]
 enum AnchorFailure {
     /// User error: `[std#x#y]` is not valid
     MultipleAnchors,
@@ -556,7 +559,17 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
         // 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))
+            .resolver_caches
+            .doc_link_resolutions
+            .get(&(Symbol::intern(path_str), ns, module_id))
+            .copied()
+            .unwrap_or_else(|| {
+                self.cx.enter_resolver(|resolver| {
+                    let parent_scope =
+                        ParentScope::module(resolver.expect_module(module_id), resolver);
+                    resolver.resolve_rustdoc_path(path_str, ns, parent_scope)
+                })
+            })
             .and_then(|res| res.try_into().ok())
             .or_else(|| resolve_primitive(path_str, ns))
             .or_else(|| self.resolve_macro_rules(path_str, ns));
@@ -1040,17 +1053,30 @@ impl<'a, 'tcx> DocVisitor for LinkCollector<'a, 'tcx> {
         // In the presence of re-exports, this is not the same as the module of the item.
         // Rather than merging all documentation into one, resolve it one attribute at a time
         // so we know which module it came from.
-        for (parent_module, doc) in item.attrs.collapsed_doc_value_by_module_level() {
+        for (parent_module, doc) in item.attrs.prepare_to_doc_link_resolution() {
+            if !may_have_doc_links(&doc) {
+                continue;
+            }
             debug!("combined_docs={}", doc);
             // NOTE: if there are links that start in one crate and end in another, this will not resolve them.
             // This is a degenerate case and it's not supported by rustdoc.
             let parent_node = parent_module.or(parent_node);
-            for md_link in markdown_links(&doc) {
+            let mut tmp_links = self
+                .cx
+                .resolver_caches
+                .markdown_links
+                .take()
+                .expect("`markdown_links` are already borrowed");
+            if !tmp_links.contains_key(&doc) {
+                tmp_links.insert(doc.clone(), preprocessed_markdown_links(&doc));
+            }
+            for md_link in &tmp_links[&doc] {
                 let link = self.resolve_link(&item, &doc, parent_node, md_link);
                 if let Some(link) = link {
                     self.cx.cache.intra_doc_links.entry(item.item_id).or_default().push(link);
                 }
             }
+            self.cx.resolver_caches.markdown_links = Some(tmp_links);
         }
 
         if item.is_mod() {
@@ -1066,18 +1092,19 @@ impl<'a, 'tcx> DocVisitor for LinkCollector<'a, 'tcx> {
     }
 }
 
-enum PreprocessingError<'a> {
+enum PreprocessingError {
     Anchor(AnchorFailure),
     Disambiguator(Range<usize>, String),
-    Resolution(ResolutionFailure<'a>, String, Option<Disambiguator>),
+    Resolution(ResolutionFailure<'static>, String, Option<Disambiguator>),
 }
 
-impl From<AnchorFailure> for PreprocessingError<'_> {
+impl From<AnchorFailure> for PreprocessingError {
     fn from(err: AnchorFailure) -> Self {
         Self::Anchor(err)
     }
 }
 
+#[derive(Clone)]
 struct PreprocessingInfo {
     path_str: String,
     disambiguator: Option<Disambiguator>,
@@ -1085,15 +1112,18 @@ struct PreprocessingInfo {
     link_text: String,
 }
 
+// Not a typedef to avoid leaking several private structures from this module.
+crate struct PreprocessedMarkdownLink(Result<PreprocessingInfo, PreprocessingError>, MarkdownLink);
+
 /// Returns:
 /// - `None` if the link should be ignored.
 /// - `Some(Err)` if the link should emit an error
 /// - `Some(Ok)` if the link is valid
 ///
 /// `link_buffer` is needed for lifetime reasons; it will always be overwritten and the contents ignored.
-fn preprocess_link<'a>(
-    ori_link: &'a MarkdownLink,
-) -> Option<Result<PreprocessingInfo, PreprocessingError<'a>>> {
+fn preprocess_link(
+    ori_link: &MarkdownLink,
+) -> Option<Result<PreprocessingInfo, PreprocessingError>> {
     // [] is mostly likely not supposed to be a link
     if ori_link.link.is_empty() {
         return None;
@@ -1172,6 +1202,12 @@ fn preprocess_link<'a>(
     }))
 }
 
+fn preprocessed_markdown_links(s: &str) -> Vec<PreprocessedMarkdownLink> {
+    markdown_links(s, |link| {
+        preprocess_link(&link).map(|pp_link| PreprocessedMarkdownLink(pp_link, link))
+    })
+}
+
 impl LinkCollector<'_, '_> {
     /// This is the entry point for resolving an intra-doc link.
     ///
@@ -1181,8 +1217,9 @@ impl LinkCollector<'_, '_> {
         item: &Item,
         dox: &str,
         parent_node: Option<DefId>,
-        ori_link: MarkdownLink,
+        link: &PreprocessedMarkdownLink,
     ) -> Option<ItemLink> {
+        let PreprocessedMarkdownLink(pp_link, ori_link) = link;
         trace!("considering link '{}'", ori_link.link);
 
         let diag_info = DiagnosticInfo {
@@ -1192,28 +1229,29 @@ impl LinkCollector<'_, '_> {
             link_range: ori_link.range.clone(),
         };
 
-        let PreprocessingInfo { ref path_str, disambiguator, extra_fragment, link_text } =
-            match preprocess_link(&ori_link)? {
-                Ok(x) => x,
-                Err(err) => {
-                    match err {
-                        PreprocessingError::Anchor(err) => anchor_failure(self.cx, diag_info, err),
-                        PreprocessingError::Disambiguator(range, msg) => {
-                            disambiguator_error(self.cx, diag_info, range, &msg)
-                        }
-                        PreprocessingError::Resolution(err, path_str, disambiguator) => {
-                            resolution_failure(
-                                self,
-                                diag_info,
-                                &path_str,
-                                disambiguator,
-                                smallvec![err],
-                            );
-                        }
+        let PreprocessingInfo { path_str, disambiguator, extra_fragment, link_text } = match pp_link
+        {
+            Ok(x) => x,
+            Err(err) => {
+                match err {
+                    PreprocessingError::Anchor(err) => anchor_failure(self.cx, diag_info, *err),
+                    PreprocessingError::Disambiguator(range, msg) => {
+                        disambiguator_error(self.cx, diag_info, range.clone(), msg)
+                    }
+                    PreprocessingError::Resolution(err, path_str, disambiguator) => {
+                        resolution_failure(
+                            self,
+                            diag_info,
+                            path_str,
+                            *disambiguator,
+                            smallvec![err.clone()],
+                        );
                     }
-                    return None;
                 }
-            };
+                return None;
+            }
+        };
+        let disambiguator = *disambiguator;
 
         let inner_docs = item.inner_docs(self.cx.tcx);
 
@@ -1250,7 +1288,7 @@ impl LinkCollector<'_, '_> {
                 module_id,
                 dis: disambiguator,
                 path_str: path_str.to_owned(),
-                extra_fragment,
+                extra_fragment: extra_fragment.clone(),
             },
             diag_info.clone(), // this struct should really be Copy, but Range is not :(
             matches!(ori_link.kind, LinkType::Reference | LinkType::Shortcut),
@@ -1320,8 +1358,8 @@ impl LinkCollector<'_, '_> {
                 }
 
                 Some(ItemLink {
-                    link: ori_link.link,
-                    link_text,
+                    link: ori_link.link.clone(),
+                    link_text: link_text.clone(),
                     did: res.def_id(self.cx.tcx),
                     fragment,
                 })
@@ -1343,7 +1381,12 @@ impl LinkCollector<'_, '_> {
                     &diag_info,
                 )?;
                 let id = clean::register_res(self.cx, rustc_hir::def::Res::Def(kind, id));
-                Some(ItemLink { link: ori_link.link, link_text, did: id, fragment })
+                Some(ItemLink {
+                    link: ori_link.link.clone(),
+                    link_text: link_text.clone(),
+                    did: id,
+                    fragment,
+                })
             }
         }
     }
diff --git a/src/librustdoc/passes/collect_intra_doc_links/early.rs b/src/librustdoc/passes/collect_intra_doc_links/early.rs
index dffceff045d..3858c1cb056 100644
--- a/src/librustdoc/passes/collect_intra_doc_links/early.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links/early.rs
@@ -1,33 +1,42 @@
 use crate::clean::Attributes;
 use crate::core::ResolverCaches;
-use crate::html::markdown::markdown_links;
-use crate::passes::collect_intra_doc_links::preprocess_link;
+use crate::passes::collect_intra_doc_links::preprocessed_markdown_links;
+use crate::passes::collect_intra_doc_links::{Disambiguator, PreprocessedMarkdownLink};
 
 use rustc_ast::visit::{self, AssocCtxt, Visitor};
 use rustc_ast::{self as ast, ItemKind};
 use rustc_ast_lowering::ResolverAstLowering;
-use rustc_hir::def::Namespace::TypeNS;
-use rustc_hir::def::{DefKind, Res};
-use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId, CRATE_DEF_ID};
+use rustc_data_structures::fx::FxHashMap;
+use rustc_hir::def::Namespace::*;
+use rustc_hir::def::{DefKind, Namespace, Res};
+use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, CRATE_DEF_ID};
 use rustc_hir::TraitCandidate;
 use rustc_middle::ty::{DefIdTree, Visibility};
 use rustc_resolve::{ParentScope, Resolver};
 use rustc_session::config::Externs;
-use rustc_span::SyntaxContext;
+use rustc_session::Session;
+use rustc_span::symbol::sym;
+use rustc_span::{Symbol, SyntaxContext};
 
 use std::collections::hash_map::Entry;
 use std::mem;
 
 crate fn early_resolve_intra_doc_links(
     resolver: &mut Resolver<'_>,
+    sess: &Session,
     krate: &ast::Crate,
     externs: Externs,
     document_private_items: bool,
 ) -> ResolverCaches {
+    let parent_scope =
+        ParentScope::module(resolver.expect_module(CRATE_DEF_ID.to_def_id()), resolver);
     let mut link_resolver = EarlyDocLinkResolver {
         resolver,
-        current_mod: CRATE_DEF_ID,
+        sess,
+        parent_scope,
         visited_mods: Default::default(),
+        markdown_links: Default::default(),
+        doc_link_resolutions: Default::default(),
         traits_in_scope: Default::default(),
         all_traits: Default::default(),
         all_trait_impls: Default::default(),
@@ -36,7 +45,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.
-    link_resolver.load_links_in_attrs(&krate.attrs);
+    link_resolver.resolve_doc_links_local(&krate.attrs);
     link_resolver.process_module_children_or_reexports(CRATE_DEF_ID.to_def_id());
     visit::walk_crate(&mut link_resolver, krate);
     link_resolver.process_extern_impls();
@@ -46,10 +55,12 @@ 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) {
-        link_resolver.resolver.resolve_rustdoc_path(extern_name, TypeNS, CRATE_DEF_ID.to_def_id());
+        link_resolver.resolver.resolve_rustdoc_path(extern_name, TypeNS, parent_scope);
     }
 
     ResolverCaches {
+        markdown_links: Some(link_resolver.markdown_links),
+        doc_link_resolutions: link_resolver.doc_link_resolutions,
         traits_in_scope: link_resolver.traits_in_scope,
         all_traits: Some(link_resolver.all_traits),
         all_trait_impls: Some(link_resolver.all_trait_impls),
@@ -57,17 +68,24 @@ crate fn early_resolve_intra_doc_links(
     }
 }
 
+fn doc_attrs<'a>(attrs: impl Iterator<Item = &'a ast::Attribute>) -> Attributes {
+    Attributes::from_ast_iter(attrs.map(|attr| (attr, None)), true)
+}
+
 struct EarlyDocLinkResolver<'r, 'ra> {
     resolver: &'r mut Resolver<'ra>,
-    current_mod: LocalDefId,
+    sess: &'r Session,
+    parent_scope: ParentScope<'ra>,
     visited_mods: DefIdSet,
+    markdown_links: FxHashMap<String, Vec<PreprocessedMarkdownLink>>,
+    doc_link_resolutions: FxHashMap<(Symbol, Namespace, DefId), Option<Res<ast::NodeId>>>,
     traits_in_scope: DefIdMap<Vec<TraitCandidate>>,
     all_traits: Vec<DefId>,
     all_trait_impls: Vec<DefId>,
     document_private_items: bool,
 }
 
-impl EarlyDocLinkResolver<'_, '_> {
+impl<'ra> EarlyDocLinkResolver<'_, 'ra> {
     fn add_traits_in_scope(&mut self, def_id: DefId) {
         // Calls to `traits_in_scope` are expensive, so try to avoid them if only possible.
         // Keys in the `traits_in_scope` cache are always module IDs.
@@ -92,18 +110,11 @@ impl EarlyDocLinkResolver<'_, '_> {
         }
     }
 
-    fn add_traits_in_parent_scope(&mut self, def_id: DefId) {
-        if let Some(module_id) = self.resolver.parent(def_id) {
-            self.add_traits_in_scope(module_id);
-        }
-    }
-
     /// Add traits in scope for links in impls collected by the `collect-intra-doc-links` pass.
     /// That pass filters impls using type-based information, but we don't yet have such
     /// information here, so we just conservatively calculate traits in scope for *all* modules
     /// having impls in them.
     fn process_extern_impls(&mut self) {
-        // FIXME: Need to resolve doc links on all these impl and trait items below.
         // Resolving links in already existing crates may trigger loading of new crates.
         let mut start_cnum = 0;
         loop {
@@ -124,7 +135,7 @@ impl EarlyDocLinkResolver<'_, '_> {
                 // the current crate, and links in their doc comments are not resolved.
                 for &def_id in &all_traits {
                     if self.resolver.cstore().visibility_untracked(def_id) == Visibility::Public {
-                        self.add_traits_in_parent_scope(def_id);
+                        self.resolve_doc_links_extern_impl(def_id, false);
                     }
                 }
                 for &(trait_def_id, impl_def_id, simplified_self_ty) in &all_trait_impls {
@@ -135,17 +146,17 @@ impl EarlyDocLinkResolver<'_, '_> {
                                 == Visibility::Public
                         })
                     {
-                        self.add_traits_in_parent_scope(impl_def_id);
+                        self.resolve_doc_links_extern_impl(impl_def_id, false);
                     }
                 }
                 for (ty_def_id, impl_def_id) in all_inherent_impls {
                     if self.resolver.cstore().visibility_untracked(ty_def_id) == Visibility::Public
                     {
-                        self.add_traits_in_parent_scope(impl_def_id);
+                        self.resolve_doc_links_extern_impl(impl_def_id, true);
                     }
                 }
-                for def_id in all_incoherent_impls {
-                    self.add_traits_in_parent_scope(def_id);
+                for impl_def_id in all_incoherent_impls {
+                    self.resolve_doc_links_extern_impl(impl_def_id, true);
                 }
 
                 self.all_traits.extend(all_traits);
@@ -161,23 +172,100 @@ impl EarlyDocLinkResolver<'_, '_> {
         }
     }
 
-    fn load_links_in_attrs(&mut self, attrs: &[ast::Attribute]) {
-        let module_id = self.current_mod.to_def_id();
+    fn resolve_doc_links_extern_impl(&mut self, def_id: DefId, is_inherent: bool) {
+        self.resolve_doc_links_extern_outer(def_id, def_id);
+        let assoc_item_def_ids = Vec::from_iter(
+            self.resolver.cstore().associated_item_def_ids_untracked(def_id, self.sess),
+        );
+        for assoc_def_id in assoc_item_def_ids {
+            if !is_inherent
+                || self.resolver.cstore().visibility_untracked(assoc_def_id) == Visibility::Public
+            {
+                self.resolve_doc_links_extern_outer(assoc_def_id, def_id);
+            }
+        }
+    }
+
+    fn resolve_doc_links_extern_outer(&mut self, def_id: DefId, scope_id: DefId) {
+        if !self.resolver.cstore().may_have_doc_links_untracked(def_id) {
+            return;
+        }
+        // FIXME: actually resolve links, not just add traits in scope.
+        if let Some(parent_id) = self.resolver.parent(scope_id) {
+            self.add_traits_in_scope(parent_id);
+        }
+    }
+
+    fn resolve_doc_links_extern_inner(&mut self, def_id: DefId) {
+        if !self.resolver.cstore().may_have_doc_links_untracked(def_id) {
+            return;
+        }
+        // FIXME: actually resolve links, not just add traits in scope.
+        self.add_traits_in_scope(def_id);
+    }
+
+    fn resolve_doc_links_local(&mut self, attrs: &[ast::Attribute]) {
+        if !attrs.iter().any(|attr| attr.may_have_doc_links()) {
+            return;
+        }
+        self.resolve_doc_links(doc_attrs(attrs.iter()), self.parent_scope);
+    }
+
+    fn resolve_and_cache(
+        &mut self,
+        path_str: &str,
+        ns: Namespace,
+        parent_scope: &ParentScope<'ra>,
+    ) -> bool {
+        // FIXME: This caching may be incorrect in case of multiple `macro_rules`
+        // items with the same name in the same module.
+        self.doc_link_resolutions
+            .entry((Symbol::intern(path_str), ns, parent_scope.module.def_id()))
+            .or_insert_with_key(|(path, ns, _)| {
+                self.resolver.resolve_rustdoc_path(path.as_str(), *ns, *parent_scope)
+            })
+            .is_some()
+    }
+
+    fn resolve_doc_links(&mut self, attrs: Attributes, parent_scope: ParentScope<'ra>) {
         let mut need_traits_in_scope = false;
-        for (doc_module, doc) in
-            Attributes::from_ast(attrs, None).collapsed_doc_value_by_module_level()
-        {
+        for (doc_module, doc) in attrs.prepare_to_doc_link_resolution() {
             assert_eq!(doc_module, None);
-            for link in markdown_links(&doc.as_str()) {
-                if let Some(Ok(pinfo)) = preprocess_link(&link) {
-                    self.resolver.resolve_rustdoc_path(&pinfo.path_str, TypeNS, module_id);
-                    need_traits_in_scope = true;
+            let mut tmp_links = mem::take(&mut self.markdown_links);
+            let links =
+                tmp_links.entry(doc).or_insert_with_key(|doc| preprocessed_markdown_links(doc));
+            for PreprocessedMarkdownLink(pp_link, _) in links {
+                if let Ok(pinfo) = pp_link {
+                    // The logic here is a conservative approximation for path resolution in
+                    // `resolve_with_disambiguator`.
+                    if let Some(ns) = pinfo.disambiguator.map(Disambiguator::ns) {
+                        if self.resolve_and_cache(&pinfo.path_str, ns, &parent_scope) {
+                            continue;
+                        }
+                    }
+
+                    // Resolve all namespaces due to no disambiguator or for diagnostics.
+                    let mut any_resolved = false;
+                    let mut need_assoc = false;
+                    for ns in [TypeNS, ValueNS, MacroNS] {
+                        if self.resolve_and_cache(&pinfo.path_str, ns, &parent_scope) {
+                            any_resolved = true;
+                        } else if ns != MacroNS {
+                            need_assoc = true;
+                        }
+                    }
+
+                    // FIXME: Resolve all prefixes for type-relative resolution or for diagnostics.
+                    if (need_assoc || !any_resolved) && pinfo.path_str.contains("::") {
+                        need_traits_in_scope = true;
+                    }
                 }
             }
+            self.markdown_links = tmp_links;
         }
 
         if need_traits_in_scope {
-            self.add_traits_in_scope(module_id);
+            self.add_traits_in_scope(parent_scope.module.def_id());
         }
     }
 
@@ -197,15 +285,13 @@ impl EarlyDocLinkResolver<'_, '_> {
                     && module_id.is_local()
             {
                 if let Some(def_id) = child.res.opt_def_id() && !def_id.is_local() {
-                    // FIXME: Need to resolve doc links on all these extern items
-                    // reached through reexports.
                     let scope_id = match child.res {
                         Res::Def(DefKind::Variant, ..) => self.resolver.parent(def_id).unwrap(),
                         _ => def_id,
                     };
-                    self.add_traits_in_parent_scope(scope_id); // Outer attribute scope
+                    self.resolve_doc_links_extern_outer(def_id, scope_id); // Outer attribute scope
                     if let Res::Def(DefKind::Mod, ..) = child.res {
-                        self.add_traits_in_scope(def_id); // Inner attribute scope
+                        self.resolve_doc_links_extern_inner(def_id); // Inner attribute scope
                     }
                     // Traits are processed in `add_extern_traits_in_scope`.
                     if let Res::Def(DefKind::Mod | DefKind::Enum, ..) = child.res {
@@ -219,21 +305,35 @@ impl EarlyDocLinkResolver<'_, '_> {
 
 impl Visitor<'_> for EarlyDocLinkResolver<'_, '_> {
     fn visit_item(&mut self, item: &ast::Item) {
-        self.load_links_in_attrs(&item.attrs); // Outer attribute scope
+        self.resolve_doc_links_local(&item.attrs); // Outer attribute scope
         if let ItemKind::Mod(..) = item.kind {
-            let old_mod = mem::replace(&mut self.current_mod, self.resolver.local_def_id(item.id));
-            self.load_links_in_attrs(&item.attrs); // Inner attribute scope
-            self.process_module_children_or_reexports(self.current_mod.to_def_id());
+            let module_def_id = self.resolver.local_def_id(item.id).to_def_id();
+            let module = self.resolver.expect_module(module_def_id);
+            let old_module = mem::replace(&mut self.parent_scope.module, module);
+            let old_macro_rules = self.parent_scope.macro_rules;
+            self.resolve_doc_links_local(&item.attrs); // Inner attribute scope
+            self.process_module_children_or_reexports(module_def_id);
             visit::walk_item(self, item);
-            self.current_mod = old_mod;
+            if item
+                .attrs
+                .iter()
+                .all(|attr| !attr.has_name(sym::macro_use) && !attr.has_name(sym::macro_escape))
+            {
+                self.parent_scope.macro_rules = old_macro_rules;
+            }
+            self.parent_scope.module = old_module;
         } else {
-            match item.kind {
+            match &item.kind {
                 ItemKind::Trait(..) => {
                     self.all_traits.push(self.resolver.local_def_id(item.id).to_def_id());
                 }
                 ItemKind::Impl(box ast::Impl { of_trait: Some(..), .. }) => {
                     self.all_trait_impls.push(self.resolver.local_def_id(item.id).to_def_id());
                 }
+                ItemKind::MacroDef(macro_def) if macro_def.macro_rules => {
+                    self.parent_scope.macro_rules =
+                        self.resolver.macro_rules_scope(self.resolver.local_def_id(item.id));
+                }
                 _ => {}
             }
             visit::walk_item(self, item);
@@ -241,25 +341,31 @@ impl Visitor<'_> for EarlyDocLinkResolver<'_, '_> {
     }
 
     fn visit_assoc_item(&mut self, item: &ast::AssocItem, ctxt: AssocCtxt) {
-        self.load_links_in_attrs(&item.attrs);
+        self.resolve_doc_links_local(&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);
+        self.resolve_doc_links_local(&item.attrs);
         visit::walk_foreign_item(self, item)
     }
 
     fn visit_variant(&mut self, v: &ast::Variant) {
-        self.load_links_in_attrs(&v.attrs);
+        self.resolve_doc_links_local(&v.attrs);
         visit::walk_variant(self, v)
     }
 
     fn visit_field_def(&mut self, field: &ast::FieldDef) {
-        self.load_links_in_attrs(&field.attrs);
+        self.resolve_doc_links_local(&field.attrs);
         visit::walk_field_def(self, field)
     }
 
+    fn visit_block(&mut self, block: &ast::Block) {
+        let old_macro_rules = self.parent_scope.macro_rules;
+        visit::walk_block(self, block);
+        self.parent_scope.macro_rules = old_macro_rules;
+    }
+
     // NOTE: if doc-comments are ever allowed on other nodes (e.g. function parameters),
     // then this will have to implement other visitor methods too.
 }
diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs
index 65459913eea..9644e3d15fd 100644
--- a/src/librustdoc/passes/collect_trait_impls.rs
+++ b/src/librustdoc/passes/collect_trait_impls.rs
@@ -33,26 +33,60 @@ crate fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> Crate
         coll.items
     };
 
-    let mut new_items = Vec::new();
+    let mut new_items_external = Vec::new();
+    let mut new_items_local = Vec::new();
 
     // External trait impls.
     cx.with_all_trait_impls(|cx, all_trait_impls| {
         let _prof_timer = cx.tcx.sess.prof.generic_activity("build_extern_trait_impls");
         for &impl_def_id in all_trait_impls.iter().skip_while(|def_id| def_id.is_local()) {
-            inline::build_impl(cx, None, impl_def_id, None, &mut new_items);
+            inline::build_impl(cx, None, impl_def_id, None, &mut new_items_external);
+        }
+    });
+
+    // Local trait impls.
+    cx.with_all_trait_impls(|cx, all_trait_impls| {
+        let _prof_timer = cx.tcx.sess.prof.generic_activity("build_local_trait_impls");
+        let mut attr_buf = Vec::new();
+        for &impl_def_id in all_trait_impls.iter().take_while(|def_id| def_id.is_local()) {
+            let mut parent = cx.tcx.parent(impl_def_id);
+            while let Some(did) = parent {
+                attr_buf.extend(
+                    cx.tcx
+                        .get_attrs(did)
+                        .iter()
+                        .filter(|attr| attr.has_name(sym::doc))
+                        .filter(|attr| {
+                            if let Some([attr]) = attr.meta_item_list().as_deref() {
+                                attr.has_name(sym::cfg)
+                            } else {
+                                false
+                            }
+                        })
+                        .cloned(),
+                );
+                parent = cx.tcx.parent(did);
+            }
+            inline::build_impl(cx, None, impl_def_id, Some(&attr_buf), &mut new_items_local);
+            attr_buf.clear();
         }
     });
 
-    // Also try to inline primitive impls from other crates.
     cx.tcx.sess.prof.generic_activity("build_primitive_trait_impls").run(|| {
         for def_id in PrimitiveType::all_impls(cx.tcx) {
+            // Try to inline primitive impls from other crates.
             if !def_id.is_local() {
-                inline::build_impl(cx, None, def_id, None, &mut new_items);
-
-                // FIXME(eddyb) is this `doc(hidden)` check needed?
-                if !cx.tcx.is_doc_hidden(def_id) {
+                inline::build_impl(cx, None, def_id, None, &mut new_items_external);
+            }
+        }
+        for (prim, did) in PrimitiveType::primitive_locations(cx.tcx) {
+            // Do not calculate blanket impl list for docs that are not going to be rendered.
+            // While the `impl` blocks themselves are only in `libcore`, the module with `doc`
+            // attached is directly included in `libstd` as well.
+            if did.is_local() {
+                for def_id in prim.impls(cx.tcx) {
                     let impls = get_auto_trait_and_blanket_impls(cx, def_id);
-                    new_items.extend(impls.filter(|i| cx.inlined.insert(i.item_id)));
+                    new_items_external.extend(impls.filter(|i| cx.inlined.insert(i.item_id)));
                 }
             }
         }
@@ -66,6 +100,7 @@ crate fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> Crate
         cx: &DocContext<'_>,
         map: &FxHashMap<DefId, &Type>,
         cleaner: &mut BadImplStripper<'_>,
+        targets: &mut FxHashSet<DefId>,
         type_did: DefId,
     ) {
         if let Some(target) = map.get(&type_did) {
@@ -74,18 +109,18 @@ crate fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> Crate
                 cleaner.prims.insert(target_prim);
             } else if let Some(target_did) = target.def_id(&cx.cache) {
                 // `impl Deref<Target = S> for S`
-                if target_did == type_did {
+                if !targets.insert(target_did) {
                     // Avoid infinite cycles
                     return;
                 }
                 cleaner.items.insert(target_did.into());
-                add_deref_target(cx, map, cleaner, target_did);
+                add_deref_target(cx, map, cleaner, targets, target_did);
             }
         }
     }
 
     // scan through included items ahead of time to splice in Deref targets to the "valid" sets
-    for it in &new_items {
+    for it in new_items_external.iter().chain(new_items_local.iter()) {
         if let ImplItem(Impl { ref for_, ref trait_, ref items, .. }) = *it.kind {
             if trait_.as_ref().map(|t| t.def_id()) == cx.tcx.lang_items().deref_trait()
                 && cleaner.keep_impl(for_, true)
@@ -109,7 +144,15 @@ crate fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> Crate
                         // `Deref` target type and the impl for type positions, this map of types is keyed by
                         // `DefId` and for convenience uses a special cleaner that accepts `DefId`s directly.
                         if cleaner.keep_impl_with_def_id(for_did.into()) {
-                            add_deref_target(cx, &type_did_to_deref_target, &mut cleaner, for_did);
+                            let mut targets = FxHashSet::default();
+                            targets.insert(for_did);
+                            add_deref_target(
+                                cx,
+                                &type_did_to_deref_target,
+                                &mut cleaner,
+                                &mut targets,
+                                for_did,
+                            );
                         }
                     }
                 }
@@ -117,7 +160,8 @@ crate fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> Crate
         }
     }
 
-    new_items.retain(|it| {
+    // Filter out external items that are not needed
+    new_items_external.retain(|it| {
         if let ImplItem(Impl { ref for_, ref trait_, ref kind, .. }) = *it.kind {
             cleaner.keep_impl(
                 for_,
@@ -129,37 +173,10 @@ crate fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> Crate
         }
     });
 
-    // Local trait impls.
-    cx.with_all_trait_impls(|cx, all_trait_impls| {
-        let _prof_timer = cx.tcx.sess.prof.generic_activity("build_local_trait_impls");
-        let mut attr_buf = Vec::new();
-        for &impl_def_id in all_trait_impls.iter().take_while(|def_id| def_id.is_local()) {
-            let mut parent = cx.tcx.parent(impl_def_id);
-            while let Some(did) = parent {
-                attr_buf.extend(
-                    cx.tcx
-                        .get_attrs(did)
-                        .iter()
-                        .filter(|attr| attr.has_name(sym::doc))
-                        .filter(|attr| {
-                            if let Some([attr]) = attr.meta_item_list().as_deref() {
-                                attr.has_name(sym::cfg)
-                            } else {
-                                false
-                            }
-                        })
-                        .cloned(),
-                );
-                parent = cx.tcx.parent(did);
-            }
-            inline::build_impl(cx, None, impl_def_id, Some(&attr_buf), &mut new_items);
-            attr_buf.clear();
-        }
-    });
-
     if let ModuleItem(Module { items, .. }) = &mut *krate.module.kind {
         items.extend(synth_impls);
-        items.extend(new_items);
+        items.extend(new_items_external);
+        items.extend(new_items_local);
     } else {
         panic!("collect-trait-impls can't run");
     };
diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs
index 5db2d0747c8..97a436c7500 100644
--- a/src/librustdoc/passes/mod.rs
+++ b/src/librustdoc/passes/mod.rs
@@ -24,9 +24,6 @@ crate use self::strip_private::STRIP_PRIVATE;
 mod strip_priv_imports;
 crate use self::strip_priv_imports::STRIP_PRIV_IMPORTS;
 
-mod unindent_comments;
-crate use self::unindent_comments::UNINDENT_COMMENTS;
-
 mod propagate_doc_cfg;
 crate use self::propagate_doc_cfg::PROPAGATE_DOC_CFG;
 
@@ -81,7 +78,6 @@ crate enum Condition {
 crate const PASSES: &[Pass] = &[
     CHECK_DOC_TEST_VISIBILITY,
     STRIP_HIDDEN,
-    UNINDENT_COMMENTS,
     STRIP_PRIVATE,
     STRIP_PRIV_IMPORTS,
     PROPAGATE_DOC_CFG,
@@ -96,7 +92,6 @@ crate const PASSES: &[Pass] = &[
 /// The list of passes run by default.
 crate const DEFAULT_PASSES: &[ConditionalPass] = &[
     ConditionalPass::always(COLLECT_TRAIT_IMPLS),
-    ConditionalPass::always(UNINDENT_COMMENTS),
     ConditionalPass::always(CHECK_DOC_TEST_VISIBILITY),
     ConditionalPass::new(STRIP_HIDDEN, WhenNotDocumentHidden),
     ConditionalPass::new(STRIP_PRIVATE, WhenNotDocumentPrivate),
diff --git a/src/librustdoc/passes/unindent_comments.rs b/src/librustdoc/passes/unindent_comments.rs
deleted file mode 100644
index 0f604157291..00000000000
--- a/src/librustdoc/passes/unindent_comments.rs
+++ /dev/null
@@ -1,116 +0,0 @@
-//! Removes excess indentation on comments in order for the Markdown
-//! to be parsed correctly. This is necessary because the convention for
-//! writing documentation is to provide a space between the /// or //! marker
-//! and the doc text, but Markdown is whitespace-sensitive. For example,
-//! a block of text with four-space indentation is parsed as a code block,
-//! so if we didn't unindent comments, these list items
-//!
-//! /// A list:
-//! ///
-//! ///    - Foo
-//! ///    - Bar
-//!
-//! would be parsed as if they were in a code block, which is likely not what the user intended.
-use std::cmp;
-
-use rustc_span::symbol::kw;
-
-use crate::clean::{self, DocFragment, DocFragmentKind, Item};
-use crate::core::DocContext;
-use crate::fold::{self, DocFolder};
-use crate::passes::Pass;
-
-#[cfg(test)]
-mod tests;
-
-crate const UNINDENT_COMMENTS: Pass = Pass {
-    name: "unindent-comments",
-    run: unindent_comments,
-    description: "removes excess indentation on comments in order for markdown to like it",
-};
-
-crate fn unindent_comments(krate: clean::Crate, _: &mut DocContext<'_>) -> clean::Crate {
-    CommentCleaner.fold_crate(krate)
-}
-
-struct CommentCleaner;
-
-impl fold::DocFolder for CommentCleaner {
-    fn fold_item(&mut self, mut i: Item) -> Option<Item> {
-        i.attrs.unindent_doc_comments();
-        Some(self.fold_item_recur(i))
-    }
-}
-
-impl clean::Attributes {
-    crate fn unindent_doc_comments(&mut self) {
-        unindent_fragments(&mut self.doc_strings);
-    }
-}
-
-fn unindent_fragments(docs: &mut Vec<DocFragment>) {
-    // `add` is used in case the most common sugared doc syntax is used ("/// "). The other
-    // fragments kind's lines are never starting with a whitespace unless they are using some
-    // markdown formatting requiring it. Therefore, if the doc block have a mix between the two,
-    // we need to take into account the fact that the minimum indent minus one (to take this
-    // whitespace into account).
-    //
-    // For example:
-    //
-    // /// hello!
-    // #[doc = "another"]
-    //
-    // In this case, you want "hello! another" and not "hello!  another".
-    let add = if docs.windows(2).any(|arr| arr[0].kind != arr[1].kind)
-        && docs.iter().any(|d| d.kind == DocFragmentKind::SugaredDoc)
-    {
-        // In case we have a mix of sugared doc comments and "raw" ones, we want the sugared one to
-        // "decide" how much the minimum indent will be.
-        1
-    } else {
-        0
-    };
-
-    // `min_indent` is used to know how much whitespaces from the start of each lines must be
-    // removed. Example:
-    //
-    // ///     hello!
-    // #[doc = "another"]
-    //
-    // In here, the `min_indent` is 1 (because non-sugared fragment are always counted with minimum
-    // 1 whitespace), meaning that "hello!" will be considered a codeblock because it starts with 4
-    // (5 - 1) whitespaces.
-    let Some(min_indent) = docs
-        .iter()
-        .map(|fragment| {
-            fragment.doc.as_str().lines().fold(usize::MAX, |min_indent, line| {
-                if line.chars().all(|c| c.is_whitespace()) {
-                    min_indent
-                } else {
-                    // Compare against either space or tab, ignoring whether they are
-                    // mixed or not.
-                    let whitespace = line.chars().take_while(|c| *c == ' ' || *c == '\t').count();
-                    cmp::min(min_indent, whitespace)
-                        + if fragment.kind == DocFragmentKind::SugaredDoc { 0 } else { add }
-                }
-            })
-        })
-        .min()
-    else {
-        return;
-    };
-
-    for fragment in docs {
-        if fragment.doc == kw::Empty {
-            continue;
-        }
-
-        let min_indent = if fragment.kind != DocFragmentKind::SugaredDoc && min_indent > 0 {
-            min_indent - add
-        } else {
-            min_indent
-        };
-
-        fragment.indent = min_indent;
-    }
-}
diff --git a/src/llvm-project b/src/llvm-project
-Subproject fd336816c3a6d228dcef22171c43140f6fa7656
+Subproject fc10370ef7d91babf512c10505f8f2176bc8519
diff --git a/src/test/assembly/nvptx-kernel-abi/nvptx-kernel-args-abi-v7.rs b/src/test/assembly/nvptx-kernel-abi/nvptx-kernel-args-abi-v7.rs
new file mode 100644
index 00000000000..5bf44f949fd
--- /dev/null
+++ b/src/test/assembly/nvptx-kernel-abi/nvptx-kernel-args-abi-v7.rs
@@ -0,0 +1,254 @@
+// assembly-output: ptx-linker
+// compile-flags: --crate-type cdylib -C target-cpu=sm_86
+// only-nvptx64
+// ignore-nvptx64
+
+// The following ABI tests are made with nvcc 11.6 does.
+//
+// The PTX ABI stability is tied to major versions of the PTX ISA
+// These tests assume major version 7
+//
+//
+// The following correspondence between types are assumed:
+// u<N> - uint<N>_t
+// i<N> - int<N>_t
+// [T, N] - std::array<T, N>
+// &T - T const*
+// &mut T - T*
+
+// CHECK: .version 7
+
+#![feature(abi_ptx, lang_items, no_core)]
+#![no_core]
+
+#[lang = "sized"]
+trait Sized {}
+#[lang = "copy"]
+trait Copy {}
+
+#[repr(C)]
+pub struct SingleU8 {
+    f: u8,
+}
+
+#[repr(C)]
+pub struct DoubleU8 {
+    f: u8,
+    g: u8,
+}
+
+#[repr(C)]
+pub struct TripleU8 {
+    f: u8,
+    g: u8,
+    h: u8,
+}
+
+#[repr(C)]
+pub struct TripleU16 {
+    f: u16,
+    g: u16,
+    h: u16,
+}
+#[repr(C)]
+pub struct TripleU32 {
+    f: u32,
+    g: u32,
+    h: u32,
+}
+#[repr(C)]
+pub struct TripleU64 {
+    f: u64,
+    g: u64,
+    h: u64,
+}
+
+#[repr(C)]
+pub struct DoubleFloat {
+    f: f32,
+    g: f32,
+}
+
+#[repr(C)]
+pub struct TripleFloat {
+    f: f32,
+    g: f32,
+    h: f32,
+}
+
+#[repr(C)]
+pub struct TripleDouble {
+    f: f64,
+    g: f64,
+    h: f64,
+}
+
+#[repr(C)]
+pub struct ManyIntegers {
+    f: u8,
+    g: u16,
+    h: u32,
+    i: u64,
+}
+
+#[repr(C)]
+pub struct ManyNumerics {
+    f: u8,
+    g: u16,
+    h: u32,
+    i: u64,
+    j: f32,
+    k: f64,
+}
+
+// CHECK: .visible .entry f_u8_arg(
+// CHECK: .param .u8 f_u8_arg_param_0
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_u8_arg(_a: u8) {}
+
+// CHECK: .visible .entry f_u16_arg(
+// CHECK: .param .u16 f_u16_arg_param_0
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_u16_arg(_a: u16) {}
+
+// CHECK: .visible .entry f_u32_arg(
+// CHECK: .param .u32 f_u32_arg_param_0
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_u32_arg(_a: u32) {}
+
+// CHECK: .visible .entry f_u64_arg(
+// CHECK: .param .u64 f_u64_arg_param_0
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_u64_arg(_a: u64) {}
+
+// CHECK: .visible .entry f_u128_arg(
+// CHECK: .param .align 16 .b8 f_u128_arg_param_0[16]
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_u128_arg(_a: u128) {}
+
+// CHECK: .visible .entry f_i8_arg(
+// CHECK: .param .u8 f_i8_arg_param_0
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_i8_arg(_a: i8) {}
+
+// CHECK: .visible .entry f_i16_arg(
+// CHECK: .param .u16 f_i16_arg_param_0
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_i16_arg(_a: i16) {}
+
+// CHECK: .visible .entry f_i32_arg(
+// CHECK: .param .u32 f_i32_arg_param_0
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_i32_arg(_a: i32) {}
+
+// CHECK: .visible .entry f_i64_arg(
+// CHECK: .param .u64 f_i64_arg_param_0
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_i64_arg(_a: i64) {}
+
+// CHECK: .visible .entry f_i128_arg(
+// CHECK: .param .align 16 .b8 f_i128_arg_param_0[16]
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_i128_arg(_a: i128) {}
+
+// CHECK: .visible .entry f_f32_arg(
+// CHECK: .param .f32 f_f32_arg_param_0
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_f32_arg(_a: f32) {}
+
+// CHECK: .visible .entry f_f64_arg(
+// CHECK: .param .f64 f_f64_arg_param_0
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_f64_arg(_a: f64) {}
+
+// CHECK: .visible .entry f_single_u8_arg(
+// CHECK: .param .align 1 .b8 f_single_u8_arg_param_0[1]
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_single_u8_arg(_a: SingleU8) {}
+
+// CHECK: .visible .entry f_double_u8_arg(
+// CHECK: .param .align 1 .b8 f_double_u8_arg_param_0[2]
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_double_u8_arg(_a: DoubleU8) {}
+
+// CHECK: .visible .entry f_triple_u8_arg(
+// CHECK: .param .align 1 .b8 f_triple_u8_arg_param_0[3]
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_triple_u8_arg(_a: TripleU8) {}
+
+// CHECK: .visible .entry f_triple_u16_arg(
+// CHECK: .param .align 2 .b8 f_triple_u16_arg_param_0[6]
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_triple_u16_arg(_a: TripleU16) {}
+
+// CHECK: .visible .entry f_triple_u32_arg(
+// CHECK: .param .align 4 .b8 f_triple_u32_arg_param_0[12]
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_triple_u32_arg(_a: TripleU32) {}
+
+// CHECK: .visible .entry f_triple_u64_arg(
+// CHECK: .param .align 8 .b8 f_triple_u64_arg_param_0[24]
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_triple_u64_arg(_a: TripleU64) {}
+
+// CHECK: .visible .entry f_many_integers_arg(
+// CHECK: .param .align 8 .b8 f_many_integers_arg_param_0[16]
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_many_integers_arg(_a: ManyIntegers) {}
+
+// CHECK: .visible .entry f_double_float_arg(
+// CHECK: .param .align 4 .b8 f_double_float_arg_param_0[8]
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_double_float_arg(_a: DoubleFloat) {}
+
+// CHECK: .visible .entry f_triple_float_arg(
+// CHECK: .param .align 4 .b8 f_triple_float_arg_param_0[12]
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_triple_float_arg(_a: TripleFloat) {}
+
+// CHECK: .visible .entry f_triple_double_arg(
+// CHECK: .param .align 8 .b8 f_triple_double_arg_param_0[24]
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_triple_double_arg(_a: TripleDouble) {}
+
+// CHECK: .visible .entry f_many_numerics_arg(
+// CHECK: .param .align 8 .b8 f_many_numerics_arg_param_0[32]
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_many_numerics_arg(_a: ManyNumerics) {}
+
+// CHECK: .visible .entry f_byte_array_arg(
+// CHECK: .param .align 1 .b8 f_byte_array_arg_param_0[5]
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_byte_array_arg(_a: [u8; 5]) {}
+
+// CHECK: .visible .entry f_float_array_arg(
+// CHECK: .param .align 4 .b8 f_float_array_arg_param_0[20]
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_float_array_arg(_a: [f32; 5]) {}
+
+// CHECK: .visible .entry f_u128_array_arg(
+// CHECK: .param .align 16 .b8 f_u128_array_arg_param_0[80]
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_u128_array_arg(_a: [u128; 5]) {}
+
+// CHECK: .visible .entry f_u32_slice_arg(
+// CHECK: .param .u64 f_u32_slice_arg_param_0
+// CHECK: .param .u64 f_u32_slice_arg_param_1
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_u32_slice_arg(_a: &[u32]) {}
+
+// CHECK: .visible .entry f_tuple_u8_u8_arg(
+// CHECK: .param .align 1 .b8 f_tuple_u8_u8_arg_param_0[2]
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_tuple_u8_u8_arg(_a: (u8, u8)) {}
+
+// CHECK: .visible .entry f_tuple_u32_u32_arg(
+// CHECK: .param .align 4 .b8 f_tuple_u32_u32_arg_param_0[8]
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_tuple_u32_u32_arg(_a: (u32, u32)) {}
+
+
+// CHECK: .visible .entry f_tuple_u8_u8_u32_arg(
+// CHECK: .param .align 4 .b8 f_tuple_u8_u8_u32_arg_param_0[8]
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_tuple_u8_u8_u32_arg(_a: (u8, u8, u32)) {}
diff --git a/src/test/codegen/asm-clobber_abi.rs b/src/test/codegen/asm-clobber_abi.rs
index 69e35270266..a87152e0321 100644
--- a/src/test/codegen/asm-clobber_abi.rs
+++ b/src/test/codegen/asm-clobber_abi.rs
@@ -6,21 +6,21 @@
 use std::arch::asm;
 
 // CHECK-LABEL: @clobber_sysv64
-// CHECK: ={ax},={cx},={dx},={si},={di},={r8},={r9},={r10},={r11},={xmm0},={xmm1},={xmm2},={xmm3},={xmm4},={xmm5},={xmm6},={xmm7},={xmm8},={xmm9},={xmm10},={xmm11},={xmm12},={xmm13},={xmm14},={xmm15},~{xmm16},~{xmm17},~{xmm18},~{xmm19},~{xmm20},~{xmm21},~{xmm22},~{xmm23},~{xmm24},~{xmm25},~{xmm26},~{xmm27},~{xmm28},~{xmm29},~{xmm30},~{xmm31},~{k1},~{k2},~{k3},~{k4},~{k5},~{k6},~{k7},~{st},~{st(1)},~{st(2)},~{st(3)},~{st(4)},~{st(5)},~{st(6)},~{st(7)},~{dirflag},~{fpsr},~{flags},~{memory}
+// CHECK: ={ax},={cx},={dx},={si},={di},={r8},={r9},={r10},={r11},={xmm0},={xmm1},={xmm2},={xmm3},={xmm4},={xmm5},={xmm6},={xmm7},={xmm8},={xmm9},={xmm10},={xmm11},={xmm12},={xmm13},={xmm14},={xmm15},~{xmm16},~{xmm17},~{xmm18},~{xmm19},~{xmm20},~{xmm21},~{xmm22},~{xmm23},~{xmm24},~{xmm25},~{xmm26},~{xmm27},~{xmm28},~{xmm29},~{xmm30},~{xmm31},~{k0},~{k1},~{k2},~{k3},~{k4},~{k5},~{k6},~{k7},~{st},~{st(1)},~{st(2)},~{st(3)},~{st(4)},~{st(5)},~{st(6)},~{st(7)},~{dirflag},~{fpsr},~{flags},~{memory}
 #[no_mangle]
 pub unsafe fn clobber_sysv64() {
     asm!("", clobber_abi("sysv64"));
 }
 
 // CHECK-LABEL: @clobber_win64
-// CHECK: ={ax},={cx},={dx},={r8},={r9},={r10},={r11},={xmm0},={xmm1},={xmm2},={xmm3},={xmm4},={xmm5},={xmm6},={xmm7},={xmm8},={xmm9},={xmm10},={xmm11},={xmm12},={xmm13},={xmm14},={xmm15},~{xmm16},~{xmm17},~{xmm18},~{xmm19},~{xmm20},~{xmm21},~{xmm22},~{xmm23},~{xmm24},~{xmm25},~{xmm26},~{xmm27},~{xmm28},~{xmm29},~{xmm30},~{xmm31},~{k1},~{k2},~{k3},~{k4},~{k5},~{k6},~{k7},~{st},~{st(1)},~{st(2)},~{st(3)},~{st(4)},~{st(5)},~{st(6)},~{st(7)},~{dirflag},~{fpsr},~{flags},~{memory}
+// CHECK: ={ax},={cx},={dx},={r8},={r9},={r10},={r11},={xmm0},={xmm1},={xmm2},={xmm3},={xmm4},={xmm5},={xmm6},={xmm7},={xmm8},={xmm9},={xmm10},={xmm11},={xmm12},={xmm13},={xmm14},={xmm15},~{xmm16},~{xmm17},~{xmm18},~{xmm19},~{xmm20},~{xmm21},~{xmm22},~{xmm23},~{xmm24},~{xmm25},~{xmm26},~{xmm27},~{xmm28},~{xmm29},~{xmm30},~{xmm31},~{k0},~{k1},~{k2},~{k3},~{k4},~{k5},~{k6},~{k7},~{st},~{st(1)},~{st(2)},~{st(3)},~{st(4)},~{st(5)},~{st(6)},~{st(7)},~{dirflag},~{fpsr},~{flags},~{memory}
 #[no_mangle]
 pub unsafe fn clobber_win64() {
     asm!("", clobber_abi("win64"));
 }
 
 // CHECK-LABEL: @clobber_sysv64
-// CHECK: =&{dx},={ax},={cx},={si},={di},={r8},={r9},={r10},={r11},={xmm0},={xmm1},={xmm2},={xmm3},={xmm4},={xmm5},={xmm6},={xmm7},={xmm8},={xmm9},={xmm10},={xmm11},={xmm12},={xmm13},={xmm14},={xmm15},~{xmm16},~{xmm17},~{xmm18},~{xmm19},~{xmm20},~{xmm21},~{xmm22},~{xmm23},~{xmm24},~{xmm25},~{xmm26},~{xmm27},~{xmm28},~{xmm29},~{xmm30},~{xmm31},~{k1},~{k2},~{k3},~{k4},~{k5},~{k6},~{k7},~{st},~{st(1)},~{st(2)},~{st(3)},~{st(4)},~{st(5)},~{st(6)},~{st(7)},~{dirflag},~{fpsr},~{flags},~{memory}
+// CHECK: =&{dx},={ax},={cx},={si},={di},={r8},={r9},={r10},={r11},={xmm0},={xmm1},={xmm2},={xmm3},={xmm4},={xmm5},={xmm6},={xmm7},={xmm8},={xmm9},={xmm10},={xmm11},={xmm12},={xmm13},={xmm14},={xmm15},~{xmm16},~{xmm17},~{xmm18},~{xmm19},~{xmm20},~{xmm21},~{xmm22},~{xmm23},~{xmm24},~{xmm25},~{xmm26},~{xmm27},~{xmm28},~{xmm29},~{xmm30},~{xmm31},~{k0},~{k1},~{k2},~{k3},~{k4},~{k5},~{k6},~{k7},~{st},~{st(1)},~{st(2)},~{st(3)},~{st(4)},~{st(5)},~{st(6)},~{st(7)},~{dirflag},~{fpsr},~{flags},~{memory}
 #[no_mangle]
 pub unsafe fn clobber_sysv64_edx() {
     let foo: i32;
@@ -28,7 +28,7 @@ pub unsafe fn clobber_sysv64_edx() {
 }
 
 // CHECK-LABEL: @clobber_win64
-// CHECK: =&{dx},={ax},={cx},={r8},={r9},={r10},={r11},={xmm0},={xmm1},={xmm2},={xmm3},={xmm4},={xmm5},={xmm6},={xmm7},={xmm8},={xmm9},={xmm10},={xmm11},={xmm12},={xmm13},={xmm14},={xmm15},~{xmm16},~{xmm17},~{xmm18},~{xmm19},~{xmm20},~{xmm21},~{xmm22},~{xmm23},~{xmm24},~{xmm25},~{xmm26},~{xmm27},~{xmm28},~{xmm29},~{xmm30},~{xmm31},~{k1},~{k2},~{k3},~{k4},~{k5},~{k6},~{k7},~{st},~{st(1)},~{st(2)},~{st(3)},~{st(4)},~{st(5)},~{st(6)},~{st(7)},~{dirflag},~{fpsr},~{flags},~{memory}
+// CHECK: =&{dx},={ax},={cx},={r8},={r9},={r10},={r11},={xmm0},={xmm1},={xmm2},={xmm3},={xmm4},={xmm5},={xmm6},={xmm7},={xmm8},={xmm9},={xmm10},={xmm11},={xmm12},={xmm13},={xmm14},={xmm15},~{xmm16},~{xmm17},~{xmm18},~{xmm19},~{xmm20},~{xmm21},~{xmm22},~{xmm23},~{xmm24},~{xmm25},~{xmm26},~{xmm27},~{xmm28},~{xmm29},~{xmm30},~{xmm31},~{k0},~{k1},~{k2},~{k3},~{k4},~{k5},~{k6},~{k7},~{st},~{st(1)},~{st(2)},~{st(3)},~{st(4)},~{st(5)},~{st(6)},~{st(7)},~{dirflag},~{fpsr},~{flags},~{memory}
 #[no_mangle]
 pub unsafe fn clobber_win64_edx() {
     let foo: i32;
diff --git a/src/test/codegen/panic-in-drop-abort.rs b/src/test/codegen/panic-in-drop-abort.rs
index 39f73c4e396..7a84484c419 100644
--- a/src/test/codegen/panic-in-drop-abort.rs
+++ b/src/test/codegen/panic-in-drop-abort.rs
@@ -1,14 +1,12 @@
-// compile-flags: -Z panic-in-drop=abort -O -Z new-llvm-pass-manager=no
+// compile-flags: -Z panic-in-drop=abort -O
+// ignore-msvc
 
 // Ensure that unwinding code paths are eliminated from the output after
 // optimization.
 
-// This test uses -Z new-llvm-pass-manager=no, because the expected optimization does not happen
-// on targets using SEH exceptions (i.e. MSVC) anymore. The core issue is that Rust promises that
-// the drop_in_place() function can't unwind, but implements it in a way that *can*, because we
-// currently go out of our way to allow longjmps, which also use the unwinding mechanism on MSVC
-// targets. We should either forbid longjmps, or not assume nounwind, making this optimization
-// incompatible with the current behavior of running cleanuppads on longjmp unwinding.
+// This test uses ignore-msvc, because the expected optimization does not happen on targets using
+// SEH exceptions with the new LLVM pass manager anymore, see
+// https://github.com/llvm/llvm-project/issues/51311.
 
 // CHECK-NOT: {{(call|invoke).*}}should_not_appear_in_output
 
diff --git a/src/test/codegen/remap_path_prefix/main.rs b/src/test/codegen/remap_path_prefix/main.rs
index 698dfe6b4f3..b13d576295c 100644
--- a/src/test/codegen/remap_path_prefix/main.rs
+++ b/src/test/codegen/remap_path_prefix/main.rs
@@ -22,7 +22,7 @@ fn main() {
 }
 
 // Here we check that local debuginfo is mapped correctly.
-// CHECK: !DIFile(filename: "/the/src/remap_path_prefix/main.rs", directory: "/the/cwd/"
+// CHECK: !DIFile(filename: "/the/src/remap_path_prefix/main.rs", directory: "/the/cwd"
 
 // And here that debuginfo from other crates are expanded to absolute paths.
 // CHECK: !DIFile(filename: "/the/aux-src/remap_path_prefix_aux.rs", directory: ""
diff --git a/src/test/codegen/set-discriminant-invalid.rs b/src/test/codegen/set-discriminant-invalid.rs
index d9614f062b7..bccb9e4c758 100644
--- a/src/test/codegen/set-discriminant-invalid.rs
+++ b/src/test/codegen/set-discriminant-invalid.rs
@@ -28,7 +28,7 @@ impl IntoError<Error> for Api
     #[no_mangle]
     fn into_error(self, error: Self::Source) -> Error {
         Error::Api {
-            source: (|v| v)(error),
+            source: error,
         }
     }
 }
diff --git a/src/test/debuginfo/thread.rs b/src/test/debuginfo/thread.rs
index 531c37a3421..388d50c5cdc 100644
--- a/src/test/debuginfo/thread.rs
+++ b/src/test/debuginfo/thread.rs
@@ -14,7 +14,7 @@
 //
 // cdb-command:dx t,d
 // cdb-check:t,d              : [...] [Type: std::thread::Thread *]
-// cdb-check:    [...] inner            : {...} [Type: alloc::sync::Arc<std::thread::Inner>]
+// cdb-check:[...] inner [...][Type: core::pin::Pin<alloc::sync::Arc<std::thread::Inner> >]
 
 use std::thread;
 
diff --git a/src/test/debuginfo/unit-type.rs b/src/test/debuginfo/unit-type.rs
new file mode 100644
index 00000000000..7aab41a3e7c
--- /dev/null
+++ b/src/test/debuginfo/unit-type.rs
@@ -0,0 +1,71 @@
+// compile-flags:-g
+
+// We only test Rust-aware versions of GDB:
+// min-gdb-version: 8.2
+
+// === GDB TESTS ===================================================================================
+
+// gdb-command: run
+
+// gdb-command: print _ref
+// gdb-check: $1 = (*mut ()) 0x[...]
+
+// gdb-command: print _ptr
+// gdb-check: $2 = (*mut ()) 0x[...]
+
+// gdb-command: print _local
+// gdb-check: $3 = ()
+
+// gdb-command: print _field
+// gdb-check: $4 = unit_type::_TypeContainingUnitField {_a: 123, _unit: (), _b: 456}
+
+// Check that we can cast "void pointers" to their actual type in the debugger
+// gdb-command: print /x *(_ptr as *const u64)
+// gdb-check: $5 = 0x1122334455667788
+
+// === CDB TESTS ===================================================================================
+
+// cdb-command: g
+// cdb-check: Breakpoint 0 hit
+
+// cdb-command: dx _ref
+// cdb-check: _ref             : 0x[...] : () [Type: tuple$<> *]
+
+// cdb-command: dx _ptr
+// cdb-check: _ptr             : 0x[...] : () [Type: tuple$<> *]
+
+// cdb-command: dx _local
+// cdb-check: _local           : () [Type: tuple$<>]
+
+// cdb-command: dx _field,d
+// cdb-check: _field,d         [Type: unit_type::_TypeContainingUnitField]
+// cdb-check:     [+0x[...]] _a               : 123 [Type: unsigned int]
+// cdb-check:     [+0x[...]] _unit            : () [Type: tuple$<>]
+// cdb-check:     [+0x[...]] _b               : 456 [Type: unsigned __int64]
+
+// Check that we can cast "void pointers" to their actual type in the debugger
+// cdb-command: dx ((__int64 *)_ptr),x
+// cdb-check: ((__int64 *)_ptr),x : 0x[...] : 0x1122334455667788 [Type: __int64 *]
+// cdb-check:     0x1122334455667788 [Type: __int64]
+
+struct _TypeContainingUnitField {
+    _a: u32,
+    _unit: (),
+    _b: u64,
+}
+
+fn foo(_ref: &(), _ptr: *const ()) {
+    let _local = ();
+    let _field = _TypeContainingUnitField { _a: 123, _unit: (), _b: 456 };
+
+    zzz(); // #break
+}
+
+fn main() {
+    let pointee = 0x1122_3344_5566_7788i64;
+
+    foo(&(), &pointee as *const i64 as *const ());
+}
+
+#[inline(never)]
+fn zzz() {}
diff --git a/src/test/incremental/hashes/enum_defs.rs b/src/test/incremental/hashes/enum_defs.rs
index ab9c740844b..6582cdc4a2c 100644
--- a/src/test/incremental/hashes/enum_defs.rs
+++ b/src/test/incremental/hashes/enum_defs.rs
@@ -29,16 +29,14 @@
 
 // Change enum visibility -----------------------------------------------------
 #[cfg(any(cfail1,cfail4))]
-enum EnumVisibility { A }
+enum     EnumVisibility { A }
 
 #[cfg(not(any(cfail1,cfail4)))]
-#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")]
+#[rustc_clean(cfg="cfail2")]
 #[rustc_clean(cfg="cfail3")]
 #[rustc_clean(cfg="cfail5", except="hir_owner,hir_owner_nodes")]
 #[rustc_clean(cfg="cfail6")]
-pub enum EnumVisibility {
-    A
-}
+pub enum EnumVisibility { A }
 
 
 
diff --git a/src/test/incremental/hashes/inherent_impls.rs b/src/test/incremental/hashes/inherent_impls.rs
index 22508e41bf8..5463b0dc87e 100644
--- a/src/test/incremental/hashes/inherent_impls.rs
+++ b/src/test/incremental/hashes/inherent_impls.rs
@@ -116,7 +116,7 @@ impl Foo {
 // Change Method Privacy -------------------------------------------------------
 #[cfg(any(cfail1,cfail4))]
 impl Foo {
-    //------------------------------------------------------------------------------
+    //----------------------------------------------------
     //--------------------------
     //------------------------------------------------------------------------------
     //--------------------------
@@ -129,9 +129,9 @@ impl Foo {
 #[rustc_clean(cfg="cfail5")]
 #[rustc_clean(cfg="cfail6")]
 impl Foo {
-    #[rustc_clean(cfg="cfail2", except="associated_item,hir_owner,hir_owner_nodes")]
+    #[rustc_clean(cfg="cfail2", except="associated_item")]
     #[rustc_clean(cfg="cfail3")]
-    #[rustc_clean(cfg="cfail5", except="associated_item,hir_owner,hir_owner_nodes")]
+    #[rustc_clean(cfg="cfail5", except="hir_owner,hir_owner_nodes,associated_item")]
     #[rustc_clean(cfg="cfail6")]
     fn     method_privacy() { }
 }
diff --git a/src/test/incremental/hashes/statics.rs b/src/test/incremental/hashes/statics.rs
index 2b2658b2f5f..697be056761 100644
--- a/src/test/incremental/hashes/statics.rs
+++ b/src/test/incremental/hashes/statics.rs
@@ -24,10 +24,10 @@
 
 // Change static visibility
 #[cfg(any(cfail1,cfail4))]
-static STATIC_VISIBILITY: u8 = 0;
+static     STATIC_VISIBILITY: u8 = 0;
 
 #[cfg(not(any(cfail1,cfail4)))]
-#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")]
+#[rustc_clean(cfg="cfail2")]
 #[rustc_clean(cfg="cfail3")]
 #[rustc_clean(cfg="cfail5", except="hir_owner,hir_owner_nodes")]
 #[rustc_clean(cfg="cfail6")]
diff --git a/src/test/incremental/hashes/struct_defs.rs b/src/test/incremental/hashes/struct_defs.rs
index b5d8a3ab341..b4d558d259f 100644
--- a/src/test/incremental/hashes/struct_defs.rs
+++ b/src/test/incremental/hashes/struct_defs.rs
@@ -84,12 +84,12 @@ struct TupleStructAddField(
 // Tuple Struct Field Visibility -----------------------------------------------
 
 #[cfg(any(cfail1,cfail4))]
-struct TupleStructFieldVisibility(char);
+struct TupleStructFieldVisibility(    char);
 
 #[cfg(not(any(cfail1,cfail4)))]
-#[rustc_clean(except="hir_owner,hir_owner_nodes,type_of", cfg="cfail2")]
+#[rustc_clean(cfg="cfail2", except="type_of")]
 #[rustc_clean(cfg="cfail3")]
-#[rustc_clean(except="hir_owner,hir_owner_nodes,type_of", cfg="cfail5")]
+#[rustc_clean(cfg="cfail5", except="hir_owner,hir_owner_nodes,type_of")]
 #[rustc_clean(cfg="cfail6")]
 struct TupleStructFieldVisibility(pub char);
 
@@ -142,16 +142,14 @@ struct RecordStructAddField {
 // Record Struct Field Visibility ----------------------------------------------
 
 #[cfg(any(cfail1,cfail4))]
-struct RecordStructFieldVisibility { x: f32 }
+struct RecordStructFieldVisibility {     x: f32 }
 
 #[cfg(not(any(cfail1,cfail4)))]
-#[rustc_clean(except="hir_owner,hir_owner_nodes,type_of", cfg="cfail2")]
+#[rustc_clean(cfg="cfail2", except="type_of")]
 #[rustc_clean(cfg="cfail3")]
-#[rustc_clean(except="hir_owner,hir_owner_nodes,type_of", cfg="cfail5")]
+#[rustc_clean(cfg="cfail5", except="hir_owner,hir_owner_nodes,type_of")]
 #[rustc_clean(cfg="cfail6")]
-struct RecordStructFieldVisibility {
-    pub x: f32
-}
+struct RecordStructFieldVisibility { pub x: f32 }
 
 
 // Add Lifetime Parameter ------------------------------------------------------
@@ -257,12 +255,12 @@ pub struct EmptyStruct;
 // Visibility ------------------------------------------------------------------
 
 #[cfg(any(cfail1,cfail4))]
-struct Visibility;
+struct     Visibility;
 
 #[cfg(not(any(cfail1,cfail4)))]
-#[rustc_clean(except="hir_owner,hir_owner_nodes", cfg="cfail2")]
+#[rustc_clean(cfg="cfail2")]
 #[rustc_clean(cfg="cfail3")]
-#[rustc_clean(except="hir_owner,hir_owner_nodes", cfg="cfail5")]
+#[rustc_clean(cfg="cfail5", except="hir_owner,hir_owner_nodes")]
 #[rustc_clean(cfg="cfail6")]
 pub struct Visibility;
 
diff --git a/src/test/incremental/hashes/trait_defs.rs b/src/test/incremental/hashes/trait_defs.rs
index 279449fce5e..717e9e8c8e1 100644
--- a/src/test/incremental/hashes/trait_defs.rs
+++ b/src/test/incremental/hashes/trait_defs.rs
@@ -31,9 +31,9 @@
 trait TraitVisibility { }
 
 #[cfg(not(any(cfail1,cfail4)))]
-#[rustc_clean(except="hir_owner,hir_owner_nodes", cfg="cfail2")]
+#[rustc_clean(cfg="cfail2")]
 #[rustc_clean(cfg="cfail3")]
-#[rustc_clean(except="hir_owner,hir_owner_nodes", cfg="cfail5")]
+#[rustc_clean(cfg="cfail5", except="hir_owner,hir_owner_nodes")]
 #[rustc_clean(cfg="cfail6")]
 pub trait TraitVisibility { }
 
diff --git a/src/test/mir-opt/combine_clone_of_primitives.rs b/src/test/mir-opt/combine_clone_of_primitives.rs
index 0972d2d68a1..7cc50a86e21 100644
--- a/src/test/mir-opt/combine_clone_of_primitives.rs
+++ b/src/test/mir-opt/combine_clone_of_primitives.rs
@@ -1,4 +1,4 @@
-// compile-flags: -C opt-level=0 -Z inline_mir=no
+// unit-test: InstCombine
 // ignore-wasm32 compiled with panic=abort by default
 
 // EMIT_MIR combine_clone_of_primitives.{impl#0}-clone.InstCombine.diff
diff --git a/src/test/mir-opt/combine_clone_of_primitives.{impl#0}-clone.InstCombine.diff b/src/test/mir-opt/combine_clone_of_primitives.{impl#0}-clone.InstCombine.diff
index 678e965cd67..fdc016a95d5 100644
--- a/src/test/mir-opt/combine_clone_of_primitives.{impl#0}-clone.InstCombine.diff
+++ b/src/test/mir-opt/combine_clone_of_primitives.{impl#0}-clone.InstCombine.diff
@@ -23,9 +23,15 @@
       }
   
       bb0: {
+          StorageLive(_2);                 // scope 0 at $DIR/combine_clone_of_primitives.rs:8:5: 8:9
           _2 = &((*_1).0: T);              // scope 0 at $DIR/combine_clone_of_primitives.rs:8:5: 8:9
+          StorageLive(_3);                 // scope 0 at $DIR/combine_clone_of_primitives.rs:9:5: 9:11
           _3 = &((*_1).1: u64);            // scope 0 at $DIR/combine_clone_of_primitives.rs:9:5: 9:11
+          StorageLive(_4);                 // scope 0 at $DIR/combine_clone_of_primitives.rs:10:5: 10:16
           _4 = &((*_1).2: [f32; 3]);       // scope 0 at $DIR/combine_clone_of_primitives.rs:10:5: 10:16
+          StorageLive(_5);                 // scope 1 at $DIR/combine_clone_of_primitives.rs:8:5: 8:9
+          StorageLive(_6);                 // scope 1 at $DIR/combine_clone_of_primitives.rs:8:5: 8:9
+          StorageLive(_7);                 // scope 1 at $DIR/combine_clone_of_primitives.rs:8:5: 8:9
 -         _7 = &(*_2);                     // scope 1 at $DIR/combine_clone_of_primitives.rs:8:5: 8:9
 -         _6 = &(*_7);                     // scope 1 at $DIR/combine_clone_of_primitives.rs:8:5: 8:9
 +         _7 = _2;                         // scope 1 at $DIR/combine_clone_of_primitives.rs:8:5: 8:9
@@ -37,6 +43,10 @@
       }
   
       bb1: {
+          StorageDead(_6);                 // scope 1 at $DIR/combine_clone_of_primitives.rs:8:8: 8:9
+          StorageLive(_8);                 // scope 1 at $DIR/combine_clone_of_primitives.rs:9:5: 9:11
+          StorageLive(_9);                 // scope 1 at $DIR/combine_clone_of_primitives.rs:9:5: 9:11
+          StorageLive(_10);                // scope 1 at $DIR/combine_clone_of_primitives.rs:9:5: 9:11
 -         _10 = &(*_3);                    // scope 1 at $DIR/combine_clone_of_primitives.rs:9:5: 9:11
 -         _9 = &(*_10);                    // scope 1 at $DIR/combine_clone_of_primitives.rs:9:5: 9:11
 -         _8 = <u64 as Clone>::clone(move _9) -> [return: bb2, unwind: bb4]; // scope 1 at $DIR/combine_clone_of_primitives.rs:9:5: 9:11
@@ -50,6 +60,10 @@
       }
   
       bb2: {
+          StorageDead(_9);                 // scope 1 at $DIR/combine_clone_of_primitives.rs:9:10: 9:11
+          StorageLive(_11);                // scope 1 at $DIR/combine_clone_of_primitives.rs:10:5: 10:16
+          StorageLive(_12);                // scope 1 at $DIR/combine_clone_of_primitives.rs:10:5: 10:16
+          StorageLive(_13);                // scope 1 at $DIR/combine_clone_of_primitives.rs:10:5: 10:16
 -         _13 = &(*_4);                    // scope 1 at $DIR/combine_clone_of_primitives.rs:10:5: 10:16
 -         _12 = &(*_13);                   // scope 1 at $DIR/combine_clone_of_primitives.rs:10:5: 10:16
 -         _11 = <[f32; 3] as Clone>::clone(move _12) -> [return: bb3, unwind: bb4]; // scope 1 at $DIR/combine_clone_of_primitives.rs:10:5: 10:16
@@ -63,10 +77,20 @@
       }
   
       bb3: {
+          StorageDead(_12);                // scope 1 at $DIR/combine_clone_of_primitives.rs:10:15: 10:16
           Deinit(_0);                      // scope 1 at $DIR/combine_clone_of_primitives.rs:6:10: 6:15
           (_0.0: T) = move _5;             // scope 1 at $DIR/combine_clone_of_primitives.rs:6:10: 6:15
           (_0.1: u64) = move _8;           // scope 1 at $DIR/combine_clone_of_primitives.rs:6:10: 6:15
           (_0.2: [f32; 3]) = move _11;     // scope 1 at $DIR/combine_clone_of_primitives.rs:6:10: 6:15
+          StorageDead(_13);                // scope 1 at $DIR/combine_clone_of_primitives.rs:6:14: 6:15
+          StorageDead(_11);                // scope 1 at $DIR/combine_clone_of_primitives.rs:6:14: 6:15
+          StorageDead(_10);                // scope 1 at $DIR/combine_clone_of_primitives.rs:6:14: 6:15
+          StorageDead(_8);                 // scope 1 at $DIR/combine_clone_of_primitives.rs:6:14: 6:15
+          StorageDead(_7);                 // scope 1 at $DIR/combine_clone_of_primitives.rs:6:14: 6:15
+          StorageDead(_5);                 // scope 1 at $DIR/combine_clone_of_primitives.rs:6:14: 6:15
+          StorageDead(_4);                 // scope 0 at $DIR/combine_clone_of_primitives.rs:6:14: 6:15
+          StorageDead(_3);                 // scope 0 at $DIR/combine_clone_of_primitives.rs:6:14: 6:15
+          StorageDead(_2);                 // scope 0 at $DIR/combine_clone_of_primitives.rs:6:14: 6:15
           return;                          // scope 0 at $DIR/combine_clone_of_primitives.rs:6:15: 6:15
       }
   
diff --git a/src/test/mir-opt/derefer_complex_case.main.Derefer.diff b/src/test/mir-opt/derefer_complex_case.main.Derefer.diff
new file mode 100644
index 00000000000..f5eabf86967
--- /dev/null
+++ b/src/test/mir-opt/derefer_complex_case.main.Derefer.diff
@@ -0,0 +1,111 @@
+- // MIR for `main` before Derefer
++ // MIR for `main` after Derefer
+  
+  fn main() -> () {
+      let mut _0: ();                      // return place in scope 0 at $DIR/derefer_complex_case.rs:3:11: 3:11
+      let mut _1: std::slice::Iter<i32>;   // in scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
+      let mut _2: &[i32; 2];               // in scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
+      let _3: [i32; 2];                    // in scope 0 at $DIR/derefer_complex_case.rs:4:18: 4:26
+      let mut _4: std::slice::Iter<i32>;   // in scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
+      let mut _5: ();                      // in scope 0 at $DIR/derefer_complex_case.rs:3:1: 5:2
+      let _6: ();                          // in scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
+      let mut _7: std::option::Option<&i32>; // in scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
+      let mut _8: &mut std::slice::Iter<i32>; // in scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
+      let mut _9: &mut std::slice::Iter<i32>; // in scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
+      let mut _10: isize;                  // in scope 0 at $DIR/derefer_complex_case.rs:4:5: 4:40
+      let mut _11: !;                      // in scope 0 at $DIR/derefer_complex_case.rs:4:5: 4:40
+      let mut _13: i32;                    // in scope 0 at $DIR/derefer_complex_case.rs:4:34: 4:37
+      let mut _14: &[i32; 2];              // in scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
++     let mut _15: &i32;                   // in scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
+      scope 1 {
+          debug iter => _4;                // in scope 1 at $DIR/derefer_complex_case.rs:4:17: 4:26
+          let _12: i32;                    // in scope 1 at $DIR/derefer_complex_case.rs:4:10: 4:13
+          scope 2 {
+              debug foo => _12;            // in scope 2 at $DIR/derefer_complex_case.rs:4:10: 4:13
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);                 // scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
+          StorageLive(_2);                 // scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
+          _14 = const main::promoted[0];   // scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
+                                           // mir::Constant
+                                           // + span: $DIR/derefer_complex_case.rs:4:17: 4:26
+                                           // + literal: Const { ty: &[i32; 2], val: Unevaluated(main, [], Some(promoted[0])) }
+          _2 = &(*_14);                    // scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
+          _1 = <&[i32; 2] as IntoIterator>::into_iter(move _2) -> bb1; // scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
+                                           // mir::Constant
+                                           // + span: $DIR/derefer_complex_case.rs:4:17: 4:26
+                                           // + literal: Const { ty: fn(&[i32; 2]) -> <&[i32; 2] as IntoIterator>::IntoIter {<&[i32; 2] as IntoIterator>::into_iter}, val: Value(Scalar(<ZST>)) }
+      }
+  
+      bb1: {
+          StorageDead(_2);                 // scope 0 at $DIR/derefer_complex_case.rs:4:25: 4:26
+          StorageLive(_4);                 // scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
+          _4 = move _1;                    // scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
+          goto -> bb2;                     // scope 1 at $DIR/derefer_complex_case.rs:4:5: 4:40
+      }
+  
+      bb2: {
+          StorageLive(_6);                 // scope 1 at $DIR/derefer_complex_case.rs:4:17: 4:26
+          StorageLive(_7);                 // scope 1 at $DIR/derefer_complex_case.rs:4:17: 4:26
+          StorageLive(_8);                 // scope 1 at $DIR/derefer_complex_case.rs:4:17: 4:26
+          StorageLive(_9);                 // scope 1 at $DIR/derefer_complex_case.rs:4:17: 4:26
+          _9 = &mut _4;                    // scope 1 at $DIR/derefer_complex_case.rs:4:17: 4:26
+          _8 = &mut (*_9);                 // scope 1 at $DIR/derefer_complex_case.rs:4:17: 4:26
+          _7 = <std::slice::Iter<i32> as Iterator>::next(move _8) -> bb3; // scope 1 at $DIR/derefer_complex_case.rs:4:17: 4:26
+                                           // mir::Constant
+                                           // + span: $DIR/derefer_complex_case.rs:4:17: 4:26
+                                           // + literal: Const { ty: for<'r> fn(&'r mut std::slice::Iter<i32>) -> Option<<std::slice::Iter<i32> as Iterator>::Item> {<std::slice::Iter<i32> as Iterator>::next}, val: Value(Scalar(<ZST>)) }
+      }
+  
+      bb3: {
+          StorageDead(_8);                 // scope 1 at $DIR/derefer_complex_case.rs:4:25: 4:26
+          _10 = discriminant(_7);          // scope 1 at $DIR/derefer_complex_case.rs:4:17: 4:26
+          switchInt(move _10) -> [0_isize: bb6, 1_isize: bb4, otherwise: bb5]; // scope 1 at $DIR/derefer_complex_case.rs:4:17: 4:26
+      }
+  
+      bb4: {
+          StorageLive(_12);                // scope 1 at $DIR/derefer_complex_case.rs:4:10: 4:13
+-         _12 = (*((_7 as Some).0: &i32)); // scope 1 at $DIR/derefer_complex_case.rs:4:10: 4:13
++         StorageLive(_15);                // scope 1 at $DIR/derefer_complex_case.rs:4:10: 4:13
++         _15 = move ((_7 as Some).0: &i32); // scope 1 at $DIR/derefer_complex_case.rs:4:10: 4:13
++         _12 = (*_15);                    // scope 1 at $DIR/derefer_complex_case.rs:4:10: 4:13
++         StorageDead(_15);                // scope 2 at $DIR/derefer_complex_case.rs:4:34: 4:37
+          StorageLive(_13);                // scope 2 at $DIR/derefer_complex_case.rs:4:34: 4:37
+          _13 = _12;                       // scope 2 at $DIR/derefer_complex_case.rs:4:34: 4:37
+          _6 = std::mem::drop::<i32>(move _13) -> bb7; // scope 2 at $DIR/derefer_complex_case.rs:4:29: 4:38
+                                           // mir::Constant
+                                           // + span: $DIR/derefer_complex_case.rs:4:29: 4:33
+                                           // + literal: Const { ty: fn(i32) {std::mem::drop::<i32>}, val: Value(Scalar(<ZST>)) }
+      }
+  
+      bb5: {
+          unreachable;                     // scope 1 at $DIR/derefer_complex_case.rs:4:17: 4:26
+      }
+  
+      bb6: {
+          _0 = const ();                   // scope 1 at $DIR/derefer_complex_case.rs:4:5: 4:40
+          StorageDead(_9);                 // scope 1 at $DIR/derefer_complex_case.rs:4:39: 4:40
+          StorageDead(_7);                 // scope 1 at $DIR/derefer_complex_case.rs:4:39: 4:40
+          StorageDead(_6);                 // scope 1 at $DIR/derefer_complex_case.rs:4:39: 4:40
+          StorageDead(_4);                 // scope 0 at $DIR/derefer_complex_case.rs:4:39: 4:40
+          StorageDead(_1);                 // scope 0 at $DIR/derefer_complex_case.rs:4:39: 4:40
+          return;                          // scope 0 at $DIR/derefer_complex_case.rs:5:2: 5:2
+      }
+  
+      bb7: {
+          StorageDead(_13);                // scope 2 at $DIR/derefer_complex_case.rs:4:37: 4:38
+          StorageDead(_12);                // scope 1 at $DIR/derefer_complex_case.rs:4:39: 4:40
+          StorageDead(_9);                 // scope 1 at $DIR/derefer_complex_case.rs:4:39: 4:40
+          StorageDead(_7);                 // scope 1 at $DIR/derefer_complex_case.rs:4:39: 4:40
+          StorageDead(_6);                 // scope 1 at $DIR/derefer_complex_case.rs:4:39: 4:40
+          _5 = const ();                   // scope 1 at $DIR/derefer_complex_case.rs:4:5: 4:40
+          goto -> bb2;                     // scope 1 at $DIR/derefer_complex_case.rs:4:5: 4:40
+      }
+  
+      bb8 (cleanup): {
+          resume;                          // scope 0 at $DIR/derefer_complex_case.rs:3:1: 5:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/derefer_complex_case.rs b/src/test/mir-opt/derefer_complex_case.rs
new file mode 100644
index 00000000000..6abf49f9966
--- /dev/null
+++ b/src/test/mir-opt/derefer_complex_case.rs
@@ -0,0 +1,5 @@
+// EMIT_MIR derefer_complex_case.main.Derefer.diff
+
+fn main() {
+    for &foo in &[42, 43] { drop(foo) }
+}
diff --git a/src/test/mir-opt/derefer_terminator_test.main.Derefer.diff b/src/test/mir-opt/derefer_terminator_test.main.Derefer.diff
new file mode 100644
index 00000000000..8b91a65bf3d
--- /dev/null
+++ b/src/test/mir-opt/derefer_terminator_test.main.Derefer.diff
@@ -0,0 +1,103 @@
+- // MIR for `main` before Derefer
++ // MIR for `main` after Derefer
+  
+  fn main() -> () {
+      let mut _0: ();                      // return place in scope 0 at $DIR/derefer_terminator_test.rs:2:11: 2:11
+      let _1: bool;                        // in scope 0 at $DIR/derefer_terminator_test.rs:3:9: 3:10
+      let _3: ();                          // in scope 0 at $DIR/derefer_terminator_test.rs:5:5: 8:6
+      let mut _4: &&&&bool;                // in scope 0 at $DIR/derefer_terminator_test.rs:5:15: 5:22
+      let _5: &&&bool;                     // in scope 0 at $DIR/derefer_terminator_test.rs:5:17: 5:21
+      let _6: &&bool;                      // in scope 0 at $DIR/derefer_terminator_test.rs:5:18: 5:21
+      let _7: &bool;                       // in scope 0 at $DIR/derefer_terminator_test.rs:5:19: 5:21
++     let mut _10: &&&bool;                // in scope 0 at $DIR/derefer_terminator_test.rs:5:15: 5:22
++     let mut _11: &&bool;                 // in scope 0 at $DIR/derefer_terminator_test.rs:5:15: 5:22
++     let mut _12: &bool;                  // in scope 0 at $DIR/derefer_terminator_test.rs:5:15: 5:22
+      scope 1 {
+          debug b => _1;                   // in scope 1 at $DIR/derefer_terminator_test.rs:3:9: 3:10
+          let _2: bool;                    // in scope 1 at $DIR/derefer_terminator_test.rs:4:9: 4:10
+          scope 2 {
+              debug d => _2;               // in scope 2 at $DIR/derefer_terminator_test.rs:4:9: 4:10
+              let _8: i32;                 // in scope 2 at $DIR/derefer_terminator_test.rs:6:22: 6:23
+              let _9: i32;                 // in scope 2 at $DIR/derefer_terminator_test.rs:9:9: 9:10
+              scope 3 {
+                  debug x => _8;           // in scope 3 at $DIR/derefer_terminator_test.rs:6:22: 6:23
+              }
+              scope 4 {
+                  debug y => _9;           // in scope 4 at $DIR/derefer_terminator_test.rs:9:9: 9:10
+              }
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);                 // scope 0 at $DIR/derefer_terminator_test.rs:3:9: 3:10
+          _1 = foo() -> bb1;               // scope 0 at $DIR/derefer_terminator_test.rs:3:13: 3:18
+                                           // mir::Constant
+                                           // + span: $DIR/derefer_terminator_test.rs:3:13: 3:16
+                                           // + literal: Const { ty: fn() -> bool {foo}, val: Value(Scalar(<ZST>)) }
+      }
+  
+      bb1: {
+          StorageLive(_2);                 // scope 1 at $DIR/derefer_terminator_test.rs:4:9: 4:10
+          _2 = foo() -> bb2;               // scope 1 at $DIR/derefer_terminator_test.rs:4:13: 4:18
+                                           // mir::Constant
+                                           // + span: $DIR/derefer_terminator_test.rs:4:13: 4:16
+                                           // + literal: Const { ty: fn() -> bool {foo}, val: Value(Scalar(<ZST>)) }
+      }
+  
+      bb2: {
+          StorageLive(_3);                 // scope 2 at $DIR/derefer_terminator_test.rs:5:5: 8:6
+          StorageLive(_4);                 // scope 2 at $DIR/derefer_terminator_test.rs:5:15: 5:22
+          StorageLive(_5);                 // scope 2 at $DIR/derefer_terminator_test.rs:5:17: 5:21
+          StorageLive(_6);                 // scope 2 at $DIR/derefer_terminator_test.rs:5:18: 5:21
+          StorageLive(_7);                 // scope 2 at $DIR/derefer_terminator_test.rs:5:19: 5:21
+          _7 = &_1;                        // scope 2 at $DIR/derefer_terminator_test.rs:5:19: 5:21
+          _6 = &_7;                        // scope 2 at $DIR/derefer_terminator_test.rs:5:18: 5:21
+          _5 = &_6;                        // scope 2 at $DIR/derefer_terminator_test.rs:5:17: 5:21
+          _4 = &_5;                        // scope 2 at $DIR/derefer_terminator_test.rs:5:15: 5:22
+-         switchInt((*(*(*(*_4))))) -> [false: bb3, otherwise: bb4]; // scope 2 at $DIR/derefer_terminator_test.rs:5:5: 5:22
++         StorageLive(_10);                // scope 2 at $DIR/derefer_terminator_test.rs:5:5: 5:22
++         _10 = move (*_4);                // scope 2 at $DIR/derefer_terminator_test.rs:5:5: 5:22
++         StorageLive(_11);                // scope 2 at $DIR/derefer_terminator_test.rs:5:5: 5:22
++         _11 = move (*_10);               // scope 2 at $DIR/derefer_terminator_test.rs:5:5: 5:22
++         StorageDead(_10);                // scope 2 at $DIR/derefer_terminator_test.rs:5:5: 5:22
++         StorageLive(_12);                // scope 2 at $DIR/derefer_terminator_test.rs:5:5: 5:22
++         _12 = move (*_11);               // scope 2 at $DIR/derefer_terminator_test.rs:5:5: 5:22
++         StorageDead(_11);                // scope 2 at $DIR/derefer_terminator_test.rs:5:5: 5:22
++         switchInt((*_12)) -> [false: bb3, otherwise: bb4]; // scope 2 at $DIR/derefer_terminator_test.rs:5:5: 5:22
+      }
+  
+      bb3: {
++         StorageDead(_12);                // scope 2 at $DIR/derefer_terminator_test.rs:5:5: 5:22
+          _3 = const ();                   // scope 2 at $DIR/derefer_terminator_test.rs:7:18: 7:20
+          goto -> bb5;                     // scope 2 at $DIR/derefer_terminator_test.rs:7:18: 7:20
+      }
+  
+      bb4: {
++         StorageDead(_12);                // scope 2 at $DIR/derefer_terminator_test.rs:5:5: 5:22
+          StorageLive(_8);                 // scope 2 at $DIR/derefer_terminator_test.rs:6:22: 6:23
+          _8 = const 5_i32;                // scope 2 at $DIR/derefer_terminator_test.rs:6:26: 6:27
+          _3 = const ();                   // scope 2 at $DIR/derefer_terminator_test.rs:6:17: 6:29
+          StorageDead(_8);                 // scope 2 at $DIR/derefer_terminator_test.rs:6:28: 6:29
+          goto -> bb5;                     // scope 2 at $DIR/derefer_terminator_test.rs:6:28: 6:29
+      }
+  
+      bb5: {
+          StorageDead(_7);                 // scope 2 at $DIR/derefer_terminator_test.rs:8:5: 8:6
+          StorageDead(_6);                 // scope 2 at $DIR/derefer_terminator_test.rs:8:5: 8:6
+          StorageDead(_5);                 // scope 2 at $DIR/derefer_terminator_test.rs:8:5: 8:6
+          StorageDead(_4);                 // scope 2 at $DIR/derefer_terminator_test.rs:8:5: 8:6
+          StorageDead(_3);                 // scope 2 at $DIR/derefer_terminator_test.rs:8:5: 8:6
+          StorageLive(_9);                 // scope 2 at $DIR/derefer_terminator_test.rs:9:9: 9:10
+          _9 = const 42_i32;               // scope 2 at $DIR/derefer_terminator_test.rs:9:13: 9:15
+          _0 = const ();                   // scope 0 at $DIR/derefer_terminator_test.rs:2:11: 10:2
+          StorageDead(_9);                 // scope 2 at $DIR/derefer_terminator_test.rs:10:1: 10:2
+          StorageDead(_2);                 // scope 1 at $DIR/derefer_terminator_test.rs:10:1: 10:2
+          StorageDead(_1);                 // scope 0 at $DIR/derefer_terminator_test.rs:10:1: 10:2
+          return;                          // scope 0 at $DIR/derefer_terminator_test.rs:10:2: 10:2
+      }
+  
+      bb6 (cleanup): {
+          resume;                          // scope 0 at $DIR/derefer_terminator_test.rs:2:1: 10:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/derefer_terminator_test.rs b/src/test/mir-opt/derefer_terminator_test.rs
new file mode 100644
index 00000000000..11f5b20636d
--- /dev/null
+++ b/src/test/mir-opt/derefer_terminator_test.rs
@@ -0,0 +1,14 @@
+// EMIT_MIR derefer_terminator_test.main.Derefer.diff
+fn main() {
+    let b = foo();
+    let d = foo();
+    match ****(&&&&b) {
+        true => {let x = 5;},
+        false => {}
+    }
+    let y = 42;
+}
+
+fn foo() -> bool {
+    true
+}
diff --git a/src/test/mir-opt/derefer_test.main.Derefer.diff b/src/test/mir-opt/derefer_test.main.Derefer.diff
index d58e4eb838d..84476aeed7a 100644
--- a/src/test/mir-opt/derefer_test.main.Derefer.diff
+++ b/src/test/mir-opt/derefer_test.main.Derefer.diff
@@ -5,8 +5,8 @@
       let mut _0: ();                      // return place in scope 0 at $DIR/derefer_test.rs:2:11: 2:11
       let mut _1: (i32, i32);              // in scope 0 at $DIR/derefer_test.rs:3:9: 3:14
       let mut _3: &mut (i32, i32);         // in scope 0 at $DIR/derefer_test.rs:4:22: 4:28
-+     let mut _6: &mut (i32, i32);         // in scope 0 at $DIR/derefer_test.rs:5:13: 5:26
-+     let mut _7: &mut (i32, i32);         // in scope 0 at $DIR/derefer_test.rs:6:13: 6:26
++     let mut _6: &mut (i32, i32);         // in scope 0 at $DIR/derefer_test.rs:4:9: 4:14
++     let mut _7: &mut (i32, i32);         // in scope 0 at $DIR/derefer_test.rs:4:9: 4:14
       scope 1 {
           debug a => _1;                   // in scope 1 at $DIR/derefer_test.rs:3:9: 3:14
           let mut _2: (i32, &mut (i32, i32)); // in scope 1 at $DIR/derefer_test.rs:4:9: 4:14
@@ -25,15 +25,11 @@
   
       bb0: {
           StorageLive(_1);                 // scope 0 at $DIR/derefer_test.rs:3:9: 3:14
-          Deinit(_1);                      // scope 0 at $DIR/derefer_test.rs:3:17: 3:24
-          (_1.0: i32) = const 42_i32;      // scope 0 at $DIR/derefer_test.rs:3:17: 3:24
-          (_1.1: i32) = const 43_i32;      // scope 0 at $DIR/derefer_test.rs:3:17: 3:24
+          _1 = (const 42_i32, const 43_i32); // scope 0 at $DIR/derefer_test.rs:3:17: 3:24
           StorageLive(_2);                 // scope 1 at $DIR/derefer_test.rs:4:9: 4:14
           StorageLive(_3);                 // scope 1 at $DIR/derefer_test.rs:4:22: 4:28
           _3 = &mut _1;                    // scope 1 at $DIR/derefer_test.rs:4:22: 4:28
-          Deinit(_2);                      // scope 1 at $DIR/derefer_test.rs:4:17: 4:29
-          (_2.0: i32) = const 99_i32;      // scope 1 at $DIR/derefer_test.rs:4:17: 4:29
-          (_2.1: &mut (i32, i32)) = move _3; // scope 1 at $DIR/derefer_test.rs:4:17: 4:29
+          _2 = (const 99_i32, move _3);    // scope 1 at $DIR/derefer_test.rs:4:17: 4:29
           StorageDead(_3);                 // scope 1 at $DIR/derefer_test.rs:4:28: 4:29
           StorageLive(_4);                 // scope 2 at $DIR/derefer_test.rs:5:9: 5:10
 -         _4 = &mut ((*(_2.1: &mut (i32, i32))).0: i32); // scope 2 at $DIR/derefer_test.rs:5:13: 5:26
@@ -53,10 +49,10 @@
           StorageDead(_2);                 // scope 1 at $DIR/derefer_test.rs:7:1: 7:2
           StorageDead(_1);                 // scope 0 at $DIR/derefer_test.rs:7:1: 7:2
           return;                          // scope 0 at $DIR/derefer_test.rs:7:2: 7:2
-+     }
-+ 
-+     bb1 (cleanup): {
-+         resume;                          // scope 0 at $DIR/derefer_test.rs:2:1: 7:2
+      }
+  
+      bb1 (cleanup): {
+          resume;                          // scope 0 at $DIR/derefer_test.rs:2:1: 7:2
       }
   }
   
diff --git a/src/test/mir-opt/derefer_test_multiple.main.Derefer.diff b/src/test/mir-opt/derefer_test_multiple.main.Derefer.diff
index db24f71c750..b8e5a0c328f 100644
--- a/src/test/mir-opt/derefer_test_multiple.main.Derefer.diff
+++ b/src/test/mir-opt/derefer_test_multiple.main.Derefer.diff
@@ -7,12 +7,12 @@
       let mut _3: &mut (i32, i32);         // in scope 0 at $DIR/derefer_test_multiple.rs:4:22: 4:28
       let mut _5: &mut (i32, &mut (i32, i32)); // in scope 0 at $DIR/derefer_test_multiple.rs:5:22: 5:28
       let mut _7: &mut (i32, &mut (i32, &mut (i32, i32))); // in scope 0 at $DIR/derefer_test_multiple.rs:6:22: 6:28
-+     let mut _10: &mut (i32, &mut (i32, &mut (i32, i32))); // in scope 0 at $DIR/derefer_test_multiple.rs:7:13: 7:30
-+     let mut _11: &mut (i32, &mut (i32, i32)); // in scope 0 at $DIR/derefer_test_multiple.rs:7:13: 7:30
-+     let mut _12: &mut (i32, i32);        // in scope 0 at $DIR/derefer_test_multiple.rs:7:13: 7:30
-+     let mut _13: &mut (i32, &mut (i32, &mut (i32, i32))); // in scope 0 at $DIR/derefer_test_multiple.rs:8:13: 8:30
-+     let mut _14: &mut (i32, &mut (i32, i32)); // in scope 0 at $DIR/derefer_test_multiple.rs:8:13: 8:30
-+     let mut _15: &mut (i32, i32);        // in scope 0 at $DIR/derefer_test_multiple.rs:8:13: 8:30
++     let mut _10: &mut (i32, &mut (i32, &mut (i32, i32))); // in scope 0 at $DIR/derefer_test_multiple.rs:6:9: 6:14
++     let mut _11: &mut (i32, &mut (i32, i32)); // in scope 0 at $DIR/derefer_test_multiple.rs:6:9: 6:14
++     let mut _12: &mut (i32, i32);        // in scope 0 at $DIR/derefer_test_multiple.rs:6:9: 6:14
++     let mut _13: &mut (i32, &mut (i32, &mut (i32, i32))); // in scope 0 at $DIR/derefer_test_multiple.rs:6:9: 6:14
++     let mut _14: &mut (i32, &mut (i32, i32)); // in scope 0 at $DIR/derefer_test_multiple.rs:6:9: 6:14
++     let mut _15: &mut (i32, i32);        // in scope 0 at $DIR/derefer_test_multiple.rs:6:9: 6:14
       scope 1 {
           debug a => _1;                   // in scope 1 at $DIR/derefer_test_multiple.rs:3:9: 3:14
           let mut _2: (i32, &mut (i32, i32)); // in scope 1 at $DIR/derefer_test_multiple.rs:4:9: 4:14
@@ -39,29 +39,21 @@
   
       bb0: {
           StorageLive(_1);                 // scope 0 at $DIR/derefer_test_multiple.rs:3:9: 3:14
-          Deinit(_1);                      // scope 0 at $DIR/derefer_test_multiple.rs:3:17: 3:25
-          (_1.0: i32) = const 42_i32;      // scope 0 at $DIR/derefer_test_multiple.rs:3:17: 3:25
-          (_1.1: i32) = const 43_i32;      // scope 0 at $DIR/derefer_test_multiple.rs:3:17: 3:25
+          _1 = (const 42_i32, const 43_i32); // scope 0 at $DIR/derefer_test_multiple.rs:3:17: 3:25
           StorageLive(_2);                 // scope 1 at $DIR/derefer_test_multiple.rs:4:9: 4:14
           StorageLive(_3);                 // scope 1 at $DIR/derefer_test_multiple.rs:4:22: 4:28
           _3 = &mut _1;                    // scope 1 at $DIR/derefer_test_multiple.rs:4:22: 4:28
-          Deinit(_2);                      // scope 1 at $DIR/derefer_test_multiple.rs:4:17: 4:29
-          (_2.0: i32) = const 99_i32;      // scope 1 at $DIR/derefer_test_multiple.rs:4:17: 4:29
-          (_2.1: &mut (i32, i32)) = move _3; // scope 1 at $DIR/derefer_test_multiple.rs:4:17: 4:29
+          _2 = (const 99_i32, move _3);    // scope 1 at $DIR/derefer_test_multiple.rs:4:17: 4:29
           StorageDead(_3);                 // scope 1 at $DIR/derefer_test_multiple.rs:4:28: 4:29
           StorageLive(_4);                 // scope 2 at $DIR/derefer_test_multiple.rs:5:9: 5:14
           StorageLive(_5);                 // scope 2 at $DIR/derefer_test_multiple.rs:5:22: 5:28
           _5 = &mut _2;                    // scope 2 at $DIR/derefer_test_multiple.rs:5:22: 5:28
-          Deinit(_4);                      // scope 2 at $DIR/derefer_test_multiple.rs:5:17: 5:29
-          (_4.0: i32) = const 11_i32;      // scope 2 at $DIR/derefer_test_multiple.rs:5:17: 5:29
-          (_4.1: &mut (i32, &mut (i32, i32))) = move _5; // scope 2 at $DIR/derefer_test_multiple.rs:5:17: 5:29
+          _4 = (const 11_i32, move _5);    // scope 2 at $DIR/derefer_test_multiple.rs:5:17: 5:29
           StorageDead(_5);                 // scope 2 at $DIR/derefer_test_multiple.rs:5:28: 5:29
           StorageLive(_6);                 // scope 3 at $DIR/derefer_test_multiple.rs:6:9: 6:14
           StorageLive(_7);                 // scope 3 at $DIR/derefer_test_multiple.rs:6:22: 6:28
           _7 = &mut _4;                    // scope 3 at $DIR/derefer_test_multiple.rs:6:22: 6:28
-          Deinit(_6);                      // scope 3 at $DIR/derefer_test_multiple.rs:6:17: 6:29
-          (_6.0: i32) = const 13_i32;      // scope 3 at $DIR/derefer_test_multiple.rs:6:17: 6:29
-          (_6.1: &mut (i32, &mut (i32, &mut (i32, i32)))) = move _7; // scope 3 at $DIR/derefer_test_multiple.rs:6:17: 6:29
+          _6 = (const 13_i32, move _7);    // scope 3 at $DIR/derefer_test_multiple.rs:6:17: 6:29
           StorageDead(_7);                 // scope 3 at $DIR/derefer_test_multiple.rs:6:28: 6:29
           StorageLive(_8);                 // scope 4 at $DIR/derefer_test_multiple.rs:7:9: 7:10
 -         _8 = &mut ((*((*((*(_6.1: &mut (i32, &mut (i32, &mut (i32, i32))))).1: &mut (i32, &mut (i32, i32)))).1: &mut (i32, i32))).1: i32); // scope 4 at $DIR/derefer_test_multiple.rs:7:13: 7:30
@@ -69,11 +61,11 @@
 +         _10 = move (_6.1: &mut (i32, &mut (i32, &mut (i32, i32)))); // scope 4 at $DIR/derefer_test_multiple.rs:7:13: 7:30
 +         StorageLive(_11);                // scope 4 at $DIR/derefer_test_multiple.rs:7:13: 7:30
 +         _11 = move ((*_10).1: &mut (i32, &mut (i32, i32))); // scope 4 at $DIR/derefer_test_multiple.rs:7:13: 7:30
++         StorageDead(_10);                // scope 4 at $DIR/derefer_test_multiple.rs:7:13: 7:30
 +         StorageLive(_12);                // scope 4 at $DIR/derefer_test_multiple.rs:7:13: 7:30
 +         _12 = move ((*_11).1: &mut (i32, i32)); // scope 4 at $DIR/derefer_test_multiple.rs:7:13: 7:30
++         StorageDead(_11);                // scope 4 at $DIR/derefer_test_multiple.rs:7:13: 7:30
 +         _8 = &mut ((*_12).1: i32);       // scope 4 at $DIR/derefer_test_multiple.rs:7:13: 7:30
-+         StorageDead(_10);                // scope 5 at $DIR/derefer_test_multiple.rs:8:9: 8:10
-+         StorageDead(_11);                // scope 5 at $DIR/derefer_test_multiple.rs:8:9: 8:10
 +         StorageDead(_12);                // scope 5 at $DIR/derefer_test_multiple.rs:8:9: 8:10
           StorageLive(_9);                 // scope 5 at $DIR/derefer_test_multiple.rs:8:9: 8:10
 -         _9 = &mut ((*((*((*(_6.1: &mut (i32, &mut (i32, &mut (i32, i32))))).1: &mut (i32, &mut (i32, i32)))).1: &mut (i32, i32))).1: i32); // scope 5 at $DIR/derefer_test_multiple.rs:8:13: 8:30
@@ -81,11 +73,11 @@
 +         _13 = move (_6.1: &mut (i32, &mut (i32, &mut (i32, i32)))); // scope 5 at $DIR/derefer_test_multiple.rs:8:13: 8:30
 +         StorageLive(_14);                // scope 5 at $DIR/derefer_test_multiple.rs:8:13: 8:30
 +         _14 = move ((*_13).1: &mut (i32, &mut (i32, i32))); // scope 5 at $DIR/derefer_test_multiple.rs:8:13: 8:30
++         StorageDead(_13);                // scope 5 at $DIR/derefer_test_multiple.rs:8:13: 8:30
 +         StorageLive(_15);                // scope 5 at $DIR/derefer_test_multiple.rs:8:13: 8:30
 +         _15 = move ((*_14).1: &mut (i32, i32)); // scope 5 at $DIR/derefer_test_multiple.rs:8:13: 8:30
++         StorageDead(_14);                // scope 5 at $DIR/derefer_test_multiple.rs:8:13: 8:30
 +         _9 = &mut ((*_15).1: i32);       // scope 5 at $DIR/derefer_test_multiple.rs:8:13: 8:30
-+         StorageDead(_13);                // scope 0 at $DIR/derefer_test_multiple.rs:2:12: 9:2
-+         StorageDead(_14);                // scope 0 at $DIR/derefer_test_multiple.rs:2:12: 9:2
 +         StorageDead(_15);                // scope 0 at $DIR/derefer_test_multiple.rs:2:12: 9:2
           _0 = const ();                   // scope 0 at $DIR/derefer_test_multiple.rs:2:12: 9:2
           StorageDead(_9);                 // scope 5 at $DIR/derefer_test_multiple.rs:9:1: 9:2
@@ -95,10 +87,10 @@
           StorageDead(_2);                 // scope 1 at $DIR/derefer_test_multiple.rs:9:1: 9:2
           StorageDead(_1);                 // scope 0 at $DIR/derefer_test_multiple.rs:9:1: 9:2
           return;                          // scope 0 at $DIR/derefer_test_multiple.rs:9:2: 9:2
-+     }
-+ 
-+     bb1 (cleanup): {
-+         resume;                          // scope 0 at $DIR/derefer_test_multiple.rs:2:1: 9:2
+      }
+  
+      bb1 (cleanup): {
+          resume;                          // scope 0 at $DIR/derefer_test_multiple.rs:2:1: 9:2
       }
   }
   
diff --git a/src/test/mir-opt/early_otherwise_branch.opt2.EarlyOtherwiseBranch.diff b/src/test/mir-opt/early_otherwise_branch.opt2.EarlyOtherwiseBranch.diff
index 4f2b9696f8c..1cdd97ab283 100644
--- a/src/test/mir-opt/early_otherwise_branch.opt2.EarlyOtherwiseBranch.diff
+++ b/src/test/mir-opt/early_otherwise_branch.opt2.EarlyOtherwiseBranch.diff
@@ -32,7 +32,7 @@
           StorageDead(_5);                 // scope 0 at $DIR/early_otherwise_branch.rs:12:16: 12:17
           StorageDead(_4);                 // scope 0 at $DIR/early_otherwise_branch.rs:12:16: 12:17
           _8 = discriminant((_3.0: std::option::Option<u32>)); // scope 0 at $DIR/early_otherwise_branch.rs:12:11: 12:17
--         switchInt(move _8) -> [0_isize: bb1, 1_isize: bb3, otherwise: bb7]; // scope 0 at $DIR/early_otherwise_branch.rs:12:5: 12:17
+-         switchInt(move _8) -> [0_isize: bb1, 1_isize: bb3, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch.rs:12:5: 12:17
 +         StorageLive(_11);                // scope 0 at $DIR/early_otherwise_branch.rs:12:5: 12:17
 +         _11 = discriminant((_3.1: std::option::Option<u32>)); // scope 0 at $DIR/early_otherwise_branch.rs:12:5: 12:17
 +         StorageLive(_12);                // scope 0 at $DIR/early_otherwise_branch.rs:12:5: 12:17
@@ -82,10 +82,8 @@
 +     bb4: {
           StorageDead(_3);                 // scope 0 at $DIR/early_otherwise_branch.rs:17:1: 17:2
           return;                          // scope 0 at $DIR/early_otherwise_branch.rs:17:2: 17:2
-      }
-  
--     bb7: {
--         unreachable;                     // scope 0 at $DIR/early_otherwise_branch.rs:15:14: 15:15
++     }
++ 
 +     bb5: {
 +         StorageDead(_12);                // scope 0 at $DIR/early_otherwise_branch.rs:12:5: 12:17
 +         switchInt(_8) -> [0_isize: bb3, 1_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch.rs:12:5: 12:17
diff --git a/src/test/mir-opt/early_otherwise_branch.rs b/src/test/mir-opt/early_otherwise_branch.rs
index e0ebcfeebfc..7be9fbd0326 100644
--- a/src/test/mir-opt/early_otherwise_branch.rs
+++ b/src/test/mir-opt/early_otherwise_branch.rs
@@ -1,4 +1,4 @@
-// compile-flags: -Z mir-opt-level=4 -Z unsound-mir-opts
+// unit-test: EarlyOtherwiseBranch
 // EMIT_MIR early_otherwise_branch.opt1.EarlyOtherwiseBranch.diff
 fn opt1(x: Option<u32>, y: Option<u32>) -> u32 {
     match (x, y) {
diff --git a/src/test/mir-opt/early_otherwise_branch_3_element_tuple.rs b/src/test/mir-opt/early_otherwise_branch_3_element_tuple.rs
index 8527c01d756..76055e1330f 100644
--- a/src/test/mir-opt/early_otherwise_branch_3_element_tuple.rs
+++ b/src/test/mir-opt/early_otherwise_branch_3_element_tuple.rs
@@ -1,4 +1,4 @@
-// compile-flags: -Z mir-opt-level=4 -Z unsound-mir-opts
+// unit-test: EarlyOtherwiseBranch
 
 // EMIT_MIR early_otherwise_branch_3_element_tuple.opt1.EarlyOtherwiseBranch.diff
 fn opt1(x: Option<u32>, y: Option<u32>, z: Option<u32>) -> u32 {
diff --git a/src/test/mir-opt/early_otherwise_branch_68867.try_sum.EarlyOtherwiseBranch.before-SimplifyConstCondition-final.after.diff b/src/test/mir-opt/early_otherwise_branch_68867.try_sum.EarlyOtherwiseBranch.before-SimplifyConstCondition-final.after.diff
index 592388e69a9..67ce0c2aabb 100644
--- a/src/test/mir-opt/early_otherwise_branch_68867.try_sum.EarlyOtherwiseBranch.before-SimplifyConstCondition-final.after.diff
+++ b/src/test/mir-opt/early_otherwise_branch_68867.try_sum.EarlyOtherwiseBranch.before-SimplifyConstCondition-final.after.diff
@@ -36,8 +36,19 @@
       let mut _31: f32;                    // in scope 0 at $DIR/early_otherwise_branch_68867.rs:25:50: 25:55
       let mut _32: !;                      // in scope 0 at $DIR/early_otherwise_branch_68867.rs:26:14: 26:28
       let mut _33: ();                     // in scope 0 at $DIR/early_otherwise_branch_68867.rs:26:25: 26:27
-+     let mut _34: isize;                  // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-+     let mut _35: bool;                   // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+      let mut _34: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+      let mut _35: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+      let mut _36: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+      let mut _37: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+      let mut _38: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+      let mut _39: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+      let mut _40: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+      let mut _41: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+      let mut _42: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+      let mut _43: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+      let mut _44: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+      let mut _45: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+      let mut _46: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
       scope 1 {
 -         debug one => _12;                // in scope 1 at $DIR/early_otherwise_branch_68867.rs:22:14: 22:17
 -         debug other => _13;              // in scope 1 at $DIR/early_otherwise_branch_68867.rs:22:24: 22:29
@@ -81,27 +92,26 @@
           StorageDead(_6);                 // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:23: 21:24
 -         StorageDead(_5);                 // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:23: 21:24
 +         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:23: 21:24
-          _11 = discriminant((*(_4.0: &ViewportPercentageLength))); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
--         switchInt(move _11) -> [0_isize: bb1, 1_isize: bb3, 2_isize: bb4, 3_isize: bb5, otherwise: bb11]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-+         StorageLive(_34);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-+         _34 = discriminant((*(_4.1: &ViewportPercentageLength))); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-+         StorageLive(_35);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-+         _35 = Ne(_11, move _34);         // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-+         StorageDead(_34);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-+         switchInt(move _35) -> [false: bb7, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+          StorageLive(_34);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+          _34 = move (_4.0: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+          _11 = discriminant((*_34));      // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+          StorageDead(_34);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+          switchInt(move _11) -> [0_isize: bb1, 1_isize: bb3, 2_isize: bb4, 3_isize: bb5, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
       }
   
       bb1: {
--         _7 = discriminant((*(_4.1: &ViewportPercentageLength))); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
--         switchInt(move _7) -> [0_isize: bb6, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
--     }
-- 
--     bb2: {
-+         StorageDead(_35);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:26:25: 26:27
+          StorageLive(_35);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+          _35 = move (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+          _7 = discriminant((*_35));       // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+          StorageDead(_35);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+          switchInt(move _7) -> [0_isize: bb6, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+      }
+  
+      bb2: {
           StorageLive(_33);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:26:25: 26:27
--         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:26:25: 26:27
+          nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:26:25: 26:27
           Deinit(_0);                      // scope 0 at $DIR/early_otherwise_branch_68867.rs:26:21: 26:28
--         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:26:21: 26:28
+          nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:26:21: 26:28
           discriminant(_0) = 1;            // scope 0 at $DIR/early_otherwise_branch_68867.rs:26:21: 26:28
           StorageDead(_33);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:26:27: 26:28
 -         StorageDead(_3);                 // scope 0 at $DIR/early_otherwise_branch_68867.rs:27:6: 27:7
@@ -111,105 +121,45 @@
           return;                          // scope 0 at $DIR/early_otherwise_branch_68867.rs:28:2: 28:2
       }
   
-+     bb2: {
-+         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:14: 22:17
-+         _15 = (((*(_4.0: &ViewportPercentageLength)) as Vw).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:14: 22:17
-+         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:24: 22:29
-+         _16 = (((*(_4.1: &ViewportPercentageLength)) as Vw).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:24: 22:29
-+         nop;                             // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:38: 22:49
-+         nop;                             // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:38: 22:41
-+         nop;                             // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:38: 22:41
-+         nop;                             // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:44: 22:49
-+         nop;                             // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:44: 22:49
-+         ((((_0 as Ok).0: ViewportPercentageLength) as Vw).0: f32) = Add(move _15, move _16); // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:38: 22:49
-+         nop;                             // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:48: 22:49
-+         nop;                             // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:48: 22:49
-+         Deinit(((_0 as Ok).0: ViewportPercentageLength)); // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:35: 22:50
-+         nop;                             // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:35: 22:50
-+         discriminant(((_0 as Ok).0: ViewportPercentageLength)) = 0; // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:35: 22:50
-+         nop;                             // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
-+         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
-+         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
-+         goto -> bb6;                     // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
-+     }
-+ 
       bb3: {
--         _8 = discriminant((*(_4.1: &ViewportPercentageLength))); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
--         switchInt(move _8) -> [1_isize: bb7, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-+         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:14: 23:17
-+         _20 = (((*(_4.0: &ViewportPercentageLength)) as Vh).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:14: 23:17
-+         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:24: 23:29
-+         _21 = (((*(_4.1: &ViewportPercentageLength)) as Vh).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:24: 23:29
-+         nop;                             // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:38: 23:49
-+         nop;                             // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:38: 23:41
-+         nop;                             // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:38: 23:41
-+         nop;                             // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:44: 23:49
-+         nop;                             // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:44: 23:49
-+         ((((_0 as Ok).0: ViewportPercentageLength) as Vh).0: f32) = Add(move _20, move _21); // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:38: 23:49
-+         nop;                             // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:48: 23:49
-+         nop;                             // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:48: 23:49
-+         Deinit(((_0 as Ok).0: ViewportPercentageLength)); // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:35: 23:50
-+         nop;                             // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:35: 23:50
-+         discriminant(((_0 as Ok).0: ViewportPercentageLength)) = 1; // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:35: 23:50
-+         nop;                             // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:49: 23:50
-+         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:49: 23:50
-+         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:49: 23:50
-+         goto -> bb6;                     // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:49: 23:50
+          StorageLive(_36);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+          _36 = move (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+          _8 = discriminant((*_36));       // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+          StorageDead(_36);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+          switchInt(move _8) -> [1_isize: bb7, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
       }
   
       bb4: {
--         _9 = discriminant((*(_4.1: &ViewportPercentageLength))); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
--         switchInt(move _9) -> [2_isize: bb8, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-+         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:16: 24:19
-+         _25 = (((*(_4.0: &ViewportPercentageLength)) as Vmin).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:16: 24:19
-+         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:28: 24:33
-+         _26 = (((*(_4.1: &ViewportPercentageLength)) as Vmin).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:28: 24:33
-+         nop;                             // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:44: 24:55
-+         nop;                             // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:44: 24:47
-+         nop;                             // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:44: 24:47
-+         nop;                             // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:50: 24:55
-+         nop;                             // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:50: 24:55
-+         ((((_0 as Ok).0: ViewportPercentageLength) as Vmin).0: f32) = Add(move _25, move _26); // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:44: 24:55
-+         nop;                             // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:54: 24:55
-+         nop;                             // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:54: 24:55
-+         Deinit(((_0 as Ok).0: ViewportPercentageLength)); // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:39: 24:56
-+         nop;                             // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:39: 24:56
-+         discriminant(((_0 as Ok).0: ViewportPercentageLength)) = 2; // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:39: 24:56
-+         nop;                             // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:55: 24:56
-+         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:55: 24:56
-+         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:55: 24:56
-+         goto -> bb6;                     // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:55: 24:56
+          StorageLive(_37);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+          _37 = move (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+          _9 = discriminant((*_37));       // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+          StorageDead(_37);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+          switchInt(move _9) -> [2_isize: bb8, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
       }
   
       bb5: {
--         _10 = discriminant((*(_4.1: &ViewportPercentageLength))); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
--         switchInt(move _10) -> [3_isize: bb9, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-+         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:16: 25:19
-+         _30 = (((*(_4.0: &ViewportPercentageLength)) as Vmax).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:16: 25:19
-+         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:28: 25:33
-+         _31 = (((*(_4.1: &ViewportPercentageLength)) as Vmax).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:28: 25:33
-+         nop;                             // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:44: 25:55
-+         nop;                             // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:44: 25:47
-+         nop;                             // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:44: 25:47
-+         nop;                             // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:50: 25:55
-+         nop;                             // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:50: 25:55
-+         ((((_0 as Ok).0: ViewportPercentageLength) as Vmax).0: f32) = Add(move _30, move _31); // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:44: 25:55
-+         nop;                             // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:54: 25:55
-+         nop;                             // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:54: 25:55
-+         Deinit(((_0 as Ok).0: ViewportPercentageLength)); // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:39: 25:56
-+         nop;                             // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:39: 25:56
-+         discriminant(((_0 as Ok).0: ViewportPercentageLength)) = 3; // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:39: 25:56
-+         nop;                             // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:55: 25:56
-+         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:55: 25:56
-+         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:55: 25:56
-+         goto -> bb6;                     // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:55: 25:56
+          StorageLive(_38);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+          _38 = move (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+          _10 = discriminant((*_38));      // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+          StorageDead(_38);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+          switchInt(move _10) -> [3_isize: bb9, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
       }
   
       bb6: {
 -         StorageLive(_12);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:14: 22:17
--         _12 = (((*(_4.0: &ViewportPercentageLength)) as Vw).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:14: 22:17
++         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:14: 22:17
+          StorageLive(_39);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:14: 22:17
+          _39 = move (_4.0: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:14: 22:17
+-         _12 = (((*_39) as Vw).0: f32);   // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:14: 22:17
++         _15 = (((*_39) as Vw).0: f32);   // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:14: 22:17
+          StorageDead(_39);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:24: 22:29
 -         StorageLive(_13);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:24: 22:29
--         _13 = (((*(_4.1: &ViewportPercentageLength)) as Vw).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:24: 22:29
++         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:24: 22:29
+          StorageLive(_40);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:24: 22:29
+          _40 = move (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:24: 22:29
+-         _13 = (((*_40) as Vw).0: f32);   // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:24: 22:29
++         _16 = (((*_40) as Vw).0: f32);   // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:24: 22:29
+          StorageDead(_40);                // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:38: 22:49
 -         StorageLive(_14);                // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:38: 22:49
 -         StorageLive(_15);                // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:38: 22:41
 -         _15 = _12;                       // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:38: 22:41
@@ -224,14 +174,38 @@
 -         StorageDead(_14);                // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
 -         StorageDead(_13);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
 -         StorageDead(_12);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
--         goto -> bb10;                    // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
--     }
-- 
--     bb7: {
++         nop;                             // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:38: 22:49
++         nop;                             // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:38: 22:41
++         nop;                             // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:38: 22:41
++         nop;                             // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:44: 22:49
++         nop;                             // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:44: 22:49
++         ((((_0 as Ok).0: ViewportPercentageLength) as Vw).0: f32) = Add(move _15, move _16); // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:38: 22:49
++         nop;                             // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:48: 22:49
++         nop;                             // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:48: 22:49
++         Deinit(((_0 as Ok).0: ViewportPercentageLength)); // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:35: 22:50
++         nop;                             // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:35: 22:50
++         discriminant(((_0 as Ok).0: ViewportPercentageLength)) = 0; // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:35: 22:50
++         nop;                             // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
++         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
++         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
+          goto -> bb10;                    // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
+      }
+  
+      bb7: {
 -         StorageLive(_17);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:14: 23:17
--         _17 = (((*(_4.0: &ViewportPercentageLength)) as Vh).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:14: 23:17
++         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:14: 23:17
+          StorageLive(_41);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:14: 23:17
+          _41 = move (_4.0: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:14: 23:17
+-         _17 = (((*_41) as Vh).0: f32);   // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:14: 23:17
++         _20 = (((*_41) as Vh).0: f32);   // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:14: 23:17
+          StorageDead(_41);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:24: 23:29
 -         StorageLive(_18);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:24: 23:29
--         _18 = (((*(_4.1: &ViewportPercentageLength)) as Vh).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:24: 23:29
++         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:24: 23:29
+          StorageLive(_42);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:24: 23:29
+          _42 = move (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:24: 23:29
+-         _18 = (((*_42) as Vh).0: f32);   // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:24: 23:29
++         _21 = (((*_42) as Vh).0: f32);   // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:24: 23:29
+          StorageDead(_42);                // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:38: 23:49
 -         StorageLive(_19);                // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:38: 23:49
 -         StorageLive(_20);                // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:38: 23:41
 -         _20 = _17;                       // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:38: 23:41
@@ -246,14 +220,38 @@
 -         StorageDead(_19);                // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:49: 23:50
 -         StorageDead(_18);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:49: 23:50
 -         StorageDead(_17);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:49: 23:50
--         goto -> bb10;                    // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:49: 23:50
--     }
-- 
--     bb8: {
++         nop;                             // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:38: 23:49
++         nop;                             // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:38: 23:41
++         nop;                             // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:38: 23:41
++         nop;                             // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:44: 23:49
++         nop;                             // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:44: 23:49
++         ((((_0 as Ok).0: ViewportPercentageLength) as Vh).0: f32) = Add(move _20, move _21); // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:38: 23:49
++         nop;                             // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:48: 23:49
++         nop;                             // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:48: 23:49
++         Deinit(((_0 as Ok).0: ViewportPercentageLength)); // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:35: 23:50
++         nop;                             // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:35: 23:50
++         discriminant(((_0 as Ok).0: ViewportPercentageLength)) = 1; // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:35: 23:50
++         nop;                             // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:49: 23:50
++         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:49: 23:50
++         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:49: 23:50
+          goto -> bb10;                    // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:49: 23:50
+      }
+  
+      bb8: {
 -         StorageLive(_22);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:16: 24:19
--         _22 = (((*(_4.0: &ViewportPercentageLength)) as Vmin).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:16: 24:19
++         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:16: 24:19
+          StorageLive(_43);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:16: 24:19
+          _43 = move (_4.0: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:16: 24:19
+-         _22 = (((*_43) as Vmin).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:16: 24:19
++         _25 = (((*_43) as Vmin).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:16: 24:19
+          StorageDead(_43);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:28: 24:33
 -         StorageLive(_23);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:28: 24:33
--         _23 = (((*(_4.1: &ViewportPercentageLength)) as Vmin).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:28: 24:33
++         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:28: 24:33
+          StorageLive(_44);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:28: 24:33
+          _44 = move (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:28: 24:33
+-         _23 = (((*_44) as Vmin).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:28: 24:33
++         _26 = (((*_44) as Vmin).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:28: 24:33
+          StorageDead(_44);                // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:44: 24:55
 -         StorageLive(_24);                // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:44: 24:55
 -         StorageLive(_25);                // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:44: 24:47
 -         _25 = _22;                       // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:44: 24:47
@@ -268,14 +266,38 @@
 -         StorageDead(_24);                // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:55: 24:56
 -         StorageDead(_23);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:55: 24:56
 -         StorageDead(_22);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:55: 24:56
--         goto -> bb10;                    // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:55: 24:56
--     }
-- 
--     bb9: {
++         nop;                             // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:44: 24:55
++         nop;                             // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:44: 24:47
++         nop;                             // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:44: 24:47
++         nop;                             // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:50: 24:55
++         nop;                             // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:50: 24:55
++         ((((_0 as Ok).0: ViewportPercentageLength) as Vmin).0: f32) = Add(move _25, move _26); // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:44: 24:55
++         nop;                             // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:54: 24:55
++         nop;                             // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:54: 24:55
++         Deinit(((_0 as Ok).0: ViewportPercentageLength)); // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:39: 24:56
++         nop;                             // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:39: 24:56
++         discriminant(((_0 as Ok).0: ViewportPercentageLength)) = 2; // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:39: 24:56
++         nop;                             // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:55: 24:56
++         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:55: 24:56
++         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:55: 24:56
+          goto -> bb10;                    // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:55: 24:56
+      }
+  
+      bb9: {
 -         StorageLive(_27);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:16: 25:19
--         _27 = (((*(_4.0: &ViewportPercentageLength)) as Vmax).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:16: 25:19
++         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:16: 25:19
+          StorageLive(_45);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:16: 25:19
+          _45 = move (_4.0: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:16: 25:19
+-         _27 = (((*_45) as Vmax).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:16: 25:19
++         _30 = (((*_45) as Vmax).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:16: 25:19
+          StorageDead(_45);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:28: 25:33
 -         StorageLive(_28);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:28: 25:33
--         _28 = (((*(_4.1: &ViewportPercentageLength)) as Vmax).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:28: 25:33
++         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:28: 25:33
+          StorageLive(_46);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:28: 25:33
+          _46 = move (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:28: 25:33
+-         _28 = (((*_46) as Vmax).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:28: 25:33
++         _31 = (((*_46) as Vmax).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:28: 25:33
+          StorageDead(_46);                // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:44: 25:55
 -         StorageLive(_29);                // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:44: 25:55
 -         StorageLive(_30);                // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:44: 25:47
 -         _30 = _27;                       // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:44: 25:47
@@ -290,10 +312,24 @@
 -         StorageDead(_29);                // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:55: 25:56
 -         StorageDead(_28);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:55: 25:56
 -         StorageDead(_27);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:55: 25:56
--         goto -> bb10;                    // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:55: 25:56
--     }
-- 
--     bb10: {
++         nop;                             // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:44: 25:55
++         nop;                             // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:44: 25:47
++         nop;                             // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:44: 25:47
++         nop;                             // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:50: 25:55
++         nop;                             // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:50: 25:55
++         ((((_0 as Ok).0: ViewportPercentageLength) as Vmax).0: f32) = Add(move _30, move _31); // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:44: 25:55
++         nop;                             // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:54: 25:55
++         nop;                             // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:54: 25:55
++         Deinit(((_0 as Ok).0: ViewportPercentageLength)); // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:39: 25:56
++         nop;                             // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:39: 25:56
++         discriminant(((_0 as Ok).0: ViewportPercentageLength)) = 3; // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:39: 25:56
++         nop;                             // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:55: 25:56
++         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:55: 25:56
++         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:55: 25:56
+          goto -> bb10;                    // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:55: 25:56
+      }
+  
+      bb10: {
           Deinit(_0);                      // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:5: 27:7
 -         ((_0 as Ok).0: ViewportPercentageLength) = move _3; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:5: 27:7
 +         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:5: 27:7
@@ -304,12 +340,5 @@
 +         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:28:1: 28:2
           return;                          // scope 0 at $DIR/early_otherwise_branch_68867.rs:28:2: 28:2
       }
-  
--     bb11: {
--         unreachable;                     // scope 0 at $DIR/early_otherwise_branch_68867.rs:28:2: 28:2
-+     bb7: {
-+         StorageDead(_35);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-+         switchInt(_11) -> [0_isize: bb2, 1_isize: bb3, 2_isize: bb4, 3_isize: bb5, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-      }
   }
   
diff --git a/src/test/mir-opt/early_otherwise_branch_68867.try_sum.EarlyOtherwiseBranch.diff b/src/test/mir-opt/early_otherwise_branch_68867.try_sum.EarlyOtherwiseBranch.diff
index 4cd34ba38ba..c2b00f915a4 100644
--- a/src/test/mir-opt/early_otherwise_branch_68867.try_sum.EarlyOtherwiseBranch.diff
+++ b/src/test/mir-opt/early_otherwise_branch_68867.try_sum.EarlyOtherwiseBranch.diff
@@ -36,8 +36,19 @@
       let mut _31: f32;                    // in scope 0 at $DIR/early_otherwise_branch_68867.rs:25:50: 25:55
       let mut _32: !;                      // in scope 0 at $DIR/early_otherwise_branch_68867.rs:26:14: 26:28
       let mut _33: ();                     // in scope 0 at $DIR/early_otherwise_branch_68867.rs:26:25: 26:27
-+     let mut _34: isize;                  // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-+     let mut _35: bool;                   // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+      let mut _34: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+      let mut _35: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+      let mut _36: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+      let mut _37: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+      let mut _38: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+      let mut _39: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+      let mut _40: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+      let mut _41: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+      let mut _42: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+      let mut _43: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+      let mut _44: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+      let mut _45: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+      let mut _46: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
       scope 1 {
           debug one => _12;                // in scope 1 at $DIR/early_otherwise_branch_68867.rs:22:14: 22:17
           debug other => _13;              // in scope 1 at $DIR/early_otherwise_branch_68867.rs:22:24: 22:29
@@ -67,27 +78,26 @@
           (_4.1: &ViewportPercentageLength) = move _6; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
           StorageDead(_6);                 // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:23: 21:24
           StorageDead(_5);                 // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:23: 21:24
-          _11 = discriminant((*(_4.0: &ViewportPercentageLength))); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
--         switchInt(move _11) -> [0_isize: bb1, 1_isize: bb3, 2_isize: bb4, 3_isize: bb5, otherwise: bb11]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-+         StorageLive(_34);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-+         _34 = discriminant((*(_4.1: &ViewportPercentageLength))); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-+         StorageLive(_35);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-+         _35 = Ne(_11, move _34);         // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-+         StorageDead(_34);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-+         switchInt(move _35) -> [false: bb7, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+          StorageLive(_34);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+          _34 = move (_4.0: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+          _11 = discriminant((*_34));      // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+          StorageDead(_34);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+          switchInt(move _11) -> [0_isize: bb1, 1_isize: bb3, 2_isize: bb4, 3_isize: bb5, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
       }
   
       bb1: {
--         _7 = discriminant((*(_4.1: &ViewportPercentageLength))); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
--         switchInt(move _7) -> [0_isize: bb6, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
--     }
-- 
--     bb2: {
-+         StorageDead(_35);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:26:25: 26:27
+          StorageLive(_35);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+          _35 = move (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+          _7 = discriminant((*_35));       // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+          StorageDead(_35);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+          switchInt(move _7) -> [0_isize: bb6, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+      }
+  
+      bb2: {
           StorageLive(_33);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:26:25: 26:27
--         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:26:25: 26:27
+          nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:26:25: 26:27
           Deinit(_0);                      // scope 0 at $DIR/early_otherwise_branch_68867.rs:26:21: 26:28
--         nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:26:21: 26:28
+          nop;                             // scope 0 at $DIR/early_otherwise_branch_68867.rs:26:21: 26:28
           discriminant(_0) = 1;            // scope 0 at $DIR/early_otherwise_branch_68867.rs:26:21: 26:28
           StorageDead(_33);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:26:27: 26:28
           StorageDead(_3);                 // scope 0 at $DIR/early_otherwise_branch_68867.rs:27:6: 27:7
@@ -95,27 +105,41 @@
           return;                          // scope 0 at $DIR/early_otherwise_branch_68867.rs:28:2: 28:2
       }
   
--     bb3: {
--         _8 = discriminant((*(_4.1: &ViewportPercentageLength))); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
--         switchInt(move _8) -> [1_isize: bb7, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
--     }
-- 
--     bb4: {
--         _9 = discriminant((*(_4.1: &ViewportPercentageLength))); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
--         switchInt(move _9) -> [2_isize: bb8, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
--     }
-- 
--     bb5: {
--         _10 = discriminant((*(_4.1: &ViewportPercentageLength))); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
--         switchInt(move _10) -> [3_isize: bb9, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
--     }
-- 
--     bb6: {
-+     bb2: {
+      bb3: {
+          StorageLive(_36);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+          _36 = move (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+          _8 = discriminant((*_36));       // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+          StorageDead(_36);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+          switchInt(move _8) -> [1_isize: bb7, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+      }
+  
+      bb4: {
+          StorageLive(_37);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+          _37 = move (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+          _9 = discriminant((*_37));       // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+          StorageDead(_37);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+          switchInt(move _9) -> [2_isize: bb8, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+      }
+  
+      bb5: {
+          StorageLive(_38);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+          _38 = move (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+          _10 = discriminant((*_38));      // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+          StorageDead(_38);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+          switchInt(move _10) -> [3_isize: bb9, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+      }
+  
+      bb6: {
           StorageLive(_12);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:14: 22:17
-          _12 = (((*(_4.0: &ViewportPercentageLength)) as Vw).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:14: 22:17
+          StorageLive(_39);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:14: 22:17
+          _39 = move (_4.0: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:14: 22:17
+          _12 = (((*_39) as Vw).0: f32);   // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:14: 22:17
+          StorageDead(_39);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:24: 22:29
           StorageLive(_13);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:24: 22:29
-          _13 = (((*(_4.1: &ViewportPercentageLength)) as Vw).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:24: 22:29
+          StorageLive(_40);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:24: 22:29
+          _40 = move (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:24: 22:29
+          _13 = (((*_40) as Vw).0: f32);   // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:24: 22:29
+          StorageDead(_40);                // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:38: 22:49
           StorageLive(_14);                // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:38: 22:49
           StorageLive(_15);                // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:38: 22:41
           _15 = _12;                       // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:38: 22:41
@@ -130,16 +154,20 @@
           StorageDead(_14);                // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
           StorageDead(_13);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
           StorageDead(_12);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
--         goto -> bb10;                    // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
-+         goto -> bb6;                     // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
+          goto -> bb10;                    // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
       }
   
--     bb7: {
-+     bb3: {
+      bb7: {
           StorageLive(_17);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:14: 23:17
-          _17 = (((*(_4.0: &ViewportPercentageLength)) as Vh).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:14: 23:17
+          StorageLive(_41);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:14: 23:17
+          _41 = move (_4.0: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:14: 23:17
+          _17 = (((*_41) as Vh).0: f32);   // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:14: 23:17
+          StorageDead(_41);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:24: 23:29
           StorageLive(_18);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:24: 23:29
-          _18 = (((*(_4.1: &ViewportPercentageLength)) as Vh).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:24: 23:29
+          StorageLive(_42);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:24: 23:29
+          _42 = move (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:24: 23:29
+          _18 = (((*_42) as Vh).0: f32);   // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:24: 23:29
+          StorageDead(_42);                // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:38: 23:49
           StorageLive(_19);                // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:38: 23:49
           StorageLive(_20);                // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:38: 23:41
           _20 = _17;                       // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:38: 23:41
@@ -154,16 +182,20 @@
           StorageDead(_19);                // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:49: 23:50
           StorageDead(_18);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:49: 23:50
           StorageDead(_17);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:49: 23:50
--         goto -> bb10;                    // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:49: 23:50
-+         goto -> bb6;                     // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:49: 23:50
+          goto -> bb10;                    // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:49: 23:50
       }
   
--     bb8: {
-+     bb4: {
+      bb8: {
           StorageLive(_22);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:16: 24:19
-          _22 = (((*(_4.0: &ViewportPercentageLength)) as Vmin).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:16: 24:19
+          StorageLive(_43);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:16: 24:19
+          _43 = move (_4.0: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:16: 24:19
+          _22 = (((*_43) as Vmin).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:16: 24:19
+          StorageDead(_43);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:28: 24:33
           StorageLive(_23);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:28: 24:33
-          _23 = (((*(_4.1: &ViewportPercentageLength)) as Vmin).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:28: 24:33
+          StorageLive(_44);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:28: 24:33
+          _44 = move (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:28: 24:33
+          _23 = (((*_44) as Vmin).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:28: 24:33
+          StorageDead(_44);                // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:44: 24:55
           StorageLive(_24);                // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:44: 24:55
           StorageLive(_25);                // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:44: 24:47
           _25 = _22;                       // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:44: 24:47
@@ -178,16 +210,20 @@
           StorageDead(_24);                // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:55: 24:56
           StorageDead(_23);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:55: 24:56
           StorageDead(_22);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:55: 24:56
--         goto -> bb10;                    // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:55: 24:56
-+         goto -> bb6;                     // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:55: 24:56
+          goto -> bb10;                    // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:55: 24:56
       }
   
--     bb9: {
-+     bb5: {
+      bb9: {
           StorageLive(_27);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:16: 25:19
-          _27 = (((*(_4.0: &ViewportPercentageLength)) as Vmax).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:16: 25:19
+          StorageLive(_45);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:16: 25:19
+          _45 = move (_4.0: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:16: 25:19
+          _27 = (((*_45) as Vmax).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:16: 25:19
+          StorageDead(_45);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:28: 25:33
           StorageLive(_28);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:28: 25:33
-          _28 = (((*(_4.1: &ViewportPercentageLength)) as Vmax).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:28: 25:33
+          StorageLive(_46);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:28: 25:33
+          _46 = move (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:28: 25:33
+          _28 = (((*_46) as Vmax).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:28: 25:33
+          StorageDead(_46);                // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:44: 25:55
           StorageLive(_29);                // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:44: 25:55
           StorageLive(_30);                // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:44: 25:47
           _30 = _27;                       // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:44: 25:47
@@ -202,12 +238,10 @@
           StorageDead(_29);                // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:55: 25:56
           StorageDead(_28);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:55: 25:56
           StorageDead(_27);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:55: 25:56
--         goto -> bb10;                    // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:55: 25:56
-+         goto -> bb6;                     // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:55: 25:56
+          goto -> bb10;                    // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:55: 25:56
       }
   
--     bb10: {
-+     bb6: {
+      bb10: {
           Deinit(_0);                      // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:5: 27:7
           ((_0 as Ok).0: ViewportPercentageLength) = move _3; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:5: 27:7
           discriminant(_0) = 0;            // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:5: 27:7
@@ -215,12 +249,5 @@
           StorageDead(_4);                 // scope 0 at $DIR/early_otherwise_branch_68867.rs:28:1: 28:2
           return;                          // scope 0 at $DIR/early_otherwise_branch_68867.rs:28:2: 28:2
       }
-  
--     bb11: {
--         unreachable;                     // scope 0 at $DIR/early_otherwise_branch_68867.rs:28:2: 28:2
-+     bb7: {
-+         StorageDead(_35);                // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-+         switchInt(_11) -> [0_isize: bb2, 1_isize: bb3, 2_isize: bb4, 3_isize: bb5, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-      }
   }
   
diff --git a/src/test/mir-opt/early_otherwise_branch_noopt.noopt1.EarlyOtherwiseBranch.diff b/src/test/mir-opt/early_otherwise_branch_noopt.noopt1.EarlyOtherwiseBranch.diff
index 6adc5194aec..848f2feb321 100644
--- a/src/test/mir-opt/early_otherwise_branch_noopt.noopt1.EarlyOtherwiseBranch.diff
+++ b/src/test/mir-opt/early_otherwise_branch_noopt.noopt1.EarlyOtherwiseBranch.diff
@@ -38,25 +38,29 @@
           StorageDead(_5);                 // scope 0 at $DIR/early_otherwise_branch_noopt.rs:8:16: 8:17
           StorageDead(_4);                 // scope 0 at $DIR/early_otherwise_branch_noopt.rs:8:16: 8:17
           _8 = discriminant((_3.0: std::option::Option<u32>)); // scope 0 at $DIR/early_otherwise_branch_noopt.rs:8:11: 8:17
-          switchInt(move _8) -> [0_isize: bb1, otherwise: bb3]; // scope 0 at $DIR/early_otherwise_branch_noopt.rs:8:5: 8:17
+          switchInt(move _8) -> [0_isize: bb1, 1_isize: bb4, otherwise: bb3]; // scope 0 at $DIR/early_otherwise_branch_noopt.rs:8:5: 8:17
       }
   
       bb1: {
           _6 = discriminant((_3.1: std::option::Option<u32>)); // scope 0 at $DIR/early_otherwise_branch_noopt.rs:8:11: 8:17
-          switchInt(move _6) -> [0_isize: bb2, otherwise: bb6]; // scope 0 at $DIR/early_otherwise_branch_noopt.rs:8:5: 8:17
+          switchInt(move _6) -> [0_isize: bb2, 1_isize: bb7, otherwise: bb3]; // scope 0 at $DIR/early_otherwise_branch_noopt.rs:8:5: 8:17
       }
   
       bb2: {
           _0 = const 3_u32;                // scope 0 at $DIR/early_otherwise_branch_noopt.rs:12:25: 12:26
-          goto -> bb7;                     // scope 0 at $DIR/early_otherwise_branch_noopt.rs:12:25: 12:26
+          goto -> bb8;                     // scope 0 at $DIR/early_otherwise_branch_noopt.rs:12:25: 12:26
       }
   
       bb3: {
-          _7 = discriminant((_3.1: std::option::Option<u32>)); // scope 0 at $DIR/early_otherwise_branch_noopt.rs:8:11: 8:17
-          switchInt(move _7) -> [0_isize: bb5, otherwise: bb4]; // scope 0 at $DIR/early_otherwise_branch_noopt.rs:8:5: 8:17
+          unreachable;                     // scope 0 at $DIR/early_otherwise_branch_noopt.rs:8:11: 8:17
       }
   
       bb4: {
+          _7 = discriminant((_3.1: std::option::Option<u32>)); // scope 0 at $DIR/early_otherwise_branch_noopt.rs:8:11: 8:17
+          switchInt(move _7) -> [0_isize: bb6, 1_isize: bb5, otherwise: bb3]; // scope 0 at $DIR/early_otherwise_branch_noopt.rs:8:5: 8:17
+      }
+  
+      bb5: {
           StorageLive(_9);                 // scope 0 at $DIR/early_otherwise_branch_noopt.rs:9:15: 9:16
           _9 = (((_3.0: std::option::Option<u32>) as Some).0: u32); // scope 0 at $DIR/early_otherwise_branch_noopt.rs:9:15: 9:16
           StorageLive(_10);                // scope 0 at $DIR/early_otherwise_branch_noopt.rs:9:24: 9:25
@@ -64,26 +68,26 @@
           _0 = const 0_u32;                // scope 1 at $DIR/early_otherwise_branch_noopt.rs:9:31: 9:32
           StorageDead(_10);                // scope 0 at $DIR/early_otherwise_branch_noopt.rs:9:31: 9:32
           StorageDead(_9);                 // scope 0 at $DIR/early_otherwise_branch_noopt.rs:9:31: 9:32
-          goto -> bb7;                     // scope 0 at $DIR/early_otherwise_branch_noopt.rs:9:31: 9:32
+          goto -> bb8;                     // scope 0 at $DIR/early_otherwise_branch_noopt.rs:9:31: 9:32
       }
   
-      bb5: {
+      bb6: {
           StorageLive(_11);                // scope 0 at $DIR/early_otherwise_branch_noopt.rs:10:15: 10:16
           _11 = (((_3.0: std::option::Option<u32>) as Some).0: u32); // scope 0 at $DIR/early_otherwise_branch_noopt.rs:10:15: 10:16
           _0 = const 1_u32;                // scope 2 at $DIR/early_otherwise_branch_noopt.rs:10:28: 10:29
           StorageDead(_11);                // scope 0 at $DIR/early_otherwise_branch_noopt.rs:10:28: 10:29
-          goto -> bb7;                     // scope 0 at $DIR/early_otherwise_branch_noopt.rs:10:28: 10:29
+          goto -> bb8;                     // scope 0 at $DIR/early_otherwise_branch_noopt.rs:10:28: 10:29
       }
   
-      bb6: {
+      bb7: {
           StorageLive(_12);                // scope 0 at $DIR/early_otherwise_branch_noopt.rs:11:21: 11:22
           _12 = (((_3.1: std::option::Option<u32>) as Some).0: u32); // scope 0 at $DIR/early_otherwise_branch_noopt.rs:11:21: 11:22
           _0 = const 2_u32;                // scope 3 at $DIR/early_otherwise_branch_noopt.rs:11:28: 11:29
           StorageDead(_12);                // scope 0 at $DIR/early_otherwise_branch_noopt.rs:11:28: 11:29
-          goto -> bb7;                     // scope 0 at $DIR/early_otherwise_branch_noopt.rs:11:28: 11:29
+          goto -> bb8;                     // scope 0 at $DIR/early_otherwise_branch_noopt.rs:11:28: 11:29
       }
   
-      bb7: {
+      bb8: {
           StorageDead(_3);                 // scope 0 at $DIR/early_otherwise_branch_noopt.rs:14:1: 14:2
           return;                          // scope 0 at $DIR/early_otherwise_branch_noopt.rs:14:2: 14:2
       }
diff --git a/src/test/mir-opt/early_otherwise_branch_noopt.rs b/src/test/mir-opt/early_otherwise_branch_noopt.rs
index 1f8c59df35f..ef766bbd4a6 100644
--- a/src/test/mir-opt/early_otherwise_branch_noopt.rs
+++ b/src/test/mir-opt/early_otherwise_branch_noopt.rs
@@ -1,4 +1,4 @@
-// compile-flags: -Z mir-opt-level=4 -Zunsound-mir-opts
+// unit-test: EarlyOtherwiseBranch
 
 // must not optimize as it does not follow the pattern of
 // left and right hand side being the same variant
diff --git a/src/test/mir-opt/early_otherwise_branch_soundness.no_deref_ptr.EarlyOtherwiseBranch.diff b/src/test/mir-opt/early_otherwise_branch_soundness.no_deref_ptr.EarlyOtherwiseBranch.diff
index a2722660662..7d42c772f16 100644
--- a/src/test/mir-opt/early_otherwise_branch_soundness.no_deref_ptr.EarlyOtherwiseBranch.diff
+++ b/src/test/mir-opt/early_otherwise_branch_soundness.no_deref_ptr.EarlyOtherwiseBranch.diff
@@ -19,7 +19,7 @@
   
       bb1: {
           _0 = const 0_i32;                // scope 0 at $DIR/early_otherwise_branch_soundness.rs:25:14: 25:15
-          return;                          // scope 0 at $DIR/early_otherwise_branch_soundness.rs:25:14: 25:15
+          goto -> bb5;                     // scope 0 at $DIR/early_otherwise_branch_soundness.rs:25:14: 25:15
       }
   
       bb2: {
@@ -29,7 +29,7 @@
   
       bb3: {
           _0 = const 0_i32;                // scope 0 at $DIR/early_otherwise_branch_soundness.rs:23:18: 23:19
-          return;                          // scope 0 at $DIR/early_otherwise_branch_soundness.rs:23:18: 23:19
+          goto -> bb5;                     // scope 0 at $DIR/early_otherwise_branch_soundness.rs:23:18: 23:19
       }
   
       bb4: {
@@ -37,7 +37,11 @@
           _5 = (((*_2) as Some).0: i32);   // scope 0 at $DIR/early_otherwise_branch_soundness.rs:22:18: 22:19
           _0 = _5;                         // scope 1 at $DIR/early_otherwise_branch_soundness.rs:22:24: 22:25
           StorageDead(_5);                 // scope 0 at $DIR/early_otherwise_branch_soundness.rs:22:24: 22:25
-          return;                          // scope 0 at $DIR/early_otherwise_branch_soundness.rs:22:24: 22:25
+          goto -> bb5;                     // scope 0 at $DIR/early_otherwise_branch_soundness.rs:22:24: 22:25
+      }
+  
+      bb5: {
+          return;                          // scope 0 at $DIR/early_otherwise_branch_soundness.rs:27:2: 27:2
       }
   }
   
diff --git a/src/test/mir-opt/early_otherwise_branch_soundness.no_downcast.EarlyOtherwiseBranch.diff b/src/test/mir-opt/early_otherwise_branch_soundness.no_downcast.EarlyOtherwiseBranch.diff
index 56b7c9a2db4..1efaba044ec 100644
--- a/src/test/mir-opt/early_otherwise_branch_soundness.no_downcast.EarlyOtherwiseBranch.diff
+++ b/src/test/mir-opt/early_otherwise_branch_soundness.no_downcast.EarlyOtherwiseBranch.diff
@@ -6,6 +6,7 @@
       let mut _0: u32;                     // return place in scope 0 at $DIR/early_otherwise_branch_soundness.rs:12:26: 12:29
       let mut _2: isize;                   // in scope 0 at $DIR/early_otherwise_branch_soundness.rs:13:20: 13:30
       let mut _3: isize;                   // in scope 0 at $DIR/early_otherwise_branch_soundness.rs:13:12: 13:31
+      let mut _4: &E;                      // in scope 0 at $DIR/early_otherwise_branch_soundness.rs:12:16: 12:17
   
       bb0: {
           _3 = discriminant((*_1));        // scope 0 at $DIR/early_otherwise_branch_soundness.rs:13:12: 13:31
@@ -13,18 +14,25 @@
       }
   
       bb1: {
-          _2 = discriminant((*(((*_1) as Some).0: &E))); // scope 0 at $DIR/early_otherwise_branch_soundness.rs:13:12: 13:31
+          StorageLive(_4);                 // scope 0 at $DIR/early_otherwise_branch_soundness.rs:13:12: 13:31
+          _4 = move (((*_1) as Some).0: &E); // scope 0 at $DIR/early_otherwise_branch_soundness.rs:13:12: 13:31
+          _2 = discriminant((*_4));        // scope 0 at $DIR/early_otherwise_branch_soundness.rs:13:12: 13:31
+          StorageDead(_4);                 // scope 0 at $DIR/early_otherwise_branch_soundness.rs:13:12: 13:31
           switchInt(move _2) -> [1_isize: bb2, otherwise: bb3]; // scope 0 at $DIR/early_otherwise_branch_soundness.rs:13:12: 13:31
       }
   
       bb2: {
           _0 = const 1_u32;                // scope 0 at $DIR/early_otherwise_branch_soundness.rs:13:38: 13:39
-          return;                          // scope 0 at $DIR/early_otherwise_branch_soundness.rs:13:5: 13:52
+          goto -> bb4;                     // scope 0 at $DIR/early_otherwise_branch_soundness.rs:13:5: 13:52
       }
   
       bb3: {
           _0 = const 2_u32;                // scope 0 at $DIR/early_otherwise_branch_soundness.rs:13:49: 13:50
-          return;                          // scope 0 at $DIR/early_otherwise_branch_soundness.rs:13:5: 13:52
+          goto -> bb4;                     // scope 0 at $DIR/early_otherwise_branch_soundness.rs:13:5: 13:52
+      }
+  
+      bb4: {
+          return;                          // scope 0 at $DIR/early_otherwise_branch_soundness.rs:14:2: 14:2
       }
   }
   
diff --git a/src/test/mir-opt/early_otherwise_branch_soundness.rs b/src/test/mir-opt/early_otherwise_branch_soundness.rs
index d2513213d79..cd458923245 100644
--- a/src/test/mir-opt/early_otherwise_branch_soundness.rs
+++ b/src/test/mir-opt/early_otherwise_branch_soundness.rs
@@ -1,4 +1,4 @@
-// compile-flags: -Z mir-opt-level=4 -Zunsound-mir-opts
+// unit-test: EarlyOtherwiseBranch
 
 // Tests various cases that the `early_otherwise_branch` opt should *not* optimize
 
diff --git a/src/test/mir-opt/if-condition-int.rs b/src/test/mir-opt/if-condition-int.rs
index b34389a0ab5..398311e6bb8 100644
--- a/src/test/mir-opt/if-condition-int.rs
+++ b/src/test/mir-opt/if-condition-int.rs
@@ -1,4 +1,4 @@
-// compile-flags: -O
+// unit-test: SimplifyComparisonIntegral
 // EMIT_MIR if_condition_int.opt_u32.SimplifyComparisonIntegral.diff
 // EMIT_MIR if_condition_int.opt_negative.SimplifyComparisonIntegral.diff
 // EMIT_MIR if_condition_int.opt_char.SimplifyComparisonIntegral.diff
diff --git a/src/test/mir-opt/inline/inline_closure_captures.foo.Inline.after.mir b/src/test/mir-opt/inline/inline_closure_captures.foo.Inline.after.mir
index 337f0871843..75e37092ff3 100644
--- a/src/test/mir-opt/inline/inline_closure_captures.foo.Inline.after.mir
+++ b/src/test/mir-opt/inline/inline_closure_captures.foo.Inline.after.mir
@@ -19,6 +19,8 @@ fn foo(_1: T, _2: i32) -> (i32, T) {
             debug t => (*((*_6).1: &T)); // in scope 2 at $DIR/inline-closure-captures.rs:10:17: 10:18
             let mut _10: i32;            // in scope 2 at $DIR/inline-closure-captures.rs:11:19: 11:20
             let mut _11: T;              // in scope 2 at $DIR/inline-closure-captures.rs:11:22: 11:23
+            let mut _12: &i32;           // in scope 2 at $DIR/inline-closure-captures.rs:11:13: 11:24
+            let mut _13: &T;             // in scope 2 at $DIR/inline-closure-captures.rs:11:13: 11:24
         }
     }
 
@@ -43,9 +45,15 @@ fn foo(_1: T, _2: i32) -> (i32, T) {
         StorageLive(_9);                 // scope 1 at $DIR/inline-closure-captures.rs:12:5: 12:9
         _9 = move (_7.0: i32);           // scope 1 at $DIR/inline-closure-captures.rs:12:5: 12:9
         StorageLive(_10);                // scope 2 at $DIR/inline-closure-captures.rs:11:19: 11:20
-        _10 = (*((*_6).0: &i32));        // scope 2 at $DIR/inline-closure-captures.rs:11:19: 11:20
+        StorageLive(_12);                // scope 2 at $DIR/inline-closure-captures.rs:11:19: 11:20
+        _12 = move ((*_6).0: &i32);      // scope 2 at $DIR/inline-closure-captures.rs:11:19: 11:20
+        _10 = (*_12);                    // scope 2 at $DIR/inline-closure-captures.rs:11:19: 11:20
+        StorageDead(_12);                // scope 2 at $DIR/inline-closure-captures.rs:11:22: 11:23
         StorageLive(_11);                // scope 2 at $DIR/inline-closure-captures.rs:11:22: 11:23
-        _11 = (*((*_6).1: &T));          // scope 2 at $DIR/inline-closure-captures.rs:11:22: 11:23
+        StorageLive(_13);                // scope 2 at $DIR/inline-closure-captures.rs:11:22: 11:23
+        _13 = move ((*_6).1: &T);        // scope 2 at $DIR/inline-closure-captures.rs:11:22: 11:23
+        _11 = (*_13);                    // scope 2 at $DIR/inline-closure-captures.rs:11:22: 11:23
+        StorageDead(_13);                // scope 2 at $DIR/inline-closure-captures.rs:11:18: 11:24
         Deinit(_0);                      // scope 2 at $DIR/inline-closure-captures.rs:11:18: 11:24
         (_0.0: i32) = move _10;          // scope 2 at $DIR/inline-closure-captures.rs:11:18: 11:24
         (_0.1: T) = move _11;            // scope 2 at $DIR/inline-closure-captures.rs:11:18: 11:24
diff --git a/src/test/mir-opt/inline/inline_diverging.h.Inline.diff b/src/test/mir-opt/inline/inline_diverging.h.Inline.diff
index bea33073366..3b890e4be2e 100644
--- a/src/test/mir-opt/inline/inline_diverging.h.Inline.diff
+++ b/src/test/mir-opt/inline/inline_diverging.h.Inline.diff
@@ -4,21 +4,22 @@
   fn h() -> () {
       let mut _0: ();                      // return place in scope 0 at $DIR/inline-diverging.rs:21:12: 21:12
       let _1: (!, !);                      // in scope 0 at $DIR/inline-diverging.rs:22:5: 22:22
-+     let mut _2: fn() -> ! {sleep};       // in scope 0 at $DIR/inline-diverging.rs:22:5: 22:22
-+     let mut _9: ();                      // in scope 0 at $DIR/inline-diverging.rs:27:13: 27:16
-+     let mut _10: ();                     // in scope 0 at $DIR/inline-diverging.rs:28:13: 28:16
++     let mut _2: (!, !);                  // in scope 0 at $DIR/inline-diverging.rs:22:5: 22:22
++     let mut _3: fn() -> ! {sleep};       // in scope 0 at $DIR/inline-diverging.rs:22:5: 22:22
++     let mut _10: ();                     // in scope 0 at $DIR/inline-diverging.rs:27:13: 27:16
++     let mut _11: ();                     // in scope 0 at $DIR/inline-diverging.rs:28:13: 28:16
 +     scope 1 (inlined call_twice::<!, fn() -> ! {sleep}>) { // at $DIR/inline-diverging.rs:22:5: 22:22
-+         debug f => _2;                   // in scope 1 at $DIR/inline-diverging.rs:26:36: 26:37
-+         let _3: !;                       // in scope 1 at $DIR/inline-diverging.rs:27:9: 27:10
-+         let mut _4: &fn() -> ! {sleep};  // in scope 1 at $DIR/inline-diverging.rs:27:13: 27:14
-+         let mut _6: &fn() -> ! {sleep};  // in scope 1 at $DIR/inline-diverging.rs:28:13: 28:14
-+         let mut _7: !;                   // in scope 1 at $DIR/inline-diverging.rs:29:6: 29:7
-+         let mut _8: !;                   // in scope 1 at $DIR/inline-diverging.rs:29:9: 29:10
++         debug f => _3;                   // in scope 1 at $DIR/inline-diverging.rs:26:36: 26:37
++         let _4: !;                       // in scope 1 at $DIR/inline-diverging.rs:27:9: 27:10
++         let mut _5: &fn() -> ! {sleep};  // in scope 1 at $DIR/inline-diverging.rs:27:13: 27:14
++         let mut _7: &fn() -> ! {sleep};  // in scope 1 at $DIR/inline-diverging.rs:28:13: 28:14
++         let mut _8: !;                   // in scope 1 at $DIR/inline-diverging.rs:29:6: 29:7
++         let mut _9: !;                   // in scope 1 at $DIR/inline-diverging.rs:29:9: 29:10
 +         scope 2 {
-+             debug a => _3;               // in scope 2 at $DIR/inline-diverging.rs:27:9: 27:10
-+             let _5: !;                   // in scope 2 at $DIR/inline-diverging.rs:28:9: 28:10
++             debug a => _4;               // in scope 2 at $DIR/inline-diverging.rs:27:9: 27:10
++             let _6: !;                   // in scope 2 at $DIR/inline-diverging.rs:28:9: 28:10
 +             scope 3 {
-+                 debug b => _5;           // in scope 3 at $DIR/inline-diverging.rs:28:9: 28:10
++                 debug b => _6;           // in scope 3 at $DIR/inline-diverging.rs:28:9: 28:10
 +             }
 +             scope 6 (inlined <fn() -> ! {sleep} as Fn<()>>::call - shim(fn() -> ! {sleep})) { // at $DIR/inline-diverging.rs:28:13: 28:16
 +                 scope 7 (inlined sleep) { // at $SRC_DIR/core/src/ops/function.rs:LL:COL
@@ -33,27 +34,25 @@
   
       bb0: {
           StorageLive(_1);                 // scope 0 at $DIR/inline-diverging.rs:22:5: 22:22
--         _1 = call_twice::<!, fn() -> ! {sleep}>(sleep) -> bb1; // scope 0 at $DIR/inline-diverging.rs:22:5: 22:22
+-         call_twice::<!, fn() -> ! {sleep}>(sleep); // scope 0 at $DIR/inline-diverging.rs:22:5: 22:22
 +         StorageLive(_2);                 // scope 0 at $DIR/inline-diverging.rs:22:5: 22:22
-+         _2 = sleep;                      // scope 0 at $DIR/inline-diverging.rs:22:5: 22:22
++         StorageLive(_3);                 // scope 0 at $DIR/inline-diverging.rs:22:5: 22:22
++         _3 = sleep;                      // scope 0 at $DIR/inline-diverging.rs:22:5: 22:22
                                            // mir::Constant
 -                                          // + span: $DIR/inline-diverging.rs:22:5: 22:15
 -                                          // + literal: Const { ty: fn(fn() -> ! {sleep}) -> (!, !) {call_twice::<!, fn() -> ! {sleep}>}, val: Value(Scalar(<ZST>)) }
 -                                          // mir::Constant
                                            // + span: $DIR/inline-diverging.rs:22:16: 22:21
                                            // + literal: Const { ty: fn() -> ! {sleep}, val: Value(Scalar(<ZST>)) }
-+         StorageLive(_3);                 // scope 1 at $DIR/inline-diverging.rs:27:9: 27:10
-+         StorageLive(_4);                 // scope 1 at $DIR/inline-diverging.rs:27:13: 27:14
-+         _4 = &_2;                        // scope 1 at $DIR/inline-diverging.rs:27:13: 27:14
-+         StorageLive(_9);                 // scope 1 at $DIR/inline-diverging.rs:27:13: 27:16
-+         _9 = const ();                   // scope 1 at $DIR/inline-diverging.rs:27:13: 27:16
++         StorageLive(_4);                 // scope 1 at $DIR/inline-diverging.rs:27:9: 27:10
++         StorageLive(_5);                 // scope 1 at $DIR/inline-diverging.rs:27:13: 27:14
++         _5 = &_3;                        // scope 1 at $DIR/inline-diverging.rs:27:13: 27:14
++         StorageLive(_10);                // scope 1 at $DIR/inline-diverging.rs:27:13: 27:16
++         _10 = const ();                  // scope 1 at $DIR/inline-diverging.rs:27:13: 27:16
 +         goto -> bb1;                     // scope 5 at $DIR/inline-diverging.rs:39:5: 39:12
-      }
-  
-      bb1: {
--         StorageDead(_1);                 // scope 0 at $DIR/inline-diverging.rs:22:22: 22:23
--         _0 = const ();                   // scope 0 at $DIR/inline-diverging.rs:21:12: 23:2
--         return;                          // scope 0 at $DIR/inline-diverging.rs:23:2: 23:2
++     }
++ 
++     bb1: {
 +         goto -> bb1;                     // scope 5 at $DIR/inline-diverging.rs:39:5: 39:12
       }
   }
diff --git a/src/test/mir-opt/issue_72181_1.main.mir_map.0.mir b/src/test/mir-opt/issue_72181_1.main.mir_map.0.mir
index 5fb57c285be..4aff4445158 100644
--- a/src/test/mir-opt/issue_72181_1.main.mir_map.0.mir
+++ b/src/test/mir-opt/issue_72181_1.main.mir_map.0.mir
@@ -21,7 +21,7 @@ fn main() -> () {
         StorageLive(_2);                 // scope 0 at $DIR/issue-72181-1.rs:16:9: 16:10
         StorageLive(_3);                 // scope 2 at $DIR/issue-72181-1.rs:17:41: 17:43
         _3 = ();                         // scope 2 at $DIR/issue-72181-1.rs:17:41: 17:43
-        _2 = transmute::<(), Void>(move _3) -> [return: bb1, unwind: bb4]; // scope 2 at $DIR/issue-72181-1.rs:17:9: 17:44
+        transmute::<(), Void>(move _3) -> bb4; // scope 2 at $DIR/issue-72181-1.rs:17:9: 17:44
                                          // mir::Constant
                                          // + span: $DIR/issue-72181-1.rs:17:9: 17:40
                                          // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(()) -> Void {transmute::<(), Void>}, val: Value(Scalar(<ZST>)) }
diff --git a/src/test/mir-opt/lower_array_len.array_bound.NormalizeArrayLen.diff b/src/test/mir-opt/lower_array_len.array_bound.NormalizeArrayLen.diff
index 2e034670186..d6c1c92cd91 100644
--- a/src/test/mir-opt/lower_array_len.array_bound.NormalizeArrayLen.diff
+++ b/src/test/mir-opt/lower_array_len.array_bound.NormalizeArrayLen.diff
@@ -64,9 +64,5 @@
           StorageDead(_3);                 // scope 0 at $DIR/lower_array_len.rs:11:5: 11:6
           return;                          // scope 0 at $DIR/lower_array_len.rs:12:2: 12:2
       }
-  
-      bb6 (cleanup): {
-          resume;                          // scope 0 at $DIR/lower_array_len.rs:6:1: 12:2
-      }
   }
   
diff --git a/src/test/mir-opt/lower_array_len.array_bound_mut.NormalizeArrayLen.diff b/src/test/mir-opt/lower_array_len.array_bound_mut.NormalizeArrayLen.diff
index 6aa77a9ed60..11fc20aa693 100644
--- a/src/test/mir-opt/lower_array_len.array_bound_mut.NormalizeArrayLen.diff
+++ b/src/test/mir-opt/lower_array_len.array_bound_mut.NormalizeArrayLen.diff
@@ -77,9 +77,5 @@
           StorageDead(_3);                 // scope 0 at $DIR/lower_array_len.rs:24:5: 24:6
           return;                          // scope 0 at $DIR/lower_array_len.rs:25:2: 25:2
       }
-  
-      bb7 (cleanup): {
-          resume;                          // scope 0 at $DIR/lower_array_len.rs:17:1: 25:2
-      }
   }
   
diff --git a/src/test/mir-opt/lower_array_len.array_len.NormalizeArrayLen.diff b/src/test/mir-opt/lower_array_len.array_len.NormalizeArrayLen.diff
index b41582477c6..892fdda818e 100644
--- a/src/test/mir-opt/lower_array_len.array_len.NormalizeArrayLen.diff
+++ b/src/test/mir-opt/lower_array_len.array_len.NormalizeArrayLen.diff
@@ -26,9 +26,5 @@
           StorageDead(_2);                 // scope 0 at $DIR/lower_array_len.rs:31:13: 31:14
           return;                          // scope 0 at $DIR/lower_array_len.rs:32:2: 32:2
       }
-  
-      bb2 (cleanup): {
-          resume;                          // scope 0 at $DIR/lower_array_len.rs:30:1: 32:2
-      }
   }
   
diff --git a/src/test/mir-opt/lower_array_len.array_len_by_value.NormalizeArrayLen.diff b/src/test/mir-opt/lower_array_len.array_len_by_value.NormalizeArrayLen.diff
index 92ec7a3633e..201fffbf0d4 100644
--- a/src/test/mir-opt/lower_array_len.array_len_by_value.NormalizeArrayLen.diff
+++ b/src/test/mir-opt/lower_array_len.array_len_by_value.NormalizeArrayLen.diff
@@ -26,9 +26,5 @@
           StorageDead(_2);                 // scope 0 at $DIR/lower_array_len.rs:38:13: 38:14
           return;                          // scope 0 at $DIR/lower_array_len.rs:39:2: 39:2
       }
-  
-      bb2 (cleanup): {
-          resume;                          // scope 0 at $DIR/lower_array_len.rs:37:1: 39:2
-      }
   }
   
diff --git a/src/test/mir-opt/lower_slice_len.bound.LowerSliceLenCalls.diff b/src/test/mir-opt/lower_slice_len.bound.LowerSliceLenCalls.diff
index 2210ad54e8d..13241d882f2 100644
--- a/src/test/mir-opt/lower_slice_len.bound.LowerSliceLenCalls.diff
+++ b/src/test/mir-opt/lower_slice_len.bound.LowerSliceLenCalls.diff
@@ -59,9 +59,5 @@
           StorageDead(_3);                 // scope 0 at $DIR/lower_slice_len.rs:9:5: 9:6
           return;                          // scope 0 at $DIR/lower_slice_len.rs:10:2: 10:2
       }
-  
-      bb6 (cleanup): {
-          resume;                          // scope 0 at $DIR/lower_slice_len.rs:4:1: 10:2
-      }
   }
   
diff --git a/src/test/mir-opt/lower_slice_len.rs b/src/test/mir-opt/lower_slice_len.rs
index f2438e69749..12955aed1fb 100644
--- a/src/test/mir-opt/lower_slice_len.rs
+++ b/src/test/mir-opt/lower_slice_len.rs
@@ -1,4 +1,4 @@
-// compile-flags: -Z mir-opt-level=3
+// unit-test: LowerSliceLenCalls
 
 // EMIT_MIR lower_slice_len.bound.LowerSliceLenCalls.diff
 pub fn bound(index: usize, slice: &[u8]) -> u8 {
diff --git a/src/test/mir-opt/nll/named_lifetimes_basic.use_x.nll.0.mir b/src/test/mir-opt/nll/named_lifetimes_basic.use_x.nll.0.mir
index 9f235248ca5..8423128123a 100644
--- a/src/test/mir-opt/nll/named_lifetimes_basic.use_x.nll.0.mir
+++ b/src/test/mir-opt/nll/named_lifetimes_basic.use_x.nll.0.mir
@@ -25,14 +25,14 @@
 | '_#2r live at {bb0[0..=1]}
 | '_#3r live at {bb0[0..=1]}
 | '_#4r live at {bb0[0..=1]}
-| '_#1r: '_#6r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:26: 12:27)
-| '_#1r: '_#8r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:54: 12:55)
-| '_#2r: '_#7r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:42: 12:43)
-| '_#3r: '_#9r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:66: 12:67)
-| '_#6r: '_#1r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:26: 12:27)
-| '_#7r: '_#2r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:42: 12:43)
-| '_#8r: '_#1r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:54: 12:55)
-| '_#9r: '_#3r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:66: 12:67)
+| '_#1r: '_#6r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:26: 12:27) ($DIR/named-lifetimes-basic.rs:12:26: 12:27 (#0)
+| '_#1r: '_#8r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:54: 12:55) ($DIR/named-lifetimes-basic.rs:12:54: 12:55 (#0)
+| '_#2r: '_#7r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:42: 12:43) ($DIR/named-lifetimes-basic.rs:12:42: 12:43 (#0)
+| '_#3r: '_#9r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:66: 12:67) ($DIR/named-lifetimes-basic.rs:12:66: 12:67 (#0)
+| '_#6r: '_#1r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:26: 12:27) ($DIR/named-lifetimes-basic.rs:12:26: 12:27 (#0)
+| '_#7r: '_#2r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:42: 12:43) ($DIR/named-lifetimes-basic.rs:12:42: 12:43 (#0)
+| '_#8r: '_#1r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:54: 12:55) ($DIR/named-lifetimes-basic.rs:12:54: 12:55 (#0)
+| '_#9r: '_#3r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:66: 12:67) ($DIR/named-lifetimes-basic.rs:12:66: 12:67 (#0)
 |
 fn use_x(_1: &'_#6r mut i32, _2: &'_#7r u32, _3: &'_#8r u32, _4: &'_#9r u32) -> bool {
     debug w => _1;                       // in scope 0 at $DIR/named-lifetimes-basic.rs:12:26: 12:27
diff --git a/src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir b/src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir
index ed94a1ecf00..f79e2705ad2 100644
--- a/src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir
+++ b/src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir
@@ -18,8 +18,8 @@
 | '_#3r live at {bb1[0]}
 | '_#4r live at {bb1[1..=3]}
 | '_#5r live at {bb1[4..=7], bb2[0..=2]}
-| '_#3r: '_#4r due to Assignment at Single(bb1[0])
-| '_#4r: '_#5r due to Assignment at Single(bb1[3])
+| '_#3r: '_#4r due to Assignment at Single(bb1[0]) ($DIR/region-subtyping-basic.rs:18:13: 18:18 (#0)
+| '_#4r: '_#5r due to Assignment at Single(bb1[3]) ($DIR/region-subtyping-basic.rs:19:13: 19:14 (#0)
 |
 fn main() -> () {
     let mut _0: ();                      // return place in scope 0 at $DIR/region-subtyping-basic.rs:16:11: 16:11
diff --git a/src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir b/src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir
index 95997b20701..162cacef8a5 100644
--- a/src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir
+++ b/src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir
@@ -18,8 +18,8 @@
 | '_#3r live at {bb1[0]}
 | '_#4r live at {bb1[1..=3]}
 | '_#5r live at {bb1[4..=7], bb2[0..=2]}
-| '_#3r: '_#4r due to Assignment at Single(bb1[0])
-| '_#4r: '_#5r due to Assignment at Single(bb1[3])
+| '_#3r: '_#4r due to Assignment at Single(bb1[0]) ($DIR/region-subtyping-basic.rs:18:13: 18:18 (#0)
+| '_#4r: '_#5r due to Assignment at Single(bb1[3]) ($DIR/region-subtyping-basic.rs:19:13: 19:14 (#0)
 |
 fn main() -> () {
     let mut _0: ();                      // return place in scope 0 at $DIR/region-subtyping-basic.rs:16:11: 16:11
diff --git a/src/test/mir-opt/nrvo-simple.rs b/src/test/mir-opt/nrvo-simple.rs
index ab46d7b94c7..5786ae62127 100644
--- a/src/test/mir-opt/nrvo-simple.rs
+++ b/src/test/mir-opt/nrvo-simple.rs
@@ -1,4 +1,4 @@
-// compile-flags: -Zmir-opt-level=1
+// unit-test: RenameReturnPlace
 
 // EMIT_MIR nrvo_simple.nrvo.RenameReturnPlace.diff
 fn nrvo(init: fn(&mut [u8; 1024])) -> [u8; 1024] {
diff --git a/src/test/mir-opt/storage_ranges.main.nll.0.mir b/src/test/mir-opt/storage_ranges.main.nll.0.mir
index e02580135af..b383c5ec9dc 100644
--- a/src/test/mir-opt/storage_ranges.main.nll.0.mir
+++ b/src/test/mir-opt/storage_ranges.main.nll.0.mir
@@ -16,7 +16,7 @@
 | '_#1r live at {bb0[0..=22]}
 | '_#3r live at {bb0[10]}
 | '_#4r live at {bb0[11]}
-| '_#3r: '_#4r due to Assignment at Single(bb0[10])
+| '_#3r: '_#4r due to Assignment at Single(bb0[10]) ($DIR/storage_ranges.rs:6:17: 6:25 (#0)
 |
 fn main() -> () {
     let mut _0: ();                      // return place in scope 0 at $DIR/storage_ranges.rs:3:11: 3:11
diff --git a/src/test/mir-opt/uninhabited_fallthrough_elimination.eliminate_fallthrough.UninhabitedEnumBranching.diff b/src/test/mir-opt/uninhabited_fallthrough_elimination.eliminate_fallthrough.UninhabitedEnumBranching.diff
index 868eeb6367e..7e843b65e88 100644
--- a/src/test/mir-opt/uninhabited_fallthrough_elimination.eliminate_fallthrough.UninhabitedEnumBranching.diff
+++ b/src/test/mir-opt/uninhabited_fallthrough_elimination.eliminate_fallthrough.UninhabitedEnumBranching.diff
@@ -9,7 +9,7 @@
       bb0: {
           _2 = discriminant(_1);           // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:22:11: 22:12
 -         switchInt(move _2) -> [1_isize: bb3, 2_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:22:5: 22:12
-+         switchInt(move _2) -> [1_isize: bb3, 2_isize: bb2, otherwise: bb6]; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:22:5: 22:12
++         switchInt(move _2) -> [1_isize: bb3, 2_isize: bb2, otherwise: bb5]; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:22:5: 22:12
       }
   
       bb1: {
@@ -29,13 +29,9 @@
   
       bb4: {
           return;                          // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:27:2: 27:2
-      }
-  
-      bb5 (cleanup): {
-          resume;                          // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:21:1: 27:2
 +     }
 + 
-+     bb6: {
++     bb5: {
 +         unreachable;                     // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:25:14: 25:15
       }
   }
diff --git a/src/test/mir-opt/uninhabited_fallthrough_elimination.keep_fallthrough.UninhabitedEnumBranching.diff b/src/test/mir-opt/uninhabited_fallthrough_elimination.keep_fallthrough.UninhabitedEnumBranching.diff
index 33c1458dc0c..5da011d427a 100644
--- a/src/test/mir-opt/uninhabited_fallthrough_elimination.keep_fallthrough.UninhabitedEnumBranching.diff
+++ b/src/test/mir-opt/uninhabited_fallthrough_elimination.keep_fallthrough.UninhabitedEnumBranching.diff
@@ -30,9 +30,5 @@
       bb4: {
           return;                          // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:18:2: 18:2
       }
-  
-      bb5 (cleanup): {
-          resume;                          // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:12:1: 18:2
-      }
   }
   
diff --git a/src/test/mir-opt/unreachable.main.UnreachablePropagation.diff b/src/test/mir-opt/unreachable.main.UnreachablePropagation.diff
index 380844f8861..08312bde20f 100644
--- a/src/test/mir-opt/unreachable.main.UnreachablePropagation.diff
+++ b/src/test/mir-opt/unreachable.main.UnreachablePropagation.diff
@@ -64,10 +64,6 @@
           _0 = const ();                   // scope 0 at $DIR/unreachable.rs:19:6: 19:6
           StorageDead(_1);                 // scope 0 at $DIR/unreachable.rs:20:1: 20:2
           return;                          // scope 0 at $DIR/unreachable.rs:20:2: 20:2
--     }
-- 
--     bb7 (cleanup): {
--         resume;                          // scope 0 at $DIR/unreachable.rs:8:1: 20:2
       }
   }
   
diff --git a/src/test/mir-opt/unreachable_diverging.main.UnreachablePropagation.diff b/src/test/mir-opt/unreachable_diverging.main.UnreachablePropagation.diff
index e26990b1def..e5867ccfc5c 100644
--- a/src/test/mir-opt/unreachable_diverging.main.UnreachablePropagation.diff
+++ b/src/test/mir-opt/unreachable_diverging.main.UnreachablePropagation.diff
@@ -69,10 +69,6 @@
           StorageDead(_1);                 // scope 0 at $DIR/unreachable_diverging.rs:20:1: 20:2
           StorageDead(_2);                 // scope 0 at $DIR/unreachable_diverging.rs:20:1: 20:2
           return;                          // scope 0 at $DIR/unreachable_diverging.rs:20:2: 20:2
--     }
-- 
--     bb7 (cleanup): {
--         resume;                          // scope 0 at $DIR/unreachable_diverging.rs:12:1: 20:2
       }
   }
   
diff --git a/src/test/pretty/dollar-crate.pp b/src/test/pretty/dollar-crate.pp
index 0c96fb593e6..3af37955f23 100644
--- a/src/test/pretty/dollar-crate.pp
+++ b/src/test/pretty/dollar-crate.pp
@@ -9,5 +9,5 @@ extern crate std;
 // pp-exact:dollar-crate.pp
 
 fn main() {
-    ::std::io::_print(::core::fmt::Arguments::new_v1(&["rust\n"], &[]));
+    { ::std::io::_print(::core::fmt::Arguments::new_v1(&["rust\n"], &[])); };
 }
diff --git a/src/test/pretty/hir-pretty-loop.pp b/src/test/pretty/hir-pretty-loop.pp
index 9b10fd86c47..a59a3002c7f 100644
--- a/src/test/pretty/hir-pretty-loop.pp
+++ b/src/test/pretty/hir-pretty-loop.pp
@@ -6,4 +6,4 @@ extern crate std;
 // pretty-mode:hir
 // pp-exact:hir-pretty-loop.pp
 
-pub fn foo() { loop { break; } }
+fn foo() { loop { break; } }
diff --git a/src/test/pretty/issue-4264.pp b/src/test/pretty/issue-4264.pp
index ea74a267be8..752c36a0fbc 100644
--- a/src/test/pretty/issue-4264.pp
+++ b/src/test/pretty/issue-4264.pp
@@ -8,9 +8,9 @@ extern crate std;
 
 // #4264 fixed-length vector types
 
-pub fn foo(_: [i32; (3 as usize)]) ({ } as ())
+fn foo(_: [i32; (3 as usize)]) ({ } as ())
 
-pub fn bar() ({
+fn bar() ({
         const FOO: usize = ((5 as usize) - (4 as usize) as usize);
         let _: [(); (FOO as usize)] = ([(() as ())] as [(); 1]);
 
@@ -41,14 +41,14 @@ pub fn bar() ({
                 (res as String)
             } as String);
     } as ())
-pub type Foo = [i32; (3 as usize)];
-pub struct Bar {
-    pub x: [i32; (3 as usize)],
+type Foo = [i32; (3 as usize)];
+struct Bar {
+    x: [i32; (3 as usize)],
 }
-pub struct TupleBar([i32; (4 as usize)]);
-pub enum Baz { BazVariant([i32; (5 as usize)]), }
-pub fn id<T>(x: T) -> T ({ (x as T) } as T)
-pub fn use_id() ({
+struct TupleBar([i32; (4 as usize)]);
+enum Baz { BazVariant([i32; (5 as usize)]), }
+fn id<T>(x: T) -> T ({ (x as T) } as T)
+fn use_id() ({
         let _ =
             ((id::<[i32; (3 as usize)]> as
                     fn([i32; 3]) -> [i32; 3] {id::<[i32; 3]>})(([(1 as i32),
diff --git a/src/test/pretty/yeet-expr.rs b/src/test/pretty/yeet-expr.rs
new file mode 100644
index 00000000000..c899f11b724
--- /dev/null
+++ b/src/test/pretty/yeet-expr.rs
@@ -0,0 +1,12 @@
+// pp-exact
+#![feature(yeet_expr)]
+
+fn yeet_no_expr() -> Option<String> { do yeet }
+
+fn yeet_no_expr_with_semicolon() -> Option<String> { do yeet; }
+
+fn yeet_with_expr() -> Result<String, i32> { do yeet 1 + 2 }
+
+fn yeet_with_expr_with_semicolon() -> Result<String, i32> { do yeet 1 + 2; }
+
+fn main() {}
diff --git a/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.closure.txt b/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.closure.txt
index 8bf9c97fdda..e463099a5ee 100644
--- a/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.closure.txt
+++ b/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.closure.txt
@@ -116,8 +116,8 @@
   116|      1|
   117|      1|    let
   118|      1|        _unused_closure
-  119|      1|    =
-  120|      1|        |
+  119|       |    =
+  120|       |        |
   121|       |            mut countdown
   122|       |        |
   123|      0|    {
@@ -173,7 +173,7 @@
   169|       |    ;
   170|       |
   171|      1|    let short_used_not_covered_closure_line_break_no_block_embedded_branch =
-  172|      1|        | _unused_arg: u8 |
+  172|       |        | _unused_arg: u8 |
   173|      0|            println!(
   174|      0|                "not called: {}",
   175|      0|                if is_true { "check" } else { "me" }
@@ -191,7 +191,7 @@
   187|       |    ;
   188|       |
   189|      1|    let short_used_covered_closure_line_break_no_block_embedded_branch =
-  190|       |        | _unused_arg: u8 |
+  190|      1|        | _unused_arg: u8 |
   191|      1|            println!(
   192|      1|                "not called: {}",
   193|      1|                if is_true { "check" } else { "me" }
diff --git a/src/test/run-make-fulldeps/reproducible-build/linker.rs b/src/test/run-make-fulldeps/reproducible-build/linker.rs
index 998d1f32859..3dda6f190e4 100644
--- a/src/test/run-make-fulldeps/reproducible-build/linker.rs
+++ b/src/test/run-make-fulldeps/reproducible-build/linker.rs
@@ -25,6 +25,12 @@ fn main() {
         let mut contents = Vec::new();
         File::open(path).unwrap().read_to_end(&mut contents).unwrap();
 
+        // This file is produced during linking in a temporary directory.
+        let arg = if arg.ends_with("/symbols.o") || arg.ends_with("\\symbols.o") {
+            "symbols.o"
+        } else {
+            &*arg
+        };
         out.push_str(&format!("{}: {}\n", arg, hash(&contents)));
     }
 
diff --git a/src/test/run-make-fulldeps/symbol-visibility/Makefile b/src/test/run-make-fulldeps/symbol-visibility/Makefile
index dc55c947d89..4bb35f33ad3 100644
--- a/src/test/run-make-fulldeps/symbol-visibility/Makefile
+++ b/src/test/run-make-fulldeps/symbol-visibility/Makefile
@@ -54,9 +54,12 @@ all:
 	# Check that a Rust dylib does not export generics if -Zshare-generics=no
 	[ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -v __imp_ | grep -c public_generic_function_from_rlib)" -eq "0" ]
 
+# FIXME(nbdd0121): This is broken in MinGW, see https://github.com/rust-lang/rust/pull/95604#issuecomment-1101564032
+ifndef IS_WINDOWS
 	# Check that an executable does not export any dynamic symbols
 	[ "$$($(NM) $(TMPDIR)/$(EXE_NAME) | grep -v __imp_ | grep -c public_c_function_from_rlib)" -eq "0" ]
 	[ "$$($(NM) $(TMPDIR)/$(EXE_NAME) | grep -v __imp_ | grep -c public_rust_function_from_exe)" -eq "0" ]
+endif
 
 
 	# Check the combined case, where we generate a cdylib and an rlib in the same
@@ -91,6 +94,8 @@ all:
 	[ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -v __imp_ | grep -c public_rust_function_from_rlib)" -eq "1" ]
 	[ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -v __imp_ | grep -c public_generic_function_from_rlib)" -eq "1" ]
 
+ifndef IS_WINDOWS
 	# Check that an executable does not export any dynamic symbols
 	[ "$$($(NM) $(TMPDIR)/$(EXE_NAME) | grep -v __imp_ | grep -c public_c_function_from_rlib)" -eq "0" ]
 	[ "$$($(NM) $(TMPDIR)/$(EXE_NAME) | grep -v __imp_ | grep -c public_rust_function_from_exe)" -eq "0" ]
+endif
diff --git a/src/test/run-make/const_fn_mir/dump.mir b/src/test/run-make/const_fn_mir/dump.mir
index 4e8936905c4..f02bccc4b2d 100644
--- a/src/test/run-make/const_fn_mir/dump.mir
+++ b/src/test/run-make/const_fn_mir/dump.mir
@@ -23,10 +23,6 @@ fn foo() -> i32 {
         _0 = move (_1.0: i32);           // scope 0 at main.rs:5:5: 5:10
         return;                          // scope 0 at main.rs:6:2: 6:2
     }
-
-    bb2 (cleanup): {
-        resume;                          // scope 0 at main.rs:4:1: 6:2
-    }
 }
 
 fn main() -> () {
diff --git a/src/test/run-make/emit-shared-files/Makefile b/src/test/run-make/emit-shared-files/Makefile
index d89b526d430..9f46883beaa 100644
--- a/src/test/run-make/emit-shared-files/Makefile
+++ b/src/test/run-make/emit-shared-files/Makefile
@@ -14,7 +14,7 @@ invocation-only:
 	[ -e $(INVOCATION_ONLY)/x/index.html ]
 	[ -e $(INVOCATION_ONLY)/theme-xxx.css ] # generated from z.css
 	! [ -e $(INVOCATION_ONLY)/storage-xxx.js ]
-	! [ -e $(INVOCATION_ONLY)/SourceSerif4-It.ttf.woff ]
+	! [ -e $(INVOCATION_ONLY)/SourceSerif4-It.ttf.woff2 ]
 
 	# FIXME: this probably shouldn't have a suffix
 	[ -e $(INVOCATION_ONLY)/y-xxx.css ]
@@ -24,7 +24,7 @@ invocation-only:
 toolchain-only:
 	$(RUSTDOC) -Z unstable-options --emit=toolchain-shared-resources --output $(TOOLCHAIN_ONLY) --resource-suffix=-xxx --extend-css z.css x.rs
 	[ -e $(TOOLCHAIN_ONLY)/storage-xxx.js ]
-	! [ -e $(TOOLCHAIN_ONLY)/SourceSerif4-It.ttf.woff ]
+	! [ -e $(TOOLCHAIN_ONLY)/SourceSerif4-It.ttf.woff2 ]
 	! [ -e $(TOOLCHAIN_ONLY)/search-index-xxx.js ]
 	! [ -e $(TOOLCHAIN_ONLY)/x/index.html ]
 	! [ -e $(TOOLCHAIN_ONLY)/theme.css ]
@@ -35,7 +35,7 @@ toolchain-only:
 all-shared:
 	$(RUSTDOC) -Z unstable-options --emit=toolchain-shared-resources,unversioned-shared-resources --output $(ALL_SHARED) --resource-suffix=-xxx --extend-css z.css x.rs
 	[ -e $(ALL_SHARED)/storage-xxx.js ]
-	[ -e $(ALL_SHARED)/SourceSerif4-It.ttf.woff ]
+	[ -e $(ALL_SHARED)/SourceSerif4-It.ttf.woff2 ]
 	! [ -e $(ALL_SHARED)/search-index-xxx.js ]
 	! [ -e $(ALL_SHARED)/settings.html ]
 	! [ -e $(ALL_SHARED)/x ]
diff --git a/src/test/run-make/issue-47384/Makefile b/src/test/run-make/issue-47384/Makefile
new file mode 100644
index 00000000000..f10365f8c88
--- /dev/null
+++ b/src/test/run-make/issue-47384/Makefile
@@ -0,0 +1,12 @@
+-include ../../run-make-fulldeps/tools.mk
+
+# only-linux
+# ignore-cross-compile
+
+all: main.rs
+	$(RUSTC) --crate-type lib lib.rs
+	$(RUSTC) --crate-type cdylib -Clink-args="-Tlinker.ld" main.rs
+	# Ensure `#[used]` and `KEEP`-ed section is there
+	objdump -s -j".static" $(TMPDIR)/libmain.so
+	# Ensure `#[no_mangle]` symbol is there
+	nm $(TMPDIR)/libmain.so | $(CGREP) bar
diff --git a/src/test/run-make/issue-47384/lib.rs b/src/test/run-make/issue-47384/lib.rs
new file mode 100644
index 00000000000..99508bcdaf3
--- /dev/null
+++ b/src/test/run-make/issue-47384/lib.rs
@@ -0,0 +1,12 @@
+mod foo {
+    #[link_section = ".rodata.STATIC"]
+    #[used]
+    static STATIC: [u32; 10] = [1; 10];
+}
+
+mod bar {
+    #[no_mangle]
+    extern "C" fn bar() -> i32 {
+        0
+    }
+}
diff --git a/src/test/run-make/issue-47384/linker.ld b/src/test/run-make/issue-47384/linker.ld
new file mode 100644
index 00000000000..2e70acab3f4
--- /dev/null
+++ b/src/test/run-make/issue-47384/linker.ld
@@ -0,0 +1,7 @@
+SECTIONS
+{
+    .static : ALIGN(4)
+    {
+        KEEP(*(.rodata.STATIC));
+    }
+}
diff --git a/src/test/run-make/issue-47384/main.rs b/src/test/run-make/issue-47384/main.rs
new file mode 100644
index 00000000000..02572632517
--- /dev/null
+++ b/src/test/run-make/issue-47384/main.rs
@@ -0,0 +1 @@
+extern crate lib;
diff --git a/src/test/run-make/issue-96498/Makefile b/src/test/run-make/issue-96498/Makefile
new file mode 100644
index 00000000000..eae6400aee4
--- /dev/null
+++ b/src/test/run-make/issue-96498/Makefile
@@ -0,0 +1,8 @@
+# only-windows
+# needs-rust-lld
+
+-include ../../run-make-fulldeps/tools.mk
+
+# Ensure that LLD can link
+all:
+	$(RUSTC) -C linker=rust-lld foo.rs
diff --git a/src/test/run-make/issue-96498/foo.rs b/src/test/run-make/issue-96498/foo.rs
new file mode 100644
index 00000000000..93ac3641b09
--- /dev/null
+++ b/src/test/run-make/issue-96498/foo.rs
@@ -0,0 +1,4 @@
+#![crate_type = "cdylib"]
+
+#[no_mangle]
+extern "C" fn foo() {}
diff --git a/src/test/rustdoc-js-std/filter-crate.js b/src/test/rustdoc-js-std/filter-crate.js
index 2e0330c4497..b47a1fefa41 100644
--- a/src/test/rustdoc-js-std/filter-crate.js
+++ b/src/test/rustdoc-js-std/filter-crate.js
@@ -1,6 +1,6 @@
 // exact-check
 
-const QUERY = 'hashmap';
+const QUERY = '"hashmap"';
 const FILTER_CRATE = 'core';
 
 const EXPECTED = {
diff --git a/src/test/rustdoc-js-std/parser-errors.js b/src/test/rustdoc-js-std/parser-errors.js
new file mode 100644
index 00000000000..dc42031e05f
--- /dev/null
+++ b/src/test/rustdoc-js-std/parser-errors.js
@@ -0,0 +1,385 @@
+const QUERY = [
+    '<P>',
+    '-> <P>',
+    'a<"P">',
+    '"P" "P"',
+    'P "P"',
+    '"p" p',
+    '"const": p',
+    "a<:a>",
+    "a<::a>",
+    "((a))",
+    "(p -> p",
+    "::a::b",
+    "a::::b",
+    "a::b::",
+    ":a",
+    "a b:",
+    "a (b:",
+    "_:",
+    "a-bb",
+    "a>bb",
+    "ab'",
+    "a->",
+    '"p" <a>',
+    '"p" a<a>',
+    "a,<",
+    "aaaaa<>b",
+    "fn:aaaaa<>b",
+    "->a<>b",
+    "a<->",
+    "a:: a",
+    "a ::a",
+    "a<a>:",
+    "a<>:",
+    "a,:",
+    "  a<>  :",
+    "mod : :",
+    "a!a",
+    "a!!",
+];
+
+const PARSED = [
+    {
+        elems: [],
+        foundElems: 0,
+        original: "<P>",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "<p>",
+        error: "Found generics without a path",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "-> <P>",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "-> <p>",
+        error: "Found generics without a path",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "a<\"P\">",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a<\"p\">",
+        error: "`\"` cannot be used in generics",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "\"P\" \"P\"",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "\"p\" \"p\"",
+        error: "Cannot have more than one literal search element",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "P \"P\"",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "p \"p\"",
+        error: "Cannot use literal search when there is more than one element",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "\"p\" p",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "\"p\" p",
+        error: "You cannot have more than one element if you use quotes",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "\"const\": p",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "\"const\": p",
+        error: "You cannot use quotes on type filter",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "a<:a>",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a<:a>",
+        error: "Unexpected `:` after `<`",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "a<::a>",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a<::a>",
+        error: "Unexpected `::`: paths cannot start with `::`",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "((a))",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "((a))",
+        error: "Unexpected `(`",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "(p -> p",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "(p -> p",
+        error: "Unexpected `(`",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "::a::b",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "::a::b",
+        error: "Paths cannot start with `::`",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "a::::b",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a::::b",
+        error: "Unexpected `::::`",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "a::b::",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a::b::",
+        error: "Paths cannot end with `::`",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: ":a",
+        returned: [],
+        typeFilter: -1,
+        userQuery: ":a",
+        error: "Expected type filter before `:`",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "a b:",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a b:",
+        error: "Unexpected `:`",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "a (b:",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a (b:",
+        error: "Unexpected `(`",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "_:",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "_:",
+        error: "Unknown type filter `_`",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "a-bb",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a-bb",
+        error: "Unexpected `-` (did you mean `->`?)",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "a>bb",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a>bb",
+        error: "Unexpected `>` (did you mean `->`?)",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "ab'",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "ab'",
+        error: "Unexpected `'`",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "a->",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a->",
+        error: "Expected at least one item after `->`",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: '"p" <a>',
+        returned: [],
+        typeFilter: -1,
+        userQuery: '"p" <a>',
+        error: "Found generics without a path",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: '"p" a<a>',
+        returned: [],
+        typeFilter: -1,
+        userQuery: '"p" a<a>',
+        error: "You cannot have more than one element if you use quotes",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: 'a,<',
+        returned: [],
+        typeFilter: -1,
+        userQuery: 'a,<',
+        error: 'Found generics without a path',
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: 'aaaaa<>b',
+        returned: [],
+        typeFilter: -1,
+        userQuery: 'aaaaa<>b',
+        error: 'Expected `,`, ` `, `:` or `->`, found `b`',
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: 'fn:aaaaa<>b',
+        returned: [],
+        typeFilter: -1,
+        userQuery: 'fn:aaaaa<>b',
+        error: 'Expected `,`, ` ` or `->`, found `b`',
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: '->a<>b',
+        returned: [],
+        typeFilter: -1,
+        userQuery: '->a<>b',
+        error: 'Expected `,` or ` `, found `b`',
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: 'a<->',
+        returned: [],
+        typeFilter: -1,
+        userQuery: 'a<->',
+        error: 'Unexpected `-` after `<`',
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: 'a:: a',
+        returned: [],
+        typeFilter: -1,
+        userQuery: 'a:: a',
+        error: 'Paths cannot end with `::`',
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: 'a ::a',
+        returned: [],
+        typeFilter: -1,
+        userQuery: 'a ::a',
+        error: 'Paths cannot start with `::`',
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "a<a>:",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a<a>:",
+        error: 'Unexpected `:`',
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "a<>:",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a<>:",
+        error: 'Unexpected `<` in type filter',
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "a,:",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a,:",
+        error: 'Unexpected `,` in type filter',
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "a<>  :",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a<>  :",
+        error: 'Unexpected `<` in type filter',
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "mod : :",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "mod : :",
+        error: 'Unexpected `:`',
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "a!a",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a!a",
+        error: '`!` can only be at the end of an ident',
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "a!!",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a!!",
+        error: 'Cannot have more than one `!` in an ident',
+    },
+];
diff --git a/src/test/rustdoc-js-std/parser-filter.js b/src/test/rustdoc-js-std/parser-filter.js
new file mode 100644
index 00000000000..e5a87a415ac
--- /dev/null
+++ b/src/test/rustdoc-js-std/parser-filter.js
@@ -0,0 +1,43 @@
+const QUERY = ['fn:foo', 'enum : foo', 'macro<f>:foo'];
+
+const PARSED = [
+    {
+        elems: [{
+            name: "foo",
+            fullPath: ["foo"],
+            pathWithoutLast: [],
+            pathLast: "foo",
+            generics: [],
+        }],
+        foundElems: 1,
+        original: "fn:foo",
+        returned: [],
+        typeFilter: 5,
+        userQuery: "fn:foo",
+        error: null,
+    },
+    {
+        elems: [{
+            name: "foo",
+            fullPath: ["foo"],
+            pathWithoutLast: [],
+            pathLast: "foo",
+            generics: [],
+        }],
+        foundElems: 1,
+        original: "enum : foo",
+        returned: [],
+        typeFilter: 4,
+        userQuery: "enum : foo",
+        error: null,
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "macro<f>:foo",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "macro<f>:foo",
+        error: "Unexpected `:`",
+    },
+];
diff --git a/src/test/rustdoc-js-std/parser-generics.js b/src/test/rustdoc-js-std/parser-generics.js
new file mode 100644
index 00000000000..0cf7f5019aa
--- /dev/null
+++ b/src/test/rustdoc-js-std/parser-generics.js
@@ -0,0 +1,62 @@
+const QUERY = ['A<B<C<D>,  E>', 'p<> u8', '"p"<a>'];
+
+const PARSED = [
+    {
+        elems: [],
+        foundElems: 0,
+        original: 'A<B<C<D>,  E>',
+        returned: [],
+        typeFilter: -1,
+        userQuery: 'a<b<c<d>,  e>',
+        error: 'Unexpected `<` after `<`',
+    },
+    {
+        elems: [
+            {
+                name: "p",
+                fullPath: ["p"],
+                pathWithoutLast: [],
+                pathLast: "p",
+                generics: [],
+            },
+            {
+                name: "u8",
+                fullPath: ["u8"],
+                pathWithoutLast: [],
+                pathLast: "u8",
+                generics: [],
+            },
+        ],
+        foundElems: 2,
+        original: "p<> u8",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "p<> u8",
+        error: null,
+    },
+    {
+        elems: [
+            {
+                name: "p",
+                fullPath: ["p"],
+                pathWithoutLast: [],
+                pathLast: "p",
+                generics: [
+                    {
+                        name: "a",
+                        fullPath: ["a"],
+                        pathWithoutLast: [],
+                        pathLast: "a",
+                        generics: [],
+                    },
+                ],
+            },
+        ],
+        foundElems: 1,
+        original: '"p"<a>',
+        returned: [],
+        typeFilter: -1,
+        userQuery: '"p"<a>',
+        error: null,
+    },
+];
diff --git a/src/test/rustdoc-js-std/parser-ident.js b/src/test/rustdoc-js-std/parser-ident.js
new file mode 100644
index 00000000000..4b5ab01ac76
--- /dev/null
+++ b/src/test/rustdoc-js-std/parser-ident.js
@@ -0,0 +1,93 @@
+const QUERY = [
+    "R<!>",
+    "!",
+    "a!",
+    "a!::b",
+    "a!::b!",
+];
+
+const PARSED = [
+    {
+        elems: [{
+            name: "r",
+            fullPath: ["r"],
+            pathWithoutLast: [],
+            pathLast: "r",
+            generics: [
+                {
+                    name: "!",
+                    fullPath: ["!"],
+                    pathWithoutLast: [],
+                    pathLast: "!",
+                    generics: [],
+                },
+            ],
+        }],
+        foundElems: 1,
+        original: "R<!>",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "r<!>",
+        error: null,
+    },
+    {
+        elems: [{
+            name: "!",
+            fullPath: ["!"],
+            pathWithoutLast: [],
+            pathLast: "!",
+            generics: [],
+        }],
+        foundElems: 1,
+        original: "!",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "!",
+        error: null,
+    },
+    {
+        elems: [{
+            name: "a!",
+            fullPath: ["a!"],
+            pathWithoutLast: [],
+            pathLast: "a!",
+            generics: [],
+        }],
+        foundElems: 1,
+        original: "a!",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a!",
+        error: null,
+    },
+    {
+        elems: [{
+            name: "a!::b",
+            fullPath: ["a!", "b"],
+            pathWithoutLast: ["a!"],
+            pathLast: "b",
+            generics: [],
+        }],
+        foundElems: 1,
+        original: "a!::b",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a!::b",
+        error: null,
+    },
+    {
+        elems: [{
+            name: "a!::b!",
+            fullPath: ["a!", "b!"],
+            pathWithoutLast: ["a!"],
+            pathLast: "b!",
+            generics: [],
+        }],
+        foundElems: 1,
+        original: "a!::b!",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a!::b!",
+        error: null,
+    },
+];
diff --git a/src/test/rustdoc-js-std/parser-literal.js b/src/test/rustdoc-js-std/parser-literal.js
new file mode 100644
index 00000000000..87b3baff1e2
--- /dev/null
+++ b/src/test/rustdoc-js-std/parser-literal.js
@@ -0,0 +1,27 @@
+const QUERY = ['R<P>'];
+
+const PARSED = [
+    {
+        elems: [{
+            name: "r",
+            fullPath: ["r"],
+            pathWithoutLast: [],
+            pathLast: "r",
+            generics: [
+                {
+                    name: "p",
+                    fullPath: ["p"],
+                    pathWithoutLast: [],
+                    pathLast: "p",
+                    generics: [],
+                },
+            ],
+        }],
+        foundElems: 1,
+        original: "R<P>",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "r<p>",
+        error: null,
+    }
+];
diff --git a/src/test/rustdoc-js-std/parser-paths.js b/src/test/rustdoc-js-std/parser-paths.js
new file mode 100644
index 00000000000..9f823f9336a
--- /dev/null
+++ b/src/test/rustdoc-js-std/parser-paths.js
@@ -0,0 +1,90 @@
+const QUERY = ['A::B', 'A::B,C',  'A::B<f>,C', 'mod::a'];
+
+const PARSED = [
+    {
+        elems: [{
+            name: "a::b",
+            fullPath: ["a", "b"],
+            pathWithoutLast: ["a"],
+            pathLast: "b",
+            generics: [],
+        }],
+        foundElems: 1,
+        original: "A::B",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a::b",
+        error: null,
+    },
+    {
+        elems: [
+            {
+                name: "a::b",
+                fullPath: ["a", "b"],
+                pathWithoutLast: ["a"],
+                pathLast: "b",
+                generics: [],
+            },
+            {
+                name: "c",
+                fullPath: ["c"],
+                pathWithoutLast: [],
+                pathLast: "c",
+                generics: [],
+            },
+        ],
+        foundElems: 2,
+        original: 'A::B,C',
+        returned: [],
+        typeFilter: -1,
+        userQuery: 'a::b,c',
+        error: null,
+    },
+    {
+        elems: [
+            {
+                name: "a::b",
+                fullPath: ["a", "b"],
+                pathWithoutLast: ["a"],
+                pathLast: "b",
+                generics: [
+                    {
+                        name: "f",
+                        fullPath: ["f"],
+                        pathWithoutLast: [],
+                        pathLast: "f",
+                        generics: [],
+                    },
+                ],
+            },
+            {
+                name: "c",
+                fullPath: ["c"],
+                pathWithoutLast: [],
+                pathLast: "c",
+                generics: [],
+            },
+        ],
+        foundElems: 2,
+        original: 'A::B<f>,C',
+        returned: [],
+        typeFilter: -1,
+        userQuery: 'a::b<f>,c',
+        error: null,
+    },
+    {
+        elems: [{
+            name: "mod::a",
+            fullPath: ["mod", "a"],
+            pathWithoutLast: ["mod"],
+            pathLast: "a",
+            generics: [],
+        }],
+        foundElems: 1,
+        original: "mod::a",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "mod::a",
+        error: null,
+    },
+];
diff --git a/src/test/rustdoc-js-std/parser-quote.js b/src/test/rustdoc-js-std/parser-quote.js
new file mode 100644
index 00000000000..1e16c90de5e
--- /dev/null
+++ b/src/test/rustdoc-js-std/parser-quote.js
@@ -0,0 +1,87 @@
+const QUERY = [
+    '-> "p"',
+    '"p",',
+    '"p" -> a',
+    '"a" -> "p"',
+    '->"-"',
+    '"a',
+    '""',
+];
+
+const PARSED = [
+    {
+        elems: [],
+        foundElems: 1,
+        original: '-> "p"',
+        returned: [{
+            name: "p",
+            fullPath: ["p"],
+            pathWithoutLast: [],
+            pathLast: "p",
+            generics: [],
+        }],
+        typeFilter: -1,
+        userQuery: '-> "p"',
+        error: null,
+    },
+    {
+        elems: [{
+            name: "p",
+            fullPath: ["p"],
+            pathWithoutLast: [],
+            pathLast: "p",
+            generics: [],
+        }],
+        foundElems: 1,
+        original: '"p",',
+        returned: [],
+        typeFilter: -1,
+        userQuery: '"p",',
+        error: null,
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: '"p" -> a',
+        returned: [],
+        typeFilter: -1,
+        userQuery: '"p" -> a',
+        error: "You cannot have more than one element if you use quotes",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: '"a" -> "p"',
+        returned: [],
+        typeFilter: -1,
+        userQuery: '"a" -> "p"',
+        error: "Cannot have more than one literal search element",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: '->"-"',
+        returned: [],
+        typeFilter: -1,
+        userQuery: '->"-"',
+        error: 'Unexpected `-` in a string element',
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: '"a',
+        returned: [],
+        typeFilter: -1,
+        userQuery: '"a',
+        error: 'Unclosed `"`',
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: '""',
+        returned: [],
+        typeFilter: -1,
+        userQuery: '""',
+        error: 'Cannot have empty string element',
+    },
+];
diff --git a/src/test/rustdoc-js-std/parser-returned.js b/src/test/rustdoc-js-std/parser-returned.js
new file mode 100644
index 00000000000..6fce17dcabd
--- /dev/null
+++ b/src/test/rustdoc-js-std/parser-returned.js
@@ -0,0 +1,99 @@
+const QUERY = [
+    "-> F<P>",
+    "-> P",
+    "->,a",
+    "aaaaa->a",
+    "-> !",
+];
+
+const PARSED = [
+    {
+        elems: [],
+        foundElems: 1,
+        original: "-> F<P>",
+        returned: [{
+            name: "f",
+            fullPath: ["f"],
+            pathWithoutLast: [],
+            pathLast: "f",
+            generics: [
+                {
+                    name: "p",
+                    fullPath: ["p"],
+                    pathWithoutLast: [],
+                    pathLast: "p",
+                    generics: [],
+                },
+            ],
+        }],
+        typeFilter: -1,
+        userQuery: "-> f<p>",
+        error: null,
+    },
+    {
+        elems: [],
+        foundElems: 1,
+        original: "-> P",
+        returned: [{
+            name: "p",
+            fullPath: ["p"],
+            pathWithoutLast: [],
+            pathLast: "p",
+            generics: [],
+        }],
+        typeFilter: -1,
+        userQuery: "-> p",
+        error: null,
+    },
+    {
+        elems: [],
+        foundElems: 1,
+        original: "->,a",
+        returned: [{
+            name: "a",
+            fullPath: ["a"],
+            pathWithoutLast: [],
+            pathLast: "a",
+            generics: [],
+        }],
+        typeFilter: -1,
+        userQuery: "->,a",
+        error: null,
+    },
+    {
+        elems: [{
+            name: "aaaaa",
+            fullPath: ["aaaaa"],
+            pathWithoutLast: [],
+            pathLast: "aaaaa",
+            generics: [],
+        }],
+        foundElems: 2,
+        original: "aaaaa->a",
+        returned: [{
+            name: "a",
+            fullPath: ["a"],
+            pathWithoutLast: [],
+            pathLast: "a",
+            generics: [],
+        }],
+        typeFilter: -1,
+        userQuery: "aaaaa->a",
+        error: null,
+    },
+    {
+        elems: [],
+        foundElems: 1,
+        original: "-> !",
+        returned: [{
+            name: "!",
+            fullPath: ["!"],
+            pathWithoutLast: [],
+            pathLast: "!",
+            generics: [],
+        }],
+        typeFilter: -1,
+        userQuery: "-> !",
+        error: null,
+    },
+];
diff --git a/src/test/rustdoc-js-std/parser-separators.js b/src/test/rustdoc-js-std/parser-separators.js
new file mode 100644
index 00000000000..5b7abdfa8d6
--- /dev/null
+++ b/src/test/rustdoc-js-std/parser-separators.js
@@ -0,0 +1,206 @@
+// ignore-tidy-tab
+
+const QUERY = [
+    'aaaaaa	b',
+    'a b',
+    'a,b',
+    'a\tb',
+    'a<b c>',
+    'a<b,c>',
+    'a<b\tc>',
+];
+
+const PARSED = [
+    {
+        elems: [
+            {
+                name: 'aaaaaa',
+                fullPath: ['aaaaaa'],
+                pathWithoutLast: [],
+                pathLast: 'aaaaaa',
+                generics: [],
+            },
+            {
+                name: 'b',
+                fullPath: ['b'],
+                pathWithoutLast: [],
+                pathLast: 'b',
+                generics: [],
+            },
+        ],
+        foundElems: 2,
+        original: "aaaaaa	b",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "aaaaaa	b",
+        error: null,
+    },
+    {
+        elems: [
+            {
+                name: 'a',
+                fullPath: ['a'],
+                pathWithoutLast: [],
+                pathLast: 'a',
+                generics: [],
+            },
+            {
+                name: 'b',
+                fullPath: ['b'],
+                pathWithoutLast: [],
+                pathLast: 'b',
+                generics: [],
+            },
+        ],
+        foundElems: 2,
+        original: "a b",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a b",
+        error: null,
+    },
+    {
+        elems: [
+            {
+                name: 'a',
+                fullPath: ['a'],
+                pathWithoutLast: [],
+                pathLast: 'a',
+                generics: [],
+            },
+            {
+                name: 'b',
+                fullPath: ['b'],
+                pathWithoutLast: [],
+                pathLast: 'b',
+                generics: [],
+            },
+        ],
+        foundElems: 2,
+        original: "a,b",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a,b",
+        error: null,
+    },
+    {
+        elems: [
+            {
+                name: 'a',
+                fullPath: ['a'],
+                pathWithoutLast: [],
+                pathLast: 'a',
+                generics: [],
+            },
+            {
+                name: 'b',
+                fullPath: ['b'],
+                pathWithoutLast: [],
+                pathLast: 'b',
+                generics: [],
+            },
+        ],
+        foundElems: 2,
+        original: "a\tb",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a\tb",
+        error: null,
+    },
+    {
+        elems: [
+            {
+                name: 'a',
+                fullPath: ['a'],
+                pathWithoutLast: [],
+                pathLast: 'a',
+                generics: [
+                    {
+                        name: 'b',
+                        fullPath: ['b'],
+                        pathWithoutLast: [],
+                        pathLast: 'b',
+                        generics: [],
+                    },
+                    {
+                        name: 'c',
+                        fullPath: ['c'],
+                        pathWithoutLast: [],
+                        pathLast: 'c',
+                        generics: [],
+                    },
+                ],
+            },
+        ],
+        foundElems: 1,
+        original: "a<b c>",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a<b c>",
+        error: null,
+    },
+    {
+        elems: [
+            {
+                name: 'a',
+                fullPath: ['a'],
+                pathWithoutLast: [],
+                pathLast: 'a',
+                generics: [
+                    {
+                        name: 'b',
+                        fullPath: ['b'],
+                        pathWithoutLast: [],
+                        pathLast: 'b',
+                        generics: [],
+                    },
+                    {
+                        name: 'c',
+                        fullPath: ['c'],
+                        pathWithoutLast: [],
+                        pathLast: 'c',
+                        generics: [],
+                    },
+                ],
+            },
+        ],
+        foundElems: 1,
+        original: "a<b,c>",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a<b,c>",
+        error: null,
+    },
+    {
+        elems: [
+            {
+                name: 'a',
+                fullPath: ['a'],
+                pathWithoutLast: [],
+                pathLast: 'a',
+                generics: [
+                    {
+                        name: 'b',
+                        fullPath: ['b'],
+                        pathWithoutLast: [],
+                        pathLast: 'b',
+                        generics: [],
+                    },
+                    {
+                        name: 'c',
+                        fullPath: ['c'],
+                        pathWithoutLast: [],
+                        pathLast: 'c',
+                        generics: [],
+                    },
+                ],
+            },
+        ],
+        foundElems: 1,
+        original: "a<b\tc>",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a<b\tc>",
+        error: null,
+    },
+];
diff --git a/src/test/rustdoc-js-std/parser-weird-queries.js b/src/test/rustdoc-js-std/parser-weird-queries.js
new file mode 100644
index 00000000000..a3d85aeca5e
--- /dev/null
+++ b/src/test/rustdoc-js-std/parser-weird-queries.js
@@ -0,0 +1,123 @@
+// This test is mostly to check that the parser still kinda outputs something
+// (and doesn't enter an infinite loop!) even though the query is completely
+// invalid.
+const QUERY = [
+    'a b',
+    'a   b',
+    'a,b(c)',
+    'aaa,a',
+    ',,,,',
+    'mod    :',
+    'mod\t:',
+];
+
+const PARSED = [
+    {
+        elems: [
+            {
+                name: "a",
+                fullPath: ["a"],
+                pathWithoutLast: [],
+                pathLast: "a",
+                generics: [],
+            },
+            {
+                name: "b",
+                fullPath: ["b"],
+                pathWithoutLast: [],
+                pathLast: "b",
+                generics: [],
+            },
+        ],
+        foundElems: 2,
+        original: "a b",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a b",
+        error: null,
+    },
+    {
+        elems: [
+            {
+                name: "a",
+                fullPath: ["a"],
+                pathWithoutLast: [],
+                pathLast: "a",
+                generics: [],
+            },
+            {
+                name: "b",
+                fullPath: ["b"],
+                pathWithoutLast: [],
+                pathLast: "b",
+                generics: [],
+            },
+        ],
+        foundElems: 2,
+        original: "a   b",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a   b",
+        error: null,
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "a,b(c)",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a,b(c)",
+        error: "Unexpected `(`",
+    },
+    {
+        elems: [
+            {
+                name: "aaa",
+                fullPath: ["aaa"],
+                pathWithoutLast: [],
+                pathLast: "aaa",
+                generics: [],
+            },
+            {
+                name: "a",
+                fullPath: ["a"],
+                pathWithoutLast: [],
+                pathLast: "a",
+                generics: [],
+            },
+        ],
+        foundElems: 2,
+        original: "aaa,a",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "aaa,a",
+        error: null,
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: ",,,,",
+        returned: [],
+        typeFilter: -1,
+        userQuery: ",,,,",
+        error: null,
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: 'mod    :',
+        returned: [],
+        typeFilter: 0,
+        userQuery: 'mod    :',
+        error: null,
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: 'mod\t:',
+        returned: [],
+        typeFilter: 0,
+        userQuery: 'mod\t:',
+        error: null,
+    },
+];
diff --git a/src/test/rustdoc-js-std/quoted.js b/src/test/rustdoc-js-std/quoted.js
index 924129f86c8..aec8484a41f 100644
--- a/src/test/rustdoc-js-std/quoted.js
+++ b/src/test/rustdoc-js-std/quoted.js
@@ -1,4 +1,7 @@
+// ignore-order
+
 const QUERY = '"error"';
+const FILTER_CRATE = 'std';
 
 const EXPECTED = {
     'others': [
@@ -6,7 +9,12 @@ const EXPECTED = {
         { 'path': 'std::fmt', 'name': 'Error' },
         { 'path': 'std::io', 'name': 'Error' },
     ],
-    'in_args': [],
+    'in_args': [
+        { 'path': 'std::fmt::Error', 'name': 'eq' },
+        { 'path': 'std::fmt::Error', 'name': 'cmp' },
+        { 'path': 'std::fmt::Error', 'name': 'partial_cmp' },
+
+    ],
     'returned': [
         { 'path': 'std::fmt::LowerExp', 'name': 'fmt' },
     ],
diff --git a/src/test/rustdoc-js-std/struct-vec.js b/src/test/rustdoc-js-std/struct-vec.js
index 2c808143bae..29609904b19 100644
--- a/src/test/rustdoc-js-std/struct-vec.js
+++ b/src/test/rustdoc-js-std/struct-vec.js
@@ -1,8 +1,8 @@
-const QUERY = 'struct:Vec';
+const QUERY = 'struct:VecD';
 
 const EXPECTED = {
     'others': [
-        { 'path': 'std::vec', 'name': 'Vec' },
         { 'path': 'std::collections', 'name': 'VecDeque' },
+        { 'path': 'std::vec', 'name': 'Vec' },
     ],
 };
diff --git a/src/test/rustdoc-js-std/typed-query.js b/src/test/rustdoc-js-std/typed-query.js
index 3915ee7dc5d..25efbad2695 100644
--- a/src/test/rustdoc-js-std/typed-query.js
+++ b/src/test/rustdoc-js-std/typed-query.js
@@ -1,6 +1,7 @@
 // exact-check
 
 const QUERY = 'macro:print';
+const FILTER_CRATE = 'std';
 
 const EXPECTED = {
     'others': [
@@ -9,6 +10,8 @@ const EXPECTED = {
         { 'path': 'std', 'name': 'println' },
         { 'path': 'std', 'name': 'eprintln' },
         { 'path': 'std::pin', 'name': 'pin' },
-        { 'path': 'core::pin', 'name': 'pin' },
+        { 'path': 'std::future', 'name': 'join' },
+        { 'path': 'std', 'name': 'line' },
+        { 'path': 'std', 'name': 'write' },
     ],
 };
diff --git a/src/test/rustdoc-js-std/vec-new.js b/src/test/rustdoc-js-std/vec-new.js
index e1a3256876b..cd0e8e7b4a9 100644
--- a/src/test/rustdoc-js-std/vec-new.js
+++ b/src/test/rustdoc-js-std/vec-new.js
@@ -4,6 +4,6 @@ const EXPECTED = {
     'others': [
         { 'path': 'std::vec::Vec', 'name': 'new' },
         { 'path': 'std::vec::Vec', 'name': 'ne' },
-        { 'path': 'std::rc::Rc', 'name': 'ne' },
+        { 'path': 'alloc::vec::Vec', 'name': 'ne' },
     ],
 };
diff --git a/src/test/rustdoc-js/doc-alias-filter.js b/src/test/rustdoc-js/doc-alias-filter.js
index 4b1e2e29704..e06047ba760 100644
--- a/src/test/rustdoc-js/doc-alias-filter.js
+++ b/src/test/rustdoc-js/doc-alias-filter.js
@@ -1,6 +1,6 @@
 // exact-check
 
-const QUERY = 'true';
+const QUERY = '"true"';
 
 const FILTER_CRATE = 'doc_alias_filter';
 
diff --git a/src/test/rustdoc-js/doc-alias.js b/src/test/rustdoc-js/doc-alias.js
index ff188d51458..7bb0cbe388f 100644
--- a/src/test/rustdoc-js/doc-alias.js
+++ b/src/test/rustdoc-js/doc-alias.js
@@ -27,6 +27,7 @@ const QUERY = [
 
 const EXPECTED = [
     {
+        // StructItem
         'others': [
             {
                 'path': 'doc_alias',
@@ -38,6 +39,7 @@ const EXPECTED = [
         ],
     },
     {
+        // StructFieldItem
         'others': [
             {
                 'path': 'doc_alias::Struct',
@@ -49,6 +51,7 @@ const EXPECTED = [
         ],
     },
     {
+        // StructMethodItem
         'others': [
             {
                 'path': 'doc_alias::Struct',
@@ -76,6 +79,7 @@ const EXPECTED = [
         ],
     },
     {
+        // ImplTraitFunction
         'others': [
             {
                 'path': 'doc_alias::Struct',
@@ -87,6 +91,7 @@ const EXPECTED = [
         ],
     },
     {
+        // EnumItem
         'others': [
             {
                 'path': 'doc_alias',
@@ -98,6 +103,7 @@ const EXPECTED = [
         ],
     },
     {
+        // VariantItem
         'others': [
             {
                 'path': 'doc_alias::Enum',
@@ -109,6 +115,7 @@ const EXPECTED = [
         ],
     },
     {
+        // EnumMethodItem
         'others': [
             {
                 'path': 'doc_alias::Enum',
@@ -120,6 +127,7 @@ const EXPECTED = [
         ],
     },
     {
+        // TypedefItem
         'others': [
             {
                 'path': 'doc_alias',
@@ -131,6 +139,7 @@ const EXPECTED = [
         ],
     },
     {
+        // TraitItem
         'others': [
             {
                 'path': 'doc_alias',
@@ -142,6 +151,7 @@ const EXPECTED = [
         ],
     },
     {
+        // TraitTypeItem
         'others': [
             {
                 'path': 'doc_alias::Trait',
@@ -153,6 +163,7 @@ const EXPECTED = [
         ],
     },
     {
+        // AssociatedConstItem
         'others': [
             {
                 'path': 'doc_alias::Trait',
@@ -164,6 +175,7 @@ const EXPECTED = [
         ],
     },
     {
+        // TraitFunctionItem
         'others': [
             {
                 'path': 'doc_alias::Trait',
@@ -175,6 +187,7 @@ const EXPECTED = [
         ],
     },
     {
+        // FunctionItem
         'others': [
             {
                 'path': 'doc_alias',
@@ -186,6 +199,7 @@ const EXPECTED = [
         ],
     },
     {
+        // ModuleItem
         'others': [
             {
                 'path': 'doc_alias',
@@ -197,6 +211,7 @@ const EXPECTED = [
         ],
     },
     {
+        // ConstItem
         'others': [
             {
                 'path': 'doc_alias',
@@ -212,6 +227,7 @@ const EXPECTED = [
         ],
     },
     {
+        // StaticItem
         'others': [
             {
                 'path': 'doc_alias',
@@ -223,6 +239,7 @@ const EXPECTED = [
         ],
     },
     {
+        // UnionItem
         'others': [
             {
                 'path': 'doc_alias',
@@ -240,6 +257,7 @@ const EXPECTED = [
         ],
     },
     {
+        // UnionFieldItem
         'others': [
             {
                 'path': 'doc_alias::Union',
@@ -251,6 +269,7 @@ const EXPECTED = [
         ],
     },
     {
+        // UnionMethodItem
         'others': [
             {
                 'path': 'doc_alias::Union',
@@ -262,6 +281,7 @@ const EXPECTED = [
         ],
     },
     {
+        // MacroItem
         'others': [
             {
                 'path': 'doc_alias',
diff --git a/src/test/rustdoc-js/generics.js b/src/test/rustdoc-js/generics.js
index 63a9ad53812..5e5ba7cd9ac 100644
--- a/src/test/rustdoc-js/generics.js
+++ b/src/test/rustdoc-js/generics.js
@@ -1,16 +1,18 @@
 // exact-check
 
 const QUERY = [
-    '"R<P>"',
+    'R<P>',
     '"P"',
     'P',
-    '"ExtraCreditStructMulti<ExtraCreditInnerMulti, ExtraCreditInnerMulti>"',
+    'ExtraCreditStructMulti<ExtraCreditInnerMulti, ExtraCreditInnerMulti>',
     'TraitCat',
     'TraitDog',
+    'Result<String>',
 ];
 
 const EXPECTED = [
     {
+        // R<P>
         'returned': [
             { 'path': 'generics', 'name': 'alef' },
         ],
@@ -19,6 +21,7 @@ const EXPECTED = [
         ],
     },
     {
+        // "P"
         'others': [
             { 'path': 'generics', 'name': 'P' },
         ],
@@ -30,29 +33,41 @@ const EXPECTED = [
         ],
     },
     {
+        // P
         'returned': [
             { 'path': 'generics', 'name': 'alef' },
-            { 'path': 'generics', 'name': 'bet' },
         ],
         'in_args': [
             { 'path': 'generics', 'name': 'alpha' },
-            { 'path': 'generics', 'name': 'beta' },
         ],
     },
     {
+        // "ExtraCreditStructMulti"<ExtraCreditInnerMulti, ExtraCreditInnerMulti>
         'in_args': [
             { 'path': 'generics', 'name': 'extracreditlabhomework' },
         ],
         'returned': [],
     },
     {
+        // TraitCat
         'in_args': [
             { 'path': 'generics', 'name': 'gamma' },
         ],
     },
     {
+        // TraitDog
         'in_args': [
             { 'path': 'generics', 'name': 'gamma' },
         ],
     },
+    {
+        // Result<String>
+        'others': [],
+        'returned': [
+            { 'path': 'generics', 'name': 'super_soup' },
+        ],
+        'in_args': [
+            { 'path': 'generics', 'name': 'super_soup' },
+        ],
+    },
 ];
diff --git a/src/test/rustdoc-js/generics.rs b/src/test/rustdoc-js/generics.rs
index 5e11a6d6018..055c51c7ec5 100644
--- a/src/test/rustdoc-js/generics.rs
+++ b/src/test/rustdoc-js/generics.rs
@@ -24,3 +24,5 @@ pub trait TraitCat {}
 pub trait TraitDog {}
 
 pub fn gamma<T: TraitCat + TraitDog>(t: T) {}
+
+pub fn super_soup(s: Result<String, i32>) -> Result<String, i32> { s }
diff --git a/src/test/rustdoc-ui/intra-doc/email-address-localhost.rs b/src/test/rustdoc-ui/intra-doc/email-address-localhost.rs
index 9465e8e7ab9..7a5156e81c4 100644
--- a/src/test/rustdoc-ui/intra-doc/email-address-localhost.rs
+++ b/src/test/rustdoc-ui/intra-doc/email-address-localhost.rs
@@ -1,7 +1,7 @@
 // normalize-stderr-test: "nightly|beta|1\.[0-9][0-9]\.[0-9]" -> "$$CHANNEL"
+// check-pass
 #![deny(warnings)]
 
 //! Email me at <hello@localhost>.
-//~^ ERROR unknown disambiguator `hello`
 
 //! This should *not* warn: <hello@example.com>.
diff --git a/src/test/rustdoc-ui/intra-doc/email-address-localhost.stderr b/src/test/rustdoc-ui/intra-doc/email-address-localhost.stderr
deleted file mode 100644
index 1b07828fc6e..00000000000
--- a/src/test/rustdoc-ui/intra-doc/email-address-localhost.stderr
+++ /dev/null
@@ -1,16 +0,0 @@
-error: unknown disambiguator `hello`
-  --> $DIR/email-address-localhost.rs:4:18
-   |
-LL | //! Email me at <hello@localhost>.
-   |                  ^^^^^
-   |
-note: the lint level is defined here
-  --> $DIR/email-address-localhost.rs:2:9
-   |
-LL | #![deny(warnings)]
-   |         ^^^^^^^^
-   = note: `#[deny(rustdoc::broken_intra_doc_links)]` implied by `#[deny(warnings)]`
-   = note: see https://doc.rust-lang.org/$CHANNEL/rustdoc/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators
-
-error: aborting due to previous error
-
diff --git a/src/test/rustdoc-ui/intra-doc/macro-rules-error.rs b/src/test/rustdoc-ui/intra-doc/macro-rules-error.rs
new file mode 100644
index 00000000000..84d63c20aa8
--- /dev/null
+++ b/src/test/rustdoc-ui/intra-doc/macro-rules-error.rs
@@ -0,0 +1,27 @@
+// `macro_rules` scopes are respected during doc link resolution.
+
+// compile-flags: --document-private-items
+
+#![deny(rustdoc::broken_intra_doc_links)]
+
+mod no_escape {
+    macro_rules! before_but_limited_to_module {
+        () => {};
+    }
+}
+
+/// [before_but_limited_to_module] FIXME: This error should be reported
+// ERROR unresolved link to `before_but_limited_to_module`
+/// [after] FIXME: This error should be reported
+// ERROR unresolved link to `after`
+/// [str] FIXME: This error shouldn not be reported
+//~^ ERROR `str` is both a builtin type and a macro
+fn check() {}
+
+macro_rules! after {
+    () => {};
+}
+
+macro_rules! str {
+    () => {};
+}
diff --git a/src/test/rustdoc-ui/intra-doc/macro-rules-error.stderr b/src/test/rustdoc-ui/intra-doc/macro-rules-error.stderr
new file mode 100644
index 00000000000..4b984f4f6c0
--- /dev/null
+++ b/src/test/rustdoc-ui/intra-doc/macro-rules-error.stderr
@@ -0,0 +1,22 @@
+error: `str` is both a builtin type and a macro
+  --> $DIR/macro-rules-error.rs:17:6
+   |
+LL | /// [str] FIXME: This error shouldn not be reported
+   |      ^^^ ambiguous link
+   |
+note: the lint level is defined here
+  --> $DIR/macro-rules-error.rs:5:9
+   |
+LL | #![deny(rustdoc::broken_intra_doc_links)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: to link to the builtin type, prefix with `prim@`
+   |
+LL | /// [prim@str] FIXME: This error shouldn not be reported
+   |      +++++
+help: to link to the macro, add an exclamation mark
+   |
+LL | /// [str!] FIXME: This error shouldn not be reported
+   |         +
+
+error: aborting due to previous error
+
diff --git a/src/test/rustdoc-ui/intra-doc/macro-rules.rs b/src/test/rustdoc-ui/intra-doc/macro-rules.rs
index a14e4bdf1d7..3aeb370ef6d 100644
--- a/src/test/rustdoc-ui/intra-doc/macro-rules.rs
+++ b/src/test/rustdoc-ui/intra-doc/macro-rules.rs
@@ -7,3 +7,18 @@ macro_rules! foo {
 
 /// [foo!]
 pub fn baz() {}
+
+#[macro_use]
+mod macros {
+    macro_rules! escaping {
+        () => {};
+    }
+}
+
+pub mod inner {
+    /// [foo!]
+    /// [escaping]
+    pub fn baz() {
+        foo!();
+    }
+}
diff --git a/src/test/rustdoc-ui/issue-91713.stdout b/src/test/rustdoc-ui/issue-91713.stdout
index d0372d4945f..a19e452b459 100644
--- a/src/test/rustdoc-ui/issue-91713.stdout
+++ b/src/test/rustdoc-ui/issue-91713.stdout
@@ -1,7 +1,6 @@
 Available passes for running rustdoc:
 check_doc_test_visibility - run various visibility-related lints on doctests
         strip-hidden - strips all `#[doc(hidden)]` items from the output
-   unindent-comments - removes excess indentation on comments in order for markdown to like it
        strip-private - strips all private items from a crate which cannot be seen externally, implies strip-priv-imports
   strip-priv-imports - strips all private import statements (`use`, `extern crate`) from a crate
    propagate-doc-cfg - propagates `#[doc(cfg(...))]` to child items
@@ -14,7 +13,6 @@ check-invalid-html-tags - detects invalid HTML tags in doc comments
 
 Default passes for rustdoc:
  collect-trait-impls
-   unindent-comments
 check_doc_test_visibility
         strip-hidden  (when not --document-hidden-items)
        strip-private  (when not --document-private-items)
diff --git a/src/test/rustdoc/deref-slice-core.rs b/src/test/rustdoc/deref-slice-core.rs
new file mode 100644
index 00000000000..cccf273a820
--- /dev/null
+++ b/src/test/rustdoc/deref-slice-core.rs
@@ -0,0 +1,22 @@
+// https://github.com/rust-lang/rust/issues/95325
+//
+// Show methods reachable from Deref of primitive.
+#![no_std]
+
+use core::ops::Deref;
+
+// @has 'deref_slice_core/struct.MyArray.html'
+// @has '-' '//*[@id="deref-methods-%5BT%5D"]' 'Methods from Deref<Target = [T]>'
+// @has '-' '//*[@class="impl-items"]//*[@id="method.len"]' 'pub fn len(&self)'
+
+pub struct MyArray<T> {
+    array: [T; 10],
+}
+
+impl<T> Deref for MyArray<T> {
+    type Target = [T];
+
+    fn deref(&self) -> &Self::Target {
+        &self.array
+    }
+}
diff --git a/src/test/rustdoc/early-unindent.rs b/src/test/rustdoc/early-unindent.rs
new file mode 100644
index 00000000000..791a452c957
--- /dev/null
+++ b/src/test/rustdoc/early-unindent.rs
@@ -0,0 +1,26 @@
+// This is a regression for https://github.com/rust-lang/rust/issues/96079.
+
+#![crate_name = "foo"]
+
+pub mod app {
+    pub struct S;
+
+    impl S {
+        // @has 'foo/app/struct.S.html'
+        // @has - '//a[@href="../enums/enum.Foo.html#method.by_name"]' 'Foo::by_name'
+        /**
+        Doc comment hello! [`Foo::by_name`](`crate::enums::Foo::by_name`).
+        */
+        pub fn whatever(&self) {}
+    }
+}
+
+pub mod enums {
+    pub enum Foo {
+        Bar,
+    }
+
+    impl Foo {
+        pub fn by_name(&self) {}
+    }
+}
diff --git a/src/test/rustdoc/issue-95873.rs b/src/test/rustdoc/issue-95873.rs
new file mode 100644
index 00000000000..ff33fb63a0b
--- /dev/null
+++ b/src/test/rustdoc/issue-95873.rs
@@ -0,0 +1,2 @@
+// @has issue_95873/index.html "//*[@class='item-left import-item']" "pub use ::std as x;"
+pub use ::std as x;
diff --git a/src/test/rustdoc/issue-96381.rs b/src/test/rustdoc/issue-96381.rs
new file mode 100644
index 00000000000..f0f123f85a0
--- /dev/null
+++ b/src/test/rustdoc/issue-96381.rs
@@ -0,0 +1,16 @@
+// should-fail
+
+#![allow(unused)]
+
+trait Foo<T>: Sized {
+    fn bar(i: i32, t: T, s: &Self) -> (T, i32);
+}
+
+impl Foo<usize> for () {
+    fn bar(i: _, t: _, s: _) -> _ {
+        //~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions
+        (1, 2)
+    }
+}
+
+fn main() {}
diff --git a/src/test/rustdoc/where.SWhere_Simd_item-decl.html b/src/test/rustdoc/where.SWhere_Simd_item-decl.html
new file mode 100644
index 00000000000..0133bcaeb66
--- /dev/null
+++ b/src/test/rustdoc/where.SWhere_Simd_item-decl.html
@@ -0,0 +1 @@
+<div class="docblock item-decl"><pre class="rust struct"><code>pub struct Simd&lt;T&gt;(_) <br /><span class="where">where<br />&#160;&#160;&#160;&#160;T: <a class="trait" href="trait.MyTrait.html" title="trait foo::MyTrait">MyTrait</a></span>;</code></pre></div>
\ No newline at end of file
diff --git a/src/test/rustdoc/where.SWhere_TraitWhere_item-decl.html b/src/test/rustdoc/where.SWhere_TraitWhere_item-decl.html
new file mode 100644
index 00000000000..54026ff034e
--- /dev/null
+++ b/src/test/rustdoc/where.SWhere_TraitWhere_item-decl.html
@@ -0,0 +1,3 @@
+<div class="docblock item-decl"><pre class="rust trait"><code>pub trait TraitWhere {
+    type <a href="#associatedtype.Item" class="associatedtype">Item</a>&lt;'a&gt;<br />&#160;&#160;&#160; <span class="where">where<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Self: 'a</span>;
+}</code></pre></div>
\ No newline at end of file
diff --git a/src/test/rustdoc/where.rs b/src/test/rustdoc/where.rs
index 549cfff96cb..50a5722fbaf 100644
--- a/src/test/rustdoc/where.rs
+++ b/src/test/rustdoc/where.rs
@@ -1,3 +1,4 @@
+#![feature(generic_associated_types)]
 #![crate_name = "foo"]
 
 pub trait MyTrait { fn dummy(&self) { } }
@@ -19,6 +20,18 @@ impl<D> Delta<D> where D: MyTrait {
 
 pub struct Echo<E>(E);
 
+// @has 'foo/struct.Simd.html'
+// @snapshot SWhere_Simd_item-decl - '//div[@class="docblock item-decl"]'
+pub struct Simd<T>([T; 1])
+where
+    T: MyTrait;
+
+// @has 'foo/trait.TraitWhere.html'
+// @snapshot SWhere_TraitWhere_item-decl - '//div[@class="docblock item-decl"]'
+pub trait TraitWhere {
+    type Item<'a> where Self: 'a;
+}
+
 // @has foo/struct.Echo.html '//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \
 //          "impl<E> MyTrait for Echo<E> where E: MyTrait"
 // @has foo/trait.MyTrait.html '//*[@id="implementors-list"]//h3[@class="code-header in-band"]' \
diff --git a/src/test/ui-fulldeps/issue-15149.rs b/src/test/ui-fulldeps/issue-15149.rs
index c7ef5ad70a1..064472f5785 100644
--- a/src/test/ui-fulldeps/issue-15149.rs
+++ b/src/test/ui-fulldeps/issue-15149.rs
@@ -5,10 +5,11 @@
 // ignore-cross-compile
 
 use std::env;
+use std::ffi::OsStr;
 use std::fs;
+use std::path::PathBuf;
 use std::process;
 use std::str;
-use std::path::PathBuf;
 
 fn main() {
     // If we're the child, make sure we were invoked correctly
@@ -18,8 +19,8 @@ fn main() {
         // checking that it ends_with the executable name. This
         // is needed because of Windows, which has a different behavior.
         // See #15149 for more info.
-        return assert!(args[0].ends_with(&format!("mytest{}",
-                                                  env::consts::EXE_SUFFIX)));
+        let my_path = env::current_exe().unwrap();
+        return assert_eq!(my_path.file_stem(), Some(OsStr::new("mytest")));
     }
 
     test();
@@ -28,14 +29,13 @@ fn main() {
 fn test() {
     // If we're the parent, copy our own binary to a new directory.
     let my_path = env::current_exe().unwrap();
-    let my_dir  = my_path.parent().unwrap();
+    let my_dir = my_path.parent().unwrap();
 
     let child_dir = PathBuf::from(env::var_os("RUST_TEST_TMPDIR").unwrap());
     let child_dir = child_dir.join("issue-15140-child");
     fs::create_dir_all(&child_dir).unwrap();
 
-    let child_path = child_dir.join(&format!("mytest{}",
-                                             env::consts::EXE_SUFFIX));
+    let child_path = child_dir.join(&format!("mytest{}", env::consts::EXE_SUFFIX));
     fs::copy(&my_path, &child_path).unwrap();
 
     // Append the new directory to our own PATH.
@@ -45,12 +45,13 @@ fn test() {
         env::join_paths(paths).unwrap()
     };
 
-    let child_output = process::Command::new("mytest").env("PATH", &path)
-                                                      .arg("child")
-                                                      .output().unwrap();
+    let child_output =
+        process::Command::new("mytest").env("PATH", &path).arg("child").output().unwrap();
 
-    assert!(child_output.status.success(),
-            "child assertion failed\n child stdout:\n {}\n child stderr:\n {}",
-            str::from_utf8(&child_output.stdout).unwrap(),
-            str::from_utf8(&child_output.stderr).unwrap());
+    assert!(
+        child_output.status.success(),
+        "child assertion failed\n child stdout:\n {}\n child stderr:\n {}",
+        str::from_utf8(&child_output.stdout).unwrap(),
+        str::from_utf8(&child_output.stderr).unwrap()
+    );
 }
diff --git a/src/test/ui-fulldeps/session-derive-errors.rs b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs
index adec548b390..efbf78ac87d 100644
--- a/src/test/ui-fulldeps/session-derive-errors.rs
+++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs
@@ -15,7 +15,7 @@ use rustc_span::symbol::Ident;
 use rustc_span::Span;
 
 extern crate rustc_macros;
-use rustc_macros::SessionDiagnostic;
+use rustc_macros::{SessionDiagnostic, SessionSubdiagnostic};
 
 extern crate rustc_middle;
 use rustc_middle::ty::Ty;
@@ -44,67 +44,74 @@ enum SessionDiagnosticOnEnum {
 #[derive(SessionDiagnostic)]
 #[error(code = "E0123", slug = "foo")]
 #[error = "E0123"]
-//~^ ERROR `#[error = ...]` is not a valid `SessionDiagnostic` struct attribute
+//~^ ERROR `#[error = ...]` is not a valid attribute
 struct WrongStructAttrStyle {}
 
 #[derive(SessionDiagnostic)]
 #[nonsense(code = "E0123", slug = "foo")]
-//~^ ERROR `#[nonsense(...)]` is not a valid `SessionDiagnostic` struct attribute
+//~^ ERROR `#[nonsense(...)]` is not a valid attribute
 //~^^ ERROR diagnostic kind not specified
 //~^^^ ERROR cannot find attribute `nonsense` in this scope
 struct InvalidStructAttr {}
 
 #[derive(SessionDiagnostic)]
 #[error("E0123")]
-//~^ ERROR `#[error("...")]` is not a valid `SessionDiagnostic` struct attribute
+//~^ ERROR `#[error("...")]` is not a valid attribute
 //~^^ ERROR `slug` not specified
 struct InvalidLitNestedAttr {}
 
 #[derive(SessionDiagnostic)]
 #[error(nonsense, code = "E0123", slug = "foo")]
-//~^ ERROR `#[error(nonsense)]` is not a valid `SessionDiagnostic` struct attribute
+//~^ ERROR `#[error(nonsense)]` is not a valid attribute
 struct InvalidNestedStructAttr {}
 
 #[derive(SessionDiagnostic)]
 #[error(nonsense("foo"), code = "E0123", slug = "foo")]
-//~^ ERROR `#[error(nonsense(...))]` is not a valid `SessionDiagnostic` struct attribute
+//~^ ERROR `#[error(nonsense(...))]` is not a valid attribute
 struct InvalidNestedStructAttr1 {}
 
 #[derive(SessionDiagnostic)]
 #[error(nonsense = "...", code = "E0123", slug = "foo")]
-//~^ ERROR `#[error(nonsense = ...)]` is not a valid `SessionDiagnostic` struct attribute
+//~^ ERROR `#[error(nonsense = ...)]` is not a valid attribute
 struct InvalidNestedStructAttr2 {}
 
 #[derive(SessionDiagnostic)]
 #[error(nonsense = 4, code = "E0123", slug = "foo")]
-//~^ ERROR `#[error(nonsense = ...)]` is not a valid `SessionDiagnostic` struct attribute
+//~^ ERROR `#[error(nonsense = ...)]` is not a valid attribute
 struct InvalidNestedStructAttr3 {}
 
 #[derive(SessionDiagnostic)]
 #[error(code = "E0123", slug = "foo")]
 struct WrongPlaceField {
     #[suggestion = "bar"]
-    //~^ ERROR `#[suggestion = ...]` is not a valid `SessionDiagnostic` field attribute
+    //~^ ERROR `#[suggestion = ...]` is not a valid attribute
     sp: Span,
 }
 
 #[derive(SessionDiagnostic)]
 #[error(code = "E0123", slug = "foo")]
-#[error(code = "E0456", slug = "bar")] //~ ERROR `error` specified multiple times
+#[error(code = "E0456", slug = "bar")]
+//~^ ERROR specified multiple times
+//~^^ ERROR specified multiple times
+//~^^^ ERROR specified multiple times
 struct ErrorSpecifiedTwice {}
 
 #[derive(SessionDiagnostic)]
 #[error(code = "E0123", slug = "foo")]
 #[warning(code = "E0293", slug = "bar")]
-//~^ ERROR `warning` specified when `error` was already specified
+//~^ ERROR specified multiple times
+//~^^ ERROR specified multiple times
+//~^^^ ERROR specified multiple times
 struct WarnSpecifiedAfterError {}
 
 #[derive(SessionDiagnostic)]
-#[error(code = "E0456", code = "E0457", slug = "bar")] //~ ERROR `code` specified multiple times
+#[error(code = "E0456", code = "E0457", slug = "bar")]
+//~^ ERROR specified multiple times
 struct CodeSpecifiedTwice {}
 
 #[derive(SessionDiagnostic)]
-#[error(code = "E0456", slug = "foo", slug = "bar")] //~ ERROR `slug` specified multiple times
+#[error(code = "E0456", slug = "foo", slug = "bar")]
+//~^ ERROR specified multiple times
 struct SlugSpecifiedTwice {}
 
 #[derive(SessionDiagnostic)]
@@ -130,7 +137,7 @@ struct MessageWrongType {
 #[error(code = "E0123", slug = "foo")]
 struct InvalidPathFieldAttr {
     #[nonsense]
-    //~^ ERROR `#[nonsense]` is not a valid `SessionDiagnostic` field attribute
+    //~^ ERROR `#[nonsense]` is not a valid attribute
     //~^^ ERROR cannot find attribute `nonsense` in this scope
     foo: String,
 }
@@ -215,7 +222,7 @@ struct SuggestWithoutCode {
 #[error(code = "E0123", slug = "foo")]
 struct SuggestWithBadKey {
     #[suggestion(nonsense = "bar")]
-    //~^ ERROR `#[suggestion(nonsense = ...)]` is not a valid `SessionDiagnostic` field attribute
+    //~^ ERROR `#[suggestion(nonsense = ...)]` is not a valid attribute
     suggestion: (Span, Applicability),
 }
 
@@ -223,7 +230,7 @@ struct SuggestWithBadKey {
 #[error(code = "E0123", slug = "foo")]
 struct SuggestWithShorthandMsg {
     #[suggestion(msg = "bar")]
-    //~^ ERROR `#[suggestion(msg = ...)]` is not a valid `SessionDiagnostic` field attribute
+    //~^ ERROR `#[suggestion(msg = ...)]` is not a valid attribute
     suggestion: (Span, Applicability),
 }
 
@@ -276,7 +283,7 @@ struct SuggestWithDuplicateApplicabilityAndSpan {
 #[error(code = "E0123", slug = "foo")]
 struct WrongKindOfAnnotation {
     #[label("bar")]
-    //~^ ERROR `#[label(...)]` is not a valid `SessionDiagnostic` field attribute
+    //~^ ERROR `#[label(...)]` is not a valid attribute
     z: Span,
 }
 
@@ -426,3 +433,44 @@ struct ErrorWithNoteWrongOrder {
 struct ErrorWithNoteCustomWrongOrder {
     val: String,
 }
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct ApplicabilityInBoth {
+    #[suggestion(message = "bar", code = "...", applicability = "maybe-incorrect")]
+    //~^ ERROR applicability cannot be set in both the field and attribute
+    suggestion: (Span, Applicability),
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct InvalidApplicability {
+    #[suggestion(message = "bar", code = "...", applicability = "batman")]
+    //~^ ERROR invalid applicability
+    suggestion: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct ValidApplicability {
+    #[suggestion(message = "bar", code = "...", applicability = "maybe-incorrect")]
+    suggestion: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct NoApplicability {
+    #[suggestion(message = "bar", code = "...")]
+    suggestion: Span,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[note(slug = "note")]
+struct Note;
+
+#[derive(SessionDiagnostic)]
+#[error(slug = "subdiagnostic")]
+struct Subdiagnostic {
+    #[subdiagnostic]
+    note: Note,
+}
diff --git a/src/test/ui-fulldeps/session-derive-errors.stderr b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr
index a528ae1607f..b1738b60bc0 100644
--- a/src/test/ui-fulldeps/session-derive-errors.stderr
+++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr
@@ -1,5 +1,5 @@
 error: `#[derive(SessionDiagnostic)]` can only be used on structs
-  --> $DIR/session-derive-errors.rs:37:1
+  --> $DIR/diagnostic-derive.rs:37:1
    |
 LL | / #[error(code = "E0123", slug = "foo")]
 LL | |
@@ -9,20 +9,22 @@ LL | |     Bar,
 LL | | }
    | |_^
 
-error: `#[error = ...]` is not a valid `SessionDiagnostic` struct attribute
-  --> $DIR/session-derive-errors.rs:46:1
+error: `#[error = ...]` is not a valid attribute
+  --> $DIR/diagnostic-derive.rs:46:1
    |
 LL | #[error = "E0123"]
    | ^^^^^^^^^^^^^^^^^^
 
-error: `#[nonsense(...)]` is not a valid `SessionDiagnostic` struct attribute
-  --> $DIR/session-derive-errors.rs:51:1
+error: `#[nonsense(...)]` is not a valid attribute
+  --> $DIR/diagnostic-derive.rs:51:1
    |
 LL | #[nonsense(code = "E0123", slug = "foo")]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: only `error` and `warning` are valid attributes
 
 error: diagnostic kind not specified
-  --> $DIR/session-derive-errors.rs:51:1
+  --> $DIR/diagnostic-derive.rs:51:1
    |
 LL | / #[nonsense(code = "E0123", slug = "foo")]
 LL | |
@@ -33,14 +35,14 @@ LL | | struct InvalidStructAttr {}
    |
    = help: use the `#[error(...)]` attribute to create an error
 
-error: `#[error("...")]` is not a valid `SessionDiagnostic` struct attribute
-  --> $DIR/session-derive-errors.rs:58:9
+error: `#[error("...")]` is not a valid attribute
+  --> $DIR/diagnostic-derive.rs:58:9
    |
 LL | #[error("E0123")]
    |         ^^^^^^^
 
 error: `slug` not specified
-  --> $DIR/session-derive-errors.rs:58:1
+  --> $DIR/diagnostic-derive.rs:58:1
    |
 LL | / #[error("E0123")]
 LL | |
@@ -50,88 +52,138 @@ LL | | struct InvalidLitNestedAttr {}
    |
    = help: use the `#[error(slug = "...")]` attribute to set this diagnostic's slug
 
-error: `#[error(nonsense)]` is not a valid `SessionDiagnostic` struct attribute
-  --> $DIR/session-derive-errors.rs:64:9
+error: `#[error(nonsense)]` is not a valid attribute
+  --> $DIR/diagnostic-derive.rs:64:9
    |
 LL | #[error(nonsense, code = "E0123", slug = "foo")]
    |         ^^^^^^^^
 
-error: `#[error(nonsense(...))]` is not a valid `SessionDiagnostic` struct attribute
-  --> $DIR/session-derive-errors.rs:69:9
+error: `#[error(nonsense(...))]` is not a valid attribute
+  --> $DIR/diagnostic-derive.rs:69:9
    |
 LL | #[error(nonsense("foo"), code = "E0123", slug = "foo")]
    |         ^^^^^^^^^^^^^^^
 
-error: `#[error(nonsense = ...)]` is not a valid `SessionDiagnostic` struct attribute
-  --> $DIR/session-derive-errors.rs:74:9
+error: `#[error(nonsense = ...)]` is not a valid attribute
+  --> $DIR/diagnostic-derive.rs:74:9
    |
 LL | #[error(nonsense = "...", code = "E0123", slug = "foo")]
    |         ^^^^^^^^^^^^^^^^
+   |
+   = help: only `slug` and `code` are valid nested attributes
 
-error: `#[error(nonsense = ...)]` is not a valid `SessionDiagnostic` struct attribute
-  --> $DIR/session-derive-errors.rs:79:9
+error: `#[error(nonsense = ...)]` is not a valid attribute
+  --> $DIR/diagnostic-derive.rs:79:9
    |
 LL | #[error(nonsense = 4, code = "E0123", slug = "foo")]
    |         ^^^^^^^^^^^^
-   |
-   = help: value must be a string
 
-error: `#[suggestion = ...]` is not a valid `SessionDiagnostic` field attribute
-  --> $DIR/session-derive-errors.rs:86:5
+error: `#[suggestion = ...]` is not a valid attribute
+  --> $DIR/diagnostic-derive.rs:86:5
    |
 LL |     #[suggestion = "bar"]
    |     ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: only `label`, `note` and `help` are valid field attributes
 
-error: `error` specified multiple times
-  --> $DIR/session-derive-errors.rs:93:1
+error: specified multiple times
+  --> $DIR/diagnostic-derive.rs:93:1
    |
 LL | #[error(code = "E0456", slug = "bar")]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: previously specified here
-  --> $DIR/session-derive-errors.rs:92:1
+  --> $DIR/diagnostic-derive.rs:92:1
    |
 LL | #[error(code = "E0123", slug = "foo")]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: `warning` specified when `error` was already specified
-  --> $DIR/session-derive-errors.rs:98:1
+error: specified multiple times
+  --> $DIR/diagnostic-derive.rs:93:16
+   |
+LL | #[error(code = "E0456", slug = "bar")]
+   |                ^^^^^^^
+   |
+note: previously specified here
+  --> $DIR/diagnostic-derive.rs:92:16
+   |
+LL | #[error(code = "E0123", slug = "foo")]
+   |                ^^^^^^^
+
+error: specified multiple times
+  --> $DIR/diagnostic-derive.rs:93:32
+   |
+LL | #[error(code = "E0456", slug = "bar")]
+   |                                ^^^^^
+   |
+note: previously specified here
+  --> $DIR/diagnostic-derive.rs:92:32
+   |
+LL | #[error(code = "E0123", slug = "foo")]
+   |                                ^^^^^
+
+error: specified multiple times
+  --> $DIR/diagnostic-derive.rs:101:1
    |
 LL | #[warning(code = "E0293", slug = "bar")]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: previously specified here
-  --> $DIR/session-derive-errors.rs:97:1
+  --> $DIR/diagnostic-derive.rs:100:1
    |
 LL | #[error(code = "E0123", slug = "foo")]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: `code` specified multiple times
-  --> $DIR/session-derive-errors.rs:103:32
+error: specified multiple times
+  --> $DIR/diagnostic-derive.rs:101:18
+   |
+LL | #[warning(code = "E0293", slug = "bar")]
+   |                  ^^^^^^^
+   |
+note: previously specified here
+  --> $DIR/diagnostic-derive.rs:100:16
+   |
+LL | #[error(code = "E0123", slug = "foo")]
+   |                ^^^^^^^
+
+error: specified multiple times
+  --> $DIR/diagnostic-derive.rs:101:34
+   |
+LL | #[warning(code = "E0293", slug = "bar")]
+   |                                  ^^^^^
+   |
+note: previously specified here
+  --> $DIR/diagnostic-derive.rs:100:32
+   |
+LL | #[error(code = "E0123", slug = "foo")]
+   |                                ^^^^^
+
+error: specified multiple times
+  --> $DIR/diagnostic-derive.rs:108:32
    |
 LL | #[error(code = "E0456", code = "E0457", slug = "bar")]
    |                                ^^^^^^^
    |
 note: previously specified here
-  --> $DIR/session-derive-errors.rs:103:16
+  --> $DIR/diagnostic-derive.rs:108:16
    |
 LL | #[error(code = "E0456", code = "E0457", slug = "bar")]
    |                ^^^^^^^
 
-error: `slug` specified multiple times
-  --> $DIR/session-derive-errors.rs:107:46
+error: specified multiple times
+  --> $DIR/diagnostic-derive.rs:113:46
    |
 LL | #[error(code = "E0456", slug = "foo", slug = "bar")]
    |                                              ^^^^^
    |
 note: previously specified here
-  --> $DIR/session-derive-errors.rs:107:32
+  --> $DIR/diagnostic-derive.rs:113:32
    |
 LL | #[error(code = "E0456", slug = "foo", slug = "bar")]
    |                                ^^^^^
 
 error: diagnostic kind not specified
-  --> $DIR/session-derive-errors.rs:111:1
+  --> $DIR/diagnostic-derive.rs:118:1
    |
 LL | struct KindNotProvided {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -139,7 +191,7 @@ LL | struct KindNotProvided {}
    = help: use the `#[error(...)]` attribute to create an error
 
 error: `slug` not specified
-  --> $DIR/session-derive-errors.rs:114:1
+  --> $DIR/diagnostic-derive.rs:121:1
    |
 LL | / #[error(code = "E0456")]
 LL | | struct SlugNotProvided {}
@@ -148,31 +200,33 @@ LL | | struct SlugNotProvided {}
    = help: use the `#[error(slug = "...")]` attribute to set this diagnostic's slug
 
 error: the `#[primary_span]` attribute can only be applied to fields of type `Span`
-  --> $DIR/session-derive-errors.rs:124:5
+  --> $DIR/diagnostic-derive.rs:131:5
    |
 LL |     #[primary_span]
    |     ^^^^^^^^^^^^^^^
 
-error: `#[nonsense]` is not a valid `SessionDiagnostic` field attribute
-  --> $DIR/session-derive-errors.rs:132:5
+error: `#[nonsense]` is not a valid attribute
+  --> $DIR/diagnostic-derive.rs:139:5
    |
 LL |     #[nonsense]
    |     ^^^^^^^^^^^
+   |
+   = help: only `skip_arg`, `primary_span`, `label`, `note`, `help` and `subdiagnostic` are valid field attributes
 
 error: the `#[label = ...]` attribute can only be applied to fields of type `Span`
-  --> $DIR/session-derive-errors.rs:149:5
+  --> $DIR/diagnostic-derive.rs:156:5
    |
 LL |     #[label = "bar"]
    |     ^^^^^^^^^^^^^^^^
 
 error: `name` doesn't refer to a field on this type
-  --> $DIR/session-derive-errors.rs:157:42
+  --> $DIR/diagnostic-derive.rs:164:42
    |
 LL |     #[suggestion(message = "bar", code = "{name}")]
    |                                          ^^^^^^^^
 
 error: invalid format string: expected `'}'` but string was terminated
-  --> $DIR/session-derive-errors.rs:162:16
+  --> $DIR/diagnostic-derive.rs:169:16
    |
 LL | #[derive(SessionDiagnostic)]
    |           -    ^ expected `'}'` in format string
@@ -183,7 +237,7 @@ LL | #[derive(SessionDiagnostic)]
    = note: this error originates in the derive macro `SessionDiagnostic` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: invalid format string: unmatched `}` found
-  --> $DIR/session-derive-errors.rs:172:15
+  --> $DIR/diagnostic-derive.rs:179:15
    |
 LL | #[derive(SessionDiagnostic)]
    |               ^ unmatched `}` in format string
@@ -192,25 +246,29 @@ LL | #[derive(SessionDiagnostic)]
    = note: this error originates in the derive macro `SessionDiagnostic` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: the `#[label = ...]` attribute can only be applied to fields of type `Span`
-  --> $DIR/session-derive-errors.rs:192:5
+  --> $DIR/diagnostic-derive.rs:199:5
    |
 LL |     #[label = "bar"]
    |     ^^^^^^^^^^^^^^^^
 
-error: `#[suggestion(nonsense = ...)]` is not a valid `SessionDiagnostic` field attribute
-  --> $DIR/session-derive-errors.rs:217:18
+error: `#[suggestion(nonsense = ...)]` is not a valid attribute
+  --> $DIR/diagnostic-derive.rs:224:18
    |
 LL |     #[suggestion(nonsense = "bar")]
    |                  ^^^^^^^^^^^^^^^^
+   |
+   = help: only `message`, `code` and `applicability` are valid field attributes
 
-error: `#[suggestion(msg = ...)]` is not a valid `SessionDiagnostic` field attribute
-  --> $DIR/session-derive-errors.rs:225:18
+error: `#[suggestion(msg = ...)]` is not a valid attribute
+  --> $DIR/diagnostic-derive.rs:232:18
    |
 LL |     #[suggestion(msg = "bar")]
    |                  ^^^^^^^^^^^
+   |
+   = help: only `message`, `code` and `applicability` are valid field attributes
 
 error: wrong field type for suggestion
-  --> $DIR/session-derive-errors.rs:247:5
+  --> $DIR/diagnostic-derive.rs:254:5
    |
 LL | /     #[suggestion(message = "bar", code = "This is suggested code")]
 LL | |
@@ -220,7 +278,7 @@ LL | |     suggestion: Applicability,
    = help: `#[suggestion(...)]` should be applied to fields of type `Span` or `(Span, Applicability)`
 
 error: type of field annotated with `#[suggestion(...)]` contains more than one `Span`
-  --> $DIR/session-derive-errors.rs:262:5
+  --> $DIR/diagnostic-derive.rs:269:5
    |
 LL | /     #[suggestion(message = "bar", code = "This is suggested code")]
 LL | |
@@ -228,57 +286,71 @@ LL | |     suggestion: (Span, Span, Applicability),
    | |___________________________________________^
 
 error: type of field annotated with `#[suggestion(...)]` contains more than one Applicability
-  --> $DIR/session-derive-errors.rs:270:5
+  --> $DIR/diagnostic-derive.rs:277:5
    |
 LL | /     #[suggestion(message = "bar", code = "This is suggested code")]
 LL | |
 LL | |     suggestion: (Applicability, Applicability, Span),
    | |____________________________________________________^
 
-error: `#[label(...)]` is not a valid `SessionDiagnostic` field attribute
-  --> $DIR/session-derive-errors.rs:278:5
+error: `#[label(...)]` is not a valid attribute
+  --> $DIR/diagnostic-derive.rs:285:5
    |
 LL |     #[label("bar")]
    |     ^^^^^^^^^^^^^^^
+   |
+   = help: only `suggestion{,_short,_hidden,_verbose}` are valid field attributes
 
 error: `#[help]` must come after `#[error(..)]` or `#[warn(..)]`
-  --> $DIR/session-derive-errors.rs:399:1
+  --> $DIR/diagnostic-derive.rs:406:1
    |
 LL | #[help]
    | ^^^^^^^
 
 error: `#[help = ...]` must come after `#[error(..)]` or `#[warn(..)]`
-  --> $DIR/session-derive-errors.rs:407:1
+  --> $DIR/diagnostic-derive.rs:414:1
    |
 LL | #[help = "bar"]
    | ^^^^^^^^^^^^^^^
 
 error: `#[note]` must come after `#[error(..)]` or `#[warn(..)]`
-  --> $DIR/session-derive-errors.rs:415:1
+  --> $DIR/diagnostic-derive.rs:422:1
    |
 LL | #[note]
    | ^^^^^^^
 
 error: `#[note = ...]` must come after `#[error(..)]` or `#[warn(..)]`
-  --> $DIR/session-derive-errors.rs:423:1
+  --> $DIR/diagnostic-derive.rs:430:1
    |
 LL | #[note = "bar"]
    | ^^^^^^^^^^^^^^^
 
+error: applicability cannot be set in both the field and attribute
+  --> $DIR/diagnostic-derive.rs:440:49
+   |
+LL |     #[suggestion(message = "bar", code = "...", applicability = "maybe-incorrect")]
+   |                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: invalid applicability
+  --> $DIR/diagnostic-derive.rs:448:49
+   |
+LL |     #[suggestion(message = "bar", code = "...", applicability = "batman")]
+   |                                                 ^^^^^^^^^^^^^^^^^^^^^^^^
+
 error: cannot find attribute `nonsense` in this scope
-  --> $DIR/session-derive-errors.rs:51:3
+  --> $DIR/diagnostic-derive.rs:51:3
    |
 LL | #[nonsense(code = "E0123", slug = "foo")]
    |   ^^^^^^^^
 
 error: cannot find attribute `nonsense` in this scope
-  --> $DIR/session-derive-errors.rs:132:7
+  --> $DIR/diagnostic-derive.rs:139:7
    |
 LL |     #[nonsense]
    |       ^^^^^^^^
 
 error[E0599]: no method named `into_diagnostic_arg` found for struct `Hello` in the current scope
-  --> $DIR/session-derive-errors.rs:322:10
+  --> $DIR/diagnostic-derive.rs:329:10
    |
 LL | struct Hello {}
    | ------------ method `into_diagnostic_arg` not found for this
@@ -288,6 +360,6 @@ LL | #[derive(SessionDiagnostic)]
    |
    = note: this error originates in the derive macro `SessionDiagnostic` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: aborting due to 37 previous errors
+error: aborting due to 43 previous errors
 
 For more information about this error, try `rustc --explain E0599`.
diff --git a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs
new file mode 100644
index 00000000000..bb406c35c0e
--- /dev/null
+++ b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs
@@ -0,0 +1,501 @@
+// check-fail
+// Tests error conditions for specifying subdiagnostics using #[derive(SessionSubdiagnostic)]
+
+// The proc_macro2 crate handles spans differently when on beta/stable release rather than nightly,
+// changing the output of this test. Since SessionSubdiagnostic is strictly internal to the compiler
+// the test is just ignored on stable and beta:
+// ignore-beta
+// ignore-stable
+
+#![feature(rustc_private)]
+#![crate_type = "lib"]
+
+extern crate rustc_errors;
+extern crate rustc_session;
+extern crate rustc_span;
+extern crate rustc_macros;
+
+use rustc_errors::Applicability;
+use rustc_span::Span;
+use rustc_macros::SessionSubdiagnostic;
+
+#[derive(SessionSubdiagnostic)]
+#[label(slug = "label-a")]
+struct A {
+    #[primary_span]
+    span: Span,
+    var: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+enum B {
+    #[label(slug = "label-b-a")]
+    A {
+        #[primary_span]
+        span: Span,
+        var: String,
+    },
+    #[label(slug = "label-b-b")]
+    B {
+        #[primary_span]
+        span: Span,
+        var: String,
+    }
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label(slug = "label-c")]
+//~^ ERROR label without `#[primary_span]` field
+struct C {
+    var: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label]
+//~^ ERROR `#[label]` is not a valid attribute
+struct D {
+    #[primary_span]
+    span: Span,
+    var: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[foo]
+//~^ ERROR `#[foo]` is not a valid attribute
+//~^^ ERROR cannot find attribute `foo` in this scope
+struct E {
+    #[primary_span]
+    span: Span,
+    var: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label = "..."]
+//~^ ERROR `#[label = ...]` is not a valid attribute
+struct F {
+    #[primary_span]
+    span: Span,
+    var: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label(bug = "...")]
+//~^ ERROR `#[label(bug = ...)]` is not a valid attribute
+struct G {
+    #[primary_span]
+    span: Span,
+    var: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label("...")]
+//~^ ERROR `#[label("...")]` is not a valid attribute
+struct H {
+    #[primary_span]
+    span: Span,
+    var: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label(slug = 4)]
+//~^ ERROR `#[label(slug = ...)]` is not a valid attribute
+struct J {
+    #[primary_span]
+    span: Span,
+    var: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label(slug("..."))]
+//~^ ERROR `#[label(slug(...))]` is not a valid attribute
+struct K {
+    #[primary_span]
+    span: Span,
+    var: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label(slug)]
+//~^ ERROR `#[label(slug)]` is not a valid attribute
+struct L {
+    #[primary_span]
+    span: Span,
+    var: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label()]
+//~^ ERROR `slug` must be set in a `#[label(...)]` attribute
+struct M {
+    #[primary_span]
+    span: Span,
+    var: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label(code = "...")]
+//~^ ERROR `code` is not a valid nested attribute of a `label` attribute
+struct N {
+    #[primary_span]
+    span: Span,
+    var: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[foo]
+//~^ ERROR cannot find attribute `foo` in this scope
+//~^^ ERROR unsupported type attribute for subdiagnostic enum
+enum O {
+    #[label(slug = "...")]
+    A {
+        #[primary_span]
+        span: Span,
+        var: String,
+    }
+}
+
+#[derive(SessionSubdiagnostic)]
+enum P {
+    #[bar]
+//~^ ERROR `#[bar]` is not a valid attribute
+//~^^ ERROR cannot find attribute `bar` in this scope
+    A {
+        #[primary_span]
+        span: Span,
+        var: String,
+    }
+}
+
+#[derive(SessionSubdiagnostic)]
+enum Q {
+    #[bar = "..."]
+//~^ ERROR `#[bar = ...]` is not a valid attribute
+//~^^ ERROR cannot find attribute `bar` in this scope
+    A {
+        #[primary_span]
+        span: Span,
+        var: String,
+    }
+}
+
+#[derive(SessionSubdiagnostic)]
+enum R {
+    #[bar = 4]
+//~^ ERROR `#[bar = ...]` is not a valid attribute
+//~^^ ERROR cannot find attribute `bar` in this scope
+    A {
+        #[primary_span]
+        span: Span,
+        var: String,
+    }
+}
+
+#[derive(SessionSubdiagnostic)]
+enum S {
+    #[bar("...")]
+//~^ ERROR `#[bar("...")]` is not a valid attribute
+//~^^ ERROR cannot find attribute `bar` in this scope
+    A {
+        #[primary_span]
+        span: Span,
+        var: String,
+    }
+}
+
+#[derive(SessionSubdiagnostic)]
+enum T {
+    #[label(code = "...")]
+//~^ ERROR `code` is not a valid nested attribute of a `label`
+    A {
+        #[primary_span]
+        span: Span,
+        var: String,
+    }
+}
+
+#[derive(SessionSubdiagnostic)]
+enum U {
+    #[label(slug = "label-u")]
+    A {
+        #[primary_span]
+        span: Span,
+        var: String,
+    },
+    B {
+//~^ ERROR subdiagnostic kind not specified
+        #[primary_span]
+        span: Span,
+        var: String,
+    }
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label(slug = "...")]
+//~^ ERROR label without `#[primary_span]` field
+struct V {
+    #[primary_span]
+    //~^ ERROR the `#[primary_span]` attribute can only be applied to fields of type `Span`
+    span: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label(slug = "...")]
+struct W {
+    #[primary_span]
+    span: Span,
+    #[applicability]
+    //~^ ERROR `#[applicability]` is only valid on suggestions
+    applicability: Applicability,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label(slug = "...")]
+struct X {
+    #[primary_span]
+    span: Span,
+    #[bar]
+    //~^ ERROR `#[bar]` is not a valid attribute
+    //~^^ ERROR cannot find attribute `bar` in this scope
+    bar: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label(slug = "...")]
+struct Y {
+    #[primary_span]
+    span: Span,
+    #[bar = "..."]
+    //~^ ERROR `#[bar = ...]` is not a valid attribute
+    //~^^ ERROR cannot find attribute `bar` in this scope
+    bar: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label(slug = "...")]
+struct Z {
+    #[primary_span]
+    span: Span,
+    #[bar("...")]
+    //~^ ERROR `#[bar(...)]` is not a valid attribute
+    //~^^ ERROR cannot find attribute `bar` in this scope
+    bar: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label(slug = "label-aa")]
+struct AA {
+    #[primary_span]
+    span: Span,
+    #[skip_arg]
+    z: Z
+}
+
+#[derive(SessionSubdiagnostic)]
+union AB {
+//~^ ERROR unexpected unsupported untagged union
+    span: u32,
+    b: u64
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label(slug = "label-ac-1")]
+//~^ NOTE previously specified here
+//~^^ NOTE previously specified here
+#[label(slug = "label-ac-2")]
+//~^ ERROR specified multiple times
+//~^^ ERROR specified multiple times
+struct AC {
+    #[primary_span]
+    span: Span,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label(slug = "label-ad-1", slug = "label-ad-2")]
+//~^ ERROR specified multiple times
+//~^^ NOTE previously specified here
+struct AD {
+    #[primary_span]
+    span: Span,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label(slug = "label-ad-1")]
+struct AE {
+    #[primary_span]
+//~^ NOTE previously specified here
+    span_a: Span,
+    #[primary_span]
+//~^ ERROR specified multiple times
+    span_b: Span,
+}
+
+#[derive(SessionSubdiagnostic)]
+struct AF {
+//~^ ERROR subdiagnostic kind not specified
+    #[primary_span]
+    span: Span,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[suggestion(slug = "suggestion-af", code = "...")]
+struct AG {
+    #[primary_span]
+    span: Span,
+    #[applicability]
+    applicability: Applicability,
+    var: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+enum AH {
+    #[suggestion(slug = "suggestion-ag-a", code = "...")]
+    A {
+        #[primary_span]
+        span: Span,
+        #[applicability]
+        applicability: Applicability,
+        var: String,
+    },
+    #[suggestion(slug = "suggestion-ag-b", code = "...")]
+    B {
+        #[primary_span]
+        span: Span,
+        #[applicability]
+        applicability: Applicability,
+        var: String,
+    }
+}
+
+#[derive(SessionSubdiagnostic)]
+#[suggestion(slug = "...", code = "...", code = "...")]
+//~^ ERROR specified multiple times
+//~^^ NOTE previously specified here
+struct AI {
+    #[primary_span]
+    span: Span,
+    #[applicability]
+    applicability: Applicability,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[suggestion(slug = "...", code = "...")]
+struct AJ {
+    #[primary_span]
+    span: Span,
+    #[applicability]
+//~^ NOTE previously specified here
+    applicability_a: Applicability,
+    #[applicability]
+//~^ ERROR specified multiple times
+    applicability_b: Applicability,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[suggestion(slug = "...", code = "...")]
+//~^ ERROR suggestion without `applicability`
+struct AK {
+    #[primary_span]
+    span: Span,
+    #[applicability]
+//~^ ERROR the `#[applicability]` attribute can only be applied to fields of type `Applicability`
+    applicability: Span,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[suggestion(slug = "...", code = "...")]
+//~^ ERROR suggestion without `applicability`
+struct AL {
+    #[primary_span]
+    span: Span,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[suggestion(slug = "...")]
+//~^ ERROR suggestion without `code = "..."`
+struct AM {
+    #[primary_span]
+    span: Span,
+    #[applicability]
+    applicability: Applicability,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[suggestion(slug = "...", code ="...", applicability = "foo")]
+//~^ ERROR invalid applicability
+struct AN {
+    #[primary_span]
+    span: Span,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[help(slug = "label-am")]
+struct AO {
+    var: String
+}
+
+#[derive(SessionSubdiagnostic)]
+#[note(slug = "label-an")]
+struct AP;
+
+#[derive(SessionSubdiagnostic)]
+#[suggestion(slug = "...", code = "...")]
+//~^ ERROR suggestion without `applicability`
+//~^^ ERROR suggestion without `#[primary_span]` field
+struct AQ {
+    var: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[suggestion(slug = "...", code ="...", applicability = "machine-applicable")]
+struct AR {
+    #[primary_span]
+    span: Span,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label]
+//~^ ERROR unsupported type attribute for subdiagnostic enum
+enum AS {
+    #[label(slug = "...")]
+    A {
+        #[primary_span]
+        span: Span,
+        var: String,
+    }
+}
+
+#[derive(SessionSubdiagnostic)]
+#[suggestion(slug = "...", code ="{var}", applicability = "machine-applicable")]
+struct AT {
+    #[primary_span]
+    span: Span,
+    var: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[suggestion(slug = "...", code ="{var}", applicability = "machine-applicable")]
+//~^ ERROR `var` doesn't refer to a field on this type
+struct AU {
+    #[primary_span]
+    span: Span,
+}
+
+#[derive(SessionSubdiagnostic)]
+enum AV {
+    #[suggestion(slug = "...", code ="{var}", applicability = "machine-applicable")]
+    A {
+        #[primary_span]
+        span: Span,
+        var: String,
+    }
+}
+
+#[derive(SessionSubdiagnostic)]
+enum AW {
+    #[suggestion(slug = "...", code ="{var}", applicability = "machine-applicable")]
+//~^ ERROR `var` doesn't refer to a field on this type
+    A {
+        #[primary_span]
+        span: Span,
+    }
+}
diff --git a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr
new file mode 100644
index 00000000000..4984cc4b318
--- /dev/null
+++ b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr
@@ -0,0 +1,389 @@
+error: label without `#[primary_span]` field
+  --> $DIR/subdiagnostic-derive.rs:47:1
+   |
+LL | / #[label(slug = "label-c")]
+LL | |
+LL | | struct C {
+LL | |     var: String,
+LL | | }
+   | |_^
+
+error: `#[label]` is not a valid attribute
+  --> $DIR/subdiagnostic-derive.rs:54:1
+   |
+LL | #[label]
+   | ^^^^^^^^
+
+error: `#[foo]` is not a valid attribute
+  --> $DIR/subdiagnostic-derive.rs:63:1
+   |
+LL | #[foo]
+   | ^^^^^^
+
+error: `#[label = ...]` is not a valid attribute
+  --> $DIR/subdiagnostic-derive.rs:73:1
+   |
+LL | #[label = "..."]
+   | ^^^^^^^^^^^^^^^^
+
+error: `#[label(bug = ...)]` is not a valid attribute
+  --> $DIR/subdiagnostic-derive.rs:82:9
+   |
+LL | #[label(bug = "...")]
+   |         ^^^^^^^^^^^
+   |
+   = help: only `code`, `slug` and `applicability` are valid nested attributes
+
+error: `#[label("...")]` is not a valid attribute
+  --> $DIR/subdiagnostic-derive.rs:91:9
+   |
+LL | #[label("...")]
+   |         ^^^^^
+
+error: `#[label(slug = ...)]` is not a valid attribute
+  --> $DIR/subdiagnostic-derive.rs:100:9
+   |
+LL | #[label(slug = 4)]
+   |         ^^^^^^^^
+
+error: `#[label(slug(...))]` is not a valid attribute
+  --> $DIR/subdiagnostic-derive.rs:109:9
+   |
+LL | #[label(slug("..."))]
+   |         ^^^^^^^^^^^
+
+error: `#[label(slug)]` is not a valid attribute
+  --> $DIR/subdiagnostic-derive.rs:118:9
+   |
+LL | #[label(slug)]
+   |         ^^^^
+
+error: `slug` must be set in a `#[label(...)]` attribute
+  --> $DIR/subdiagnostic-derive.rs:127:1
+   |
+LL | #[label()]
+   | ^^^^^^^^^^
+
+error: `code` is not a valid nested attribute of a `label` attribute
+  --> $DIR/subdiagnostic-derive.rs:136:1
+   |
+LL | #[label(code = "...")]
+   | ^^^^^^^^^^^^^^^^^^^^^^
+
+error: unsupported type attribute for subdiagnostic enum
+  --> $DIR/subdiagnostic-derive.rs:145:1
+   |
+LL | #[foo]
+   | ^^^^^^
+
+error: `#[bar]` is not a valid attribute
+  --> $DIR/subdiagnostic-derive.rs:159:5
+   |
+LL |     #[bar]
+   |     ^^^^^^
+
+error: `#[bar = ...]` is not a valid attribute
+  --> $DIR/subdiagnostic-derive.rs:171:5
+   |
+LL |     #[bar = "..."]
+   |     ^^^^^^^^^^^^^^
+
+error: `#[bar = ...]` is not a valid attribute
+  --> $DIR/subdiagnostic-derive.rs:183:5
+   |
+LL |     #[bar = 4]
+   |     ^^^^^^^^^^
+
+error: `#[bar("...")]` is not a valid attribute
+  --> $DIR/subdiagnostic-derive.rs:195:11
+   |
+LL |     #[bar("...")]
+   |           ^^^^^
+
+error: `code` is not a valid nested attribute of a `label` attribute
+  --> $DIR/subdiagnostic-derive.rs:207:5
+   |
+LL |     #[label(code = "...")]
+   |     ^^^^^^^^^^^^^^^^^^^^^^
+
+error: subdiagnostic kind not specified
+  --> $DIR/subdiagnostic-derive.rs:224:5
+   |
+LL |     B {
+   |     ^
+
+error: the `#[primary_span]` attribute can only be applied to fields of type `Span`
+  --> $DIR/subdiagnostic-derive.rs:236:5
+   |
+LL |     #[primary_span]
+   |     ^^^^^^^^^^^^^^^
+
+error: label without `#[primary_span]` field
+  --> $DIR/subdiagnostic-derive.rs:233:1
+   |
+LL | / #[label(slug = "...")]
+LL | |
+LL | | struct V {
+LL | |     #[primary_span]
+LL | |
+LL | |     span: String,
+LL | | }
+   | |_^
+
+error: `#[applicability]` is only valid on suggestions
+  --> $DIR/subdiagnostic-derive.rs:246:5
+   |
+LL |     #[applicability]
+   |     ^^^^^^^^^^^^^^^^
+
+error: `#[bar]` is not a valid attribute
+  --> $DIR/subdiagnostic-derive.rs:256:5
+   |
+LL |     #[bar]
+   |     ^^^^^^
+   |
+   = help: only `primary_span`, `applicability` and `skip_arg` are valid field attributes
+
+error: `#[bar = ...]` is not a valid attribute
+  --> $DIR/subdiagnostic-derive.rs:267:5
+   |
+LL |     #[bar = "..."]
+   |     ^^^^^^^^^^^^^^
+
+error: `#[bar(...)]` is not a valid attribute
+  --> $DIR/subdiagnostic-derive.rs:278:5
+   |
+LL |     #[bar("...")]
+   |     ^^^^^^^^^^^^^
+
+error: unexpected unsupported untagged union
+  --> $DIR/subdiagnostic-derive.rs:294:1
+   |
+LL | / union AB {
+LL | |
+LL | |     span: u32,
+LL | |     b: u64
+LL | | }
+   | |_^
+
+error: specified multiple times
+  --> $DIR/subdiagnostic-derive.rs:304:9
+   |
+LL | #[label(slug = "label-ac-2")]
+   |         ^^^^^^^^^^^^^^^^^^^
+   |
+note: previously specified here
+  --> $DIR/subdiagnostic-derive.rs:301:9
+   |
+LL | #[label(slug = "label-ac-1")]
+   |         ^^^^^^^^^^^^^^^^^^^
+
+error: specified multiple times
+  --> $DIR/subdiagnostic-derive.rs:304:1
+   |
+LL | #[label(slug = "label-ac-2")]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: previously specified here
+  --> $DIR/subdiagnostic-derive.rs:301:1
+   |
+LL | #[label(slug = "label-ac-1")]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: specified multiple times
+  --> $DIR/subdiagnostic-derive.rs:313:30
+   |
+LL | #[label(slug = "label-ad-1", slug = "label-ad-2")]
+   |                              ^^^^^^^^^^^^^^^^^^^
+   |
+note: previously specified here
+  --> $DIR/subdiagnostic-derive.rs:313:9
+   |
+LL | #[label(slug = "label-ad-1", slug = "label-ad-2")]
+   |         ^^^^^^^^^^^^^^^^^^^
+
+error: specified multiple times
+  --> $DIR/subdiagnostic-derive.rs:327:5
+   |
+LL |     #[primary_span]
+   |     ^^^^^^^^^^^^^^^
+   |
+note: previously specified here
+  --> $DIR/subdiagnostic-derive.rs:324:5
+   |
+LL |     #[primary_span]
+   |     ^^^^^^^^^^^^^^^
+
+error: subdiagnostic kind not specified
+  --> $DIR/subdiagnostic-derive.rs:333:8
+   |
+LL | struct AF {
+   |        ^^
+
+error: specified multiple times
+  --> $DIR/subdiagnostic-derive.rs:370:42
+   |
+LL | #[suggestion(slug = "...", code = "...", code = "...")]
+   |                                          ^^^^^^^^^^^^
+   |
+note: previously specified here
+  --> $DIR/subdiagnostic-derive.rs:370:28
+   |
+LL | #[suggestion(slug = "...", code = "...", code = "...")]
+   |                            ^^^^^^^^^^^^
+
+error: specified multiple times
+  --> $DIR/subdiagnostic-derive.rs:388:5
+   |
+LL |     #[applicability]
+   |     ^^^^^^^^^^^^^^^^
+   |
+note: previously specified here
+  --> $DIR/subdiagnostic-derive.rs:385:5
+   |
+LL |     #[applicability]
+   |     ^^^^^^^^^^^^^^^^
+
+error: the `#[applicability]` attribute can only be applied to fields of type `Applicability`
+  --> $DIR/subdiagnostic-derive.rs:399:5
+   |
+LL |     #[applicability]
+   |     ^^^^^^^^^^^^^^^^
+
+error: suggestion without `applicability`
+  --> $DIR/subdiagnostic-derive.rs:394:1
+   |
+LL | / #[suggestion(slug = "...", code = "...")]
+LL | |
+LL | | struct AK {
+LL | |     #[primary_span]
+...  |
+LL | |     applicability: Span,
+LL | | }
+   | |_^
+
+error: suggestion without `applicability`
+  --> $DIR/subdiagnostic-derive.rs:405:1
+   |
+LL | / #[suggestion(slug = "...", code = "...")]
+LL | |
+LL | | struct AL {
+LL | |     #[primary_span]
+LL | |     span: Span,
+LL | | }
+   | |_^
+
+error: suggestion without `code = "..."`
+  --> $DIR/subdiagnostic-derive.rs:413:1
+   |
+LL | / #[suggestion(slug = "...")]
+LL | |
+LL | | struct AM {
+LL | |     #[primary_span]
+...  |
+LL | |     applicability: Applicability,
+LL | | }
+   | |_^
+
+error: invalid applicability
+  --> $DIR/subdiagnostic-derive.rs:423:41
+   |
+LL | #[suggestion(slug = "...", code ="...", applicability = "foo")]
+   |                                         ^^^^^^^^^^^^^^^^^^^^^
+
+error: suggestion without `applicability`
+  --> $DIR/subdiagnostic-derive.rs:441:1
+   |
+LL | / #[suggestion(slug = "...", code = "...")]
+LL | |
+LL | |
+LL | | struct AQ {
+LL | |     var: String,
+LL | | }
+   | |_^
+
+error: suggestion without `#[primary_span]` field
+  --> $DIR/subdiagnostic-derive.rs:441:1
+   |
+LL | / #[suggestion(slug = "...", code = "...")]
+LL | |
+LL | |
+LL | | struct AQ {
+LL | |     var: String,
+LL | | }
+   | |_^
+
+error: unsupported type attribute for subdiagnostic enum
+  --> $DIR/subdiagnostic-derive.rs:456:1
+   |
+LL | #[label]
+   | ^^^^^^^^
+
+error: `var` doesn't refer to a field on this type
+  --> $DIR/subdiagnostic-derive.rs:476:34
+   |
+LL | #[suggestion(slug = "...", code ="{var}", applicability = "machine-applicable")]
+   |                                  ^^^^^^^
+
+error: `var` doesn't refer to a field on this type
+  --> $DIR/subdiagnostic-derive.rs:495:38
+   |
+LL |     #[suggestion(slug = "...", code ="{var}", applicability = "machine-applicable")]
+   |                                      ^^^^^^^
+
+error: cannot find attribute `foo` in this scope
+  --> $DIR/subdiagnostic-derive.rs:63:3
+   |
+LL | #[foo]
+   |   ^^^
+
+error: cannot find attribute `foo` in this scope
+  --> $DIR/subdiagnostic-derive.rs:145:3
+   |
+LL | #[foo]
+   |   ^^^
+
+error: cannot find attribute `bar` in this scope
+  --> $DIR/subdiagnostic-derive.rs:159:7
+   |
+LL |     #[bar]
+   |       ^^^
+
+error: cannot find attribute `bar` in this scope
+  --> $DIR/subdiagnostic-derive.rs:171:7
+   |
+LL |     #[bar = "..."]
+   |       ^^^
+
+error: cannot find attribute `bar` in this scope
+  --> $DIR/subdiagnostic-derive.rs:183:7
+   |
+LL |     #[bar = 4]
+   |       ^^^
+
+error: cannot find attribute `bar` in this scope
+  --> $DIR/subdiagnostic-derive.rs:195:7
+   |
+LL |     #[bar("...")]
+   |       ^^^
+
+error: cannot find attribute `bar` in this scope
+  --> $DIR/subdiagnostic-derive.rs:256:7
+   |
+LL |     #[bar]
+   |       ^^^
+
+error: cannot find attribute `bar` in this scope
+  --> $DIR/subdiagnostic-derive.rs:267:7
+   |
+LL |     #[bar = "..."]
+   |       ^^^
+
+error: cannot find attribute `bar` in this scope
+  --> $DIR/subdiagnostic-derive.rs:278:7
+   |
+LL |     #[bar("...")]
+   |       ^^^
+
+error: aborting due to 51 previous errors
+
diff --git a/src/test/ui/array-slice-vec/repeat_empty_ok.stderr b/src/test/ui/array-slice-vec/repeat_empty_ok.stderr
index eba1a8e2278..724bdcd920a 100644
--- a/src/test/ui/array-slice-vec/repeat_empty_ok.stderr
+++ b/src/test/ui/array-slice-vec/repeat_empty_ok.stderr
@@ -1,22 +1,22 @@
 error[E0277]: the trait bound `Header<'_>: Copy` is not satisfied
-  --> $DIR/repeat_empty_ok.rs:8:19
+  --> $DIR/repeat_empty_ok.rs:8:20
    |
 LL |     let headers = [Header{value: &[]}; 128];
-   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Header<'_>`
+   |                    ^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Header<'_>`
    |
-   = note: the `Copy` trait is required because the repeated element will be copied
+   = note: the `Copy` trait is required because this value will be copied for each element of the array
 help: consider annotating `Header<'_>` with `#[derive(Copy)]`
    |
 LL | #[derive(Copy)]
    |
 
 error[E0277]: the trait bound `Header<'_>: Copy` is not satisfied
-  --> $DIR/repeat_empty_ok.rs:13:19
+  --> $DIR/repeat_empty_ok.rs:13:20
    |
 LL |     let headers = [Header{value: &[0]}; 128];
-   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Header<'_>`
+   |                    ^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Header<'_>`
    |
-   = note: the `Copy` trait is required because the repeated element will be copied
+   = note: the `Copy` trait is required because this value will be copied for each element of the array
 help: consider annotating `Header<'_>` with `#[derive(Copy)]`
    |
 LL | #[derive(Copy)]
diff --git a/src/test/ui/asm/x86_64/bad-reg.rs b/src/test/ui/asm/x86_64/bad-reg.rs
index 4c4ce8b5e9e..272372ebedc 100644
--- a/src/test/ui/asm/x86_64/bad-reg.rs
+++ b/src/test/ui/asm/x86_64/bad-reg.rs
@@ -29,13 +29,13 @@ fn main() {
         //~^ ERROR invalid register `rsp`: the stack pointer cannot be used as an operand
         asm!("", in("ip") foo);
         //~^ ERROR invalid register `ip`: the instruction pointer cannot be used as an operand
-        asm!("", in("k0") foo);
-        //~^ ERROR invalid register `k0`: the k0 AVX mask register cannot be used as an operand
 
         asm!("", in("st(2)") foo);
         //~^ ERROR register class `x87_reg` can only be used as a clobber, not as an input or output
         asm!("", in("mm0") foo);
         //~^ ERROR register class `mmx_reg` can only be used as a clobber, not as an input or output
+        asm!("", in("k0") foo);
+        //~^ ERROR register class `kreg0` can only be used as a clobber, not as an input or output
         asm!("", out("st(2)") _);
         asm!("", out("mm0") _);
         asm!("{}", in(x87_reg) foo);
diff --git a/src/test/ui/asm/x86_64/bad-reg.stderr b/src/test/ui/asm/x86_64/bad-reg.stderr
index f8b024e1acd..84b8b5ec285 100644
--- a/src/test/ui/asm/x86_64/bad-reg.stderr
+++ b/src/test/ui/asm/x86_64/bad-reg.stderr
@@ -64,24 +64,24 @@ error: invalid register `ip`: the instruction pointer cannot be used as an opera
 LL |         asm!("", in("ip") foo);
    |                  ^^^^^^^^^^^^
 
-error: invalid register `k0`: the k0 AVX mask register cannot be used as an operand for inline asm
-  --> $DIR/bad-reg.rs:32:18
-   |
-LL |         asm!("", in("k0") foo);
-   |                  ^^^^^^^^^^^^
-
 error: register class `x87_reg` can only be used as a clobber, not as an input or output
-  --> $DIR/bad-reg.rs:35:18
+  --> $DIR/bad-reg.rs:33:18
    |
 LL |         asm!("", in("st(2)") foo);
    |                  ^^^^^^^^^^^^^^^
 
 error: register class `mmx_reg` can only be used as a clobber, not as an input or output
-  --> $DIR/bad-reg.rs:37:18
+  --> $DIR/bad-reg.rs:35:18
    |
 LL |         asm!("", in("mm0") foo);
    |                  ^^^^^^^^^^^^^
 
+error: register class `kreg0` can only be used as a clobber, not as an input or output
+  --> $DIR/bad-reg.rs:37:18
+   |
+LL |         asm!("", in("k0") foo);
+   |                  ^^^^^^^^^^^^
+
 error: register class `x87_reg` can only be used as a clobber, not as an input or output
   --> $DIR/bad-reg.rs:41:20
    |
diff --git a/src/test/ui/associated-types/impl-trait-return-missing-constraint.rs b/src/test/ui/associated-types/impl-trait-return-missing-constraint.rs
index ef0443034ec..1de1ddbe3a4 100644
--- a/src/test/ui/associated-types/impl-trait-return-missing-constraint.rs
+++ b/src/test/ui/associated-types/impl-trait-return-missing-constraint.rs
@@ -24,7 +24,6 @@ fn bar() -> impl Bar {
 
 fn baz() -> impl Bar<Item = i32> {
     //~^ ERROR type mismatch resolving `<impl Bar as Foo>::Item == i32`
-    //~| ERROR type mismatch resolving `<impl Bar as Foo>::Item == i32`
     bar()
 }
 
diff --git a/src/test/ui/associated-types/impl-trait-return-missing-constraint.stderr b/src/test/ui/associated-types/impl-trait-return-missing-constraint.stderr
index b7c49570ca4..bd8c8a4414c 100644
--- a/src/test/ui/associated-types/impl-trait-return-missing-constraint.stderr
+++ b/src/test/ui/associated-types/impl-trait-return-missing-constraint.stderr
@@ -16,29 +16,6 @@ help: consider constraining the associated type `<impl Bar as Foo>::Item` to `i3
 LL | fn bar() -> impl Bar<Item = i32> {
    |                     ++++++++++++
 
-error[E0271]: type mismatch resolving `<impl Bar as Foo>::Item == i32`
-  --> $DIR/impl-trait-return-missing-constraint.rs:25:34
-   |
-LL |   fn bar() -> impl Bar {
-   |               -------- the expected opaque type
-...
-LL |   fn baz() -> impl Bar<Item = i32> {
-   |  __________________________________^
-LL | |
-LL | |
-LL | |     bar()
-LL | | }
-   | |_^ expected associated type, found `i32`
-   |
-   = note: expected associated type `<impl Bar as Foo>::Item`
-                         found type `i32`
-   = help: consider constraining the associated type `<impl Bar as Foo>::Item` to `i32` or calling a method that returns `<impl Bar as Foo>::Item`
-   = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
-help: consider constraining the associated type `<impl Bar as Foo>::Item` to `i32`
-   |
-LL | fn bar() -> impl Bar<Item = i32> {
-   |                     ++++++++++++
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0271`.
diff --git a/src/test/ui/issues/issue-25339.rs b/src/test/ui/associated-types/issue-25339.rs
index 6f8ec700951..6f8ec700951 100644
--- a/src/test/ui/issues/issue-25339.rs
+++ b/src/test/ui/associated-types/issue-25339.rs
diff --git a/src/test/ui/issues/issue-55846.rs b/src/test/ui/associated-types/issue-55846.rs
index bd766752360..bd766752360 100644
--- a/src/test/ui/issues/issue-55846.rs
+++ b/src/test/ui/associated-types/issue-55846.rs
diff --git a/src/test/ui/async-await/async-unsafe-fn-call-in-safe.mir.stderr b/src/test/ui/async-await/async-unsafe-fn-call-in-safe.mir.stderr
index d22413beecb..2114fb59ba3 100644
--- a/src/test/ui/async-await/async-unsafe-fn-call-in-safe.mir.stderr
+++ b/src/test/ui/async-await/async-unsafe-fn-call-in-safe.mir.stderr
@@ -7,7 +7,7 @@ LL |     S::f();
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
 error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/async-unsafe-fn-call-in-safe.rs:15:5
+  --> $DIR/async-unsafe-fn-call-in-safe.rs:17:5
    |
 LL |     f();
    |     ^^^ call to unsafe function
@@ -15,7 +15,7 @@ LL |     f();
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
 error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/async-unsafe-fn-call-in-safe.rs:19:5
+  --> $DIR/async-unsafe-fn-call-in-safe.rs:23:5
    |
 LL |     S::f();
    |     ^^^^^^ call to unsafe function
@@ -23,7 +23,7 @@ LL |     S::f();
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
 error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/async-unsafe-fn-call-in-safe.rs:20:5
+  --> $DIR/async-unsafe-fn-call-in-safe.rs:24:5
    |
 LL |     f();
    |     ^^^ call to unsafe function
diff --git a/src/test/ui/async-await/async-unsafe-fn-call-in-safe.rs b/src/test/ui/async-await/async-unsafe-fn-call-in-safe.rs
index fc37822cb7b..c941dc27aa3 100644
--- a/src/test/ui/async-await/async-unsafe-fn-call-in-safe.rs
+++ b/src/test/ui/async-await/async-unsafe-fn-call-in-safe.rs
@@ -11,8 +11,12 @@ impl S {
 async unsafe fn f() {}
 
 async fn g() {
-    S::f(); //~ ERROR call to unsafe function is unsafe
-    f(); //~ ERROR call to unsafe function is unsafe
+    S::f();
+    //[mir]~^ ERROR call to unsafe function is unsafe
+    //[thir]~^^ ERROR call to unsafe function `S::f` is unsafe
+    f();
+    //[mir]~^ ERROR call to unsafe function is unsafe
+    //[thir]~^^ ERROR call to unsafe function `f` is unsafe
 }
 
 fn main() {
diff --git a/src/test/ui/async-await/async-unsafe-fn-call-in-safe.thir.stderr b/src/test/ui/async-await/async-unsafe-fn-call-in-safe.thir.stderr
index 21ba45d7f1e..68d97d3fd7d 100644
--- a/src/test/ui/async-await/async-unsafe-fn-call-in-safe.thir.stderr
+++ b/src/test/ui/async-await/async-unsafe-fn-call-in-safe.thir.stderr
@@ -1,4 +1,4 @@
-error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+error[E0133]: call to unsafe function `S::f` is unsafe and requires unsafe function or block
   --> $DIR/async-unsafe-fn-call-in-safe.rs:14:5
    |
 LL |     S::f();
@@ -6,8 +6,8 @@ LL |     S::f();
    |
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
-error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/async-unsafe-fn-call-in-safe.rs:15:5
+error[E0133]: call to unsafe function `f` is unsafe and requires unsafe function or block
+  --> $DIR/async-unsafe-fn-call-in-safe.rs:17:5
    |
 LL |     f();
    |     ^^^ call to unsafe function
diff --git a/src/test/ui/async-await/issue-86507.stderr b/src/test/ui/async-await/issue-86507.stderr
index ad0a3592241..5bbc20359c6 100644
--- a/src/test/ui/async-await/issue-86507.stderr
+++ b/src/test/ui/async-await/issue-86507.stderr
@@ -14,10 +14,10 @@ note: captured value is not `Send` because `&` references cannot be sent unless
 LL |                     let x = x;
    |                             ^ has type `&T` which is not `Send`, because `T` is not `Sync`
    = note: required for the cast to the object type `dyn Future<Output = ()> + Send`
-help: consider further restricting type parameter `T`
+help: consider further restricting this bound
    |
-LL |         where 'me:'async_trait, T: std::marker::Sync {
-   |                               ++++++++++++++++++++++
+LL |     fn bar<'me, 'async_trait, T: Send + std::marker::Sync>(x: &'me T)
+   |                                       +++++++++++++++++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issues/issue-3820.rs b/src/test/ui/binop/issue-3820.rs
index b987a90b28b..b987a90b28b 100644
--- a/src/test/ui/issues/issue-3820.rs
+++ b/src/test/ui/binop/issue-3820.rs
diff --git a/src/test/ui/issues/issue-3820.stderr b/src/test/ui/binop/issue-3820.stderr
index d5c24e1bb6c..d5c24e1bb6c 100644
--- a/src/test/ui/issues/issue-3820.stderr
+++ b/src/test/ui/binop/issue-3820.stderr
diff --git a/src/test/ui/binop/issue-93927.rs b/src/test/ui/binop/issue-93927.rs
new file mode 100644
index 00000000000..de27c9785e6
--- /dev/null
+++ b/src/test/ui/binop/issue-93927.rs
@@ -0,0 +1,20 @@
+// Regression test for #93927: suggested trait bound for T should be Eq, not PartialEq
+struct MyType<T>(T);
+
+impl<T> PartialEq for MyType<T>
+where
+    T: Eq,
+{
+    fn eq(&self, other: &Self) -> bool {
+        true
+    }
+}
+
+fn cond<T: PartialEq>(val: MyType<T>) -> bool {
+    val == val
+    //~^ ERROR binary operation `==` cannot be applied to type `MyType<T>`
+}
+
+fn main() {
+    cond(MyType(0));
+}
diff --git a/src/test/ui/binop/issue-93927.stderr b/src/test/ui/binop/issue-93927.stderr
new file mode 100644
index 00000000000..75558b502f9
--- /dev/null
+++ b/src/test/ui/binop/issue-93927.stderr
@@ -0,0 +1,16 @@
+error[E0369]: binary operation `==` cannot be applied to type `MyType<T>`
+  --> $DIR/issue-93927.rs:14:9
+   |
+LL |     val == val
+   |     --- ^^ --- MyType<T>
+   |     |
+   |     MyType<T>
+   |
+help: consider further restricting this bound
+   |
+LL | fn cond<T: PartialEq + std::cmp::Eq>(val: MyType<T>) -> bool {
+   |                      ++++++++++++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0369`.
diff --git a/src/test/ui/builtin-superkinds/builtin-superkinds-self-type.stderr b/src/test/ui/builtin-superkinds/builtin-superkinds-self-type.stderr
index 2dac4a22ae7..b1e59e9d5de 100644
--- a/src/test/ui/builtin-superkinds/builtin-superkinds-self-type.stderr
+++ b/src/test/ui/builtin-superkinds/builtin-superkinds-self-type.stderr
@@ -2,9 +2,12 @@ error[E0310]: the parameter type `T` may not live long enough
   --> $DIR/builtin-superkinds-self-type.rs:10:16
    |
 LL | impl <T: Sync> Foo for T { }
-   |       --       ^^^ ...so that the type `T` will meet its required lifetime bounds
-   |       |
-   |       help: consider adding an explicit lifetime bound...: `T: 'static +`
+   |                ^^^ ...so that the type `T` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL | impl <T: Sync + 'static> Foo for T { }
+   |               +++++++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.thir.stderr b/src/test/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.thir.stderr
index a60100ddaea..8c516e8900c 100644
--- a/src/test/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.thir.stderr
+++ b/src/test/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.thir.stderr
@@ -1,4 +1,4 @@
-error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+error[E0133]: call to unsafe function `Pin::<P>::new_unchecked` is unsafe and requires unsafe function or block
   --> $DIR/coerce-unsafe-closure-to-unsafe-fn-ptr.rs:5:31
    |
 LL |     let _: unsafe fn() = || { ::std::pin::Pin::new_unchecked(&0_u8); };
diff --git a/src/test/ui/coercion/coerce-issue-49593-box-never-windows.nofallback.stderr b/src/test/ui/coercion/coerce-issue-49593-box-never-windows.nofallback.stderr
new file mode 100644
index 00000000000..ab9cc707aa0
--- /dev/null
+++ b/src/test/ui/coercion/coerce-issue-49593-box-never-windows.nofallback.stderr
@@ -0,0 +1,39 @@
+error[E0277]: the trait bound `(): std::error::Error` is not satisfied
+  --> $DIR/coerce-issue-49593-box-never-windows.rs:18:53
+   |
+LL |     /* *mut $0 is coerced to Box<dyn Error> here */ Box::<_ /* ! */>::new(x)
+   |                                                     ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::error::Error` is not implemented for `()`
+   |
+   = help: the following other types implement trait `std::error::Error`:
+             !
+             &'a T
+             AccessError
+             AddrParseError
+             Arc<T>
+             BorrowError
+             BorrowMutError
+             Box<T>
+           and 45 others
+   = note: required for the cast to the object type `dyn std::error::Error`
+
+error[E0277]: the trait bound `(): std::error::Error` is not satisfied
+  --> $DIR/coerce-issue-49593-box-never-windows.rs:23:49
+   |
+LL |     /* *mut $0 is coerced to *mut Error here */ raw_ptr_box::<_ /* ! */>(x)
+   |                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::error::Error` is not implemented for `()`
+   |
+   = help: the following other types implement trait `std::error::Error`:
+             !
+             &'a T
+             AccessError
+             AddrParseError
+             Arc<T>
+             BorrowError
+             BorrowMutError
+             Box<T>
+           and 45 others
+   = note: required for the cast to the object type `(dyn std::error::Error + 'static)`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/coercion/coerce-issue-49593-box-never-windows.rs b/src/test/ui/coercion/coerce-issue-49593-box-never-windows.rs
new file mode 100644
index 00000000000..95d3935caa9
--- /dev/null
+++ b/src/test/ui/coercion/coerce-issue-49593-box-never-windows.rs
@@ -0,0 +1,58 @@
+// revisions: nofallback fallback
+// only-windows - the number of `Error` impls is platform-dependent
+//[fallback] check-pass
+//[nofallback] check-fail
+
+#![feature(never_type)]
+#![cfg_attr(fallback, feature(never_type_fallback))]
+#![allow(unreachable_code)]
+
+use std::error::Error;
+use std::mem;
+
+fn raw_ptr_box<T>(t: T) -> *mut T {
+    panic!()
+}
+
+fn foo(x: !) -> Box<dyn Error> {
+    /* *mut $0 is coerced to Box<dyn Error> here */ Box::<_ /* ! */>::new(x)
+    //[nofallback]~^ ERROR trait bound `(): std::error::Error` is not satisfied
+}
+
+fn foo_raw_ptr(x: !) -> *mut dyn Error {
+    /* *mut $0 is coerced to *mut Error here */ raw_ptr_box::<_ /* ! */>(x)
+    //[nofallback]~^ ERROR trait bound `(): std::error::Error` is not satisfied
+}
+
+fn no_coercion(d: *mut dyn Error) -> *mut dyn Error {
+    /* an unsize coercion won't compile here, and it is indeed not used
+       because there is nothing requiring the _ to be Sized */
+    d as *mut _
+}
+
+trait Xyz {}
+struct S;
+struct T;
+impl Xyz for S {}
+impl Xyz for T {}
+
+fn foo_no_never() {
+    let mut x /* : Option<S> */ = None;
+    let mut first_iter = false;
+    loop {
+        if !first_iter {
+            let y: Box<dyn Xyz>
+                = /* Box<$0> is coerced to Box<Xyz> here */ Box::new(x.unwrap());
+        }
+
+        x = Some(S);
+        first_iter = true;
+    }
+
+    let mut y : Option<S> = None;
+    // assert types are equal
+    mem::swap(&mut x, &mut y);
+}
+
+fn main() {
+}
diff --git a/src/test/ui/coercion/coerce-issue-49593-box-never.nofallback.stderr b/src/test/ui/coercion/coerce-issue-49593-box-never.nofallback.stderr
index 1b1ce67cb0c..1daa91f025a 100644
--- a/src/test/ui/coercion/coerce-issue-49593-box-never.nofallback.stderr
+++ b/src/test/ui/coercion/coerce-issue-49593-box-never.nofallback.stderr
@@ -1,5 +1,5 @@
 error[E0277]: the trait bound `(): std::error::Error` is not satisfied
-  --> $DIR/coerce-issue-49593-box-never.rs:17:53
+  --> $DIR/coerce-issue-49593-box-never.rs:18:53
    |
 LL |     /* *mut $0 is coerced to Box<dyn Error> here */ Box::<_ /* ! */>::new(x)
    |                                                     ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::error::Error` is not implemented for `()`
@@ -17,7 +17,7 @@ LL |     /* *mut $0 is coerced to Box<dyn Error> here */ Box::<_ /* ! */>::new(x
    = note: required for the cast to the object type `dyn std::error::Error`
 
 error[E0277]: the trait bound `(): std::error::Error` is not satisfied
-  --> $DIR/coerce-issue-49593-box-never.rs:22:49
+  --> $DIR/coerce-issue-49593-box-never.rs:23:49
    |
 LL |     /* *mut $0 is coerced to *mut Error here */ raw_ptr_box::<_ /* ! */>(x)
    |                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::error::Error` is not implemented for `()`
diff --git a/src/test/ui/coercion/coerce-issue-49593-box-never.rs b/src/test/ui/coercion/coerce-issue-49593-box-never.rs
index 7a4324bd5ad..16efb65acb2 100644
--- a/src/test/ui/coercion/coerce-issue-49593-box-never.rs
+++ b/src/test/ui/coercion/coerce-issue-49593-box-never.rs
@@ -1,4 +1,5 @@
 // revisions: nofallback fallback
+// ignore-windows - the number of `Error` impls is platform-dependent
 //[fallback] check-pass
 //[nofallback] check-fail
 
diff --git a/src/test/ui/coherence/coherence-cow.re_a.stderr b/src/test/ui/coherence/coherence-cow.re_a.stderr
index 0cf2a406da4..fe4b5b41078 100644
--- a/src/test/ui/coherence/coherence-cow.re_a.stderr
+++ b/src/test/ui/coherence/coherence-cow.re_a.stderr
@@ -1,4 +1,4 @@
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate
   --> $DIR/coherence-cow.rs:18:1
    |
 LL | impl<T> Remote for Pair<T,Cover<T>> { }
diff --git a/src/test/ui/coherence/coherence-cow.re_b.stderr b/src/test/ui/coherence/coherence-cow.re_b.stderr
index b523db4da23..da4ede3251e 100644
--- a/src/test/ui/coherence/coherence-cow.re_b.stderr
+++ b/src/test/ui/coherence/coherence-cow.re_b.stderr
@@ -1,4 +1,4 @@
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate
   --> $DIR/coherence-cow.rs:22:1
    |
 LL | impl<T> Remote for Pair<Cover<T>,T> { }
diff --git a/src/test/ui/coherence/coherence-cow.re_c.stderr b/src/test/ui/coherence/coherence-cow.re_c.stderr
index bd635fc2e8c..d1a20c0ca10 100644
--- a/src/test/ui/coherence/coherence-cow.re_c.stderr
+++ b/src/test/ui/coherence/coherence-cow.re_c.stderr
@@ -1,4 +1,4 @@
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate
   --> $DIR/coherence-cow.rs:26:1
    |
 LL | impl<T,U> Remote for Pair<Cover<T>,U> { }
diff --git a/src/test/ui/coherence/coherence-impls-copy.stderr b/src/test/ui/coherence/coherence-impls-copy.stderr
index a7d6968a296..b3ca354c633 100644
--- a/src/test/ui/coherence/coherence-impls-copy.stderr
+++ b/src/test/ui/coherence/coherence-impls-copy.stderr
@@ -1,4 +1,4 @@
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for primitive types
   --> $DIR/coherence-impls-copy.rs:5:1
    |
 LL | impl Copy for i32 {}
diff --git a/src/test/ui/coherence/coherence-orphan.stderr b/src/test/ui/coherence/coherence-orphan.stderr
index 52d2cc88cbe..01f166a21f7 100644
--- a/src/test/ui/coherence/coherence-orphan.stderr
+++ b/src/test/ui/coherence/coherence-orphan.stderr
@@ -1,4 +1,4 @@
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for primitive types
   --> $DIR/coherence-orphan.rs:10:1
    |
 LL | impl TheTrait<usize> for isize { }
@@ -10,7 +10,7 @@ LL | impl TheTrait<usize> for isize { }
    |
    = note: define and implement a trait or new type instead
 
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate
   --> $DIR/coherence-orphan.rs:17:1
    |
 LL | impl !Send for Vec<isize> { }
diff --git a/src/test/ui/coherence/coherence-overlapping-pairs.stderr b/src/test/ui/coherence/coherence-overlapping-pairs.stderr
index c1a02681c13..15c92dfeb07 100644
--- a/src/test/ui/coherence/coherence-overlapping-pairs.stderr
+++ b/src/test/ui/coherence/coherence-overlapping-pairs.stderr
@@ -1,4 +1,4 @@
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate
   --> $DIR/coherence-overlapping-pairs.rs:8:1
    |
 LL | impl<T> Remote for lib::Pair<T,Foo> { }
diff --git a/src/test/ui/coherence/coherence-pair-covered-uncovered-1.stderr b/src/test/ui/coherence/coherence-pair-covered-uncovered-1.stderr
index b18bf44dbdf..03d78712381 100644
--- a/src/test/ui/coherence/coherence-pair-covered-uncovered-1.stderr
+++ b/src/test/ui/coherence/coherence-pair-covered-uncovered-1.stderr
@@ -1,4 +1,4 @@
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for primitive types
   --> $DIR/coherence-pair-covered-uncovered-1.rs:12:1
    |
 LL | impl<T, U> Remote1<Pair<T, Local<U>>> for i32 { }
diff --git a/src/test/ui/coherence/coherence-pair-covered-uncovered.stderr b/src/test/ui/coherence/coherence-pair-covered-uncovered.stderr
index 34fdf64ea10..73dfe2f572a 100644
--- a/src/test/ui/coherence/coherence-pair-covered-uncovered.stderr
+++ b/src/test/ui/coherence/coherence-pair-covered-uncovered.stderr
@@ -1,4 +1,4 @@
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate
   --> $DIR/coherence-pair-covered-uncovered.rs:8:1
    |
 LL | impl<T,U> Remote for Pair<T,Local<U>> { }
diff --git a/src/test/ui/coherence/coherence-vec-local-2.stderr b/src/test/ui/coherence/coherence-vec-local-2.stderr
index 567b6a6c17f..95fdf172ec2 100644
--- a/src/test/ui/coherence/coherence-vec-local-2.stderr
+++ b/src/test/ui/coherence/coherence-vec-local-2.stderr
@@ -1,4 +1,4 @@
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate
   --> $DIR/coherence-vec-local-2.rs:11:1
    |
 LL | impl<T> Remote for Vec<Local<T>> { }
diff --git a/src/test/ui/coherence/coherence-vec-local.stderr b/src/test/ui/coherence/coherence-vec-local.stderr
index 38464f12a21..4835e771abd 100644
--- a/src/test/ui/coherence/coherence-vec-local.stderr
+++ b/src/test/ui/coherence/coherence-vec-local.stderr
@@ -1,4 +1,4 @@
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate
   --> $DIR/coherence-vec-local.rs:11:1
    |
 LL | impl Remote for Vec<Local> { }
diff --git a/src/test/ui/coherence/coherence_local_err_struct.stderr b/src/test/ui/coherence/coherence_local_err_struct.stderr
index 8c310b318a7..afc6fc45d0e 100644
--- a/src/test/ui/coherence/coherence_local_err_struct.stderr
+++ b/src/test/ui/coherence/coherence_local_err_struct.stderr
@@ -1,4 +1,4 @@
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate
   --> $DIR/coherence_local_err_struct.rs:14:1
    |
 LL | impl lib::MyCopy for lib::MyStruct<MyType> { }
diff --git a/src/test/ui/coherence/impl-foreign-for-foreign.stderr b/src/test/ui/coherence/impl-foreign-for-foreign.stderr
index fe7c9b93f54..93f7a6fdc25 100644
--- a/src/test/ui/coherence/impl-foreign-for-foreign.stderr
+++ b/src/test/ui/coherence/impl-foreign-for-foreign.stderr
@@ -1,4 +1,4 @@
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for primitive types
   --> $DIR/impl-foreign-for-foreign.rs:10:1
    |
 LL | impl Remote for i32 {
diff --git a/src/test/ui/coherence/impl-foreign-for-foreign[foreign].stderr b/src/test/ui/coherence/impl-foreign-for-foreign[foreign].stderr
index bdf19cf00a7..e24537bce22 100644
--- a/src/test/ui/coherence/impl-foreign-for-foreign[foreign].stderr
+++ b/src/test/ui/coherence/impl-foreign-for-foreign[foreign].stderr
@@ -1,4 +1,4 @@
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for primitive types
   --> $DIR/impl-foreign-for-foreign[foreign].rs:10:1
    |
 LL | impl Remote1<Rc<i32>> for i32 {
@@ -10,7 +10,7 @@ LL | impl Remote1<Rc<i32>> for i32 {
    |
    = note: define and implement a trait or new type instead
 
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for primitive types
   --> $DIR/impl-foreign-for-foreign[foreign].rs:14:1
    |
 LL | impl Remote1<Rc<Local>> for f64 {
@@ -22,7 +22,7 @@ LL | impl Remote1<Rc<Local>> for f64 {
    |
    = note: define and implement a trait or new type instead
 
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for primitive types
   --> $DIR/impl-foreign-for-foreign[foreign].rs:18:1
    |
 LL | impl<T> Remote1<Rc<T>> for f32 {
diff --git a/src/test/ui/coherence/impl-foreign-for-fundamental[foreign].stderr b/src/test/ui/coherence/impl-foreign-for-fundamental[foreign].stderr
index 20dc955ffe4..55ea4409e6f 100644
--- a/src/test/ui/coherence/impl-foreign-for-fundamental[foreign].stderr
+++ b/src/test/ui/coherence/impl-foreign-for-fundamental[foreign].stderr
@@ -1,4 +1,4 @@
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate
   --> $DIR/impl-foreign-for-fundamental[foreign].rs:10:1
    |
 LL | impl Remote for Box<i32> {
@@ -10,7 +10,7 @@ LL | impl Remote for Box<i32> {
    |
    = note: define and implement a trait or new type instead
 
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate
   --> $DIR/impl-foreign-for-fundamental[foreign].rs:14:1
    |
 LL | impl<T> Remote for Box<Rc<T>> {
diff --git a/src/test/ui/coherence/impl-foreign[foreign]-for-foreign.stderr b/src/test/ui/coherence/impl-foreign[foreign]-for-foreign.stderr
index 5552d825793..65b3aa394a8 100644
--- a/src/test/ui/coherence/impl-foreign[foreign]-for-foreign.stderr
+++ b/src/test/ui/coherence/impl-foreign[foreign]-for-foreign.stderr
@@ -1,4 +1,4 @@
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for primitive types
   --> $DIR/impl-foreign[foreign]-for-foreign.rs:10:1
    |
 LL | impl Remote1<u32> for f64 {
diff --git a/src/test/ui/coherence/impl-foreign[fundemental[foreign]]-for-foreign.stderr b/src/test/ui/coherence/impl-foreign[fundemental[foreign]]-for-foreign.stderr
index c1e2fdaf5e3..8e77c13e111 100644
--- a/src/test/ui/coherence/impl-foreign[fundemental[foreign]]-for-foreign.stderr
+++ b/src/test/ui/coherence/impl-foreign[fundemental[foreign]]-for-foreign.stderr
@@ -1,4 +1,4 @@
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for primitive types
   --> $DIR/impl-foreign[fundemental[foreign]]-for-foreign.rs:11:1
    |
 LL | impl Remote1<Box<String>> for i32 {
@@ -11,7 +11,7 @@ LL | impl Remote1<Box<String>> for i32 {
    |
    = note: define and implement a trait or new type instead
 
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for primitive types
   --> $DIR/impl-foreign[fundemental[foreign]]-for-foreign.rs:15:1
    |
 LL | impl Remote1<Box<Rc<i32>>> for f64 {
@@ -24,7 +24,7 @@ LL | impl Remote1<Box<Rc<i32>>> for f64 {
    |
    = note: define and implement a trait or new type instead
 
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for primitive types
   --> $DIR/impl-foreign[fundemental[foreign]]-for-foreign.rs:19:1
    |
 LL | impl<T> Remote1<Box<Rc<T>>> for f32 {
diff --git a/src/test/ui/coherence/impl[t]-foreign-for-foreign[t].stderr b/src/test/ui/coherence/impl[t]-foreign-for-foreign[t].stderr
index 7f8ec83b24a..92346c29198 100644
--- a/src/test/ui/coherence/impl[t]-foreign-for-foreign[t].stderr
+++ b/src/test/ui/coherence/impl[t]-foreign-for-foreign[t].stderr
@@ -1,4 +1,4 @@
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate
   --> $DIR/impl[t]-foreign-for-foreign[t].rs:11:1
    |
 LL | impl Remote for Rc<Local> {
@@ -9,7 +9,7 @@ LL | impl Remote for Rc<Local> {
    |
    = note: define and implement a trait or new type instead
 
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate
   --> $DIR/impl[t]-foreign-for-foreign[t].rs:16:1
    |
 LL | impl<T> Remote for Arc<T> {
diff --git a/src/test/ui/conservative_impl_trait.rs b/src/test/ui/conservative_impl_trait.rs
index b8488d83998..b7f795eadb7 100644
--- a/src/test/ui/conservative_impl_trait.rs
+++ b/src/test/ui/conservative_impl_trait.rs
@@ -2,7 +2,6 @@
 
 fn will_ice(something: &u32) -> impl Iterator<Item = &u32> {
     //~^ ERROR `()` is not an iterator
-    //~| ERROR `()` is not an iterator
 }
 
 fn main() {}
diff --git a/src/test/ui/conservative_impl_trait.stderr b/src/test/ui/conservative_impl_trait.stderr
index 2348f2f5297..63a4df242f8 100644
--- a/src/test/ui/conservative_impl_trait.stderr
+++ b/src/test/ui/conservative_impl_trait.stderr
@@ -6,18 +6,6 @@ LL | fn will_ice(something: &u32) -> impl Iterator<Item = &u32> {
    |
    = help: the trait `Iterator` is not implemented for `()`
 
-error[E0277]: `()` is not an iterator
-  --> $DIR/conservative_impl_trait.rs:3:60
-   |
-LL |   fn will_ice(something: &u32) -> impl Iterator<Item = &u32> {
-   |  ____________________________________________________________^
-LL | |
-LL | |
-LL | | }
-   | |_^ `()` is not an iterator
-   |
-   = help: the trait `Iterator` is not implemented for `()`
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/const-generics/const-arg-in-const-arg.min.stderr b/src/test/ui/const-generics/const-arg-in-const-arg.min.stderr
index ac693426fbd..88d9ed46e1a 100644
--- a/src/test/ui/const-generics/const-arg-in-const-arg.min.stderr
+++ b/src/test/ui/const-generics/const-arg-in-const-arg.min.stderr
@@ -16,53 +16,8 @@ LL |     let _: [u8; bar::<N>()];
    = help: const parameters may only be used as standalone arguments, i.e. `N`
    = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions
 
-error: generic parameters may not be used in const operations
-  --> $DIR/const-arg-in-const-arg.rs:24:23
-   |
-LL |     let _ = [0; bar::<N>()];
-   |                       ^ cannot perform const operation using `N`
-   |
-   = help: const parameters may only be used as standalone arguments, i.e. `N`
-   = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions
-
-error: generic parameters may not be used in const operations
-  --> $DIR/const-arg-in-const-arg.rs:29:24
-   |
-LL |     let _: Foo<{ foo::<T>() }>;
-   |                        ^ cannot perform const operation using `T`
-   |
-   = note: type parameters may not be used in const expressions
-   = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions
-
-error: generic parameters may not be used in const operations
-  --> $DIR/const-arg-in-const-arg.rs:30:24
-   |
-LL |     let _: Foo<{ bar::<N>() }>;
-   |                        ^ cannot perform const operation using `N`
-   |
-   = help: const parameters may only be used as standalone arguments, i.e. `N`
-   = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions
-
-error: generic parameters may not be used in const operations
-  --> $DIR/const-arg-in-const-arg.rs:35:27
-   |
-LL |     let _ = Foo::<{ foo::<T>() }>;
-   |                           ^ cannot perform const operation using `T`
-   |
-   = note: type parameters may not be used in const expressions
-   = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions
-
-error: generic parameters may not be used in const operations
-  --> $DIR/const-arg-in-const-arg.rs:36:27
-   |
-LL |     let _ = Foo::<{ bar::<N>() }>;
-   |                           ^ cannot perform const operation using `N`
-   |
-   = help: const parameters may only be used as standalone arguments, i.e. `N`
-   = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions
-
 error[E0658]: a non-static lifetime is not allowed in a `const`
-  --> $DIR/const-arg-in-const-arg.rs:15:23
+  --> $DIR/const-arg-in-const-arg.rs:16:23
    |
 LL |     let _: [u8; faz::<'a>(&())];
    |                       ^^
@@ -71,7 +26,7 @@ LL |     let _: [u8; faz::<'a>(&())];
    = help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable
 
 error[E0658]: a non-static lifetime is not allowed in a `const`
-  --> $DIR/const-arg-in-const-arg.rs:16:23
+  --> $DIR/const-arg-in-const-arg.rs:18:23
    |
 LL |     let _: [u8; baz::<'a>(&())];
    |                       ^^
@@ -80,7 +35,7 @@ LL |     let _: [u8; baz::<'a>(&())];
    = help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable
 
 error[E0658]: a non-static lifetime is not allowed in a `const`
-  --> $DIR/const-arg-in-const-arg.rs:17:23
+  --> $DIR/const-arg-in-const-arg.rs:19:23
    |
 LL |     let _: [u8; faz::<'b>(&())];
    |                       ^^
@@ -89,7 +44,7 @@ LL |     let _: [u8; faz::<'b>(&())];
    = help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable
 
 error[E0658]: a non-static lifetime is not allowed in a `const`
-  --> $DIR/const-arg-in-const-arg.rs:18:23
+  --> $DIR/const-arg-in-const-arg.rs:21:23
    |
 LL |     let _: [u8; baz::<'b>(&())];
    |                       ^^
@@ -97,8 +52,17 @@ LL |     let _: [u8; baz::<'b>(&())];
    = note: see issue #76560 <https://github.com/rust-lang/rust/issues/76560> for more information
    = help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable
 
+error: generic parameters may not be used in const operations
+  --> $DIR/const-arg-in-const-arg.rs:24:23
+   |
+LL |     let _ = [0; bar::<N>()];
+   |                       ^ cannot perform const operation using `N`
+   |
+   = help: const parameters may only be used as standalone arguments, i.e. `N`
+   = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions
+
 error[E0658]: a non-static lifetime is not allowed in a `const`
-  --> $DIR/const-arg-in-const-arg.rs:25:23
+  --> $DIR/const-arg-in-const-arg.rs:26:23
    |
 LL |     let _ = [0; faz::<'a>(&())];
    |                       ^^
@@ -107,7 +71,7 @@ LL |     let _ = [0; faz::<'a>(&())];
    = help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable
 
 error[E0658]: a non-static lifetime is not allowed in a `const`
-  --> $DIR/const-arg-in-const-arg.rs:26:23
+  --> $DIR/const-arg-in-const-arg.rs:28:23
    |
 LL |     let _ = [0; baz::<'a>(&())];
    |                       ^^
@@ -116,7 +80,7 @@ LL |     let _ = [0; baz::<'a>(&())];
    = help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable
 
 error[E0658]: a non-static lifetime is not allowed in a `const`
-  --> $DIR/const-arg-in-const-arg.rs:27:23
+  --> $DIR/const-arg-in-const-arg.rs:29:23
    |
 LL |     let _ = [0; faz::<'b>(&())];
    |                       ^^
@@ -125,7 +89,7 @@ LL |     let _ = [0; faz::<'b>(&())];
    = help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable
 
 error[E0658]: a non-static lifetime is not allowed in a `const`
-  --> $DIR/const-arg-in-const-arg.rs:28:23
+  --> $DIR/const-arg-in-const-arg.rs:31:23
    |
 LL |     let _ = [0; baz::<'b>(&())];
    |                       ^^
@@ -133,8 +97,26 @@ LL |     let _ = [0; baz::<'b>(&())];
    = note: see issue #76560 <https://github.com/rust-lang/rust/issues/76560> for more information
    = help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable
 
+error: generic parameters may not be used in const operations
+  --> $DIR/const-arg-in-const-arg.rs:32:24
+   |
+LL |     let _: Foo<{ foo::<T>() }>;
+   |                        ^ cannot perform const operation using `T`
+   |
+   = note: type parameters may not be used in const expressions
+   = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions
+
+error: generic parameters may not be used in const operations
+  --> $DIR/const-arg-in-const-arg.rs:33:24
+   |
+LL |     let _: Foo<{ bar::<N>() }>;
+   |                        ^ cannot perform const operation using `N`
+   |
+   = help: const parameters may only be used as standalone arguments, i.e. `N`
+   = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions
+
 error[E0658]: a non-static lifetime is not allowed in a `const`
-  --> $DIR/const-arg-in-const-arg.rs:31:24
+  --> $DIR/const-arg-in-const-arg.rs:35:24
    |
 LL |     let _: Foo<{ faz::<'a>(&()) }>;
    |                        ^^
@@ -143,7 +125,7 @@ LL |     let _: Foo<{ faz::<'a>(&()) }>;
    = help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable
 
 error[E0658]: a non-static lifetime is not allowed in a `const`
-  --> $DIR/const-arg-in-const-arg.rs:32:24
+  --> $DIR/const-arg-in-const-arg.rs:37:24
    |
 LL |     let _: Foo<{ baz::<'a>(&()) }>;
    |                        ^^
@@ -152,7 +134,7 @@ LL |     let _: Foo<{ baz::<'a>(&()) }>;
    = help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable
 
 error[E0658]: a non-static lifetime is not allowed in a `const`
-  --> $DIR/const-arg-in-const-arg.rs:33:24
+  --> $DIR/const-arg-in-const-arg.rs:38:24
    |
 LL |     let _: Foo<{ faz::<'b>(&()) }>;
    |                        ^^
@@ -161,7 +143,7 @@ LL |     let _: Foo<{ faz::<'b>(&()) }>;
    = help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable
 
 error[E0658]: a non-static lifetime is not allowed in a `const`
-  --> $DIR/const-arg-in-const-arg.rs:34:24
+  --> $DIR/const-arg-in-const-arg.rs:40:24
    |
 LL |     let _: Foo<{ baz::<'b>(&()) }>;
    |                        ^^
@@ -169,8 +151,26 @@ LL |     let _: Foo<{ baz::<'b>(&()) }>;
    = note: see issue #76560 <https://github.com/rust-lang/rust/issues/76560> for more information
    = help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable
 
+error: generic parameters may not be used in const operations
+  --> $DIR/const-arg-in-const-arg.rs:41:27
+   |
+LL |     let _ = Foo::<{ foo::<T>() }>;
+   |                           ^ cannot perform const operation using `T`
+   |
+   = note: type parameters may not be used in const expressions
+   = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions
+
+error: generic parameters may not be used in const operations
+  --> $DIR/const-arg-in-const-arg.rs:42:27
+   |
+LL |     let _ = Foo::<{ bar::<N>() }>;
+   |                           ^ cannot perform const operation using `N`
+   |
+   = help: const parameters may only be used as standalone arguments, i.e. `N`
+   = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions
+
 error[E0658]: a non-static lifetime is not allowed in a `const`
-  --> $DIR/const-arg-in-const-arg.rs:37:27
+  --> $DIR/const-arg-in-const-arg.rs:44:27
    |
 LL |     let _ = Foo::<{ faz::<'a>(&()) }>;
    |                           ^^
@@ -179,7 +179,7 @@ LL |     let _ = Foo::<{ faz::<'a>(&()) }>;
    = help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable
 
 error[E0658]: a non-static lifetime is not allowed in a `const`
-  --> $DIR/const-arg-in-const-arg.rs:38:27
+  --> $DIR/const-arg-in-const-arg.rs:46:27
    |
 LL |     let _ = Foo::<{ baz::<'a>(&()) }>;
    |                           ^^
@@ -188,7 +188,7 @@ LL |     let _ = Foo::<{ baz::<'a>(&()) }>;
    = help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable
 
 error[E0658]: a non-static lifetime is not allowed in a `const`
-  --> $DIR/const-arg-in-const-arg.rs:39:27
+  --> $DIR/const-arg-in-const-arg.rs:47:27
    |
 LL |     let _ = Foo::<{ faz::<'b>(&()) }>;
    |                           ^^
@@ -197,7 +197,7 @@ LL |     let _ = Foo::<{ faz::<'b>(&()) }>;
    = help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable
 
 error[E0658]: a non-static lifetime is not allowed in a `const`
-  --> $DIR/const-arg-in-const-arg.rs:40:27
+  --> $DIR/const-arg-in-const-arg.rs:49:27
    |
 LL |     let _ = Foo::<{ baz::<'b>(&()) }>;
    |                           ^^
@@ -205,6 +205,155 @@ LL |     let _ = Foo::<{ baz::<'b>(&()) }>;
    = note: see issue #76560 <https://github.com/rust-lang/rust/issues/76560> for more information
    = help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable
 
-error: aborting due to 23 previous errors
+error[E0747]: unresolved item provided when a constant was expected
+  --> $DIR/const-arg-in-const-arg.rs:14:23
+   |
+LL |     let _: [u8; bar::<N>()];
+   |                       ^
+   |
+help: if this generic argument was intended as a const parameter, surround it with braces
+   |
+LL |     let _: [u8; bar::<{ N }>()];
+   |                       +   +
+
+error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present
+  --> $DIR/const-arg-in-const-arg.rs:16:23
+   |
+LL |     let _: [u8; faz::<'a>(&())];
+   |                       ^^
+   |
+note: the late bound lifetime parameter is introduced here
+  --> $DIR/const-arg-in-const-arg.rs:8:14
+   |
+LL | const fn faz<'a>(_: &'a ()) -> usize { 13 }
+   |              ^^
+
+error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present
+  --> $DIR/const-arg-in-const-arg.rs:19:23
+   |
+LL |     let _: [u8; faz::<'b>(&())];
+   |                       ^^
+   |
+note: the late bound lifetime parameter is introduced here
+  --> $DIR/const-arg-in-const-arg.rs:8:14
+   |
+LL | const fn faz<'a>(_: &'a ()) -> usize { 13 }
+   |              ^^
+
+error[E0747]: unresolved item provided when a constant was expected
+  --> $DIR/const-arg-in-const-arg.rs:24:23
+   |
+LL |     let _ = [0; bar::<N>()];
+   |                       ^
+   |
+help: if this generic argument was intended as a const parameter, surround it with braces
+   |
+LL |     let _ = [0; bar::<{ N }>()];
+   |                       +   +
+
+error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present
+  --> $DIR/const-arg-in-const-arg.rs:26:23
+   |
+LL |     let _ = [0; faz::<'a>(&())];
+   |                       ^^
+   |
+note: the late bound lifetime parameter is introduced here
+  --> $DIR/const-arg-in-const-arg.rs:8:14
+   |
+LL | const fn faz<'a>(_: &'a ()) -> usize { 13 }
+   |              ^^
+
+error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present
+  --> $DIR/const-arg-in-const-arg.rs:29:23
+   |
+LL |     let _ = [0; faz::<'b>(&())];
+   |                       ^^
+   |
+note: the late bound lifetime parameter is introduced here
+  --> $DIR/const-arg-in-const-arg.rs:8:14
+   |
+LL | const fn faz<'a>(_: &'a ()) -> usize { 13 }
+   |              ^^
+
+error[E0747]: unresolved item provided when a constant was expected
+  --> $DIR/const-arg-in-const-arg.rs:33:24
+   |
+LL |     let _: Foo<{ bar::<N>() }>;
+   |                        ^
+   |
+help: if this generic argument was intended as a const parameter, surround it with braces
+   |
+LL |     let _: Foo<{ bar::<{ N }>() }>;
+   |                        +   +
+
+error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present
+  --> $DIR/const-arg-in-const-arg.rs:35:24
+   |
+LL |     let _: Foo<{ faz::<'a>(&()) }>;
+   |                        ^^
+   |
+note: the late bound lifetime parameter is introduced here
+  --> $DIR/const-arg-in-const-arg.rs:8:14
+   |
+LL | const fn faz<'a>(_: &'a ()) -> usize { 13 }
+   |              ^^
+
+error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present
+  --> $DIR/const-arg-in-const-arg.rs:38:24
+   |
+LL |     let _: Foo<{ faz::<'b>(&()) }>;
+   |                        ^^
+   |
+note: the late bound lifetime parameter is introduced here
+  --> $DIR/const-arg-in-const-arg.rs:8:14
+   |
+LL | const fn faz<'a>(_: &'a ()) -> usize { 13 }
+   |              ^^
+
+error: constant expression depends on a generic parameter
+  --> $DIR/const-arg-in-const-arg.rs:23:17
+   |
+LL |     let _ = [0; foo::<T>()];
+   |                 ^^^^^^^^^^
+   |
+   = note: this may fail depending on what value the parameter takes
+
+error[E0747]: unresolved item provided when a constant was expected
+  --> $DIR/const-arg-in-const-arg.rs:42:27
+   |
+LL |     let _ = Foo::<{ bar::<N>() }>;
+   |                           ^
+   |
+help: if this generic argument was intended as a const parameter, surround it with braces
+   |
+LL |     let _ = Foo::<{ bar::<{ N }>() }>;
+   |                           +   +
+
+error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present
+  --> $DIR/const-arg-in-const-arg.rs:44:27
+   |
+LL |     let _ = Foo::<{ faz::<'a>(&()) }>;
+   |                           ^^
+   |
+note: the late bound lifetime parameter is introduced here
+  --> $DIR/const-arg-in-const-arg.rs:8:14
+   |
+LL | const fn faz<'a>(_: &'a ()) -> usize { 13 }
+   |              ^^
+
+error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present
+  --> $DIR/const-arg-in-const-arg.rs:47:27
+   |
+LL |     let _ = Foo::<{ faz::<'b>(&()) }>;
+   |                           ^^
+   |
+note: the late bound lifetime parameter is introduced here
+  --> $DIR/const-arg-in-const-arg.rs:8:14
+   |
+LL | const fn faz<'a>(_: &'a ()) -> usize { 13 }
+   |              ^^
+
+error: aborting due to 36 previous errors
 
-For more information about this error, try `rustc --explain E0658`.
+Some errors have detailed explanations: E0658, E0747.
+For more information about an error, try `rustc --explain E0658`.
diff --git a/src/test/ui/const-generics/const-arg-in-const-arg.rs b/src/test/ui/const-generics/const-arg-in-const-arg.rs
index 39f0b237330..b9daadb5474 100644
--- a/src/test/ui/const-generics/const-arg-in-const-arg.rs
+++ b/src/test/ui/const-generics/const-arg-in-const-arg.rs
@@ -12,31 +12,40 @@ struct Foo<const N: usize>;
 fn test<'a, 'b, T, const N: usize>() where &'b (): Sized {
     let _: [u8; foo::<T>()]; //~ ERROR generic parameters may not
     let _: [u8; bar::<N>()]; //~ ERROR generic parameters may not
+                             //~^ ERROR unresolved item provided when a constant was expected
     let _: [u8; faz::<'a>(&())]; //~ ERROR a non-static lifetime
+                                 //~^ ERROR cannot specify lifetime arguments
     let _: [u8; baz::<'a>(&())]; //~ ERROR a non-static lifetime
     let _: [u8; faz::<'b>(&())]; //~ ERROR a non-static lifetime
+                                 //~^ ERROR cannot specify lifetime arguments
     let _: [u8; baz::<'b>(&())]; //~ ERROR a non-static lifetime
 
-    // NOTE: This can be a future compat warning instead of an error,
-    // so we stop compilation before emitting this error in this test.
-    let _ = [0; foo::<T>()];
-
+    let _ = [0; foo::<T>()]; //~ ERROR constant expression depends on a generic parameter
     let _ = [0; bar::<N>()]; //~ ERROR generic parameters may not
+                             //~^ ERROR unresolved item provided when a constant was expected
     let _ = [0; faz::<'a>(&())]; //~ ERROR a non-static lifetime
+                                 //~^ ERROR cannot specify lifetime arguments
     let _ = [0; baz::<'a>(&())]; //~ ERROR a non-static lifetime
     let _ = [0; faz::<'b>(&())]; //~ ERROR a non-static lifetime
+                                 //~^ ERROR cannot specify lifetime arguments
     let _ = [0; baz::<'b>(&())]; //~ ERROR a non-static lifetime
     let _: Foo<{ foo::<T>() }>; //~ ERROR generic parameters may not
     let _: Foo<{ bar::<N>() }>; //~ ERROR generic parameters may not
+                                //~^ ERROR unresolved item provided when a constant was expected
     let _: Foo<{ faz::<'a>(&()) }>; //~ ERROR a non-static lifetime
+                                    //~^ ERROR cannot specify lifetime arguments
     let _: Foo<{ baz::<'a>(&()) }>; //~ ERROR a non-static lifetime
     let _: Foo<{ faz::<'b>(&()) }>; //~ ERROR a non-static lifetime
+                                    //~^ ERROR cannot specify lifetime arguments
     let _: Foo<{ baz::<'b>(&()) }>; //~ ERROR a non-static lifetime
     let _ = Foo::<{ foo::<T>() }>; //~ ERROR generic parameters may not
     let _ = Foo::<{ bar::<N>() }>; //~ ERROR generic parameters may not
+                                   //~^ ERROR unresolved item provided when a constant was expected
     let _ = Foo::<{ faz::<'a>(&()) }>; //~ ERROR a non-static lifetime
+                                       //~^ ERROR cannot specify lifetime arguments
     let _ = Foo::<{ baz::<'a>(&()) }>; //~ ERROR a non-static lifetime
     let _ = Foo::<{ faz::<'b>(&()) }>; //~ ERROR a non-static lifetime
+                                       //~^ ERROR cannot specify lifetime arguments
     let _ = Foo::<{ baz::<'b>(&()) }>; //~ ERROR a non-static lifetime
 }
 
diff --git a/src/test/ui/const-generics/defaults/rp_impl_trait_fail.rs b/src/test/ui/const-generics/defaults/rp_impl_trait_fail.rs
index 24031aa1e61..308c121a941 100644
--- a/src/test/ui/const-generics/defaults/rp_impl_trait_fail.rs
+++ b/src/test/ui/const-generics/defaults/rp_impl_trait_fail.rs
@@ -5,7 +5,6 @@ impl<const N: u32> Trait for Uwu<N> {}
 
 fn rawr() -> impl Trait {
     //~^ error: the trait bound `Uwu<10_u32, 12_u32>: Trait` is not satisfied
-    //~| error: the trait bound `Uwu<10_u32, 12_u32>: Trait` is not satisfied
     Uwu::<10, 12>
 }
 
@@ -17,13 +16,11 @@ impl Traitor<1, 2> for u64 {}
 
 fn uwu<const N: u8>() -> impl Traitor<N> {
     //~^ error: the trait bound `u32: Traitor<N, N>` is not satisfied
-    //~| error: the trait bound `u32: Traitor<N, N>` is not satisfied
     1_u32
 }
 
 fn owo() -> impl Traitor {
     //~^ error: the trait bound `u64: Traitor<1_u8, 1_u8>` is not satisfied
-    //~| error: the trait bound `u64: Traitor<1_u8, 1_u8>` is not satisfied
     1_u64
 }
 
diff --git a/src/test/ui/const-generics/defaults/rp_impl_trait_fail.stderr b/src/test/ui/const-generics/defaults/rp_impl_trait_fail.stderr
index 48c61c22e7a..8031da28ca1 100644
--- a/src/test/ui/const-generics/defaults/rp_impl_trait_fail.stderr
+++ b/src/test/ui/const-generics/defaults/rp_impl_trait_fail.stderr
@@ -6,21 +6,8 @@ LL | fn rawr() -> impl Trait {
    |
    = help: the trait `Trait` is implemented for `Uwu<N>`
 
-error[E0277]: the trait bound `Uwu<10_u32, 12_u32>: Trait` is not satisfied
-  --> $DIR/rp_impl_trait_fail.rs:6:25
-   |
-LL |   fn rawr() -> impl Trait {
-   |  _________________________^
-LL | |
-LL | |
-LL | |     Uwu::<10, 12>
-LL | | }
-   | |_^ the trait `Trait` is not implemented for `Uwu<10_u32, 12_u32>`
-   |
-   = help: the trait `Trait` is implemented for `Uwu<N>`
-
 error[E0277]: the trait bound `u32: Traitor<N, N>` is not satisfied
-  --> $DIR/rp_impl_trait_fail.rs:18:26
+  --> $DIR/rp_impl_trait_fail.rs:17:26
    |
 LL | fn uwu<const N: u8>() -> impl Traitor<N> {
    |                          ^^^^^^^^^^^^^^^ the trait `Traitor<N, N>` is not implemented for `u32`
@@ -29,23 +16,8 @@ LL | fn uwu<const N: u8>() -> impl Traitor<N> {
              <u32 as Traitor<N, 2_u8>>
              <u64 as Traitor<1_u8, 2_u8>>
 
-error[E0277]: the trait bound `u32: Traitor<N, N>` is not satisfied
-  --> $DIR/rp_impl_trait_fail.rs:18:42
-   |
-LL |   fn uwu<const N: u8>() -> impl Traitor<N> {
-   |  __________________________________________^
-LL | |
-LL | |
-LL | |     1_u32
-LL | | }
-   | |_^ the trait `Traitor<N, N>` is not implemented for `u32`
-   |
-   = help: the following other types implement trait `Traitor<N, M>`:
-             <u32 as Traitor<N, 2_u8>>
-             <u64 as Traitor<1_u8, 2_u8>>
-
 error[E0277]: the trait bound `u64: Traitor<1_u8, 1_u8>` is not satisfied
-  --> $DIR/rp_impl_trait_fail.rs:24:13
+  --> $DIR/rp_impl_trait_fail.rs:22:13
    |
 LL | fn owo() -> impl Traitor {
    |             ^^^^^^^^^^^^ the trait `Traitor<1_u8, 1_u8>` is not implemented for `u64`
@@ -54,21 +26,6 @@ LL | fn owo() -> impl Traitor {
              <u32 as Traitor<N, 2_u8>>
              <u64 as Traitor<1_u8, 2_u8>>
 
-error[E0277]: the trait bound `u64: Traitor<1_u8, 1_u8>` is not satisfied
-  --> $DIR/rp_impl_trait_fail.rs:24:26
-   |
-LL |   fn owo() -> impl Traitor {
-   |  __________________________^
-LL | |
-LL | |
-LL | |     1_u64
-LL | | }
-   | |_^ the trait `Traitor<1_u8, 1_u8>` is not implemented for `u64`
-   |
-   = help: the following other types implement trait `Traitor<N, M>`:
-             <u32 as Traitor<N, 2_u8>>
-             <u64 as Traitor<1_u8, 2_u8>>
-
-error: aborting due to 6 previous errors
+error: aborting due to 3 previous errors
 
 For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/const-generics/issues/issue-56445-1.min.stderr b/src/test/ui/const-generics/issues/issue-56445-1.min.stderr
index 179643a7552..43a5df117fd 100644
--- a/src/test/ui/const-generics/issues/issue-56445-1.min.stderr
+++ b/src/test/ui/const-generics/issues/issue-56445-1.min.stderr
@@ -6,6 +6,15 @@ LL | struct Bug<'a, const S: &'a str>(PhantomData<&'a ()>);
    |
    = note: for more information, see issue #74052 <https://github.com/rust-lang/rust/issues/74052>
 
-error: aborting due to previous error
+error: `&'static str` is forbidden as the type of a const generic parameter
+  --> $DIR/issue-56445-1.rs:9:25
+   |
+LL | struct Bug<'a, const S: &'a str>(PhantomData<&'a ()>);
+   |                         ^^^^^^^
+   |
+   = note: the only supported types are integers, `bool` and `char`
+   = help: more complex types are supported with `#![feature(adt_const_params)]`
+
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0771`.
diff --git a/src/test/ui/const-generics/issues/issue-56445-1.rs b/src/test/ui/const-generics/issues/issue-56445-1.rs
index aeef778991f..13eb2ea9f69 100644
--- a/src/test/ui/const-generics/issues/issue-56445-1.rs
+++ b/src/test/ui/const-generics/issues/issue-56445-1.rs
@@ -8,5 +8,6 @@ use std::marker::PhantomData;
 
 struct Bug<'a, const S: &'a str>(PhantomData<&'a ()>);
 //~^ ERROR: use of non-static lifetime `'a` in const generic
+//[min]~| ERROR: `&'static str` is forbidden as the type of a const generic parameter
 
 impl Bug<'_, ""> {}
diff --git a/src/test/ui/const-generics/issues/issue-61336-2.stderr b/src/test/ui/const-generics/issues/issue-61336-2.stderr
index 48aaaf5e544..5bb35669623 100644
--- a/src/test/ui/const-generics/issues/issue-61336-2.stderr
+++ b/src/test/ui/const-generics/issues/issue-61336-2.stderr
@@ -1,10 +1,10 @@
 error[E0277]: the trait bound `T: Copy` is not satisfied
-  --> $DIR/issue-61336-2.rs:6:5
+  --> $DIR/issue-61336-2.rs:6:6
    |
 LL |     [x; { N }]
-   |     ^^^^^^^^^^ the trait `Copy` is not implemented for `T`
+   |      ^ the trait `Copy` is not implemented for `T`
    |
-   = note: the `Copy` trait is required because the repeated element will be copied
+   = note: the `Copy` trait is required because this value will be copied for each element of the array
 help: consider restricting type parameter `T`
    |
 LL | fn g<T: std::marker::Copy, const N: usize>(x: T) -> [T; N] {
diff --git a/src/test/ui/const-generics/issues/issue-61336.stderr b/src/test/ui/const-generics/issues/issue-61336.stderr
index 665a1a677a1..8d9e545b456 100644
--- a/src/test/ui/const-generics/issues/issue-61336.stderr
+++ b/src/test/ui/const-generics/issues/issue-61336.stderr
@@ -1,10 +1,10 @@
 error[E0277]: the trait bound `T: Copy` is not satisfied
-  --> $DIR/issue-61336.rs:6:5
+  --> $DIR/issue-61336.rs:6:6
    |
 LL |     [x; N]
-   |     ^^^^^^ the trait `Copy` is not implemented for `T`
+   |      ^ the trait `Copy` is not implemented for `T`
    |
-   = note: the `Copy` trait is required because the repeated element will be copied
+   = note: the `Copy` trait is required because this value will be copied for each element of the array
 help: consider restricting type parameter `T`
    |
 LL | fn g<T: std::marker::Copy, const N: usize>(x: T) -> [T; N] {
diff --git a/src/test/ui/consts/const-blocks/fn-call-in-non-const.rs b/src/test/ui/consts/const-blocks/fn-call-in-non-const.rs
index 19217843759..18b4dc714de 100644
--- a/src/test/ui/consts/const-blocks/fn-call-in-non-const.rs
+++ b/src/test/ui/consts/const-blocks/fn-call-in-non-const.rs
@@ -12,5 +12,5 @@ const fn copy() -> u32 {
 fn main() {
     let _: [u32; 2] = [copy(); 2];
     let _: [Option<Bar>; 2] = [no_copy(); 2];
-    //~^ ERROR the trait bound `Option<Bar>: Copy` is not satisfied
+    //~^ ERROR the trait bound `Bar: Copy` is not satisfied
 }
diff --git a/src/test/ui/consts/const-blocks/fn-call-in-non-const.stderr b/src/test/ui/consts/const-blocks/fn-call-in-non-const.stderr
index 52a1669e330..5306fed2251 100644
--- a/src/test/ui/consts/const-blocks/fn-call-in-non-const.stderr
+++ b/src/test/ui/consts/const-blocks/fn-call-in-non-const.stderr
@@ -1,13 +1,17 @@
-error[E0277]: the trait bound `Option<Bar>: Copy` is not satisfied
-  --> $DIR/fn-call-in-non-const.rs:14:31
+error[E0277]: the trait bound `Bar: Copy` is not satisfied
+  --> $DIR/fn-call-in-non-const.rs:14:32
    |
 LL |     let _: [Option<Bar>; 2] = [no_copy(); 2];
-   |                               ^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Option<Bar>`
+   |                                ^^^^^^^^^ the trait `Copy` is not implemented for `Bar`
    |
-   = help: the trait `Copy` is implemented for `Option<T>`
-   = note: the `Copy` trait is required because the repeated element will be copied
+   = note: required because of the requirements on the impl of `Copy` for `Option<Bar>`
+   = note: the `Copy` trait is required because this value will be copied for each element of the array
    = help: consider creating a new `const` item and initializing it with the result of the function call to be used in the repeat position, like `const VAL: Type = const_fn();` and `let x = [VAL; 42];`
    = help: create an inline `const` block, see RFC #2920 <https://github.com/rust-lang/rfcs/pull/2920> for more information
+help: consider annotating `Bar` with `#[derive(Copy)]`
+   |
+LL | #[derive(Copy)]
+   |
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/consts/const-blocks/migrate-fail.rs b/src/test/ui/consts/const-blocks/migrate-fail.rs
index bb12139a7ba..d5a17249cc9 100644
--- a/src/test/ui/consts/const-blocks/migrate-fail.rs
+++ b/src/test/ui/consts/const-blocks/migrate-fail.rs
@@ -11,13 +11,13 @@ mod non_constants {
     fn no_impl_copy_empty_value_multiple_elements() {
         let x = None;
         let arr: [Option<Bar>; 2] = [x; 2];
-        //~^ ERROR the trait bound `Option<Bar>: Copy` is not satisfied [E0277]
+        //~^ ERROR the trait bound `Bar: Copy` is not satisfied [E0277]
     }
 
     fn no_impl_copy_value_multiple_elements() {
         let x = Some(Bar);
         let arr: [Option<Bar>; 2] = [x; 2];
-        //~^ ERROR the trait bound `Option<Bar>: Copy` is not satisfied [E0277]
+        //~^ ERROR the trait bound `Bar: Copy` is not satisfied [E0277]
     }
 }
 
diff --git a/src/test/ui/consts/const-blocks/migrate-fail.stderr b/src/test/ui/consts/const-blocks/migrate-fail.stderr
index 318fec60290..2e7ff5cb8b3 100644
--- a/src/test/ui/consts/const-blocks/migrate-fail.stderr
+++ b/src/test/ui/consts/const-blocks/migrate-fail.stderr
@@ -1,20 +1,28 @@
-error[E0277]: the trait bound `Option<Bar>: Copy` is not satisfied
-  --> $DIR/migrate-fail.rs:13:37
+error[E0277]: the trait bound `Bar: Copy` is not satisfied
+  --> $DIR/migrate-fail.rs:13:38
    |
 LL |         let arr: [Option<Bar>; 2] = [x; 2];
-   |                                     ^^^^^^ the trait `Copy` is not implemented for `Option<Bar>`
+   |                                      ^ the trait `Copy` is not implemented for `Bar`
+   |
+   = note: required because of the requirements on the impl of `Copy` for `Option<Bar>`
+   = note: the `Copy` trait is required because this value will be copied for each element of the array
+help: consider annotating `Bar` with `#[derive(Copy)]`
+   |
+LL | #[derive(Copy)]
    |
-   = help: the trait `Copy` is implemented for `Option<T>`
-   = note: the `Copy` trait is required because the repeated element will be copied
 
-error[E0277]: the trait bound `Option<Bar>: Copy` is not satisfied
-  --> $DIR/migrate-fail.rs:19:37
+error[E0277]: the trait bound `Bar: Copy` is not satisfied
+  --> $DIR/migrate-fail.rs:19:38
    |
 LL |         let arr: [Option<Bar>; 2] = [x; 2];
-   |                                     ^^^^^^ the trait `Copy` is not implemented for `Option<Bar>`
+   |                                      ^ the trait `Copy` is not implemented for `Bar`
+   |
+   = note: required because of the requirements on the impl of `Copy` for `Option<Bar>`
+   = note: the `Copy` trait is required because this value will be copied for each element of the array
+help: consider annotating `Bar` with `#[derive(Copy)]`
+   |
+LL | #[derive(Copy)]
    |
-   = help: the trait `Copy` is implemented for `Option<T>`
-   = note: the `Copy` trait is required because the repeated element will be copied
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/consts/const-blocks/nll-fail.rs b/src/test/ui/consts/const-blocks/nll-fail.rs
index 871387c1fd0..9d4aef39e54 100644
--- a/src/test/ui/consts/const-blocks/nll-fail.rs
+++ b/src/test/ui/consts/const-blocks/nll-fail.rs
@@ -10,13 +10,13 @@ mod non_constants {
     fn no_impl_copy_empty_value_multiple_elements() {
         let x = None;
         let arr: [Option<Bar>; 2] = [x; 2];
-        //~^ ERROR the trait bound `Option<Bar>: Copy` is not satisfied [E0277]
+        //~^ ERROR the trait bound `Bar: Copy` is not satisfied [E0277]
     }
 
     fn no_impl_copy_value_multiple_elements() {
         let x = Some(Bar);
         let arr: [Option<Bar>; 2] = [x; 2];
-        //~^ ERROR the trait bound `Option<Bar>: Copy` is not satisfied [E0277]
+        //~^ ERROR the trait bound `Bar: Copy` is not satisfied [E0277]
     }
 }
 
diff --git a/src/test/ui/consts/const-blocks/nll-fail.stderr b/src/test/ui/consts/const-blocks/nll-fail.stderr
index 5a34361aa83..c0d273b5a9a 100644
--- a/src/test/ui/consts/const-blocks/nll-fail.stderr
+++ b/src/test/ui/consts/const-blocks/nll-fail.stderr
@@ -1,20 +1,28 @@
-error[E0277]: the trait bound `Option<Bar>: Copy` is not satisfied
-  --> $DIR/nll-fail.rs:12:37
+error[E0277]: the trait bound `Bar: Copy` is not satisfied
+  --> $DIR/nll-fail.rs:12:38
    |
 LL |         let arr: [Option<Bar>; 2] = [x; 2];
-   |                                     ^^^^^^ the trait `Copy` is not implemented for `Option<Bar>`
+   |                                      ^ the trait `Copy` is not implemented for `Bar`
+   |
+   = note: required because of the requirements on the impl of `Copy` for `Option<Bar>`
+   = note: the `Copy` trait is required because this value will be copied for each element of the array
+help: consider annotating `Bar` with `#[derive(Copy)]`
+   |
+LL | #[derive(Copy)]
    |
-   = help: the trait `Copy` is implemented for `Option<T>`
-   = note: the `Copy` trait is required because the repeated element will be copied
 
-error[E0277]: the trait bound `Option<Bar>: Copy` is not satisfied
-  --> $DIR/nll-fail.rs:18:37
+error[E0277]: the trait bound `Bar: Copy` is not satisfied
+  --> $DIR/nll-fail.rs:18:38
    |
 LL |         let arr: [Option<Bar>; 2] = [x; 2];
-   |                                     ^^^^^^ the trait `Copy` is not implemented for `Option<Bar>`
+   |                                      ^ the trait `Copy` is not implemented for `Bar`
+   |
+   = note: required because of the requirements on the impl of `Copy` for `Option<Bar>`
+   = note: the `Copy` trait is required because this value will be copied for each element of the array
+help: consider annotating `Bar` with `#[derive(Copy)]`
+   |
+LL | #[derive(Copy)]
    |
-   = help: the trait `Copy` is implemented for `Option<T>`
-   = note: the `Copy` trait is required because the repeated element will be copied
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/consts/const-blocks/trait-error.rs b/src/test/ui/consts/const-blocks/trait-error.rs
index 5a614cbdd15..49d1e9b9434 100644
--- a/src/test/ui/consts/const-blocks/trait-error.rs
+++ b/src/test/ui/consts/const-blocks/trait-error.rs
@@ -3,5 +3,5 @@ struct Foo<T>(T);
 
 fn main() {
     [Foo(String::new()); 4];
-    //~^ ERROR the trait bound `Foo<String>: Copy` is not satisfied [E0277]
+    //~^ ERROR the trait bound `String: Copy` is not satisfied [E0277]
 }
diff --git a/src/test/ui/consts/const-blocks/trait-error.stderr b/src/test/ui/consts/const-blocks/trait-error.stderr
index 6979ff36176..ece200ad10b 100644
--- a/src/test/ui/consts/const-blocks/trait-error.stderr
+++ b/src/test/ui/consts/const-blocks/trait-error.stderr
@@ -1,11 +1,18 @@
-error[E0277]: the trait bound `Foo<String>: Copy` is not satisfied
-  --> $DIR/trait-error.rs:5:5
+error[E0277]: the trait bound `String: Copy` is not satisfied
+  --> $DIR/trait-error.rs:5:6
    |
 LL |     [Foo(String::new()); 4];
-   |     ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Foo<String>`
+   |      ^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String`
    |
-   = help: the trait `Copy` is implemented for `Foo<T>`
-   = note: the `Copy` trait is required because the repeated element will be copied
+note: required because of the requirements on the impl of `Copy` for `Foo<String>`
+  --> $DIR/trait-error.rs:1:10
+   |
+LL | #[derive(Copy, Clone)]
+   |          ^^^^
+   = note: the `Copy` trait is required because this value will be copied for each element of the array
+   = help: consider creating a new `const` item and initializing it with the result of the function call to be used in the repeat position, like `const VAL: Type = const_fn();` and `let x = [VAL; 42];`
+   = help: create an inline `const` block, see RFC #2920 <https://github.com/rust-lang/rfcs/pull/2920> for more information
+   = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/consts/const-eval/issue-50814-2.stderr b/src/test/ui/consts/const-eval/issue-50814-2.stderr
index d34ac773da2..298f0a4a446 100644
--- a/src/test/ui/consts/const-eval/issue-50814-2.stderr
+++ b/src/test/ui/consts/const-eval/issue-50814-2.stderr
@@ -16,6 +16,12 @@ error[E0080]: evaluation of `foo::<()>` failed
 LL |     &<A<T> as Foo<T>>::BAR
    |      ^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors
 
+note: the above error was encountered while instantiating `fn foo::<()>`
+  --> $DIR/issue-50814-2.rs:31:22
+   |
+LL |     println!("{:x}", foo::<()>() as *const usize as usize);
+   |                      ^^^^^^^^^^^
+
 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/issue-50814.stderr b/src/test/ui/consts/const-eval/issue-50814.stderr
index dd8d6bf839a..87bea28e763 100644
--- a/src/test/ui/consts/const-eval/issue-50814.stderr
+++ b/src/test/ui/consts/const-eval/issue-50814.stderr
@@ -16,6 +16,12 @@ error[E0080]: evaluation of `foo::<i32>` failed
 LL |     &Sum::<U8,U8>::MAX
    |      ^^^^^^^^^^^^^^^^^ referenced constant has errors
 
+note: the above error was encountered while instantiating `fn foo::<i32>`
+  --> $DIR/issue-50814.rs:26:5
+   |
+LL |     foo(0);
+   |     ^^^^^^
+
 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/ub-enum.32bit.stderr b/src/test/ui/consts/const-eval/ub-enum.32bit.stderr
index 111d243959a..b60bac541f4 100644
--- a/src/test/ui/consts/const-eval/ub-enum.32bit.stderr
+++ b/src/test/ui/consts/const-eval/ub-enum.32bit.stderr
@@ -119,27 +119,17 @@ LL | const BAD_OPTION_CHAR: Option<(char, char)> = Some(('x', unsafe { mem::tran
                78 00 00 00 ff ff ff ff                         │ x.......
            }
 
-error[E0080]: it is undefined behavior to use this value
-  --> $DIR/ub-enum.rs:92:1
+error[E0080]: evaluation of constant value failed
+  --> $DIR/ub-enum.rs:92:77
    |
 LL | const BAD_UNINHABITED_WITH_DATA1: Result<(i32, Never), (i32, !)> = unsafe { mem::transmute(0u64) };
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .<enum-variant(Ok)>.0.1: encountered a value of uninhabited type Never
-   |
-   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
-   = note: the raw bytes of the constant (size: 8, align: 4) {
-               00 00 00 00 00 00 00 00                         │ ........
-           }
+   |                                                                             ^^^^^^^^^^^^^^^^^^^^ transmuting to uninhabited type
 
-error[E0080]: it is undefined behavior to use this value
-  --> $DIR/ub-enum.rs:94:1
+error[E0080]: evaluation of constant value failed
+  --> $DIR/ub-enum.rs:94:77
    |
 LL | const BAD_UNINHABITED_WITH_DATA2: Result<(i32, !), (i32, Never)> = unsafe { mem::transmute(0u64) };
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .<enum-variant(Ok)>.0.1: encountered a value of the never type `!`
-   |
-   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
-   = note: the raw bytes of the constant (size: 8, align: 4) {
-               00 00 00 00 00 00 00 00                         │ ........
-           }
+   |                                                                             ^^^^^^^^^^^^^^^^^^^^ transmuting to uninhabited type
 
 error: aborting due to 13 previous errors
 
diff --git a/src/test/ui/consts/const-eval/ub-enum.64bit.stderr b/src/test/ui/consts/const-eval/ub-enum.64bit.stderr
index eee132bae00..1d81e2b3eed 100644
--- a/src/test/ui/consts/const-eval/ub-enum.64bit.stderr
+++ b/src/test/ui/consts/const-eval/ub-enum.64bit.stderr
@@ -119,27 +119,17 @@ LL | const BAD_OPTION_CHAR: Option<(char, char)> = Some(('x', unsafe { mem::tran
                78 00 00 00 ff ff ff ff                         │ x.......
            }
 
-error[E0080]: it is undefined behavior to use this value
-  --> $DIR/ub-enum.rs:92:1
+error[E0080]: evaluation of constant value failed
+  --> $DIR/ub-enum.rs:92:77
    |
 LL | const BAD_UNINHABITED_WITH_DATA1: Result<(i32, Never), (i32, !)> = unsafe { mem::transmute(0u64) };
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .<enum-variant(Ok)>.0.1: encountered a value of uninhabited type Never
-   |
-   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
-   = note: the raw bytes of the constant (size: 8, align: 4) {
-               00 00 00 00 00 00 00 00                         │ ........
-           }
+   |                                                                             ^^^^^^^^^^^^^^^^^^^^ transmuting to uninhabited type
 
-error[E0080]: it is undefined behavior to use this value
-  --> $DIR/ub-enum.rs:94:1
+error[E0080]: evaluation of constant value failed
+  --> $DIR/ub-enum.rs:94:77
    |
 LL | const BAD_UNINHABITED_WITH_DATA2: Result<(i32, !), (i32, Never)> = unsafe { mem::transmute(0u64) };
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .<enum-variant(Ok)>.0.1: encountered a value of the never type `!`
-   |
-   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
-   = note: the raw bytes of the constant (size: 8, align: 4) {
-               00 00 00 00 00 00 00 00                         │ ........
-           }
+   |                                                                             ^^^^^^^^^^^^^^^^^^^^ transmuting to uninhabited type
 
 error: aborting due to 13 previous errors
 
diff --git a/src/test/ui/consts/const-eval/ub-enum.rs b/src/test/ui/consts/const-eval/ub-enum.rs
index e408d8ec072..86288685303 100644
--- a/src/test/ui/consts/const-eval/ub-enum.rs
+++ b/src/test/ui/consts/const-eval/ub-enum.rs
@@ -90,9 +90,9 @@ const BAD_OPTION_CHAR: Option<(char, char)> = Some(('x', unsafe { mem::transmute
 // All variants are uninhabited but also have data.
 // Use `0` as constant to make behavior endianess-independent.
 const BAD_UNINHABITED_WITH_DATA1: Result<(i32, Never), (i32, !)> = unsafe { mem::transmute(0u64) };
-//~^ ERROR is undefined behavior
+//~^ ERROR evaluation of constant value failed
 const BAD_UNINHABITED_WITH_DATA2: Result<(i32, !), (i32, Never)> = unsafe { mem::transmute(0u64) };
-//~^ ERROR is undefined behavior
+//~^ ERROR evaluation of constant value failed
 
 fn main() {
 }
diff --git a/src/test/ui/consts/const-eval/validate_uninhabited_zsts.32bit.stderr b/src/test/ui/consts/const-eval/validate_uninhabited_zsts.32bit.stderr
index 7dc1ec865af..65ab1b02b35 100644
--- a/src/test/ui/consts/const-eval/validate_uninhabited_zsts.32bit.stderr
+++ b/src/test/ui/consts/const-eval/validate_uninhabited_zsts.32bit.stderr
@@ -7,14 +7,14 @@ LL |     unsafe { std::mem::transmute(()) }
    |              transmuting to uninhabited type
    |              inside `foo` at $DIR/validate_uninhabited_zsts.rs:4:14
 ...
-LL | const FOO: [Empty; 3] = [foo(); 3];
-   |                          ----- inside `FOO` at $DIR/validate_uninhabited_zsts.rs:13:26
+LL | const FOO: [empty::Empty; 3] = [foo(); 3];
+   |                                 ----- inside `FOO` at $DIR/validate_uninhabited_zsts.rs:20:33
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/validate_uninhabited_zsts.rs:16:1
+  --> $DIR/validate_uninhabited_zsts.rs:23:1
    |
-LL | const BAR: [Empty; 3] = [unsafe { std::mem::transmute(()) }; 3];
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at [0]: encountered a value of uninhabited type Empty
+LL | const BAR: [empty::Empty; 3] = [unsafe { std::mem::transmute(()) }; 3];
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at [0].0: encountered a value of uninhabited type empty::Void
    |
    = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
    = note: the raw bytes of the constant (size: 0, align: 1) {}
@@ -31,16 +31,20 @@ LL |     unsafe { std::mem::transmute(()) }
    = note: `#[warn(invalid_value)]` on by default
    = note: the `!` type has no valid value
 
-warning: the type `Empty` does not permit zero-initialization
-  --> $DIR/validate_uninhabited_zsts.rs:16:35
+warning: the type `empty::Empty` does not permit zero-initialization
+  --> $DIR/validate_uninhabited_zsts.rs:23:42
    |
-LL | const BAR: [Empty; 3] = [unsafe { std::mem::transmute(()) }; 3];
-   |                                   ^^^^^^^^^^^^^^^^^^^^^^^
-   |                                   |
-   |                                   this code causes undefined behavior when executed
-   |                                   help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
+LL | const BAR: [empty::Empty; 3] = [unsafe { std::mem::transmute(()) }; 3];
+   |                                          ^^^^^^^^^^^^^^^^^^^^^^^
+   |                                          |
+   |                                          this code causes undefined behavior when executed
+   |                                          help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
    |
-   = note: enums with no variants have no valid value
+note: enums with no variants have no valid value (in this struct field)
+  --> $DIR/validate_uninhabited_zsts.rs:16:22
+   |
+LL |     pub struct Empty(Void);
+   |                      ^^^^
 
 error: aborting due to 2 previous errors; 2 warnings emitted
 
diff --git a/src/test/ui/consts/const-eval/validate_uninhabited_zsts.64bit.stderr b/src/test/ui/consts/const-eval/validate_uninhabited_zsts.64bit.stderr
index 7dc1ec865af..65ab1b02b35 100644
--- a/src/test/ui/consts/const-eval/validate_uninhabited_zsts.64bit.stderr
+++ b/src/test/ui/consts/const-eval/validate_uninhabited_zsts.64bit.stderr
@@ -7,14 +7,14 @@ LL |     unsafe { std::mem::transmute(()) }
    |              transmuting to uninhabited type
    |              inside `foo` at $DIR/validate_uninhabited_zsts.rs:4:14
 ...
-LL | const FOO: [Empty; 3] = [foo(); 3];
-   |                          ----- inside `FOO` at $DIR/validate_uninhabited_zsts.rs:13:26
+LL | const FOO: [empty::Empty; 3] = [foo(); 3];
+   |                                 ----- inside `FOO` at $DIR/validate_uninhabited_zsts.rs:20:33
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/validate_uninhabited_zsts.rs:16:1
+  --> $DIR/validate_uninhabited_zsts.rs:23:1
    |
-LL | const BAR: [Empty; 3] = [unsafe { std::mem::transmute(()) }; 3];
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at [0]: encountered a value of uninhabited type Empty
+LL | const BAR: [empty::Empty; 3] = [unsafe { std::mem::transmute(()) }; 3];
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at [0].0: encountered a value of uninhabited type empty::Void
    |
    = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
    = note: the raw bytes of the constant (size: 0, align: 1) {}
@@ -31,16 +31,20 @@ LL |     unsafe { std::mem::transmute(()) }
    = note: `#[warn(invalid_value)]` on by default
    = note: the `!` type has no valid value
 
-warning: the type `Empty` does not permit zero-initialization
-  --> $DIR/validate_uninhabited_zsts.rs:16:35
+warning: the type `empty::Empty` does not permit zero-initialization
+  --> $DIR/validate_uninhabited_zsts.rs:23:42
    |
-LL | const BAR: [Empty; 3] = [unsafe { std::mem::transmute(()) }; 3];
-   |                                   ^^^^^^^^^^^^^^^^^^^^^^^
-   |                                   |
-   |                                   this code causes undefined behavior when executed
-   |                                   help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
+LL | const BAR: [empty::Empty; 3] = [unsafe { std::mem::transmute(()) }; 3];
+   |                                          ^^^^^^^^^^^^^^^^^^^^^^^
+   |                                          |
+   |                                          this code causes undefined behavior when executed
+   |                                          help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
    |
-   = note: enums with no variants have no valid value
+note: enums with no variants have no valid value (in this struct field)
+  --> $DIR/validate_uninhabited_zsts.rs:16:22
+   |
+LL |     pub struct Empty(Void);
+   |                      ^^^^
 
 error: aborting due to 2 previous errors; 2 warnings emitted
 
diff --git a/src/test/ui/consts/const-eval/validate_uninhabited_zsts.rs b/src/test/ui/consts/const-eval/validate_uninhabited_zsts.rs
index 34650484556..96f33127582 100644
--- a/src/test/ui/consts/const-eval/validate_uninhabited_zsts.rs
+++ b/src/test/ui/consts/const-eval/validate_uninhabited_zsts.rs
@@ -6,16 +6,23 @@ const fn foo() -> ! {
     //~| WARN the type `!` does not permit zero-initialization [invalid_value]
 }
 
-#[derive(Clone, Copy)]
-enum Empty { }
+// Type defined in a submodule, so that it is not "visibly"
+// uninhabited (which would change interpreter behavior).
+pub mod empty {
+    #[derive(Clone, Copy)]
+    enum Void {}
+
+    #[derive(Clone, Copy)]
+    pub struct Empty(Void);
+}
 
 #[warn(const_err)]
-const FOO: [Empty; 3] = [foo(); 3];
+const FOO: [empty::Empty; 3] = [foo(); 3];
 
 #[warn(const_err)]
-const BAR: [Empty; 3] = [unsafe { std::mem::transmute(()) }; 3];
+const BAR: [empty::Empty; 3] = [unsafe { std::mem::transmute(()) }; 3];
 //~^ ERROR it is undefined behavior to use this value
-//~| WARN the type `Empty` does not permit zero-initialization
+//~| WARN the type `empty::Empty` does not permit zero-initialization
 
 fn main() {
     FOO;
diff --git a/src/test/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.mir.stderr b/src/test/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.mir.stderr
index 33014a1500c..4cd0fd2eaf7 100644
--- a/src/test/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.mir.stderr
+++ b/src/test/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.mir.stderr
@@ -7,7 +7,7 @@ LL |     let a: [u8; foo()];
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
 error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/const-extern-fn-requires-unsafe.rs:11:5
+  --> $DIR/const-extern-fn-requires-unsafe.rs:12:5
    |
 LL |     foo();
    |     ^^^^^ call to unsafe function
diff --git a/src/test/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.rs b/src/test/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.rs
index 031e67a1e3c..afe645ae881 100644
--- a/src/test/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.rs
+++ b/src/test/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.rs
@@ -7,7 +7,8 @@ const unsafe extern "C" fn foo() -> usize { 5 }
 
 fn main() {
     let a: [u8; foo()];
-    //~^ ERROR call to unsafe function is unsafe and requires unsafe function or block
+    //[mir]~^ call to unsafe function is unsafe and requires unsafe function or block
+    //[thir]~^^ call to unsafe function `foo` is unsafe and requires unsafe function or block
     foo();
     //[mir]~^ ERROR call to unsafe function is unsafe and requires unsafe function or block
 }
diff --git a/src/test/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.thir.stderr b/src/test/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.thir.stderr
index c6077da768b..b313f06539f 100644
--- a/src/test/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.thir.stderr
+++ b/src/test/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.thir.stderr
@@ -1,4 +1,4 @@
-error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+error[E0133]: call to unsafe function `foo` is unsafe and requires unsafe function or block
   --> $DIR/const-extern-fn-requires-unsafe.rs:9:17
    |
 LL |     let a: [u8; foo()];
diff --git a/src/test/ui/consts/const-float-bits-conv.rs b/src/test/ui/consts/const-float-bits-conv.rs
index c857ad6df2d..310db2174aa 100644
--- a/src/test/ui/consts/const-float-bits-conv.rs
+++ b/src/test/ui/consts/const-float-bits-conv.rs
@@ -37,22 +37,6 @@ fn f32() {
     const_assert!(f32::from_bits(0x44a72000), 1337.0);
     const_assert!(f32::from_ne_bytes(0x44a72000u32.to_ne_bytes()), 1337.0);
     const_assert!(f32::from_bits(0xc1640000), -14.25);
-
-    // Check that NaNs roundtrip their bits regardless of signalingness
-    // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits
-    const MASKED_NAN1: u32 = f32::NAN.to_bits() ^ 0x002A_AAAA;
-    const MASKED_NAN2: u32 = f32::NAN.to_bits() ^ 0x0055_5555;
-
-    const_assert!(f32::from_bits(MASKED_NAN1).is_nan());
-    const_assert!(f32::from_bits(MASKED_NAN1).is_nan());
-
-    // LLVM does not guarantee that loads and stores of NaNs preserve their exact bit pattern.
-    // In practice, this seems to only cause a problem on x86, since the most widely used calling
-    // convention mandates that floating point values are returned on the x87 FPU stack. See #73328.
-    if !cfg!(target_arch = "x86") {
-        const_assert!(f32::from_bits(MASKED_NAN1).to_bits(), MASKED_NAN1);
-        const_assert!(f32::from_bits(MASKED_NAN2).to_bits(), MASKED_NAN2);
-    }
 }
 
 fn f64() {
@@ -70,20 +54,6 @@ fn f64() {
     const_assert!(f64::from_bits(0x4094e40000000000), 1337.0);
     const_assert!(f64::from_ne_bytes(0x4094e40000000000u64.to_ne_bytes()), 1337.0);
     const_assert!(f64::from_bits(0xc02c800000000000), -14.25);
-
-    // Check that NaNs roundtrip their bits regardless of signalingness
-    // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits
-    const MASKED_NAN1: u64 = f64::NAN.to_bits() ^ 0x000A_AAAA_AAAA_AAAA;
-    const MASKED_NAN2: u64 = f64::NAN.to_bits() ^ 0x0005_5555_5555_5555;
-
-    const_assert!(f64::from_bits(MASKED_NAN1).is_nan());
-    const_assert!(f64::from_bits(MASKED_NAN1).is_nan());
-
-    // See comment above.
-    if !cfg!(target_arch = "x86") {
-        const_assert!(f64::from_bits(MASKED_NAN1).to_bits(), MASKED_NAN1);
-        const_assert!(f64::from_bits(MASKED_NAN2).to_bits(), MASKED_NAN2);
-    }
 }
 
 fn main() {
diff --git a/src/test/ui/consts/const-float-bits-reject-conv.rs b/src/test/ui/consts/const-float-bits-reject-conv.rs
new file mode 100644
index 00000000000..122f5b97eee
--- /dev/null
+++ b/src/test/ui/consts/const-float-bits-reject-conv.rs
@@ -0,0 +1,62 @@
+// compile-flags: -Zmir-opt-level=0
+#![feature(const_float_bits_conv)]
+#![feature(const_float_classify)]
+
+// Don't promote
+const fn nop<T>(x: T) -> T { x }
+
+macro_rules! const_assert {
+    ($a:expr) => {
+        {
+            const _: () = assert!($a);
+            assert!(nop($a));
+        }
+    };
+    ($a:expr, $b:expr) => {
+        {
+            const _: () = assert!($a == $b);
+            assert_eq!(nop($a), nop($b));
+        }
+    };
+}
+
+fn f32() {
+    // Check that NaNs roundtrip their bits regardless of signalingness
+    // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits
+    // ...actually, let's just check that these break. :D
+    const MASKED_NAN1: u32 = f32::NAN.to_bits() ^ 0x002A_AAAA;
+    const MASKED_NAN2: u32 = f32::NAN.to_bits() ^ 0x0055_5555;
+
+    const_assert!(f32::from_bits(MASKED_NAN1).is_nan());
+    const_assert!(f32::from_bits(MASKED_NAN1).is_nan());
+
+    // LLVM does not guarantee that loads and stores of NaNs preserve their exact bit pattern.
+    // In practice, this seems to only cause a problem on x86, since the most widely used calling
+    // convention mandates that floating point values are returned on the x87 FPU stack. See #73328.
+    if !cfg!(target_arch = "x86") {
+        const_assert!(f32::from_bits(MASKED_NAN1).to_bits(), MASKED_NAN1);
+        const_assert!(f32::from_bits(MASKED_NAN2).to_bits(), MASKED_NAN2);
+    }
+}
+
+fn f64() {
+    // Check that NaNs roundtrip their bits regardless of signalingness
+    // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits
+    // ...actually, let's just check that these break. :D
+    const MASKED_NAN1: u64 = f64::NAN.to_bits() ^ 0x000A_AAAA_AAAA_AAAA;
+    const MASKED_NAN2: u64 = f64::NAN.to_bits() ^ 0x0005_5555_5555_5555;
+
+    const_assert!(f64::from_bits(MASKED_NAN1).is_nan());
+    const_assert!(f64::from_bits(MASKED_NAN1).is_nan());
+
+    // See comment above.
+    if !cfg!(target_arch = "x86") {
+        const_assert!(f64::from_bits(MASKED_NAN1).to_bits(), MASKED_NAN1);
+        const_assert!(f64::from_bits(MASKED_NAN2).to_bits(), MASKED_NAN2);
+    }
+}
+
+fn main() {
+    f32();
+    f64();
+}
diff --git a/src/test/ui/consts/const-float-bits-reject-conv.stderr b/src/test/ui/consts/const-float-bits-reject-conv.stderr
new file mode 100644
index 00000000000..b39e8819701
--- /dev/null
+++ b/src/test/ui/consts/const-float-bits-reject-conv.stderr
@@ -0,0 +1,119 @@
+error[E0080]: evaluation of constant value failed
+  --> $SRC_DIR/core/src/num/f32.rs:LL:COL
+   |
+LL |                     panic!("const-eval error: cannot use f32::to_bits on a NaN")
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                     |
+   |                     the evaluated program panicked at 'const-eval error: cannot use f32::to_bits on a NaN', $SRC_DIR/core/src/num/f32.rs:LL:COL
+   |                     inside `core::f32::<impl f32>::to_bits::ct_f32_to_u32` at $SRC_DIR/core/src/panic.rs:LL:COL
+...
+LL |         unsafe { intrinsics::const_eval_select((self,), ct_f32_to_u32, rt_f32_to_u32) }
+   |                  -------------------------------------------------------------------- inside `core::f32::<impl f32>::to_bits` at $SRC_DIR/core/src/num/f32.rs:LL:COL
+   |
+  ::: $SRC_DIR/core/src/ops/function.rs:LL:COL
+   |
+LL |     extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
+   |     ------------------------------------------------------------------ inside `<fn(f32) -> u32 {core::f32::<impl f32>::to_bits::ct_f32_to_u32} as FnOnce<(f32,)>>::call_once - shim(fn(f32) -> u32 {core::f32::<impl f32>::to_bits::ct_f32_to_u32})` at $SRC_DIR/core/src/ops/function.rs:LL:COL
+   |
+  ::: $SRC_DIR/core/src/intrinsics.rs:LL:COL
+   |
+LL |     called_in_const.call_once(arg)
+   |     ------------------------------ inside `const_eval_select::<(f32,), fn(f32) -> u32 {core::f32::<impl f32>::to_bits::ct_f32_to_u32}, [closure@core::f32::<impl f32>::to_bits::{closure#0}], u32>` at $SRC_DIR/core/src/intrinsics.rs:LL:COL
+   |
+  ::: $DIR/const-float-bits-reject-conv.rs:27:30
+   |
+LL |     const MASKED_NAN1: u32 = f32::NAN.to_bits() ^ 0x002A_AAAA;
+   |                              ------------------ inside `f32::MASKED_NAN1` at $DIR/const-float-bits-reject-conv.rs:27:30
+   |
+   = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0080]: evaluation of constant value failed
+  --> $SRC_DIR/core/src/num/f32.rs:LL:COL
+   |
+LL |                     panic!("const-eval error: cannot use f32::to_bits on a NaN")
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                     |
+   |                     the evaluated program panicked at 'const-eval error: cannot use f32::to_bits on a NaN', $SRC_DIR/core/src/num/f32.rs:LL:COL
+   |                     inside `core::f32::<impl f32>::to_bits::ct_f32_to_u32` at $SRC_DIR/core/src/panic.rs:LL:COL
+...
+LL |         unsafe { intrinsics::const_eval_select((self,), ct_f32_to_u32, rt_f32_to_u32) }
+   |                  -------------------------------------------------------------------- inside `core::f32::<impl f32>::to_bits` at $SRC_DIR/core/src/num/f32.rs:LL:COL
+   |
+  ::: $SRC_DIR/core/src/ops/function.rs:LL:COL
+   |
+LL |     extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
+   |     ------------------------------------------------------------------ inside `<fn(f32) -> u32 {core::f32::<impl f32>::to_bits::ct_f32_to_u32} as FnOnce<(f32,)>>::call_once - shim(fn(f32) -> u32 {core::f32::<impl f32>::to_bits::ct_f32_to_u32})` at $SRC_DIR/core/src/ops/function.rs:LL:COL
+   |
+  ::: $SRC_DIR/core/src/intrinsics.rs:LL:COL
+   |
+LL |     called_in_const.call_once(arg)
+   |     ------------------------------ inside `const_eval_select::<(f32,), fn(f32) -> u32 {core::f32::<impl f32>::to_bits::ct_f32_to_u32}, [closure@core::f32::<impl f32>::to_bits::{closure#0}], u32>` at $SRC_DIR/core/src/intrinsics.rs:LL:COL
+   |
+  ::: $DIR/const-float-bits-reject-conv.rs:28:30
+   |
+LL |     const MASKED_NAN2: u32 = f32::NAN.to_bits() ^ 0x0055_5555;
+   |                              ------------------ inside `f32::MASKED_NAN2` at $DIR/const-float-bits-reject-conv.rs:28:30
+   |
+   = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0080]: evaluation of constant value failed
+  --> $SRC_DIR/core/src/num/f64.rs:LL:COL
+   |
+LL |                     panic!("const-eval error: cannot use f64::to_bits on a NaN")
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                     |
+   |                     the evaluated program panicked at 'const-eval error: cannot use f64::to_bits on a NaN', $SRC_DIR/core/src/num/f64.rs:LL:COL
+   |                     inside `core::f64::<impl f64>::to_bits::ct_f64_to_u64` at $SRC_DIR/core/src/panic.rs:LL:COL
+...
+LL |         unsafe { intrinsics::const_eval_select((self,), ct_f64_to_u64, rt_f64_to_u64) }
+   |                  -------------------------------------------------------------------- inside `core::f64::<impl f64>::to_bits` at $SRC_DIR/core/src/num/f64.rs:LL:COL
+   |
+  ::: $SRC_DIR/core/src/ops/function.rs:LL:COL
+   |
+LL |     extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
+   |     ------------------------------------------------------------------ inside `<fn(f64) -> u64 {core::f64::<impl f64>::to_bits::ct_f64_to_u64} as FnOnce<(f64,)>>::call_once - shim(fn(f64) -> u64 {core::f64::<impl f64>::to_bits::ct_f64_to_u64})` at $SRC_DIR/core/src/ops/function.rs:LL:COL
+   |
+  ::: $SRC_DIR/core/src/intrinsics.rs:LL:COL
+   |
+LL |     called_in_const.call_once(arg)
+   |     ------------------------------ inside `const_eval_select::<(f64,), fn(f64) -> u64 {core::f64::<impl f64>::to_bits::ct_f64_to_u64}, [closure@core::f64::<impl f64>::to_bits::{closure#0}], u64>` at $SRC_DIR/core/src/intrinsics.rs:LL:COL
+   |
+  ::: $DIR/const-float-bits-reject-conv.rs:46:30
+   |
+LL |     const MASKED_NAN1: u64 = f64::NAN.to_bits() ^ 0x000A_AAAA_AAAA_AAAA;
+   |                              ------------------ inside `f64::MASKED_NAN1` at $DIR/const-float-bits-reject-conv.rs:46:30
+   |
+   = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0080]: evaluation of constant value failed
+  --> $SRC_DIR/core/src/num/f64.rs:LL:COL
+   |
+LL |                     panic!("const-eval error: cannot use f64::to_bits on a NaN")
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                     |
+   |                     the evaluated program panicked at 'const-eval error: cannot use f64::to_bits on a NaN', $SRC_DIR/core/src/num/f64.rs:LL:COL
+   |                     inside `core::f64::<impl f64>::to_bits::ct_f64_to_u64` at $SRC_DIR/core/src/panic.rs:LL:COL
+...
+LL |         unsafe { intrinsics::const_eval_select((self,), ct_f64_to_u64, rt_f64_to_u64) }
+   |                  -------------------------------------------------------------------- inside `core::f64::<impl f64>::to_bits` at $SRC_DIR/core/src/num/f64.rs:LL:COL
+   |
+  ::: $SRC_DIR/core/src/ops/function.rs:LL:COL
+   |
+LL |     extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
+   |     ------------------------------------------------------------------ inside `<fn(f64) -> u64 {core::f64::<impl f64>::to_bits::ct_f64_to_u64} as FnOnce<(f64,)>>::call_once - shim(fn(f64) -> u64 {core::f64::<impl f64>::to_bits::ct_f64_to_u64})` at $SRC_DIR/core/src/ops/function.rs:LL:COL
+   |
+  ::: $SRC_DIR/core/src/intrinsics.rs:LL:COL
+   |
+LL |     called_in_const.call_once(arg)
+   |     ------------------------------ inside `const_eval_select::<(f64,), fn(f64) -> u64 {core::f64::<impl f64>::to_bits::ct_f64_to_u64}, [closure@core::f64::<impl f64>::to_bits::{closure#0}], u64>` at $SRC_DIR/core/src/intrinsics.rs:LL:COL
+   |
+  ::: $DIR/const-float-bits-reject-conv.rs:47:30
+   |
+LL |     const MASKED_NAN2: u64 = f64::NAN.to_bits() ^ 0x0005_5555_5555_5555;
+   |                              ------------------ inside `f64::MASKED_NAN2` at $DIR/const-float-bits-reject-conv.rs:47:30
+   |
+   = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/consts/const-fn-error.stderr b/src/test/ui/consts/const-fn-error.stderr
index 4d53cfc35e1..e36324f0b3e 100644
--- a/src/test/ui/consts/const-fn-error.stderr
+++ b/src/test/ui/consts/const-fn-error.stderr
@@ -22,8 +22,8 @@ LL |     for i in 0..x {
 note: impl defined here, but it is not `const`
   --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
    |
-LL | impl<I: Iterator> IntoIterator for I {
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | impl<I: ~const Iterator> const IntoIterator for I {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
 
 error[E0658]: mutable references are not allowed in constant functions
diff --git a/src/test/ui/consts/const-fn-in-vec.stderr b/src/test/ui/consts/const-fn-in-vec.stderr
index f02cb4f1ff1..9eb7524b504 100644
--- a/src/test/ui/consts/const-fn-in-vec.stderr
+++ b/src/test/ui/consts/const-fn-in-vec.stderr
@@ -1,10 +1,10 @@
 error[E0277]: the trait bound `String: Copy` is not satisfied
-  --> $DIR/const-fn-in-vec.rs:4:32
+  --> $DIR/const-fn-in-vec.rs:4:33
    |
 LL |     let strings: [String; 5] = [String::new(); 5];
-   |                                ^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String`
+   |                                 ^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String`
    |
-   = note: the `Copy` trait is required because the repeated element will be copied
+   = note: the `Copy` trait is required because this value will be copied for each element of the array
    = help: consider creating a new `const` item and initializing it with the result of the function call to be used in the repeat position, like `const VAL: Type = const_fn();` and `let x = [VAL; 42];`
    = help: create an inline `const` block, see RFC #2920 <https://github.com/rust-lang/rfcs/pull/2920> for more information
 
diff --git a/src/test/ui/consts/const-for.stderr b/src/test/ui/consts/const-for.stderr
index b0dc43eb8e8..f2e1c8a4991 100644
--- a/src/test/ui/consts/const-for.stderr
+++ b/src/test/ui/consts/const-for.stderr
@@ -7,8 +7,8 @@ LL |     for _ in 0..5 {}
 note: impl defined here, but it is not `const`
   --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
    |
-LL | impl<I: Iterator> IntoIterator for I {
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | impl<I: ~const Iterator> const IntoIterator for I {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
 
 error[E0015]: cannot call non-const fn `<std::ops::Range<i32> as Iterator>::next` in constants
diff --git a/src/test/ui/issues/issue-19244.rs b/src/test/ui/consts/issue-19244.rs
index 44d9748fd2a..44d9748fd2a 100644
--- a/src/test/ui/issues/issue-19244.rs
+++ b/src/test/ui/consts/issue-19244.rs
diff --git a/src/test/ui/did_you_mean/replace-impl-infer-ty-from-trait.fixed b/src/test/ui/did_you_mean/replace-impl-infer-ty-from-trait.fixed
new file mode 100644
index 00000000000..4963790c35d
--- /dev/null
+++ b/src/test/ui/did_you_mean/replace-impl-infer-ty-from-trait.fixed
@@ -0,0 +1,15 @@
+// run-rustfix
+#![allow(unused)]
+
+trait Foo<T>: Sized {
+    fn bar(i: i32, t: T, s: &Self) -> (T, i32);
+}
+
+impl Foo<usize> for () {
+    fn bar(i: i32, t: usize, s: &()) -> (usize, i32) {
+        //~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions
+        (1, 2)
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/did_you_mean/replace-impl-infer-ty-from-trait.rs b/src/test/ui/did_you_mean/replace-impl-infer-ty-from-trait.rs
new file mode 100644
index 00000000000..ddf39c9c861
--- /dev/null
+++ b/src/test/ui/did_you_mean/replace-impl-infer-ty-from-trait.rs
@@ -0,0 +1,15 @@
+// run-rustfix
+#![allow(unused)]
+
+trait Foo<T>: Sized {
+    fn bar(i: i32, t: T, s: &Self) -> (T, i32);
+}
+
+impl Foo<usize> for () {
+    fn bar(i: _, t: _, s: _) -> _ {
+        //~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions
+        (1, 2)
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/did_you_mean/replace-impl-infer-ty-from-trait.stderr b/src/test/ui/did_you_mean/replace-impl-infer-ty-from-trait.stderr
new file mode 100644
index 00000000000..730836a40c2
--- /dev/null
+++ b/src/test/ui/did_you_mean/replace-impl-infer-ty-from-trait.stderr
@@ -0,0 +1,18 @@
+error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions
+  --> $DIR/replace-impl-infer-ty-from-trait.rs:9:15
+   |
+LL |     fn bar(i: _, t: _, s: _) -> _ {
+   |               ^     ^     ^     ^ not allowed in type signatures
+   |               |     |     |
+   |               |     |     not allowed in type signatures
+   |               |     not allowed in type signatures
+   |               not allowed in type signatures
+   |
+help: try replacing `_` with the types in the corresponding trait method signature
+   |
+LL |     fn bar(i: i32, t: usize, s: &()) -> (usize, i32) {
+   |               ~~~     ~~~~~     ~~~     ~~~~~~~~~~~~
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0121`.
diff --git a/src/test/ui/error-codes/E0117.stderr b/src/test/ui/error-codes/E0117.stderr
index cdbafff2a20..76d9f5cc0e5 100644
--- a/src/test/ui/error-codes/E0117.stderr
+++ b/src/test/ui/error-codes/E0117.stderr
@@ -1,4 +1,4 @@
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for primitive types
   --> $DIR/E0117.rs:1:1
    |
 LL | impl Drop for u32 {}
diff --git a/src/test/ui/error-codes/E0133.thir.stderr b/src/test/ui/error-codes/E0133.thir.stderr
index b11d5e2c2fc..f1d7aba2aa3 100644
--- a/src/test/ui/error-codes/E0133.thir.stderr
+++ b/src/test/ui/error-codes/E0133.thir.stderr
@@ -1,4 +1,4 @@
-error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+error[E0133]: call to unsafe function `f` is unsafe and requires unsafe function or block
   --> $DIR/E0133.rs:7:5
    |
 LL |     f();
diff --git a/src/test/ui/error-codes/E0283.stderr b/src/test/ui/error-codes/E0283.stderr
index 7dcfe96b35c..e2bab486064 100644
--- a/src/test/ui/error-codes/E0283.stderr
+++ b/src/test/ui/error-codes/E0283.stderr
@@ -14,7 +14,6 @@ LL |     let bar = foo_impl.into() * 1u32;
    |               |        |
    |               |        cannot infer type for type parameter `T` declared on the trait `Into`
    |               this method call resolves to `T`
-   |               help: use the fully qualified path for the potential candidate: `<Impl as Into<u32>>::into(foo_impl)`
    |
 note: multiple `impl`s satisfying `Impl: Into<_>` found
   --> $DIR/E0283.rs:17:1
@@ -24,6 +23,10 @@ LL | impl Into<u32> for Impl {
    = note: and another `impl` found in the `core` crate:
            - impl<T, U> Into<U> for T
              where U: From<T>;
+help: use the fully qualified path for the potential candidate
+   |
+LL |     let bar = <Impl as Into<u32>>::into(foo_impl) * 1u32;
+   |               ++++++++++++++++++++++++++        ~
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/error-codes/E0771.stderr b/src/test/ui/error-codes/E0771.stderr
index 5a16d5845a6..3ab727f5f69 100644
--- a/src/test/ui/error-codes/E0771.stderr
+++ b/src/test/ui/error-codes/E0771.stderr
@@ -1,3 +1,11 @@
+error[E0771]: use of non-static lifetime `'a` in const generic
+  --> $DIR/E0771.rs:4:41
+   |
+LL | fn function_with_str<'a, const STRING: &'a str>() {}
+   |                                         ^^
+   |
+   = note: for more information, see issue #74052 <https://github.com/rust-lang/rust/issues/74052>
+
 warning: the feature `adt_const_params` is incomplete and may not be safe to use and/or cause compiler crashes
   --> $DIR/E0771.rs:1:12
    |
@@ -7,14 +15,6 @@ LL | #![feature(adt_const_params)]
    = note: `#[warn(incomplete_features)]` on by default
    = note: see issue #95174 <https://github.com/rust-lang/rust/issues/95174> for more information
 
-error[E0771]: use of non-static lifetime `'a` in const generic
-  --> $DIR/E0771.rs:4:41
-   |
-LL | fn function_with_str<'a, const STRING: &'a str>() {}
-   |                                         ^^
-   |
-   = note: for more information, see issue #74052 <https://github.com/rust-lang/rust/issues/74052>
-
 error: aborting due to previous error; 1 warning emitted
 
 For more information about this error, try `rustc --explain E0771`.
diff --git a/src/test/ui/extern-flag/no-nounused.rs b/src/test/ui/extern-flag/no-nounused.rs
new file mode 100644
index 00000000000..5ec75595243
--- /dev/null
+++ b/src/test/ui/extern-flag/no-nounused.rs
@@ -0,0 +1,6 @@
+// aux-crate:somedep=somedep.rs
+// compile-flags: -Zunstable-options -Dunused-crate-dependencies
+// edition:2018
+
+fn main() { //~ ERROR external crate `somedep` unused in `no_nounused`
+}
diff --git a/src/test/ui/extern-flag/no-nounused.stderr b/src/test/ui/extern-flag/no-nounused.stderr
new file mode 100644
index 00000000000..6446c53236c
--- /dev/null
+++ b/src/test/ui/extern-flag/no-nounused.stderr
@@ -0,0 +1,10 @@
+error: external crate `somedep` unused in `no_nounused`: remove the dependency or add `use somedep as _;`
+  --> $DIR/no-nounused.rs:5:1
+   |
+LL | fn main() {
+   | ^
+   |
+   = note: requested on the command line with `-D unused-crate-dependencies`
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/extern-flag/nounused.rs b/src/test/ui/extern-flag/nounused.rs
new file mode 100644
index 00000000000..2513986bbec
--- /dev/null
+++ b/src/test/ui/extern-flag/nounused.rs
@@ -0,0 +1,7 @@
+// check-pass
+// aux-crate:nounused:somedep=somedep.rs
+// compile-flags: -Zunstable-options -Dunused-crate-dependencies
+// edition:2018
+
+fn main() {
+}
diff --git a/src/test/ui/extern/extern-type-diag-not-similar.rs b/src/test/ui/extern/extern-type-diag-not-similar.rs
new file mode 100644
index 00000000000..39d00a6c1bc
--- /dev/null
+++ b/src/test/ui/extern/extern-type-diag-not-similar.rs
@@ -0,0 +1,22 @@
+// We previously mentioned other extern types in the error message here.
+//
+// Two extern types shouldn't really be considered similar just
+// because they are both extern types.
+
+#![feature(extern_types)]
+extern {
+    type ShouldNotBeMentioned;
+}
+
+extern {
+    type Foo;
+}
+
+unsafe impl Send for ShouldNotBeMentioned {}
+
+fn assert_send<T: Send + ?Sized>() {}
+
+fn main() {
+    assert_send::<Foo>()
+    //~^ ERROR `Foo` cannot be sent between threads safely
+}
diff --git a/src/test/ui/extern/extern-type-diag-not-similar.stderr b/src/test/ui/extern/extern-type-diag-not-similar.stderr
new file mode 100644
index 00000000000..75836f7eca1
--- /dev/null
+++ b/src/test/ui/extern/extern-type-diag-not-similar.stderr
@@ -0,0 +1,16 @@
+error[E0277]: `Foo` cannot be sent between threads safely
+  --> $DIR/extern-type-diag-not-similar.rs:20:19
+   |
+LL |     assert_send::<Foo>()
+   |                   ^^^ `Foo` cannot be sent between threads safely
+   |
+   = help: the trait `Send` is not implemented for `Foo`
+note: required by a bound in `assert_send`
+  --> $DIR/extern-type-diag-not-similar.rs:17:19
+   |
+LL | fn assert_send<T: Send + ?Sized>() {}
+   |                   ^^^^ required by this bound in `assert_send`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/feature-gates/feature-gate-infer_static_outlives_requirements.stderr b/src/test/ui/feature-gates/feature-gate-infer_static_outlives_requirements.stderr
index 2bccec45894..7ffebab4153 100644
--- a/src/test/ui/feature-gates/feature-gate-infer_static_outlives_requirements.stderr
+++ b/src/test/ui/feature-gates/feature-gate-infer_static_outlives_requirements.stderr
@@ -1,8 +1,6 @@
 error[E0310]: the parameter type `U` may not live long enough
   --> $DIR/feature-gate-infer_static_outlives_requirements.rs:5:10
    |
-LL | struct Foo<U> {
-   |            - help: consider adding an explicit lifetime bound...: `U: 'static`
 LL |     bar: Bar<U>
    |          ^^^^^^ ...so that the type `U` will meet its required lifetime bounds...
    |
@@ -11,6 +9,10 @@ note: ...that is required by this bound
    |
 LL | struct Bar<T: 'static> {
    |               ^^^^^^^
+help: consider adding an explicit lifetime bound...
+   |
+LL | struct Foo<U: 'static> {
+   |             +++++++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/feature-gates/feature-gate-yeet_expr-in-cfg.rs b/src/test/ui/feature-gates/feature-gate-yeet_expr-in-cfg.rs
new file mode 100644
index 00000000000..a33bd34508c
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-yeet_expr-in-cfg.rs
@@ -0,0 +1,19 @@
+// compile-flags: --edition 2021
+
+pub fn demo() -> Option<i32> {
+    #[cfg(nope)]
+    {
+        do yeet //~ ERROR `do yeet` expression is experimental
+    }
+
+    Some(1)
+}
+
+#[cfg(nope)]
+pub fn alternative() -> Result<(), String> {
+    do yeet "hello"; //~ ERROR `do yeet` expression is experimental
+}
+
+fn main() {
+    demo();
+}
diff --git a/src/test/ui/feature-gates/feature-gate-yeet_expr-in-cfg.stderr b/src/test/ui/feature-gates/feature-gate-yeet_expr-in-cfg.stderr
new file mode 100644
index 00000000000..f90c379bdaf
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-yeet_expr-in-cfg.stderr
@@ -0,0 +1,21 @@
+error[E0658]: `do yeet` expression is experimental
+  --> $DIR/feature-gate-yeet_expr-in-cfg.rs:6:9
+   |
+LL |         do yeet
+   |         ^^^^^^^
+   |
+   = note: see issue #96373 <https://github.com/rust-lang/rust/issues/96373> for more information
+   = help: add `#![feature(yeet_expr)]` to the crate attributes to enable
+
+error[E0658]: `do yeet` expression is experimental
+  --> $DIR/feature-gate-yeet_expr-in-cfg.rs:14:5
+   |
+LL |     do yeet "hello";
+   |     ^^^^^^^^^^^^^^^
+   |
+   = note: see issue #96373 <https://github.com/rust-lang/rust/issues/96373> for more information
+   = help: add `#![feature(yeet_expr)]` to the crate attributes to enable
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/feature-gates/feature-gate-yeet_expr.rs b/src/test/ui/feature-gates/feature-gate-yeet_expr.rs
new file mode 100644
index 00000000000..978a84cf6e5
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-yeet_expr.rs
@@ -0,0 +1,9 @@
+// compile-flags: --edition 2018
+
+pub fn demo() -> Option<i32> {
+    do yeet //~ ERROR `do yeet` expression is experimental
+}
+
+pub fn main() -> Result<(), String> {
+    do yeet "hello"; //~ ERROR `do yeet` expression is experimental
+}
diff --git a/src/test/ui/feature-gates/feature-gate-yeet_expr.stderr b/src/test/ui/feature-gates/feature-gate-yeet_expr.stderr
new file mode 100644
index 00000000000..8d1b92370fb
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-yeet_expr.stderr
@@ -0,0 +1,21 @@
+error[E0658]: `do yeet` expression is experimental
+  --> $DIR/feature-gate-yeet_expr.rs:4:5
+   |
+LL |     do yeet
+   |     ^^^^^^^
+   |
+   = note: see issue #96373 <https://github.com/rust-lang/rust/issues/96373> for more information
+   = help: add `#![feature(yeet_expr)]` to the crate attributes to enable
+
+error[E0658]: `do yeet` expression is experimental
+  --> $DIR/feature-gate-yeet_expr.rs:8:5
+   |
+LL |     do yeet "hello";
+   |     ^^^^^^^^^^^^^^^
+   |
+   = note: see issue #96373 <https://github.com/rust-lang/rust/issues/96373> for more information
+   = help: add `#![feature(yeet_expr)]` to the crate attributes to enable
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/foreign-unsafe-fn-called.rs b/src/test/ui/foreign-unsafe-fn-called.rs
index de3de286fc9..67302ea1bcd 100644
--- a/src/test/ui/foreign-unsafe-fn-called.rs
+++ b/src/test/ui/foreign-unsafe-fn-called.rs
@@ -9,5 +9,6 @@ mod test {
 
 fn main() {
     test::free();
-    //~^ ERROR call to unsafe function is unsafe
+    //[mir]~^ ERROR call to unsafe function is unsafe
+    //[thir]~^^ ERROR call to unsafe function `test::free` is unsafe
 }
diff --git a/src/test/ui/foreign-unsafe-fn-called.thir.stderr b/src/test/ui/foreign-unsafe-fn-called.thir.stderr
index d3cf5d84fdd..00ba0f7a6a3 100644
--- a/src/test/ui/foreign-unsafe-fn-called.thir.stderr
+++ b/src/test/ui/foreign-unsafe-fn-called.thir.stderr
@@ -1,4 +1,4 @@
-error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+error[E0133]: call to unsafe function `test::free` is unsafe and requires unsafe function or block
   --> $DIR/foreign-unsafe-fn-called.rs:11:5
    |
 LL |     test::free();
diff --git a/src/test/ui/functions-closures/fn-help-with-err.rs b/src/test/ui/functions-closures/fn-help-with-err.rs
index f8a81af786f..3d2bcb8ad35 100644
--- a/src/test/ui/functions-closures/fn-help-with-err.rs
+++ b/src/test/ui/functions-closures/fn-help-with-err.rs
@@ -3,14 +3,14 @@ fn main() {
     let arc = std::sync::Arc::new(oops);
     //~^ ERROR cannot find value `oops` in this scope
     //~| NOTE not found
-    // The error "note: `arc` is a function, perhaps you wish to call it" MUST NOT appear.
+    // The error "note: this is a function, perhaps you wish to call it" MUST NOT appear.
     arc.blablabla();
     //~^ ERROR no method named `blablabla`
     //~| NOTE method not found
     let arc2 = std::sync::Arc::new(|| 1);
-    // The error "note: `arc2` is a function, perhaps you wish to call it" SHOULD appear
+    // The error "note: this is a function, perhaps you wish to call it" SHOULD appear
     arc2.blablabla();
     //~^ ERROR no method named `blablabla`
     //~| NOTE method not found
-    //~| NOTE `arc2` is a function, perhaps you wish to call it
+    //~| NOTE this is a function, perhaps you wish to call it
 }
diff --git a/src/test/ui/functions-closures/fn-help-with-err.stderr b/src/test/ui/functions-closures/fn-help-with-err.stderr
index 4d6b3282ad9..3e42cb1fb6e 100644
--- a/src/test/ui/functions-closures/fn-help-with-err.stderr
+++ b/src/test/ui/functions-closures/fn-help-with-err.stderr
@@ -14,9 +14,9 @@ error[E0599]: no method named `blablabla` found for struct `Arc<[closure@$DIR/fn
   --> $DIR/fn-help-with-err.rs:12:10
    |
 LL |     arc2.blablabla();
-   |          ^^^^^^^^^ method not found in `Arc<[closure@$DIR/fn-help-with-err.rs:10:36: 10:40]>`
-   |
-   = note: `arc2` is a function, perhaps you wish to call it
+   |     ---- ^^^^^^^^^ method not found in `Arc<[closure@$DIR/fn-help-with-err.rs:10:36: 10:40]>`
+   |     |
+   |     this is a function, perhaps you wish to call it
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/generator/issue-88653.rs b/src/test/ui/generator/issue-88653.rs
index f27956e4595..caa452060b1 100644
--- a/src/test/ui/generator/issue-88653.rs
+++ b/src/test/ui/generator/issue-88653.rs
@@ -7,13 +7,10 @@ use std::ops::Generator;
 
 fn foo(bar: bool) -> impl Generator<(bool,)> {
     //~^ ERROR: type mismatch in generator arguments [E0631]
-    //~| ERROR: type mismatch in generator arguments [E0631]
-    //~| NOTE: expected signature of `fn((bool,)) -> _`
     //~| NOTE: expected signature of `fn((bool,)) -> _`
     //~| NOTE: in this expansion of desugaring of `impl Trait`
     |bar| {
         //~^ NOTE: found signature of `fn(bool) -> _`
-        //~| NOTE: found signature of `fn(bool) -> _`
         if bar {
             yield bar;
         }
diff --git a/src/test/ui/generator/issue-88653.stderr b/src/test/ui/generator/issue-88653.stderr
index 25357411ce1..5bd8ad129fe 100644
--- a/src/test/ui/generator/issue-88653.stderr
+++ b/src/test/ui/generator/issue-88653.stderr
@@ -7,22 +7,6 @@ LL | fn foo(bar: bool) -> impl Generator<(bool,)> {
 LL |     |bar| {
    |     ----- found signature of `fn(bool) -> _`
 
-error[E0631]: type mismatch in generator arguments
-  --> $DIR/issue-88653.rs:8:46
-   |
-LL |   fn foo(bar: bool) -> impl Generator<(bool,)> {
-   |  ______________________________________________^
-LL | |
-LL | |
-LL | |
-...  |
-LL | |     |bar| {
-   | |     ----- found signature of `fn(bool) -> _`
-...  |
-LL | |     }
-LL | | }
-   | |_^ expected signature of `fn((bool,)) -> _`
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0631`.
diff --git a/src/test/ui/generator/issue-93161.rs b/src/test/ui/generator/issue-93161.rs
index 9988acbcb73..92305609c83 100644
--- a/src/test/ui/generator/issue-93161.rs
+++ b/src/test/ui/generator/issue-93161.rs
@@ -1,5 +1,6 @@
 // edition:2021
 // run-pass
+// compile-flags: -Zdrop-tracking
 
 #![feature(never_type)]
 
@@ -32,7 +33,7 @@ fn never() -> Never {
 }
 
 async fn includes_never(crash: bool, x: u32) -> u32 {
-    let mut result = async { x * x }.await;
+    let result = async { x * x }.await;
     if !crash {
         return result;
     }
diff --git a/src/test/ui/generator/type-mismatch-signature-deduction.rs b/src/test/ui/generator/type-mismatch-signature-deduction.rs
index 77b830783c3..8d1ce6c7a43 100644
--- a/src/test/ui/generator/type-mismatch-signature-deduction.rs
+++ b/src/test/ui/generator/type-mismatch-signature-deduction.rs
@@ -4,7 +4,6 @@ use std::ops::Generator;
 
 fn foo() -> impl Generator<Return = i32> {
     //~^ ERROR type mismatch
-    //~| ERROR type mismatch
     || {
         if false {
             return Ok(6);
diff --git a/src/test/ui/generator/type-mismatch-signature-deduction.stderr b/src/test/ui/generator/type-mismatch-signature-deduction.stderr
index 6369e7ec4c7..f05faedf21b 100644
--- a/src/test/ui/generator/type-mismatch-signature-deduction.stderr
+++ b/src/test/ui/generator/type-mismatch-signature-deduction.stderr
@@ -1,5 +1,5 @@
 error[E0308]: mismatched types
-  --> $DIR/type-mismatch-signature-deduction.rs:15:9
+  --> $DIR/type-mismatch-signature-deduction.rs:14:9
    |
 LL |         5
    |         ^ expected enum `Result`, found integer
@@ -7,12 +7,12 @@ LL |         5
    = note: expected type `Result<{integer}, _>`
               found type `{integer}`
 note: return type inferred to be `Result<{integer}, _>` here
-  --> $DIR/type-mismatch-signature-deduction.rs:10:20
+  --> $DIR/type-mismatch-signature-deduction.rs:9:20
    |
 LL |             return Ok(6);
    |                    ^^^^^
 
-error[E0271]: type mismatch resolving `<[generator@$DIR/type-mismatch-signature-deduction.rs:8:5: 16:6] as Generator>::Return == i32`
+error[E0271]: type mismatch resolving `<[generator@$DIR/type-mismatch-signature-deduction.rs:7:5: 15:6] as Generator>::Return == i32`
   --> $DIR/type-mismatch-signature-deduction.rs:5:13
    |
 LL | fn foo() -> impl Generator<Return = i32> {
@@ -21,23 +21,7 @@ LL | fn foo() -> impl Generator<Return = i32> {
    = note: expected enum `Result<{integer}, _>`
               found type `i32`
 
-error[E0271]: type mismatch resolving `<[generator@$DIR/type-mismatch-signature-deduction.rs:8:5: 16:6] as Generator>::Return == i32`
-  --> $DIR/type-mismatch-signature-deduction.rs:5:42
-   |
-LL |   fn foo() -> impl Generator<Return = i32> {
-   |  __________________________________________^
-LL | |
-LL | |
-LL | |     || {
-...  |
-LL | |     }
-LL | | }
-   | |_^ expected enum `Result`, found `i32`
-   |
-   = note: expected enum `Result<{integer}, _>`
-              found type `i32`
-
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
 
 Some errors have detailed explanations: E0271, E0308.
 For more information about an error, try `rustc --explain E0271`.
diff --git a/src/test/ui/generic-associated-types/issue-70304.rs b/src/test/ui/generic-associated-types/issue-70304.rs
index 448d7ec2873..1c3d166a1af 100644
--- a/src/test/ui/generic-associated-types/issue-70304.rs
+++ b/src/test/ui/generic-associated-types/issue-70304.rs
@@ -46,6 +46,7 @@ where
 
 fn create_doc() -> impl Document<Cursor<'_> = DocCursorImpl<'_>> {
     //~^ ERROR: missing lifetime specifier
+    //~| ERROR: missing lifetime specifier
     DocumentImpl {}
 }
 
diff --git a/src/test/ui/generic-associated-types/issue-70304.stderr b/src/test/ui/generic-associated-types/issue-70304.stderr
index c5f59a24057..08efc82c886 100644
--- a/src/test/ui/generic-associated-types/issue-70304.stderr
+++ b/src/test/ui/generic-associated-types/issue-70304.stderr
@@ -10,6 +10,18 @@ help: consider using the `'static` lifetime
 LL | fn create_doc() -> impl Document<Cursor<'static> = DocCursorImpl<'_>> {
    |                                         ~~~~~~~
 
-error: aborting due to previous error
+error[E0106]: missing lifetime specifier
+  --> $DIR/issue-70304.rs:47:61
+   |
+LL | fn create_doc() -> impl Document<Cursor<'_> = DocCursorImpl<'_>> {
+   |                                                             ^^ expected named lifetime parameter
+   |
+   = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
+help: consider using the `'static` lifetime
+   |
+LL | fn create_doc() -> impl Document<Cursor<'_> = DocCursorImpl<'static>> {
+   |                                                             ~~~~~~~
+
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0106`.
diff --git a/src/test/ui/generic-associated-types/issue-86483.stderr b/src/test/ui/generic-associated-types/issue-86483.stderr
index 2f29cd5d9e9..a13dc043dc5 100644
--- a/src/test/ui/generic-associated-types/issue-86483.stderr
+++ b/src/test/ui/generic-associated-types/issue-86483.stderr
@@ -1,10 +1,7 @@
 error[E0311]: the parameter type `T` may not live long enough
   --> $DIR/issue-86483.rs:5:1
    |
-LL |   pub trait IceIce<T>
-   |   ^                - help: consider adding an explicit lifetime bound...: `T: 'a`
-   |  _|
-   | |
+LL | / pub trait IceIce<T>
 LL | | where
 LL | |     for<'a> T: 'a,
 LL | | {
@@ -19,13 +16,14 @@ note: ...that is required by this bound
    |
 LL |     for<'a> T: 'a,
    |                ^^
+help: consider adding an explicit lifetime bound...
+   |
+LL |     for<'a> T: 'a + 'a,
+   |                   ++++
 
 error[E0311]: the parameter type `T` may not live long enough
   --> $DIR/issue-86483.rs:9:5
    |
-LL | pub trait IceIce<T>
-   |                  - help: consider adding an explicit lifetime bound...: `T: 'a`
-...
 LL |     type Ice<'v>: IntoIterator<Item = &'v T>;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds...
    |
@@ -34,6 +32,10 @@ note: ...that is required by this bound
    |
 LL |     for<'a> T: 'a,
    |                ^^
+help: consider adding an explicit lifetime bound...
+   |
+LL |     for<'a> T: 'a + 'a,
+   |                   ++++
 
 error[E0309]: the parameter type `T` may not live long enough
   --> $DIR/issue-86483.rs:9:32
diff --git a/src/test/ui/generic-associated-types/issue-91139.migrate.stderr b/src/test/ui/generic-associated-types/issue-91139.migrate.stderr
index a27d8110238..b424d9a2fdb 100644
--- a/src/test/ui/generic-associated-types/issue-91139.migrate.stderr
+++ b/src/test/ui/generic-associated-types/issue-91139.migrate.stderr
@@ -1,10 +1,13 @@
 error[E0311]: the parameter type `T` may not live long enough
   --> $DIR/issue-91139.rs:27:12
    |
-LL | fn foo<T>() {
-   |        - help: consider adding an explicit lifetime bound...: `T: 'a`
 LL |     let _: for<'a> fn(<() as Foo<T>>::Type<'a>, &'a T) = |_, _| ();
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn foo<T: 'a>() {
+   |         ++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/generic-associated-types/issue-92096.migrate.stderr b/src/test/ui/generic-associated-types/issue-92096.migrate.stderr
index 72ade5774d7..c74161cd3e0 100644
--- a/src/test/ui/generic-associated-types/issue-92096.migrate.stderr
+++ b/src/test/ui/generic-associated-types/issue-92096.migrate.stderr
@@ -2,17 +2,23 @@ error[E0311]: the parameter type `C` may not live long enough
   --> $DIR/issue-92096.rs:20:33
    |
 LL | fn call_connect<C>(c: &'_ C) -> impl '_ + Future + Send
-   |                 -               ^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `C` will meet its required lifetime bounds
-   |                 |
-   |                 help: consider adding an explicit lifetime bound...: `C: 'a`
+   |                                 ^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `C` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL |     C: Client + Send + Sync + 'a,
+   |                             ++++
 
 error[E0311]: the parameter type `C` may not live long enough
   --> $DIR/issue-92096.rs:20:33
    |
 LL | fn call_connect<C>(c: &'_ C) -> impl '_ + Future + Send
-   |                 -               ^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `C` will meet its required lifetime bounds
-   |                 |
-   |                 help: consider adding an explicit lifetime bound...: `C: 'a`
+   |                                 ^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `C` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL |     C: Client + Send + Sync + 'a,
+   |                             ++++
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/generic-associated-types/issue-95305.rs b/src/test/ui/generic-associated-types/issue-95305.rs
new file mode 100644
index 00000000000..9ead347984b
--- /dev/null
+++ b/src/test/ui/generic-associated-types/issue-95305.rs
@@ -0,0 +1,17 @@
+// It's not yet clear how '_ and GATs should interact.
+// Forbid it for now but proper support might be added
+// at some point in the future.
+
+#![feature(generic_associated_types)]
+
+trait Foo {
+    type Item<'a>;
+}
+
+fn foo(x: &impl Foo<Item<'_> = u32>) { }
+                       //~^ ERROR missing lifetime specifier
+
+fn bar(x: &impl for<'a> Foo<Item<'a> = &'_ u32>) { }
+                                      //~^ ERROR missing lifetime specifier
+
+fn main() {}
diff --git a/src/test/ui/generic-associated-types/issue-95305.stderr b/src/test/ui/generic-associated-types/issue-95305.stderr
new file mode 100644
index 00000000000..2b48378dc43
--- /dev/null
+++ b/src/test/ui/generic-associated-types/issue-95305.stderr
@@ -0,0 +1,25 @@
+error[E0106]: missing lifetime specifier
+  --> $DIR/issue-95305.rs:11:26
+   |
+LL | fn foo(x: &impl Foo<Item<'_> = u32>) { }
+   |                          ^^ expected named lifetime parameter
+   |
+help: consider introducing a named lifetime parameter
+   |
+LL | fn foo<'a>(x: &impl Foo<Item<'a> = u32>) { }
+   |       ++++                   ~~
+
+error[E0106]: missing lifetime specifier
+  --> $DIR/issue-95305.rs:14:41
+   |
+LL | fn bar(x: &impl for<'a> Foo<Item<'a> = &'_ u32>) { }
+   |                                         ^^ expected named lifetime parameter
+   |
+help: consider using the `'a` lifetime
+   |
+LL | fn bar(x: &impl for<'a> Foo<Item<'a> = &'a u32>) { }
+   |                                         ~~
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0106`.
diff --git a/src/test/ui/generic-associated-types/missing-bounds.fixed b/src/test/ui/generic-associated-types/missing-bounds.fixed
deleted file mode 100644
index 0e234120a51..00000000000
--- a/src/test/ui/generic-associated-types/missing-bounds.fixed
+++ /dev/null
@@ -1,46 +0,0 @@
-// run-rustfix
-
-use std::ops::Add;
-
-struct A<B>(B);
-
-impl<B> Add for A<B> where B: Add + Add<Output = B> {
-    type Output = Self;
-
-    fn add(self, rhs: Self) -> Self {
-        A(self.0 + rhs.0) //~ ERROR mismatched types
-    }
-}
-
-struct C<B>(B);
-
-impl<B: Add + Add<Output = B>> Add for C<B> {
-    type Output = Self;
-
-    fn add(self, rhs: Self) -> Self {
-        Self(self.0 + rhs.0) //~ ERROR mismatched types
-    }
-}
-
-struct D<B>(B);
-
-impl<B: std::ops::Add<Output = B>> Add for D<B> {
-    type Output = Self;
-
-    fn add(self, rhs: Self) -> Self {
-        Self(self.0 + rhs.0) //~ ERROR cannot add `B` to `B`
-    }
-}
-
-struct E<B>(B);
-
-impl<B: Add> Add for E<B> where B: Add<Output = B>, B: Add<Output = B> {
-    //~^ ERROR equality constraints are not yet supported in `where` clauses
-    type Output = Self;
-
-    fn add(self, rhs: Self) -> Self {
-        Self(self.0 + rhs.0) //~ ERROR mismatched types
-    }
-}
-
-fn main() {}
diff --git a/src/test/ui/generic-associated-types/missing-bounds.rs b/src/test/ui/generic-associated-types/missing-bounds.rs
index ffafff5e9f5..b3661ba3744 100644
--- a/src/test/ui/generic-associated-types/missing-bounds.rs
+++ b/src/test/ui/generic-associated-types/missing-bounds.rs
@@ -1,5 +1,3 @@
-// run-rustfix
-
 use std::ops::Add;
 
 struct A<B>(B);
diff --git a/src/test/ui/generic-associated-types/missing-bounds.stderr b/src/test/ui/generic-associated-types/missing-bounds.stderr
index 240be93cf96..5323ee17226 100644
--- a/src/test/ui/generic-associated-types/missing-bounds.stderr
+++ b/src/test/ui/generic-associated-types/missing-bounds.stderr
@@ -1,5 +1,5 @@
 error: equality constraints are not yet supported in `where` clauses
-  --> $DIR/missing-bounds.rs:37:33
+  --> $DIR/missing-bounds.rs:35:33
    |
 LL | impl<B: Add> Add for E<B> where <B as Add>::Output = B {
    |                                 ^^^^^^^^^^^^^^^^^^^^^^ not supported
@@ -11,7 +11,7 @@ LL | impl<B: Add> Add for E<B> where B: Add<Output = B> {
    |                                 ~~~~~~~~~~~~~~~~~~
 
 error[E0308]: mismatched types
-  --> $DIR/missing-bounds.rs:11:11
+  --> $DIR/missing-bounds.rs:9:11
    |
 LL | impl<B> Add for A<B> where B: Add {
    |      - this type parameter
@@ -24,7 +24,7 @@ LL |         A(self.0 + rhs.0)
    = note: expected type parameter `B`
              found associated type `<B as Add>::Output`
 note: tuple struct defined here
-  --> $DIR/missing-bounds.rs:5:8
+  --> $DIR/missing-bounds.rs:3:8
    |
 LL | struct A<B>(B);
    |        ^
@@ -34,7 +34,7 @@ LL | impl<B> Add for A<B> where B: Add + Add<Output = B> {
    |                                   +++++++++++++++++
 
 error[E0308]: mismatched types
-  --> $DIR/missing-bounds.rs:21:14
+  --> $DIR/missing-bounds.rs:19:14
    |
 LL | impl<B: Add> Add for C<B> {
    |      - this type parameter
@@ -47,7 +47,7 @@ LL |         Self(self.0 + rhs.0)
    = note: expected type parameter `B`
              found associated type `<B as Add>::Output`
 note: tuple struct defined here
-  --> $DIR/missing-bounds.rs:15:8
+  --> $DIR/missing-bounds.rs:13:8
    |
 LL | struct C<B>(B);
    |        ^
@@ -57,7 +57,7 @@ LL | impl<B: Add + Add<Output = B>> Add for C<B> {
    |             +++++++++++++++++
 
 error[E0369]: cannot add `B` to `B`
-  --> $DIR/missing-bounds.rs:31:21
+  --> $DIR/missing-bounds.rs:29:21
    |
 LL |         Self(self.0 + rhs.0)
    |              ------ ^ ----- B
@@ -66,11 +66,11 @@ LL |         Self(self.0 + rhs.0)
    |
 help: consider restricting type parameter `B`
    |
-LL | impl<B: std::ops::Add<Output = B>> Add for D<B> {
-   |       +++++++++++++++++++++++++++
+LL | impl<B: std::ops::Add> Add for D<B> {
+   |       +++++++++++++++
 
 error[E0308]: mismatched types
-  --> $DIR/missing-bounds.rs:42:14
+  --> $DIR/missing-bounds.rs:40:14
    |
 LL | impl<B: Add> Add for E<B> where <B as Add>::Output = B {
    |      - this type parameter
@@ -83,14 +83,14 @@ LL |         Self(self.0 + rhs.0)
    = note: expected type parameter `B`
              found associated type `<B as Add>::Output`
 note: tuple struct defined here
-  --> $DIR/missing-bounds.rs:35:8
+  --> $DIR/missing-bounds.rs:33:8
    |
 LL | struct E<B>(B);
    |        ^
-help: consider further restricting type parameter `B`
+help: consider further restricting this bound
    |
-LL | impl<B: Add> Add for E<B> where <B as Add>::Output = B, B: Add<Output = B> {
-   |                                                       ++++++++++++++++++++
+LL | impl<B: Add + Add<Output = B>> Add for E<B> where <B as Add>::Output = B {
+   |             +++++++++++++++++
 
 error: aborting due to 5 previous errors
 
diff --git a/src/test/ui/generic-associated-types/unsatified-item-lifetime-bound.stderr b/src/test/ui/generic-associated-types/unsatified-item-lifetime-bound.stderr
index 4f0a023ee39..ae52010cc50 100644
--- a/src/test/ui/generic-associated-types/unsatified-item-lifetime-bound.stderr
+++ b/src/test/ui/generic-associated-types/unsatified-item-lifetime-bound.stderr
@@ -2,7 +2,7 @@ warning: unnecessary lifetime parameter `'a`
   --> $DIR/unsatified-item-lifetime-bound.rs:4:12
    |
 LL |     type Y<'a: 'static>;
-   |            ^^^^^^^^^^^
+   |            ^^
    |
    = help: you can use the `'static` lifetime directly, in place of `'a`
 
diff --git a/src/test/ui/issues/issue-80512-param-reordering-with-defaults.rs b/src/test/ui/generics/issue-80512-param-reordering-with-defaults.rs
index fe3e4fbc7e0..fe3e4fbc7e0 100644
--- a/src/test/ui/issues/issue-80512-param-reordering-with-defaults.rs
+++ b/src/test/ui/generics/issue-80512-param-reordering-with-defaults.rs
diff --git a/src/test/ui/issues/issue-80512-param-reordering-with-defaults.stderr b/src/test/ui/generics/issue-80512-param-reordering-with-defaults.stderr
index 119b1a0d207..119b1a0d207 100644
--- a/src/test/ui/issues/issue-80512-param-reordering-with-defaults.stderr
+++ b/src/test/ui/generics/issue-80512-param-reordering-with-defaults.stderr
diff --git a/src/test/ui/generics/post_monomorphization_error_backtrace.rs b/src/test/ui/generics/post_monomorphization_error_backtrace.rs
new file mode 100644
index 00000000000..1fd9b6b3b9d
--- /dev/null
+++ b/src/test/ui/generics/post_monomorphization_error_backtrace.rs
@@ -0,0 +1,33 @@
+// build-fail
+
+fn assert_zst<T>() {
+    struct F<T>(T);
+    impl<T> F<T> {
+        const V: () = assert!(std::mem::size_of::<T>() == 0);
+        //~^ ERROR: evaluation of `assert_zst::F::<u32>::V` failed [E0080]
+        //~| NOTE: in this expansion of assert!
+        //~| NOTE: the evaluated program panicked
+        //~| ERROR: evaluation of `assert_zst::F::<i32>::V` failed [E0080]
+        //~| NOTE: in this expansion of assert!
+        //~| NOTE: the evaluated program panicked
+    }
+    let _ = F::<T>::V;
+}
+
+fn foo<U>() {
+    assert_zst::<U>()
+    //~^ NOTE: the above error was encountered while instantiating `fn assert_zst::<u32>`
+    //~| NOTE: the above error was encountered while instantiating `fn assert_zst::<i32>`
+}
+
+
+fn bar<V>() {
+    foo::<V>()
+}
+
+fn main() {
+    bar::<()>();
+    bar::<u32>();
+    bar::<u32>();
+    bar::<i32>();
+}
diff --git a/src/test/ui/generics/post_monomorphization_error_backtrace.stderr b/src/test/ui/generics/post_monomorphization_error_backtrace.stderr
new file mode 100644
index 00000000000..0d707d83d26
--- /dev/null
+++ b/src/test/ui/generics/post_monomorphization_error_backtrace.stderr
@@ -0,0 +1,31 @@
+error[E0080]: evaluation of `assert_zst::F::<u32>::V` failed
+  --> $DIR/post_monomorphization_error_backtrace.rs:6:23
+   |
+LL |         const V: () = assert!(std::mem::size_of::<T>() == 0);
+   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: std::mem::size_of::<T>() == 0', $DIR/post_monomorphization_error_backtrace.rs:6:23
+   |
+   = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+note: the above error was encountered while instantiating `fn assert_zst::<u32>`
+  --> $DIR/post_monomorphization_error_backtrace.rs:18:5
+   |
+LL |     assert_zst::<U>()
+   |     ^^^^^^^^^^^^^^^^^
+
+error[E0080]: evaluation of `assert_zst::F::<i32>::V` failed
+  --> $DIR/post_monomorphization_error_backtrace.rs:6:23
+   |
+LL |         const V: () = assert!(std::mem::size_of::<T>() == 0);
+   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: std::mem::size_of::<T>() == 0', $DIR/post_monomorphization_error_backtrace.rs:6:23
+   |
+   = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+note: the above error was encountered while instantiating `fn assert_zst::<i32>`
+  --> $DIR/post_monomorphization_error_backtrace.rs:18:5
+   |
+LL |     assert_zst::<U>()
+   |     ^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/hrtb/issue-30786.migrate.stderr b/src/test/ui/hrtb/issue-30786.migrate.stderr
index 7ffe2f4cd7e..7157b186fc8 100644
--- a/src/test/ui/hrtb/issue-30786.migrate.stderr
+++ b/src/test/ui/hrtb/issue-30786.migrate.stderr
@@ -18,6 +18,10 @@ note: the following trait bounds were not satisfied:
    |
 LL | impl<T> StreamExt for T where for<'a> &'a mut T: Stream {}
    |         ---------     -                          ^^^^^^ unsatisfied trait bound introduced here
+help: one of the expressions' fields has a method of the same name
+   |
+LL |     let filter = map.stream.filterx(|x: &_| true);
+   |                      +++++++
 
 error[E0599]: the method `countx` exists for struct `Filter<Map<Repeat, for<'r> fn(&'r u64) -> &'r u64 {identity::<u64>}>, [closure@$DIR/issue-30786.rs:139:30: 139:42]>`, but its trait bounds were not satisfied
   --> $DIR/issue-30786.rs:140:24
@@ -39,6 +43,10 @@ note: the following trait bounds were not satisfied:
    |
 LL | impl<T> StreamExt for T where for<'a> &'a mut T: Stream {}
    |         ---------     -                          ^^^^^^ unsatisfied trait bound introduced here
+help: one of the expressions' fields has a method of the same name
+   |
+LL |     let count = filter.stream.countx();
+   |                        +++++++
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/hrtb/issue-30786.nll.stderr b/src/test/ui/hrtb/issue-30786.nll.stderr
index 7ffe2f4cd7e..7157b186fc8 100644
--- a/src/test/ui/hrtb/issue-30786.nll.stderr
+++ b/src/test/ui/hrtb/issue-30786.nll.stderr
@@ -18,6 +18,10 @@ note: the following trait bounds were not satisfied:
    |
 LL | impl<T> StreamExt for T where for<'a> &'a mut T: Stream {}
    |         ---------     -                          ^^^^^^ unsatisfied trait bound introduced here
+help: one of the expressions' fields has a method of the same name
+   |
+LL |     let filter = map.stream.filterx(|x: &_| true);
+   |                      +++++++
 
 error[E0599]: the method `countx` exists for struct `Filter<Map<Repeat, for<'r> fn(&'r u64) -> &'r u64 {identity::<u64>}>, [closure@$DIR/issue-30786.rs:139:30: 139:42]>`, but its trait bounds were not satisfied
   --> $DIR/issue-30786.rs:140:24
@@ -39,6 +43,10 @@ note: the following trait bounds were not satisfied:
    |
 LL | impl<T> StreamExt for T where for<'a> &'a mut T: Stream {}
    |         ---------     -                          ^^^^^^ unsatisfied trait bound introduced here
+help: one of the expressions' fields has a method of the same name
+   |
+LL |     let count = filter.stream.countx();
+   |                        +++++++
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/impl-trait/bound-normalization-fail.rs b/src/test/ui/impl-trait/bound-normalization-fail.rs
index 9f962fa9bba..3329592478d 100644
--- a/src/test/ui/impl-trait/bound-normalization-fail.rs
+++ b/src/test/ui/impl-trait/bound-normalization-fail.rs
@@ -24,7 +24,6 @@ mod impl_trait {
     /// `T::Assoc` can't be normalized any further here.
     fn foo_fail<T: Trait>() -> impl FooLike<Output = T::Assoc> {
         //~^ ERROR: type mismatch
-        //~| ERROR: type mismatch
         Foo(())
     }
 }
@@ -42,7 +41,6 @@ mod lifetimes {
     fn foo2_fail<'a, T: Trait<'a>>() -> impl FooLike<Output = T::Assoc> {
         //~^ ERROR `impl Trait` return type cannot contain a projection or `Self` that references lifetimes from a parent scope
         //~| ERROR: type mismatch
-        //~| ERROR: type mismatch
         Foo(())
     }
 }
diff --git a/src/test/ui/impl-trait/bound-normalization-fail.stderr b/src/test/ui/impl-trait/bound-normalization-fail.stderr
index 0344f416eb7..eac7e6b315e 100644
--- a/src/test/ui/impl-trait/bound-normalization-fail.stderr
+++ b/src/test/ui/impl-trait/bound-normalization-fail.stderr
@@ -16,37 +16,14 @@ help: consider constraining the associated type `<T as impl_trait::Trait>::Assoc
 LL |     fn foo_fail<T: Trait<Assoc = ()>>() -> impl FooLike<Output = T::Assoc> {
    |                         ++++++++++++
 
-error[E0271]: type mismatch resolving `<Foo<()> as FooLike>::Output == <T as impl_trait::Trait>::Assoc`
-  --> $DIR/bound-normalization-fail.rs:25:64
-   |
-LL |       fn foo_fail<T: Trait>() -> impl FooLike<Output = T::Assoc> {
-   |  ________________________________________________________________^
-LL | |
-LL | |
-LL | |         Foo(())
-LL | |     }
-   | |_____^ type mismatch resolving `<Foo<()> as FooLike>::Output == <T as impl_trait::Trait>::Assoc`
-   |
-note: expected this to be `()`
-  --> $DIR/bound-normalization-fail.rs:14:19
-   |
-LL |     type Output = T;
-   |                   ^
-   = note:    expected unit type `()`
-           found associated type `<T as impl_trait::Trait>::Assoc`
-help: consider constraining the associated type `<T as impl_trait::Trait>::Assoc` to `()`
-   |
-LL |     fn foo_fail<T: Trait<Assoc = ()>>() -> impl FooLike<Output = T::Assoc> {
-   |                         ++++++++++++
-
 error[E0760]: `impl Trait` return type cannot contain a projection or `Self` that references lifetimes from a parent scope
-  --> $DIR/bound-normalization-fail.rs:42:41
+  --> $DIR/bound-normalization-fail.rs:41:41
    |
 LL |     fn foo2_fail<'a, T: Trait<'a>>() -> impl FooLike<Output = T::Assoc> {
    |                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0271]: type mismatch resolving `<Foo<()> as FooLike>::Output == <T as lifetimes::Trait<'static>>::Assoc`
-  --> $DIR/bound-normalization-fail.rs:42:41
+  --> $DIR/bound-normalization-fail.rs:41:41
    |
 LL |     fn foo2_fail<'a, T: Trait<'a>>() -> impl FooLike<Output = T::Assoc> {
    |                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type mismatch resolving `<Foo<()> as FooLike>::Output == <T as lifetimes::Trait<'static>>::Assoc`
@@ -63,31 +40,7 @@ help: consider constraining the associated type `<T as lifetimes::Trait<'static>
 LL |     fn foo2_fail<'a, T: Trait<'a, Assoc = ()>>() -> impl FooLike<Output = T::Assoc> {
    |                                 ++++++++++++
 
-error[E0271]: type mismatch resolving `<Foo<()> as FooLike>::Output == <T as lifetimes::Trait<'static>>::Assoc`
-  --> $DIR/bound-normalization-fail.rs:42:73
-   |
-LL |       fn foo2_fail<'a, T: Trait<'a>>() -> impl FooLike<Output = T::Assoc> {
-   |  _________________________________________________________________________^
-LL | |
-LL | |
-LL | |
-LL | |         Foo(())
-LL | |     }
-   | |_____^ type mismatch resolving `<Foo<()> as FooLike>::Output == <T as lifetimes::Trait<'static>>::Assoc`
-   |
-note: expected this to be `()`
-  --> $DIR/bound-normalization-fail.rs:14:19
-   |
-LL |     type Output = T;
-   |                   ^
-   = note:    expected unit type `()`
-           found associated type `<T as lifetimes::Trait<'static>>::Assoc`
-help: consider constraining the associated type `<T as lifetimes::Trait<'static>>::Assoc` to `()`
-   |
-LL |     fn foo2_fail<'a, T: Trait<'a, Assoc = ()>>() -> impl FooLike<Output = T::Assoc> {
-   |                                 ++++++++++++
-
-error: aborting due to 5 previous errors
+error: aborting due to 3 previous errors
 
 Some errors have detailed explanations: E0271, E0760.
 For more information about an error, try `rustc --explain E0271`.
diff --git a/src/test/ui/impl-trait/cross-return-site-inference.rs b/src/test/ui/impl-trait/cross-return-site-inference.rs
index c27b5ca9f66..a7428f9bf12 100644
--- a/src/test/ui/impl-trait/cross-return-site-inference.rs
+++ b/src/test/ui/impl-trait/cross-return-site-inference.rs
@@ -29,17 +29,17 @@ fn baa(b: bool) -> impl std::fmt::Debug {
 }
 
 fn muh() -> Result<(), impl std::fmt::Debug> {
-    Err("whoops")?; //~ ERROR `?` couldn't convert the error to `impl Debug`
+    Err("whoops")?; //~^ ERROR type annotations needed
     Ok(())
 }
 
 fn muh2() -> Result<(), impl std::fmt::Debug> {
-    return Err(From::from("foo")); //~ ERROR the trait bound `impl Debug: From<&str>` is not satisfied
+    return Err(From::from("foo")); //~^ ERROR type annotations needed
     Ok(())
 }
 
 fn muh3() -> Result<(), impl std::fmt::Debug> {
-    Err(From::from("foo")) //~ ERROR the trait bound `impl Debug: From<&str>` is not satisfied
+    Err(From::from("foo")) //~^ ERROR type annotations needed
 }
 
 fn main() {}
diff --git a/src/test/ui/impl-trait/cross-return-site-inference.stderr b/src/test/ui/impl-trait/cross-return-site-inference.stderr
index d458c7be783..5209d7a5743 100644
--- a/src/test/ui/impl-trait/cross-return-site-inference.stderr
+++ b/src/test/ui/impl-trait/cross-return-site-inference.stderr
@@ -1,27 +1,21 @@
-error[E0277]: `?` couldn't convert the error to `impl Debug`
-  --> $DIR/cross-return-site-inference.rs:32:18
+error[E0282]: type annotations needed
+  --> $DIR/cross-return-site-inference.rs:31:24
    |
 LL | fn muh() -> Result<(), impl std::fmt::Debug> {
-   |             -------------------------------- expected `impl Debug` because of this
-LL |     Err("whoops")?;
-   |                  ^ the trait `From<&str>` is not implemented for `impl Debug`
-   |
-   = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
-   = help: the trait `FromResidual<Result<Infallible, E>>` is implemented for `Result<T, F>`
-   = note: required because of the requirements on the impl of `FromResidual<Result<Infallible, &str>>` for `Result<(), impl Debug>`
+   |                        ^^^^^^^^^^^^^^^^^^^^ cannot infer type
 
-error[E0277]: the trait bound `impl Debug: From<&str>` is not satisfied
-  --> $DIR/cross-return-site-inference.rs:37:16
+error[E0282]: type annotations needed
+  --> $DIR/cross-return-site-inference.rs:36:25
    |
-LL |     return Err(From::from("foo"));
-   |                ^^^^^^^^^^ the trait `From<&str>` is not implemented for `impl Debug`
+LL | fn muh2() -> Result<(), impl std::fmt::Debug> {
+   |                         ^^^^^^^^^^^^^^^^^^^^ cannot infer type
 
-error[E0277]: the trait bound `impl Debug: From<&str>` is not satisfied
-  --> $DIR/cross-return-site-inference.rs:42:9
+error[E0282]: type annotations needed
+  --> $DIR/cross-return-site-inference.rs:41:25
    |
-LL |     Err(From::from("foo"))
-   |         ^^^^^^^^^^ the trait `From<&str>` is not implemented for `impl Debug`
+LL | fn muh3() -> Result<(), impl std::fmt::Debug> {
+   |                         ^^^^^^^^^^^^^^^^^^^^ cannot infer type
 
 error: aborting due to 3 previous errors
 
-For more information about this error, try `rustc --explain E0277`.
+For more information about this error, try `rustc --explain E0282`.
diff --git a/src/test/ui/impl-trait/equal-hidden-lifetimes.stderr b/src/test/ui/impl-trait/equal-hidden-lifetimes.stderr
index 51247f1d7b0..3e48aef553b 100644
--- a/src/test/ui/impl-trait/equal-hidden-lifetimes.stderr
+++ b/src/test/ui/impl-trait/equal-hidden-lifetimes.stderr
@@ -2,7 +2,7 @@ warning: unnecessary lifetime parameter `'a`
   --> $DIR/equal-hidden-lifetimes.rs:7:25
    |
 LL | fn equal_regions_static<'a: 'static>(x: &'a i32) -> impl Sized {
-   |                         ^^^^^^^^^^^
+   |                         ^^
    |
    = help: you can use the `'static` lifetime directly, in place of `'a`
 
diff --git a/src/test/ui/impl-trait/fallback_inference.rs b/src/test/ui/impl-trait/fallback_inference.rs
new file mode 100644
index 00000000000..001f9ee4877
--- /dev/null
+++ b/src/test/ui/impl-trait/fallback_inference.rs
@@ -0,0 +1,7 @@
+use std::marker::PhantomData;
+
+fn weird() -> PhantomData<impl Sized> {
+    PhantomData //~^ ERROR type annotations needed
+}
+
+fn main() {}
diff --git a/src/test/ui/impl-trait/fallback_inference.stderr b/src/test/ui/impl-trait/fallback_inference.stderr
new file mode 100644
index 00000000000..b637ca694c2
--- /dev/null
+++ b/src/test/ui/impl-trait/fallback_inference.stderr
@@ -0,0 +1,9 @@
+error[E0282]: type annotations needed
+  --> $DIR/fallback_inference.rs:3:27
+   |
+LL | fn weird() -> PhantomData<impl Sized> {
+   |                           ^^^^^^^^^^ cannot infer type
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0282`.
diff --git a/src/test/ui/impl-trait/issue-72911.rs b/src/test/ui/impl-trait/issue-72911.rs
index cf2c8b7e415..be9c643b2d8 100644
--- a/src/test/ui/impl-trait/issue-72911.rs
+++ b/src/test/ui/impl-trait/issue-72911.rs
@@ -5,7 +5,7 @@ pub struct Lint {}
 impl Lint {}
 
 pub fn gather_all() -> impl Iterator<Item = Lint> {
-    //~^ ERROR `()` is not an iterator
+    //~^ ERROR type annotations needed
     lint_files().flat_map(|f| gather_from_file(&f))
 }
 
diff --git a/src/test/ui/impl-trait/issue-72911.stderr b/src/test/ui/impl-trait/issue-72911.stderr
index 4a990286d96..fc7200c75c2 100644
--- a/src/test/ui/impl-trait/issue-72911.stderr
+++ b/src/test/ui/impl-trait/issue-72911.stderr
@@ -10,15 +10,13 @@ error[E0433]: failed to resolve: use of undeclared crate or module `foo`
 LL | fn lint_files() -> impl Iterator<Item = foo::MissingItem> {
    |                                         ^^^ use of undeclared crate or module `foo`
 
-error[E0277]: `()` is not an iterator
+error[E0282]: type annotations needed
   --> $DIR/issue-72911.rs:7:24
    |
 LL | pub fn gather_all() -> impl Iterator<Item = Lint> {
-   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^ `()` is not an iterator
-   |
-   = help: the trait `Iterator` is not implemented for `()`
+   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type
 
 error: aborting due to 3 previous errors
 
-Some errors have detailed explanations: E0277, E0433.
-For more information about an error, try `rustc --explain E0277`.
+Some errors have detailed explanations: E0282, E0433.
+For more information about an error, try `rustc --explain E0282`.
diff --git a/src/test/ui/impl-trait/issues/issue-67830.stderr b/src/test/ui/impl-trait/issues/issue-67830.stderr
index 74e2f99cd33..4c0490c721b 100644
--- a/src/test/ui/impl-trait/issues/issue-67830.stderr
+++ b/src/test/ui/impl-trait/issues/issue-67830.stderr
@@ -1,12 +1,8 @@
 error: implementation of `FnOnce` is not general enough
-  --> $DIR/issue-67830.rs:21:66
+  --> $DIR/issue-67830.rs:21:14
    |
-LL |   fn test() -> impl for<'a> MyFn<&'a A, Output=impl Iterator + 'a> {
-   |  __________________________________________________________________^
-LL | |
-LL | |     Wrap(|a| Some(a).into_iter())
-LL | | }
-   | |_^ implementation of `FnOnce` is not general enough
+LL | fn test() -> impl for<'a> MyFn<&'a A, Output=impl Iterator + 'a> {
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
    |
    = note: closure with signature `fn(&'2 A) -> std::option::IntoIter<&A>` must implement `FnOnce<(&'1 A,)>`, for any lifetime `'1`...
    = note: ...but it actually implements `FnOnce<(&'2 A,)>`, for some specific lifetime `'2`
diff --git a/src/test/ui/impl-trait/issues/issue-86800.rs b/src/test/ui/impl-trait/issues/issue-86800.rs
index e8cef42f208..19edeaffc49 100644
--- a/src/test/ui/impl-trait/issues/issue-86800.rs
+++ b/src/test/ui/impl-trait/issues/issue-86800.rs
@@ -27,16 +27,16 @@ type TransactionFuture<'__, O> = impl '__ + Future<Output = TransactionResult<O>
 
 fn execute_transaction_fut<'f, F, O>(
     f: F,
-) -> impl FnOnce(&mut dyn Transaction) -> TransactionFuture<O>
+) -> impl FnOnce(&mut dyn Transaction) -> TransactionFuture<'_, O>
 where
-    F: FnOnce(&mut dyn Transaction) -> TransactionFuture<O> + 'f
+    F: FnOnce(&mut dyn Transaction) -> TransactionFuture<'_, O> + 'f
 {
     f
 }
 
 impl Context {
     async fn do_transaction<O>(
-        &self, f: impl FnOnce(&mut dyn Transaction) -> TransactionFuture<O>
+        &self, f: impl FnOnce(&mut dyn Transaction) -> TransactionFuture<'_, O>
     ) -> TransactionResult<O>
     {
         let mut conn = Connection {};
diff --git a/src/test/ui/impl-trait/issues/issue-88236-2.nll.stderr b/src/test/ui/impl-trait/issues/issue-88236-2.nll.stderr
index 9cf8ff76c87..66cffa9e36c 100644
--- a/src/test/ui/impl-trait/issues/issue-88236-2.nll.stderr
+++ b/src/test/ui/impl-trait/issues/issue-88236-2.nll.stderr
@@ -24,10 +24,14 @@ LL | fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send
 LL |     x
    |     ^ returning this value requires that `'b` must outlive `'static`
    |
-help: to allow this `impl Trait` to capture borrowed data with lifetime `'b`, add `'b` as a bound
+help: to declare that the `impl Trait` captures data from argument `x`, you can add an explicit `'b` lifetime bound
    |
 LL | fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> + 'b {
    |                                                                                  ++++
+help: to declare that the `impl Trait` captures data from argument `x`, you can add an explicit `'b` lifetime bound
+   |
+LL | fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a + 'b> {
+   |                                                                                 ++++
 
 error: implementation of `Hrtb` is not general enough
   --> $DIR/issue-88236-2.rs:20:5
diff --git a/src/test/ui/impl-trait/issues/issue-88236-2.stderr b/src/test/ui/impl-trait/issues/issue-88236-2.stderr
index 45fadcab3f2..9574b880f7d 100644
--- a/src/test/ui/impl-trait/issues/issue-88236-2.stderr
+++ b/src/test/ui/impl-trait/issues/issue-88236-2.stderr
@@ -8,15 +8,12 @@ LL | fn make_weird_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Sen
    = note: ...but `Hrtb<'1>` is actually implemented for the type `&'1 ()`, for some specific lifetime `'1`
 
 error: implementation of `Hrtb` is not general enough
-  --> $DIR/issue-88236-2.rs:19:82
+  --> $DIR/issue-88236-2.rs:19:36
    |
-LL |   fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> {
-   |  __________________________________________________________________________________^
-LL | |     x
-LL | | }
-   | |_^ implementation of `Hrtb` is not general enough
+LL | fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> {
+   |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Hrtb` is not general enough
    |
-   = note: `&()` must implement `Hrtb<'0>`, for any lifetime `'0`...
+   = note: `Hrtb<'1>` would have to be implemented for the type `&()`, for any lifetime `'1`...
    = note: ...but `Hrtb<'_>` is actually implemented for the type `&()`
 
 error: aborting due to 2 previous errors
diff --git a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr
index 9589b69491e..db737a9c544 100644
--- a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr
+++ b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr
@@ -32,7 +32,14 @@ LL | fn elided2(x: &i32) -> impl Copy + 'static { x }
    |               |
    |               let's call the lifetime of this reference `'1`
    |
-   = help: consider replacing `'1` with `'static`
+help: consider changing the `impl Trait`'s explicit `'static` bound to the lifetime of argument `x`
+   |
+LL | fn elided2(x: &i32) -> impl Copy + '_ { x }
+   |                                    ~~
+help: alternatively, add an explicit `'static` bound to this reference
+   |
+LL | fn elided2(x: &'static i32) -> impl Copy + 'static { x }
+   |               ~~~~~~~~~~~~
 
 error: lifetime may not live long enough
   --> $DIR/must_outlive_least_region_or_bound.rs:11:55
@@ -40,7 +47,14 @@ error: lifetime may not live long enough
 LL | fn explicit2<'a>(x: &'a i32) -> impl Copy + 'static { x }
    |              -- lifetime `'a` defined here            ^ returning this value requires that `'a` must outlive `'static`
    |
-   = help: consider replacing `'a` with `'static`
+help: consider changing the `impl Trait`'s explicit `'static` bound to the lifetime of argument `x`
+   |
+LL | fn explicit2<'a>(x: &'a i32) -> impl Copy + 'a { x }
+   |                                             ~~
+help: alternatively, add an explicit `'static` bound to this reference
+   |
+LL | fn explicit2<'a>(x: &'static i32) -> impl Copy + 'static { x }
+   |                     ~~~~~~~~~~~~
 
 error[E0621]: explicit lifetime required in the type of `x`
   --> $DIR/must_outlive_least_region_or_bound.rs:13:41
@@ -57,6 +71,15 @@ LL | fn elided5(x: &i32) -> (Box<dyn Debug>, impl Debug) { (Box::new(x), x) }
    |               -                                       ^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static`
    |               |
    |               let's call the lifetime of this reference `'1`
+   |
+help: to declare that the trait object captures data from argument `x`, you can add an explicit `'_` lifetime bound
+   |
+LL | fn elided5(x: &i32) -> (Box<dyn Debug + '_>, impl Debug) { (Box::new(x), x) }
+   |                                       ++++
+help: to declare that the `impl Trait` captures data from argument `x`, you can add an explicit `'_` lifetime bound
+   |
+LL | fn elided5(x: &i32) -> (Box<dyn Debug>, impl Debug + '_) { (Box::new(x), x) }
+   |                                                    ++++
 
 error: lifetime may not live long enough
   --> $DIR/must_outlive_least_region_or_bound.rs:29:69
@@ -64,7 +87,14 @@ error: lifetime may not live long enough
 LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x }
    |               -- lifetime `'a` defined here                         ^ returning this value requires that `'a` must outlive `'static`
    |
-   = help: consider replacing `'a` with `'static`
+help: consider changing the `impl Trait`'s explicit `'static` bound to the lifetime of argument `x`
+   |
+LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'a { x }
+   |                                                           ~~
+help: alternatively, add an explicit `'static` bound to this reference
+   |
+LL | fn with_bound<'a>(x: &'static i32) -> impl LifetimeTrait<'a> + 'static { x }
+   |                      ~~~~~~~~~~~~
 
 error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds
   --> $DIR/must_outlive_least_region_or_bound.rs:34:5
@@ -80,12 +110,15 @@ LL | fn move_lifetime_into_fn<'a, 'b>(x: &'a u32, y: &'b u32) -> impl Fn(&'a u32
    |                                                                              ++++
 
 error[E0310]: the parameter type `T` may not live long enough
-  --> $DIR/must_outlive_least_region_or_bound.rs:41:5
+  --> $DIR/must_outlive_least_region_or_bound.rs:40:5
    |
 LL |     x
-   |     ^
+   |     ^ ...so that the type `T` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
    |
-   = help: consider adding an explicit lifetime bound `T: 'static`...
+LL | fn ty_param_wont_outlive_static<T:Debug + 'static>(x: T) -> impl Debug + 'static {
+   |                                         +++++++++
 
 error: aborting due to 9 previous errors
 
diff --git a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.rs b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.rs
index baa42da6446..60e4672f1b7 100644
--- a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.rs
+++ b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.rs
@@ -37,7 +37,6 @@ fn move_lifetime_into_fn<'a, 'b>(x: &'a u32, y: &'b u32) -> impl Fn(&'a u32) {
 
 fn ty_param_wont_outlive_static<T:Debug>(x: T) -> impl Debug + 'static {
     //~^ ERROR the parameter type `T` may not live long enough
-    //~| ERROR the parameter type `T` may not live long enough
     x
 }
 
diff --git a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr
index 1272adb35e9..dade2b91fe0 100644
--- a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr
+++ b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr
@@ -128,22 +128,12 @@ error[E0310]: the parameter type `T` may not live long enough
   --> $DIR/must_outlive_least_region_or_bound.rs:38:51
    |
 LL | fn ty_param_wont_outlive_static<T:Debug>(x: T) -> impl Debug + 'static {
-   |                                 --                ^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
-   |                                 |
-   |                                 help: consider adding an explicit lifetime bound...: `T: 'static +`
-
-error[E0310]: the parameter type `T` may not live long enough
-  --> $DIR/must_outlive_least_region_or_bound.rs:38:72
-   |
-LL |   fn ty_param_wont_outlive_static<T:Debug>(x: T) -> impl Debug + 'static {
-   |  _________________________________--_____________________________________^
-   | |                                 |
-   | |                                 help: consider adding an explicit lifetime bound...: `T: 'static +`
-LL | |
-LL | |
-LL | |     x
-LL | | }
-   | |_^ ...so that the type `T` will meet its required lifetime bounds
+   |                                                   ^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn ty_param_wont_outlive_static<T:Debug + 'static>(x: T) -> impl Debug + 'static {
+   |                                         +++++++++
 
 error[E0759]: `x` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
   --> $DIR/must_outlive_least_region_or_bound.rs:16:50
@@ -231,7 +221,7 @@ help: alternatively, add an explicit `'static` bound to this reference
 LL | fn explicit4<'a>(x: &'static i32) -> Box<dyn Debug + 'static> { Box::new(x) }
    |                     ~~~~~~~~~~~~
 
-error: aborting due to 14 previous errors
+error: aborting due to 13 previous errors
 
 Some errors have detailed explanations: E0310, E0621, E0700, E0759.
 For more information about an error, try `rustc --explain E0310`.
diff --git a/src/test/ui/impl-trait/nested-return-type2-tait.rs b/src/test/ui/impl-trait/nested-return-type2-tait.rs
index f2217f699fb..a2a49c5535d 100644
--- a/src/test/ui/impl-trait/nested-return-type2-tait.rs
+++ b/src/test/ui/impl-trait/nested-return-type2-tait.rs
@@ -24,7 +24,6 @@ type Sendable = impl Send;
 // type does not implement `Duh`, even if its hidden type does. So we error out.
 fn foo() -> impl Trait<Assoc = Sendable> {
     //~^ ERROR `Sendable: Duh` is not satisfied
-    //~| ERROR `Sendable: Duh` is not satisfied
     || 42
 }
 
diff --git a/src/test/ui/impl-trait/nested-return-type2-tait.stderr b/src/test/ui/impl-trait/nested-return-type2-tait.stderr
index 81a75e39c91..359fb909e54 100644
--- a/src/test/ui/impl-trait/nested-return-type2-tait.stderr
+++ b/src/test/ui/impl-trait/nested-return-type2-tait.stderr
@@ -5,30 +5,12 @@ LL | fn foo() -> impl Trait<Assoc = Sendable> {
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Duh` is not implemented for `Sendable`
    |
    = help: the trait `Duh` is implemented for `i32`
-note: required because of the requirements on the impl of `Trait` for `[closure@$DIR/nested-return-type2-tait.rs:28:5: 28:10]`
+note: required because of the requirements on the impl of `Trait` for `[closure@$DIR/nested-return-type2-tait.rs:27:5: 27:10]`
   --> $DIR/nested-return-type2-tait.rs:14:31
    |
 LL | impl<R: Duh, F: FnMut() -> R> Trait for F {
    |                               ^^^^^     ^
 
-error[E0277]: the trait bound `Sendable: Duh` is not satisfied
-  --> $DIR/nested-return-type2-tait.rs:25:42
-   |
-LL |   fn foo() -> impl Trait<Assoc = Sendable> {
-   |  __________________________________________^
-LL | |
-LL | |
-LL | |     || 42
-LL | | }
-   | |_^ the trait `Duh` is not implemented for `Sendable`
-   |
-   = help: the trait `Duh` is implemented for `i32`
-note: required because of the requirements on the impl of `Trait` for `[closure@$DIR/nested-return-type2-tait.rs:28:5: 28:10]`
-  --> $DIR/nested-return-type2-tait.rs:14:31
-   |
-LL | impl<R: Duh, F: FnMut() -> R> Trait for F {
-   |                               ^^^^^     ^
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/impl-trait/type_parameters_captured.nll.stderr b/src/test/ui/impl-trait/type_parameters_captured.nll.stderr
index b1175a5952e..3050f10b205 100644
--- a/src/test/ui/impl-trait/type_parameters_captured.nll.stderr
+++ b/src/test/ui/impl-trait/type_parameters_captured.nll.stderr
@@ -1,10 +1,13 @@
 error[E0310]: the parameter type `T` may not live long enough
-  --> $DIR/type_parameters_captured.rs:10:5
+  --> $DIR/type_parameters_captured.rs:9:5
    |
 LL |     x
-   |     ^
+   |     ^ ...so that the type `T` will meet its required lifetime bounds
    |
-   = help: consider adding an explicit lifetime bound `T: 'static`...
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn foo<T: 'static>(x: T) -> impl Any + 'static {
+   |         +++++++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/impl-trait/type_parameters_captured.rs b/src/test/ui/impl-trait/type_parameters_captured.rs
index bb9cab742a5..6c9c9d4a42a 100644
--- a/src/test/ui/impl-trait/type_parameters_captured.rs
+++ b/src/test/ui/impl-trait/type_parameters_captured.rs
@@ -6,7 +6,6 @@ impl<T> Any for T {}
 // Check that type parameters are captured and not considered 'static
 fn foo<T>(x: T) -> impl Any + 'static {
     //~^ ERROR the parameter type `T` may not live long enough
-    //~| ERROR the parameter type `T` may not live long enough
     x
 }
 
diff --git a/src/test/ui/impl-trait/type_parameters_captured.stderr b/src/test/ui/impl-trait/type_parameters_captured.stderr
index c4ca34a6ed3..9f28a8d44a7 100644
--- a/src/test/ui/impl-trait/type_parameters_captured.stderr
+++ b/src/test/ui/impl-trait/type_parameters_captured.stderr
@@ -2,23 +2,13 @@ error[E0310]: the parameter type `T` may not live long enough
   --> $DIR/type_parameters_captured.rs:7:20
    |
 LL | fn foo<T>(x: T) -> impl Any + 'static {
-   |        -           ^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
-   |        |
-   |        help: consider adding an explicit lifetime bound...: `T: 'static`
-
-error[E0310]: the parameter type `T` may not live long enough
-  --> $DIR/type_parameters_captured.rs:7:39
+   |                    ^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
    |
-LL |   fn foo<T>(x: T) -> impl Any + 'static {
-   |  ________-______________________________^
-   | |        |
-   | |        help: consider adding an explicit lifetime bound...: `T: 'static`
-LL | |
-LL | |
-LL | |     x
-LL | | }
-   | |_^ ...so that the type `T` will meet its required lifetime bounds
+LL | fn foo<T: 'static>(x: T) -> impl Any + 'static {
+   |         +++++++++
 
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0310`.
diff --git a/src/test/ui/impl-trait/where-allowed-2.rs b/src/test/ui/impl-trait/where-allowed-2.rs
index d5a87b5d468..1a1210d0072 100644
--- a/src/test/ui/impl-trait/where-allowed-2.rs
+++ b/src/test/ui/impl-trait/where-allowed-2.rs
@@ -1,7 +1,6 @@
 use std::fmt::Debug;
 
-// check-pass
-
 fn in_adt_in_return() -> Vec<impl Debug> { panic!() }
+//~^ ERROR type annotations needed
 
 fn main() {}
diff --git a/src/test/ui/impl-trait/where-allowed-2.stderr b/src/test/ui/impl-trait/where-allowed-2.stderr
new file mode 100644
index 00000000000..2b328c01c87
--- /dev/null
+++ b/src/test/ui/impl-trait/where-allowed-2.stderr
@@ -0,0 +1,9 @@
+error[E0282]: type annotations needed
+  --> $DIR/where-allowed-2.rs:3:30
+   |
+LL | fn in_adt_in_return() -> Vec<impl Debug> { panic!() }
+   |                              ^^^^^^^^^^ cannot infer type
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0282`.
diff --git a/src/test/ui/inference/question-mark-type-infer.stderr b/src/test/ui/inference/question-mark-type-infer.stderr
index e7d5fee1812..9b822714f82 100644
--- a/src/test/ui/inference/question-mark-type-infer.stderr
+++ b/src/test/ui/inference/question-mark-type-infer.stderr
@@ -1,15 +1,9 @@
-error[E0284]: type annotations needed
-  --> $DIR/question-mark-type-infer.rs:10:21
+error[E0282]: type annotations needed
+  --> $DIR/question-mark-type-infer.rs:10:30
    |
 LL |     l.iter().map(f).collect()?
-   |                     ^^^^^^^ cannot infer type
-   |
-   = note: cannot satisfy `<_ as Try>::Residual == _`
-help: consider specifying the type argument in the method call
-   |
-LL |     l.iter().map(f).collect::<B>()?
-   |                            +++++
+   |                              ^ cannot infer type
 
 error: aborting due to previous error
 
-For more information about this error, try `rustc --explain E0284`.
+For more information about this error, try `rustc --explain E0282`.
diff --git a/src/test/ui/intrinsics/unchecked_math_unsafe.thir.stderr b/src/test/ui/intrinsics/unchecked_math_unsafe.thir.stderr
index 26b2f9f2713..5c3728ccdf8 100644
--- a/src/test/ui/intrinsics/unchecked_math_unsafe.thir.stderr
+++ b/src/test/ui/intrinsics/unchecked_math_unsafe.thir.stderr
@@ -1,4 +1,4 @@
-error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+error[E0133]: call to unsafe function `unchecked_add` is unsafe and requires unsafe function or block
   --> $DIR/unchecked_math_unsafe.rs:8:15
    |
 LL |     let add = std::intrinsics::unchecked_add(x, y);
@@ -6,7 +6,7 @@ LL |     let add = std::intrinsics::unchecked_add(x, y);
    |
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
-error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+error[E0133]: call to unsafe function `unchecked_sub` is unsafe and requires unsafe function or block
   --> $DIR/unchecked_math_unsafe.rs:9:15
    |
 LL |     let sub = std::intrinsics::unchecked_sub(x, y);
@@ -14,7 +14,7 @@ LL |     let sub = std::intrinsics::unchecked_sub(x, y);
    |
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
-error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+error[E0133]: call to unsafe function `unchecked_mul` is unsafe and requires unsafe function or block
   --> $DIR/unchecked_math_unsafe.rs:10:15
    |
 LL |     let mul = std::intrinsics::unchecked_mul(x, y);
diff --git a/src/test/ui/issues-71798.rs b/src/test/ui/issues-71798.rs
index fde59f39b1c..14b6c0f3581 100644
--- a/src/test/ui/issues-71798.rs
+++ b/src/test/ui/issues-71798.rs
@@ -1,6 +1,5 @@
 fn test_ref(x: &u32) -> impl std::future::Future<Output = u32> + '_ {
     //~^ ERROR `u32` is not a future
-    //~| ERROR `u32` is not a future
     *x
 }
 
diff --git a/src/test/ui/issues-71798.stderr b/src/test/ui/issues-71798.stderr
index 63669c0513d..ab72c3e41af 100644
--- a/src/test/ui/issues-71798.stderr
+++ b/src/test/ui/issues-71798.stderr
@@ -1,5 +1,5 @@
 error[E0425]: cannot find value `u` in this scope
-  --> $DIR/issues-71798.rs:8:24
+  --> $DIR/issues-71798.rs:7:24
    |
 LL |     let _ = test_ref & u;
    |                        ^ not found in this scope
@@ -13,21 +13,7 @@ LL | fn test_ref(x: &u32) -> impl std::future::Future<Output = u32> + '_ {
    = help: the trait `Future` is not implemented for `u32`
    = note: u32 must be a future or must implement `IntoFuture` to be awaited
 
-error[E0277]: `u32` is not a future
-  --> $DIR/issues-71798.rs:1:69
-   |
-LL |   fn test_ref(x: &u32) -> impl std::future::Future<Output = u32> + '_ {
-   |  _____________________________________________________________________^
-LL | |
-LL | |
-LL | |     *x
-LL | | }
-   | |_^ `u32` is not a future
-   |
-   = help: the trait `Future` is not implemented for `u32`
-   = note: u32 must be a future or must implement `IntoFuture` to be awaited
-
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
 
 Some errors have detailed explanations: E0277, E0425.
 For more information about an error, try `rustc --explain E0277`.
diff --git a/src/test/ui/issues/issue-16922.nll.stderr b/src/test/ui/issues/issue-16922.nll.stderr
index 7f4f5b22eb3..9d9f32a97c0 100644
--- a/src/test/ui/issues/issue-16922.nll.stderr
+++ b/src/test/ui/issues/issue-16922.nll.stderr
@@ -5,6 +5,11 @@ LL | fn foo<T: Any>(value: &T) -> Box<dyn Any> {
    |                       - let's call the lifetime of this reference `'1`
 LL |     Box::new(value) as Box<dyn Any>
    |     ^^^^^^^^^^^^^^^ cast requires that `'1` must outlive `'static`
+   |
+help: to declare that the trait object captures data from argument `value`, you can add an explicit `'_` lifetime bound
+   |
+LL | fn foo<T: Any>(value: &T) -> Box<dyn Any + '_> {
+   |                                          ++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issues/issue-28776.thir.stderr b/src/test/ui/issues/issue-28776.thir.stderr
index 1d470fb5e0f..e3562810b3a 100644
--- a/src/test/ui/issues/issue-28776.thir.stderr
+++ b/src/test/ui/issues/issue-28776.thir.stderr
@@ -1,4 +1,4 @@
-error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+error[E0133]: call to unsafe function `std::ptr::write` is unsafe and requires unsafe function or block
   --> $DIR/issue-28776.rs:7:5
    |
 LL |     (&ptr::write)(1 as *mut _, 42);
diff --git a/src/test/ui/issues/issue-29124.stderr b/src/test/ui/issues/issue-29124.stderr
index 42d89cd01a4..c5d2ec08409 100644
--- a/src/test/ui/issues/issue-29124.stderr
+++ b/src/test/ui/issues/issue-29124.stderr
@@ -2,17 +2,17 @@ error[E0599]: no method named `x` found for fn item `fn() -> Ret {Obj::func}` in
   --> $DIR/issue-29124.rs:15:15
    |
 LL |     Obj::func.x();
-   |               ^ method not found in `fn() -> Ret {Obj::func}`
-   |
-   = note: `Obj::func` is a function, perhaps you wish to call it
+   |     --------- ^ method not found in `fn() -> Ret {Obj::func}`
+   |     |
+   |     this is a function, perhaps you wish to call it
 
 error[E0599]: no method named `x` found for fn item `fn() -> Ret {func}` in the current scope
   --> $DIR/issue-29124.rs:17:10
    |
 LL |     func.x();
-   |          ^ method not found in `fn() -> Ret {func}`
-   |
-   = note: `func` is a function, perhaps you wish to call it
+   |     ---- ^ method not found in `fn() -> Ret {func}`
+   |     |
+   |     this is a function, perhaps you wish to call it
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/issues/issue-30438-c.rs b/src/test/ui/issues/issue-30438-c.rs
index 813c1d3e2cc..4cf634245be 100644
--- a/src/test/ui/issues/issue-30438-c.rs
+++ b/src/test/ui/issues/issue-30438-c.rs
@@ -5,6 +5,7 @@ trait Trait { type Out; }
 struct Test<'a> { s: &'a str }
 
 fn silly<'y, 'z>(_s: &'y Test<'z>) -> &'y <Test<'z> as Trait>::Out where 'z: 'static {
+    //~^ WARN unnecessary lifetime parameter `'z`
     let x = Test { s: "this cannot last" };
     &x
     //~^ ERROR: cannot return reference to local variable `x`
diff --git a/src/test/ui/issues/issue-30438-c.stderr b/src/test/ui/issues/issue-30438-c.stderr
index 7c001088097..a7a5c0500fd 100644
--- a/src/test/ui/issues/issue-30438-c.stderr
+++ b/src/test/ui/issues/issue-30438-c.stderr
@@ -1,9 +1,17 @@
+warning: unnecessary lifetime parameter `'z`
+  --> $DIR/issue-30438-c.rs:7:74
+   |
+LL | fn silly<'y, 'z>(_s: &'y Test<'z>) -> &'y <Test<'z> as Trait>::Out where 'z: 'static {
+   |                                                                          ^^
+   |
+   = help: you can use the `'static` lifetime directly, in place of `'z`
+
 error[E0515]: cannot return reference to local variable `x`
-  --> $DIR/issue-30438-c.rs:9:5
+  --> $DIR/issue-30438-c.rs:10:5
    |
 LL |     &x
    |     ^^ returns a reference to data owned by the current function
 
-error: aborting due to previous error
+error: aborting due to previous error; 1 warning emitted
 
 For more information about this error, try `rustc --explain E0515`.
diff --git a/src/test/ui/issues/issue-3080.thir.stderr b/src/test/ui/issues/issue-3080.thir.stderr
index f395c30b815..4d8acac61d9 100644
--- a/src/test/ui/issues/issue-3080.thir.stderr
+++ b/src/test/ui/issues/issue-3080.thir.stderr
@@ -1,4 +1,4 @@
-error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+error[E0133]: call to unsafe function `X::with` is unsafe and requires unsafe function or block
   --> $DIR/issue-3080.rs:10:5
    |
 LL |     X(()).with();
diff --git a/src/test/ui/issues/issue-32709.stderr b/src/test/ui/issues/issue-32709.stderr
index ed5addcbec5..112cb335932 100644
--- a/src/test/ui/issues/issue-32709.stderr
+++ b/src/test/ui/issues/issue-32709.stderr
@@ -7,7 +7,9 @@ LL |     Err(5)?;
    |           ^ the trait `From<{integer}>` is not implemented for `()`
    |
    = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
-   = help: the trait `FromResidual<Result<Infallible, E>>` is implemented for `Result<T, F>`
+   = help: the following other types implement trait `FromResidual<R>`:
+             <Result<T, F> as FromResidual<Result<Infallible, E>>>
+             <Result<T, F> as FromResidual<Yeet<E>>>
    = note: required because of the requirements on the impl of `FromResidual<Result<Infallible, {integer}>>` for `Result<i32, ()>`
 
 error: aborting due to previous error
diff --git a/src/test/ui/issues/issue-35668.stderr b/src/test/ui/issues/issue-35668.stderr
index 04faea9008a..07409e9834a 100644
--- a/src/test/ui/issues/issue-35668.stderr
+++ b/src/test/ui/issues/issue-35668.stderr
@@ -6,10 +6,10 @@ LL |     a.iter().map(|a| a*a)
    |                      |
    |                      &T
    |
-help: consider restricting type parameter `T`
+help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
    |
-LL | fn func<'a, T: std::ops::Mul<Output = &T>>(a: &'a [T]) -> impl Iterator<Item=&'a T> {
-   |              ++++++++++++++++++++++++++++
+LL | fn func<'a, T>(a: &'a [T]) -> impl Iterator<Item=&'a T> where &T: Mul<&T> {
+   |                                                         +++++++++++++++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issues/issue-57362-1.stderr b/src/test/ui/issues/issue-57362-1.stderr
index 5c611cd43d3..8e19f14009a 100644
--- a/src/test/ui/issues/issue-57362-1.stderr
+++ b/src/test/ui/issues/issue-57362-1.stderr
@@ -2,9 +2,10 @@ error[E0599]: no method named `f` found for fn pointer `fn(&u8)` in the current
   --> $DIR/issue-57362-1.rs:20:7
    |
 LL |     a.f();
-   |       ^ method not found in `fn(&u8)`
+   |     - ^ method not found in `fn(&u8)`
+   |     |
+   |     this is a function, perhaps you wish to call it
    |
-   = note: `a` is a function, perhaps you wish to call it
    = help: items from traits can only be used if the trait is implemented and in scope
 note: `Trait` defines an item `f`, perhaps you need to implement it
   --> $DIR/issue-57362-1.rs:8:1
diff --git a/src/test/ui/issues/issue-5844.thir.stderr b/src/test/ui/issues/issue-5844.thir.stderr
index 6134d6889ff..310a2b593fe 100644
--- a/src/test/ui/issues/issue-5844.thir.stderr
+++ b/src/test/ui/issues/issue-5844.thir.stderr
@@ -1,4 +1,4 @@
-error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+error[E0133]: call to unsafe function `rand` is unsafe and requires unsafe function or block
   --> $DIR/issue-5844.rs:8:5
    |
 LL |     issue_5844_aux::rand();
diff --git a/src/test/ui/iterators/collect-into-array.rs b/src/test/ui/iterators/collect-into-array.rs
new file mode 100644
index 00000000000..a1144c8cb8c
--- /dev/null
+++ b/src/test/ui/iterators/collect-into-array.rs
@@ -0,0 +1,7 @@
+fn main() {
+    //~^ NOTE required by a bound in this
+    let whatever: [u32; 10] = (0..10).collect();
+    //~^ ERROR an array of type `[u32; 10]` cannot be built directly from an iterator
+    //~| NOTE try collecting into a `Vec<{integer}>`, then using `.try_into()`
+    //~| NOTE required by a bound in `collect`
+}
diff --git a/src/test/ui/iterators/collect-into-array.stderr b/src/test/ui/iterators/collect-into-array.stderr
new file mode 100644
index 00000000000..7be53a4873b
--- /dev/null
+++ b/src/test/ui/iterators/collect-into-array.stderr
@@ -0,0 +1,16 @@
+error[E0277]: an array of type `[u32; 10]` cannot be built directly from an iterator
+  --> $DIR/collect-into-array.rs:3:39
+   |
+LL |     let whatever: [u32; 10] = (0..10).collect();
+   |                                       ^^^^^^^ try collecting into a `Vec<{integer}>`, then using `.try_into()`
+   |
+   = help: the trait `FromIterator<{integer}>` is not implemented for `[u32; 10]`
+note: required by a bound in `collect`
+  --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
+   |
+LL |     fn collect<B: FromIterator<Self::Item>>(self) -> B
+   |                   ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `collect`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/iterators/collect-into-slice.rs b/src/test/ui/iterators/collect-into-slice.rs
index 905752dec74..aafa6bc8b95 100644
--- a/src/test/ui/iterators/collect-into-slice.rs
+++ b/src/test/ui/iterators/collect-into-slice.rs
@@ -6,7 +6,7 @@ fn process_slice(data: &[i32]) {
 fn main() {
     let some_generated_vec = (0..10).collect();
     //~^ ERROR the size for values of type `[i32]` cannot be known at compilation time
-    //~| ERROR a value of type `[i32]` cannot be built since `[i32]` has no definite size
+    //~| ERROR a slice of type `[i32]` cannot be built since `[i32]` has no definite size
     //~| NOTE try explicitly collecting into a `Vec<{integer}>`
     //~| NOTE required by a bound in `collect`
     //~| NOTE all local variables must have a statically known size
diff --git a/src/test/ui/iterators/collect-into-slice.stderr b/src/test/ui/iterators/collect-into-slice.stderr
index 521f239451d..4842e65fe97 100644
--- a/src/test/ui/iterators/collect-into-slice.stderr
+++ b/src/test/ui/iterators/collect-into-slice.stderr
@@ -8,7 +8,7 @@ LL |     let some_generated_vec = (0..10).collect();
    = note: all local variables must have a statically known size
    = help: unsized locals are gated as an unstable feature
 
-error[E0277]: a value of type `[i32]` cannot be built since `[i32]` has no definite size
+error[E0277]: a slice of type `[i32]` cannot be built since `[i32]` has no definite size
   --> $DIR/collect-into-slice.rs:7:38
    |
 LL |     let some_generated_vec = (0..10).collect();
diff --git a/src/test/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.rs b/src/test/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.rs
new file mode 100644
index 00000000000..89387e01ba5
--- /dev/null
+++ b/src/test/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.rs
@@ -0,0 +1,54 @@
+// normalize-stderr-test "pref: Align \{\n *pow2: [1-3],\n *\}" -> "pref: $$PREF_ALIGN"
+#![crate_type = "lib"]
+#![feature(rustc_attrs)]
+
+use std::mem::MaybeUninit;
+
+enum HasNiche {
+    A,
+    B,
+    C,
+}
+
+// This should result in ScalarPair(Initialized, Union),
+// since the u8 payload will be uninit for `None`.
+#[rustc_layout(debug)]
+pub enum MissingPayloadField { //~ ERROR: layout_of
+    Some(u8),
+    None
+}
+
+// This should result in ScalarPair(Initialized, Initialized),
+// since the u8 field is present in all variants,
+// and hence will always be initialized.
+#[rustc_layout(debug)]
+pub enum CommonPayloadField { //~ ERROR: layout_of
+    A(u8),
+    B(u8),
+}
+
+// This should result in ScalarPair(Initialized, Union),
+// since, though a u8-sized field is present in all variants, it might be uninit.
+#[rustc_layout(debug)]
+pub enum CommonPayloadFieldIsMaybeUninit { //~ ERROR: layout_of
+    A(u8),
+    B(MaybeUninit<u8>),
+}
+
+// This should result in ScalarPair(Initialized, Union),
+// since only the niche field (used for the tag) is guaranteed to be initialized.
+#[rustc_layout(debug)]
+pub enum NicheFirst { //~ ERROR: layout_of
+    A(HasNiche, u8),
+    B,
+    C
+}
+
+// This should result in ScalarPair(Union, Initialized),
+// since only the niche field (used for the tag) is guaranteed to be initialized.
+#[rustc_layout(debug)]
+pub enum NicheSecond { //~ ERROR: layout_of
+    A(u8, HasNiche),
+    B,
+    C,
+}
diff --git a/src/test/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.stderr b/src/test/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.stderr
new file mode 100644
index 00000000000..46187aae304
--- /dev/null
+++ b/src/test/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.stderr
@@ -0,0 +1,720 @@
+error: layout_of(MissingPayloadField) = Layout {
+           fields: Arbitrary {
+               offsets: [
+                   Size {
+                       raw: 0,
+                   },
+               ],
+               memory_index: [
+                   0,
+               ],
+           },
+           variants: Multiple {
+               tag: Initialized {
+                   value: Int(
+                       I8,
+                       false,
+                   ),
+                   valid_range: 0..=1,
+               },
+               tag_encoding: Direct,
+               tag_field: 0,
+               variants: [
+                   Layout {
+                       fields: Arbitrary {
+                           offsets: [
+                               Size {
+                                   raw: 1,
+                               },
+                           ],
+                           memory_index: [
+                               0,
+                           ],
+                       },
+                       variants: Single {
+                           index: 0,
+                       },
+                       abi: Aggregate {
+                           sized: true,
+                       },
+                       largest_niche: None,
+                       align: AbiAndPrefAlign {
+                           abi: Align {
+                               pow2: 0,
+                           },
+                           pref: $PREF_ALIGN,
+                       },
+                       size: Size {
+                           raw: 2,
+                       },
+                   },
+                   Layout {
+                       fields: Arbitrary {
+                           offsets: [],
+                           memory_index: [],
+                       },
+                       variants: Single {
+                           index: 1,
+                       },
+                       abi: Aggregate {
+                           sized: true,
+                       },
+                       largest_niche: None,
+                       align: AbiAndPrefAlign {
+                           abi: Align {
+                               pow2: 0,
+                           },
+                           pref: $PREF_ALIGN,
+                       },
+                       size: Size {
+                           raw: 1,
+                       },
+                   },
+               ],
+           },
+           abi: ScalarPair(
+               Initialized {
+                   value: Int(
+                       I8,
+                       false,
+                   ),
+                   valid_range: 0..=1,
+               },
+               Union {
+                   value: Int(
+                       I8,
+                       false,
+                   ),
+               },
+           ),
+           largest_niche: Some(
+               Niche {
+                   offset: Size {
+                       raw: 0,
+                   },
+                   value: Int(
+                       I8,
+                       false,
+                   ),
+                   valid_range: 0..=1,
+               },
+           ),
+           align: AbiAndPrefAlign {
+               abi: Align {
+                   pow2: 0,
+               },
+               pref: $PREF_ALIGN,
+           },
+           size: Size {
+               raw: 2,
+           },
+       }
+  --> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:16:1
+   |
+LL | / pub enum MissingPayloadField {
+LL | |     Some(u8),
+LL | |     None
+LL | | }
+   | |_^
+
+error: layout_of(CommonPayloadField) = Layout {
+           fields: Arbitrary {
+               offsets: [
+                   Size {
+                       raw: 0,
+                   },
+               ],
+               memory_index: [
+                   0,
+               ],
+           },
+           variants: Multiple {
+               tag: Initialized {
+                   value: Int(
+                       I8,
+                       false,
+                   ),
+                   valid_range: 0..=1,
+               },
+               tag_encoding: Direct,
+               tag_field: 0,
+               variants: [
+                   Layout {
+                       fields: Arbitrary {
+                           offsets: [
+                               Size {
+                                   raw: 1,
+                               },
+                           ],
+                           memory_index: [
+                               0,
+                           ],
+                       },
+                       variants: Single {
+                           index: 0,
+                       },
+                       abi: Aggregate {
+                           sized: true,
+                       },
+                       largest_niche: None,
+                       align: AbiAndPrefAlign {
+                           abi: Align {
+                               pow2: 0,
+                           },
+                           pref: $PREF_ALIGN,
+                       },
+                       size: Size {
+                           raw: 2,
+                       },
+                   },
+                   Layout {
+                       fields: Arbitrary {
+                           offsets: [
+                               Size {
+                                   raw: 1,
+                               },
+                           ],
+                           memory_index: [
+                               0,
+                           ],
+                       },
+                       variants: Single {
+                           index: 1,
+                       },
+                       abi: Aggregate {
+                           sized: true,
+                       },
+                       largest_niche: None,
+                       align: AbiAndPrefAlign {
+                           abi: Align {
+                               pow2: 0,
+                           },
+                           pref: $PREF_ALIGN,
+                       },
+                       size: Size {
+                           raw: 2,
+                       },
+                   },
+               ],
+           },
+           abi: ScalarPair(
+               Initialized {
+                   value: Int(
+                       I8,
+                       false,
+                   ),
+                   valid_range: 0..=1,
+               },
+               Initialized {
+                   value: Int(
+                       I8,
+                       false,
+                   ),
+                   valid_range: 0..=255,
+               },
+           ),
+           largest_niche: Some(
+               Niche {
+                   offset: Size {
+                       raw: 0,
+                   },
+                   value: Int(
+                       I8,
+                       false,
+                   ),
+                   valid_range: 0..=1,
+               },
+           ),
+           align: AbiAndPrefAlign {
+               abi: Align {
+                   pow2: 0,
+               },
+               pref: $PREF_ALIGN,
+           },
+           size: Size {
+               raw: 2,
+           },
+       }
+  --> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:25:1
+   |
+LL | / pub enum CommonPayloadField {
+LL | |     A(u8),
+LL | |     B(u8),
+LL | | }
+   | |_^
+
+error: layout_of(CommonPayloadFieldIsMaybeUninit) = Layout {
+           fields: Arbitrary {
+               offsets: [
+                   Size {
+                       raw: 0,
+                   },
+               ],
+               memory_index: [
+                   0,
+               ],
+           },
+           variants: Multiple {
+               tag: Initialized {
+                   value: Int(
+                       I8,
+                       false,
+                   ),
+                   valid_range: 0..=1,
+               },
+               tag_encoding: Direct,
+               tag_field: 0,
+               variants: [
+                   Layout {
+                       fields: Arbitrary {
+                           offsets: [
+                               Size {
+                                   raw: 1,
+                               },
+                           ],
+                           memory_index: [
+                               0,
+                           ],
+                       },
+                       variants: Single {
+                           index: 0,
+                       },
+                       abi: Aggregate {
+                           sized: true,
+                       },
+                       largest_niche: None,
+                       align: AbiAndPrefAlign {
+                           abi: Align {
+                               pow2: 0,
+                           },
+                           pref: $PREF_ALIGN,
+                       },
+                       size: Size {
+                           raw: 2,
+                       },
+                   },
+                   Layout {
+                       fields: Arbitrary {
+                           offsets: [
+                               Size {
+                                   raw: 1,
+                               },
+                           ],
+                           memory_index: [
+                               0,
+                           ],
+                       },
+                       variants: Single {
+                           index: 1,
+                       },
+                       abi: Aggregate {
+                           sized: true,
+                       },
+                       largest_niche: None,
+                       align: AbiAndPrefAlign {
+                           abi: Align {
+                               pow2: 0,
+                           },
+                           pref: $PREF_ALIGN,
+                       },
+                       size: Size {
+                           raw: 2,
+                       },
+                   },
+               ],
+           },
+           abi: ScalarPair(
+               Initialized {
+                   value: Int(
+                       I8,
+                       false,
+                   ),
+                   valid_range: 0..=1,
+               },
+               Union {
+                   value: Int(
+                       I8,
+                       false,
+                   ),
+               },
+           ),
+           largest_niche: Some(
+               Niche {
+                   offset: Size {
+                       raw: 0,
+                   },
+                   value: Int(
+                       I8,
+                       false,
+                   ),
+                   valid_range: 0..=1,
+               },
+           ),
+           align: AbiAndPrefAlign {
+               abi: Align {
+                   pow2: 0,
+               },
+               pref: $PREF_ALIGN,
+           },
+           size: Size {
+               raw: 2,
+           },
+       }
+  --> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:33:1
+   |
+LL | / pub enum CommonPayloadFieldIsMaybeUninit {
+LL | |     A(u8),
+LL | |     B(MaybeUninit<u8>),
+LL | | }
+   | |_^
+
+error: layout_of(NicheFirst) = Layout {
+           fields: Arbitrary {
+               offsets: [
+                   Size {
+                       raw: 0,
+                   },
+               ],
+               memory_index: [
+                   0,
+               ],
+           },
+           variants: Multiple {
+               tag: Initialized {
+                   value: Int(
+                       I8,
+                       false,
+                   ),
+                   valid_range: 0..=4,
+               },
+               tag_encoding: Niche {
+                   dataful_variant: 0,
+                   niche_variants: 1..=2,
+                   niche_start: 3,
+               },
+               tag_field: 0,
+               variants: [
+                   Layout {
+                       fields: Arbitrary {
+                           offsets: [
+                               Size {
+                                   raw: 0,
+                               },
+                               Size {
+                                   raw: 1,
+                               },
+                           ],
+                           memory_index: [
+                               0,
+                               1,
+                           ],
+                       },
+                       variants: Single {
+                           index: 0,
+                       },
+                       abi: ScalarPair(
+                           Initialized {
+                               value: Int(
+                                   I8,
+                                   false,
+                               ),
+                               valid_range: 0..=2,
+                           },
+                           Initialized {
+                               value: Int(
+                                   I8,
+                                   false,
+                               ),
+                               valid_range: 0..=255,
+                           },
+                       ),
+                       largest_niche: Some(
+                           Niche {
+                               offset: Size {
+                                   raw: 0,
+                               },
+                               value: Int(
+                                   I8,
+                                   false,
+                               ),
+                               valid_range: 0..=2,
+                           },
+                       ),
+                       align: AbiAndPrefAlign {
+                           abi: Align {
+                               pow2: 0,
+                           },
+                           pref: $PREF_ALIGN,
+                       },
+                       size: Size {
+                           raw: 2,
+                       },
+                   },
+                   Layout {
+                       fields: Arbitrary {
+                           offsets: [],
+                           memory_index: [],
+                       },
+                       variants: Single {
+                           index: 1,
+                       },
+                       abi: Aggregate {
+                           sized: true,
+                       },
+                       largest_niche: None,
+                       align: AbiAndPrefAlign {
+                           abi: Align {
+                               pow2: 0,
+                           },
+                           pref: $PREF_ALIGN,
+                       },
+                       size: Size {
+                           raw: 0,
+                       },
+                   },
+                   Layout {
+                       fields: Arbitrary {
+                           offsets: [],
+                           memory_index: [],
+                       },
+                       variants: Single {
+                           index: 2,
+                       },
+                       abi: Aggregate {
+                           sized: true,
+                       },
+                       largest_niche: None,
+                       align: AbiAndPrefAlign {
+                           abi: Align {
+                               pow2: 0,
+                           },
+                           pref: $PREF_ALIGN,
+                       },
+                       size: Size {
+                           raw: 0,
+                       },
+                   },
+               ],
+           },
+           abi: ScalarPair(
+               Initialized {
+                   value: Int(
+                       I8,
+                       false,
+                   ),
+                   valid_range: 0..=4,
+               },
+               Union {
+                   value: Int(
+                       I8,
+                       false,
+                   ),
+               },
+           ),
+           largest_niche: Some(
+               Niche {
+                   offset: Size {
+                       raw: 0,
+                   },
+                   value: Int(
+                       I8,
+                       false,
+                   ),
+                   valid_range: 0..=4,
+               },
+           ),
+           align: AbiAndPrefAlign {
+               abi: Align {
+                   pow2: 0,
+               },
+               pref: $PREF_ALIGN,
+           },
+           size: Size {
+               raw: 2,
+           },
+       }
+  --> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:41:1
+   |
+LL | / pub enum NicheFirst {
+LL | |     A(HasNiche, u8),
+LL | |     B,
+LL | |     C
+LL | | }
+   | |_^
+
+error: layout_of(NicheSecond) = Layout {
+           fields: Arbitrary {
+               offsets: [
+                   Size {
+                       raw: 1,
+                   },
+               ],
+               memory_index: [
+                   0,
+               ],
+           },
+           variants: Multiple {
+               tag: Initialized {
+                   value: Int(
+                       I8,
+                       false,
+                   ),
+                   valid_range: 0..=4,
+               },
+               tag_encoding: Niche {
+                   dataful_variant: 0,
+                   niche_variants: 1..=2,
+                   niche_start: 3,
+               },
+               tag_field: 0,
+               variants: [
+                   Layout {
+                       fields: Arbitrary {
+                           offsets: [
+                               Size {
+                                   raw: 0,
+                               },
+                               Size {
+                                   raw: 1,
+                               },
+                           ],
+                           memory_index: [
+                               0,
+                               1,
+                           ],
+                       },
+                       variants: Single {
+                           index: 0,
+                       },
+                       abi: ScalarPair(
+                           Initialized {
+                               value: Int(
+                                   I8,
+                                   false,
+                               ),
+                               valid_range: 0..=255,
+                           },
+                           Initialized {
+                               value: Int(
+                                   I8,
+                                   false,
+                               ),
+                               valid_range: 0..=2,
+                           },
+                       ),
+                       largest_niche: Some(
+                           Niche {
+                               offset: Size {
+                                   raw: 1,
+                               },
+                               value: Int(
+                                   I8,
+                                   false,
+                               ),
+                               valid_range: 0..=2,
+                           },
+                       ),
+                       align: AbiAndPrefAlign {
+                           abi: Align {
+                               pow2: 0,
+                           },
+                           pref: $PREF_ALIGN,
+                       },
+                       size: Size {
+                           raw: 2,
+                       },
+                   },
+                   Layout {
+                       fields: Arbitrary {
+                           offsets: [],
+                           memory_index: [],
+                       },
+                       variants: Single {
+                           index: 1,
+                       },
+                       abi: Aggregate {
+                           sized: true,
+                       },
+                       largest_niche: None,
+                       align: AbiAndPrefAlign {
+                           abi: Align {
+                               pow2: 0,
+                           },
+                           pref: $PREF_ALIGN,
+                       },
+                       size: Size {
+                           raw: 0,
+                       },
+                   },
+                   Layout {
+                       fields: Arbitrary {
+                           offsets: [],
+                           memory_index: [],
+                       },
+                       variants: Single {
+                           index: 2,
+                       },
+                       abi: Aggregate {
+                           sized: true,
+                       },
+                       largest_niche: None,
+                       align: AbiAndPrefAlign {
+                           abi: Align {
+                               pow2: 0,
+                           },
+                           pref: $PREF_ALIGN,
+                       },
+                       size: Size {
+                           raw: 0,
+                       },
+                   },
+               ],
+           },
+           abi: ScalarPair(
+               Union {
+                   value: Int(
+                       I8,
+                       false,
+                   ),
+               },
+               Initialized {
+                   value: Int(
+                       I8,
+                       false,
+                   ),
+                   valid_range: 0..=4,
+               },
+           ),
+           largest_niche: Some(
+               Niche {
+                   offset: Size {
+                       raw: 1,
+                   },
+                   value: Int(
+                       I8,
+                       false,
+                   ),
+                   valid_range: 0..=4,
+               },
+           ),
+           align: AbiAndPrefAlign {
+               abi: Align {
+                   pow2: 0,
+               },
+               pref: $PREF_ALIGN,
+           },
+           size: Size {
+               raw: 2,
+           },
+       }
+  --> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:50:1
+   |
+LL | / pub enum NicheSecond {
+LL | |     A(u8, HasNiche),
+LL | |     B,
+LL | |     C,
+LL | | }
+   | |_^
+
+error: aborting due to 5 previous errors
+
diff --git a/src/test/ui/lifetimes/copy_modulo_regions.rs b/src/test/ui/lifetimes/copy_modulo_regions.rs
new file mode 100644
index 00000000000..1d5d90ffcb4
--- /dev/null
+++ b/src/test/ui/lifetimes/copy_modulo_regions.rs
@@ -0,0 +1,19 @@
+#![feature(nll)]
+
+#[derive(Clone)]
+struct Foo<'a>(fn(&'a ()) -> &'a ());
+
+impl Copy for Foo<'static> {}
+
+fn mk_foo<'a>() -> Foo<'a> {
+    println!("mk_foo");
+    Foo(|x| x)
+}
+
+fn foo<'a>() -> [Foo<'a>; 100] {
+    [mk_foo::<'a>(); 100] //~ ERROR lifetime may not live long enough
+}
+
+fn main() {
+    foo();
+}
diff --git a/src/test/ui/lifetimes/copy_modulo_regions.stderr b/src/test/ui/lifetimes/copy_modulo_regions.stderr
new file mode 100644
index 00000000000..e027bc45426
--- /dev/null
+++ b/src/test/ui/lifetimes/copy_modulo_regions.stderr
@@ -0,0 +1,14 @@
+error: lifetime may not live long enough
+  --> $DIR/copy_modulo_regions.rs:14:5
+   |
+LL | fn foo<'a>() -> [Foo<'a>; 100] {
+   |        -- lifetime `'a` defined here
+LL |     [mk_foo::<'a>(); 100]
+   |     ^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static`
+   |
+   = note: requirement occurs because of the type `Foo<'_>`, which makes the generic argument `'_` invariant
+   = note: the struct `Foo<'a>` is invariant over the parameter `'a`
+   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/lifetimes/elided-lifetime-in-path-in-impl-Fn.rs b/src/test/ui/lifetimes/elided-lifetime-in-path-in-impl-Fn.rs
new file mode 100644
index 00000000000..9c9965d8fb8
--- /dev/null
+++ b/src/test/ui/lifetimes/elided-lifetime-in-path-in-impl-Fn.rs
@@ -0,0 +1,19 @@
+// check-pass
+
+struct Foo<'a>(&'a ());
+
+fn with_fn() -> fn(Foo) {
+    |_| ()
+}
+
+fn with_impl_fn() -> impl Fn(Foo) {
+    |_| ()
+}
+
+fn with_where_fn<T>()
+where
+    T: Fn(Foo),
+{
+}
+
+fn main() {}
diff --git a/src/test/ui/lifetimes/issue-90170-elision-mismatch.nll.stderr b/src/test/ui/lifetimes/issue-90170-elision-mismatch.nll.stderr
index a5bc7450bbf..48fb3fb4a22 100644
--- a/src/test/ui/lifetimes/issue-90170-elision-mismatch.nll.stderr
+++ b/src/test/ui/lifetimes/issue-90170-elision-mismatch.nll.stderr
@@ -6,6 +6,11 @@ LL | pub fn foo(x: &mut Vec<&u8>, y: &u8) { x.push(y); }
    |                        |        |
    |                        |        let's call the lifetime of this reference `'1`
    |                        let's call the lifetime of this reference `'2`
+   |
+help: consider introducing a named lifetime parameter
+   |
+LL | pub fn foo<'a>(x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); }
+   |           ++++              ++          ++
 
 error: lifetime may not live long enough
   --> $DIR/issue-90170-elision-mismatch.rs:5:44
@@ -15,6 +20,11 @@ LL | pub fn foo2(x: &mut Vec<&'_ u8>, y: &u8) { x.push(y); }
    |                         |           |
    |                         |           let's call the lifetime of this reference `'1`
    |                         let's call the lifetime of this reference `'2`
+   |
+help: consider introducing a named lifetime parameter
+   |
+LL | pub fn foo2<'a>(x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); }
+   |            ++++              ~~          ++
 
 error: lifetime may not live long enough
   --> $DIR/issue-90170-elision-mismatch.rs:7:63
@@ -24,6 +34,11 @@ LL | pub fn foo3<'a>(_other: &'a [u8], x: &mut Vec<&u8>, y: &u8) { x.push(y); }
    |                                               |        |
    |                                               |        let's call the lifetime of this reference `'1`
    |                                               let's call the lifetime of this reference `'2`
+   |
+help: consider introducing a named lifetime parameter
+   |
+LL | pub fn foo3<'a>(_other: &'a [u8], x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); }
+   |                                                ++          ++
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.stderr b/src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.stderr
index e5083e3a088..33f6c498b6f 100644
--- a/src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.stderr
+++ b/src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.stderr
@@ -1,18 +1,24 @@
 error[E0310]: the parameter type `T` may not live long enough
   --> $DIR/lifetime-doesnt-live-long-enough.rs:19:10
    |
-LL | struct Foo<T> {
-   |            - help: consider adding an explicit lifetime bound...: `T: 'static`
 LL |     foo: &'static T
    |          ^^^^^^^^^^ ...so that the reference type `&'static T` does not outlive the data it points at
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL | struct Foo<T: 'static> {
+   |             +++++++++
 
 error[E0309]: the parameter type `K` may not live long enough
   --> $DIR/lifetime-doesnt-live-long-enough.rs:24:19
    |
-LL | trait X<K>: Sized {
-   |         - help: consider adding an explicit lifetime bound...: `K: 'a`
 LL |     fn foo<'a, L: X<&'a Nested<K>>>();
    |                   ^^^^^^^^^^^^^^^^ ...so that the reference type `&'a Nested<K>` does not outlive the data it points at
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL | trait X<K: 'a>: Sized {
+   |          ++++
 
 error[E0309]: the parameter type `Self` may not live long enough
   --> $DIR/lifetime-doesnt-live-long-enough.rs:28:19
@@ -27,25 +33,34 @@ error[E0309]: the parameter type `L` may not live long enough
   --> $DIR/lifetime-doesnt-live-long-enough.rs:32:22
    |
 LL |     fn baz<'a, L, M: X<&'a Nested<L>>>() {
-   |                -     ^^^^^^^^^^^^^^^^ ...so that the reference type `&'a Nested<L>` does not outlive the data it points at
-   |                |
-   |                help: consider adding an explicit lifetime bound...: `L: 'a`
+   |                      ^^^^^^^^^^^^^^^^ ...so that the reference type `&'a Nested<L>` does not outlive the data it points at
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL |     fn baz<'a, L: 'a, M: X<&'a Nested<L>>>() {
+   |                 ++++
 
 error[E0309]: the parameter type `K` may not live long enough
   --> $DIR/lifetime-doesnt-live-long-enough.rs:41:33
    |
-LL | impl<K> Nested<K> {
-   |      - help: consider adding an explicit lifetime bound...: `K: 'a`
 LL |     fn generic_in_parent<'a, L: X<&'a Nested<K>>>() {
    |                                 ^^^^^^^^^^^^^^^^ ...so that the reference type `&'a Nested<K>` does not outlive the data it points at
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL | impl<K: 'a> Nested<K> {
+   |       ++++
 
 error[E0309]: the parameter type `M` may not live long enough
   --> $DIR/lifetime-doesnt-live-long-enough.rs:44:36
    |
 LL |     fn generic_in_child<'a, 'b, L: X<&'a Nested<M>>, M: 'b>() {
-   |                                    ^^^^^^^^^^^^^^^^  -- help: consider adding an explicit lifetime bound...: `M: 'a +`
-   |                                    |
-   |                                    ...so that the reference type `&'a Nested<M>` does not outlive the data it points at
+   |                                    ^^^^^^^^^^^^^^^^ ...so that the reference type `&'a Nested<M>` does not outlive the data it points at
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL |     fn generic_in_child<'a, 'b, L: X<&'a Nested<M>>, M: 'b + 'a>() {
+   |                                                            ++++
 
 error: aborting due to 6 previous errors
 
diff --git a/src/test/ui/lifetimes/lifetime-elision-return-type-trait.rs b/src/test/ui/lifetimes/lifetime-elision-return-type-trait.rs
index 133679f30f8..5168cb20d9e 100644
--- a/src/test/ui/lifetimes/lifetime-elision-return-type-trait.rs
+++ b/src/test/ui/lifetimes/lifetime-elision-return-type-trait.rs
@@ -7,7 +7,6 @@ use std::error::Error;
 
 fn foo() -> impl Future<Item=(), Error=Box<dyn Error>> {
     //~^ ERROR not satisfied
-    //~| ERROR not satisfied
     Ok(())
 }
 
diff --git a/src/test/ui/lifetimes/lifetime-elision-return-type-trait.stderr b/src/test/ui/lifetimes/lifetime-elision-return-type-trait.stderr
index a3badd7b25a..ef1127c59ac 100644
--- a/src/test/ui/lifetimes/lifetime-elision-return-type-trait.stderr
+++ b/src/test/ui/lifetimes/lifetime-elision-return-type-trait.stderr
@@ -4,17 +4,6 @@ error[E0277]: the trait bound `Result<(), _>: Future` is not satisfied
 LL | fn foo() -> impl Future<Item=(), Error=Box<dyn Error>> {
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Future` is not implemented for `Result<(), _>`
 
-error[E0277]: the trait bound `Result<(), _>: Future` is not satisfied
-  --> $DIR/lifetime-elision-return-type-trait.rs:8:56
-   |
-LL |   fn foo() -> impl Future<Item=(), Error=Box<dyn Error>> {
-   |  ________________________________________________________^
-LL | |
-LL | |
-LL | |     Ok(())
-LL | | }
-   | |_^ the trait `Future` is not implemented for `Result<(), _>`
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-2.nll.stderr b/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-2.nll.stderr
index a94f9a79906..5a23f1e0e9d 100644
--- a/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-2.nll.stderr
+++ b/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-2.nll.stderr
@@ -7,6 +7,11 @@ LL | fn foo(&mut (ref mut v, w): &mut (&u8, &u8), x: &u8) {
    |                                   let's call the lifetime of this reference `'2`
 LL |     *v = x;
    |     ^^^^^^ assignment requires that `'1` must outlive `'2`
+   |
+help: consider introducing a named lifetime parameter
+   |
+LL | fn foo<'a>(&mut (ref mut v, w): &mut (&'a u8, &u8), x: &'a u8) {
+   |       ++++                             ++               ++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-3.nll.stderr b/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-3.nll.stderr
index 2ed4d6d4401..6ba130308a3 100644
--- a/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-3.nll.stderr
+++ b/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-3.nll.stderr
@@ -7,6 +7,11 @@ LL | fn foo(z: &mut Vec<(&u8,&u8)>, (x, y): (&u8, &u8)) {
    |                     let's call the lifetime of this reference `'2`
 LL |     z.push((x,y));
    |     ^^^^^^^^^^^^^ argument requires that `'1` must outlive `'2`
+   |
+help: consider introducing a named lifetime parameter
+   |
+LL | fn foo<'a>(z: &mut Vec<(&'a u8,&u8)>, (x, y): (&'a u8, &u8)) {
+   |       ++++               ++                     ++
 
 error: lifetime may not live long enough
   --> $DIR/ex3-both-anon-regions-3.rs:2:5
@@ -17,6 +22,11 @@ LL | fn foo(z: &mut Vec<(&u8,&u8)>, (x, y): (&u8, &u8)) {
    |                         let's call the lifetime of this reference `'4`
 LL |     z.push((x,y));
    |     ^^^^^^^^^^^^^ argument requires that `'3` must outlive `'4`
+   |
+help: consider introducing a named lifetime parameter
+   |
+LL | fn foo<'a>(z: &mut Vec<(&u8,&'a u8)>, (x, y): (&u8, &'a u8)) {
+   |       ++++                   ++                      ++
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-return-type-is-anon.nll.stderr b/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-return-type-is-anon.nll.stderr
index 1a19e81f235..5601335d275 100644
--- a/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-return-type-is-anon.nll.stderr
+++ b/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-return-type-is-anon.nll.stderr
@@ -7,6 +7,11 @@ LL |   fn foo<'a>(&self, x: &i32) -> &i32 {
    |              let's call the lifetime of this reference `'2`
 LL |     x
    |     ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+   |
+help: consider introducing a named lifetime parameter and update trait if needed
+   |
+LL |   fn foo<'a>(&'a self, x: &'a i32) -> &i32 {
+   |               ++           ++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-self-is-anon.nll.stderr b/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-self-is-anon.nll.stderr
index 87b13dc1591..e221902c4a9 100644
--- a/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-self-is-anon.nll.stderr
+++ b/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-self-is-anon.nll.stderr
@@ -7,6 +7,11 @@ LL |     fn foo<'a>(&self, x: &Foo) -> &Foo {
    |                let's call the lifetime of this reference `'2`
 LL |         if true { x } else { self }
    |                   ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+   |
+help: consider introducing a named lifetime parameter and update trait if needed
+   |
+LL |     fn foo<'a>(&'a self, x: &'a Foo) -> &Foo {
+   |                 ++           ++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-fn-items.nll.stderr b/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-fn-items.nll.stderr
index 825c45b2434..a909c5fa823 100644
--- a/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-fn-items.nll.stderr
+++ b/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-fn-items.nll.stderr
@@ -7,6 +7,11 @@ LL | fn foo(x:fn(&u8, &u8), y: Vec<&u8>, z: &u8) {
    |                               let's call the lifetime of this reference `'2`
 LL |   y.push(z);
    |   ^^^^^^^^^ argument requires that `'1` must outlive `'2`
+   |
+help: consider introducing a named lifetime parameter
+   |
+LL | fn foo<'a>(x:fn(&u8, &u8), y: Vec<&'a u8>, z: &'a u8) {
+   |       ++++                         ++          ++
 
 error[E0596]: cannot borrow `y` as mutable, as it is not declared as mutable
   --> $DIR/ex3-both-anon-regions-using-fn-items.rs:2:3
diff --git a/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-impl-items.nll.stderr b/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-impl-items.nll.stderr
index f3502674849..9661f1e5144 100644
--- a/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-impl-items.nll.stderr
+++ b/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-impl-items.nll.stderr
@@ -7,6 +7,11 @@ LL |     fn foo(x: &mut Vec<&u8>, y: &u8) {
    |                        let's call the lifetime of this reference `'2`
 LL |         x.push(y);
    |         ^^^^^^^^^ argument requires that `'1` must outlive `'2`
+   |
+help: consider introducing a named lifetime parameter and update trait if needed
+   |
+LL |     fn foo<'a>(x: &mut Vec<&'a u8>, y: &'a u8) {
+   |           ++++              ++          ++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-trait-objects.nll.stderr b/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-trait-objects.nll.stderr
index 78a828dde86..cce0a31bfbb 100644
--- a/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-trait-objects.nll.stderr
+++ b/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-trait-objects.nll.stderr
@@ -7,6 +7,11 @@ LL | fn foo(x:Box<dyn Fn(&u8, &u8)> , y: Vec<&u8>, z: &u8) {
    |                                         let's call the lifetime of this reference `'2`
 LL |   y.push(z);
    |   ^^^^^^^^^ argument requires that `'1` must outlive `'2`
+   |
+help: consider introducing a named lifetime parameter
+   |
+LL | fn foo<'a>(x:Box<dyn Fn(&'a u8, &'a u8)> , y: Vec<&u8>, z: &u8) {
+   |       ++++               ++      ++
 
 error[E0596]: cannot borrow `y` as mutable, as it is not declared as mutable
   --> $DIR/ex3-both-anon-regions-using-trait-objects.rs:2:3
diff --git a/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions.nll.stderr b/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions.nll.stderr
index 6989acfa196..ec9fac0c288 100644
--- a/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions.nll.stderr
+++ b/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions.nll.stderr
@@ -7,6 +7,11 @@ LL | fn foo(x: &mut Vec<&u8>, y: &u8) {
    |                    let's call the lifetime of this reference `'2`
 LL |     x.push(y);
    |     ^^^^^^^^^ argument requires that `'1` must outlive `'2`
+   |
+help: consider introducing a named lifetime parameter
+   |
+LL | fn foo<'a>(x: &mut Vec<&'a u8>, y: &'a u8) {
+   |       ++++              ++          ++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/lifetimes/lifetime-errors/issue_74400.nll.stderr b/src/test/ui/lifetimes/lifetime-errors/issue_74400.nll.stderr
index 5509226cb1c..2906c05864b 100644
--- a/src/test/ui/lifetimes/lifetime-errors/issue_74400.nll.stderr
+++ b/src/test/ui/lifetimes/lifetime-errors/issue_74400.nll.stderr
@@ -2,9 +2,12 @@ error[E0310]: the parameter type `T` may not live long enough
   --> $DIR/issue_74400.rs:12:5
    |
 LL |     f(data, identity)
-   |     ^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
    |
-   = help: consider adding an explicit lifetime bound `T: 'static`...
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn g<T: 'static>(data: &[T]) {
+   |       +++++++++
 
 error[E0308]: mismatched types
   --> $DIR/issue_74400.rs:12:5
diff --git a/src/test/ui/issues/issue-1866.rs b/src/test/ui/lint/issue-1866.rs
index caac0c50414..caac0c50414 100644
--- a/src/test/ui/issues/issue-1866.rs
+++ b/src/test/ui/lint/issue-1866.rs
diff --git a/src/test/ui/issues/issue-1866.stderr b/src/test/ui/lint/issue-1866.stderr
index 5edae48a10f..5edae48a10f 100644
--- a/src/test/ui/issues/issue-1866.stderr
+++ b/src/test/ui/lint/issue-1866.stderr
diff --git a/src/test/ui/issues/issue-20343.rs b/src/test/ui/lint/issue-20343.rs
index 000b6398442..000b6398442 100644
--- a/src/test/ui/issues/issue-20343.rs
+++ b/src/test/ui/lint/issue-20343.rs
diff --git a/src/test/ui/lint/unreachable_pub-pub_crate.rs b/src/test/ui/lint/unreachable_pub-pub_crate.rs
index 27c31c22311..4dc951985ae 100644
--- a/src/test/ui/lint/unreachable_pub-pub_crate.rs
+++ b/src/test/ui/lint/unreachable_pub-pub_crate.rs
@@ -26,6 +26,11 @@ mod private_mod {
         pub fn count_neutrons(&self) -> usize { self.neutrons } //~ WARNING unreachable_pub
         pub(crate) fn count_electrons(&self) -> usize { self.electrons }
     }
+    impl Clone for Hydrogen {
+        fn clone(&self) -> Hydrogen {
+            Hydrogen { neutrons: self.neutrons, electrons: self.electrons }
+        }
+    }
 
     pub enum Helium {} //~ WARNING unreachable_pub
     pub union Lithium { c1: usize, c2: u8 } //~ WARNING unreachable_pub
diff --git a/src/test/ui/lint/unreachable_pub-pub_crate.stderr b/src/test/ui/lint/unreachable_pub-pub_crate.stderr
index f284db80ff9..6c05a030138 100644
--- a/src/test/ui/lint/unreachable_pub-pub_crate.stderr
+++ b/src/test/ui/lint/unreachable_pub-pub_crate.stderr
@@ -50,7 +50,7 @@ LL |         pub fn count_neutrons(&self) -> usize { self.neutrons }
    |         help: consider restricting its visibility: `pub(crate)`
 
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub-pub_crate.rs:30:5
+  --> $DIR/unreachable_pub-pub_crate.rs:35:5
    |
 LL |     pub enum Helium {}
    |     ---^^^^^^^^^^^^
@@ -60,7 +60,7 @@ LL |     pub enum Helium {}
    = help: or consider exporting it for use by other crates
 
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub-pub_crate.rs:31:5
+  --> $DIR/unreachable_pub-pub_crate.rs:36:5
    |
 LL |     pub union Lithium { c1: usize, c2: u8 }
    |     ---^^^^^^^^^^^^^^
@@ -70,7 +70,7 @@ LL |     pub union Lithium { c1: usize, c2: u8 }
    = help: or consider exporting it for use by other crates
 
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub-pub_crate.rs:32:5
+  --> $DIR/unreachable_pub-pub_crate.rs:37:5
    |
 LL |     pub fn beryllium() {}
    |     ---^^^^^^^^^^^^^^^
@@ -80,7 +80,7 @@ LL |     pub fn beryllium() {}
    = help: or consider exporting it for use by other crates
 
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub-pub_crate.rs:33:5
+  --> $DIR/unreachable_pub-pub_crate.rs:38:5
    |
 LL |     pub trait Boron {}
    |     ---^^^^^^^^^^^^
@@ -90,7 +90,7 @@ LL |     pub trait Boron {}
    = help: or consider exporting it for use by other crates
 
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub-pub_crate.rs:34:5
+  --> $DIR/unreachable_pub-pub_crate.rs:39:5
    |
 LL |     pub const CARBON: usize = 1;
    |     ---^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -100,7 +100,7 @@ LL |     pub const CARBON: usize = 1;
    = help: or consider exporting it for use by other crates
 
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub-pub_crate.rs:35:5
+  --> $DIR/unreachable_pub-pub_crate.rs:40:5
    |
 LL |     pub static NITROGEN: usize = 2;
    |     ---^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -110,7 +110,7 @@ LL |     pub static NITROGEN: usize = 2;
    = help: or consider exporting it for use by other crates
 
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub-pub_crate.rs:36:5
+  --> $DIR/unreachable_pub-pub_crate.rs:41:5
    |
 LL |     pub type Oxygen = bool;
    |     ---^^^^^^^^^^^^^^^^^^^^
@@ -120,7 +120,7 @@ LL |     pub type Oxygen = bool;
    = help: or consider exporting it for use by other crates
 
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub-pub_crate.rs:39:47
+  --> $DIR/unreachable_pub-pub_crate.rs:44:47
    |
 LL |         ($visibility: vis, $name: ident) => { $visibility struct $name {} }
    |                                               ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -135,7 +135,7 @@ LL |     define_empty_struct_with_visibility!(pub, Fluorine);
    = note: this warning originates in the macro `define_empty_struct_with_visibility` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub-pub_crate.rs:45:9
+  --> $DIR/unreachable_pub-pub_crate.rs:50:9
    |
 LL |         pub fn catalyze() -> bool;
    |         ---^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/test/ui/lint/unreachable_pub.rs b/src/test/ui/lint/unreachable_pub.rs
index 6bfec0ec5e8..39e2b596156 100644
--- a/src/test/ui/lint/unreachable_pub.rs
+++ b/src/test/ui/lint/unreachable_pub.rs
@@ -22,6 +22,11 @@ mod private_mod {
         pub fn count_neutrons(&self) -> usize { self.neutrons } //~ WARNING unreachable_pub
         crate fn count_electrons(&self) -> usize { self.electrons }
     }
+    impl Clone for Hydrogen {
+        fn clone(&self) -> Hydrogen {
+            Hydrogen { neutrons: self.neutrons, electrons: self.electrons }
+        }
+    }
 
     pub enum Helium {} //~ WARNING unreachable_pub
     pub union Lithium { c1: usize, c2: u8 } //~ WARNING unreachable_pub
diff --git a/src/test/ui/lint/unreachable_pub.stderr b/src/test/ui/lint/unreachable_pub.stderr
index 61c9582287c..e8e55be5a47 100644
--- a/src/test/ui/lint/unreachable_pub.stderr
+++ b/src/test/ui/lint/unreachable_pub.stderr
@@ -50,7 +50,7 @@ LL |         pub fn count_neutrons(&self) -> usize { self.neutrons }
    |         help: consider restricting its visibility: `crate`
 
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub.rs:26:5
+  --> $DIR/unreachable_pub.rs:31:5
    |
 LL |     pub enum Helium {}
    |     ---^^^^^^^^^^^^
@@ -60,7 +60,7 @@ LL |     pub enum Helium {}
    = help: or consider exporting it for use by other crates
 
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub.rs:27:5
+  --> $DIR/unreachable_pub.rs:32:5
    |
 LL |     pub union Lithium { c1: usize, c2: u8 }
    |     ---^^^^^^^^^^^^^^
@@ -70,7 +70,7 @@ LL |     pub union Lithium { c1: usize, c2: u8 }
    = help: or consider exporting it for use by other crates
 
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub.rs:28:5
+  --> $DIR/unreachable_pub.rs:33:5
    |
 LL |     pub fn beryllium() {}
    |     ---^^^^^^^^^^^^^^^
@@ -80,7 +80,7 @@ LL |     pub fn beryllium() {}
    = help: or consider exporting it for use by other crates
 
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub.rs:29:5
+  --> $DIR/unreachable_pub.rs:34:5
    |
 LL |     pub trait Boron {}
    |     ---^^^^^^^^^^^^
@@ -90,7 +90,7 @@ LL |     pub trait Boron {}
    = help: or consider exporting it for use by other crates
 
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub.rs:30:5
+  --> $DIR/unreachable_pub.rs:35:5
    |
 LL |     pub const CARBON: usize = 1;
    |     ---^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -100,7 +100,7 @@ LL |     pub const CARBON: usize = 1;
    = help: or consider exporting it for use by other crates
 
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub.rs:31:5
+  --> $DIR/unreachable_pub.rs:36:5
    |
 LL |     pub static NITROGEN: usize = 2;
    |     ---^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -110,7 +110,7 @@ LL |     pub static NITROGEN: usize = 2;
    = help: or consider exporting it for use by other crates
 
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub.rs:32:5
+  --> $DIR/unreachable_pub.rs:37:5
    |
 LL |     pub type Oxygen = bool;
    |     ---^^^^^^^^^^^^^^^^^^^^
@@ -120,7 +120,7 @@ LL |     pub type Oxygen = bool;
    = help: or consider exporting it for use by other crates
 
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub.rs:35:47
+  --> $DIR/unreachable_pub.rs:40:47
    |
 LL |         ($visibility: vis, $name: ident) => { $visibility struct $name {} }
    |                                               ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -135,7 +135,7 @@ LL |     define_empty_struct_with_visibility!(pub, Fluorine);
    = note: this warning originates in the macro `define_empty_struct_with_visibility` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub.rs:41:9
+  --> $DIR/unreachable_pub.rs:46:9
    |
 LL |         pub fn catalyze() -> bool;
    |         ---^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/test/ui/issues/issue-41776.rs b/src/test/ui/macros/issue-41776.rs
index 24696d86d0f..24696d86d0f 100644
--- a/src/test/ui/issues/issue-41776.rs
+++ b/src/test/ui/macros/issue-41776.rs
diff --git a/src/test/ui/issues/issue-41776.stderr b/src/test/ui/macros/issue-41776.stderr
index e06873b5026..e06873b5026 100644
--- a/src/test/ui/issues/issue-41776.stderr
+++ b/src/test/ui/macros/issue-41776.stderr
diff --git a/src/test/ui/issues/issue-44127.rs b/src/test/ui/macros/issue-44127.rs
index 21b2e68264a..21b2e68264a 100644
--- a/src/test/ui/issues/issue-44127.rs
+++ b/src/test/ui/macros/issue-44127.rs
diff --git a/src/test/ui/macros/trace-macro.stderr b/src/test/ui/macros/trace-macro.stderr
index c8a0fd68430..43272248c28 100644
--- a/src/test/ui/macros/trace-macro.stderr
+++ b/src/test/ui/macros/trace-macro.stderr
@@ -5,5 +5,5 @@ LL |     println!("Hello, World!");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: expanding `println! { "Hello, World!" }`
-   = note: to `$crate :: io :: _print($crate :: format_args_nl! ("Hello, World!"))`
+   = note: to `{ $crate :: io :: _print($crate :: format_args_nl! ("Hello, World!")) ; }`
 
diff --git a/src/test/ui/match/issue-82392.stdout b/src/test/ui/match/issue-82392.stdout
index bcab76b7c6b..ffe73074324 100644
--- a/src/test/ui/match/issue-82392.stdout
+++ b/src/test/ui/match/issue-82392.stdout
@@ -6,7 +6,7 @@ extern crate std;
 // compile-flags: -Zunpretty=hir,typed
 // check-pass
 
-pub fn main() ({
+fn main() ({
         (if (true as bool)
                 ({ } as
                     ()) else if (let Some(a) =
diff --git a/src/test/ui/methods/issues/issue-90315.rs b/src/test/ui/methods/issues/issue-90315.rs
new file mode 100644
index 00000000000..01bf9f48402
--- /dev/null
+++ b/src/test/ui/methods/issues/issue-90315.rs
@@ -0,0 +1,7 @@
+fn main() {
+  let arr = &[0,1,2,3];
+  for _i in 0..arr.len().rev() { //~ERROR not an iterator
+     // The above error used to say “the method `rev` exists for type `usize`”.
+     // This regression test ensures it doesn't say that any more.
+  }
+}
diff --git a/src/test/ui/methods/issues/issue-90315.stderr b/src/test/ui/methods/issues/issue-90315.stderr
new file mode 100644
index 00000000000..c6a76c9e790
--- /dev/null
+++ b/src/test/ui/methods/issues/issue-90315.stderr
@@ -0,0 +1,13 @@
+error[E0599]: `usize` is not an iterator
+  --> $DIR/issue-90315.rs:3:26
+   |
+LL |   for _i in 0..arr.len().rev() {
+   |                          ^^^ `usize` is not an iterator
+   |
+   = note: the following trait bounds were not satisfied:
+           `usize: Iterator`
+           which is required by `&mut usize: Iterator`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0599`.
diff --git a/src/test/ui/moves/use_of_moved_value_copy_suggestions.fixed b/src/test/ui/moves/use_of_moved_value_copy_suggestions.fixed
index fb6cf7e8996..ba1b745ba84 100644
--- a/src/test/ui/moves/use_of_moved_value_copy_suggestions.fixed
+++ b/src/test/ui/moves/use_of_moved_value_copy_suggestions.fixed
@@ -54,17 +54,17 @@ where
 
 fn duplicate_custom_3<T>(t: S<T>) -> (S<T>, S<T>)
 where
-    T: A,
-    T: B, T: Trait, T: Copy
-    //~^ HELP consider further restricting type parameter `T`
+    T: A + Trait + Copy,
+    //~^ HELP consider further restricting this bound
+    T: B,
 {
     (t, t) //~ use of moved value: `t`
 }
 
-fn duplicate_custom_4<T: A>(t: S<T>) -> (S<T>, S<T>)
+fn duplicate_custom_4<T: A + Trait + Copy>(t: S<T>) -> (S<T>, S<T>)
+//~^ HELP consider further restricting this bound
 where
-    T: B + Trait + Copy,
-    //~^ HELP consider further restricting this bound
+    T: B,
 {
     (t, t) //~ use of moved value: `t`
 }
@@ -77,8 +77,8 @@ fn existing_colon<T: Copy>(t: T) {
 
 fn existing_colon_in_where<T>(t: T)
 where
-    T: Copy,
-    //~^ HELP consider further restricting this bound
+    T:, T: Copy
+    //~^ HELP consider further restricting type parameter `T`
 {
     [t, t]; //~ use of moved value: `t`
 }
diff --git a/src/test/ui/moves/use_of_moved_value_copy_suggestions.rs b/src/test/ui/moves/use_of_moved_value_copy_suggestions.rs
index cadbf2a54cc..0a43dd1a9a3 100644
--- a/src/test/ui/moves/use_of_moved_value_copy_suggestions.rs
+++ b/src/test/ui/moves/use_of_moved_value_copy_suggestions.rs
@@ -55,16 +55,16 @@ where
 fn duplicate_custom_3<T>(t: S<T>) -> (S<T>, S<T>)
 where
     T: A,
+    //~^ HELP consider further restricting this bound
     T: B,
-    //~^ HELP consider further restricting type parameter `T`
 {
     (t, t) //~ use of moved value: `t`
 }
 
 fn duplicate_custom_4<T: A>(t: S<T>) -> (S<T>, S<T>)
+//~^ HELP consider further restricting this bound
 where
     T: B,
-    //~^ HELP consider further restricting this bound
 {
     (t, t) //~ use of moved value: `t`
 }
@@ -78,7 +78,7 @@ fn existing_colon<T:>(t: T) {
 fn existing_colon_in_where<T>(t: T)
 where
     T:,
-    //~^ HELP consider further restricting this bound
+    //~^ HELP consider further restricting type parameter `T`
 {
     [t, t]; //~ use of moved value: `t`
 }
diff --git a/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr b/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr
index f5252084d68..2353cd079a3 100644
--- a/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr
+++ b/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr
@@ -121,10 +121,10 @@ LL |     (t, t)
    |      |
    |      value moved here
    |
-help: consider further restricting type parameter `T`
+help: consider further restricting this bound
    |
-LL |     T: B, T: Trait, T: Copy
-   |         ~~~~~~~~~~~~~~~~~~~
+LL |     T: A + Trait + Copy,
+   |          ++++++++++++++
 
 error[E0382]: use of moved value: `t`
   --> $DIR/use_of_moved_value_copy_suggestions.rs:69:9
@@ -139,8 +139,8 @@ LL |     (t, t)
    |
 help: consider further restricting this bound
    |
-LL |     T: B + Trait + Copy,
-   |          ++++++++++++++
+LL | fn duplicate_custom_4<T: A + Trait + Copy>(t: S<T>) -> (S<T>, S<T>)
+   |                            ++++++++++++++
 
 error[E0382]: use of moved value: `t`
   --> $DIR/use_of_moved_value_copy_suggestions.rs:83:9
@@ -153,10 +153,10 @@ LL |     [t, t];
    |      |
    |      value moved here
    |
-help: consider further restricting this bound
+help: consider further restricting type parameter `T`
    |
-LL |     T: Copy,
-   |        ++++
+LL |     T:, T: Copy
+   |       ~~~~~~~~~
 
 error[E0382]: use of moved value: `t`
   --> $DIR/use_of_moved_value_copy_suggestions.rs:75:9
diff --git a/src/test/ui/never_type/issue-52443.stderr b/src/test/ui/never_type/issue-52443.stderr
index 8c1755205f0..c2079a19d0a 100644
--- a/src/test/ui/never_type/issue-52443.stderr
+++ b/src/test/ui/never_type/issue-52443.stderr
@@ -47,8 +47,8 @@ LL |     [(); { for _ in 0usize.. {}; 0}];
 note: impl defined here, but it is not `const`
   --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
    |
-LL | impl<I: Iterator> IntoIterator for I {
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | impl<I: ~const Iterator> const IntoIterator for I {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
 
 error[E0658]: mutable references are not allowed in constants
diff --git a/src/test/ui/never_type/issue-96335.rs b/src/test/ui/never_type/issue-96335.rs
new file mode 100644
index 00000000000..411a7c9df65
--- /dev/null
+++ b/src/test/ui/never_type/issue-96335.rs
@@ -0,0 +1,5 @@
+fn main() {
+    0.....{loop{}1};
+    //~^ ERROR unexpected token
+    //~| ERROR mismatched types
+}
diff --git a/src/test/ui/never_type/issue-96335.stderr b/src/test/ui/never_type/issue-96335.stderr
new file mode 100644
index 00000000000..168cf2f8353
--- /dev/null
+++ b/src/test/ui/never_type/issue-96335.stderr
@@ -0,0 +1,35 @@
+error: unexpected token: `...`
+  --> $DIR/issue-96335.rs:2:6
+   |
+LL |     0.....{loop{}1};
+   |      ^^^
+   |
+help: use `..` for an exclusive range
+   |
+LL |     0....{loop{}1};
+   |      ~~
+help: or `..=` for an inclusive range
+   |
+LL |     0..=..{loop{}1};
+   |      ~~~
+
+error[E0308]: mismatched types
+  --> $DIR/issue-96335.rs:2:9
+   |
+LL |     0.....{loop{}1};
+   |     ----^^^^^^^^^^^
+   |     |   |
+   |     |   expected integer, found struct `RangeTo`
+   |     arguments to this function are incorrect
+   |
+   = note: expected type `{integer}`
+            found struct `RangeTo<{integer}>`
+note: associated function defined here
+  --> $SRC_DIR/core/src/ops/range.rs:LL:COL
+   |
+LL |     pub const fn new(start: Idx, end: Idx) -> Self {
+   |                  ^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr
index 4b860a55057..08605efa2ea 100644
--- a/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr
+++ b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr
@@ -44,9 +44,12 @@ LL | |         // This function call requires that
 ...  |
 LL | |         require(value);
 LL | |     });
-   | |_____^
+   | |_____^ ...so that the type `T` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
    |
-   = help: consider adding an explicit lifetime bound `T: 'a`...
+LL |     T: Trait<'a> + 'a,
+   |                  ++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issues/issue-45696-no-variant-box-recur.rs b/src/test/ui/nll/issue-45696-no-variant-box-recur.rs
index c688261fa1c..c688261fa1c 100644
--- a/src/test/ui/issues/issue-45696-no-variant-box-recur.rs
+++ b/src/test/ui/nll/issue-45696-no-variant-box-recur.rs
diff --git a/src/test/ui/nll/mir_check_cast_unsize.stderr b/src/test/ui/nll/mir_check_cast_unsize.stderr
index 364d6c17ea7..8d02ef71d1b 100644
--- a/src/test/ui/nll/mir_check_cast_unsize.stderr
+++ b/src/test/ui/nll/mir_check_cast_unsize.stderr
@@ -5,6 +5,11 @@ LL | fn bar<'a>(x: &'a u32) -> &'static dyn Debug {
    |        -- lifetime `'a` defined here
 LL |     x
    |     ^ returning this value requires that `'a` must outlive `'static`
+   |
+help: to declare that the trait object captures data from argument `x`, you can add an explicit `'a` lifetime bound
+   |
+LL | fn bar<'a>(x: &'a u32) -> &'static dyn Debug + 'a {
+   |                                              ++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/nll/ty-outlives/impl-trait-outlives.stderr b/src/test/ui/nll/ty-outlives/impl-trait-outlives.stderr
index 31ee540cce9..64b08a9b32f 100644
--- a/src/test/ui/nll/ty-outlives/impl-trait-outlives.stderr
+++ b/src/test/ui/nll/ty-outlives/impl-trait-outlives.stderr
@@ -2,17 +2,23 @@ error[E0309]: the parameter type `T` may not live long enough
   --> $DIR/impl-trait-outlives.rs:11:5
    |
 LL |     x
-   |     ^
+   |     ^ ...so that the type `T` will meet its required lifetime bounds
    |
-   = help: consider adding an explicit lifetime bound `T: 'a`...
+help: consider adding an explicit lifetime bound...
+   |
+LL |     T: Debug + 'a,
+   |              ++++
 
 error[E0309]: the parameter type `T` may not live long enough
   --> $DIR/impl-trait-outlives.rs:26:5
    |
 LL |     x
-   |     ^
+   |     ^ ...so that the type `T` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
    |
-   = help: consider adding an explicit lifetime bound `T: 'a`...
+LL |     T: 'b + Debug + 'a,
+   |                   ++++
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/nll/ty-outlives/projection-implied-bounds.stderr b/src/test/ui/nll/ty-outlives/projection-implied-bounds.stderr
index 9f0c60c1e17..3b9b2956c51 100644
--- a/src/test/ui/nll/ty-outlives/projection-implied-bounds.stderr
+++ b/src/test/ui/nll/ty-outlives/projection-implied-bounds.stderr
@@ -2,9 +2,12 @@ error[E0310]: the parameter type `T` may not live long enough
   --> $DIR/projection-implied-bounds.rs:30:18
    |
 LL |     twice(value, |value_ref, item| invoke2(value_ref, item));
-   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
    |
-   = help: consider adding an explicit lifetime bound `T: 'static`...
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn generic2<T: Iterator + 'static>(value: T) {
+   |                         +++++++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr
index 983d6a06afa..8fe25181da1 100644
--- a/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr
+++ b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr
@@ -33,6 +33,7 @@ LL |     with_signature(x, |mut y| Box::new(y.next()))
    |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: consider adding an explicit lifetime bound `<T as Iterator>::Item: 'a`...
+   = note: ...so that the type `<T as Iterator>::Item` will meet its required lifetime bounds
 
 note: external requirements
   --> $DIR/projection-no-regions-closure.rs:34:23
@@ -96,6 +97,7 @@ LL |     with_signature(x, |mut y| Box::new(y.next()))
    |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: consider adding an explicit lifetime bound `<T as Iterator>::Item: 'a`...
+   = note: ...so that the type `<T as Iterator>::Item` will meet its required lifetime bounds
 
 note: external requirements
   --> $DIR/projection-no-regions-closure.rs:52:23
diff --git a/src/test/ui/nll/ty-outlives/projection-no-regions-fn.stderr b/src/test/ui/nll/ty-outlives/projection-no-regions-fn.stderr
index c4df04b99b5..e0ff544fe47 100644
--- a/src/test/ui/nll/ty-outlives/projection-no-regions-fn.stderr
+++ b/src/test/ui/nll/ty-outlives/projection-no-regions-fn.stderr
@@ -5,6 +5,7 @@ LL |     Box::new(x.next())
    |     ^^^^^^^^^^^^^^^^^^
    |
    = help: consider adding an explicit lifetime bound `<T as Iterator>::Item: 'a`...
+   = note: ...so that the type `<T as Iterator>::Item` will meet its required lifetime bounds
 
 error[E0309]: the associated type `<T as Iterator>::Item` may not live long enough
   --> $DIR/projection-no-regions-fn.rs:28:5
@@ -13,6 +14,7 @@ LL |     Box::new(x.next())
    |     ^^^^^^^^^^^^^^^^^^
    |
    = help: consider adding an explicit lifetime bound `<T as Iterator>::Item: 'a`...
+   = note: ...so that the type `<T as Iterator>::Item` will meet its required lifetime bounds
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr b/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr
index 2513b0bfccb..caf2e3c4747 100644
--- a/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr
+++ b/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr
@@ -32,9 +32,12 @@ error[E0309]: the parameter type `T` may not live long enough
   --> $DIR/projection-one-region-closure.rs:45:29
    |
 LL |     with_signature(cell, t, |cell, t| require(cell, t));
-   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
    |
-   = help: consider adding an explicit lifetime bound `T: 'a`...
+LL |     T: Anything<'b> + 'a,
+   |                     ++++
 
 error: lifetime may not live long enough
   --> $DIR/projection-one-region-closure.rs:45:39
@@ -82,9 +85,12 @@ error[E0309]: the parameter type `T` may not live long enough
   --> $DIR/projection-one-region-closure.rs:56:29
    |
 LL |     with_signature(cell, t, |cell, t| require(cell, t));
-   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
    |
-   = help: consider adding an explicit lifetime bound `T: 'a`...
+LL |     T: Anything<'b> + 'a,
+   |                     ++++
 
 error: lifetime may not live long enough
   --> $DIR/projection-one-region-closure.rs:56:39
diff --git a/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr
index 4e0155bdf2c..1ee788b40ab 100644
--- a/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr
+++ b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr
@@ -34,6 +34,7 @@ LL |     with_signature(cell, t, |cell, t| require(cell, t));
    |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: consider adding an explicit lifetime bound `<T as Anything<ReEarlyBound(0, 'b), ReEarlyBound(1, 'c)>>::AssocType: 'a`...
+   = note: ...so that the type `<T as Anything<ReEarlyBound(0, 'b), ReEarlyBound(1, 'c)>>::AssocType` will meet its required lifetime bounds
 
 note: external requirements
   --> $DIR/projection-two-region-trait-bound-closure.rs:48:29
@@ -70,6 +71,7 @@ LL |     with_signature(cell, t, |cell, t| require(cell, t));
    |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: consider adding an explicit lifetime bound `<T as Anything<ReEarlyBound(1, 'b), ReEarlyBound(2, 'c)>>::AssocType: 'a`...
+   = note: ...so that the type `<T as Anything<ReEarlyBound(1, 'b), ReEarlyBound(2, 'c)>>::AssocType` will meet its required lifetime bounds
 
 note: external requirements
   --> $DIR/projection-two-region-trait-bound-closure.rs:61:29
diff --git a/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-bound.nll.stderr b/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-bound.nll.stderr
index 3a84cbfbedc..b4435fe06bc 100644
--- a/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-bound.nll.stderr
+++ b/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-bound.nll.stderr
@@ -5,6 +5,7 @@ LL |     bar::<T::Output>()
    |     ^^^^^^^^^^^^^^^^
    |
    = help: consider adding an explicit lifetime bound `<T as MyTrait<'_>>::Output: 'a`...
+   = note: ...so that the type `<T as MyTrait<'_>>::Output` will meet its required lifetime bounds
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.nll.stderr b/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.nll.stderr
index 58bfb600452..ddeaf3c1f9e 100644
--- a/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.nll.stderr
+++ b/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.nll.stderr
@@ -5,6 +5,7 @@ LL |     bar::<<T as MyTrait<'a>>::Output>()
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: consider adding an explicit lifetime bound `<T as MyTrait<'_>>::Output: 'a`...
+   = note: ...so that the type `<T as MyTrait<'_>>::Output` will meet its required lifetime bounds
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/nll/ty-outlives/projection-where-clause-none.stderr b/src/test/ui/nll/ty-outlives/projection-where-clause-none.stderr
index 8175c302155..e28b89580bc 100644
--- a/src/test/ui/nll/ty-outlives/projection-where-clause-none.stderr
+++ b/src/test/ui/nll/ty-outlives/projection-where-clause-none.stderr
@@ -2,9 +2,12 @@ error[E0309]: the parameter type `T` may not live long enough
   --> $DIR/projection-where-clause-none.rs:16:5
    |
 LL |     bar::<T::Output>()
-   |     ^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
    |
-   = help: consider adding an explicit lifetime bound `T: 'a`...
+help: consider adding an explicit lifetime bound...
+   |
+LL |     T: MyTrait<'a> + 'a,
+   |                    ++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr b/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr
index baf223b786b..a4588730b3f 100644
--- a/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr
+++ b/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr
@@ -53,9 +53,12 @@ error[E0309]: the parameter type `T` may not live long enough
   --> $DIR/ty-param-closure-approximate-lower-bound.rs:29:24
    |
 LL |     twice(cell, value, |a, b| invoke(a, b));
-   |                        ^^^^^^^^^^^^^^^^^^^
+   |                        ^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
    |
-   = help: consider adding an explicit lifetime bound `T: 'a`...
+LL | fn generic_fail<'a, T: 'a>(cell: Cell<&'a ()>, value: T) {
+   |                      ++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr
index 88d73e7a729..084dd93cb86 100644
--- a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr
+++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr
@@ -30,17 +30,23 @@ error[E0309]: the parameter type `T` may not live long enough
   --> $DIR/ty-param-closure-outlives-from-return-type.rs:26:23
    |
 LL |     with_signature(x, |y| y)
-   |                       ^^^^^
+   |                       ^^^^^ ...so that the type `T` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
    |
-   = help: consider adding an explicit lifetime bound `T: 'a`...
+LL |     T: Debug + 'a,
+   |              ++++
 
 error[E0309]: the parameter type `T` may not live long enough
   --> $DIR/ty-param-closure-outlives-from-return-type.rs:41:5
    |
 LL |     x
-   |     ^
+   |     ^ ...so that the type `T` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
    |
-   = help: consider adding an explicit lifetime bound `T: 'a`...
+LL |     T: 'b + Debug + 'a,
+   |                   ++++
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr
index 5b175aac1e1..11a737ba291 100644
--- a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr
+++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr
@@ -45,9 +45,12 @@ LL | |         // See `correct_region`, which explains the point of this
 ...  |
 LL | |         require(&x, &y)
 LL | |     })
-   | |_____^
+   | |_____^ ...so that the type `T` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
    |
-   = help: consider adding an explicit lifetime bound `T: 'a`...
+LL | fn no_region<'a, T: 'a>(a: Cell<&'a ()>, b: T) {
+   |                   ++++
 
 note: external requirements
   --> $DIR/ty-param-closure-outlives-from-where-clause.rs:43:26
@@ -127,9 +130,12 @@ LL | |
 LL | |         // See `correct_region`
 LL | |         require(&x, &y)
 LL | |     })
-   | |_____^
+   | |_____^ ...so that the type `T` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
    |
-   = help: consider adding an explicit lifetime bound `T: 'a`...
+LL |     T: 'b + 'a,
+   |           ++++
 
 note: external requirements
   --> $DIR/ty-param-closure-outlives-from-where-clause.rs:77:26
diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn-body.stderr b/src/test/ui/nll/ty-outlives/ty-param-fn-body.stderr
index 9a023a0e58e..ba79137d18d 100644
--- a/src/test/ui/nll/ty-outlives/ty-param-fn-body.stderr
+++ b/src/test/ui/nll/ty-outlives/ty-param-fn-body.stderr
@@ -2,9 +2,12 @@ error[E0309]: the parameter type `T` may not live long enough
   --> $DIR/ty-param-fn-body.rs:19:5
    |
 LL |     outlives(cell, t)
-   |     ^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
    |
-   = help: consider adding an explicit lifetime bound `T: 'a`...
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn region_static<'a, T: 'a>(cell: Cell<&'a usize>, t: T) {
+   |                       ++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn.stderr b/src/test/ui/nll/ty-outlives/ty-param-fn.stderr
index 8c8529620d5..729f14d84ad 100644
--- a/src/test/ui/nll/ty-outlives/ty-param-fn.stderr
+++ b/src/test/ui/nll/ty-outlives/ty-param-fn.stderr
@@ -2,17 +2,23 @@ error[E0309]: the parameter type `T` may not live long enough
   --> $DIR/ty-param-fn.rs:11:5
    |
 LL |     x
-   |     ^
+   |     ^ ...so that the type `T` will meet its required lifetime bounds
    |
-   = help: consider adding an explicit lifetime bound `T: 'a`...
+help: consider adding an explicit lifetime bound...
+   |
+LL |     T: Debug + 'a,
+   |              ++++
 
 error[E0309]: the parameter type `T` may not live long enough
   --> $DIR/ty-param-fn.rs:26:5
    |
 LL |     x
-   |     ^
+   |     ^ ...so that the type `T` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
    |
-   = help: consider adding an explicit lifetime bound `T: 'a`...
+LL |     T: 'b + Debug + 'a,
+   |                   ++++
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.nll.stderr b/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.nll.stderr
index ae02c58d080..43695a7511d 100644
--- a/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.nll.stderr
+++ b/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.nll.stderr
@@ -6,6 +6,11 @@ LL | fn load(ss: &mut SomeStruct) -> Box<dyn SomeTrait> {
 ...
 LL |     ss.r
    |     ^^^^ returning this value requires that `'1` must outlive `'static`
+   |
+help: to declare that the trait object captures data from argument `ss`, you can add an explicit `'_` lifetime bound
+   |
+LL | fn load(ss: &mut SomeStruct) -> Box<dyn SomeTrait + '_> {
+   |                                                   ++++
 
 error[E0507]: cannot move out of `ss.r` which is behind a mutable reference
   --> $DIR/object-lifetime-default-from-box-error.rs:18:5
diff --git a/src/test/ui/issues/issue-19538.rs b/src/test/ui/object-safety/issue-19538.rs
index 7054ef41b1c..7054ef41b1c 100644
--- a/src/test/ui/issues/issue-19538.rs
+++ b/src/test/ui/object-safety/issue-19538.rs
diff --git a/src/test/ui/issues/issue-19538.stderr b/src/test/ui/object-safety/issue-19538.stderr
index 7b37e1f95dc..7b37e1f95dc 100644
--- a/src/test/ui/issues/issue-19538.stderr
+++ b/src/test/ui/object-safety/issue-19538.stderr
diff --git a/src/test/ui/or-patterns/missing-bindings.stderr b/src/test/ui/or-patterns/missing-bindings.stderr
index c173a3a9aba..8fafa275b5c 100644
--- a/src/test/ui/or-patterns/missing-bindings.stderr
+++ b/src/test/ui/or-patterns/missing-bindings.stderr
@@ -103,6 +103,22 @@ LL |     let (A(A(a, b) | B(c), d) | B(e)) = Y;
    |            |
    |            pattern doesn't bind `c`
 
+error[E0408]: variable `d` is not bound in all patterns
+  --> $DIR/missing-bindings.rs:45:33
+   |
+LL |     let (A(A(a, b) | B(c), d) | B(e)) = Y;
+   |                            -    ^^^^ pattern doesn't bind `d`
+   |                            |
+   |                            variable not in all patterns
+
+error[E0408]: variable `e` is not bound in all patterns
+  --> $DIR/missing-bindings.rs:45:10
+   |
+LL |     let (A(A(a, b) | B(c), d) | B(e)) = Y;
+   |          ^^^^^^^^^^^^^^^^^^^^     - variable not in all patterns
+   |          |
+   |          pattern doesn't bind `e`
+
 error[E0408]: variable `a` is not bound in all patterns
   --> $DIR/missing-bindings.rs:45:33
    |
@@ -127,22 +143,6 @@ LL |     let (A(A(a, b) | B(c), d) | B(e)) = Y;
    |                        |
    |                        variable not in all patterns
 
-error[E0408]: variable `d` is not bound in all patterns
-  --> $DIR/missing-bindings.rs:45:33
-   |
-LL |     let (A(A(a, b) | B(c), d) | B(e)) = Y;
-   |                            -    ^^^^ pattern doesn't bind `d`
-   |                            |
-   |                            variable not in all patterns
-
-error[E0408]: variable `e` is not bound in all patterns
-  --> $DIR/missing-bindings.rs:45:10
-   |
-LL |     let (A(A(a, b) | B(c), d) | B(e)) = Y;
-   |          ^^^^^^^^^^^^^^^^^^^^     - variable not in all patterns
-   |          |
-   |          pattern doesn't bind `e`
-
 error[E0408]: variable `a` is not bound in all patterns
   --> $DIR/missing-bindings.rs:61:29
    |
diff --git a/src/test/ui/issues/issue-61858.rs b/src/test/ui/parser/issue-61858.rs
index 6c3b56586c4..6c3b56586c4 100644
--- a/src/test/ui/issues/issue-61858.rs
+++ b/src/test/ui/parser/issue-61858.rs
diff --git a/src/test/ui/issues/issue-61858.stderr b/src/test/ui/parser/issue-61858.stderr
index 8b95d9c6ae4..8b95d9c6ae4 100644
--- a/src/test/ui/issues/issue-61858.stderr
+++ b/src/test/ui/parser/issue-61858.stderr
diff --git a/src/test/ui/parser/raw/raw-str-unbalanced.rs b/src/test/ui/parser/raw/raw-str-unbalanced.rs
index 35f118f5ce6..38537f8b31e 100644
--- a/src/test/ui/parser/raw/raw-str-unbalanced.rs
+++ b/src/test/ui/parser/raw/raw-str-unbalanced.rs
@@ -1,4 +1,22 @@
 static s: &'static str =
+    r#""## //~ ERROR too many `#` when terminating raw string
+;
+
+static s2: &'static str =
     r#"
-      "## //~ too many `#` when terminating raw string
+      "#### //~ ERROR too many `#` when terminating raw string
 ;
+
+const A: &'static str = r"" //~ ERROR expected `;`, found `#`
+
+// Test
+#[test]
+fn test() {}
+
+const B: &'static str = r""## //~ ERROR too many `#` when terminating raw string
+
+// Test
+#[test]
+fn test2() {}
+
+fn main() {}
diff --git a/src/test/ui/parser/raw/raw-str-unbalanced.stderr b/src/test/ui/parser/raw/raw-str-unbalanced.stderr
index bf8f3a7a5a4..eac8c06c1df 100644
--- a/src/test/ui/parser/raw/raw-str-unbalanced.stderr
+++ b/src/test/ui/parser/raw/raw-str-unbalanced.stderr
@@ -1,10 +1,36 @@
 error: too many `#` when terminating raw string
-  --> $DIR/raw-str-unbalanced.rs:3:9
+  --> $DIR/raw-str-unbalanced.rs:2:10
    |
-LL |       "##
-   |         ^ help: remove the extra `#`
+LL |     r#""##
+   |     -----^ help: remove the extra `#`
+   |     |
+   |     this raw string started with 1 `#`
+
+error: too many `#` when terminating raw string
+  --> $DIR/raw-str-unbalanced.rs:7:9
+   |
+LL | /     r#"
+LL | |       "####
+   | |        -^^^ help: remove the extra `#`s
+   | |________|
+   |          this raw string started with 1 `#`
+
+error: expected `;`, found `#`
+  --> $DIR/raw-str-unbalanced.rs:10:28
+   |
+LL | const A: &'static str = r""
+   |                            ^ help: add `;` here
+...
+LL | #[test]
+   | - unexpected token
+
+error: too many `#` when terminating raw string
+  --> $DIR/raw-str-unbalanced.rs:16:28
    |
-   = note: the raw string started with 1 `#`s
+LL | const B: &'static str = r""##
+   |                         ---^^ help: remove the extra `#`s
+   |                         |
+   |                         this raw string started with 0 `#`s
 
-error: aborting due to previous error
+error: aborting due to 4 previous errors
 
diff --git a/src/test/ui/issues/issue-72574-1.rs b/src/test/ui/pattern/issue-72574-1.rs
index 1b80a21793a..1b80a21793a 100644
--- a/src/test/ui/issues/issue-72574-1.rs
+++ b/src/test/ui/pattern/issue-72574-1.rs
diff --git a/src/test/ui/issues/issue-72574-1.stderr b/src/test/ui/pattern/issue-72574-1.stderr
index 653869a237d..653869a237d 100644
--- a/src/test/ui/issues/issue-72574-1.stderr
+++ b/src/test/ui/pattern/issue-72574-1.stderr
diff --git a/src/test/ui/pattern/usefulness/tuple-struct-nonexhaustive.stderr b/src/test/ui/pattern/usefulness/tuple-struct-nonexhaustive.stderr
index e2a65ff8524..fc0430d06fa 100644
--- a/src/test/ui/pattern/usefulness/tuple-struct-nonexhaustive.stderr
+++ b/src/test/ui/pattern/usefulness/tuple-struct-nonexhaustive.stderr
@@ -12,7 +12,7 @@ LL | struct Foo(isize, isize);
    = note: the matched value is of type `Foo`
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
-LL ~         Foo(2, b) => println!("{}", b),
+LL ~         Foo(2, b) => println!("{}", b)
 LL +         Foo(_, _) => todo!()
    |
 
diff --git a/src/test/ui/polymorphization/generators.stderr b/src/test/ui/polymorphization/generators.stderr
index 9cabb21e784..5edbb119f78 100644
--- a/src/test/ui/polymorphization/generators.stderr
+++ b/src/test/ui/polymorphization/generators.stderr
@@ -19,6 +19,12 @@ LL | |         2
 LL | |     }
    | |_____^
 
+note: the above error was encountered while instantiating `fn finish::<[generator@$DIR/generators.rs:35:5: 39:6], u32, u32>`
+  --> $DIR/generators.rs:86:5
+   |
+LL |     finish(unused_type::<u32>());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
 error: item has unused generic parameters
   --> $DIR/generators.rs:60:5
    |
@@ -31,5 +37,11 @@ LL | |         2
 LL | |     }
    | |_____^
 
+note: the above error was encountered while instantiating `fn finish::<[generator@$DIR/generators.rs:60:5: 64:6], u32, u32>`
+  --> $DIR/generators.rs:89:5
+   |
+LL |     finish(unused_const::<1u32>());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
 error: aborting due to 2 previous errors; 1 warning emitted
 
diff --git a/src/test/ui/polymorphization/predicates.stderr b/src/test/ui/polymorphization/predicates.stderr
index 5fc51e58d72..dc6ebceae21 100644
--- a/src/test/ui/polymorphization/predicates.stderr
+++ b/src/test/ui/polymorphization/predicates.stderr
@@ -41,5 +41,11 @@ error: item has unused generic parameters
 LL | fn bar<I>() {
    |    ^^^ - generic parameter `I` is unused
 
+note: the above error was encountered while instantiating `fn foo::<std::slice::Iter<u32>, T>`
+  --> $DIR/predicates.rs:85:5
+   |
+LL |     foo(x.iter());
+   |     ^^^^^^^^^^^^^
+
 error: aborting due to 6 previous errors
 
diff --git a/src/test/ui/proc-macro/nodelim-groups.rs b/src/test/ui/proc-macro/nodelim-groups.rs
index db2a879f405..ec301990281 100644
--- a/src/test/ui/proc-macro/nodelim-groups.rs
+++ b/src/test/ui/proc-macro/nodelim-groups.rs
@@ -3,7 +3,7 @@
 // compile-flags: -Z span-debug
 // edition:2018
 //
-// Tests the pretty-printing behavior of inserting `NoDelim` groups
+// Tests the pretty-printing behavior of inserting `Invisible`-delimited groups
 
 #![no_std] // Don't load unnecessary hygiene information from std
 extern crate std;
diff --git a/src/test/ui/regions/issue-28848.stderr b/src/test/ui/regions/issue-28848.base.stderr
index afa0c9c76b2..f10b19738c4 100644
--- a/src/test/ui/regions/issue-28848.stderr
+++ b/src/test/ui/regions/issue-28848.base.stderr
@@ -1,16 +1,16 @@
 error[E0478]: lifetime bound not satisfied
-  --> $DIR/issue-28848.rs:10:5
+  --> $DIR/issue-28848.rs:14:5
    |
 LL |     Foo::<'a, 'b>::xmute(u)
    |     ^^^^^^^^^^^^^
    |
 note: lifetime parameter instantiated with the lifetime `'b` as defined here
-  --> $DIR/issue-28848.rs:9:16
+  --> $DIR/issue-28848.rs:13:16
    |
 LL | pub fn foo<'a, 'b>(u: &'b ()) -> &'a () {
    |                ^^
 note: but lifetime parameter must outlive the lifetime `'a` as defined here
-  --> $DIR/issue-28848.rs:9:12
+  --> $DIR/issue-28848.rs:13:12
    |
 LL | pub fn foo<'a, 'b>(u: &'b ()) -> &'a () {
    |            ^^
diff --git a/src/test/ui/regions/issue-28848.nll.stderr b/src/test/ui/regions/issue-28848.nll.stderr
index a29dac4c9c8..f9de8948272 100644
--- a/src/test/ui/regions/issue-28848.nll.stderr
+++ b/src/test/ui/regions/issue-28848.nll.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/issue-28848.rs:10:5
+  --> $DIR/issue-28848.rs:14:5
    |
 LL | pub fn foo<'a, 'b>(u: &'b ()) -> &'a () {
    |            --  -- lifetime `'b` defined here
diff --git a/src/test/ui/regions/issue-28848.rs b/src/test/ui/regions/issue-28848.rs
index a6250239030..d8ab42a08d4 100644
--- a/src/test/ui/regions/issue-28848.rs
+++ b/src/test/ui/regions/issue-28848.rs
@@ -1,3 +1,7 @@
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 struct Foo<'a, 'b: 'a>(&'a &'b ());
 
 impl<'a, 'b> Foo<'a, 'b> {
@@ -7,7 +11,9 @@ impl<'a, 'b> Foo<'a, 'b> {
 }
 
 pub fn foo<'a, 'b>(u: &'b ()) -> &'a () {
-    Foo::<'a, 'b>::xmute(u) //~ ERROR lifetime bound not satisfied
+    Foo::<'a, 'b>::xmute(u)
+    //[base]~^ ERROR lifetime bound not satisfied
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn main() {}
diff --git a/src/test/ui/regions/region-invariant-static-error-reporting.stderr b/src/test/ui/regions/region-invariant-static-error-reporting.base.stderr
index 34287070eec..ce21751a95a 100644
--- a/src/test/ui/regions/region-invariant-static-error-reporting.stderr
+++ b/src/test/ui/regions/region-invariant-static-error-reporting.base.stderr
@@ -1,5 +1,5 @@
 error[E0308]: `if` and `else` have incompatible types
-  --> $DIR/region-invariant-static-error-reporting.rs:17:9
+  --> $DIR/region-invariant-static-error-reporting.rs:21:9
    |
 LL |       let bad = if x.is_some() {
    |  _______________-
@@ -14,7 +14,7 @@ LL | |     };
    = note: expected struct `Invariant<'a>`
               found struct `Invariant<'static>`
 note: the lifetime `'a` as defined here...
-  --> $DIR/region-invariant-static-error-reporting.rs:13:10
+  --> $DIR/region-invariant-static-error-reporting.rs:17:10
    |
 LL | fn unify<'a>(x: Option<Invariant<'a>>, f: fn(Invariant<'a>)) {
    |          ^^
diff --git a/src/test/ui/regions/region-invariant-static-error-reporting.nll.stderr b/src/test/ui/regions/region-invariant-static-error-reporting.nll.stderr
index 6e7eb734a50..6905fd008c5 100644
--- a/src/test/ui/regions/region-invariant-static-error-reporting.nll.stderr
+++ b/src/test/ui/regions/region-invariant-static-error-reporting.nll.stderr
@@ -1,5 +1,5 @@
 error[E0521]: borrowed data escapes outside of function
-  --> $DIR/region-invariant-static-error-reporting.rs:15:9
+  --> $DIR/region-invariant-static-error-reporting.rs:19:9
    |
 LL | fn unify<'a>(x: Option<Invariant<'a>>, f: fn(Invariant<'a>)) {
    |          --  - `x` is a reference that is only valid in the function body
diff --git a/src/test/ui/regions/region-invariant-static-error-reporting.rs b/src/test/ui/regions/region-invariant-static-error-reporting.rs
index 911904813d0..b81022ca4b4 100644
--- a/src/test/ui/regions/region-invariant-static-error-reporting.rs
+++ b/src/test/ui/regions/region-invariant-static-error-reporting.rs
@@ -3,8 +3,12 @@
 // over time, but this test used to exhibit some pretty bogus messages
 // that were not remotely helpful.
 
-// error-pattern:the lifetime `'a`
-// error-pattern:the static lifetime
+// revisions: base nll
+// ignore-compare-mode-nll
+//[base] error-pattern:the lifetime `'a`
+//[base] error-pattern:the static lifetime
+//[nll] compile-flags: -Z borrowck=mir
+//[nll] error-pattern:argument requires that `'a` must outlive `'static`
 
 struct Invariant<'a>(Option<&'a mut &'a mut ()>);
 
@@ -12,9 +16,9 @@ fn mk_static() -> Invariant<'static> { Invariant(None) }
 
 fn unify<'a>(x: Option<Invariant<'a>>, f: fn(Invariant<'a>)) {
     let bad = if x.is_some() {
-        x.unwrap()
+        x.unwrap() //[nll]~ ERROR borrowed data escapes outside of function [E0521]
     } else {
-        mk_static()
+        mk_static() //[base]~ ERROR `if` and `else` have incompatible types [E0308]
     };
     f(bad);
 }
diff --git a/src/test/ui/regions/region-lifetime-bounds-on-fns-where-clause.stderr b/src/test/ui/regions/region-lifetime-bounds-on-fns-where-clause.base.stderr
index b83e07663fa..2ba4f4f5d9f 100644
--- a/src/test/ui/regions/region-lifetime-bounds-on-fns-where-clause.stderr
+++ b/src/test/ui/regions/region-lifetime-bounds-on-fns-where-clause.base.stderr
@@ -1,5 +1,5 @@
 error[E0623]: lifetime mismatch
-  --> $DIR/region-lifetime-bounds-on-fns-where-clause.rs:8:10
+  --> $DIR/region-lifetime-bounds-on-fns-where-clause.rs:12:10
    |
 LL | fn b<'a, 'b>(x: &mut &'a isize, y: &mut &'b isize) {
    |                      ---------          --------- these two types are declared with different lifetimes...
@@ -8,7 +8,7 @@ LL |     *x = *y;
    |          ^^ ...but data from `y` flows into `x` here
 
 error[E0623]: lifetime mismatch
-  --> $DIR/region-lifetime-bounds-on-fns-where-clause.rs:14:7
+  --> $DIR/region-lifetime-bounds-on-fns-where-clause.rs:20:7
    |
 LL | fn c<'a,'b>(x: &mut &'a isize, y: &mut &'b isize) {
    |                     ---------          --------- these two types are declared with different lifetimes...
@@ -17,7 +17,7 @@ LL |     a(x, y);
    |       ^ ...but data from `y` flows into `x` here
 
 error[E0308]: mismatched types
-  --> $DIR/region-lifetime-bounds-on-fns-where-clause.rs:20:43
+  --> $DIR/region-lifetime-bounds-on-fns-where-clause.rs:28:43
    |
 LL |     let _: fn(&mut &isize, &mut &isize) = a;
    |                                           ^ one type is more general than the other
diff --git a/src/test/ui/regions/region-lifetime-bounds-on-fns-where-clause.nll.stderr b/src/test/ui/regions/region-lifetime-bounds-on-fns-where-clause.nll.stderr
index 233a040491c..c6430974334 100644
--- a/src/test/ui/regions/region-lifetime-bounds-on-fns-where-clause.nll.stderr
+++ b/src/test/ui/regions/region-lifetime-bounds-on-fns-where-clause.nll.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/region-lifetime-bounds-on-fns-where-clause.rs:8:5
+  --> $DIR/region-lifetime-bounds-on-fns-where-clause.rs:12:5
    |
 LL | fn b<'a, 'b>(x: &mut &'a isize, y: &mut &'b isize) {
    |      --  -- lifetime `'b` defined here
@@ -12,7 +12,7 @@ LL |     *x = *y;
    = help: consider adding the following bound: `'b: 'a`
 
 error: lifetime may not live long enough
-  --> $DIR/region-lifetime-bounds-on-fns-where-clause.rs:14:5
+  --> $DIR/region-lifetime-bounds-on-fns-where-clause.rs:20:5
    |
 LL | fn c<'a,'b>(x: &mut &'a isize, y: &mut &'b isize) {
    |      -- -- lifetime `'b` defined here
@@ -28,7 +28,7 @@ LL |     a(x, y);
    = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
 
 error[E0308]: mismatched types
-  --> $DIR/region-lifetime-bounds-on-fns-where-clause.rs:20:12
+  --> $DIR/region-lifetime-bounds-on-fns-where-clause.rs:28:12
    |
 LL |     let _: fn(&mut &isize, &mut &isize) = a;
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
@@ -37,7 +37,7 @@ LL |     let _: fn(&mut &isize, &mut &isize) = a;
               found fn pointer `for<'r, 's> fn(&'r mut &isize, &'s mut &isize)`
 
 error[E0308]: mismatched types
-  --> $DIR/region-lifetime-bounds-on-fns-where-clause.rs:20:12
+  --> $DIR/region-lifetime-bounds-on-fns-where-clause.rs:28:12
    |
 LL |     let _: fn(&mut &isize, &mut &isize) = a;
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
diff --git a/src/test/ui/regions/region-lifetime-bounds-on-fns-where-clause.rs b/src/test/ui/regions/region-lifetime-bounds-on-fns-where-clause.rs
index ab4c6d9cf91..ec91d179808 100644
--- a/src/test/ui/regions/region-lifetime-bounds-on-fns-where-clause.rs
+++ b/src/test/ui/regions/region-lifetime-bounds-on-fns-where-clause.rs
@@ -1,3 +1,7 @@
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 fn a<'a, 'b>(x: &mut &'a isize, y: &mut &'b isize) where 'b: 'a {
     // Note: this is legal because of the `'b:'a` declaration.
     *x = *y;
@@ -5,19 +9,25 @@ fn a<'a, 'b>(x: &mut &'a isize, y: &mut &'b isize) where 'b: 'a {
 
 fn b<'a, 'b>(x: &mut &'a isize, y: &mut &'b isize) {
     // Illegal now because there is no `'b:'a` declaration.
-    *x = *y; //~ ERROR E0623
+    *x = *y;
+    //[base]~^ ERROR E0623
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn c<'a,'b>(x: &mut &'a isize, y: &mut &'b isize) {
     // Here we try to call `foo` but do not know that `'a` and `'b` are
     // related as required.
-    a(x, y); //~ ERROR lifetime mismatch [E0623]
+    a(x, y);
+    //[base]~^ ERROR lifetime mismatch [E0623]
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn d() {
     // 'a and 'b are early bound in the function `a` because they appear
     // inconstraints:
-    let _: fn(&mut &isize, &mut &isize) = a; //~ ERROR mismatched types
+    let _: fn(&mut &isize, &mut &isize) = a;
+    //~^ ERROR mismatched types [E0308]
+    //[nll]~^^ ERROR mismatched types [E0308]
 }
 
 fn e() {
diff --git a/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.stderr b/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.base.stderr
index c93f2890f11..537a1fb98a5 100644
--- a/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.stderr
+++ b/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.base.stderr
@@ -1,5 +1,5 @@
 error[E0623]: lifetime mismatch
-  --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:9:10
+  --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:13:10
    |
 LL | fn b<'a, 'b, 'c>(x: &mut &'a isize, y: &mut &'b isize, z: &mut &'c isize) {
    |                          ---------          --------- these two types are declared with different lifetimes...
@@ -8,7 +8,7 @@ LL |     *x = *y;
    |          ^^ ...but data from `y` flows into `x` here
 
 error[E0623]: lifetime mismatch
-  --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:10:10
+  --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:16:10
    |
 LL | fn b<'a, 'b, 'c>(x: &mut &'a isize, y: &mut &'b isize, z: &mut &'c isize) {
    |                                             ---------          ---------
@@ -19,7 +19,7 @@ LL |     *z = *y;
    |          ^^ ...but data from `y` flows into `z` here
 
 error[E0623]: lifetime mismatch
-  --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:16:7
+  --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:22:7
    |
 LL | fn c<'a,'b, 'c>(x: &mut &'a isize, y: &mut &'b isize, z: &mut &'c isize) {
    |                         ---------          --------- these two types are declared with different lifetimes...
@@ -28,7 +28,7 @@ LL |     a(x, y, z);
    |       ^ ...but data from `y` flows into `x` here
 
 error[E0308]: mismatched types
-  --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:22:56
+  --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:30:56
    |
 LL |     let _: fn(&mut &isize, &mut &isize, &mut &isize) = a;
    |                                                        ^ one type is more general than the other
diff --git a/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.nll.stderr b/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.nll.stderr
index 00119743acd..053078f58df 100644
--- a/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.nll.stderr
+++ b/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.nll.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:9:5
+  --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:13:5
    |
 LL | fn b<'a, 'b, 'c>(x: &mut &'a isize, y: &mut &'b isize, z: &mut &'c isize) {
    |      --  -- lifetime `'b` defined here
@@ -12,7 +12,7 @@ LL |     *x = *y;
    = help: consider adding the following bound: `'b: 'a`
 
 error: lifetime may not live long enough
-  --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:16:5
+  --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:22:5
    |
 LL | fn c<'a,'b, 'c>(x: &mut &'a isize, y: &mut &'b isize, z: &mut &'c isize) {
    |      -- -- lifetime `'b` defined here
@@ -28,7 +28,7 @@ LL |     a(x, y, z);
    = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
 
 error[E0308]: mismatched types
-  --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:22:12
+  --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:30:12
    |
 LL |     let _: fn(&mut &isize, &mut &isize, &mut &isize) = a;
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
@@ -37,7 +37,7 @@ LL |     let _: fn(&mut &isize, &mut &isize, &mut &isize) = a;
               found fn pointer `for<'r, 's, 't0> fn(&'r mut &isize, &'s mut &isize, &'t0 mut &isize)`
 
 error[E0308]: mismatched types
-  --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:22:12
+  --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:30:12
    |
 LL |     let _: fn(&mut &isize, &mut &isize, &mut &isize) = a;
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
@@ -46,7 +46,7 @@ LL |     let _: fn(&mut &isize, &mut &isize, &mut &isize) = a;
               found fn pointer `for<'r, 's, 't0> fn(&'r mut &isize, &'s mut &isize, &'t0 mut &isize)`
 
 error[E0308]: mismatched types
-  --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:22:12
+  --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:30:12
    |
 LL |     let _: fn(&mut &isize, &mut &isize, &mut &isize) = a;
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
diff --git a/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.polonius.stderr b/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.polonius.stderr
deleted file mode 100644
index 13741664ef2..00000000000
--- a/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.polonius.stderr
+++ /dev/null
@@ -1,82 +0,0 @@
-error: lifetime may not live long enough
-  --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:9:5
-   |
-LL | fn b<'a, 'b, 'c>(x: &mut &'a isize, y: &mut &'b isize, z: &mut &'c isize) {
-   |      --  -- lifetime `'b` defined here
-   |      |
-   |      lifetime `'a` defined here
-LL |     // Illegal now because there is no `'b:'a` declaration.
-LL |     *x = *y;
-   |     ^^^^^^^ assignment requires that `'b` must outlive `'a`
-   |
-   = help: consider adding the following bound: `'b: 'a`
-
-error: lifetime may not live long enough
-  --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:10:5
-   |
-LL | fn b<'a, 'b, 'c>(x: &mut &'a isize, y: &mut &'b isize, z: &mut &'c isize) {
-   |          --  -- lifetime `'c` defined here
-   |          |
-   |          lifetime `'b` defined here
-...
-LL |     *z = *y;
-   |     ^^^^^^^ assignment requires that `'b` must outlive `'c`
-   |
-   = help: consider adding the following bound: `'b: 'c`
-
-help: add bound `'b: 'a + 'c`
-
-error: lifetime may not live long enough
-  --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:16:5
-   |
-LL | fn c<'a,'b, 'c>(x: &mut &'a isize, y: &mut &'b isize, z: &mut &'c isize) {
-   |      -- -- lifetime `'b` defined here
-   |      |
-   |      lifetime `'a` defined here
-...
-LL |     a(x, y, z);
-   |     ^^^^^^^^^^ argument requires that `'b` must outlive `'a`
-   |
-   = help: consider adding the following bound: `'b: 'a`
-   = note: requirement occurs because of a mutable reference to &isize
-   = note: mutable references are invariant over their type parameter
-   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
-
-error: lifetime may not live long enough
-  --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:16:5
-   |
-LL | fn c<'a,'b, 'c>(x: &mut &'a isize, y: &mut &'b isize, z: &mut &'c isize) {
-   |         --  -- lifetime `'c` defined here
-   |         |
-   |         lifetime `'b` defined here
-...
-LL |     a(x, y, z);
-   |     ^^^^^^^^^^ argument requires that `'b` must outlive `'c`
-   |
-   = help: consider adding the following bound: `'b: 'c`
-   = note: requirement occurs because of a mutable reference to &isize
-   = note: mutable references are invariant over their type parameter
-   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
-
-help: add bound `'b: 'a + 'c`
-
-error: higher-ranked subtype error
-  --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:22:12
-   |
-LL |     let _: fn(&mut &isize, &mut &isize, &mut &isize) = a;
-   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: higher-ranked subtype error
-  --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:22:12
-   |
-LL |     let _: fn(&mut &isize, &mut &isize, &mut &isize) = a;
-   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: higher-ranked subtype error
-  --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:22:12
-   |
-LL |     let _: fn(&mut &isize, &mut &isize, &mut &isize) = a;
-   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 7 previous errors
-
diff --git a/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.rs b/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.rs
index 066522548ad..8b5c1d47ec6 100644
--- a/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.rs
+++ b/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.rs
@@ -1,3 +1,7 @@
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 fn a<'a, 'b, 'c>(x: &mut &'a isize, y: &mut &'b isize, z: &mut &'c isize) where 'b: 'a + 'c {
     // Note: this is legal because of the `'b:'a` declaration.
     *x = *y;
@@ -6,20 +10,27 @@ fn a<'a, 'b, 'c>(x: &mut &'a isize, y: &mut &'b isize, z: &mut &'c isize) where
 
 fn b<'a, 'b, 'c>(x: &mut &'a isize, y: &mut &'b isize, z: &mut &'c isize) {
     // Illegal now because there is no `'b:'a` declaration.
-    *x = *y; //~ ERROR E0623
-    *z = *y; //~ ERROR E0623
+    *x = *y;
+    //[base]~^ ERROR E0623
+    //[nll]~^^ ERROR lifetime may not live long enough
+    *z = *y; //[base]~ ERROR E0623
 }
 
 fn c<'a,'b, 'c>(x: &mut &'a isize, y: &mut &'b isize, z: &mut &'c isize) {
     // Here we try to call `foo` but do not know that `'a` and `'b` are
     // related as required.
-    a(x, y, z); //~ ERROR lifetime mismatch [E0623]
+    a(x, y, z);
+    //[base]~^ ERROR lifetime mismatch [E0623]
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn d() {
     // 'a and 'b are early bound in the function `a` because they appear
     // inconstraints:
-    let _: fn(&mut &isize, &mut &isize, &mut &isize) = a; //~ ERROR E0308
+    let _: fn(&mut &isize, &mut &isize, &mut &isize) = a;
+    //~^ ERROR E0308
+    //[nll]~^^ ERROR mismatched types [E0308]
+    //[nll]~| ERROR mismatched types [E0308]
 }
 
 fn e() {
diff --git a/src/test/ui/regions/region-object-lifetime-2.stderr b/src/test/ui/regions/region-object-lifetime-2.base.stderr
index 380e27ab0e0..118fe476500 100644
--- a/src/test/ui/regions/region-object-lifetime-2.stderr
+++ b/src/test/ui/regions/region-object-lifetime-2.base.stderr
@@ -1,26 +1,26 @@
 error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
-  --> $DIR/region-object-lifetime-2.rs:10:7
+  --> $DIR/region-object-lifetime-2.rs:14:7
    |
 LL |     x.borrowed()
    |       ^^^^^^^^
    |
 note: first, the lifetime cannot outlive the lifetime `'a` as defined here...
-  --> $DIR/region-object-lifetime-2.rs:9:42
+  --> $DIR/region-object-lifetime-2.rs:13:42
    |
 LL | fn borrowed_receiver_different_lifetimes<'a,'b>(x: &'a dyn Foo) -> &'b () {
    |                                          ^^
 note: ...so that reference does not outlive borrowed content
-  --> $DIR/region-object-lifetime-2.rs:10:5
+  --> $DIR/region-object-lifetime-2.rs:14:5
    |
 LL |     x.borrowed()
    |     ^
 note: but, the lifetime must be valid for the lifetime `'b` as defined here...
-  --> $DIR/region-object-lifetime-2.rs:9:45
+  --> $DIR/region-object-lifetime-2.rs:13:45
    |
 LL | fn borrowed_receiver_different_lifetimes<'a,'b>(x: &'a dyn Foo) -> &'b () {
    |                                             ^^
 note: ...so that reference does not outlive borrowed content
-  --> $DIR/region-object-lifetime-2.rs:10:5
+  --> $DIR/region-object-lifetime-2.rs:14:5
    |
 LL |     x.borrowed()
    |     ^^^^^^^^^^^^
diff --git a/src/test/ui/regions/region-object-lifetime-2.nll.stderr b/src/test/ui/regions/region-object-lifetime-2.nll.stderr
index d95289f3f9d..c0b09ebb6f5 100644
--- a/src/test/ui/regions/region-object-lifetime-2.nll.stderr
+++ b/src/test/ui/regions/region-object-lifetime-2.nll.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/region-object-lifetime-2.rs:10:5
+  --> $DIR/region-object-lifetime-2.rs:14:5
    |
 LL | fn borrowed_receiver_different_lifetimes<'a,'b>(x: &'a dyn Foo) -> &'b () {
    |                                          -- -- lifetime `'b` defined here
diff --git a/src/test/ui/regions/region-object-lifetime-2.rs b/src/test/ui/regions/region-object-lifetime-2.rs
index 42798487893..e12b9822f60 100644
--- a/src/test/ui/regions/region-object-lifetime-2.rs
+++ b/src/test/ui/regions/region-object-lifetime-2.rs
@@ -1,13 +1,19 @@
 // Various tests related to testing how region inference works
 // with respect to the object receivers.
 
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 trait Foo {
     fn borrowed<'a>(&'a self) -> &'a ();
 }
 
 // Borrowed receiver but two distinct lifetimes, we get an error.
 fn borrowed_receiver_different_lifetimes<'a,'b>(x: &'a dyn Foo) -> &'b () {
-    x.borrowed() //~ ERROR cannot infer
+    x.borrowed()
+    //[base]~^ ERROR cannot infer
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn main() {}
diff --git a/src/test/ui/regions/region-object-lifetime-4.stderr b/src/test/ui/regions/region-object-lifetime-4.base.stderr
index b59163ef13b..3765076a9c5 100644
--- a/src/test/ui/regions/region-object-lifetime-4.stderr
+++ b/src/test/ui/regions/region-object-lifetime-4.base.stderr
@@ -1,26 +1,26 @@
 error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
-  --> $DIR/region-object-lifetime-4.rs:12:7
+  --> $DIR/region-object-lifetime-4.rs:16:7
    |
 LL |     x.borrowed()
    |       ^^^^^^^^
    |
 note: first, the lifetime cannot outlive the lifetime `'a` as defined here...
-  --> $DIR/region-object-lifetime-4.rs:11:41
+  --> $DIR/region-object-lifetime-4.rs:15:41
    |
 LL | fn borrowed_receiver_related_lifetimes2<'a,'b>(x: &'a (dyn Foo + 'b)) -> &'b () {
    |                                         ^^
 note: ...so that reference does not outlive borrowed content
-  --> $DIR/region-object-lifetime-4.rs:12:5
+  --> $DIR/region-object-lifetime-4.rs:16:5
    |
 LL |     x.borrowed()
    |     ^
 note: but, the lifetime must be valid for the lifetime `'b` as defined here...
-  --> $DIR/region-object-lifetime-4.rs:11:44
+  --> $DIR/region-object-lifetime-4.rs:15:44
    |
 LL | fn borrowed_receiver_related_lifetimes2<'a,'b>(x: &'a (dyn Foo + 'b)) -> &'b () {
    |                                            ^^
 note: ...so that reference does not outlive borrowed content
-  --> $DIR/region-object-lifetime-4.rs:12:5
+  --> $DIR/region-object-lifetime-4.rs:16:5
    |
 LL |     x.borrowed()
    |     ^^^^^^^^^^^^
diff --git a/src/test/ui/regions/region-object-lifetime-4.nll.stderr b/src/test/ui/regions/region-object-lifetime-4.nll.stderr
index fda66a2412c..a2a958f90b2 100644
--- a/src/test/ui/regions/region-object-lifetime-4.nll.stderr
+++ b/src/test/ui/regions/region-object-lifetime-4.nll.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/region-object-lifetime-4.rs:12:5
+  --> $DIR/region-object-lifetime-4.rs:16:5
    |
 LL | fn borrowed_receiver_related_lifetimes2<'a,'b>(x: &'a (dyn Foo + 'b)) -> &'b () {
    |                                         -- -- lifetime `'b` defined here
diff --git a/src/test/ui/regions/region-object-lifetime-4.rs b/src/test/ui/regions/region-object-lifetime-4.rs
index 4fe12b2acfc..aad9c2c9521 100644
--- a/src/test/ui/regions/region-object-lifetime-4.rs
+++ b/src/test/ui/regions/region-object-lifetime-4.rs
@@ -1,6 +1,10 @@
 // Various tests related to testing how region inference works
 // with respect to the object receivers.
 
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 trait Foo {
     fn borrowed<'a>(&'a self) -> &'a ();
 }
@@ -9,7 +13,9 @@ trait Foo {
 // with the longer lifetime when (from the signature) we only know
 // that it lives as long as the shorter lifetime. Therefore, error.
 fn borrowed_receiver_related_lifetimes2<'a,'b>(x: &'a (dyn Foo + 'b)) -> &'b () {
-    x.borrowed() //~ ERROR cannot infer
+    x.borrowed()
+    //[base]~^ ERROR cannot infer
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn main() {}
diff --git a/src/test/ui/regions/region-object-lifetime-in-coercion.stderr b/src/test/ui/regions/region-object-lifetime-in-coercion.base.stderr
index d8932c067ac..85bfa16b3d3 100644
--- a/src/test/ui/regions/region-object-lifetime-in-coercion.stderr
+++ b/src/test/ui/regions/region-object-lifetime-in-coercion.base.stderr
@@ -1,5 +1,5 @@
 error[E0759]: `v` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
-  --> $DIR/region-object-lifetime-in-coercion.rs:8:46
+  --> $DIR/region-object-lifetime-in-coercion.rs:12:46
    |
 LL | fn a(v: &[u8]) -> Box<dyn Foo + 'static> {
    |         ----- this data with an anonymous lifetime `'_`...
@@ -16,7 +16,7 @@ LL | fn a(v: &'static [u8]) -> Box<dyn Foo + 'static> {
    |         ~~~~~~~~~~~~~
 
 error[E0759]: `v` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
-  --> $DIR/region-object-lifetime-in-coercion.rs:13:14
+  --> $DIR/region-object-lifetime-in-coercion.rs:19:14
    |
 LL | fn b(v: &[u8]) -> Box<dyn Foo + 'static> {
    |         ----- this data with an anonymous lifetime `'_`...
@@ -24,7 +24,7 @@ LL |     Box::new(v)
    |              ^ ...is used and required to live as long as `'static` here
    |
 note: `'static` lifetime requirement introduced by the return type
-  --> $DIR/region-object-lifetime-in-coercion.rs:12:33
+  --> $DIR/region-object-lifetime-in-coercion.rs:18:33
    |
 LL | fn b(v: &[u8]) -> Box<dyn Foo + 'static> {
    |                                 ^^^^^^^ `'static` requirement introduced here
@@ -40,7 +40,7 @@ LL | fn b(v: &'static [u8]) -> Box<dyn Foo + 'static> {
    |         ~~~~~~~~~~~~~
 
 error[E0759]: `v` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
-  --> $DIR/region-object-lifetime-in-coercion.rs:19:14
+  --> $DIR/region-object-lifetime-in-coercion.rs:27:14
    |
 LL | fn c(v: &[u8]) -> Box<dyn Foo> {
    |         ----- this data with an anonymous lifetime `'_`...
@@ -49,7 +49,7 @@ LL |     Box::new(v)
    |              ^ ...is used and required to live as long as `'static` here
    |
 note: `'static` lifetime requirement introduced by the return type
-  --> $DIR/region-object-lifetime-in-coercion.rs:16:23
+  --> $DIR/region-object-lifetime-in-coercion.rs:24:23
    |
 LL | fn c(v: &[u8]) -> Box<dyn Foo> {
    |                       ^^^^^^^ `'static` requirement introduced here
@@ -62,30 +62,30 @@ LL | fn c(v: &[u8]) -> Box<dyn Foo + '_> {
    |                               ++++
 
 error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
-  --> $DIR/region-object-lifetime-in-coercion.rs:23:14
+  --> $DIR/region-object-lifetime-in-coercion.rs:33:14
    |
 LL |     Box::new(v)
    |              ^
    |
 note: first, the lifetime cannot outlive the lifetime `'a` as defined here...
-  --> $DIR/region-object-lifetime-in-coercion.rs:22:6
+  --> $DIR/region-object-lifetime-in-coercion.rs:32:6
    |
 LL | fn d<'a,'b>(v: &'a [u8]) -> Box<dyn Foo+'b> {
    |      ^^
 note: ...so that the expression is assignable
-  --> $DIR/region-object-lifetime-in-coercion.rs:23:14
+  --> $DIR/region-object-lifetime-in-coercion.rs:33:14
    |
 LL |     Box::new(v)
    |              ^
    = note: expected `&[u8]`
               found `&'a [u8]`
 note: but, the lifetime must be valid for the lifetime `'b` as defined here...
-  --> $DIR/region-object-lifetime-in-coercion.rs:22:9
+  --> $DIR/region-object-lifetime-in-coercion.rs:32:9
    |
 LL | fn d<'a,'b>(v: &'a [u8]) -> Box<dyn Foo+'b> {
    |         ^^
 note: ...so that the types are compatible
-  --> $DIR/region-object-lifetime-in-coercion.rs:23:5
+  --> $DIR/region-object-lifetime-in-coercion.rs:33:5
    |
 LL |     Box::new(v)
    |     ^^^^^^^^^^^
diff --git a/src/test/ui/regions/region-object-lifetime-in-coercion.nll.stderr b/src/test/ui/regions/region-object-lifetime-in-coercion.nll.stderr
index 92588819076..724b06ce8b1 100644
--- a/src/test/ui/regions/region-object-lifetime-in-coercion.nll.stderr
+++ b/src/test/ui/regions/region-object-lifetime-in-coercion.nll.stderr
@@ -1,30 +1,53 @@
 error: lifetime may not live long enough
-  --> $DIR/region-object-lifetime-in-coercion.rs:8:12
+  --> $DIR/region-object-lifetime-in-coercion.rs:12:12
    |
 LL | fn a(v: &[u8]) -> Box<dyn Foo + 'static> {
    |         - let's call the lifetime of this reference `'1`
 LL |     let x: Box<dyn Foo + 'static> = Box::new(v);
    |            ^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'1` must outlive `'static`
+   |
+help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v`
+   |
+LL | fn a(v: &[u8]) -> Box<dyn Foo + '_> {
+   |                                 ~~
+help: alternatively, add an explicit `'static` bound to this reference
+   |
+LL | fn a(v: &'static [u8]) -> Box<dyn Foo + 'static> {
+   |         ~~~~~~~~~~~~~
 
 error: lifetime may not live long enough
-  --> $DIR/region-object-lifetime-in-coercion.rs:13:5
+  --> $DIR/region-object-lifetime-in-coercion.rs:19:5
    |
 LL | fn b(v: &[u8]) -> Box<dyn Foo + 'static> {
    |         - let's call the lifetime of this reference `'1`
 LL |     Box::new(v)
    |     ^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static`
+   |
+help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v`
+   |
+LL | fn b(v: &[u8]) -> Box<dyn Foo + '_> {
+   |                                 ~~
+help: alternatively, add an explicit `'static` bound to this reference
+   |
+LL | fn b(v: &'static [u8]) -> Box<dyn Foo + 'static> {
+   |         ~~~~~~~~~~~~~
 
 error: lifetime may not live long enough
-  --> $DIR/region-object-lifetime-in-coercion.rs:19:5
+  --> $DIR/region-object-lifetime-in-coercion.rs:27:5
    |
 LL | fn c(v: &[u8]) -> Box<dyn Foo> {
    |         - let's call the lifetime of this reference `'1`
 ...
 LL |     Box::new(v)
    |     ^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static`
+   |
+help: to declare that the trait object captures data from argument `v`, you can add an explicit `'_` lifetime bound
+   |
+LL | fn c(v: &[u8]) -> Box<dyn Foo + '_> {
+   |                               ++++
 
 error: lifetime may not live long enough
-  --> $DIR/region-object-lifetime-in-coercion.rs:23:5
+  --> $DIR/region-object-lifetime-in-coercion.rs:33:5
    |
 LL | fn d<'a,'b>(v: &'a [u8]) -> Box<dyn Foo+'b> {
    |      -- -- lifetime `'b` defined here
diff --git a/src/test/ui/regions/region-object-lifetime-in-coercion.rs b/src/test/ui/regions/region-object-lifetime-in-coercion.rs
index 9d3f485e314..ed28d6c0ff1 100644
--- a/src/test/ui/regions/region-object-lifetime-in-coercion.rs
+++ b/src/test/ui/regions/region-object-lifetime-in-coercion.rs
@@ -1,26 +1,38 @@
 // Test that attempts to implicitly coerce a value into an
 // object respect the lifetime bound on the object type.
 
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 trait Foo {}
 impl<'a> Foo for &'a [u8] {}
 
 fn a(v: &[u8]) -> Box<dyn Foo + 'static> {
-    let x: Box<dyn Foo + 'static> = Box::new(v); //~ ERROR E0759
+    let x: Box<dyn Foo + 'static> = Box::new(v);
+    //[base]~^ ERROR E0759
+    //[nll]~^^ ERROR lifetime may not live long enough
     x
 }
 
 fn b(v: &[u8]) -> Box<dyn Foo + 'static> {
-    Box::new(v) //~ ERROR E0759
+    Box::new(v)
+    //[base]~^ ERROR E0759
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn c(v: &[u8]) -> Box<dyn Foo> {
     // same as previous case due to RFC 599
 
-    Box::new(v) //~ ERROR E0759
+    Box::new(v)
+    //[base]~^ ERROR E0759
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn d<'a,'b>(v: &'a [u8]) -> Box<dyn Foo+'b> {
-    Box::new(v) //~ ERROR cannot infer an appropriate lifetime due to conflicting
+    Box::new(v)
+    //[base]~^ ERROR cannot infer an appropriate lifetime due to conflicting
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn e<'a:'b,'b>(v: &'a [u8]) -> Box<dyn Foo+'b> {
diff --git a/src/test/ui/regions/regions-addr-of-self.stderr b/src/test/ui/regions/regions-addr-of-self.base.stderr
index 3453c6458f1..3167c2f2107 100644
--- a/src/test/ui/regions/regions-addr-of-self.stderr
+++ b/src/test/ui/regions/regions-addr-of-self.base.stderr
@@ -1,5 +1,5 @@
 error[E0759]: `self` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
-  --> $DIR/regions-addr-of-self.rs:7:37
+  --> $DIR/regions-addr-of-self.rs:11:37
    |
 LL |     pub fn chase_cat(&mut self) {
    |                      --------- this data with an anonymous lifetime `'_`...
diff --git a/src/test/ui/regions/regions-addr-of-self.nll.stderr b/src/test/ui/regions/regions-addr-of-self.nll.stderr
index 3d7aac74bd4..1f720520f6b 100644
--- a/src/test/ui/regions/regions-addr-of-self.nll.stderr
+++ b/src/test/ui/regions/regions-addr-of-self.nll.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/regions-addr-of-self.rs:7:16
+  --> $DIR/regions-addr-of-self.rs:11:16
    |
 LL |     pub fn chase_cat(&mut self) {
    |                      - let's call the lifetime of this reference `'1`
diff --git a/src/test/ui/regions/regions-addr-of-self.rs b/src/test/ui/regions/regions-addr-of-self.rs
index 4eb1b275f16..698433c71b3 100644
--- a/src/test/ui/regions/regions-addr-of-self.rs
+++ b/src/test/ui/regions/regions-addr-of-self.rs
@@ -1,10 +1,16 @@
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 struct Dog {
     cats_chased: usize,
 }
 
 impl Dog {
     pub fn chase_cat(&mut self) {
-        let p: &'static mut usize = &mut self.cats_chased; //~ ERROR E0759
+        let p: &'static mut usize = &mut self.cats_chased;
+        //[base]~^ ERROR E0759
+        //[nll]~^^ ERROR lifetime may not live long enough
         *p += 1;
     }
 
diff --git a/src/test/ui/regions/regions-addr-of-upvar-self.stderr b/src/test/ui/regions/regions-addr-of-upvar-self.base.stderr
index f638064ef83..42d0638e8b7 100644
--- a/src/test/ui/regions/regions-addr-of-upvar-self.stderr
+++ b/src/test/ui/regions/regions-addr-of-upvar-self.base.stderr
@@ -1,22 +1,22 @@
 error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
-  --> $DIR/regions-addr-of-upvar-self.rs:8:41
+  --> $DIR/regions-addr-of-upvar-self.rs:12:41
    |
 LL |             let p: &'static mut usize = &mut self.food;
    |                                         ^^^^^^^^^^^^^^
    |
 note: first, the lifetime cannot outlive the lifetime `'_` as defined here...
-  --> $DIR/regions-addr-of-upvar-self.rs:7:18
+  --> $DIR/regions-addr-of-upvar-self.rs:11:18
    |
 LL |         let _f = || {
    |                  ^^
 note: ...so that closure can access `self`
-  --> $DIR/regions-addr-of-upvar-self.rs:8:41
+  --> $DIR/regions-addr-of-upvar-self.rs:12:41
    |
 LL |             let p: &'static mut usize = &mut self.food;
    |                                         ^^^^^^^^^^^^^^
    = note: but, the lifetime must be valid for the static lifetime...
 note: ...so that reference does not outlive borrowed content
-  --> $DIR/regions-addr-of-upvar-self.rs:8:41
+  --> $DIR/regions-addr-of-upvar-self.rs:12:41
    |
 LL |             let p: &'static mut usize = &mut self.food;
    |                                         ^^^^^^^^^^^^^^
diff --git a/src/test/ui/regions/regions-addr-of-upvar-self.nll.stderr b/src/test/ui/regions/regions-addr-of-upvar-self.nll.stderr
index c16a6f8585b..b8e37e92316 100644
--- a/src/test/ui/regions/regions-addr-of-upvar-self.nll.stderr
+++ b/src/test/ui/regions/regions-addr-of-upvar-self.nll.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/regions-addr-of-upvar-self.rs:8:20
+  --> $DIR/regions-addr-of-upvar-self.rs:12:20
    |
 LL |         let _f = || {
    |                  -- lifetime `'1` represents this closure's body
@@ -9,7 +9,7 @@ LL |             let p: &'static mut usize = &mut self.food;
    = note: closure implements `FnMut`, so references to captured variables can't escape the closure
 
 error: lifetime may not live long enough
-  --> $DIR/regions-addr-of-upvar-self.rs:8:20
+  --> $DIR/regions-addr-of-upvar-self.rs:12:20
    |
 LL |     pub fn chase_cat(&mut self) {
    |                      - let's call the lifetime of this reference `'1`
@@ -18,7 +18,7 @@ LL |             let p: &'static mut usize = &mut self.food;
    |                    ^^^^^^^^^^^^^^^^^^ type annotation requires that `'1` must outlive `'static`
 
 error[E0597]: `self` does not live long enough
-  --> $DIR/regions-addr-of-upvar-self.rs:8:46
+  --> $DIR/regions-addr-of-upvar-self.rs:12:46
    |
 LL |         let _f = || {
    |                  -- value captured here
diff --git a/src/test/ui/regions/regions-addr-of-upvar-self.rs b/src/test/ui/regions/regions-addr-of-upvar-self.rs
index 6159ab02d3d..36cc592d47c 100644
--- a/src/test/ui/regions/regions-addr-of-upvar-self.rs
+++ b/src/test/ui/regions/regions-addr-of-upvar-self.rs
@@ -1,3 +1,7 @@
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 struct Dog {
     food: usize,
 }
@@ -5,7 +9,11 @@ struct Dog {
 impl Dog {
     pub fn chase_cat(&mut self) {
         let _f = || {
-            let p: &'static mut usize = &mut self.food; //~ ERROR cannot infer
+            let p: &'static mut usize = &mut self.food;
+            //[base]~^ ERROR cannot infer
+            //[nll]~^^ ERROR lifetime may not live long enough
+            //[nll]~^^^ ERROR lifetime may not live long enough
+            //[nll]~^^^^ ERROR E0597
             *p = 3;
         };
     }
diff --git a/src/test/ui/regions/regions-bounded-by-trait-requiring-static.stderr b/src/test/ui/regions/regions-bounded-by-trait-requiring-static.base.stderr
index 68b90eee72d..9b45dcfd95a 100644
--- a/src/test/ui/regions/regions-bounded-by-trait-requiring-static.stderr
+++ b/src/test/ui/regions/regions-bounded-by-trait-requiring-static.base.stderr
@@ -1,71 +1,71 @@
 error[E0477]: the type `&'a isize` does not fulfill the required lifetime
-  --> $DIR/regions-bounded-by-trait-requiring-static.rs:22:5
+  --> $DIR/regions-bounded-by-trait-requiring-static.rs:26:5
    |
 LL |     assert_send::<&'a isize>();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: type must satisfy the static lifetime as required by this binding
-  --> $DIR/regions-bounded-by-trait-requiring-static.rs:6:18
+  --> $DIR/regions-bounded-by-trait-requiring-static.rs:10:18
    |
 LL | fn assert_send<T:'static>() { }
    |                  ^^^^^^^
 
 error[E0477]: the type `&'a str` does not fulfill the required lifetime
-  --> $DIR/regions-bounded-by-trait-requiring-static.rs:26:5
+  --> $DIR/regions-bounded-by-trait-requiring-static.rs:32:5
    |
 LL |     assert_send::<&'a str>();
    |     ^^^^^^^^^^^^^^^^^^^^^^
    |
 note: type must satisfy the static lifetime as required by this binding
-  --> $DIR/regions-bounded-by-trait-requiring-static.rs:6:18
+  --> $DIR/regions-bounded-by-trait-requiring-static.rs:10:18
    |
 LL | fn assert_send<T:'static>() { }
    |                  ^^^^^^^
 
 error[E0477]: the type `&'a [isize]` does not fulfill the required lifetime
-  --> $DIR/regions-bounded-by-trait-requiring-static.rs:30:5
+  --> $DIR/regions-bounded-by-trait-requiring-static.rs:38:5
    |
 LL |     assert_send::<&'a [isize]>();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: type must satisfy the static lifetime as required by this binding
-  --> $DIR/regions-bounded-by-trait-requiring-static.rs:6:18
+  --> $DIR/regions-bounded-by-trait-requiring-static.rs:10:18
    |
 LL | fn assert_send<T:'static>() { }
    |                  ^^^^^^^
 
 error[E0477]: the type `Box<&'a isize>` does not fulfill the required lifetime
-  --> $DIR/regions-bounded-by-trait-requiring-static.rs:44:5
+  --> $DIR/regions-bounded-by-trait-requiring-static.rs:54:5
    |
 LL |     assert_send::<Box<&'a isize>>();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: type must satisfy the static lifetime as required by this binding
-  --> $DIR/regions-bounded-by-trait-requiring-static.rs:6:18
+  --> $DIR/regions-bounded-by-trait-requiring-static.rs:10:18
    |
 LL | fn assert_send<T:'static>() { }
    |                  ^^^^^^^
 
 error[E0477]: the type `*const &'a isize` does not fulfill the required lifetime
-  --> $DIR/regions-bounded-by-trait-requiring-static.rs:55:5
+  --> $DIR/regions-bounded-by-trait-requiring-static.rs:67:5
    |
 LL |     assert_send::<*const &'a isize>();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: type must satisfy the static lifetime as required by this binding
-  --> $DIR/regions-bounded-by-trait-requiring-static.rs:6:18
+  --> $DIR/regions-bounded-by-trait-requiring-static.rs:10:18
    |
 LL | fn assert_send<T:'static>() { }
    |                  ^^^^^^^
 
 error[E0477]: the type `*mut &'a isize` does not fulfill the required lifetime
-  --> $DIR/regions-bounded-by-trait-requiring-static.rs:59:5
+  --> $DIR/regions-bounded-by-trait-requiring-static.rs:73:5
    |
 LL |     assert_send::<*mut &'a isize>();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: type must satisfy the static lifetime as required by this binding
-  --> $DIR/regions-bounded-by-trait-requiring-static.rs:6:18
+  --> $DIR/regions-bounded-by-trait-requiring-static.rs:10:18
    |
 LL | fn assert_send<T:'static>() { }
    |                  ^^^^^^^
diff --git a/src/test/ui/regions/regions-bounded-by-trait-requiring-static.nll.stderr b/src/test/ui/regions/regions-bounded-by-trait-requiring-static.nll.stderr
index 86bd100538d..558a77516bb 100644
--- a/src/test/ui/regions/regions-bounded-by-trait-requiring-static.nll.stderr
+++ b/src/test/ui/regions/regions-bounded-by-trait-requiring-static.nll.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/regions-bounded-by-trait-requiring-static.rs:22:5
+  --> $DIR/regions-bounded-by-trait-requiring-static.rs:26:5
    |
 LL | fn param_not_ok<'a>(x: &'a isize) {
    |                 -- lifetime `'a` defined here
@@ -7,7 +7,7 @@ LL |     assert_send::<&'a isize>();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
 
 error: lifetime may not live long enough
-  --> $DIR/regions-bounded-by-trait-requiring-static.rs:26:5
+  --> $DIR/regions-bounded-by-trait-requiring-static.rs:32:5
    |
 LL | fn param_not_ok1<'a>(_: &'a isize) {
    |                  -- lifetime `'a` defined here
@@ -15,7 +15,7 @@ LL |     assert_send::<&'a str>();
    |     ^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
 
 error: lifetime may not live long enough
-  --> $DIR/regions-bounded-by-trait-requiring-static.rs:30:5
+  --> $DIR/regions-bounded-by-trait-requiring-static.rs:38:5
    |
 LL | fn param_not_ok2<'a>(_: &'a isize) {
    |                  -- lifetime `'a` defined here
@@ -23,7 +23,7 @@ LL |     assert_send::<&'a [isize]>();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
 
 error: lifetime may not live long enough
-  --> $DIR/regions-bounded-by-trait-requiring-static.rs:44:5
+  --> $DIR/regions-bounded-by-trait-requiring-static.rs:54:5
    |
 LL | fn box_with_region_not_ok<'a>() {
    |                           -- lifetime `'a` defined here
@@ -31,7 +31,7 @@ LL |     assert_send::<Box<&'a isize>>();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
 
 error: lifetime may not live long enough
-  --> $DIR/regions-bounded-by-trait-requiring-static.rs:55:5
+  --> $DIR/regions-bounded-by-trait-requiring-static.rs:67:5
    |
 LL | fn unsafe_ok2<'a>(_: &'a isize) {
    |               -- lifetime `'a` defined here
@@ -39,7 +39,7 @@ LL |     assert_send::<*const &'a isize>();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
 
 error: lifetime may not live long enough
-  --> $DIR/regions-bounded-by-trait-requiring-static.rs:59:5
+  --> $DIR/regions-bounded-by-trait-requiring-static.rs:73:5
    |
 LL | fn unsafe_ok3<'a>(_: &'a isize) {
    |               -- lifetime `'a` defined here
diff --git a/src/test/ui/regions/regions-bounded-by-trait-requiring-static.rs b/src/test/ui/regions/regions-bounded-by-trait-requiring-static.rs
index c583f43638a..37dc1300d39 100644
--- a/src/test/ui/regions/regions-bounded-by-trait-requiring-static.rs
+++ b/src/test/ui/regions/regions-bounded-by-trait-requiring-static.rs
@@ -2,6 +2,10 @@
 // in this file all test region bound and lifetime violations that are
 // detected during type check.
 
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 trait Dummy : 'static { }
 fn assert_send<T:'static>() { }
 
@@ -19,15 +23,21 @@ fn static_lifime_ok<'a,T,U:Send>(_: &'a isize) {
 // otherwise lifetime pointers are not ok
 
 fn param_not_ok<'a>(x: &'a isize) {
-    assert_send::<&'a isize>(); //~ ERROR does not fulfill the required lifetime
+    assert_send::<&'a isize>();
+    //[base]~^ ERROR does not fulfill the required lifetime
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn param_not_ok1<'a>(_: &'a isize) {
-    assert_send::<&'a str>(); //~ ERROR does not fulfill the required lifetime
+    assert_send::<&'a str>();
+    //[base]~^ ERROR does not fulfill the required lifetime
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn param_not_ok2<'a>(_: &'a isize) {
-    assert_send::<&'a [isize]>(); //~ ERROR does not fulfill the required lifetime
+    assert_send::<&'a [isize]>();
+    //[base]~^ ERROR does not fulfill the required lifetime
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 // boxes are ok
@@ -41,7 +51,9 @@ fn box_ok() {
 // but not if they own a bad thing
 
 fn box_with_region_not_ok<'a>() {
-    assert_send::<Box<&'a isize>>(); //~ ERROR does not fulfill the required lifetime
+    assert_send::<Box<&'a isize>>();
+    //[base]~^ ERROR does not fulfill the required lifetime
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 // raw pointers are ok unless they point at unsendable things
@@ -52,11 +64,15 @@ fn unsafe_ok1<'a>(_: &'a isize) {
 }
 
 fn unsafe_ok2<'a>(_: &'a isize) {
-    assert_send::<*const &'a isize>(); //~ ERROR does not fulfill the required lifetime
+    assert_send::<*const &'a isize>();
+    //[base]~^ ERROR does not fulfill the required lifetime
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn unsafe_ok3<'a>(_: &'a isize) {
-    assert_send::<*mut &'a isize>(); //~ ERROR does not fulfill the required lifetime
+    assert_send::<*mut &'a isize>();
+    //[base]~^ ERROR does not fulfill the required lifetime
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn main() {
diff --git a/src/test/ui/regions/regions-bounded-method-type-parameters-cross-crate.stderr b/src/test/ui/regions/regions-bounded-method-type-parameters-cross-crate.base.stderr
index eb205a303db..e031f0db412 100644
--- a/src/test/ui/regions/regions-bounded-method-type-parameters-cross-crate.stderr
+++ b/src/test/ui/regions/regions-bounded-method-type-parameters-cross-crate.base.stderr
@@ -1,5 +1,5 @@
 error[E0623]: lifetime mismatch
-  --> $DIR/regions-bounded-method-type-parameters-cross-crate.rs:20:7
+  --> $DIR/regions-bounded-method-type-parameters-cross-crate.rs:23:7
    |
 LL | fn call_bigger_region<'x, 'y>(a: Inv<'x>, b: Inv<'y>) {
    |                                  -------     ------- these two types are declared with different lifetimes...
diff --git a/src/test/ui/regions/regions-bounded-method-type-parameters-cross-crate.nll.stderr b/src/test/ui/regions/regions-bounded-method-type-parameters-cross-crate.nll.stderr
index 6193bf02f6d..4f5d747be71 100644
--- a/src/test/ui/regions/regions-bounded-method-type-parameters-cross-crate.nll.stderr
+++ b/src/test/ui/regions/regions-bounded-method-type-parameters-cross-crate.nll.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/regions-bounded-method-type-parameters-cross-crate.rs:20:5
+  --> $DIR/regions-bounded-method-type-parameters-cross-crate.rs:23:5
    |
 LL | fn call_bigger_region<'x, 'y>(a: Inv<'x>, b: Inv<'y>) {
    |                       --  -- lifetime `'y` defined here
diff --git a/src/test/ui/regions/regions-bounded-method-type-parameters-cross-crate.rs b/src/test/ui/regions/regions-bounded-method-type-parameters-cross-crate.rs
index a7987d0e1fe..e0965613f1d 100644
--- a/src/test/ui/regions/regions-bounded-method-type-parameters-cross-crate.rs
+++ b/src/test/ui/regions/regions-bounded-method-type-parameters-cross-crate.rs
@@ -1,4 +1,7 @@
 // aux-build:rbmtp_cross_crate_lib.rs
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
 
 // Check explicit region bounds on methods in the cross crate case.
 
@@ -17,7 +20,9 @@ fn call_into_maybe_owned<'x,F:IntoMaybeOwned<'x>>(f: F) {
 
 fn call_bigger_region<'x, 'y>(a: Inv<'x>, b: Inv<'y>) {
     // Here the value provided for 'y is 'y, and hence 'y:'x does not hold.
-    a.bigger_region(b) //~ ERROR lifetime mismatch [E0623]
+    a.bigger_region(b)
+    //[base]~^ ERROR lifetime mismatch [E0623]
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn main() { }
diff --git a/src/test/ui/regions/regions-bounded-method-type-parameters-trait-bound.stderr b/src/test/ui/regions/regions-bounded-method-type-parameters-trait-bound.base.stderr
index de1073cd1d9..0a213e3f779 100644
--- a/src/test/ui/regions/regions-bounded-method-type-parameters-trait-bound.stderr
+++ b/src/test/ui/regions/regions-bounded-method-type-parameters-trait-bound.base.stderr
@@ -1,5 +1,5 @@
 error[E0623]: lifetime mismatch
-  --> $DIR/regions-bounded-method-type-parameters-trait-bound.rs:20:7
+  --> $DIR/regions-bounded-method-type-parameters-trait-bound.rs:24:7
    |
 LL | fn caller2<'a,'b,F:Foo<'a>>(a: Inv<'a>, b: Inv<'b>, f: F) {
    |                                -------     ------- these two types are declared with different lifetimes...
diff --git a/src/test/ui/regions/regions-bounded-method-type-parameters-trait-bound.nll.stderr b/src/test/ui/regions/regions-bounded-method-type-parameters-trait-bound.nll.stderr
index 0e0086be9ea..1c2f46a5fc1 100644
--- a/src/test/ui/regions/regions-bounded-method-type-parameters-trait-bound.nll.stderr
+++ b/src/test/ui/regions/regions-bounded-method-type-parameters-trait-bound.nll.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/regions-bounded-method-type-parameters-trait-bound.rs:20:5
+  --> $DIR/regions-bounded-method-type-parameters-trait-bound.rs:24:5
    |
 LL | fn caller2<'a,'b,F:Foo<'a>>(a: Inv<'a>, b: Inv<'b>, f: F) {
    |            -- -- lifetime `'b` defined here
diff --git a/src/test/ui/regions/regions-bounded-method-type-parameters-trait-bound.rs b/src/test/ui/regions/regions-bounded-method-type-parameters-trait-bound.rs
index 8adf496b230..8a52a1549ab 100644
--- a/src/test/ui/regions/regions-bounded-method-type-parameters-trait-bound.rs
+++ b/src/test/ui/regions/regions-bounded-method-type-parameters-trait-bound.rs
@@ -2,6 +2,10 @@
 // nominal types (but not on other types) and that they are type
 // checked.
 
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 struct Inv<'a> { // invariant w/r/t 'a
     x: &'a mut &'a isize
 }
@@ -17,7 +21,9 @@ fn caller1<'a,'b,F:Foo<'a>>(a: Inv<'a>, b: Inv<'b>, f: F) {
 
 fn caller2<'a,'b,F:Foo<'a>>(a: Inv<'a>, b: Inv<'b>, f: F) {
     // Here the value provided for 'y is 'b, and hence 'b:'a does not hold.
-    f.method(b); //~ ERROR lifetime mismatch [E0623]
+    f.method(b);
+    //[base]~^ ERROR lifetime mismatch [E0623]
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn caller3<'a,'b:'a,F:Foo<'a>>(a: Inv<'a>, b: Inv<'b>, f: F) {
diff --git a/src/test/ui/regions/regions-bounded-method-type-parameters.stderr b/src/test/ui/regions/regions-bounded-method-type-parameters.base.stderr
index 318e9d006a1..3d37a1ba47b 100644
--- a/src/test/ui/regions/regions-bounded-method-type-parameters.stderr
+++ b/src/test/ui/regions/regions-bounded-method-type-parameters.base.stderr
@@ -1,11 +1,11 @@
 error[E0477]: the type `&'a isize` does not fulfill the required lifetime
-  --> $DIR/regions-bounded-method-type-parameters.rs:12:9
+  --> $DIR/regions-bounded-method-type-parameters.rs:16:9
    |
 LL |     Foo.some_method::<&'a isize>();
    |         ^^^^^^^^^^^
    |
 note: type must satisfy the static lifetime as required by this binding
-  --> $DIR/regions-bounded-method-type-parameters.rs:8:22
+  --> $DIR/regions-bounded-method-type-parameters.rs:12:22
    |
 LL |     fn some_method<A:'static>(self) { }
    |                      ^^^^^^^
diff --git a/src/test/ui/regions/regions-bounded-method-type-parameters.nll.stderr b/src/test/ui/regions/regions-bounded-method-type-parameters.nll.stderr
index b6d7b8aac5f..05c3fa58ea3 100644
--- a/src/test/ui/regions/regions-bounded-method-type-parameters.nll.stderr
+++ b/src/test/ui/regions/regions-bounded-method-type-parameters.nll.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/regions-bounded-method-type-parameters.rs:12:9
+  --> $DIR/regions-bounded-method-type-parameters.rs:16:9
    |
 LL | fn caller<'a>(x: &isize) {
    |           -- lifetime `'a` defined here
diff --git a/src/test/ui/regions/regions-bounded-method-type-parameters.rs b/src/test/ui/regions/regions-bounded-method-type-parameters.rs
index 90af120f052..06bc1544a38 100644
--- a/src/test/ui/regions/regions-bounded-method-type-parameters.rs
+++ b/src/test/ui/regions/regions-bounded-method-type-parameters.rs
@@ -2,6 +2,10 @@
 // nominal types (but not on other types) and that they are type
 // checked.
 
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 struct Foo;
 
 impl Foo {
@@ -10,7 +14,8 @@ impl Foo {
 
 fn caller<'a>(x: &isize) {
     Foo.some_method::<&'a isize>();
-    //~^ ERROR does not fulfill the required lifetime
+    //[base]~^ ERROR does not fulfill the required lifetime
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn main() { }
diff --git a/src/test/ui/regions/regions-bounds.stderr b/src/test/ui/regions/regions-bounds.base.stderr
index 90227e574ad..d853cdde336 100644
--- a/src/test/ui/regions/regions-bounds.stderr
+++ b/src/test/ui/regions/regions-bounds.base.stderr
@@ -1,5 +1,5 @@
 error[E0308]: mismatched types
-  --> $DIR/regions-bounds.rs:9:12
+  --> $DIR/regions-bounds.rs:13:12
    |
 LL |     return e;
    |            ^ lifetime mismatch
@@ -7,18 +7,18 @@ LL |     return e;
    = note: expected struct `TupleStruct<'b>`
               found struct `TupleStruct<'a>`
 note: the lifetime `'a` as defined here...
-  --> $DIR/regions-bounds.rs:8:10
+  --> $DIR/regions-bounds.rs:12:10
    |
 LL | fn a_fn1<'a,'b>(e: TupleStruct<'a>) -> TupleStruct<'b> {
    |          ^^
 note: ...does not necessarily outlive the lifetime `'b` as defined here
-  --> $DIR/regions-bounds.rs:8:13
+  --> $DIR/regions-bounds.rs:12:13
    |
 LL | fn a_fn1<'a,'b>(e: TupleStruct<'a>) -> TupleStruct<'b> {
    |             ^^
 
 error[E0308]: mismatched types
-  --> $DIR/regions-bounds.rs:13:12
+  --> $DIR/regions-bounds.rs:19:12
    |
 LL |     return e;
    |            ^ lifetime mismatch
@@ -26,12 +26,12 @@ LL |     return e;
    = note: expected struct `Struct<'b>`
               found struct `Struct<'a>`
 note: the lifetime `'a` as defined here...
-  --> $DIR/regions-bounds.rs:12:10
+  --> $DIR/regions-bounds.rs:18:10
    |
 LL | fn a_fn3<'a,'b>(e: Struct<'a>) -> Struct<'b> {
    |          ^^
 note: ...does not necessarily outlive the lifetime `'b` as defined here
-  --> $DIR/regions-bounds.rs:12:13
+  --> $DIR/regions-bounds.rs:18:13
    |
 LL | fn a_fn3<'a,'b>(e: Struct<'a>) -> Struct<'b> {
    |             ^^
diff --git a/src/test/ui/regions/regions-bounds.nll.stderr b/src/test/ui/regions/regions-bounds.nll.stderr
index 84226a57553..7109220165f 100644
--- a/src/test/ui/regions/regions-bounds.nll.stderr
+++ b/src/test/ui/regions/regions-bounds.nll.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/regions-bounds.rs:9:12
+  --> $DIR/regions-bounds.rs:13:12
    |
 LL | fn a_fn1<'a,'b>(e: TupleStruct<'a>) -> TupleStruct<'b> {
    |          -- -- lifetime `'b` defined here
@@ -11,7 +11,7 @@ LL |     return e;
    = help: consider adding the following bound: `'a: 'b`
 
 error: lifetime may not live long enough
-  --> $DIR/regions-bounds.rs:13:12
+  --> $DIR/regions-bounds.rs:19:12
    |
 LL | fn a_fn3<'a,'b>(e: Struct<'a>) -> Struct<'b> {
    |          -- -- lifetime `'b` defined here
diff --git a/src/test/ui/regions/regions-bounds.rs b/src/test/ui/regions/regions-bounds.rs
index d3e4e6e8712..b13dac49f8c 100644
--- a/src/test/ui/regions/regions-bounds.rs
+++ b/src/test/ui/regions/regions-bounds.rs
@@ -2,15 +2,23 @@
 // nominal types (but not on other types) and that they are type
 // checked.
 
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 struct TupleStruct<'a>(&'a isize);
 struct Struct<'a> { x:&'a isize }
 
 fn a_fn1<'a,'b>(e: TupleStruct<'a>) -> TupleStruct<'b> {
-    return e; //~ ERROR mismatched types
+    return e;
+    //[base]~^ ERROR mismatched types
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn a_fn3<'a,'b>(e: Struct<'a>) -> Struct<'b> {
-    return e; //~ ERROR mismatched types
+    return e;
+    //[base]~^ ERROR mismatched types
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn main() { }
diff --git a/src/test/ui/regions/regions-close-associated-type-into-object.stderr b/src/test/ui/regions/regions-close-associated-type-into-object.base.stderr
index 536a1b5e359..fbbb5980401 100644
--- a/src/test/ui/regions/regions-close-associated-type-into-object.stderr
+++ b/src/test/ui/regions/regions-close-associated-type-into-object.base.stderr
@@ -1,5 +1,5 @@
 error[E0310]: the associated type `<T as Iter>::Item` may not live long enough
-  --> $DIR/regions-close-associated-type-into-object.rs:15:5
+  --> $DIR/regions-close-associated-type-into-object.rs:19:5
    |
 LL |     Box::new(item)
    |     ^^^^^^^^^^^^^^
@@ -8,7 +8,7 @@ LL |     Box::new(item)
    = note: ...so that the type `<T as Iter>::Item` will meet its required lifetime bounds
 
 error[E0310]: the associated type `<T as Iter>::Item` may not live long enough
-  --> $DIR/regions-close-associated-type-into-object.rs:22:5
+  --> $DIR/regions-close-associated-type-into-object.rs:26:5
    |
 LL |     Box::new(item)
    |     ^^^^^^^^^^^^^^
@@ -17,7 +17,7 @@ LL |     Box::new(item)
    = note: ...so that the type `Box<<T as Iter>::Item>` will meet its required lifetime bounds
 
 error[E0309]: the associated type `<T as Iter>::Item` may not live long enough
-  --> $DIR/regions-close-associated-type-into-object.rs:28:5
+  --> $DIR/regions-close-associated-type-into-object.rs:32:5
    |
 LL |     Box::new(item)
    |     ^^^^^^^^^^^^^^
@@ -26,7 +26,7 @@ LL |     Box::new(item)
    = note: ...so that the type `<T as Iter>::Item` will meet its required lifetime bounds
 
 error[E0309]: the associated type `<T as Iter>::Item` may not live long enough
-  --> $DIR/regions-close-associated-type-into-object.rs:35:5
+  --> $DIR/regions-close-associated-type-into-object.rs:39:5
    |
 LL |     Box::new(item)
    |     ^^^^^^^^^^^^^^
diff --git a/src/test/ui/regions/regions-close-associated-type-into-object.nll.stderr b/src/test/ui/regions/regions-close-associated-type-into-object.nll.stderr
index 92c4956da02..dd4b97aa562 100644
--- a/src/test/ui/regions/regions-close-associated-type-into-object.nll.stderr
+++ b/src/test/ui/regions/regions-close-associated-type-into-object.nll.stderr
@@ -1,34 +1,38 @@
 error[E0310]: the associated type `<T as Iter>::Item` may not live long enough
-  --> $DIR/regions-close-associated-type-into-object.rs:15:5
+  --> $DIR/regions-close-associated-type-into-object.rs:19:5
    |
 LL |     Box::new(item)
    |     ^^^^^^^^^^^^^^
    |
    = help: consider adding an explicit lifetime bound `<T as Iter>::Item: 'static`...
+   = note: ...so that the type `<T as Iter>::Item` will meet its required lifetime bounds
 
 error[E0310]: the associated type `<T as Iter>::Item` may not live long enough
-  --> $DIR/regions-close-associated-type-into-object.rs:22:5
+  --> $DIR/regions-close-associated-type-into-object.rs:26:5
    |
 LL |     Box::new(item)
    |     ^^^^^^^^^^^^^^
    |
    = help: consider adding an explicit lifetime bound `<T as Iter>::Item: 'static`...
+   = note: ...so that the type `<T as Iter>::Item` will meet its required lifetime bounds
 
 error[E0309]: the associated type `<T as Iter>::Item` may not live long enough
-  --> $DIR/regions-close-associated-type-into-object.rs:28:5
+  --> $DIR/regions-close-associated-type-into-object.rs:32:5
    |
 LL |     Box::new(item)
    |     ^^^^^^^^^^^^^^
    |
    = help: consider adding an explicit lifetime bound `<T as Iter>::Item: 'a`...
+   = note: ...so that the type `<T as Iter>::Item` will meet its required lifetime bounds
 
 error[E0309]: the associated type `<T as Iter>::Item` may not live long enough
-  --> $DIR/regions-close-associated-type-into-object.rs:35:5
+  --> $DIR/regions-close-associated-type-into-object.rs:39:5
    |
 LL |     Box::new(item)
    |     ^^^^^^^^^^^^^^
    |
    = help: consider adding an explicit lifetime bound `<T as Iter>::Item: 'a`...
+   = note: ...so that the type `<T as Iter>::Item` will meet its required lifetime bounds
 
 error: aborting due to 4 previous errors
 
diff --git a/src/test/ui/regions/regions-close-associated-type-into-object.rs b/src/test/ui/regions/regions-close-associated-type-into-object.rs
index 428477e2489..94199f56212 100644
--- a/src/test/ui/regions/regions-close-associated-type-into-object.rs
+++ b/src/test/ui/regions/regions-close-associated-type-into-object.rs
@@ -1,3 +1,7 @@
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 trait X {}
 
 
diff --git a/src/test/ui/regions/regions-close-object-into-object-2.stderr b/src/test/ui/regions/regions-close-object-into-object-2.base.stderr
index 4153f4f29bc..ddf168ffd10 100644
--- a/src/test/ui/regions/regions-close-object-into-object-2.stderr
+++ b/src/test/ui/regions/regions-close-object-into-object-2.base.stderr
@@ -1,5 +1,5 @@
 error[E0759]: `v` has lifetime `'a` but it needs to satisfy a `'static` lifetime requirement
-  --> $DIR/regions-close-object-into-object-2.rs:9:16
+  --> $DIR/regions-close-object-into-object-2.rs:13:16
    |
 LL | fn g<'a, T: 'static>(v: Box<dyn A<T> + 'a>) -> Box<dyn X + 'static> {
    |                         ------------------ this data with lifetime `'a`...
@@ -7,7 +7,7 @@ LL |     Box::new(B(&*v)) as Box<dyn X>
    |                ^^^ ...is used and required to live as long as `'static` here
    |
 note: `'static` lifetime requirement introduced by the return type
-  --> $DIR/regions-close-object-into-object-2.rs:8:60
+  --> $DIR/regions-close-object-into-object-2.rs:12:60
    |
 LL | fn g<'a, T: 'static>(v: Box<dyn A<T> + 'a>) -> Box<dyn X + 'static> {
    |                                                            ^^^^^^^ `'static` requirement introduced here
diff --git a/src/test/ui/regions/regions-close-object-into-object-2.nll.stderr b/src/test/ui/regions/regions-close-object-into-object-2.nll.stderr
index 6a0e9586161..473c99b672f 100644
--- a/src/test/ui/regions/regions-close-object-into-object-2.nll.stderr
+++ b/src/test/ui/regions/regions-close-object-into-object-2.nll.stderr
@@ -1,13 +1,22 @@
 error: lifetime may not live long enough
-  --> $DIR/regions-close-object-into-object-2.rs:9:5
+  --> $DIR/regions-close-object-into-object-2.rs:13:5
    |
 LL | fn g<'a, T: 'static>(v: Box<dyn A<T> + 'a>) -> Box<dyn X + 'static> {
    |      -- lifetime `'a` defined here
 LL |     Box::new(B(&*v)) as Box<dyn X>
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static`
+   |
+help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v`
+   |
+LL | fn g<'a, T: 'static>(v: Box<dyn A<T> + 'a>) -> Box<dyn X + 'a> {
+   |                                                            ~~
+help: alternatively, add an explicit `'static` bound to this reference
+   |
+LL | fn g<'a, T: 'static>(v: Box<(dyn A<T> + 'static)>) -> Box<dyn X + 'static> {
+   |                         ~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error[E0515]: cannot return value referencing local data `*v`
-  --> $DIR/regions-close-object-into-object-2.rs:9:5
+  --> $DIR/regions-close-object-into-object-2.rs:13:5
    |
 LL |     Box::new(B(&*v)) as Box<dyn X>
    |     ^^^^^^^^^^^---^^^^^^^^^^^^^^^^
diff --git a/src/test/ui/regions/regions-close-object-into-object-2.rs b/src/test/ui/regions/regions-close-object-into-object-2.rs
index 9c41174e24d..33ea03f061e 100644
--- a/src/test/ui/regions/regions-close-object-into-object-2.rs
+++ b/src/test/ui/regions/regions-close-object-into-object-2.rs
@@ -1,3 +1,7 @@
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 trait A<T> { }
 
 struct B<'a, T:'a>(&'a (dyn A<T> + 'a));
@@ -6,7 +10,10 @@ trait X { }
 impl<'a, T> X for B<'a, T> {}
 
 fn g<'a, T: 'static>(v: Box<dyn A<T> + 'a>) -> Box<dyn X + 'static> {
-    Box::new(B(&*v)) as Box<dyn X> //~ ERROR E0759
+    Box::new(B(&*v)) as Box<dyn X>
+    //[base]~^ ERROR E0759
+    //[nll]~^^ ERROR lifetime may not live long enough
+    //[nll]~| ERROR cannot return value referencing local data `*v` [E0515]
 }
 
 fn main() { }
diff --git a/src/test/ui/regions/regions-close-object-into-object-4.stderr b/src/test/ui/regions/regions-close-object-into-object-4.base.stderr
index 2ea4b431b38..33d4df3d194 100644
--- a/src/test/ui/regions/regions-close-object-into-object-4.stderr
+++ b/src/test/ui/regions/regions-close-object-into-object-4.base.stderr
@@ -1,5 +1,5 @@
 error[E0759]: `v` has lifetime `'a` but it needs to satisfy a `'static` lifetime requirement
-  --> $DIR/regions-close-object-into-object-4.rs:9:16
+  --> $DIR/regions-close-object-into-object-4.rs:13:16
    |
 LL | fn i<'a, T, U>(v: Box<dyn A<U>+'a>) -> Box<dyn X + 'static> {
    |                   ---------------- this data with lifetime `'a`...
@@ -7,7 +7,7 @@ LL |     Box::new(B(&*v)) as Box<dyn X>
    |                ^^^ ...is used and required to live as long as `'static` here
    |
 note: `'static` lifetime requirement introduced by the return type
-  --> $DIR/regions-close-object-into-object-4.rs:8:52
+  --> $DIR/regions-close-object-into-object-4.rs:12:52
    |
 LL | fn i<'a, T, U>(v: Box<dyn A<U>+'a>) -> Box<dyn X + 'static> {
    |                                                    ^^^^^^^ `'static` requirement introduced here
diff --git a/src/test/ui/regions/regions-close-object-into-object-4.nll.stderr b/src/test/ui/regions/regions-close-object-into-object-4.nll.stderr
index b30626830ad..66d3102225e 100644
--- a/src/test/ui/regions/regions-close-object-into-object-4.nll.stderr
+++ b/src/test/ui/regions/regions-close-object-into-object-4.nll.stderr
@@ -1,37 +1,55 @@
 error[E0310]: the parameter type `U` may not live long enough
-  --> $DIR/regions-close-object-into-object-4.rs:9:5
+  --> $DIR/regions-close-object-into-object-4.rs:13:5
    |
 LL |     Box::new(B(&*v)) as Box<dyn X>
-   |     ^^^^^^^^
+   |     ^^^^^^^^ ...so that the type `U` will meet its required lifetime bounds
    |
-   = help: consider adding an explicit lifetime bound `U: 'static`...
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn i<'a, T, U: 'static>(v: Box<dyn A<U>+'a>) -> Box<dyn X + 'static> {
+   |              +++++++++
 
 error[E0310]: the parameter type `U` may not live long enough
-  --> $DIR/regions-close-object-into-object-4.rs:9:5
+  --> $DIR/regions-close-object-into-object-4.rs:13:5
    |
 LL |     Box::new(B(&*v)) as Box<dyn X>
-   |     ^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^ ...so that the type `U` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
    |
-   = help: consider adding an explicit lifetime bound `U: 'static`...
+LL | fn i<'a, T, U: 'static>(v: Box<dyn A<U>+'a>) -> Box<dyn X + 'static> {
+   |              +++++++++
 
 error[E0310]: the parameter type `U` may not live long enough
-  --> $DIR/regions-close-object-into-object-4.rs:9:5
+  --> $DIR/regions-close-object-into-object-4.rs:13:5
    |
 LL |     Box::new(B(&*v)) as Box<dyn X>
-   |     ^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^ ...so that the type `U` will meet its required lifetime bounds
    |
-   = help: consider adding an explicit lifetime bound `U: 'static`...
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn i<'a, T, U: 'static>(v: Box<dyn A<U>+'a>) -> Box<dyn X + 'static> {
+   |              +++++++++
 
 error: lifetime may not live long enough
-  --> $DIR/regions-close-object-into-object-4.rs:9:5
+  --> $DIR/regions-close-object-into-object-4.rs:13:5
    |
 LL | fn i<'a, T, U>(v: Box<dyn A<U>+'a>) -> Box<dyn X + 'static> {
    |      -- lifetime `'a` defined here
 LL |     Box::new(B(&*v)) as Box<dyn X>
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static`
+   |
+help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v`
+   |
+LL | fn i<'a, T, U>(v: Box<dyn A<U>+'a>) -> Box<dyn X + 'a> {
+   |                                                    ~~
+help: alternatively, add an explicit `'static` bound to this reference
+   |
+LL | fn i<'a, T, U>(v: Box<(dyn A<U> + 'static)>) -> Box<dyn X + 'static> {
+   |                   ~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error[E0515]: cannot return value referencing local data `*v`
-  --> $DIR/regions-close-object-into-object-4.rs:9:5
+  --> $DIR/regions-close-object-into-object-4.rs:13:5
    |
 LL |     Box::new(B(&*v)) as Box<dyn X>
    |     ^^^^^^^^^^^---^^^^^^^^^^^^^^^^
@@ -40,12 +58,15 @@ LL |     Box::new(B(&*v)) as Box<dyn X>
    |     returns a value referencing data owned by the current function
 
 error[E0310]: the parameter type `U` may not live long enough
-  --> $DIR/regions-close-object-into-object-4.rs:9:14
+  --> $DIR/regions-close-object-into-object-4.rs:13:14
    |
 LL |     Box::new(B(&*v)) as Box<dyn X>
-   |              ^^^^^^
+   |              ^^^^^^ ...so that the type `U` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
    |
-   = help: consider adding an explicit lifetime bound `U: 'static`...
+LL | fn i<'a, T, U: 'static>(v: Box<dyn A<U>+'a>) -> Box<dyn X + 'static> {
+   |              +++++++++
 
 error: aborting due to 6 previous errors
 
diff --git a/src/test/ui/regions/regions-close-object-into-object-4.rs b/src/test/ui/regions/regions-close-object-into-object-4.rs
index 2a06a2b7c05..5a852b7329a 100644
--- a/src/test/ui/regions/regions-close-object-into-object-4.rs
+++ b/src/test/ui/regions/regions-close-object-into-object-4.rs
@@ -1,3 +1,7 @@
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 trait A<T> { }
 
 struct B<'a, T:'a>(&'a (dyn A<T> + 'a));
@@ -6,7 +10,15 @@ trait X { }
 impl<'a, T> X for B<'a, T> {}
 
 fn i<'a, T, U>(v: Box<dyn A<U>+'a>) -> Box<dyn X + 'static> {
-    Box::new(B(&*v)) as Box<dyn X> //~ ERROR E0759
+    Box::new(B(&*v)) as Box<dyn X>
+    //[base]~^ ERROR E0759
+    //[nll]~^^ ERROR the parameter type `U` may not live long enough [E0310]
+    //[nll]~| ERROR the parameter type `U` may not live long enough [E0310]
+    //[nll]~| ERROR the parameter type `U` may not live long enough [E0310]
+    //[nll]~| ERROR lifetime may not live long enough
+    //[nll]~| ERROR cannot return value referencing local data `*v` [E0515]
+    //[nll]~| ERROR the parameter type `U` may not live long enough [E0310]
+
 }
 
 fn main() {}
diff --git a/src/test/ui/regions/regions-close-object-into-object-5.stderr b/src/test/ui/regions/regions-close-object-into-object-5.base.stderr
index 512a7ab35fb..1a78079b638 100644
--- a/src/test/ui/regions/regions-close-object-into-object-5.stderr
+++ b/src/test/ui/regions/regions-close-object-into-object-5.base.stderr
@@ -1,83 +1,94 @@
 error[E0310]: the parameter type `T` may not live long enough
-  --> $DIR/regions-close-object-into-object-5.rs:17:5
+  --> $DIR/regions-close-object-into-object-5.rs:21:5
    |
-LL | fn f<'a, T, U>(v: Box<A<T> + 'static>) -> Box<X + 'static> {
-   |          - help: consider adding an explicit lifetime bound...: `T: 'static`
-LL |     // oh dear!
 LL |     Box::new(B(&*v)) as Box<dyn X>
    |     ^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds...
    |
 note: ...that is required by this bound
-  --> $DIR/regions-close-object-into-object-5.rs:9:17
+  --> $DIR/regions-close-object-into-object-5.rs:13:17
    |
 LL | struct B<'a, T: 'a>(&'a (A<T> + 'a));
    |                 ^^
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn f<'a, T: 'static, U>(v: Box<A<T> + 'static>) -> Box<X + 'static> {
+   |           +++++++++
 
 error[E0310]: the parameter type `T` may not live long enough
-  --> $DIR/regions-close-object-into-object-5.rs:17:5
+  --> $DIR/regions-close-object-into-object-5.rs:21:5
    |
-LL | fn f<'a, T, U>(v: Box<A<T> + 'static>) -> Box<X + 'static> {
-   |          - help: consider adding an explicit lifetime bound...: `T: 'static`
-LL |     // oh dear!
 LL |     Box::new(B(&*v)) as Box<dyn X>
    |     ^^^^^^^^^^^^^^^^ ...so that the type `B<'_, T>` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn f<'a, T: 'static, U>(v: Box<A<T> + 'static>) -> Box<X + 'static> {
+   |           +++++++++
 
 error[E0310]: the parameter type `T` may not live long enough
-  --> $DIR/regions-close-object-into-object-5.rs:17:14
+  --> $DIR/regions-close-object-into-object-5.rs:21:14
    |
-LL | fn f<'a, T, U>(v: Box<A<T> + 'static>) -> Box<X + 'static> {
-   |          - help: consider adding an explicit lifetime bound...: `T: 'static`
-LL |     // oh dear!
 LL |     Box::new(B(&*v)) as Box<dyn X>
    |              ^ ...so that the type `T` will meet its required lifetime bounds...
    |
 note: ...that is required by this bound
-  --> $DIR/regions-close-object-into-object-5.rs:9:17
+  --> $DIR/regions-close-object-into-object-5.rs:13:17
    |
 LL | struct B<'a, T: 'a>(&'a (A<T> + 'a));
    |                 ^^
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn f<'a, T: 'static, U>(v: Box<A<T> + 'static>) -> Box<X + 'static> {
+   |           +++++++++
 
 error[E0310]: the parameter type `T` may not live long enough
-  --> $DIR/regions-close-object-into-object-5.rs:17:14
+  --> $DIR/regions-close-object-into-object-5.rs:21:14
    |
-LL | fn f<'a, T, U>(v: Box<A<T> + 'static>) -> Box<X + 'static> {
-   |          - help: consider adding an explicit lifetime bound...: `T: 'static`
-LL |     // oh dear!
 LL |     Box::new(B(&*v)) as Box<dyn X>
    |              ^^^^^^ ...so that the type `T` will meet its required lifetime bounds...
    |
 note: ...that is required by this bound
-  --> $DIR/regions-close-object-into-object-5.rs:9:17
+  --> $DIR/regions-close-object-into-object-5.rs:13:17
    |
 LL | struct B<'a, T: 'a>(&'a (A<T> + 'a));
    |                 ^^
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn f<'a, T: 'static, U>(v: Box<A<T> + 'static>) -> Box<X + 'static> {
+   |           +++++++++
 
 error[E0310]: the parameter type `T` may not live long enough
-  --> $DIR/regions-close-object-into-object-5.rs:17:16
+  --> $DIR/regions-close-object-into-object-5.rs:21:16
    |
-LL | fn f<'a, T, U>(v: Box<A<T> + 'static>) -> Box<X + 'static> {
-   |          - help: consider adding an explicit lifetime bound...: `T: 'static`
-LL |     // oh dear!
 LL |     Box::new(B(&*v)) as Box<dyn X>
    |                ^^^ ...so that the reference type `&dyn A<T>` does not outlive the data it points at
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn f<'a, T: 'static, U>(v: Box<A<T> + 'static>) -> Box<X + 'static> {
+   |           +++++++++
 
 error[E0310]: the parameter type `T` may not live long enough
-  --> $DIR/regions-close-object-into-object-5.rs:17:16
+  --> $DIR/regions-close-object-into-object-5.rs:21:16
    |
-LL | fn f<'a, T, U>(v: Box<A<T> + 'static>) -> Box<X + 'static> {
-   |          - help: consider adding an explicit lifetime bound...: `T: 'static`
-LL |     // oh dear!
 LL |     Box::new(B(&*v)) as Box<dyn X>
    |                ^^^ ...so that the type `(dyn A<T> + 'static)` is not borrowed for too long
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn f<'a, T: 'static, U>(v: Box<A<T> + 'static>) -> Box<X + 'static> {
+   |           +++++++++
 
 error[E0310]: the parameter type `T` may not live long enough
-  --> $DIR/regions-close-object-into-object-5.rs:17:16
+  --> $DIR/regions-close-object-into-object-5.rs:21:16
    |
-LL | fn f<'a, T, U>(v: Box<A<T> + 'static>) -> Box<X + 'static> {
-   |          - help: consider adding an explicit lifetime bound...: `T: 'static`
-LL |     // oh dear!
 LL |     Box::new(B(&*v)) as Box<dyn X>
    |                ^^^ ...so that the type `(dyn A<T> + 'static)` is not borrowed for too long
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn f<'a, T: 'static, U>(v: Box<A<T> + 'static>) -> Box<X + 'static> {
+   |           +++++++++
 
 error: aborting due to 7 previous errors
 
diff --git a/src/test/ui/regions/regions-close-object-into-object-5.nll.stderr b/src/test/ui/regions/regions-close-object-into-object-5.nll.stderr
index 7486e73e66a..cb06326130e 100644
--- a/src/test/ui/regions/regions-close-object-into-object-5.nll.stderr
+++ b/src/test/ui/regions/regions-close-object-into-object-5.nll.stderr
@@ -1,29 +1,38 @@
 error[E0310]: the parameter type `T` may not live long enough
-  --> $DIR/regions-close-object-into-object-5.rs:17:5
+  --> $DIR/regions-close-object-into-object-5.rs:21:5
    |
 LL |     Box::new(B(&*v)) as Box<dyn X>
-   |     ^^^^^^^^
+   |     ^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
    |
-   = help: consider adding an explicit lifetime bound `T: 'static`...
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn f<'a, T: 'static, U>(v: Box<A<T> + 'static>) -> Box<X + 'static> {
+   |           +++++++++
 
 error[E0310]: the parameter type `T` may not live long enough
-  --> $DIR/regions-close-object-into-object-5.rs:17:5
+  --> $DIR/regions-close-object-into-object-5.rs:21:5
    |
 LL |     Box::new(B(&*v)) as Box<dyn X>
-   |     ^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
    |
-   = help: consider adding an explicit lifetime bound `T: 'static`...
+LL | fn f<'a, T: 'static, U>(v: Box<A<T> + 'static>) -> Box<X + 'static> {
+   |           +++++++++
 
 error[E0310]: the parameter type `T` may not live long enough
-  --> $DIR/regions-close-object-into-object-5.rs:17:5
+  --> $DIR/regions-close-object-into-object-5.rs:21:5
    |
 LL |     Box::new(B(&*v)) as Box<dyn X>
-   |     ^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
    |
-   = help: consider adding an explicit lifetime bound `T: 'static`...
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn f<'a, T: 'static, U>(v: Box<A<T> + 'static>) -> Box<X + 'static> {
+   |           +++++++++
 
 error[E0515]: cannot return value referencing local data `*v`
-  --> $DIR/regions-close-object-into-object-5.rs:17:5
+  --> $DIR/regions-close-object-into-object-5.rs:21:5
    |
 LL |     Box::new(B(&*v)) as Box<dyn X>
    |     ^^^^^^^^^^^---^^^^^^^^^^^^^^^^
@@ -32,12 +41,15 @@ LL |     Box::new(B(&*v)) as Box<dyn X>
    |     returns a value referencing data owned by the current function
 
 error[E0310]: the parameter type `T` may not live long enough
-  --> $DIR/regions-close-object-into-object-5.rs:17:14
+  --> $DIR/regions-close-object-into-object-5.rs:21:14
    |
 LL |     Box::new(B(&*v)) as Box<dyn X>
-   |              ^^^^^^
+   |              ^^^^^^ ...so that the type `T` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
    |
-   = help: consider adding an explicit lifetime bound `T: 'static`...
+LL | fn f<'a, T: 'static, U>(v: Box<A<T> + 'static>) -> Box<X + 'static> {
+   |           +++++++++
 
 error: aborting due to 5 previous errors
 
diff --git a/src/test/ui/regions/regions-close-object-into-object-5.rs b/src/test/ui/regions/regions-close-object-into-object-5.rs
index 5471c375b49..0e5fec28d19 100644
--- a/src/test/ui/regions/regions-close-object-into-object-5.rs
+++ b/src/test/ui/regions/regions-close-object-into-object-5.rs
@@ -1,3 +1,7 @@
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 #![allow(warnings)]
 
 
@@ -19,9 +23,10 @@ fn f<'a, T, U>(v: Box<A<T> + 'static>) -> Box<X + 'static> {
     //~| ERROR the parameter type `T` may not live long enough
     //~| ERROR the parameter type `T` may not live long enough
     //~| ERROR the parameter type `T` may not live long enough
-    //~| ERROR the parameter type `T` may not live long enough
-    //~| ERROR the parameter type `T` may not live long enough
-    //~| ERROR the parameter type `T` may not live long enough
+    //[base]~| ERROR the parameter type `T` may not live long enough
+    //[base]~| ERROR the parameter type `T` may not live long enough
+    //[base]~| ERROR the parameter type `T` may not live long enough
+    //[nll]~| ERROR cannot return value referencing local data `*v` [E0515]
 }
 
 fn main() {}
diff --git a/src/test/ui/regions/regions-close-over-type-parameter-1.stderr b/src/test/ui/regions/regions-close-over-type-parameter-1.base.stderr
index 063c3b19a19..d8f77ad85c9 100644
--- a/src/test/ui/regions/regions-close-over-type-parameter-1.stderr
+++ b/src/test/ui/regions/regions-close-over-type-parameter-1.base.stderr
@@ -1,18 +1,24 @@
 error[E0310]: the parameter type `A` may not live long enough
-  --> $DIR/regions-close-over-type-parameter-1.rs:12:5
+  --> $DIR/regions-close-over-type-parameter-1.rs:15:5
    |
-LL | fn make_object1<A: SomeTrait>(v: A) -> Box<dyn SomeTrait + 'static> {
-   |                 -- help: consider adding an explicit lifetime bound...: `A: 'static +`
 LL |     Box::new(v) as Box<dyn SomeTrait + 'static>
    |     ^^^^^^^^^^^ ...so that the type `A` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn make_object1<A: SomeTrait + 'static>(v: A) -> Box<dyn SomeTrait + 'static> {
+   |                              +++++++++
 
 error[E0309]: the parameter type `A` may not live long enough
-  --> $DIR/regions-close-over-type-parameter-1.rs:21:5
+  --> $DIR/regions-close-over-type-parameter-1.rs:24:5
    |
-LL | fn make_object3<'a, 'b, A: SomeTrait + 'a>(v: A) -> Box<dyn SomeTrait + 'b> {
-   |                         -- help: consider adding an explicit lifetime bound...: `A: 'b +`
 LL |     Box::new(v) as Box<dyn SomeTrait + 'b>
    |     ^^^^^^^^^^^ ...so that the type `A` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn make_object3<'a, 'b, A: SomeTrait + 'a + 'b>(v: A) -> Box<dyn SomeTrait + 'b> {
+   |                                           ++++
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/regions/regions-close-over-type-parameter-1.nll.stderr b/src/test/ui/regions/regions-close-over-type-parameter-1.nll.stderr
index b576ae87011..d8f77ad85c9 100644
--- a/src/test/ui/regions/regions-close-over-type-parameter-1.nll.stderr
+++ b/src/test/ui/regions/regions-close-over-type-parameter-1.nll.stderr
@@ -1,18 +1,24 @@
 error[E0310]: the parameter type `A` may not live long enough
-  --> $DIR/regions-close-over-type-parameter-1.rs:12:5
+  --> $DIR/regions-close-over-type-parameter-1.rs:15:5
    |
 LL |     Box::new(v) as Box<dyn SomeTrait + 'static>
-   |     ^^^^^^^^^^^
+   |     ^^^^^^^^^^^ ...so that the type `A` will meet its required lifetime bounds
    |
-   = help: consider adding an explicit lifetime bound `A: 'static`...
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn make_object1<A: SomeTrait + 'static>(v: A) -> Box<dyn SomeTrait + 'static> {
+   |                              +++++++++
 
 error[E0309]: the parameter type `A` may not live long enough
-  --> $DIR/regions-close-over-type-parameter-1.rs:21:5
+  --> $DIR/regions-close-over-type-parameter-1.rs:24:5
    |
 LL |     Box::new(v) as Box<dyn SomeTrait + 'b>
-   |     ^^^^^^^^^^^
+   |     ^^^^^^^^^^^ ...so that the type `A` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
    |
-   = help: consider adding an explicit lifetime bound `A: 'b`...
+LL | fn make_object3<'a, 'b, A: SomeTrait + 'a + 'b>(v: A) -> Box<dyn SomeTrait + 'b> {
+   |                                           ++++
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/regions/regions-close-over-type-parameter-1.rs b/src/test/ui/regions/regions-close-over-type-parameter-1.rs
index 52d18c5d7a6..cf425bcd4f3 100644
--- a/src/test/ui/regions/regions-close-over-type-parameter-1.rs
+++ b/src/test/ui/regions/regions-close-over-type-parameter-1.rs
@@ -2,6 +2,9 @@
 // an object. This should yield errors unless `A` (and the object)
 // both have suitable bounds.
 
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
 
 trait SomeTrait {
     fn get(&self) -> isize;
diff --git a/src/test/ui/regions/regions-close-over-type-parameter-multiple.stderr b/src/test/ui/regions/regions-close-over-type-parameter-multiple.base.stderr
index aa22fd96deb..171203897d7 100644
--- a/src/test/ui/regions/regions-close-over-type-parameter-multiple.stderr
+++ b/src/test/ui/regions/regions-close-over-type-parameter-multiple.base.stderr
@@ -1,26 +1,26 @@
 error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
-  --> $DIR/regions-close-over-type-parameter-multiple.rs:20:5
+  --> $DIR/regions-close-over-type-parameter-multiple.rs:23:5
    |
 LL |     Box::new(v) as Box<dyn SomeTrait + 'a>
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: first, the lifetime cannot outlive the lifetime `'a` as defined here...
-  --> $DIR/regions-close-over-type-parameter-multiple.rs:18:20
+  --> $DIR/regions-close-over-type-parameter-multiple.rs:21:20
    |
 LL | fn make_object_bad<'a,'b,'c,A:SomeTrait+'a+'b>(v: A) -> Box<dyn SomeTrait + 'c> {
    |                    ^^
 note: ...so that the declared lifetime parameter bounds are satisfied
-  --> $DIR/regions-close-over-type-parameter-multiple.rs:20:5
+  --> $DIR/regions-close-over-type-parameter-multiple.rs:23:5
    |
 LL |     Box::new(v) as Box<dyn SomeTrait + 'a>
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 note: but, the lifetime must be valid for the lifetime `'c` as defined here...
-  --> $DIR/regions-close-over-type-parameter-multiple.rs:18:26
+  --> $DIR/regions-close-over-type-parameter-multiple.rs:21:26
    |
 LL | fn make_object_bad<'a,'b,'c,A:SomeTrait+'a+'b>(v: A) -> Box<dyn SomeTrait + 'c> {
    |                          ^^
 note: ...so that the types are compatible
-  --> $DIR/regions-close-over-type-parameter-multiple.rs:20:5
+  --> $DIR/regions-close-over-type-parameter-multiple.rs:23:5
    |
 LL |     Box::new(v) as Box<dyn SomeTrait + 'a>
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/test/ui/regions/regions-close-over-type-parameter-multiple.nll.stderr b/src/test/ui/regions/regions-close-over-type-parameter-multiple.nll.stderr
index 25566742099..66459957ed4 100644
--- a/src/test/ui/regions/regions-close-over-type-parameter-multiple.nll.stderr
+++ b/src/test/ui/regions/regions-close-over-type-parameter-multiple.nll.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/regions-close-over-type-parameter-multiple.rs:20:5
+  --> $DIR/regions-close-over-type-parameter-multiple.rs:23:5
    |
 LL | fn make_object_bad<'a,'b,'c,A:SomeTrait+'a+'b>(v: A) -> Box<dyn SomeTrait + 'c> {
    |                    --    -- lifetime `'c` defined here
diff --git a/src/test/ui/regions/regions-close-over-type-parameter-multiple.rs b/src/test/ui/regions/regions-close-over-type-parameter-multiple.rs
index fc7696e7e03..3d5f4e12665 100644
--- a/src/test/ui/regions/regions-close-over-type-parameter-multiple.rs
+++ b/src/test/ui/regions/regions-close-over-type-parameter-multiple.rs
@@ -1,6 +1,9 @@
 // Various tests where we over type parameters with multiple lifetime
 // bounds.
 
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
 
 trait SomeTrait { fn get(&self) -> isize; }
 
@@ -17,7 +20,9 @@ fn make_object_good2<'a,'b,A:SomeTrait+'a+'b>(v: A) -> Box<dyn SomeTrait + 'b> {
 
 fn make_object_bad<'a,'b,'c,A:SomeTrait+'a+'b>(v: A) -> Box<dyn SomeTrait + 'c> {
     // A outlives 'a AND 'b...but not 'c.
-    Box::new(v) as Box<dyn SomeTrait + 'a> //~ ERROR cannot infer an appropriate lifetime
+    Box::new(v) as Box<dyn SomeTrait + 'a>
+    //[base]~^ ERROR cannot infer an appropriate lifetime
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn main() {
diff --git a/src/test/ui/regions/regions-close-param-into-object.stderr b/src/test/ui/regions/regions-close-param-into-object.base.stderr
index 5c355bbb734..79a5d34dea8 100644
--- a/src/test/ui/regions/regions-close-param-into-object.stderr
+++ b/src/test/ui/regions/regions-close-param-into-object.base.stderr
@@ -1,38 +1,46 @@
 error[E0310]: the parameter type `T` may not live long enough
-  --> $DIR/regions-close-param-into-object.rs:6:5
+  --> $DIR/regions-close-param-into-object.rs:10:5
    |
-LL | fn p1<T>(v: T) -> Box<dyn X + 'static>
-   |       - help: consider adding an explicit lifetime bound...: `T: 'static`
-...
 LL |     Box::new(v)
    |     ^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL |     where T : X + 'static
+   |                 +++++++++
 
 error[E0310]: the parameter type `T` may not live long enough
-  --> $DIR/regions-close-param-into-object.rs:12:5
+  --> $DIR/regions-close-param-into-object.rs:16:5
    |
-LL | fn p2<T>(v: Box<T>) -> Box<dyn X + 'static>
-   |       - help: consider adding an explicit lifetime bound...: `T: 'static`
-...
 LL |     Box::new(v)
    |     ^^^^^^^^^^^ ...so that the type `Box<T>` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn p2<T: 'static>(v: Box<T>) -> Box<dyn X + 'static>
+   |        +++++++++
 
 error[E0309]: the parameter type `T` may not live long enough
-  --> $DIR/regions-close-param-into-object.rs:18:5
+  --> $DIR/regions-close-param-into-object.rs:22:5
    |
-LL | fn p3<'a,T>(v: T) -> Box<dyn X + 'a>
-   |          - help: consider adding an explicit lifetime bound...: `T: 'a`
-...
 LL |     Box::new(v)
    |     ^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL |     where T : X + 'a
+   |                 ++++
 
 error[E0309]: the parameter type `T` may not live long enough
-  --> $DIR/regions-close-param-into-object.rs:24:5
+  --> $DIR/regions-close-param-into-object.rs:28:5
    |
-LL | fn p4<'a,T>(v: Box<T>) -> Box<dyn X + 'a>
-   |          - help: consider adding an explicit lifetime bound...: `T: 'a`
-...
 LL |     Box::new(v)
    |     ^^^^^^^^^^^ ...so that the type `Box<T>` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn p4<'a,T: 'a>(v: Box<T>) -> Box<dyn X + 'a>
+   |           ++++
 
 error: aborting due to 4 previous errors
 
diff --git a/src/test/ui/regions/regions-close-param-into-object.nll.stderr b/src/test/ui/regions/regions-close-param-into-object.nll.stderr
index 7bd7824f00a..6ee12d5b82c 100644
--- a/src/test/ui/regions/regions-close-param-into-object.nll.stderr
+++ b/src/test/ui/regions/regions-close-param-into-object.nll.stderr
@@ -1,34 +1,46 @@
 error[E0310]: the parameter type `T` may not live long enough
-  --> $DIR/regions-close-param-into-object.rs:6:5
+  --> $DIR/regions-close-param-into-object.rs:10:5
    |
 LL |     Box::new(v)
-   |     ^^^^^^^^^^^
+   |     ^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
    |
-   = help: consider adding an explicit lifetime bound `T: 'static`...
+help: consider adding an explicit lifetime bound...
+   |
+LL |     where T : X + 'static
+   |                 +++++++++
 
 error[E0310]: the parameter type `T` may not live long enough
-  --> $DIR/regions-close-param-into-object.rs:12:5
+  --> $DIR/regions-close-param-into-object.rs:16:5
    |
 LL |     Box::new(v)
-   |     ^^^^^^^^^^^
+   |     ^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
    |
-   = help: consider adding an explicit lifetime bound `T: 'static`...
+LL | fn p2<T: 'static>(v: Box<T>) -> Box<dyn X + 'static>
+   |        +++++++++
 
 error[E0309]: the parameter type `T` may not live long enough
-  --> $DIR/regions-close-param-into-object.rs:18:5
+  --> $DIR/regions-close-param-into-object.rs:22:5
    |
 LL |     Box::new(v)
-   |     ^^^^^^^^^^^
+   |     ^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
    |
-   = help: consider adding an explicit lifetime bound `T: 'a`...
+help: consider adding an explicit lifetime bound...
+   |
+LL |     where T : X + 'a
+   |                 ++++
 
 error[E0309]: the parameter type `T` may not live long enough
-  --> $DIR/regions-close-param-into-object.rs:24:5
+  --> $DIR/regions-close-param-into-object.rs:28:5
    |
 LL |     Box::new(v)
-   |     ^^^^^^^^^^^
+   |     ^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
    |
-   = help: consider adding an explicit lifetime bound `T: 'a`...
+LL | fn p4<'a,T: 'a>(v: Box<T>) -> Box<dyn X + 'a>
+   |           ++++
 
 error: aborting due to 4 previous errors
 
diff --git a/src/test/ui/regions/regions-close-param-into-object.rs b/src/test/ui/regions/regions-close-param-into-object.rs
index 2760e5eed95..aab3ad202e6 100644
--- a/src/test/ui/regions/regions-close-param-into-object.rs
+++ b/src/test/ui/regions/regions-close-param-into-object.rs
@@ -1,3 +1,7 @@
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 trait X { fn foo(&self) {} }
 
 fn p1<T>(v: T) -> Box<dyn X + 'static>
diff --git a/src/test/ui/regions/regions-creating-enums3.stderr b/src/test/ui/regions/regions-creating-enums3.base.stderr
index 2fc1fc3f681..68a7b473695 100644
--- a/src/test/ui/regions/regions-creating-enums3.stderr
+++ b/src/test/ui/regions/regions-creating-enums3.base.stderr
@@ -1,5 +1,5 @@
 error[E0623]: lifetime mismatch
-  --> $DIR/regions-creating-enums3.rs:7:5
+  --> $DIR/regions-creating-enums3.rs:11:5
    |
 LL | fn mk_add_bad1<'a,'b>(x: &'a Ast<'a>, y: &'b Ast<'b>) -> Ast<'a> {
    |                                          -----------     -------
diff --git a/src/test/ui/regions/regions-creating-enums3.nll.stderr b/src/test/ui/regions/regions-creating-enums3.nll.stderr
index 41d609b56d2..8334dc77687 100644
--- a/src/test/ui/regions/regions-creating-enums3.nll.stderr
+++ b/src/test/ui/regions/regions-creating-enums3.nll.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/regions-creating-enums3.rs:7:5
+  --> $DIR/regions-creating-enums3.rs:11:5
    |
 LL | fn mk_add_bad1<'a,'b>(x: &'a Ast<'a>, y: &'b Ast<'b>) -> Ast<'a> {
    |                -- -- lifetime `'b` defined here
diff --git a/src/test/ui/regions/regions-creating-enums3.rs b/src/test/ui/regions/regions-creating-enums3.rs
index 3da0cb4cb81..dcea761d33f 100644
--- a/src/test/ui/regions/regions-creating-enums3.rs
+++ b/src/test/ui/regions/regions-creating-enums3.rs
@@ -1,10 +1,16 @@
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 enum Ast<'a> {
     Num(usize),
     Add(&'a Ast<'a>, &'a Ast<'a>)
 }
 
 fn mk_add_bad1<'a,'b>(x: &'a Ast<'a>, y: &'b Ast<'b>) -> Ast<'a> {
-    Ast::Add(x, y) //~ ERROR lifetime mismatch [E0623]
+    Ast::Add(x, y)
+    //[base]~^ ERROR lifetime mismatch [E0623]
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn main() {
diff --git a/src/test/ui/regions/regions-creating-enums4.stderr b/src/test/ui/regions/regions-creating-enums4.base.stderr
index 8b1b90f5b1a..445a4291f27 100644
--- a/src/test/ui/regions/regions-creating-enums4.stderr
+++ b/src/test/ui/regions/regions-creating-enums4.base.stderr
@@ -1,28 +1,28 @@
 error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
-  --> $DIR/regions-creating-enums4.rs:7:5
+  --> $DIR/regions-creating-enums4.rs:11:5
    |
 LL |     Ast::Add(x, y)
    |     ^^^^^^^^
    |
 note: first, the lifetime cannot outlive the lifetime `'a` as defined here...
-  --> $DIR/regions-creating-enums4.rs:6:16
+  --> $DIR/regions-creating-enums4.rs:10:16
    |
 LL | fn mk_add_bad2<'a,'b>(x: &'a Ast<'a>, y: &'a Ast<'a>, z: &Ast) -> Ast<'b> {
    |                ^^
 note: ...so that the expression is assignable
-  --> $DIR/regions-creating-enums4.rs:7:14
+  --> $DIR/regions-creating-enums4.rs:11:14
    |
 LL |     Ast::Add(x, y)
    |              ^
    = note: expected `&Ast<'_>`
               found `&Ast<'a>`
 note: but, the lifetime must be valid for the lifetime `'b` as defined here...
-  --> $DIR/regions-creating-enums4.rs:6:19
+  --> $DIR/regions-creating-enums4.rs:10:19
    |
 LL | fn mk_add_bad2<'a,'b>(x: &'a Ast<'a>, y: &'a Ast<'a>, z: &Ast) -> Ast<'b> {
    |                   ^^
 note: ...so that the types are compatible
-  --> $DIR/regions-creating-enums4.rs:7:5
+  --> $DIR/regions-creating-enums4.rs:11:5
    |
 LL |     Ast::Add(x, y)
    |     ^^^^^^^^^^^^^^
diff --git a/src/test/ui/regions/regions-creating-enums4.nll.stderr b/src/test/ui/regions/regions-creating-enums4.nll.stderr
index 91cf57e099d..e215c63d39d 100644
--- a/src/test/ui/regions/regions-creating-enums4.nll.stderr
+++ b/src/test/ui/regions/regions-creating-enums4.nll.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/regions-creating-enums4.rs:7:5
+  --> $DIR/regions-creating-enums4.rs:11:5
    |
 LL | fn mk_add_bad2<'a,'b>(x: &'a Ast<'a>, y: &'a Ast<'a>, z: &Ast) -> Ast<'b> {
    |                -- -- lifetime `'b` defined here
diff --git a/src/test/ui/regions/regions-creating-enums4.rs b/src/test/ui/regions/regions-creating-enums4.rs
index 11d3d831151..18bd592b1c3 100644
--- a/src/test/ui/regions/regions-creating-enums4.rs
+++ b/src/test/ui/regions/regions-creating-enums4.rs
@@ -1,10 +1,16 @@
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 enum Ast<'a> {
     Num(usize),
     Add(&'a Ast<'a>, &'a Ast<'a>)
 }
 
 fn mk_add_bad2<'a,'b>(x: &'a Ast<'a>, y: &'a Ast<'a>, z: &Ast) -> Ast<'b> {
-    Ast::Add(x, y) //~ ERROR cannot infer
+    Ast::Add(x, y)
+    //[base]~^ ERROR cannot infer
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn main() {
diff --git a/src/test/ui/regions/regions-early-bound-error-method.stderr b/src/test/ui/regions/regions-early-bound-error-method.base.stderr
index 99a5f0ce4cd..9e1f2b0e5bd 100644
--- a/src/test/ui/regions/regions-early-bound-error-method.stderr
+++ b/src/test/ui/regions/regions-early-bound-error-method.base.stderr
@@ -1,16 +1,16 @@
 error[E0312]: lifetime of reference outlives lifetime of borrowed content...
-  --> $DIR/regions-early-bound-error-method.rs:20:9
+  --> $DIR/regions-early-bound-error-method.rs:24:9
    |
 LL |         g2.get()
    |         ^^^^^^^^
    |
 note: ...the reference is valid for the lifetime `'a` as defined here...
-  --> $DIR/regions-early-bound-error-method.rs:18:6
+  --> $DIR/regions-early-bound-error-method.rs:22:6
    |
 LL | impl<'a> Box<'a> {
    |      ^^
 note: ...but the borrowed content is only valid for the lifetime `'b` as defined here
-  --> $DIR/regions-early-bound-error-method.rs:19:11
+  --> $DIR/regions-early-bound-error-method.rs:23:11
    |
 LL |     fn or<'b,G:GetRef<'b>>(&self, g2: G) -> &'a isize {
    |           ^^
diff --git a/src/test/ui/regions/regions-early-bound-error-method.nll.stderr b/src/test/ui/regions/regions-early-bound-error-method.nll.stderr
index 7f10c051f29..98389ed0ca5 100644
--- a/src/test/ui/regions/regions-early-bound-error-method.nll.stderr
+++ b/src/test/ui/regions/regions-early-bound-error-method.nll.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/regions-early-bound-error-method.rs:20:9
+  --> $DIR/regions-early-bound-error-method.rs:24:9
    |
 LL | impl<'a> Box<'a> {
    |      -- lifetime `'a` defined here
diff --git a/src/test/ui/regions/regions-early-bound-error-method.rs b/src/test/ui/regions/regions-early-bound-error-method.rs
index 32428143ef9..44ee19fa898 100644
--- a/src/test/ui/regions/regions-early-bound-error-method.rs
+++ b/src/test/ui/regions/regions-early-bound-error-method.rs
@@ -1,6 +1,10 @@
 // Tests that you can use a fn lifetime parameter as part of
 // the value for a type parameter in a bound.
 
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 trait GetRef<'a> {
     fn get(&self) -> &'a isize;
 }
@@ -18,7 +22,8 @@ impl<'a> GetRef<'a> for Box<'a> {
 impl<'a> Box<'a> {
     fn or<'b,G:GetRef<'b>>(&self, g2: G) -> &'a isize {
         g2.get()
-        //~^ ERROR E0312
+        //[base]~^ ERROR E0312
+        //[nll]~^^ ERROR lifetime may not live long enough
     }
 }
 
diff --git a/src/test/ui/regions/regions-early-bound-error.stderr b/src/test/ui/regions/regions-early-bound-error.base.stderr
index df9e979eacf..e1b60536d29 100644
--- a/src/test/ui/regions/regions-early-bound-error.stderr
+++ b/src/test/ui/regions/regions-early-bound-error.base.stderr
@@ -1,16 +1,16 @@
 error[E0312]: lifetime of reference outlives lifetime of borrowed content...
-  --> $DIR/regions-early-bound-error.rs:19:5
+  --> $DIR/regions-early-bound-error.rs:23:5
    |
 LL |     g1.get()
    |     ^^^^^^^^
    |
 note: ...the reference is valid for the lifetime `'b` as defined here...
-  --> $DIR/regions-early-bound-error.rs:18:11
+  --> $DIR/regions-early-bound-error.rs:22:11
    |
 LL | fn get<'a,'b,G:GetRef<'a, isize>>(g1: G, b: &'b isize) -> &'b isize {
    |           ^^
 note: ...but the borrowed content is only valid for the lifetime `'a` as defined here
-  --> $DIR/regions-early-bound-error.rs:18:8
+  --> $DIR/regions-early-bound-error.rs:22:8
    |
 LL | fn get<'a,'b,G:GetRef<'a, isize>>(g1: G, b: &'b isize) -> &'b isize {
    |        ^^
diff --git a/src/test/ui/regions/regions-early-bound-error.nll.stderr b/src/test/ui/regions/regions-early-bound-error.nll.stderr
index eb4cd5ca72e..f57249e4e8f 100644
--- a/src/test/ui/regions/regions-early-bound-error.nll.stderr
+++ b/src/test/ui/regions/regions-early-bound-error.nll.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/regions-early-bound-error.rs:19:5
+  --> $DIR/regions-early-bound-error.rs:23:5
    |
 LL | fn get<'a,'b,G:GetRef<'a, isize>>(g1: G, b: &'b isize) -> &'b isize {
    |        -- -- lifetime `'b` defined here
diff --git a/src/test/ui/regions/regions-early-bound-error.rs b/src/test/ui/regions/regions-early-bound-error.rs
index 78dad4f2649..372596cd5f4 100644
--- a/src/test/ui/regions/regions-early-bound-error.rs
+++ b/src/test/ui/regions/regions-early-bound-error.rs
@@ -1,6 +1,10 @@
 // Tests that you can use a fn lifetime parameter as part of
 // the value for a type parameter in a bound.
 
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 trait GetRef<'a, T> {
     fn get(&self) -> &'a T;
 }
@@ -17,7 +21,8 @@ impl<'a,T:Clone> GetRef<'a,T> for Box<'a,T> {
 
 fn get<'a,'b,G:GetRef<'a, isize>>(g1: G, b: &'b isize) -> &'b isize {
     g1.get()
-    //~^ ERROR E0312
+    //[base]~^ ERROR E0312
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn main() {
diff --git a/src/test/ui/regions/regions-fn-subtyping-return-static-fail.stderr b/src/test/ui/regions/regions-fn-subtyping-return-static-fail.base.stderr
index c9ce936c7d4..2182d8f661f 100644
--- a/src/test/ui/regions/regions-fn-subtyping-return-static-fail.stderr
+++ b/src/test/ui/regions/regions-fn-subtyping-return-static-fail.base.stderr
@@ -1,5 +1,5 @@
 error[E0308]: mismatched types
-  --> $DIR/regions-fn-subtyping-return-static-fail.rs:48:12
+  --> $DIR/regions-fn-subtyping-return-static-fail.rs:52:12
    |
 LL |     want_G(baz);
    |            ^^^ one type is more general than the other
diff --git a/src/test/ui/regions/regions-fn-subtyping-return-static-fail.nll.stderr b/src/test/ui/regions/regions-fn-subtyping-return-static-fail.nll.stderr
index c2956cd8958..0bca2cfbefd 100644
--- a/src/test/ui/regions/regions-fn-subtyping-return-static-fail.nll.stderr
+++ b/src/test/ui/regions/regions-fn-subtyping-return-static-fail.nll.stderr
@@ -1,5 +1,5 @@
 error[E0308]: mismatched types
-  --> $DIR/regions-fn-subtyping-return-static-fail.rs:48:5
+  --> $DIR/regions-fn-subtyping-return-static-fail.rs:52:5
    |
 LL |     want_G(baz);
    |     ^^^^^^^^^^^ one type is more general than the other
diff --git a/src/test/ui/regions/regions-fn-subtyping-return-static-fail.rs b/src/test/ui/regions/regions-fn-subtyping-return-static-fail.rs
index 539221b5a04..05c6ac0cb1a 100644
--- a/src/test/ui/regions/regions-fn-subtyping-return-static-fail.rs
+++ b/src/test/ui/regions/regions-fn-subtyping-return-static-fail.rs
@@ -6,6 +6,10 @@
 // This can safely be considered to be an instance of `F` because all
 // lifetimes are sublifetimes of 'static.
 
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 #![allow(dead_code)]
 #![allow(unused_variables)]
 
diff --git a/src/test/ui/regions/regions-free-region-ordering-callee.stderr b/src/test/ui/regions/regions-free-region-ordering-callee.base.stderr
index 4648bf046bc..ae6d95dd469 100644
--- a/src/test/ui/regions/regions-free-region-ordering-callee.stderr
+++ b/src/test/ui/regions/regions-free-region-ordering-callee.base.stderr
@@ -1,5 +1,5 @@
 error[E0623]: lifetime mismatch
-  --> $DIR/regions-free-region-ordering-callee.rs:13:5
+  --> $DIR/regions-free-region-ordering-callee.rs:17:5
    |
 LL | fn ordering2<'a, 'b>(x: &'a &'b usize, y: &'a usize) -> &'b usize {
    |                         -------------                   ---------
@@ -10,7 +10,7 @@ LL |     &*y
    |     ^^^ ...but data from `x` is returned here
 
 error[E0623]: lifetime mismatch
-  --> $DIR/regions-free-region-ordering-callee.rs:18:24
+  --> $DIR/regions-free-region-ordering-callee.rs:24:24
    |
 LL | fn ordering3<'a, 'b>(x: &'a usize, y: &'b usize) -> &'a &'b usize {
    |                                       ---------     -------------
diff --git a/src/test/ui/regions/regions-free-region-ordering-callee.nll.stderr b/src/test/ui/regions/regions-free-region-ordering-callee.nll.stderr
index f61f068a486..7dfff2bb060 100644
--- a/src/test/ui/regions/regions-free-region-ordering-callee.nll.stderr
+++ b/src/test/ui/regions/regions-free-region-ordering-callee.nll.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/regions-free-region-ordering-callee.rs:13:5
+  --> $DIR/regions-free-region-ordering-callee.rs:17:5
    |
 LL | fn ordering2<'a, 'b>(x: &'a &'b usize, y: &'a usize) -> &'b usize {
    |              --  -- lifetime `'b` defined here
@@ -12,7 +12,7 @@ LL |     &*y
    = help: consider adding the following bound: `'a: 'b`
 
 error: lifetime may not live long enough
-  --> $DIR/regions-free-region-ordering-callee.rs:18:12
+  --> $DIR/regions-free-region-ordering-callee.rs:24:12
    |
 LL | fn ordering3<'a, 'b>(x: &'a usize, y: &'b usize) -> &'a &'b usize {
    |              --  -- lifetime `'b` defined here
diff --git a/src/test/ui/regions/regions-free-region-ordering-callee.rs b/src/test/ui/regions/regions-free-region-ordering-callee.rs
index ee9a977a74f..eca863f2e79 100644
--- a/src/test/ui/regions/regions-free-region-ordering-callee.rs
+++ b/src/test/ui/regions/regions-free-region-ordering-callee.rs
@@ -2,6 +2,10 @@
 // that appear in their parameter list.  See also
 // regions-free-region-ordering-caller.rs
 
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 fn ordering1<'a, 'b>(x: &'a &'b usize) -> &'a usize {
     // It is safe to assume that 'a <= 'b due to the type of x
     let y: &'b usize = &**x;
@@ -10,13 +14,16 @@ fn ordering1<'a, 'b>(x: &'a &'b usize) -> &'a usize {
 
 fn ordering2<'a, 'b>(x: &'a &'b usize, y: &'a usize) -> &'b usize {
     // However, it is not safe to assume that 'b <= 'a
-    &*y //~ ERROR lifetime mismatch [E0623]
+    &*y
+    //[base]~^ ERROR lifetime mismatch [E0623]
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn ordering3<'a, 'b>(x: &'a usize, y: &'b usize) -> &'a &'b usize {
     // Do not infer an ordering from the return value.
     let z: &'b usize = &*x;
-    //~^ ERROR lifetime mismatch [E0623]
+    //[base]~^ ERROR lifetime mismatch [E0623]
+    //[nll]~^^ ERROR lifetime may not live long enough
     panic!();
 }
 
diff --git a/src/test/ui/regions/regions-free-region-ordering-incorrect.stderr b/src/test/ui/regions/regions-free-region-ordering-incorrect.base.stderr
index b0a8f4af397..eb4ffce89a3 100644
--- a/src/test/ui/regions/regions-free-region-ordering-incorrect.stderr
+++ b/src/test/ui/regions/regions-free-region-ordering-incorrect.base.stderr
@@ -1,26 +1,26 @@
 error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
-  --> $DIR/regions-free-region-ordering-incorrect.rs:17:21
+  --> $DIR/regions-free-region-ordering-incorrect.rs:21:21
    |
 LL |             None => &self.val
    |                     ^^^^^^^^^
    |
 note: first, the lifetime cannot outlive the lifetime `'a` as defined here...
-  --> $DIR/regions-free-region-ordering-incorrect.rs:14:12
+  --> $DIR/regions-free-region-ordering-incorrect.rs:18:12
    |
 LL |     fn get<'a>(&'a self) -> &'b T {
    |            ^^
 note: ...so that reference does not outlive borrowed content
-  --> $DIR/regions-free-region-ordering-incorrect.rs:17:21
+  --> $DIR/regions-free-region-ordering-incorrect.rs:21:21
    |
 LL |             None => &self.val
    |                     ^^^^^^^^^
 note: but, the lifetime must be valid for the lifetime `'b` as defined here...
-  --> $DIR/regions-free-region-ordering-incorrect.rs:13:6
+  --> $DIR/regions-free-region-ordering-incorrect.rs:17:6
    |
 LL | impl<'b, T> Node<'b, T> {
    |      ^^
 note: ...so that reference does not outlive borrowed content
-  --> $DIR/regions-free-region-ordering-incorrect.rs:15:9
+  --> $DIR/regions-free-region-ordering-incorrect.rs:19:9
    |
 LL | /         match self.next {
 LL | |             Some(ref next) => next.get(),
diff --git a/src/test/ui/regions/regions-free-region-ordering-incorrect.nll.stderr b/src/test/ui/regions/regions-free-region-ordering-incorrect.nll.stderr
index f7c75033c04..336cfd3e6c5 100644
--- a/src/test/ui/regions/regions-free-region-ordering-incorrect.nll.stderr
+++ b/src/test/ui/regions/regions-free-region-ordering-incorrect.nll.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/regions-free-region-ordering-incorrect.rs:15:9
+  --> $DIR/regions-free-region-ordering-incorrect.rs:19:9
    |
 LL |   impl<'b, T> Node<'b, T> {
    |        -- lifetime `'b` defined here
diff --git a/src/test/ui/regions/regions-free-region-ordering-incorrect.rs b/src/test/ui/regions/regions-free-region-ordering-incorrect.rs
index 65e3f52609e..43427d13ffa 100644
--- a/src/test/ui/regions/regions-free-region-ordering-incorrect.rs
+++ b/src/test/ui/regions/regions-free-region-ordering-incorrect.rs
@@ -5,6 +5,10 @@
 //
 // This test began its life as a test for issue #4325.
 
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 struct Node<'b, T: 'b> {
     val: T,
     next: Option<&'b Node<'b, T>>
@@ -12,9 +16,9 @@ struct Node<'b, T: 'b> {
 
 impl<'b, T> Node<'b, T> {
     fn get<'a>(&'a self) -> &'b T {
-        match self.next {
+        match self.next { //[nll]~ ERROR lifetime may not live long enough
             Some(ref next) => next.get(),
-            None => &self.val //~ ERROR cannot infer
+            None => &self.val //[base]~ ERROR cannot infer
         }
     }
 }
diff --git a/src/test/ui/regions/regions-free-region-outlives-static-outlives-free-region.rs b/src/test/ui/regions/regions-free-region-outlives-static-outlives-free-region.rs
index f464cab7554..7c2e1aeeea6 100644
--- a/src/test/ui/regions/regions-free-region-outlives-static-outlives-free-region.rs
+++ b/src/test/ui/regions/regions-free-region-outlives-static-outlives-free-region.rs
@@ -9,7 +9,7 @@
 //     'a : 'b
 
 fn test<'a,'b>(x: &'a i32) -> &'b i32
-    where 'a: 'static
+    where 'a: 'static //~ WARN unnecessary lifetime parameter `'a`
 {
     x
 }
diff --git a/src/test/ui/regions/regions-free-region-outlives-static-outlives-free-region.stderr b/src/test/ui/regions/regions-free-region-outlives-static-outlives-free-region.stderr
new file mode 100644
index 00000000000..70ed418d5cb
--- /dev/null
+++ b/src/test/ui/regions/regions-free-region-outlives-static-outlives-free-region.stderr
@@ -0,0 +1,10 @@
+warning: unnecessary lifetime parameter `'a`
+  --> $DIR/regions-free-region-outlives-static-outlives-free-region.rs:12:11
+   |
+LL |     where 'a: 'static
+   |           ^^
+   |
+   = help: you can use the `'static` lifetime directly, in place of `'a`
+
+warning: 1 warning emitted
+
diff --git a/src/test/ui/regions/regions-implied-bounds-projection-gap-1.stderr b/src/test/ui/regions/regions-implied-bounds-projection-gap-1.base.stderr
index ea59ea11a14..85ced4b5241 100644
--- a/src/test/ui/regions/regions-implied-bounds-projection-gap-1.stderr
+++ b/src/test/ui/regions/regions-implied-bounds-projection-gap-1.base.stderr
@@ -1,11 +1,13 @@
 error[E0309]: the parameter type `T` may not live long enough
-  --> $DIR/regions-implied-bounds-projection-gap-1.rs:16:10
+  --> $DIR/regions-implied-bounds-projection-gap-1.rs:20:10
    |
-LL | fn func<'x, T:Trait1<'x>>(t: &'x T::Foo)
-   |             -- help: consider adding an explicit lifetime bound...: `T: 'x +`
-LL | {
 LL |     wf::<&'x T>();
    |          ^^^^^ ...so that the reference type `&'x T` does not outlive the data it points at
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn func<'x, T:Trait1<'x> + 'x>(t: &'x T::Foo)
+   |                          ++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/regions/regions-implied-bounds-projection-gap-1.nll.stderr b/src/test/ui/regions/regions-implied-bounds-projection-gap-1.nll.stderr
index 0f0f86dfcdd..1a428eb25d7 100644
--- a/src/test/ui/regions/regions-implied-bounds-projection-gap-1.nll.stderr
+++ b/src/test/ui/regions/regions-implied-bounds-projection-gap-1.nll.stderr
@@ -1,10 +1,13 @@
 error[E0309]: the parameter type `T` may not live long enough
-  --> $DIR/regions-implied-bounds-projection-gap-1.rs:16:5
+  --> $DIR/regions-implied-bounds-projection-gap-1.rs:20:5
    |
 LL |     wf::<&'x T>();
-   |     ^^^^^^^^^^^
+   |     ^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
    |
-   = help: consider adding an explicit lifetime bound `T: 'x`...
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn func<'x, T:Trait1<'x> + 'x>(t: &'x T::Foo)
+   |                          ++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/regions/regions-implied-bounds-projection-gap-1.rs b/src/test/ui/regions/regions-implied-bounds-projection-gap-1.rs
index 38fc9c462da..f11fc207b91 100644
--- a/src/test/ui/regions/regions-implied-bounds-projection-gap-1.rs
+++ b/src/test/ui/regions/regions-implied-bounds-projection-gap-1.rs
@@ -3,6 +3,10 @@
 // there might be other ways for the caller of `func` to show that
 // `T::Foo: 'x` holds (e.g., where-clause).
 
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 trait Trait1<'x> {
     type Foo;
 }
diff --git a/src/test/ui/regions/regions-infer-bound-from-trait-self.stderr b/src/test/ui/regions/regions-infer-bound-from-trait-self.base.stderr
index 97a3947bc0a..faa638aa281 100644
--- a/src/test/ui/regions/regions-infer-bound-from-trait-self.stderr
+++ b/src/test/ui/regions/regions-infer-bound-from-trait-self.base.stderr
@@ -1,5 +1,5 @@
 error[E0309]: the parameter type `Self` may not live long enough
-  --> $DIR/regions-infer-bound-from-trait-self.rs:46:9
+  --> $DIR/regions-infer-bound-from-trait-self.rs:50:9
    |
 LL |         check_bound(x, self)
    |         ^^^^^^^^^^^
@@ -7,7 +7,7 @@ LL |         check_bound(x, self)
    = help: consider adding an explicit lifetime bound `Self: 'a`...
    = note: ...so that the type `Self` will meet its required lifetime bounds...
 note: ...that is required by this bound
-  --> $DIR/regions-infer-bound-from-trait-self.rs:12:21
+  --> $DIR/regions-infer-bound-from-trait-self.rs:16:21
    |
 LL | fn check_bound<'a,A:'a>(x: Inv<'a>, a: A) { }
    |                     ^^
diff --git a/src/test/ui/regions/regions-infer-bound-from-trait-self.nll.stderr b/src/test/ui/regions/regions-infer-bound-from-trait-self.nll.stderr
index 0651e305cde..9c886c42c72 100644
--- a/src/test/ui/regions/regions-infer-bound-from-trait-self.nll.stderr
+++ b/src/test/ui/regions/regions-infer-bound-from-trait-self.nll.stderr
@@ -1,10 +1,11 @@
 error[E0309]: the parameter type `Self` may not live long enough
-  --> $DIR/regions-infer-bound-from-trait-self.rs:46:9
+  --> $DIR/regions-infer-bound-from-trait-self.rs:50:9
    |
 LL |         check_bound(x, self)
    |         ^^^^^^^^^^^^^^^^^^^^
    |
    = help: consider adding an explicit lifetime bound `Self: 'a`...
+   = note: ...so that the type `Self` will meet its required lifetime bounds
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/regions/regions-infer-bound-from-trait-self.rs b/src/test/ui/regions/regions-infer-bound-from-trait-self.rs
index d15bfffe9d9..ef8be05b2d2 100644
--- a/src/test/ui/regions/regions-infer-bound-from-trait-self.rs
+++ b/src/test/ui/regions/regions-infer-bound-from-trait-self.rs
@@ -1,6 +1,10 @@
 // Test that we can derive lifetime bounds on `Self` from trait
 // inheritance.
 
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 trait Static : 'static { }
 
 trait Is<'a> : 'a { }
diff --git a/src/test/ui/regions/regions-infer-bound-from-trait.stderr b/src/test/ui/regions/regions-infer-bound-from-trait.base.stderr
index fd1090d2dbd..658740f3f87 100644
--- a/src/test/ui/regions/regions-infer-bound-from-trait.stderr
+++ b/src/test/ui/regions/regions-infer-bound-from-trait.base.stderr
@@ -1,30 +1,34 @@
 error[E0309]: the parameter type `A` may not live long enough
-  --> $DIR/regions-infer-bound-from-trait.rs:33:5
+  --> $DIR/regions-infer-bound-from-trait.rs:37:5
    |
-LL | fn bar1<'a,A>(x: Inv<'a>, a: A) {
-   |            - help: consider adding an explicit lifetime bound...: `A: 'a`
 LL |     check_bound(x, a)
    |     ^^^^^^^^^^^ ...so that the type `A` will meet its required lifetime bounds...
    |
 note: ...that is required by this bound
-  --> $DIR/regions-infer-bound-from-trait.rs:12:21
+  --> $DIR/regions-infer-bound-from-trait.rs:16:21
    |
 LL | fn check_bound<'a,A:'a>(x: Inv<'a>, a: A) { }
    |                     ^^
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn bar1<'a,A: 'a>(x: Inv<'a>, a: A) {
+   |             ++++
 
 error[E0309]: the parameter type `A` may not live long enough
-  --> $DIR/regions-infer-bound-from-trait.rs:37:5
+  --> $DIR/regions-infer-bound-from-trait.rs:41:5
    |
-LL | fn bar2<'a,'b,A:Is<'b>>(x: Inv<'a>, y: Inv<'b>, a: A) {
-   |               -- help: consider adding an explicit lifetime bound...: `A: 'a +`
 LL |     check_bound(x, a)
    |     ^^^^^^^^^^^ ...so that the type `A` will meet its required lifetime bounds...
    |
 note: ...that is required by this bound
-  --> $DIR/regions-infer-bound-from-trait.rs:12:21
+  --> $DIR/regions-infer-bound-from-trait.rs:16:21
    |
 LL | fn check_bound<'a,A:'a>(x: Inv<'a>, a: A) { }
    |                     ^^
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn bar2<'a,'b,A:Is<'b> + 'a>(x: Inv<'a>, y: Inv<'b>, a: A) {
+   |                        ++++
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/regions/regions-infer-bound-from-trait.nll.stderr b/src/test/ui/regions/regions-infer-bound-from-trait.nll.stderr
index 1f7b34fc699..5cc2d20c2e0 100644
--- a/src/test/ui/regions/regions-infer-bound-from-trait.nll.stderr
+++ b/src/test/ui/regions/regions-infer-bound-from-trait.nll.stderr
@@ -1,18 +1,24 @@
 error[E0309]: the parameter type `A` may not live long enough
-  --> $DIR/regions-infer-bound-from-trait.rs:33:5
+  --> $DIR/regions-infer-bound-from-trait.rs:37:5
    |
 LL |     check_bound(x, a)
-   |     ^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^^ ...so that the type `A` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
    |
-   = help: consider adding an explicit lifetime bound `A: 'a`...
+LL | fn bar1<'a,A: 'a>(x: Inv<'a>, a: A) {
+   |             ++++
 
 error[E0309]: the parameter type `A` may not live long enough
-  --> $DIR/regions-infer-bound-from-trait.rs:37:5
+  --> $DIR/regions-infer-bound-from-trait.rs:41:5
    |
 LL |     check_bound(x, a)
-   |     ^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^^ ...so that the type `A` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
    |
-   = help: consider adding an explicit lifetime bound `A: 'a`...
+LL | fn bar2<'a,'b,A:Is<'b> + 'a>(x: Inv<'a>, y: Inv<'b>, a: A) {
+   |                        ++++
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/regions/regions-infer-bound-from-trait.rs b/src/test/ui/regions/regions-infer-bound-from-trait.rs
index 610452182f8..96f9125313b 100644
--- a/src/test/ui/regions/regions-infer-bound-from-trait.rs
+++ b/src/test/ui/regions/regions-infer-bound-from-trait.rs
@@ -1,6 +1,10 @@
 // Test that we can derive lifetime bounds on type parameters
 // from trait inheritance.
 
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 trait Static : 'static { }
 
 trait Is<'a> : 'a { }
diff --git a/src/test/ui/regions/regions-infer-contravariance-due-to-decl.stderr b/src/test/ui/regions/regions-infer-contravariance-due-to-decl.base.stderr
index f3a0358b90f..fbe2c0da6e2 100644
--- a/src/test/ui/regions/regions-infer-contravariance-due-to-decl.stderr
+++ b/src/test/ui/regions/regions-infer-contravariance-due-to-decl.base.stderr
@@ -1,5 +1,5 @@
 error[E0623]: lifetime mismatch
-  --> $DIR/regions-infer-contravariance-due-to-decl.rs:25:35
+  --> $DIR/regions-infer-contravariance-due-to-decl.rs:29:35
    |
 LL | fn use_<'short,'long>(c: Contravariant<'short>,
    |                          --------------------- these two types are declared with different lifetimes...
diff --git a/src/test/ui/regions/regions-infer-contravariance-due-to-decl.nll.stderr b/src/test/ui/regions/regions-infer-contravariance-due-to-decl.nll.stderr
index 94b80852d01..0b1bf5271a7 100644
--- a/src/test/ui/regions/regions-infer-contravariance-due-to-decl.nll.stderr
+++ b/src/test/ui/regions/regions-infer-contravariance-due-to-decl.nll.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/regions-infer-contravariance-due-to-decl.rs:25:12
+  --> $DIR/regions-infer-contravariance-due-to-decl.rs:29:12
    |
 LL | fn use_<'short,'long>(c: Contravariant<'short>,
    |         ------ ----- lifetime `'long` defined here
diff --git a/src/test/ui/regions/regions-infer-contravariance-due-to-decl.rs b/src/test/ui/regions/regions-infer-contravariance-due-to-decl.rs
index 84161388a6e..233f72fd296 100644
--- a/src/test/ui/regions/regions-infer-contravariance-due-to-decl.rs
+++ b/src/test/ui/regions/regions-infer-contravariance-due-to-decl.rs
@@ -4,6 +4,10 @@
 // Note: see variance-regions-*.rs for the tests that check that the
 // variance inference works in the first place.
 
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 use std::marker;
 
 // This is contravariant with respect to 'a, meaning that
@@ -22,7 +26,9 @@ fn use_<'short,'long>(c: Contravariant<'short>,
     // 'short <= 'long, this would be true if the Contravariant type were
     // covariant with respect to its parameter 'a.
 
-    let _: Contravariant<'long> = c; //~ ERROR E0623
+    let _: Contravariant<'long> = c;
+    //[base]~^ ERROR E0623
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn main() {}
diff --git a/src/test/ui/regions/regions-infer-covariance-due-to-decl.stderr b/src/test/ui/regions/regions-infer-covariance-due-to-decl.base.stderr
index c3e2075fbc3..bb22e15af69 100644
--- a/src/test/ui/regions/regions-infer-covariance-due-to-decl.stderr
+++ b/src/test/ui/regions/regions-infer-covariance-due-to-decl.base.stderr
@@ -1,5 +1,5 @@
 error[E0623]: lifetime mismatch
-  --> $DIR/regions-infer-covariance-due-to-decl.rs:22:32
+  --> $DIR/regions-infer-covariance-due-to-decl.rs:26:32
    |
 LL | fn use_<'short,'long>(c: Covariant<'long>,
    |                          ----------------
diff --git a/src/test/ui/regions/regions-infer-covariance-due-to-decl.nll.stderr b/src/test/ui/regions/regions-infer-covariance-due-to-decl.nll.stderr
index f44a0fad59b..4d72b8471dc 100644
--- a/src/test/ui/regions/regions-infer-covariance-due-to-decl.nll.stderr
+++ b/src/test/ui/regions/regions-infer-covariance-due-to-decl.nll.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/regions-infer-covariance-due-to-decl.rs:22:12
+  --> $DIR/regions-infer-covariance-due-to-decl.rs:26:12
    |
 LL | fn use_<'short,'long>(c: Covariant<'long>,
    |         ------ ----- lifetime `'long` defined here
diff --git a/src/test/ui/regions/regions-infer-covariance-due-to-decl.rs b/src/test/ui/regions/regions-infer-covariance-due-to-decl.rs
index b5079206578..c4225e76967 100644
--- a/src/test/ui/regions/regions-infer-covariance-due-to-decl.rs
+++ b/src/test/ui/regions/regions-infer-covariance-due-to-decl.rs
@@ -4,6 +4,10 @@
 // Note: see variance-regions-*.rs for the tests that check that the
 // variance inference works in the first place.
 
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 use std::marker;
 
 struct Covariant<'a> {
@@ -19,7 +23,9 @@ fn use_<'short,'long>(c: Covariant<'long>,
     // 'short <= 'long, this would be true if the Covariant type were
     // contravariant with respect to its parameter 'a.
 
-    let _: Covariant<'short> = c; //~ ERROR E0623
+    let _: Covariant<'short> = c;
+    //[base]~^ ERROR E0623
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn main() {}
diff --git a/src/test/ui/regions/regions-infer-invariance-due-to-decl.stderr b/src/test/ui/regions/regions-infer-invariance-due-to-decl.base.stderr
index afd522aa003..dc7d0005ca2 100644
--- a/src/test/ui/regions/regions-infer-invariance-due-to-decl.stderr
+++ b/src/test/ui/regions/regions-infer-invariance-due-to-decl.base.stderr
@@ -1,5 +1,5 @@
 error[E0308]: mismatched types
-  --> $DIR/regions-infer-invariance-due-to-decl.rs:12:5
+  --> $DIR/regions-infer-invariance-due-to-decl.rs:16:5
    |
 LL |     b_isize
    |     ^^^^^^^ lifetime mismatch
@@ -7,7 +7,7 @@ LL |     b_isize
    = note: expected struct `Invariant<'static>`
               found struct `Invariant<'r>`
 note: the lifetime `'r` as defined here...
-  --> $DIR/regions-infer-invariance-due-to-decl.rs:11:23
+  --> $DIR/regions-infer-invariance-due-to-decl.rs:15:23
    |
 LL | fn to_longer_lifetime<'r>(b_isize: Invariant<'r>) -> Invariant<'static> {
    |                       ^^
diff --git a/src/test/ui/regions/regions-infer-invariance-due-to-decl.nll.stderr b/src/test/ui/regions/regions-infer-invariance-due-to-decl.nll.stderr
index c8c7808e06c..d7b7f9883a7 100644
--- a/src/test/ui/regions/regions-infer-invariance-due-to-decl.nll.stderr
+++ b/src/test/ui/regions/regions-infer-invariance-due-to-decl.nll.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/regions-infer-invariance-due-to-decl.rs:12:5
+  --> $DIR/regions-infer-invariance-due-to-decl.rs:16:5
    |
 LL | fn to_longer_lifetime<'r>(b_isize: Invariant<'r>) -> Invariant<'static> {
    |                       -- lifetime `'r` defined here
diff --git a/src/test/ui/regions/regions-infer-invariance-due-to-decl.rs b/src/test/ui/regions/regions-infer-invariance-due-to-decl.rs
index e0fa904e8fe..6433773b2d1 100644
--- a/src/test/ui/regions/regions-infer-invariance-due-to-decl.rs
+++ b/src/test/ui/regions/regions-infer-invariance-due-to-decl.rs
@@ -1,3 +1,7 @@
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 use std::marker;
 
 struct Invariant<'a> {
@@ -9,7 +13,9 @@ fn to_same_lifetime<'r>(b_isize: Invariant<'r>) {
 }
 
 fn to_longer_lifetime<'r>(b_isize: Invariant<'r>) -> Invariant<'static> {
-    b_isize //~ ERROR mismatched types
+    b_isize
+    //[base]~^ ERROR mismatched types
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn main() {
diff --git a/src/test/ui/regions/regions-infer-invariance-due-to-mutability-3.stderr b/src/test/ui/regions/regions-infer-invariance-due-to-mutability-3.base.stderr
index bb594f3676e..b2530d7b6cd 100644
--- a/src/test/ui/regions/regions-infer-invariance-due-to-mutability-3.stderr
+++ b/src/test/ui/regions/regions-infer-invariance-due-to-mutability-3.base.stderr
@@ -1,5 +1,5 @@
 error[E0308]: mismatched types
-  --> $DIR/regions-infer-invariance-due-to-mutability-3.rs:10:5
+  --> $DIR/regions-infer-invariance-due-to-mutability-3.rs:14:5
    |
 LL |     b_isize
    |     ^^^^^^^ lifetime mismatch
@@ -7,7 +7,7 @@ LL |     b_isize
    = note: expected struct `Invariant<'static>`
               found struct `Invariant<'r>`
 note: the lifetime `'r` as defined here...
-  --> $DIR/regions-infer-invariance-due-to-mutability-3.rs:9:23
+  --> $DIR/regions-infer-invariance-due-to-mutability-3.rs:13:23
    |
 LL | fn to_longer_lifetime<'r>(b_isize: Invariant<'r>) -> Invariant<'static> {
    |                       ^^
diff --git a/src/test/ui/regions/regions-infer-invariance-due-to-mutability-3.nll.stderr b/src/test/ui/regions/regions-infer-invariance-due-to-mutability-3.nll.stderr
index 1165011c1f4..37fa5e3bf44 100644
--- a/src/test/ui/regions/regions-infer-invariance-due-to-mutability-3.nll.stderr
+++ b/src/test/ui/regions/regions-infer-invariance-due-to-mutability-3.nll.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/regions-infer-invariance-due-to-mutability-3.rs:10:5
+  --> $DIR/regions-infer-invariance-due-to-mutability-3.rs:14:5
    |
 LL | fn to_longer_lifetime<'r>(b_isize: Invariant<'r>) -> Invariant<'static> {
    |                       -- lifetime `'r` defined here
diff --git a/src/test/ui/regions/regions-infer-invariance-due-to-mutability-3.rs b/src/test/ui/regions/regions-infer-invariance-due-to-mutability-3.rs
index 5843598ab48..4690f9d8b08 100644
--- a/src/test/ui/regions/regions-infer-invariance-due-to-mutability-3.rs
+++ b/src/test/ui/regions/regions-infer-invariance-due-to-mutability-3.rs
@@ -1,3 +1,7 @@
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 struct Invariant<'a> {
     f: Box<dyn FnOnce(&mut &'a isize) + 'static>,
 }
@@ -7,7 +11,9 @@ fn to_same_lifetime<'r>(b_isize: Invariant<'r>) {
 }
 
 fn to_longer_lifetime<'r>(b_isize: Invariant<'r>) -> Invariant<'static> {
-    b_isize //~ ERROR mismatched types
+    b_isize
+    //[base]~^ ERROR mismatched types
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn main() {
diff --git a/src/test/ui/regions/regions-infer-invariance-due-to-mutability-4.stderr b/src/test/ui/regions/regions-infer-invariance-due-to-mutability-4.base.stderr
index 04d11b5b7c7..12774ca92e2 100644
--- a/src/test/ui/regions/regions-infer-invariance-due-to-mutability-4.stderr
+++ b/src/test/ui/regions/regions-infer-invariance-due-to-mutability-4.base.stderr
@@ -1,5 +1,5 @@
 error[E0308]: mismatched types
-  --> $DIR/regions-infer-invariance-due-to-mutability-4.rs:10:5
+  --> $DIR/regions-infer-invariance-due-to-mutability-4.rs:14:5
    |
 LL |     b_isize
    |     ^^^^^^^ lifetime mismatch
@@ -7,7 +7,7 @@ LL |     b_isize
    = note: expected struct `Invariant<'static>`
               found struct `Invariant<'r>`
 note: the lifetime `'r` as defined here...
-  --> $DIR/regions-infer-invariance-due-to-mutability-4.rs:9:23
+  --> $DIR/regions-infer-invariance-due-to-mutability-4.rs:13:23
    |
 LL | fn to_longer_lifetime<'r>(b_isize: Invariant<'r>) -> Invariant<'static> {
    |                       ^^
diff --git a/src/test/ui/regions/regions-infer-invariance-due-to-mutability-4.nll.stderr b/src/test/ui/regions/regions-infer-invariance-due-to-mutability-4.nll.stderr
index f3973a93bad..1b3ef7bc028 100644
--- a/src/test/ui/regions/regions-infer-invariance-due-to-mutability-4.nll.stderr
+++ b/src/test/ui/regions/regions-infer-invariance-due-to-mutability-4.nll.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/regions-infer-invariance-due-to-mutability-4.rs:10:5
+  --> $DIR/regions-infer-invariance-due-to-mutability-4.rs:14:5
    |
 LL | fn to_longer_lifetime<'r>(b_isize: Invariant<'r>) -> Invariant<'static> {
    |                       -- lifetime `'r` defined here
diff --git a/src/test/ui/regions/regions-infer-invariance-due-to-mutability-4.rs b/src/test/ui/regions/regions-infer-invariance-due-to-mutability-4.rs
index f0af18cf618..8e257c4fd0e 100644
--- a/src/test/ui/regions/regions-infer-invariance-due-to-mutability-4.rs
+++ b/src/test/ui/regions/regions-infer-invariance-due-to-mutability-4.rs
@@ -1,3 +1,7 @@
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 struct Invariant<'a> {
     f: Box<dyn FnOnce() -> *mut &'a isize + 'static>,
 }
@@ -7,7 +11,9 @@ fn to_same_lifetime<'r>(b_isize: Invariant<'r>) {
 }
 
 fn to_longer_lifetime<'r>(b_isize: Invariant<'r>) -> Invariant<'static> {
-    b_isize //~ ERROR mismatched types
+    b_isize
+    //[base]~^ ERROR mismatched types
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn main() {
diff --git a/src/test/ui/regions/regions-infer-not-param.stderr b/src/test/ui/regions/regions-infer-not-param.base.stderr
index a23bdeb834f..f43274163d0 100644
--- a/src/test/ui/regions/regions-infer-not-param.stderr
+++ b/src/test/ui/regions/regions-infer-not-param.base.stderr
@@ -1,5 +1,5 @@
 error[E0308]: mismatched types
-  --> $DIR/regions-infer-not-param.rs:15:54
+  --> $DIR/regions-infer-not-param.rs:19:54
    |
 LL | fn take_direct<'a,'b>(p: Direct<'a>) -> Direct<'b> { p }
    |                                                      ^ lifetime mismatch
@@ -7,18 +7,18 @@ LL | fn take_direct<'a,'b>(p: Direct<'a>) -> Direct<'b> { p }
    = note: expected struct `Direct<'b>`
               found struct `Direct<'a>`
 note: the lifetime `'a` as defined here...
-  --> $DIR/regions-infer-not-param.rs:15:16
+  --> $DIR/regions-infer-not-param.rs:19:16
    |
 LL | fn take_direct<'a,'b>(p: Direct<'a>) -> Direct<'b> { p }
    |                ^^
 note: ...does not necessarily outlive the lifetime `'b` as defined here
-  --> $DIR/regions-infer-not-param.rs:15:19
+  --> $DIR/regions-infer-not-param.rs:19:19
    |
 LL | fn take_direct<'a,'b>(p: Direct<'a>) -> Direct<'b> { p }
    |                   ^^
 
 error[E0308]: mismatched types
-  --> $DIR/regions-infer-not-param.rs:19:63
+  --> $DIR/regions-infer-not-param.rs:25:63
    |
 LL | fn take_indirect2<'a,'b>(p: Indirect2<'a>) -> Indirect2<'b> { p }
    |                                                               ^ lifetime mismatch
@@ -26,18 +26,18 @@ LL | fn take_indirect2<'a,'b>(p: Indirect2<'a>) -> Indirect2<'b> { p }
    = note: expected struct `Indirect2<'b>`
               found struct `Indirect2<'a>`
 note: the lifetime `'a` as defined here...
-  --> $DIR/regions-infer-not-param.rs:19:19
+  --> $DIR/regions-infer-not-param.rs:25:19
    |
 LL | fn take_indirect2<'a,'b>(p: Indirect2<'a>) -> Indirect2<'b> { p }
    |                   ^^
 note: ...does not necessarily outlive the lifetime `'b` as defined here
-  --> $DIR/regions-infer-not-param.rs:19:22
+  --> $DIR/regions-infer-not-param.rs:25:22
    |
 LL | fn take_indirect2<'a,'b>(p: Indirect2<'a>) -> Indirect2<'b> { p }
    |                      ^^
 
 error[E0308]: mismatched types
-  --> $DIR/regions-infer-not-param.rs:19:63
+  --> $DIR/regions-infer-not-param.rs:25:63
    |
 LL | fn take_indirect2<'a,'b>(p: Indirect2<'a>) -> Indirect2<'b> { p }
    |                                                               ^ lifetime mismatch
@@ -45,12 +45,12 @@ LL | fn take_indirect2<'a,'b>(p: Indirect2<'a>) -> Indirect2<'b> { p }
    = note: expected struct `Indirect2<'b>`
               found struct `Indirect2<'a>`
 note: the lifetime `'b` as defined here...
-  --> $DIR/regions-infer-not-param.rs:19:22
+  --> $DIR/regions-infer-not-param.rs:25:22
    |
 LL | fn take_indirect2<'a,'b>(p: Indirect2<'a>) -> Indirect2<'b> { p }
    |                      ^^
 note: ...does not necessarily outlive the lifetime `'a` as defined here
-  --> $DIR/regions-infer-not-param.rs:19:19
+  --> $DIR/regions-infer-not-param.rs:25:19
    |
 LL | fn take_indirect2<'a,'b>(p: Indirect2<'a>) -> Indirect2<'b> { p }
    |                   ^^
diff --git a/src/test/ui/regions/regions-infer-not-param.nll.stderr b/src/test/ui/regions/regions-infer-not-param.nll.stderr
index f4875e49c3d..95e6b03c350 100644
--- a/src/test/ui/regions/regions-infer-not-param.nll.stderr
+++ b/src/test/ui/regions/regions-infer-not-param.nll.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/regions-infer-not-param.rs:15:54
+  --> $DIR/regions-infer-not-param.rs:19:54
    |
 LL | fn take_direct<'a,'b>(p: Direct<'a>) -> Direct<'b> { p }
    |                -- -- lifetime `'b` defined here      ^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
@@ -9,7 +9,7 @@ LL | fn take_direct<'a,'b>(p: Direct<'a>) -> Direct<'b> { p }
    = help: consider adding the following bound: `'a: 'b`
 
 error: lifetime may not live long enough
-  --> $DIR/regions-infer-not-param.rs:19:63
+  --> $DIR/regions-infer-not-param.rs:25:63
    |
 LL | fn take_indirect2<'a,'b>(p: Indirect2<'a>) -> Indirect2<'b> { p }
    |                   -- -- lifetime `'b` defined here            ^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b`
@@ -22,7 +22,7 @@ LL | fn take_indirect2<'a,'b>(p: Indirect2<'a>) -> Indirect2<'b> { p }
    = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
 
 error: lifetime may not live long enough
-  --> $DIR/regions-infer-not-param.rs:19:63
+  --> $DIR/regions-infer-not-param.rs:25:63
    |
 LL | fn take_indirect2<'a,'b>(p: Indirect2<'a>) -> Indirect2<'b> { p }
    |                   -- -- lifetime `'b` defined here            ^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
diff --git a/src/test/ui/regions/regions-infer-not-param.rs b/src/test/ui/regions/regions-infer-not-param.rs
index 7643be64d5b..0b8af5bc152 100644
--- a/src/test/ui/regions/regions-infer-not-param.rs
+++ b/src/test/ui/regions/regions-infer-not-param.rs
@@ -1,3 +1,7 @@
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 struct Direct<'a> {
     f: &'a isize
 }
@@ -12,15 +16,20 @@ struct Indirect2<'a> {
     g: Box<dyn FnOnce(Direct<'a>) + 'static>
 }
 
-fn take_direct<'a,'b>(p: Direct<'a>) -> Direct<'b> { p } //~ ERROR mismatched types
+fn take_direct<'a,'b>(p: Direct<'a>) -> Direct<'b> { p }
+//[base]~^ ERROR mismatched types
+//[nll]~^^ ERROR lifetime may not live long enough
 
 fn take_indirect1(p: Indirect1) -> Indirect1 { p }
 
-fn take_indirect2<'a,'b>(p: Indirect2<'a>) -> Indirect2<'b> { p } //~ ERROR mismatched types
-//~| expected struct `Indirect2<'b>`
-//~| found struct `Indirect2<'a>`
-//~| ERROR mismatched types
-//~| expected struct `Indirect2<'b>`
-//~| found struct `Indirect2<'a>`
+fn take_indirect2<'a,'b>(p: Indirect2<'a>) -> Indirect2<'b> { p }
+//[base]~^ ERROR mismatched types
+//[base]~| expected struct `Indirect2<'b>`
+//[base]~| found struct `Indirect2<'a>`
+//[base]~| ERROR mismatched types
+//[base]~| expected struct `Indirect2<'b>`
+//[base]~| found struct `Indirect2<'a>`
+//[nll]~^^^^^^^ ERROR lifetime may not live long enough
+//[nll]~| ERROR lifetime may not live long enough
 
 fn main() {}
diff --git a/src/test/ui/regions/regions-infer-paramd-indirect.stderr b/src/test/ui/regions/regions-infer-paramd-indirect.base.stderr
index d2b369fb07b..1d4471f910d 100644
--- a/src/test/ui/regions/regions-infer-paramd-indirect.stderr
+++ b/src/test/ui/regions/regions-infer-paramd-indirect.base.stderr
@@ -1,5 +1,5 @@
 error[E0308]: mismatched types
-  --> $DIR/regions-infer-paramd-indirect.rs:22:18
+  --> $DIR/regions-infer-paramd-indirect.rs:26:18
    |
 LL |         self.f = b;
    |                  ^ lifetime mismatch
@@ -7,12 +7,12 @@ LL |         self.f = b;
    = note: expected struct `Box<Box<&'a isize>>`
               found struct `Box<Box<&isize>>`
 note: the anonymous lifetime defined here...
-  --> $DIR/regions-infer-paramd-indirect.rs:21:36
+  --> $DIR/regions-infer-paramd-indirect.rs:25:36
    |
 LL |     fn set_f_bad(&mut self, b: Box<B>) {
    |                                    ^
 note: ...does not necessarily outlive the lifetime `'a` as defined here
-  --> $DIR/regions-infer-paramd-indirect.rs:16:6
+  --> $DIR/regions-infer-paramd-indirect.rs:20:6
    |
 LL | impl<'a> SetF<'a> for C<'a> {
    |      ^^
diff --git a/src/test/ui/regions/regions-infer-paramd-indirect.nll.stderr b/src/test/ui/regions/regions-infer-paramd-indirect.nll.stderr
index afabdc1de1c..96377cbdab4 100644
--- a/src/test/ui/regions/regions-infer-paramd-indirect.nll.stderr
+++ b/src/test/ui/regions/regions-infer-paramd-indirect.nll.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/regions-infer-paramd-indirect.rs:22:9
+  --> $DIR/regions-infer-paramd-indirect.rs:26:9
    |
 LL | impl<'a> SetF<'a> for C<'a> {
    |      -- lifetime `'a` defined here
diff --git a/src/test/ui/regions/regions-infer-paramd-indirect.rs b/src/test/ui/regions/regions-infer-paramd-indirect.rs
index 3b18bbf1df3..060306f611e 100644
--- a/src/test/ui/regions/regions-infer-paramd-indirect.rs
+++ b/src/test/ui/regions/regions-infer-paramd-indirect.rs
@@ -1,6 +1,10 @@
 // Check that we correctly infer that b and c must be region
 // parameterized because they reference a which requires a region.
 
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 type A<'a> = &'a isize;
 type B<'a> = Box<A<'a>>;
 
@@ -20,10 +24,11 @@ impl<'a> SetF<'a> for C<'a> {
 
     fn set_f_bad(&mut self, b: Box<B>) {
         self.f = b;
-        //~^ ERROR mismatched types
-        //~| expected struct `Box<Box<&'a isize>>`
-        //~| found struct `Box<Box<&isize>>`
-        //~| lifetime mismatch
+        //[base]~^ ERROR mismatched types
+        //[base]~| expected struct `Box<Box<&'a isize>>`
+        //[base]~| found struct `Box<Box<&isize>>`
+        //[base]~| lifetime mismatch
+        //[nll]~^^^^^ ERROR lifetime may not live long enough
     }
 }
 
diff --git a/src/test/ui/regions/regions-lifetime-bounds-on-fns.stderr b/src/test/ui/regions/regions-lifetime-bounds-on-fns.base.stderr
index 2b2dd0dbbf2..e57b06aac39 100644
--- a/src/test/ui/regions/regions-lifetime-bounds-on-fns.stderr
+++ b/src/test/ui/regions/regions-lifetime-bounds-on-fns.base.stderr
@@ -1,5 +1,5 @@
 error[E0623]: lifetime mismatch
-  --> $DIR/regions-lifetime-bounds-on-fns.rs:8:10
+  --> $DIR/regions-lifetime-bounds-on-fns.rs:12:10
    |
 LL | fn b<'a, 'b>(x: &mut &'a isize, y: &mut &'b isize) {
    |                      ---------          --------- these two types are declared with different lifetimes...
@@ -8,7 +8,7 @@ LL |     *x = *y;
    |          ^^ ...but data from `y` flows into `x` here
 
 error[E0623]: lifetime mismatch
-  --> $DIR/regions-lifetime-bounds-on-fns.rs:14:7
+  --> $DIR/regions-lifetime-bounds-on-fns.rs:20:7
    |
 LL | fn c<'a,'b>(x: &mut &'a isize, y: &mut &'b isize) {
    |                     ---------          --------- these two types are declared with different lifetimes...
@@ -17,7 +17,7 @@ LL |     a(x, y);
    |       ^ ...but data from `y` flows into `x` here
 
 error[E0308]: mismatched types
-  --> $DIR/regions-lifetime-bounds-on-fns.rs:20:43
+  --> $DIR/regions-lifetime-bounds-on-fns.rs:28:43
    |
 LL |     let _: fn(&mut &isize, &mut &isize) = a;
    |                                           ^ one type is more general than the other
diff --git a/src/test/ui/regions/regions-lifetime-bounds-on-fns.nll.stderr b/src/test/ui/regions/regions-lifetime-bounds-on-fns.nll.stderr
index ee3dcef1cb5..7fe8b4bf57f 100644
--- a/src/test/ui/regions/regions-lifetime-bounds-on-fns.nll.stderr
+++ b/src/test/ui/regions/regions-lifetime-bounds-on-fns.nll.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/regions-lifetime-bounds-on-fns.rs:8:5
+  --> $DIR/regions-lifetime-bounds-on-fns.rs:12:5
    |
 LL | fn b<'a, 'b>(x: &mut &'a isize, y: &mut &'b isize) {
    |      --  -- lifetime `'b` defined here
@@ -12,7 +12,7 @@ LL |     *x = *y;
    = help: consider adding the following bound: `'b: 'a`
 
 error: lifetime may not live long enough
-  --> $DIR/regions-lifetime-bounds-on-fns.rs:14:5
+  --> $DIR/regions-lifetime-bounds-on-fns.rs:20:5
    |
 LL | fn c<'a,'b>(x: &mut &'a isize, y: &mut &'b isize) {
    |      -- -- lifetime `'b` defined here
@@ -28,7 +28,7 @@ LL |     a(x, y);
    = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
 
 error[E0308]: mismatched types
-  --> $DIR/regions-lifetime-bounds-on-fns.rs:20:12
+  --> $DIR/regions-lifetime-bounds-on-fns.rs:28:12
    |
 LL |     let _: fn(&mut &isize, &mut &isize) = a;
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
@@ -37,7 +37,7 @@ LL |     let _: fn(&mut &isize, &mut &isize) = a;
               found fn pointer `for<'r, 's> fn(&'r mut &isize, &'s mut &isize)`
 
 error[E0308]: mismatched types
-  --> $DIR/regions-lifetime-bounds-on-fns.rs:20:12
+  --> $DIR/regions-lifetime-bounds-on-fns.rs:28:12
    |
 LL |     let _: fn(&mut &isize, &mut &isize) = a;
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
diff --git a/src/test/ui/regions/regions-lifetime-bounds-on-fns.rs b/src/test/ui/regions/regions-lifetime-bounds-on-fns.rs
index 7d7f62e1979..97c08d8ab0e 100644
--- a/src/test/ui/regions/regions-lifetime-bounds-on-fns.rs
+++ b/src/test/ui/regions/regions-lifetime-bounds-on-fns.rs
@@ -1,3 +1,7 @@
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 fn a<'a, 'b:'a>(x: &mut &'a isize, y: &mut &'b isize) {
     // Note: this is legal because of the `'b:'a` declaration.
     *x = *y;
@@ -5,19 +9,25 @@ fn a<'a, 'b:'a>(x: &mut &'a isize, y: &mut &'b isize) {
 
 fn b<'a, 'b>(x: &mut &'a isize, y: &mut &'b isize) {
     // Illegal now because there is no `'b:'a` declaration.
-    *x = *y; //~ ERROR E0623
+    *x = *y;
+    //[base]~^ ERROR lifetime mismatch [E0623]
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn c<'a,'b>(x: &mut &'a isize, y: &mut &'b isize) {
     // Here we try to call `foo` but do not know that `'a` and `'b` are
     // related as required.
-    a(x, y); //~ ERROR lifetime mismatch [E0623]
+    a(x, y);
+    //[base]~^ ERROR lifetime mismatch [E0623]
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn d() {
     // 'a and 'b are early bound in the function `a` because they appear
     // inconstraints:
-    let _: fn(&mut &isize, &mut &isize) = a; //~ ERROR E0308
+    let _: fn(&mut &isize, &mut &isize) = a;
+    //~^ ERROR mismatched types [E0308]
+    //[nll]~^^ ERROR mismatched types [E0308]
 }
 
 fn e() {
diff --git a/src/test/ui/regions/regions-nested-fns.stderr b/src/test/ui/regions/regions-nested-fns.base.stderr
index 11affcaaa79..37ce569e761 100644
--- a/src/test/ui/regions/regions-nested-fns.stderr
+++ b/src/test/ui/regions/regions-nested-fns.base.stderr
@@ -1,40 +1,46 @@
 error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
-  --> $DIR/regions-nested-fns.rs:5:18
+  --> $DIR/regions-nested-fns.rs:9:18
    |
 LL |     let mut ay = &y;
    |                  ^^
    |
 note: first, the lifetime cannot outlive the anonymous lifetime #1 defined here...
-  --> $DIR/regions-nested-fns.rs:7:58
+  --> $DIR/regions-nested-fns.rs:13:58
    |
 LL |       ignore::<Box<dyn for<'z> FnMut(&'z isize)>>(Box::new(|z| {
    |  __________________________________________________________^
 LL | |         ay = x;
 LL | |         ay = &y;
+LL | |
 LL | |         ay = z;
+LL | |
 LL | |     }));
    | |_____^
 note: ...so that reference does not outlive borrowed content
-  --> $DIR/regions-nested-fns.rs:10:14
+  --> $DIR/regions-nested-fns.rs:17:14
    |
 LL |         ay = z;
    |              ^
 note: but, the lifetime must be valid for the anonymous lifetime #1 defined here...
-  --> $DIR/regions-nested-fns.rs:13:72
+  --> $DIR/regions-nested-fns.rs:21:72
    |
 LL |       ignore::< Box<dyn for<'z> FnMut(&'z isize) -> &'z isize>>(Box::new(|z| {
    |  ________________________________________________________________________^
 LL | |         if false { return x; }
+LL | |
+LL | |
 LL | |         if false { return ay; }
 LL | |         return z;
 LL | |     }));
    | |_____^
 note: ...so that the types are compatible
-  --> $DIR/regions-nested-fns.rs:13:76
+  --> $DIR/regions-nested-fns.rs:21:76
    |
 LL |       ignore::< Box<dyn for<'z> FnMut(&'z isize) -> &'z isize>>(Box::new(|z| {
    |  ____________________________________________________________________________^
 LL | |         if false { return x; }
+LL | |
+LL | |
 LL | |         if false { return ay; }
 LL | |         return z;
 LL | |     }));
@@ -43,23 +49,25 @@ LL | |     }));
               found `&isize`
 
 error[E0312]: lifetime of reference outlives lifetime of borrowed content...
-  --> $DIR/regions-nested-fns.rs:14:27
+  --> $DIR/regions-nested-fns.rs:22:27
    |
 LL |         if false { return x; }
    |                           ^
    |
 note: ...the reference is valid for the anonymous lifetime #1 defined here...
-  --> $DIR/regions-nested-fns.rs:13:72
+  --> $DIR/regions-nested-fns.rs:21:72
    |
 LL |       ignore::< Box<dyn for<'z> FnMut(&'z isize) -> &'z isize>>(Box::new(|z| {
    |  ________________________________________________________________________^
 LL | |         if false { return x; }
+LL | |
+LL | |
 LL | |         if false { return ay; }
 LL | |         return z;
 LL | |     }));
    | |_____^
 note: ...but the borrowed content is only valid for the lifetime `'x` as defined here
-  --> $DIR/regions-nested-fns.rs:3:11
+  --> $DIR/regions-nested-fns.rs:7:11
    |
 LL | fn nested<'x>(x: &'x isize) {
    |           ^^
diff --git a/src/test/ui/regions/regions-nested-fns.nll.stderr b/src/test/ui/regions/regions-nested-fns.nll.stderr
index a0cfa362472..6f2a89994b0 100644
--- a/src/test/ui/regions/regions-nested-fns.nll.stderr
+++ b/src/test/ui/regions/regions-nested-fns.nll.stderr
@@ -1,9 +1,9 @@
 error[E0521]: borrowed data escapes outside of closure
-  --> $DIR/regions-nested-fns.rs:10:9
+  --> $DIR/regions-nested-fns.rs:17:9
    |
 LL |     let mut ay = &y;
    |         ------ `ay` declared here, outside of the closure body
-LL | 
+...
 LL |     ignore::<Box<dyn for<'z> FnMut(&'z isize)>>(Box::new(|z| {
    |                                                           - `z` is a reference that is only valid in the closure body
 ...
@@ -11,7 +11,7 @@ LL |         ay = z;
    |         ^^^^^^ `z` escapes the closure body here
 
 error[E0597]: `y` does not live long enough
-  --> $DIR/regions-nested-fns.rs:5:18
+  --> $DIR/regions-nested-fns.rs:9:18
    |
 LL |     let mut ay = &y;
    |                  ^^ borrowed value does not live long enough
@@ -23,7 +23,7 @@ LL | }
    | - `y` dropped here while still borrowed
 
 error[E0597]: `y` does not live long enough
-  --> $DIR/regions-nested-fns.rs:9:15
+  --> $DIR/regions-nested-fns.rs:15:15
    |
 LL |     ignore::<Box<dyn for<'z> FnMut(&'z isize)>>(Box::new(|z| {
    |                                                          --- value captured here
@@ -38,7 +38,7 @@ LL | }
    | - `y` dropped here while still borrowed
 
 error: lifetime may not live long enough
-  --> $DIR/regions-nested-fns.rs:14:27
+  --> $DIR/regions-nested-fns.rs:22:27
    |
 LL | fn nested<'x>(x: &'x isize) {
    |           -- lifetime `'x` defined here
diff --git a/src/test/ui/regions/regions-nested-fns.rs b/src/test/ui/regions/regions-nested-fns.rs
index c02d4e0ce45..8cc39792bd9 100644
--- a/src/test/ui/regions/regions-nested-fns.rs
+++ b/src/test/ui/regions/regions-nested-fns.rs
@@ -1,17 +1,27 @@
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 fn ignore<T>(t: T) {}
 
 fn nested<'x>(x: &'x isize) {
     let y = 3;
-    let mut ay = &y; //~ ERROR E0495
+    let mut ay = &y;
+    //[base]~^ ERROR E0495
+    //[nll]~^^ ERROR `y` does not live long enough [E0597]
 
     ignore::<Box<dyn for<'z> FnMut(&'z isize)>>(Box::new(|z| {
         ay = x;
         ay = &y;
+        //[nll]~^ ERROR `y` does not live long enough
         ay = z;
+        //[nll]~^ ERROR borrowed data escapes outside of closure [E0521]
     }));
 
     ignore::< Box<dyn for<'z> FnMut(&'z isize) -> &'z isize>>(Box::new(|z| {
-        if false { return x; } //~ ERROR E0312
+        if false { return x; }
+        //[base]~^ ERROR E0312
+        //[nll]~^^ ERROR lifetime may not live long enough
         if false { return ay; }
         return z;
     }));
diff --git a/src/test/ui/regions/regions-outlives-projection-container.stderr b/src/test/ui/regions/regions-outlives-projection-container.base.stderr
index 8c2b2c1e24a..9a66f67ea6e 100644
--- a/src/test/ui/regions/regions-outlives-projection-container.stderr
+++ b/src/test/ui/regions/regions-outlives-projection-container.base.stderr
@@ -1,67 +1,67 @@
 error[E0491]: in type `&'a WithAssoc<TheType<'b>>`, reference has a longer lifetime than the data it references
-  --> $DIR/regions-outlives-projection-container.rs:36:13
+  --> $DIR/regions-outlives-projection-container.rs:40:13
    |
 LL |     let _x: &'a WithAssoc<TheType<'b>> = loop { };
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: the pointer is valid for the lifetime `'a` as defined here
-  --> $DIR/regions-outlives-projection-container.rs:28:15
+  --> $DIR/regions-outlives-projection-container.rs:32:15
    |
 LL | fn with_assoc<'a,'b>() {
    |               ^^
 note: but the referenced data is only valid for the lifetime `'b` as defined here
-  --> $DIR/regions-outlives-projection-container.rs:28:18
+  --> $DIR/regions-outlives-projection-container.rs:32:18
    |
 LL | fn with_assoc<'a,'b>() {
    |                  ^^
 
 error[E0491]: in type `&'a WithoutAssoc<TheType<'b>>`, reference has a longer lifetime than the data it references
-  --> $DIR/regions-outlives-projection-container.rs:54:13
+  --> $DIR/regions-outlives-projection-container.rs:59:13
    |
 LL |     let _x: &'a WithoutAssoc<TheType<'b>> = loop { };
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: the pointer is valid for the lifetime `'a` as defined here
-  --> $DIR/regions-outlives-projection-container.rs:50:18
+  --> $DIR/regions-outlives-projection-container.rs:55:18
    |
 LL | fn without_assoc<'a,'b>() {
    |                  ^^
 note: but the referenced data is only valid for the lifetime `'b` as defined here
-  --> $DIR/regions-outlives-projection-container.rs:50:21
+  --> $DIR/regions-outlives-projection-container.rs:55:21
    |
 LL | fn without_assoc<'a,'b>() {
    |                     ^^
 
 error[E0491]: in type `&'a WithAssoc<TheType<'b>>`, reference has a longer lifetime than the data it references
-  --> $DIR/regions-outlives-projection-container.rs:63:12
+  --> $DIR/regions-outlives-projection-container.rs:69:12
    |
 LL |     call::<&'a WithAssoc<TheType<'b>>>();
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: the pointer is valid for the lifetime `'a` as defined here
-  --> $DIR/regions-outlives-projection-container.rs:58:20
+  --> $DIR/regions-outlives-projection-container.rs:64:20
    |
 LL | fn call_with_assoc<'a,'b>() {
    |                    ^^
 note: but the referenced data is only valid for the lifetime `'b` as defined here
-  --> $DIR/regions-outlives-projection-container.rs:58:23
+  --> $DIR/regions-outlives-projection-container.rs:64:23
    |
 LL | fn call_with_assoc<'a,'b>() {
    |                       ^^
 
 error[E0491]: in type `&'a WithoutAssoc<TheType<'b>>`, reference has a longer lifetime than the data it references
-  --> $DIR/regions-outlives-projection-container.rs:70:12
+  --> $DIR/regions-outlives-projection-container.rs:77:12
    |
 LL |     call::<&'a WithoutAssoc<TheType<'b>>>();
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: the pointer is valid for the lifetime `'a` as defined here
-  --> $DIR/regions-outlives-projection-container.rs:67:23
+  --> $DIR/regions-outlives-projection-container.rs:74:23
    |
 LL | fn call_without_assoc<'a,'b>() {
    |                       ^^
 note: but the referenced data is only valid for the lifetime `'b` as defined here
-  --> $DIR/regions-outlives-projection-container.rs:67:26
+  --> $DIR/regions-outlives-projection-container.rs:74:26
    |
 LL | fn call_without_assoc<'a,'b>() {
    |                          ^^
diff --git a/src/test/ui/regions/regions-outlives-projection-container.nll.stderr b/src/test/ui/regions/regions-outlives-projection-container.nll.stderr
index 073a3190022..d93eef9ce0b 100644
--- a/src/test/ui/regions/regions-outlives-projection-container.nll.stderr
+++ b/src/test/ui/regions/regions-outlives-projection-container.nll.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/regions-outlives-projection-container.rs:36:13
+  --> $DIR/regions-outlives-projection-container.rs:40:13
    |
 LL | fn with_assoc<'a,'b>() {
    |               -- -- lifetime `'b` defined here
@@ -12,7 +12,7 @@ LL |     let _x: &'a WithAssoc<TheType<'b>> = loop { };
    = help: consider adding the following bound: `'b: 'a`
 
 error: lifetime may not live long enough
-  --> $DIR/regions-outlives-projection-container.rs:54:13
+  --> $DIR/regions-outlives-projection-container.rs:59:13
    |
 LL | fn without_assoc<'a,'b>() {
    |                  -- -- lifetime `'b` defined here
@@ -25,7 +25,7 @@ LL |     let _x: &'a WithoutAssoc<TheType<'b>> = loop { };
    = help: consider adding the following bound: `'b: 'a`
 
 error: lifetime may not live long enough
-  --> $DIR/regions-outlives-projection-container.rs:63:5
+  --> $DIR/regions-outlives-projection-container.rs:69:5
    |
 LL | fn call_with_assoc<'a,'b>() {
    |                    -- -- lifetime `'b` defined here
@@ -38,7 +38,7 @@ LL |     call::<&'a WithAssoc<TheType<'b>>>();
    = help: consider adding the following bound: `'b: 'a`
 
 error: lifetime may not live long enough
-  --> $DIR/regions-outlives-projection-container.rs:70:5
+  --> $DIR/regions-outlives-projection-container.rs:77:5
    |
 LL | fn call_without_assoc<'a,'b>() {
    |                       -- -- lifetime `'b` defined here
diff --git a/src/test/ui/regions/regions-outlives-projection-container.rs b/src/test/ui/regions/regions-outlives-projection-container.rs
index 3afc600becb..ccfd2213b6f 100644
--- a/src/test/ui/regions/regions-outlives-projection-container.rs
+++ b/src/test/ui/regions/regions-outlives-projection-container.rs
@@ -2,6 +2,10 @@
 // type of a bound that appears in the where clause on a struct must
 // outlive the location in which the type appears. Issue #22246.
 
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 #![allow(dead_code)]
 #![feature(rustc_attrs)]
 
@@ -34,7 +38,8 @@ fn with_assoc<'a,'b>() {
     // FIXME (#54943) NLL doesn't enforce WF condition in unreachable code if
     // `_x` is changed to `_`
     let _x: &'a WithAssoc<TheType<'b>> = loop { };
-    //~^ ERROR reference has a longer lifetime
+    //[base]~^ ERROR reference has a longer lifetime
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn with_assoc1<'a,'b>() where 'b : 'a {
@@ -52,7 +57,8 @@ fn without_assoc<'a,'b>() {
     // that `'b:'a` holds because the `'b` appears in `TheType<'b>`.
 
     let _x: &'a WithoutAssoc<TheType<'b>> = loop { };
-    //~^ ERROR reference has a longer lifetime
+    //[base]~^ ERROR reference has a longer lifetime
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn call_with_assoc<'a,'b>() {
@@ -61,13 +67,16 @@ fn call_with_assoc<'a,'b>() {
     // no data.
 
     call::<&'a WithAssoc<TheType<'b>>>();
-    //~^ ERROR reference has a longer lifetime
+    //[base]~^ ERROR reference has a longer lifetime
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn call_without_assoc<'a,'b>() {
     // As `without_assoc`, but in a distinct scenario.
 
-    call::<&'a WithoutAssoc<TheType<'b>>>(); //~ ERROR reference has a longer lifetime
+    call::<&'a WithoutAssoc<TheType<'b>>>();
+    //[base]~^ ERROR reference has a longer lifetime
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn call<T>() { }
diff --git a/src/test/ui/regions/regions-proc-bound-capture.stderr b/src/test/ui/regions/regions-proc-bound-capture.base.stderr
index 2ebe874da93..427c6f4ec8c 100644
--- a/src/test/ui/regions/regions-proc-bound-capture.stderr
+++ b/src/test/ui/regions/regions-proc-bound-capture.base.stderr
@@ -1,5 +1,5 @@
 error[E0759]: `x` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
-  --> $DIR/regions-proc-bound-capture.rs:9:14
+  --> $DIR/regions-proc-bound-capture.rs:13:14
    |
 LL | fn static_proc(x: &isize) -> Box<dyn FnMut() -> (isize) + 'static> {
    |                   ------ this data with an anonymous lifetime `'_`...
@@ -8,7 +8,7 @@ LL |     Box::new(move || { *x })
    |              ^^^^^^^^^^^^^^ ...is used and required to live as long as `'static` here
    |
 note: `'static` lifetime requirement introduced by the return type
-  --> $DIR/regions-proc-bound-capture.rs:7:59
+  --> $DIR/regions-proc-bound-capture.rs:11:59
    |
 LL | fn static_proc(x: &isize) -> Box<dyn FnMut() -> (isize) + 'static> {
    |                                                           ^^^^^^^ `'static` requirement introduced here
diff --git a/src/test/ui/regions/regions-proc-bound-capture.nll.stderr b/src/test/ui/regions/regions-proc-bound-capture.nll.stderr
index 75890b85815..ce4d2d4d111 100644
--- a/src/test/ui/regions/regions-proc-bound-capture.nll.stderr
+++ b/src/test/ui/regions/regions-proc-bound-capture.nll.stderr
@@ -1,11 +1,20 @@
 error: lifetime may not live long enough
-  --> $DIR/regions-proc-bound-capture.rs:9:5
+  --> $DIR/regions-proc-bound-capture.rs:13:5
    |
 LL | fn static_proc(x: &isize) -> Box<dyn FnMut() -> (isize) + 'static> {
    |                   - let's call the lifetime of this reference `'1`
 LL |     // This is illegal, because the region bound on `proc` is 'static.
 LL |     Box::new(move || { *x })
    |     ^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static`
+   |
+help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `x`
+   |
+LL | fn static_proc(x: &isize) -> Box<dyn FnMut() -> (isize) + '_> {
+   |                                                           ~~
+help: alternatively, add an explicit `'static` bound to this reference
+   |
+LL | fn static_proc(x: &'static isize) -> Box<dyn FnMut() -> (isize) + 'static> {
+   |                   ~~~~~~~~~~~~~~
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/regions/regions-proc-bound-capture.rs b/src/test/ui/regions/regions-proc-bound-capture.rs
index 55d964ac534..1033163c8dd 100644
--- a/src/test/ui/regions/regions-proc-bound-capture.rs
+++ b/src/test/ui/regions/regions-proc-bound-capture.rs
@@ -1,3 +1,7 @@
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 fn borrowed_proc<'a>(x: &'a isize) -> Box<dyn FnMut()->(isize) + 'a> {
     // This is legal, because the region bound on `proc`
     // states that it captures `x`.
@@ -6,7 +10,9 @@ fn borrowed_proc<'a>(x: &'a isize) -> Box<dyn FnMut()->(isize) + 'a> {
 
 fn static_proc(x: &isize) -> Box<dyn FnMut() -> (isize) + 'static> {
     // This is illegal, because the region bound on `proc` is 'static.
-    Box::new(move || { *x }) //~ ERROR E0759
+    Box::new(move || { *x })
+    //[base]~^ ERROR E0759
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn main() { }
diff --git a/src/test/ui/regions/regions-reborrow-from-shorter-mut-ref-mut-ref.stderr b/src/test/ui/regions/regions-reborrow-from-shorter-mut-ref-mut-ref.base.stderr
index aca3a1ed057..7ecdb0cd15e 100644
--- a/src/test/ui/regions/regions-reborrow-from-shorter-mut-ref-mut-ref.stderr
+++ b/src/test/ui/regions/regions-reborrow-from-shorter-mut-ref-mut-ref.base.stderr
@@ -1,5 +1,5 @@
 error[E0623]: lifetime mismatch
-  --> $DIR/regions-reborrow-from-shorter-mut-ref-mut-ref.rs:4:5
+  --> $DIR/regions-reborrow-from-shorter-mut-ref-mut-ref.rs:8:5
    |
 LL | fn copy_borrowed_ptr<'a, 'b, 'c>(p: &'a mut &'b mut &'c mut isize) -> &'b mut isize {
    |                                     -----------------------------     -------------
diff --git a/src/test/ui/regions/regions-reborrow-from-shorter-mut-ref-mut-ref.nll.stderr b/src/test/ui/regions/regions-reborrow-from-shorter-mut-ref-mut-ref.nll.stderr
index dc905d076bb..519ada7bdfc 100644
--- a/src/test/ui/regions/regions-reborrow-from-shorter-mut-ref-mut-ref.nll.stderr
+++ b/src/test/ui/regions/regions-reborrow-from-shorter-mut-ref-mut-ref.nll.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/regions-reborrow-from-shorter-mut-ref-mut-ref.rs:4:5
+  --> $DIR/regions-reborrow-from-shorter-mut-ref-mut-ref.rs:8:5
    |
 LL | fn copy_borrowed_ptr<'a, 'b, 'c>(p: &'a mut &'b mut &'c mut isize) -> &'b mut isize {
    |                      --  -- lifetime `'b` defined here
diff --git a/src/test/ui/regions/regions-reborrow-from-shorter-mut-ref-mut-ref.rs b/src/test/ui/regions/regions-reborrow-from-shorter-mut-ref-mut-ref.rs
index 35aca8be25b..c4ad05010fb 100644
--- a/src/test/ui/regions/regions-reborrow-from-shorter-mut-ref-mut-ref.rs
+++ b/src/test/ui/regions/regions-reborrow-from-shorter-mut-ref-mut-ref.rs
@@ -1,7 +1,13 @@
 // Issue #8624. Test for reborrowing with 3 levels, not just two.
 
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 fn copy_borrowed_ptr<'a, 'b, 'c>(p: &'a mut &'b mut &'c mut isize) -> &'b mut isize {
-    &mut ***p //~ ERROR lifetime mismatch [E0623]
+    &mut ***p
+    //[base]~^ ERROR lifetime mismatch [E0623]
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn main() {
diff --git a/src/test/ui/regions/regions-reborrow-from-shorter-mut-ref.stderr b/src/test/ui/regions/regions-reborrow-from-shorter-mut-ref.base.stderr
index a9916dbe4f5..3cb7de15850 100644
--- a/src/test/ui/regions/regions-reborrow-from-shorter-mut-ref.stderr
+++ b/src/test/ui/regions/regions-reborrow-from-shorter-mut-ref.base.stderr
@@ -1,5 +1,5 @@
 error[E0623]: lifetime mismatch
-  --> $DIR/regions-reborrow-from-shorter-mut-ref.rs:6:5
+  --> $DIR/regions-reborrow-from-shorter-mut-ref.rs:10:5
    |
 LL | fn copy_borrowed_ptr<'a, 'b>(p: &'a mut &'b mut isize) -> &'b mut isize {
    |                                 ---------------------     -------------
diff --git a/src/test/ui/regions/regions-reborrow-from-shorter-mut-ref.nll.stderr b/src/test/ui/regions/regions-reborrow-from-shorter-mut-ref.nll.stderr
index c98ec477417..4dd2a83739c 100644
--- a/src/test/ui/regions/regions-reborrow-from-shorter-mut-ref.nll.stderr
+++ b/src/test/ui/regions/regions-reborrow-from-shorter-mut-ref.nll.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/regions-reborrow-from-shorter-mut-ref.rs:6:5
+  --> $DIR/regions-reborrow-from-shorter-mut-ref.rs:10:5
    |
 LL | fn copy_borrowed_ptr<'a, 'b>(p: &'a mut &'b mut isize) -> &'b mut isize {
    |                      --  -- lifetime `'b` defined here
diff --git a/src/test/ui/regions/regions-reborrow-from-shorter-mut-ref.rs b/src/test/ui/regions/regions-reborrow-from-shorter-mut-ref.rs
index 77041ab4f05..c41e76e4d2a 100644
--- a/src/test/ui/regions/regions-reborrow-from-shorter-mut-ref.rs
+++ b/src/test/ui/regions/regions-reborrow-from-shorter-mut-ref.rs
@@ -2,8 +2,14 @@
 // pointer which is backed by another `&'a mut` can only be done
 // for `'a` (which must be a sublifetime of `'b`).
 
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 fn copy_borrowed_ptr<'a, 'b>(p: &'a mut &'b mut isize) -> &'b mut isize {
-    &mut **p //~ ERROR lifetime mismatch [E0623]
+    &mut **p
+    //[base]~^ ERROR lifetime mismatch [E0623]
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn main() {
diff --git a/src/test/ui/regions/regions-ret-borrowed-1.stderr b/src/test/ui/regions/regions-ret-borrowed-1.base.stderr
index 86df7bfeb70..d102f93a647 100644
--- a/src/test/ui/regions/regions-ret-borrowed-1.stderr
+++ b/src/test/ui/regions/regions-ret-borrowed-1.base.stderr
@@ -1,28 +1,28 @@
 error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
-  --> $DIR/regions-ret-borrowed-1.rs:10:14
+  --> $DIR/regions-ret-borrowed-1.rs:14:14
    |
 LL |     with(|o| o)
    |              ^
    |
 note: first, the lifetime cannot outlive the anonymous lifetime #1 defined here...
-  --> $DIR/regions-ret-borrowed-1.rs:10:10
+  --> $DIR/regions-ret-borrowed-1.rs:14:10
    |
 LL |     with(|o| o)
    |          ^^^^^
 note: ...so that the types are compatible
-  --> $DIR/regions-ret-borrowed-1.rs:10:14
+  --> $DIR/regions-ret-borrowed-1.rs:14:14
    |
 LL |     with(|o| o)
    |              ^
    = note: expected `&isize`
               found `&isize`
 note: but, the lifetime must be valid for the lifetime `'a` as defined here...
-  --> $DIR/regions-ret-borrowed-1.rs:9:14
+  --> $DIR/regions-ret-borrowed-1.rs:13:14
    |
 LL | fn return_it<'a>() -> &'a isize {
    |              ^^
 note: ...so that reference does not outlive borrowed content
-  --> $DIR/regions-ret-borrowed-1.rs:10:5
+  --> $DIR/regions-ret-borrowed-1.rs:14:5
    |
 LL |     with(|o| o)
    |     ^^^^^^^^^^^
diff --git a/src/test/ui/regions/regions-ret-borrowed-1.nll.stderr b/src/test/ui/regions/regions-ret-borrowed-1.nll.stderr
index 0784e894ea9..4fdadccab15 100644
--- a/src/test/ui/regions/regions-ret-borrowed-1.nll.stderr
+++ b/src/test/ui/regions/regions-ret-borrowed-1.nll.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/regions-ret-borrowed-1.rs:10:14
+  --> $DIR/regions-ret-borrowed-1.rs:14:14
    |
 LL |     with(|o| o)
    |           -- ^ returning this value requires that `'1` must outlive `'2`
diff --git a/src/test/ui/regions/regions-ret-borrowed-1.rs b/src/test/ui/regions/regions-ret-borrowed-1.rs
index 1be5edee599..fed631320b4 100644
--- a/src/test/ui/regions/regions-ret-borrowed-1.rs
+++ b/src/test/ui/regions/regions-ret-borrowed-1.rs
@@ -2,13 +2,18 @@
 // some point regions-ret-borrowed reported an error but this file did
 // not, due to special hardcoding around the anonymous region.
 
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 fn with<R, F>(f: F) -> R where F: for<'a> FnOnce(&'a isize) -> R {
     f(&3)
 }
 
 fn return_it<'a>() -> &'a isize {
     with(|o| o)
-        //~^ ERROR cannot infer
+    //[base]~^ ERROR cannot infer an appropriate lifetime due to conflicting requirements [E0495]
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn main() {
diff --git a/src/test/ui/regions/regions-ret-borrowed.stderr b/src/test/ui/regions/regions-ret-borrowed.base.stderr
index b9a06d97433..62b42b5dd11 100644
--- a/src/test/ui/regions/regions-ret-borrowed.stderr
+++ b/src/test/ui/regions/regions-ret-borrowed.base.stderr
@@ -1,28 +1,28 @@
 error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
-  --> $DIR/regions-ret-borrowed.rs:13:14
+  --> $DIR/regions-ret-borrowed.rs:17:14
    |
 LL |     with(|o| o)
    |              ^
    |
 note: first, the lifetime cannot outlive the anonymous lifetime #1 defined here...
-  --> $DIR/regions-ret-borrowed.rs:13:10
+  --> $DIR/regions-ret-borrowed.rs:17:10
    |
 LL |     with(|o| o)
    |          ^^^^^
 note: ...so that the types are compatible
-  --> $DIR/regions-ret-borrowed.rs:13:14
+  --> $DIR/regions-ret-borrowed.rs:17:14
    |
 LL |     with(|o| o)
    |              ^
    = note: expected `&isize`
               found `&isize`
 note: but, the lifetime must be valid for the lifetime `'a` as defined here...
-  --> $DIR/regions-ret-borrowed.rs:12:14
+  --> $DIR/regions-ret-borrowed.rs:16:14
    |
 LL | fn return_it<'a>() -> &'a isize {
    |              ^^
 note: ...so that reference does not outlive borrowed content
-  --> $DIR/regions-ret-borrowed.rs:13:5
+  --> $DIR/regions-ret-borrowed.rs:17:5
    |
 LL |     with(|o| o)
    |     ^^^^^^^^^^^
diff --git a/src/test/ui/regions/regions-ret-borrowed.nll.stderr b/src/test/ui/regions/regions-ret-borrowed.nll.stderr
index d9be5ef89cc..d3ea5bd875f 100644
--- a/src/test/ui/regions/regions-ret-borrowed.nll.stderr
+++ b/src/test/ui/regions/regions-ret-borrowed.nll.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/regions-ret-borrowed.rs:13:14
+  --> $DIR/regions-ret-borrowed.rs:17:14
    |
 LL |     with(|o| o)
    |           -- ^ returning this value requires that `'1` must outlive `'2`
diff --git a/src/test/ui/regions/regions-ret-borrowed.rs b/src/test/ui/regions/regions-ret-borrowed.rs
index 5fca92d68b6..2b6855d1c71 100644
--- a/src/test/ui/regions/regions-ret-borrowed.rs
+++ b/src/test/ui/regions/regions-ret-borrowed.rs
@@ -5,13 +5,18 @@
 // used to successfully compile because we failed to account for the
 // fact that fn(x: &isize) rebound the region &.
 
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 fn with<R, F>(f: F) -> R where F: FnOnce(&isize) -> R {
     f(&3)
 }
 
 fn return_it<'a>() -> &'a isize {
     with(|o| o)
-        //~^ ERROR cannot infer
+    //[base]~^ ERROR cannot infer an appropriate lifetime due to conflicting requirements [E0495]
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn main() {
diff --git a/src/test/ui/regions/regions-static-bound-rpass.rs b/src/test/ui/regions/regions-static-bound-rpass.rs
index c91c6f87493..25232b455b6 100644
--- a/src/test/ui/regions/regions-static-bound-rpass.rs
+++ b/src/test/ui/regions/regions-static-bound-rpass.rs
@@ -1,10 +1,17 @@
 // run-pass
+
 fn invariant_id<'a,'b>(t: &'b mut &'static ()) -> &'b mut &'a ()
     where 'a: 'static { t }
+//~^ WARN unnecessary lifetime parameter `'a`
+
 fn static_id<'a>(t: &'a ()) -> &'static ()
     where 'a: 'static { t }
+//~^ WARN unnecessary lifetime parameter `'a`
+
 fn static_id_indirect<'a,'b>(t: &'a ()) -> &'static ()
     where 'a: 'b, 'b: 'static { t }
+//~^ WARN unnecessary lifetime parameter `'b`
+
 fn ref_id<'a>(t: &'a ()) -> &'a () where 'static: 'a { t }
 
 static UNIT: () = ();
diff --git a/src/test/ui/regions/regions-static-bound-rpass.stderr b/src/test/ui/regions/regions-static-bound-rpass.stderr
new file mode 100644
index 00000000000..9355a409d50
--- /dev/null
+++ b/src/test/ui/regions/regions-static-bound-rpass.stderr
@@ -0,0 +1,26 @@
+warning: unnecessary lifetime parameter `'a`
+  --> $DIR/regions-static-bound-rpass.rs:4:11
+   |
+LL |     where 'a: 'static { t }
+   |           ^^
+   |
+   = help: you can use the `'static` lifetime directly, in place of `'a`
+
+warning: unnecessary lifetime parameter `'a`
+  --> $DIR/regions-static-bound-rpass.rs:8:11
+   |
+LL |     where 'a: 'static { t }
+   |           ^^
+   |
+   = help: you can use the `'static` lifetime directly, in place of `'a`
+
+warning: unnecessary lifetime parameter `'b`
+  --> $DIR/regions-static-bound-rpass.rs:12:19
+   |
+LL |     where 'a: 'b, 'b: 'static { t }
+   |                   ^^
+   |
+   = help: you can use the `'static` lifetime directly, in place of `'b`
+
+warning: 3 warnings emitted
+
diff --git a/src/test/ui/regions/regions-static-bound.stderr b/src/test/ui/regions/regions-static-bound.base.stderr
index b8e69e02609..6b8120444d0 100644
--- a/src/test/ui/regions/regions-static-bound.stderr
+++ b/src/test/ui/regions/regions-static-bound.base.stderr
@@ -1,18 +1,34 @@
+warning: unnecessary lifetime parameter `'a`
+  --> $DIR/regions-static-bound.rs:6:11
+   |
+LL |     where 'a: 'static { t }
+   |           ^^
+   |
+   = help: you can use the `'static` lifetime directly, in place of `'a`
+
+warning: unnecessary lifetime parameter `'b`
+  --> $DIR/regions-static-bound.rs:10:19
+   |
+LL |     where 'a: 'b, 'b: 'static { t }
+   |                   ^^
+   |
+   = help: you can use the `'static` lifetime directly, in place of `'b`
+
 error[E0312]: lifetime of reference outlives lifetime of borrowed content...
-  --> $DIR/regions-static-bound.rs:6:5
+  --> $DIR/regions-static-bound.rs:14:5
    |
 LL |     t
    |     ^
    |
    = note: ...the reference is valid for the static lifetime...
 note: ...but the borrowed content is only valid for the lifetime `'a` as defined here
-  --> $DIR/regions-static-bound.rs:5:24
+  --> $DIR/regions-static-bound.rs:13:24
    |
 LL | fn static_id_wrong_way<'a>(t: &'a ()) -> &'static () where 'static: 'a {
    |                        ^^
 
 error[E0759]: `u` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
-  --> $DIR/regions-static-bound.rs:10:5
+  --> $DIR/regions-static-bound.rs:20:5
    |
 LL | fn error(u: &(), v: &()) {
    |             --- this data with an anonymous lifetime `'_`...
@@ -20,27 +36,27 @@ LL |     static_id(&u);
    |     ^^^^^^^^^ -- ...is used here...
    |
 note: ...and is required to live as long as `'static` here
-  --> $DIR/regions-static-bound.rs:10:5
+  --> $DIR/regions-static-bound.rs:20:5
    |
 LL |     static_id(&u);
    |     ^^^^^^^^^
 
 error[E0759]: `v` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
-  --> $DIR/regions-static-bound.rs:11:5
+  --> $DIR/regions-static-bound.rs:23:5
    |
 LL | fn error(u: &(), v: &()) {
    |                     --- this data with an anonymous lifetime `'_`...
-LL |     static_id(&u);
+...
 LL |     static_id_indirect(&v);
    |     ^^^^^^^^^^^^^^^^^^ -- ...is used here...
    |
 note: ...and is required to live as long as `'static` here
-  --> $DIR/regions-static-bound.rs:11:5
+  --> $DIR/regions-static-bound.rs:23:5
    |
 LL |     static_id_indirect(&v);
    |     ^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 3 previous errors
+error: aborting due to 3 previous errors; 2 warnings emitted
 
 Some errors have detailed explanations: E0312, E0759.
 For more information about an error, try `rustc --explain E0312`.
diff --git a/src/test/ui/regions/regions-static-bound.nll.stderr b/src/test/ui/regions/regions-static-bound.nll.stderr
index 699638c7ef9..68e36f3aeea 100644
--- a/src/test/ui/regions/regions-static-bound.nll.stderr
+++ b/src/test/ui/regions/regions-static-bound.nll.stderr
@@ -1,5 +1,21 @@
+warning: unnecessary lifetime parameter `'a`
+  --> $DIR/regions-static-bound.rs:6:11
+   |
+LL |     where 'a: 'static { t }
+   |           ^^
+   |
+   = help: you can use the `'static` lifetime directly, in place of `'a`
+
+warning: unnecessary lifetime parameter `'b`
+  --> $DIR/regions-static-bound.rs:10:19
+   |
+LL |     where 'a: 'b, 'b: 'static { t }
+   |                   ^^
+   |
+   = help: you can use the `'static` lifetime directly, in place of `'b`
+
 error: lifetime may not live long enough
-  --> $DIR/regions-static-bound.rs:6:5
+  --> $DIR/regions-static-bound.rs:14:5
    |
 LL | fn static_id_wrong_way<'a>(t: &'a ()) -> &'static () where 'static: 'a {
    |                        -- lifetime `'a` defined here
@@ -7,7 +23,7 @@ LL |     t
    |     ^ returning this value requires that `'a` must outlive `'static`
 
 error[E0521]: borrowed data escapes outside of function
-  --> $DIR/regions-static-bound.rs:10:5
+  --> $DIR/regions-static-bound.rs:20:5
    |
 LL | fn error(u: &(), v: &()) {
    |          -  - let's call the lifetime of this reference `'1`
@@ -20,19 +36,19 @@ LL |     static_id(&u);
    |     argument requires that `'1` must outlive `'static`
 
 error[E0521]: borrowed data escapes outside of function
-  --> $DIR/regions-static-bound.rs:11:5
+  --> $DIR/regions-static-bound.rs:23:5
    |
 LL | fn error(u: &(), v: &()) {
    |                  -  - let's call the lifetime of this reference `'2`
    |                  |
    |                  `v` is a reference that is only valid in the function body
-LL |     static_id(&u);
+...
 LL |     static_id_indirect(&v);
    |     ^^^^^^^^^^^^^^^^^^^^^^
    |     |
    |     `v` escapes the function body here
    |     argument requires that `'2` must outlive `'static`
 
-error: aborting due to 3 previous errors
+error: aborting due to 3 previous errors; 2 warnings emitted
 
 For more information about this error, try `rustc --explain E0521`.
diff --git a/src/test/ui/regions/regions-static-bound.rs b/src/test/ui/regions/regions-static-bound.rs
index a977a8b36d0..1eed7e71745 100644
--- a/src/test/ui/regions/regions-static-bound.rs
+++ b/src/test/ui/regions/regions-static-bound.rs
@@ -1,14 +1,28 @@
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 fn static_id<'a,'b>(t: &'a ()) -> &'static ()
     where 'a: 'static { t }
+//~^ WARN unnecessary lifetime parameter `'a`
+
 fn static_id_indirect<'a,'b>(t: &'a ()) -> &'static ()
     where 'a: 'b, 'b: 'static { t }
+//~^ WARN unnecessary lifetime parameter `'b`
+
 fn static_id_wrong_way<'a>(t: &'a ()) -> &'static () where 'static: 'a {
-    t //~ ERROR E0312
+    t
+    //[base]~^ ERROR E0312
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn error(u: &(), v: &()) {
-    static_id(&u); //~ ERROR `u` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement [E0759]
-    static_id_indirect(&v); //~ ERROR `v` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement [E0759]
+    static_id(&u);
+    //[base]~^ ERROR `u` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement [E0759]
+    //[nll]~^^ ERROR borrowed data escapes outside of function [E0521]
+    static_id_indirect(&v);
+    //[base]~^ ERROR `v` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement [E0759]
+    //[nll]~^^ ERROR borrowed data escapes outside of function [E0521]
 }
 
 fn main() {}
diff --git a/src/test/ui/regions/regions-trait-object-subtyping.stderr b/src/test/ui/regions/regions-trait-object-subtyping.base.stderr
index d45ca94ad27..9f52136f0c0 100644
--- a/src/test/ui/regions/regions-trait-object-subtyping.stderr
+++ b/src/test/ui/regions/regions-trait-object-subtyping.base.stderr
@@ -1,43 +1,43 @@
 error[E0478]: lifetime bound not satisfied
-  --> $DIR/regions-trait-object-subtyping.rs:15:5
+  --> $DIR/regions-trait-object-subtyping.rs:19:5
    |
 LL |     x
    |     ^
    |
 note: lifetime parameter instantiated with the lifetime `'a` as defined here
-  --> $DIR/regions-trait-object-subtyping.rs:13:9
+  --> $DIR/regions-trait-object-subtyping.rs:17:9
    |
 LL | fn foo3<'a,'b>(x: &'a mut dyn Dummy) -> &'b mut dyn Dummy {
    |         ^^
 note: but lifetime parameter must outlive the lifetime `'b` as defined here
-  --> $DIR/regions-trait-object-subtyping.rs:13:12
+  --> $DIR/regions-trait-object-subtyping.rs:17:12
    |
 LL | fn foo3<'a,'b>(x: &'a mut dyn Dummy) -> &'b mut dyn Dummy {
    |            ^^
 
 error[E0495]: cannot infer an appropriate lifetime for automatic coercion due to conflicting requirements
-  --> $DIR/regions-trait-object-subtyping.rs:15:5
+  --> $DIR/regions-trait-object-subtyping.rs:19:5
    |
 LL |     x
    |     ^
    |
 note: first, the lifetime cannot outlive the lifetime `'a` as defined here...
-  --> $DIR/regions-trait-object-subtyping.rs:13:9
+  --> $DIR/regions-trait-object-subtyping.rs:17:9
    |
 LL | fn foo3<'a,'b>(x: &'a mut dyn Dummy) -> &'b mut dyn Dummy {
    |         ^^
 note: ...so that reference does not outlive borrowed content
-  --> $DIR/regions-trait-object-subtyping.rs:15:5
+  --> $DIR/regions-trait-object-subtyping.rs:19:5
    |
 LL |     x
    |     ^
 note: but, the lifetime must be valid for the lifetime `'b` as defined here...
-  --> $DIR/regions-trait-object-subtyping.rs:13:12
+  --> $DIR/regions-trait-object-subtyping.rs:17:12
    |
 LL | fn foo3<'a,'b>(x: &'a mut dyn Dummy) -> &'b mut dyn Dummy {
    |            ^^
 note: ...so that the types are compatible
-  --> $DIR/regions-trait-object-subtyping.rs:15:5
+  --> $DIR/regions-trait-object-subtyping.rs:19:5
    |
 LL |     x
    |     ^
@@ -45,7 +45,7 @@ LL |     x
               found `&mut (dyn Dummy + 'b)`
 
 error[E0308]: mismatched types
-  --> $DIR/regions-trait-object-subtyping.rs:22:5
+  --> $DIR/regions-trait-object-subtyping.rs:28:5
    |
 LL |     x
    |     ^ lifetime mismatch
@@ -53,12 +53,12 @@ LL |     x
    = note: expected struct `Wrapper<&'b mut (dyn Dummy + 'b)>`
               found struct `Wrapper<&'a mut (dyn Dummy + 'a)>`
 note: the lifetime `'b` as defined here...
-  --> $DIR/regions-trait-object-subtyping.rs:20:15
+  --> $DIR/regions-trait-object-subtyping.rs:26:15
    |
 LL | fn foo4<'a:'b,'b>(x: Wrapper<&'a mut dyn Dummy>) -> Wrapper<&'b mut dyn Dummy> {
    |               ^^
 note: ...does not necessarily outlive the lifetime `'a` as defined here
-  --> $DIR/regions-trait-object-subtyping.rs:20:9
+  --> $DIR/regions-trait-object-subtyping.rs:26:9
    |
 LL | fn foo4<'a:'b,'b>(x: Wrapper<&'a mut dyn Dummy>) -> Wrapper<&'b mut dyn Dummy> {
    |         ^^
diff --git a/src/test/ui/regions/regions-trait-object-subtyping.nll.stderr b/src/test/ui/regions/regions-trait-object-subtyping.nll.stderr
index 1b3a116d508..c8cec3bd377 100644
--- a/src/test/ui/regions/regions-trait-object-subtyping.nll.stderr
+++ b/src/test/ui/regions/regions-trait-object-subtyping.nll.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/regions-trait-object-subtyping.rs:15:5
+  --> $DIR/regions-trait-object-subtyping.rs:19:5
    |
 LL | fn foo3<'a,'b>(x: &'a mut dyn Dummy) -> &'b mut dyn Dummy {
    |         -- -- lifetime `'b` defined here
@@ -15,7 +15,7 @@ LL |     x
    = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
 
 error: lifetime may not live long enough
-  --> $DIR/regions-trait-object-subtyping.rs:22:5
+  --> $DIR/regions-trait-object-subtyping.rs:28:5
    |
 LL | fn foo4<'a:'b,'b>(x: Wrapper<&'a mut dyn Dummy>) -> Wrapper<&'b mut dyn Dummy> {
    |         --    -- lifetime `'b` defined here
diff --git a/src/test/ui/regions/regions-trait-object-subtyping.rs b/src/test/ui/regions/regions-trait-object-subtyping.rs
index 5b36194870e..f108fc81cea 100644
--- a/src/test/ui/regions/regions-trait-object-subtyping.rs
+++ b/src/test/ui/regions/regions-trait-object-subtyping.rs
@@ -1,3 +1,7 @@
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 trait Dummy { fn dummy(&self); }
 
 fn foo1<'a:'b,'b>(x: &'a mut (dyn Dummy+'a)) -> &'b mut (dyn Dummy+'b) {
@@ -12,14 +16,18 @@ fn foo2<'a:'b,'b>(x: &'b mut (dyn Dummy+'a)) -> &'b mut (dyn Dummy+'b) {
 
 fn foo3<'a,'b>(x: &'a mut dyn Dummy) -> &'b mut dyn Dummy {
     // Without knowing 'a:'b, we can't coerce
-    x //~ ERROR lifetime bound not satisfied
-     //~^ ERROR cannot infer an appropriate lifetime
+    x
+    //[base]~^ ERROR lifetime bound not satisfied
+    //[base]~| ERROR cannot infer an appropriate lifetime
+    //[nll]~^^^ ERROR lifetime may not live long enough
 }
 
 struct Wrapper<T>(T);
 fn foo4<'a:'b,'b>(x: Wrapper<&'a mut dyn Dummy>) -> Wrapper<&'b mut dyn Dummy> {
     // We can't coerce because it is packed in `Wrapper`
-    x //~ ERROR mismatched types
+    x
+    //[base]~^ ERROR mismatched types
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn main() {}
diff --git a/src/test/ui/regions/regions-variance-contravariant-use-covariant-in-second-position.stderr b/src/test/ui/regions/regions-variance-contravariant-use-covariant-in-second-position.base.stderr
index 98f7a8136da..23b3dea885d 100644
--- a/src/test/ui/regions/regions-variance-contravariant-use-covariant-in-second-position.stderr
+++ b/src/test/ui/regions/regions-variance-contravariant-use-covariant-in-second-position.base.stderr
@@ -1,5 +1,5 @@
 error[E0623]: lifetime mismatch
-  --> $DIR/regions-variance-contravariant-use-covariant-in-second-position.rs:25:30
+  --> $DIR/regions-variance-contravariant-use-covariant-in-second-position.rs:29:30
    |
 LL | fn use_<'short,'long>(c: S<'long, 'short>,
    |                          ---------------- this type is declared with multiple lifetimes...
diff --git a/src/test/ui/regions/regions-variance-contravariant-use-covariant-in-second-position.nll.stderr b/src/test/ui/regions/regions-variance-contravariant-use-covariant-in-second-position.nll.stderr
index 5352be430fb..f364f423f4e 100644
--- a/src/test/ui/regions/regions-variance-contravariant-use-covariant-in-second-position.nll.stderr
+++ b/src/test/ui/regions/regions-variance-contravariant-use-covariant-in-second-position.nll.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/regions-variance-contravariant-use-covariant-in-second-position.rs:25:12
+  --> $DIR/regions-variance-contravariant-use-covariant-in-second-position.rs:29:12
    |
 LL | fn use_<'short,'long>(c: S<'long, 'short>,
    |         ------ ----- lifetime `'long` defined here
diff --git a/src/test/ui/regions/regions-variance-contravariant-use-covariant-in-second-position.rs b/src/test/ui/regions/regions-variance-contravariant-use-covariant-in-second-position.rs
index 8ddd041d457..4bf32ec3082 100644
--- a/src/test/ui/regions/regions-variance-contravariant-use-covariant-in-second-position.rs
+++ b/src/test/ui/regions/regions-variance-contravariant-use-covariant-in-second-position.rs
@@ -4,6 +4,10 @@
 // Note: see variance-regions-*.rs for the tests that check that the
 // variance inference works in the first place.
 
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 // `S` is contravariant with respect to both parameters.
 struct S<'a, 'b> {
     f: &'a isize,
@@ -22,7 +26,9 @@ fn use_<'short,'long>(c: S<'long, 'short>,
     // 'short <= 'long, this would be true if the Contravariant type were
     // covariant with respect to its parameter 'a.
 
-    let _: S<'long, 'long> = c; //~ ERROR E0623
+    let _: S<'long, 'long> = c;
+    //[base]~^ ERROR E0623
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn main() {}
diff --git a/src/test/ui/regions/regions-variance-contravariant-use-covariant.stderr b/src/test/ui/regions/regions-variance-contravariant-use-covariant.base.stderr
index e7c106cbbe3..8eca0d4d121 100644
--- a/src/test/ui/regions/regions-variance-contravariant-use-covariant.stderr
+++ b/src/test/ui/regions/regions-variance-contravariant-use-covariant.base.stderr
@@ -1,5 +1,5 @@
 error[E0623]: lifetime mismatch
-  --> $DIR/regions-variance-contravariant-use-covariant.rs:23:35
+  --> $DIR/regions-variance-contravariant-use-covariant.rs:27:35
    |
 LL | fn use_<'short,'long>(c: Contravariant<'short>,
    |                          --------------------- these two types are declared with different lifetimes...
diff --git a/src/test/ui/regions/regions-variance-contravariant-use-covariant.nll.stderr b/src/test/ui/regions/regions-variance-contravariant-use-covariant.nll.stderr
index 22c9b915bb9..bc6dd6e69ed 100644
--- a/src/test/ui/regions/regions-variance-contravariant-use-covariant.nll.stderr
+++ b/src/test/ui/regions/regions-variance-contravariant-use-covariant.nll.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/regions-variance-contravariant-use-covariant.rs:23:12
+  --> $DIR/regions-variance-contravariant-use-covariant.rs:27:12
    |
 LL | fn use_<'short,'long>(c: Contravariant<'short>,
    |         ------ ----- lifetime `'long` defined here
diff --git a/src/test/ui/regions/regions-variance-contravariant-use-covariant.rs b/src/test/ui/regions/regions-variance-contravariant-use-covariant.rs
index cbdf62ecb61..ea08a709230 100644
--- a/src/test/ui/regions/regions-variance-contravariant-use-covariant.rs
+++ b/src/test/ui/regions/regions-variance-contravariant-use-covariant.rs
@@ -4,6 +4,10 @@
 // Note: see variance-regions-*.rs for the tests that check that the
 // variance inference works in the first place.
 
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 // This is contravariant with respect to 'a, meaning that
 // Contravariant<'long> <: Contravariant<'short> iff
 // 'short <= 'long
@@ -20,7 +24,9 @@ fn use_<'short,'long>(c: Contravariant<'short>,
     // 'short <= 'long, this would be true if the Contravariant type were
     // covariant with respect to its parameter 'a.
 
-    let _: Contravariant<'long> = c; //~ ERROR E0623
+    let _: Contravariant<'long> = c;
+    //[base]~^ ERROR E0623
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn main() {}
diff --git a/src/test/ui/regions/regions-variance-covariant-use-contravariant.stderr b/src/test/ui/regions/regions-variance-covariant-use-contravariant.base.stderr
index e5e5261ba99..565de38ee11 100644
--- a/src/test/ui/regions/regions-variance-covariant-use-contravariant.stderr
+++ b/src/test/ui/regions/regions-variance-covariant-use-contravariant.base.stderr
@@ -1,5 +1,5 @@
 error[E0623]: lifetime mismatch
-  --> $DIR/regions-variance-covariant-use-contravariant.rs:23:32
+  --> $DIR/regions-variance-covariant-use-contravariant.rs:27:32
    |
 LL | fn use_<'short,'long>(c: Covariant<'long>,
    |                          ----------------
diff --git a/src/test/ui/regions/regions-variance-covariant-use-contravariant.nll.stderr b/src/test/ui/regions/regions-variance-covariant-use-contravariant.nll.stderr
index a07181ad553..9d3cebc9a4d 100644
--- a/src/test/ui/regions/regions-variance-covariant-use-contravariant.nll.stderr
+++ b/src/test/ui/regions/regions-variance-covariant-use-contravariant.nll.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/regions-variance-covariant-use-contravariant.rs:23:12
+  --> $DIR/regions-variance-covariant-use-contravariant.rs:27:12
    |
 LL | fn use_<'short,'long>(c: Covariant<'long>,
    |         ------ ----- lifetime `'long` defined here
diff --git a/src/test/ui/regions/regions-variance-covariant-use-contravariant.rs b/src/test/ui/regions/regions-variance-covariant-use-contravariant.rs
index 9aa0c819271..748ad84840a 100644
--- a/src/test/ui/regions/regions-variance-covariant-use-contravariant.rs
+++ b/src/test/ui/regions/regions-variance-covariant-use-contravariant.rs
@@ -4,6 +4,10 @@
 // Note: see variance-regions-*.rs for the tests that check that the
 // variance inference works in the first place.
 
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 // This is covariant with respect to 'a, meaning that
 // Covariant<'foo> <: Covariant<'static> because
 // 'foo <= 'static
@@ -20,7 +24,9 @@ fn use_<'short,'long>(c: Covariant<'long>,
     // 'short <= 'long, this would be true if the Covariant type were
     // contravariant with respect to its parameter 'a.
 
-    let _: Covariant<'short> = c; //~ ERROR E0623
+    let _: Covariant<'short> = c;
+    //[base]~^ ERROR E0623
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn main() {}
diff --git a/src/test/ui/regions/regions-variance-invariant-use-contravariant.stderr b/src/test/ui/regions/regions-variance-invariant-use-contravariant.base.stderr
index 2a2d5d019a1..e2e8958f53e 100644
--- a/src/test/ui/regions/regions-variance-invariant-use-contravariant.stderr
+++ b/src/test/ui/regions/regions-variance-invariant-use-contravariant.base.stderr
@@ -1,5 +1,5 @@
 error[E0623]: lifetime mismatch
-  --> $DIR/regions-variance-invariant-use-contravariant.rs:20:32
+  --> $DIR/regions-variance-invariant-use-contravariant.rs:24:32
    |
 LL | fn use_<'short,'long>(c: Invariant<'long>,
    |                          ----------------
diff --git a/src/test/ui/regions/regions-variance-invariant-use-contravariant.nll.stderr b/src/test/ui/regions/regions-variance-invariant-use-contravariant.nll.stderr
index b35a2cb905d..b4ccb1693a7 100644
--- a/src/test/ui/regions/regions-variance-invariant-use-contravariant.nll.stderr
+++ b/src/test/ui/regions/regions-variance-invariant-use-contravariant.nll.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/regions-variance-invariant-use-contravariant.rs:20:12
+  --> $DIR/regions-variance-invariant-use-contravariant.rs:24:12
    |
 LL | fn use_<'short,'long>(c: Invariant<'long>,
    |         ------ ----- lifetime `'long` defined here
diff --git a/src/test/ui/regions/regions-variance-invariant-use-contravariant.rs b/src/test/ui/regions/regions-variance-invariant-use-contravariant.rs
index 663b23b37d4..788f9b1b4d0 100644
--- a/src/test/ui/regions/regions-variance-invariant-use-contravariant.rs
+++ b/src/test/ui/regions/regions-variance-invariant-use-contravariant.rs
@@ -4,6 +4,10 @@
 // Note: see variance-regions-*.rs for the tests that check that the
 // variance inference works in the first place.
 
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 struct Invariant<'a> {
     f: &'a mut &'a isize
 }
@@ -17,7 +21,9 @@ fn use_<'short,'long>(c: Invariant<'long>,
     // 'short <= 'long, this would be true if the Invariant type were
     // contravariant with respect to its parameter 'a.
 
-    let _: Invariant<'short> = c; //~ ERROR E0623
+    let _: Invariant<'short> = c;
+    //[base]~^ ERROR E0623
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn main() { }
diff --git a/src/test/ui/regions/regions-variance-invariant-use-covariant.stderr b/src/test/ui/regions/regions-variance-invariant-use-covariant.base.stderr
index 7801517595d..da91db1b918 100644
--- a/src/test/ui/regions/regions-variance-invariant-use-covariant.stderr
+++ b/src/test/ui/regions/regions-variance-invariant-use-covariant.base.stderr
@@ -1,5 +1,5 @@
 error[E0308]: mismatched types
-  --> $DIR/regions-variance-invariant-use-covariant.rs:17:33
+  --> $DIR/regions-variance-invariant-use-covariant.rs:21:33
    |
 LL |     let _: Invariant<'static> = c;
    |                                 ^ lifetime mismatch
@@ -7,7 +7,7 @@ LL |     let _: Invariant<'static> = c;
    = note: expected struct `Invariant<'static>`
               found struct `Invariant<'b>`
 note: the lifetime `'b` as defined here...
-  --> $DIR/regions-variance-invariant-use-covariant.rs:11:9
+  --> $DIR/regions-variance-invariant-use-covariant.rs:15:9
    |
 LL | fn use_<'b>(c: Invariant<'b>) {
    |         ^^
diff --git a/src/test/ui/regions/regions-variance-invariant-use-covariant.nll.stderr b/src/test/ui/regions/regions-variance-invariant-use-covariant.nll.stderr
index 761e78d179e..7b05c357589 100644
--- a/src/test/ui/regions/regions-variance-invariant-use-covariant.nll.stderr
+++ b/src/test/ui/regions/regions-variance-invariant-use-covariant.nll.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/regions-variance-invariant-use-covariant.rs:17:12
+  --> $DIR/regions-variance-invariant-use-covariant.rs:21:12
    |
 LL | fn use_<'b>(c: Invariant<'b>) {
    |         -- lifetime `'b` defined here
diff --git a/src/test/ui/regions/regions-variance-invariant-use-covariant.rs b/src/test/ui/regions/regions-variance-invariant-use-covariant.rs
index 07482e6fd19..4b7da4493ac 100644
--- a/src/test/ui/regions/regions-variance-invariant-use-covariant.rs
+++ b/src/test/ui/regions/regions-variance-invariant-use-covariant.rs
@@ -4,6 +4,10 @@
 // Note: see variance-regions-*.rs for the tests that check that the
 // variance inference works in the first place.
 
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
 struct Invariant<'a> {
     f: &'a mut &'a isize
 }
@@ -14,7 +18,9 @@ fn use_<'b>(c: Invariant<'b>) {
     // Since 'b <= 'static, this would be true if Invariant were covariant
     // with respect to its parameter 'a.
 
-    let _: Invariant<'static> = c; //~ ERROR mismatched types
+    let _: Invariant<'static> = c;
+    //[base]~^ ERROR mismatched types [E0308]
+    //[nll]~^^ ERROR lifetime may not live long enough
 }
 
 fn main() { }
diff --git a/src/test/ui/repeat-expr/infer.rs b/src/test/ui/repeat-expr/infer.rs
new file mode 100644
index 00000000000..8197713b97e
--- /dev/null
+++ b/src/test/ui/repeat-expr/infer.rs
@@ -0,0 +1,16 @@
+// check-pass
+
+#[derive(Clone, Default)]
+struct MaybeCopy<T>(T);
+
+impl Copy for MaybeCopy<u8> {}
+
+fn is_copy<T: Copy>(x: T) {
+    println!("{}", std::any::type_name::<T>());
+}
+
+fn main() {
+    is_copy(MaybeCopy::default());
+    [MaybeCopy::default(); 13];
+    // didn't work, because `Copy` was only checked in the mir
+}
diff --git a/src/test/ui/repeat-expr-in-static.rs b/src/test/ui/repeat-expr/repeat-expr-in-static.rs
index 0b895379330..0b895379330 100644
--- a/src/test/ui/repeat-expr-in-static.rs
+++ b/src/test/ui/repeat-expr/repeat-expr-in-static.rs
diff --git a/src/test/ui/repeat-to-run-dtor-twice.rs b/src/test/ui/repeat-expr/repeat-to-run-dtor-twice.rs
index 0cd8eceefc5..0cd8eceefc5 100644
--- a/src/test/ui/repeat-to-run-dtor-twice.rs
+++ b/src/test/ui/repeat-expr/repeat-to-run-dtor-twice.rs
diff --git a/src/test/ui/repeat-to-run-dtor-twice.stderr b/src/test/ui/repeat-expr/repeat-to-run-dtor-twice.stderr
index 904413712cd..36b93616375 100644
--- a/src/test/ui/repeat-to-run-dtor-twice.stderr
+++ b/src/test/ui/repeat-expr/repeat-to-run-dtor-twice.stderr
@@ -1,10 +1,10 @@
 error[E0277]: the trait bound `Foo: Copy` is not satisfied
-  --> $DIR/repeat-to-run-dtor-twice.rs:17:13
+  --> $DIR/repeat-to-run-dtor-twice.rs:17:15
    |
 LL |     let _ = [ a; 5 ];
-   |             ^^^^^^^^ the trait `Copy` is not implemented for `Foo`
+   |               ^ the trait `Copy` is not implemented for `Foo`
    |
-   = note: the `Copy` trait is required because the repeated element will be copied
+   = note: the `Copy` trait is required because this value will be copied for each element of the array
 help: consider annotating `Foo` with `#[derive(Copy)]`
    |
 LL | #[derive(Copy)]
diff --git a/src/test/ui/repeat_count.rs b/src/test/ui/repeat-expr/repeat_count.rs
index 96abff4ab41..96abff4ab41 100644
--- a/src/test/ui/repeat_count.rs
+++ b/src/test/ui/repeat-expr/repeat_count.rs
diff --git a/src/test/ui/repeat_count.stderr b/src/test/ui/repeat-expr/repeat_count.stderr
index 59bcd954a1f..59bcd954a1f 100644
--- a/src/test/ui/repeat_count.stderr
+++ b/src/test/ui/repeat-expr/repeat_count.stderr
diff --git a/src/test/ui/issues/issue-26545.rs b/src/test/ui/resolve/issue-26545.rs
index 5652ee74706..5652ee74706 100644
--- a/src/test/ui/issues/issue-26545.rs
+++ b/src/test/ui/resolve/issue-26545.rs
diff --git a/src/test/ui/issues/issue-26545.stderr b/src/test/ui/resolve/issue-26545.stderr
index d3c86692501..d3c86692501 100644
--- a/src/test/ui/issues/issue-26545.stderr
+++ b/src/test/ui/resolve/issue-26545.stderr
diff --git a/src/test/ui/resolve/resolve-inconsistent-names.rs b/src/test/ui/resolve/resolve-inconsistent-names.rs
index b9202f556d1..989d2d45230 100644
--- a/src/test/ui/resolve/resolve-inconsistent-names.rs
+++ b/src/test/ui/resolve/resolve-inconsistent-names.rs
@@ -2,9 +2,9 @@
 
 enum E { A, B, c }
 
-mod m {
+pub mod m {
     const CONST1: usize = 10;
-    const Const2: usize = 20;
+    pub const Const2: usize = 20;
 }
 
 fn main() {
@@ -22,15 +22,14 @@ fn main() {
         //~| ERROR variable `B` is bound inconsistently
         //~| ERROR mismatched types
         //~| ERROR variable `c` is not bound in all patterns
-        //~| HELP consider making the path in the pattern qualified: `?::A`
+        //~| HELP if you meant to match on unit variant `E::A`, use the full path in the pattern
     }
 
     let z = (10, 20);
     match z {
         (CONST1, _) | (_, Const2) => ()
         //~^ ERROR variable `CONST1` is not bound in all patterns
-        //~| HELP consider making the path in the pattern qualified: `?::CONST1`
         //~| ERROR variable `Const2` is not bound in all patterns
-        //~| HELP consider making the path in the pattern qualified: `?::Const2`
+        //~| HELP if you meant to match on constant `m::Const2`, use the full path in the pattern
     }
 }
diff --git a/src/test/ui/resolve/resolve-inconsistent-names.stderr b/src/test/ui/resolve/resolve-inconsistent-names.stderr
index 70e9c2e5bf5..9de191f7d32 100644
--- a/src/test/ui/resolve/resolve-inconsistent-names.stderr
+++ b/src/test/ui/resolve/resolve-inconsistent-names.stderr
@@ -23,11 +23,10 @@ LL |         (A, B) | (ref B, c) | (c, A) => ()
    |          |       pattern doesn't bind `A`
    |          variable not in all patterns
    |
-help: if you meant to match on a variant or a `const` item, consider making the path in the pattern qualified: `?::A`
-  --> $DIR/resolve-inconsistent-names.rs:19:10
+help: if you meant to match on unit variant `E::A`, use the full path in the pattern
    |
-LL |         (A, B) | (ref B, c) | (c, A) => ()
-   |          ^
+LL |         (E::A, B) | (ref B, c) | (c, A) => ()
+   |          ~~~~
 
 error[E0408]: variable `B` is not bound in all patterns
   --> $DIR/resolve-inconsistent-names.rs:19:31
@@ -63,11 +62,11 @@ LL |         (CONST1, _) | (_, Const2) => ()
    |          |
    |          variable not in all patterns
    |
-help: if you meant to match on a variant or a `const` item, consider making the path in the pattern qualified: `?::CONST1`
-  --> $DIR/resolve-inconsistent-names.rs:30:10
+note: you might have meant to match on constant `m::CONST1`, which exists but is inaccessible
+  --> $DIR/resolve-inconsistent-names.rs:6:5
    |
-LL |         (CONST1, _) | (_, Const2) => ()
-   |          ^^^^^^
+LL |     const CONST1: usize = 10;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^ not accessible
 
 error[E0408]: variable `Const2` is not bound in all patterns
   --> $DIR/resolve-inconsistent-names.rs:30:9
@@ -77,11 +76,10 @@ LL |         (CONST1, _) | (_, Const2) => ()
    |         |
    |         pattern doesn't bind `Const2`
    |
-help: if you meant to match on a variant or a `const` item, consider making the path in the pattern qualified: `?::Const2`
-  --> $DIR/resolve-inconsistent-names.rs:30:27
+help: if you meant to match on constant `m::Const2`, use the full path in the pattern
    |
-LL |         (CONST1, _) | (_, Const2) => ()
-   |                           ^^^^^^
+LL |         (CONST1, _) | (_, m::Const2) => ()
+   |                           ~~~~~~~~~
 
 error[E0308]: mismatched types
   --> $DIR/resolve-inconsistent-names.rs:19:19
diff --git a/src/test/ui/rfc-2093-infer-outlives/dont-infer-static.stderr b/src/test/ui/rfc-2093-infer-outlives/dont-infer-static.stderr
index 1f387a042e6..950ffd6c89b 100644
--- a/src/test/ui/rfc-2093-infer-outlives/dont-infer-static.stderr
+++ b/src/test/ui/rfc-2093-infer-outlives/dont-infer-static.stderr
@@ -1,8 +1,6 @@
 error[E0310]: the parameter type `U` may not live long enough
   --> $DIR/dont-infer-static.rs:8:10
    |
-LL | struct Foo<U> {
-   |            - help: consider adding an explicit lifetime bound...: `U: 'static`
 LL |     bar: Bar<U>
    |          ^^^^^^ ...so that the type `U` will meet its required lifetime bounds...
    |
@@ -11,6 +9,10 @@ note: ...that is required by this bound
    |
 LL | struct Bar<T: 'static> {
    |               ^^^^^^^
+help: consider adding an explicit lifetime bound...
+   |
+LL | struct Foo<U: 'static> {
+   |             +++++++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/rfc-2093-infer-outlives/regions-enum-not-wf.stderr b/src/test/ui/rfc-2093-infer-outlives/regions-enum-not-wf.stderr
index 553a3e71c16..2c660b28500 100644
--- a/src/test/ui/rfc-2093-infer-outlives/regions-enum-not-wf.stderr
+++ b/src/test/ui/rfc-2093-infer-outlives/regions-enum-not-wf.stderr
@@ -1,27 +1,35 @@
 error[E0309]: the parameter type `T` may not live long enough
   --> $DIR/regions-enum-not-wf.rs:17:18
    |
-LL | enum Ref1<'a, T> {
-   |               - help: consider adding an explicit lifetime bound...: `T: 'a`
 LL |     Ref1Variant1(RequireOutlives<'a, T>),
    |                  ^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL | enum Ref1<'a, T: 'a> {
+   |                ++++
 
 error[E0309]: the parameter type `T` may not live long enough
   --> $DIR/regions-enum-not-wf.rs:22:25
    |
-LL | enum Ref2<'a, T> {
-   |               - help: consider adding an explicit lifetime bound...: `T: 'a`
-LL |     Ref2Variant1,
 LL |     Ref2Variant2(isize, RequireOutlives<'a, T>),
    |                         ^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL | enum Ref2<'a, T: 'a> {
+   |                ++++
 
 error[E0309]: the parameter type `T` may not live long enough
   --> $DIR/regions-enum-not-wf.rs:35:23
    |
-LL | enum RefDouble<'a, 'b, T> {
-   |                        - help: consider adding an explicit lifetime bound...: `T: 'b`
 LL |     RefDoubleVariant1(&'a RequireOutlives<'b, T>),
    |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL | enum RefDouble<'a, 'b, T: 'b> {
+   |                         ++++
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/rfc-2093-infer-outlives/regions-struct-not-wf.stderr b/src/test/ui/rfc-2093-infer-outlives/regions-struct-not-wf.stderr
index f8861262991..34ff1362cf3 100644
--- a/src/test/ui/rfc-2093-infer-outlives/regions-struct-not-wf.stderr
+++ b/src/test/ui/rfc-2093-infer-outlives/regions-struct-not-wf.stderr
@@ -1,16 +1,17 @@
 error[E0309]: the parameter type `T` may not live long enough
   --> $DIR/regions-struct-not-wf.rs:13:16
    |
-LL | impl<'a, T> Trait<'a, T> for usize {
-   |          - help: consider adding an explicit lifetime bound...: `T: 'a`
 LL |     type Out = &'a T;
    |                ^^^^^ ...so that the reference type `&'a T` does not outlive the data it points at
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL | impl<'a, T: 'a> Trait<'a, T> for usize {
+   |           ++++
 
 error[E0309]: the parameter type `T` may not live long enough
   --> $DIR/regions-struct-not-wf.rs:21:16
    |
-LL | impl<'a, T> Trait<'a, T> for u32 {
-   |          - help: consider adding an explicit lifetime bound...: `T: 'a`
 LL |     type Out = RefOk<'a, T>;
    |                ^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds...
    |
@@ -19,6 +20,10 @@ note: ...that is required by this bound
    |
 LL | struct RefOk<'a, T:'a> {
    |                    ^^
+help: consider adding an explicit lifetime bound...
+   |
+LL | impl<'a, T: 'a> Trait<'a, T> for u32 {
+   |           ++++
 
 error[E0491]: in type `&'a &'b T`, reference has a longer lifetime than the data it references
   --> $DIR/regions-struct-not-wf.rs:25:16
diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.rs b/src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.rs
index 961968186de..dd509931766 100644
--- a/src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.rs
+++ b/src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.rs
@@ -3,7 +3,7 @@
 pub struct Int(i32);
 
 impl const std::ops::Add for i32 { //~ ERROR type annotations needed
-    //~^ ERROR only traits defined in the current crate can be implemented for arbitrary types
+    //~^ ERROR only traits defined in the current crate can be implemented for primitive types
     type Output = Self;
 
     fn add(self, rhs: Self) -> Self {
diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr b/src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr
index 154a6a35a44..9fd82196e79 100644
--- a/src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr
+++ b/src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr
@@ -1,4 +1,4 @@
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for primitive types
   --> $DIR/const-and-non-const-impl.rs:5:1
    |
 LL | impl const std::ops::Add for i32 {
diff --git a/src/test/ui/rfc1623.base.stderr b/src/test/ui/rfc1623.base.stderr
index 6d389a1317a..364c8c8f706 100644
--- a/src/test/ui/rfc1623.base.stderr
+++ b/src/test/ui/rfc1623.base.stderr
@@ -1,5 +1,5 @@
 error: implementation of `FnOnce` is not general enough
-  --> $DIR/rfc1623.rs:36:8
+  --> $DIR/rfc1623.rs:32:8
    |
 LL |     f: &id,
    |        ^^^ implementation of `FnOnce` is not general enough
diff --git a/src/test/ui/rfc1623.nll.stderr b/src/test/ui/rfc1623.nll.stderr
index f85b6ff8ff7..2eff4708547 100644
--- a/src/test/ui/rfc1623.nll.stderr
+++ b/src/test/ui/rfc1623.nll.stderr
@@ -1,63 +1,35 @@
 error[E0308]: mismatched types
-  --> $DIR/rfc1623.rs:29:35
+  --> $DIR/rfc1623.rs:32:8
    |
-LL |   static SOME_STRUCT: &SomeStruct = &SomeStruct {
-   |  ___________________________________^
-LL | |
-LL | |
-LL | |
-...  |
-LL | |
-LL | | };
-   | |_^ one type is more general than the other
+LL |     f: &id,
+   |        ^^^ one type is more general than the other
    |
    = note: expected type `for<'a, 'b> Fn<(&'a Foo<'b>,)>`
               found type `Fn<(&Foo<'_>,)>`
 
 error[E0308]: mismatched types
-  --> $DIR/rfc1623.rs:29:35
+  --> $DIR/rfc1623.rs:32:8
    |
-LL |   static SOME_STRUCT: &SomeStruct = &SomeStruct {
-   |  ___________________________________^
-LL | |
-LL | |
-LL | |
-...  |
-LL | |
-LL | | };
-   | |_^ one type is more general than the other
+LL |     f: &id,
+   |        ^^^ one type is more general than the other
    |
    = note: expected type `for<'a, 'b> Fn<(&'a Foo<'b>,)>`
               found type `Fn<(&Foo<'_>,)>`
 
 error: implementation of `FnOnce` is not general enough
-  --> $DIR/rfc1623.rs:29:35
+  --> $DIR/rfc1623.rs:32:8
    |
-LL |   static SOME_STRUCT: &SomeStruct = &SomeStruct {
-   |  ___________________________________^
-LL | |
-LL | |
-LL | |
-...  |
-LL | |
-LL | | };
-   | |_^ implementation of `FnOnce` is not general enough
+LL |     f: &id,
+   |        ^^^ implementation of `FnOnce` is not general enough
    |
    = note: `fn(&'2 Foo<'_>) -> &'2 Foo<'_> {id::<&'2 Foo<'_>>}` must implement `FnOnce<(&'1 Foo<'b>,)>`, for any lifetime `'1`...
    = note: ...but it actually implements `FnOnce<(&'2 Foo<'_>,)>`, for some specific lifetime `'2`
 
 error: implementation of `FnOnce` is not general enough
-  --> $DIR/rfc1623.rs:29:35
+  --> $DIR/rfc1623.rs:32:8
    |
-LL |   static SOME_STRUCT: &SomeStruct = &SomeStruct {
-   |  ___________________________________^
-LL | |
-LL | |
-LL | |
-...  |
-LL | |
-LL | | };
-   | |_^ implementation of `FnOnce` is not general enough
+LL |     f: &id,
+   |        ^^^ implementation of `FnOnce` is not general enough
    |
    = note: `fn(&Foo<'2>) -> &Foo<'2> {id::<&Foo<'2>>}` must implement `FnOnce<(&'a Foo<'1>,)>`, for any lifetime `'1`...
    = note: ...but it actually implements `FnOnce<(&Foo<'2>,)>`, for some specific lifetime `'2`
diff --git a/src/test/ui/rfc1623.rs b/src/test/ui/rfc1623.rs
index 0e9d2140324..443da0aa955 100644
--- a/src/test/ui/rfc1623.rs
+++ b/src/test/ui/rfc1623.rs
@@ -27,14 +27,14 @@ fn id<T>(t: T) -> T {
 }
 
 static SOME_STRUCT: &SomeStruct = &SomeStruct {
-    //[nll]~^ ERROR mismatched types
-    //[nll]~| ERROR mismatched types
-    //[nll]~| ERROR implementation of `FnOnce` is not general enough
-    //[nll]~| ERROR implementation of `FnOnce` is not general enough
     foo: &Foo { bools: &[false, true] },
     bar: &Bar { bools: &[true, true] },
     f: &id,
     //[base]~^ ERROR implementation of `FnOnce` is not general enough
+    //[nll]~^^ ERROR mismatched types
+    //[nll]~| ERROR mismatched types
+    //[nll]~| ERROR implementation of `FnOnce` is not general enough
+    //[nll]~| ERROR implementation of `FnOnce` is not general enough
 };
 
 // very simple test for a 'static static with default lifetime
diff --git a/src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.mir.stderr b/src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.mir.stderr
index 79273a1dcbf..0ef7b8b09f1 100644
--- a/src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.mir.stderr
+++ b/src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.mir.stderr
@@ -7,7 +7,7 @@ LL |     sse2();
    = note: can only be called if the required target features are available
 
 error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:24:5
+  --> $DIR/safe-calls.rs:26:5
    |
 LL |     avx_bmi2();
    |     ^^^^^^^^^^ call to function with `#[target_feature]`
@@ -15,7 +15,7 @@ LL |     avx_bmi2();
    = note: can only be called if the required target features are available
 
 error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:25:5
+  --> $DIR/safe-calls.rs:29:5
    |
 LL |     Quux.avx_bmi2();
    |     ^^^^^^^^^^^^^^^ call to function with `#[target_feature]`
@@ -23,7 +23,7 @@ LL |     Quux.avx_bmi2();
    = note: can only be called if the required target features are available
 
 error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:30:5
+  --> $DIR/safe-calls.rs:36:5
    |
 LL |     avx_bmi2();
    |     ^^^^^^^^^^ call to function with `#[target_feature]`
@@ -31,7 +31,7 @@ LL |     avx_bmi2();
    = note: can only be called if the required target features are available
 
 error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:31:5
+  --> $DIR/safe-calls.rs:39:5
    |
 LL |     Quux.avx_bmi2();
    |     ^^^^^^^^^^^^^^^ call to function with `#[target_feature]`
@@ -39,7 +39,7 @@ LL |     Quux.avx_bmi2();
    = note: can only be called if the required target features are available
 
 error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:36:5
+  --> $DIR/safe-calls.rs:46:5
    |
 LL |     sse2();
    |     ^^^^^^ call to function with `#[target_feature]`
@@ -47,7 +47,7 @@ LL |     sse2();
    = note: can only be called if the required target features are available
 
 error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:37:5
+  --> $DIR/safe-calls.rs:49:5
    |
 LL |     avx_bmi2();
    |     ^^^^^^^^^^ call to function with `#[target_feature]`
@@ -55,7 +55,7 @@ LL |     avx_bmi2();
    = note: can only be called if the required target features are available
 
 error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:38:5
+  --> $DIR/safe-calls.rs:52:5
    |
 LL |     Quux.avx_bmi2();
    |     ^^^^^^^^^^^^^^^ call to function with `#[target_feature]`
@@ -63,7 +63,7 @@ LL |     Quux.avx_bmi2();
    = note: can only be called if the required target features are available
 
 error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:44:5
+  --> $DIR/safe-calls.rs:60:5
    |
 LL |     sse2();
    |     ^^^^^^ call to function with `#[target_feature]`
@@ -71,7 +71,7 @@ LL |     sse2();
    = note: can only be called if the required target features are available
 
 error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:47:18
+  --> $DIR/safe-calls.rs:65:18
    |
 LL | const name: () = sse2();
    |                  ^^^^^^ call to function with `#[target_feature]`
diff --git a/src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.rs b/src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.rs
index de0b89f46ba..cebc6f94784 100644
--- a/src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.rs
+++ b/src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.rs
@@ -20,30 +20,50 @@ impl Quux {
 }
 
 fn foo() {
-    sse2();              //~ ERROR call to function with `#[target_feature]` is unsafe
-    avx_bmi2();          //~ ERROR call to function with `#[target_feature]` is unsafe
-    Quux.avx_bmi2();     //~ ERROR call to function with `#[target_feature]` is unsafe
+    sse2();
+    //[mir]~^ ERROR call to function with `#[target_feature]` is unsafe
+    //[thir]~^^ ERROR call to function `sse2` with `#[target_feature]` is unsafe
+    avx_bmi2();
+    //[mir]~^ ERROR call to function with `#[target_feature]` is unsafe
+    //[thir]~^^ ERROR call to function `avx_bmi2` with `#[target_feature]` is unsafe
+    Quux.avx_bmi2();
+    //[mir]~^ ERROR call to function with `#[target_feature]` is unsafe
+    //[thir]~^^ ERROR call to function `Quux::avx_bmi2` with `#[target_feature]` is unsafe
 }
 
 #[target_feature(enable = "sse2")]
 fn bar() {
-    avx_bmi2();          //~ ERROR call to function with `#[target_feature]` is unsafe
-    Quux.avx_bmi2();     //~ ERROR call to function with `#[target_feature]` is unsafe
+    avx_bmi2();
+    //[mir]~^ ERROR call to function with `#[target_feature]` is unsafe
+    //[thir]~^^ ERROR call to function `avx_bmi2` with `#[target_feature]` is unsafe
+    Quux.avx_bmi2();
+    //[mir]~^ ERROR call to function with `#[target_feature]` is unsafe
+    //[thir]~^^ ERROR call to function `Quux::avx_bmi2` with `#[target_feature]` is unsafe
 }
 
 #[target_feature(enable = "avx")]
 fn baz() {
-    sse2();              //~ ERROR call to function with `#[target_feature]` is unsafe
-    avx_bmi2();          //~ ERROR call to function with `#[target_feature]` is unsafe
-    Quux.avx_bmi2();     //~ ERROR call to function with `#[target_feature]` is unsafe
+    sse2();
+    //[mir]~^ ERROR call to function with `#[target_feature]` is unsafe
+    //[thir]~^^ ERROR call to function `sse2` with `#[target_feature]` is unsafe
+    avx_bmi2();
+    //[mir]~^ ERROR call to function with `#[target_feature]` is unsafe
+    //[thir]~^^ ERROR call to function `avx_bmi2` with `#[target_feature]` is unsafe
+    Quux.avx_bmi2();
+    //[mir]~^ ERROR call to function with `#[target_feature]` is unsafe
+    //[thir]~^^ ERROR call to function `Quux::avx_bmi2` with `#[target_feature]` is unsafe
 }
 
 #[target_feature(enable = "avx")]
 #[target_feature(enable = "bmi2")]
 fn qux() {
-    sse2();              //~ ERROR call to function with `#[target_feature]` is unsafe
+    sse2();
+    //[mir]~^ ERROR call to function with `#[target_feature]` is unsafe
+    //[thir]~^^ ERROR call to function `sse2` with `#[target_feature]` is unsafe
 }
 
-const name: () = sse2(); //~ ERROR call to function with `#[target_feature]` is unsafe
+const name: () = sse2();
+//[mir]~^ ERROR call to function with `#[target_feature]` is unsafe
+//[thir]~^^ ERROR call to function `sse2` with `#[target_feature]` is unsafe
 
 fn main() {}
diff --git a/src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.thir.stderr b/src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.thir.stderr
index 79273a1dcbf..c75ac6e8b9a 100644
--- a/src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.thir.stderr
+++ b/src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.thir.stderr
@@ -1,4 +1,4 @@
-error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
+error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe function or block
   --> $DIR/safe-calls.rs:23:5
    |
 LL |     sse2();
@@ -6,72 +6,72 @@ LL |     sse2();
    |
    = note: can only be called if the required target features are available
 
-error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:24:5
+error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block
+  --> $DIR/safe-calls.rs:26:5
    |
 LL |     avx_bmi2();
    |     ^^^^^^^^^^ call to function with `#[target_feature]`
    |
    = note: can only be called if the required target features are available
 
-error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:25:5
+error[E0133]: call to function `Quux::avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block
+  --> $DIR/safe-calls.rs:29:5
    |
 LL |     Quux.avx_bmi2();
    |     ^^^^^^^^^^^^^^^ call to function with `#[target_feature]`
    |
    = note: can only be called if the required target features are available
 
-error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:30:5
+error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block
+  --> $DIR/safe-calls.rs:36:5
    |
 LL |     avx_bmi2();
    |     ^^^^^^^^^^ call to function with `#[target_feature]`
    |
    = note: can only be called if the required target features are available
 
-error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:31:5
+error[E0133]: call to function `Quux::avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block
+  --> $DIR/safe-calls.rs:39:5
    |
 LL |     Quux.avx_bmi2();
    |     ^^^^^^^^^^^^^^^ call to function with `#[target_feature]`
    |
    = note: can only be called if the required target features are available
 
-error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:36:5
+error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe function or block
+  --> $DIR/safe-calls.rs:46:5
    |
 LL |     sse2();
    |     ^^^^^^ call to function with `#[target_feature]`
    |
    = note: can only be called if the required target features are available
 
-error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:37:5
+error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block
+  --> $DIR/safe-calls.rs:49:5
    |
 LL |     avx_bmi2();
    |     ^^^^^^^^^^ call to function with `#[target_feature]`
    |
    = note: can only be called if the required target features are available
 
-error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:38:5
+error[E0133]: call to function `Quux::avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block
+  --> $DIR/safe-calls.rs:52:5
    |
 LL |     Quux.avx_bmi2();
    |     ^^^^^^^^^^^^^^^ call to function with `#[target_feature]`
    |
    = note: can only be called if the required target features are available
 
-error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:44:5
+error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe function or block
+  --> $DIR/safe-calls.rs:60:5
    |
 LL |     sse2();
    |     ^^^^^^ call to function with `#[target_feature]`
    |
    = note: can only be called if the required target features are available
 
-error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:47:18
+error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe function or block
+  --> $DIR/safe-calls.rs:65:18
    |
 LL | const name: () = sse2();
    |                  ^^^^^^ call to function with `#[target_feature]`
diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.nll.stderr b/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.nll.stderr
index b06ebf70477..057146e7cb0 100644
--- a/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.nll.stderr
+++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.nll.stderr
@@ -6,6 +6,11 @@ LL |     fn a(self: Pin<&Foo>, f: &Foo) -> &Foo { f }
    |                    |         |
    |                    |         let's call the lifetime of this reference `'1`
    |                    let's call the lifetime of this reference `'2`
+   |
+help: consider introducing a named lifetime parameter and update trait if needed
+   |
+LL |     fn a<'a>(self: Pin<&'a Foo>, f: &'a Foo) -> &Foo { f }
+   |         ++++            ++           ++
 
 error: lifetime may not live long enough
   --> $DIR/arbitrary_self_types_pin_lifetime_mismatch.rs:14:69
@@ -15,6 +20,11 @@ LL |     fn c(self: Pin<&Self>, f: &Foo, g: &Foo) -> (Pin<&Foo>, &Foo) { (self,
    |                    |          |
    |                    |          let's call the lifetime of this reference `'1`
    |                    let's call the lifetime of this reference `'2`
+   |
+help: consider introducing a named lifetime parameter and update trait if needed
+   |
+LL |     fn c<'a>(self: Pin<&'a Self>, f: &'a Foo, g: &Foo) -> (Pin<&Foo>, &Foo) { (self, f) }
+   |         ++++            ++            ++
 
 error: lifetime may not live long enough
   --> $DIR/arbitrary_self_types_pin_lifetime_mismatch.rs:21:58
diff --git a/src/test/ui/self/elision/lt-ref-self.nll.stderr b/src/test/ui/self/elision/lt-ref-self.nll.stderr
index 1934207527b..2e26c703b65 100644
--- a/src/test/ui/self/elision/lt-ref-self.nll.stderr
+++ b/src/test/ui/self/elision/lt-ref-self.nll.stderr
@@ -7,6 +7,11 @@ LL |     fn ref_self(&self, f: &u32) -> &u32 {
    |                 let's call the lifetime of this reference `'2`
 LL |         f
    |         ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+   |
+help: consider introducing a named lifetime parameter and update trait if needed
+   |
+LL |     fn ref_self<'a>(&'a self, f: &'a u32) -> &u32 {
+   |                ++++  ++           ++
 
 error: lifetime may not live long enough
   --> $DIR/lt-ref-self.rs:23:9
@@ -17,6 +22,11 @@ LL |     fn ref_Self(self: &Self, f: &u32) -> &u32 {
    |                       let's call the lifetime of this reference `'2`
 LL |         f
    |         ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+   |
+help: consider introducing a named lifetime parameter and update trait if needed
+   |
+LL |     fn ref_Self<'a>(self: &'a Self, f: &'a u32) -> &u32 {
+   |                ++++        ++           ++
 
 error: lifetime may not live long enough
   --> $DIR/lt-ref-self.rs:29:9
@@ -27,6 +37,11 @@ LL |     fn box_ref_Self(self: Box<&Self>, f: &u32) -> &u32 {
    |                               let's call the lifetime of this reference `'2`
 LL |         f
    |         ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+   |
+help: consider introducing a named lifetime parameter and update trait if needed
+   |
+LL |     fn box_ref_Self<'a>(self: Box<&'a Self>, f: &'a u32) -> &u32 {
+   |                    ++++            ++            ++
 
 error: lifetime may not live long enough
   --> $DIR/lt-ref-self.rs:35:9
@@ -37,6 +52,11 @@ LL |     fn pin_ref_Self(self: Pin<&Self>, f: &u32) -> &u32 {
    |                               let's call the lifetime of this reference `'2`
 LL |         f
    |         ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+   |
+help: consider introducing a named lifetime parameter and update trait if needed
+   |
+LL |     fn pin_ref_Self<'a>(self: Pin<&'a Self>, f: &'a u32) -> &u32 {
+   |                    ++++            ++            ++
 
 error: lifetime may not live long enough
   --> $DIR/lt-ref-self.rs:41:9
@@ -47,6 +67,11 @@ LL |     fn box_box_ref_Self(self: Box<Box<&Self>>, f: &u32) -> &u32 {
    |                                       let's call the lifetime of this reference `'2`
 LL |         f
    |         ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+   |
+help: consider introducing a named lifetime parameter and update trait if needed
+   |
+LL |     fn box_box_ref_Self<'a>(self: Box<Box<&'a Self>>, f: &'a u32) -> &u32 {
+   |                        ++++                ++             ++
 
 error: lifetime may not live long enough
   --> $DIR/lt-ref-self.rs:47:9
@@ -57,6 +82,11 @@ LL |     fn box_pin_Self(self: Box<Pin<&Self>>, f: &u32) -> &u32 {
    |                                   let's call the lifetime of this reference `'2`
 LL |         f
    |         ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+   |
+help: consider introducing a named lifetime parameter and update trait if needed
+   |
+LL |     fn box_pin_Self<'a>(self: Box<Pin<&'a Self>>, f: &'a u32) -> &u32 {
+   |                    ++++                ++             ++
 
 error: aborting due to 6 previous errors
 
diff --git a/src/test/ui/self/elision/ref-mut-self.nll.stderr b/src/test/ui/self/elision/ref-mut-self.nll.stderr
index f1f4d341b2b..fd4ecae3cfe 100644
--- a/src/test/ui/self/elision/ref-mut-self.nll.stderr
+++ b/src/test/ui/self/elision/ref-mut-self.nll.stderr
@@ -7,6 +7,11 @@ LL |     fn ref_self(&mut self, f: &u32) -> &u32 {
    |                 let's call the lifetime of this reference `'2`
 LL |         f
    |         ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+   |
+help: consider introducing a named lifetime parameter and update trait if needed
+   |
+LL |     fn ref_self<'a>(&'a mut self, f: &'a u32) -> &u32 {
+   |                ++++  ++               ++
 
 error: lifetime may not live long enough
   --> $DIR/ref-mut-self.rs:23:9
@@ -17,6 +22,11 @@ LL |     fn ref_Self(self: &mut Self, f: &u32) -> &u32 {
    |                       let's call the lifetime of this reference `'2`
 LL |         f
    |         ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+   |
+help: consider introducing a named lifetime parameter and update trait if needed
+   |
+LL |     fn ref_Self<'a>(self: &'a mut Self, f: &'a u32) -> &u32 {
+   |                ++++        ++               ++
 
 error: lifetime may not live long enough
   --> $DIR/ref-mut-self.rs:29:9
@@ -27,6 +37,11 @@ LL |     fn box_ref_Self(self: Box<&mut Self>, f: &u32) -> &u32 {
    |                               let's call the lifetime of this reference `'2`
 LL |         f
    |         ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+   |
+help: consider introducing a named lifetime parameter and update trait if needed
+   |
+LL |     fn box_ref_Self<'a>(self: Box<&'a mut Self>, f: &'a u32) -> &u32 {
+   |                    ++++            ++                ++
 
 error: lifetime may not live long enough
   --> $DIR/ref-mut-self.rs:35:9
@@ -37,6 +52,11 @@ LL |     fn pin_ref_Self(self: Pin<&mut Self>, f: &u32) -> &u32 {
    |                               let's call the lifetime of this reference `'2`
 LL |         f
    |         ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+   |
+help: consider introducing a named lifetime parameter and update trait if needed
+   |
+LL |     fn pin_ref_Self<'a>(self: Pin<&'a mut Self>, f: &'a u32) -> &u32 {
+   |                    ++++            ++                ++
 
 error: lifetime may not live long enough
   --> $DIR/ref-mut-self.rs:41:9
@@ -47,6 +67,11 @@ LL |     fn box_box_ref_Self(self: Box<Box<&mut Self>>, f: &u32) -> &u32 {
    |                                       let's call the lifetime of this reference `'2`
 LL |         f
    |         ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+   |
+help: consider introducing a named lifetime parameter and update trait if needed
+   |
+LL |     fn box_box_ref_Self<'a>(self: Box<Box<&'a mut Self>>, f: &'a u32) -> &u32 {
+   |                        ++++                ++                 ++
 
 error: lifetime may not live long enough
   --> $DIR/ref-mut-self.rs:47:9
@@ -57,6 +82,11 @@ LL |     fn box_pin_ref_Self(self: Box<Pin<&mut Self>>, f: &u32) -> &u32 {
    |                                       let's call the lifetime of this reference `'2`
 LL |         f
    |         ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+   |
+help: consider introducing a named lifetime parameter and update trait if needed
+   |
+LL |     fn box_pin_ref_Self<'a>(self: Box<Pin<&'a mut Self>>, f: &'a u32) -> &u32 {
+   |                        ++++                ++                 ++
 
 error: aborting due to 6 previous errors
 
diff --git a/src/test/ui/self/elision/ref-mut-struct.nll.stderr b/src/test/ui/self/elision/ref-mut-struct.nll.stderr
index de7eb02d7a7..ede790c0611 100644
--- a/src/test/ui/self/elision/ref-mut-struct.nll.stderr
+++ b/src/test/ui/self/elision/ref-mut-struct.nll.stderr
@@ -7,6 +7,11 @@ LL |     fn ref_Struct(self: &mut Struct, f: &u32) -> &u32 {
    |                         let's call the lifetime of this reference `'2`
 LL |         f
    |         ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+   |
+help: consider introducing a named lifetime parameter and update trait if needed
+   |
+LL |     fn ref_Struct<'a>(self: &'a mut Struct, f: &'a u32) -> &u32 {
+   |                  ++++        ++                 ++
 
 error: lifetime may not live long enough
   --> $DIR/ref-mut-struct.rs:21:9
@@ -17,6 +22,11 @@ LL |     fn box_ref_Struct(self: Box<&mut Struct>, f: &u32) -> &u32 {
    |                                 let's call the lifetime of this reference `'2`
 LL |         f
    |         ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+   |
+help: consider introducing a named lifetime parameter and update trait if needed
+   |
+LL |     fn box_ref_Struct<'a>(self: Box<&'a mut Struct>, f: &'a u32) -> &u32 {
+   |                      ++++            ++                  ++
 
 error: lifetime may not live long enough
   --> $DIR/ref-mut-struct.rs:27:9
@@ -27,6 +37,11 @@ LL |     fn pin_ref_Struct(self: Pin<&mut Struct>, f: &u32) -> &u32 {
    |                                 let's call the lifetime of this reference `'2`
 LL |         f
    |         ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+   |
+help: consider introducing a named lifetime parameter and update trait if needed
+   |
+LL |     fn pin_ref_Struct<'a>(self: Pin<&'a mut Struct>, f: &'a u32) -> &u32 {
+   |                      ++++            ++                  ++
 
 error: lifetime may not live long enough
   --> $DIR/ref-mut-struct.rs:33:9
@@ -37,6 +52,11 @@ LL |     fn box_box_ref_Struct(self: Box<Box<&mut Struct>>, f: &u32) -> &u32 {
    |                                         let's call the lifetime of this reference `'2`
 LL |         f
    |         ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+   |
+help: consider introducing a named lifetime parameter and update trait if needed
+   |
+LL |     fn box_box_ref_Struct<'a>(self: Box<Box<&'a mut Struct>>, f: &'a u32) -> &u32 {
+   |                          ++++                ++                   ++
 
 error: lifetime may not live long enough
   --> $DIR/ref-mut-struct.rs:39:9
@@ -47,6 +67,11 @@ LL |     fn box_pin_ref_Struct(self: Box<Pin<&mut Struct>>, f: &u32) -> &u32 {
    |                                         let's call the lifetime of this reference `'2`
 LL |         f
    |         ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+   |
+help: consider introducing a named lifetime parameter and update trait if needed
+   |
+LL |     fn box_pin_ref_Struct<'a>(self: Box<Pin<&'a mut Struct>>, f: &'a u32) -> &u32 {
+   |                          ++++                ++                   ++
 
 error: aborting due to 5 previous errors
 
diff --git a/src/test/ui/self/elision/ref-self.nll.stderr b/src/test/ui/self/elision/ref-self.nll.stderr
index f2b7b0ad019..c0efc35fa6c 100644
--- a/src/test/ui/self/elision/ref-self.nll.stderr
+++ b/src/test/ui/self/elision/ref-self.nll.stderr
@@ -7,6 +7,11 @@ LL |     fn ref_self(&self, f: &u32) -> &u32 {
    |                 let's call the lifetime of this reference `'2`
 LL |         f
    |         ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+   |
+help: consider introducing a named lifetime parameter and update trait if needed
+   |
+LL |     fn ref_self<'a>(&'a self, f: &'a u32) -> &u32 {
+   |                ++++  ++           ++
 
 error: lifetime may not live long enough
   --> $DIR/ref-self.rs:33:9
@@ -17,6 +22,11 @@ LL |     fn ref_Self(self: &Self, f: &u32) -> &u32 {
    |                       let's call the lifetime of this reference `'2`
 LL |         f
    |         ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+   |
+help: consider introducing a named lifetime parameter and update trait if needed
+   |
+LL |     fn ref_Self<'a>(self: &'a Self, f: &'a u32) -> &u32 {
+   |                ++++        ++           ++
 
 error: lifetime may not live long enough
   --> $DIR/ref-self.rs:39:9
@@ -27,6 +37,11 @@ LL |     fn box_ref_Self(self: Box<&Self>, f: &u32) -> &u32 {
    |                               let's call the lifetime of this reference `'2`
 LL |         f
    |         ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+   |
+help: consider introducing a named lifetime parameter and update trait if needed
+   |
+LL |     fn box_ref_Self<'a>(self: Box<&'a Self>, f: &'a u32) -> &u32 {
+   |                    ++++            ++            ++
 
 error: lifetime may not live long enough
   --> $DIR/ref-self.rs:45:9
@@ -37,6 +52,11 @@ LL |     fn pin_ref_Self(self: Pin<&Self>, f: &u32) -> &u32 {
    |                               let's call the lifetime of this reference `'2`
 LL |         f
    |         ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+   |
+help: consider introducing a named lifetime parameter and update trait if needed
+   |
+LL |     fn pin_ref_Self<'a>(self: Pin<&'a Self>, f: &'a u32) -> &u32 {
+   |                    ++++            ++            ++
 
 error: lifetime may not live long enough
   --> $DIR/ref-self.rs:51:9
@@ -47,6 +67,11 @@ LL |     fn box_box_ref_Self(self: Box<Box<&Self>>, f: &u32) -> &u32 {
    |                                       let's call the lifetime of this reference `'2`
 LL |         f
    |         ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+   |
+help: consider introducing a named lifetime parameter and update trait if needed
+   |
+LL |     fn box_box_ref_Self<'a>(self: Box<Box<&'a Self>>, f: &'a u32) -> &u32 {
+   |                        ++++                ++             ++
 
 error: lifetime may not live long enough
   --> $DIR/ref-self.rs:57:9
@@ -57,6 +82,11 @@ LL |     fn box_pin_ref_Self(self: Box<Pin<&Self>>, f: &u32) -> &u32 {
    |                                       let's call the lifetime of this reference `'2`
 LL |         f
    |         ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+   |
+help: consider introducing a named lifetime parameter and update trait if needed
+   |
+LL |     fn box_pin_ref_Self<'a>(self: Box<Pin<&'a Self>>, f: &'a u32) -> &u32 {
+   |                        ++++                ++             ++
 
 error: lifetime may not live long enough
   --> $DIR/ref-self.rs:63:9
@@ -67,6 +97,11 @@ LL |     fn wrap_ref_Self_Self(self: Wrap<&Self, Self>, f: &u8) -> &u8 {
    |                                      let's call the lifetime of this reference `'2`
 LL |         f
    |         ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+   |
+help: consider introducing a named lifetime parameter and update trait if needed
+   |
+LL |     fn wrap_ref_Self_Self<'a>(self: Wrap<&'a Self, Self>, f: &'a u8) -> &u8 {
+   |                          ++++             ++                  ++
 
 error: aborting due to 7 previous errors
 
diff --git a/src/test/ui/self/elision/ref-struct.nll.stderr b/src/test/ui/self/elision/ref-struct.nll.stderr
index 70453b0ddfc..226923f59ff 100644
--- a/src/test/ui/self/elision/ref-struct.nll.stderr
+++ b/src/test/ui/self/elision/ref-struct.nll.stderr
@@ -7,6 +7,11 @@ LL |     fn ref_Struct(self: &Struct, f: &u32) -> &u32 {
    |                         let's call the lifetime of this reference `'2`
 LL |         f
    |         ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+   |
+help: consider introducing a named lifetime parameter and update trait if needed
+   |
+LL |     fn ref_Struct<'a>(self: &'a Struct, f: &'a u32) -> &u32 {
+   |                  ++++        ++             ++
 
 error: lifetime may not live long enough
   --> $DIR/ref-struct.rs:21:9
@@ -17,6 +22,11 @@ LL |     fn box_ref_Struct(self: Box<&Struct>, f: &u32) -> &u32 {
    |                                 let's call the lifetime of this reference `'2`
 LL |         f
    |         ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+   |
+help: consider introducing a named lifetime parameter and update trait if needed
+   |
+LL |     fn box_ref_Struct<'a>(self: Box<&'a Struct>, f: &'a u32) -> &u32 {
+   |                      ++++            ++              ++
 
 error: lifetime may not live long enough
   --> $DIR/ref-struct.rs:27:9
@@ -27,6 +37,11 @@ LL |     fn pin_ref_Struct(self: Pin<&Struct>, f: &u32) -> &u32 {
    |                                 let's call the lifetime of this reference `'2`
 LL |         f
    |         ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+   |
+help: consider introducing a named lifetime parameter and update trait if needed
+   |
+LL |     fn pin_ref_Struct<'a>(self: Pin<&'a Struct>, f: &'a u32) -> &u32 {
+   |                      ++++            ++              ++
 
 error: lifetime may not live long enough
   --> $DIR/ref-struct.rs:33:9
@@ -37,6 +52,11 @@ LL |     fn box_box_ref_Struct(self: Box<Box<&Struct>>, f: &u32) -> &u32 {
    |                                         let's call the lifetime of this reference `'2`
 LL |         f
    |         ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+   |
+help: consider introducing a named lifetime parameter and update trait if needed
+   |
+LL |     fn box_box_ref_Struct<'a>(self: Box<Box<&'a Struct>>, f: &'a u32) -> &u32 {
+   |                          ++++                ++               ++
 
 error: lifetime may not live long enough
   --> $DIR/ref-struct.rs:39:9
@@ -47,6 +67,11 @@ LL |     fn box_pin_Struct(self: Box<Pin<&Struct>>, f: &u32) -> &u32 {
    |                                     let's call the lifetime of this reference `'2`
 LL |         f
    |         ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+   |
+help: consider introducing a named lifetime parameter and update trait if needed
+   |
+LL |     fn box_pin_Struct<'a>(self: Box<Pin<&'a Struct>>, f: &'a u32) -> &u32 {
+   |                      ++++                ++               ++
 
 error: aborting due to 5 previous errors
 
diff --git a/src/test/ui/span/issue-39698.stderr b/src/test/ui/span/issue-39698.stderr
index 445df90d395..25c35fd5479 100644
--- a/src/test/ui/span/issue-39698.stderr
+++ b/src/test/ui/span/issue-39698.stderr
@@ -1,3 +1,13 @@
+error[E0408]: variable `d` is not bound in all patterns
+  --> $DIR/issue-39698.rs:10:37
+   |
+LL |         T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?}", a); }
+   |                  -          -       ^^^^^^^^   ^^^^^^^^ pattern doesn't bind `d`
+   |                  |          |       |
+   |                  |          |       pattern doesn't bind `d`
+   |                  |          variable not in all patterns
+   |                  variable not in all patterns
+
 error[E0408]: variable `a` is not bound in all patterns
   --> $DIR/issue-39698.rs:10:23
    |
@@ -28,16 +38,6 @@ LL |         T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?}
    |         |             pattern doesn't bind `c`
    |         pattern doesn't bind `c`
 
-error[E0408]: variable `d` is not bound in all patterns
-  --> $DIR/issue-39698.rs:10:37
-   |
-LL |         T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?}", a); }
-   |                  -          -       ^^^^^^^^   ^^^^^^^^ pattern doesn't bind `d`
-   |                  |          |       |
-   |                  |          |       pattern doesn't bind `d`
-   |                  |          variable not in all patterns
-   |                  variable not in all patterns
-
 error: aborting due to 4 previous errors
 
 For more information about this error, try `rustc --explain E0408`.
diff --git a/src/test/ui/static/static-lifetime-bound.stderr b/src/test/ui/static/static-lifetime-bound.stderr
index 79d9506619e..ef07a89315f 100644
--- a/src/test/ui/static/static-lifetime-bound.stderr
+++ b/src/test/ui/static/static-lifetime-bound.stderr
@@ -2,7 +2,7 @@ warning: unnecessary lifetime parameter `'a`
   --> $DIR/static-lifetime-bound.rs:1:6
    |
 LL | fn f<'a: 'static>(_: &'a i32) {}
-   |      ^^^^^^^^^^^
+   |      ^^
    |
    = help: you can use the `'static` lifetime directly, in place of `'a`
 
diff --git a/src/test/ui/issues/issue-17233.rs b/src/test/ui/statics/issue-17233.rs
index 54a12fdf8e8..54a12fdf8e8 100644
--- a/src/test/ui/issues/issue-17233.rs
+++ b/src/test/ui/statics/issue-17233.rs
diff --git a/src/test/ui/statics/uninhabited-static.rs b/src/test/ui/statics/uninhabited-static.rs
index d5645474891..f5c6f444317 100644
--- a/src/test/ui/statics/uninhabited-static.rs
+++ b/src/test/ui/statics/uninhabited-static.rs
@@ -11,11 +11,11 @@ extern {
 
 static VOID2: Void = unsafe { std::mem::transmute(()) }; //~ ERROR static of uninhabited type
 //~| WARN: previously accepted
-//~| ERROR undefined behavior to use this value
+//~| ERROR could not evaluate static initializer
 //~| WARN: type `Void` does not permit zero-initialization
 static NEVER2: Void = unsafe { std::mem::transmute(()) }; //~ ERROR static of uninhabited type
 //~| WARN: previously accepted
-//~| ERROR undefined behavior to use this value
+//~| ERROR could not evaluate static initializer
 //~| WARN: type `Void` does not permit zero-initialization
 
 fn main() {}
diff --git a/src/test/ui/statics/uninhabited-static.stderr b/src/test/ui/statics/uninhabited-static.stderr
index c38cf10d6e6..1e0becb7d5a 100644
--- a/src/test/ui/statics/uninhabited-static.stderr
+++ b/src/test/ui/statics/uninhabited-static.stderr
@@ -43,23 +43,17 @@ LL | static NEVER2: Void = unsafe { std::mem::transmute(()) };
    = note: for more information, see issue #74840 <https://github.com/rust-lang/rust/issues/74840>
    = note: uninhabited statics cannot be initialized, and any access would be an immediate error
 
-error[E0080]: it is undefined behavior to use this value
-  --> $DIR/uninhabited-static.rs:12:1
+error[E0080]: could not evaluate static initializer
+  --> $DIR/uninhabited-static.rs:12:31
    |
 LL | static VOID2: Void = unsafe { std::mem::transmute(()) };
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of uninhabited type Void
-   |
-   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
-   = note: the raw bytes of the constant (size: 0, align: 1) {}
+   |                               ^^^^^^^^^^^^^^^^^^^^^^^ transmuting to uninhabited type
 
-error[E0080]: it is undefined behavior to use this value
-  --> $DIR/uninhabited-static.rs:16:1
+error[E0080]: could not evaluate static initializer
+  --> $DIR/uninhabited-static.rs:16:32
    |
 LL | static NEVER2: Void = unsafe { std::mem::transmute(()) };
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of uninhabited type Void
-   |
-   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
-   = note: the raw bytes of the constant (size: 0, align: 1) {}
+   |                                ^^^^^^^^^^^^^^^^^^^^^^^ transmuting to uninhabited type
 
 warning: the type `Void` does not permit zero-initialization
   --> $DIR/uninhabited-static.rs:12:31
diff --git a/src/test/ui/suggestions/bound-suggestions.fixed b/src/test/ui/suggestions/bound-suggestions.fixed
index 31fdd2b67e2..17a019c6984 100644
--- a/src/test/ui/suggestions/bound-suggestions.fixed
+++ b/src/test/ui/suggestions/bound-suggestions.fixed
@@ -35,7 +35,7 @@ fn test_one_bound_where<X>(x: X) where X: Sized + std::fmt::Debug {
 }
 
 #[allow(dead_code)]
-fn test_many_bounds_where<X>(x: X) where X: Sized, X: Sized, X: std::fmt::Debug {
+fn test_many_bounds_where<X>(x: X) where X: Sized + std::fmt::Debug, X: Sized {
     println!("{:?}", x);
     //~^ ERROR doesn't implement
 }
diff --git a/src/test/ui/suggestions/bound-suggestions.stderr b/src/test/ui/suggestions/bound-suggestions.stderr
index 04f233a1e87..e5e19444d24 100644
--- a/src/test/ui/suggestions/bound-suggestions.stderr
+++ b/src/test/ui/suggestions/bound-suggestions.stderr
@@ -65,10 +65,10 @@ LL |     println!("{:?}", x);
    |                      ^ `X` cannot be formatted using `{:?}` because it doesn't implement `Debug`
    |
    = note: this error originates in the macro `$crate::format_args_nl` (in Nightly builds, run with -Z macro-backtrace for more info)
-help: consider further restricting type parameter `X`
+help: consider further restricting this bound
    |
-LL | fn test_many_bounds_where<X>(x: X) where X: Sized, X: Sized, X: std::fmt::Debug {
-   |                                                            ++++++++++++++++++++
+LL | fn test_many_bounds_where<X>(x: X) where X: Sized + std::fmt::Debug, X: Sized {
+   |                                                   +++++++++++++++++
 
 error[E0277]: the size for values of type `Self` cannot be known at compilation time
   --> $DIR/bound-suggestions.rs:44:46
diff --git a/src/test/ui/suggestions/field-has-method.rs b/src/test/ui/suggestions/field-has-method.rs
new file mode 100644
index 00000000000..980000151e2
--- /dev/null
+++ b/src/test/ui/suggestions/field-has-method.rs
@@ -0,0 +1,23 @@
+struct Kind;
+
+struct Ty {
+    kind: Kind,
+}
+
+impl Ty {
+    fn kind(&self) -> Kind {
+        todo!()
+    }
+}
+
+struct InferOk<T> {
+    value: T,
+    predicates: Vec<()>,
+}
+
+fn foo(i: InferOk<Ty>) {
+    let k = i.kind();
+    //~^ no method named `kind` found for struct `InferOk` in the current scope
+}
+
+fn main() {}
diff --git a/src/test/ui/suggestions/field-has-method.stderr b/src/test/ui/suggestions/field-has-method.stderr
new file mode 100644
index 00000000000..3a57436f200
--- /dev/null
+++ b/src/test/ui/suggestions/field-has-method.stderr
@@ -0,0 +1,17 @@
+error[E0599]: no method named `kind` found for struct `InferOk` in the current scope
+  --> $DIR/field-has-method.rs:19:15
+   |
+LL | struct InferOk<T> {
+   | ----------------- method `kind` not found for this
+...
+LL |     let k = i.kind();
+   |               ^^^^ method not found in `InferOk<Ty>`
+   |
+help: one of the expressions' fields has a method of the same name
+   |
+LL |     let k = i.value.kind();
+   |               ++++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0599`.
diff --git a/src/test/ui/suggestions/impl-trait-return-trailing-semicolon.rs b/src/test/ui/suggestions/impl-trait-return-trailing-semicolon.rs
index 5a17c108ccc..cd3741356f4 100644
--- a/src/test/ui/suggestions/impl-trait-return-trailing-semicolon.rs
+++ b/src/test/ui/suggestions/impl-trait-return-trailing-semicolon.rs
@@ -8,16 +8,12 @@ impl Bar for Qux {}
 
 fn foo() -> impl Bar {
     //~^ ERROR the trait bound `(): Bar` is not satisfied
-    //~| ERROR the trait bound `(): Bar` is not satisfied
-    //~| HELP the following other types implement trait `Bar`:
     5;
     //~^ HELP remove this semicolon
 }
 
 fn bar() -> impl Bar {
     //~^ ERROR the trait bound `(): Bar` is not satisfied
-    //~| ERROR the trait bound `(): Bar` is not satisfied
-    //~| HELP the following other types implement trait `Bar`:
     //~| HELP the following other types implement trait `Bar`:
     "";
 }
diff --git a/src/test/ui/suggestions/impl-trait-return-trailing-semicolon.stderr b/src/test/ui/suggestions/impl-trait-return-trailing-semicolon.stderr
index 43f8b7c66f0..e74c2c4214f 100644
--- a/src/test/ui/suggestions/impl-trait-return-trailing-semicolon.stderr
+++ b/src/test/ui/suggestions/impl-trait-return-trailing-semicolon.stderr
@@ -3,31 +3,14 @@ error[E0277]: the trait bound `(): Bar` is not satisfied
    |
 LL | fn foo() -> impl Bar {
    |             ^^^^^^^^ the trait `Bar` is not implemented for `()`
-...
+LL |
 LL |     5;
    |     -- help: remove this semicolon
    |     |
    |     this expression has type `{integer}`, which implements `Bar`
 
 error[E0277]: the trait bound `(): Bar` is not satisfied
-  --> $DIR/impl-trait-return-trailing-semicolon.rs:9:22
-   |
-LL |   fn foo() -> impl Bar {
-   |  ______________________^
-LL | |
-LL | |
-LL | |
-LL | |     5;
-LL | |
-LL | | }
-   | |_^ the trait `Bar` is not implemented for `()`
-   |
-   = help: the following other types implement trait `Bar`:
-             Qux
-             i32
-
-error[E0277]: the trait bound `(): Bar` is not satisfied
-  --> $DIR/impl-trait-return-trailing-semicolon.rs:17:13
+  --> $DIR/impl-trait-return-trailing-semicolon.rs:15:13
    |
 LL | fn bar() -> impl Bar {
    |             ^^^^^^^^ the trait `Bar` is not implemented for `()`
@@ -36,23 +19,6 @@ LL | fn bar() -> impl Bar {
              Qux
              i32
 
-error[E0277]: the trait bound `(): Bar` is not satisfied
-  --> $DIR/impl-trait-return-trailing-semicolon.rs:17:22
-   |
-LL |   fn bar() -> impl Bar {
-   |  ______________________^
-LL | |
-LL | |
-LL | |
-LL | |
-LL | |     "";
-LL | | }
-   | |_^ the trait `Bar` is not implemented for `()`
-   |
-   = help: the following other types implement trait `Bar`:
-             Qux
-             i32
-
-error: aborting due to 4 previous errors
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/suggestions/impl-trait-with-missing-trait-bounds-in-arg.stderr b/src/test/ui/suggestions/impl-trait-with-missing-trait-bounds-in-arg.stderr
index 6255df06efb..8ec7b7bf496 100644
--- a/src/test/ui/suggestions/impl-trait-with-missing-trait-bounds-in-arg.stderr
+++ b/src/test/ui/suggestions/impl-trait-with-missing-trait-bounds-in-arg.stderr
@@ -8,7 +8,7 @@ LL |     foo.hello();
 help: the following trait defines an item `hello`, perhaps you need to restrict type parameter `impl Foo` with it:
    |
 LL | fn test(foo: impl Foo + Bar) {
-   |              ~~~~~~~~~~~~~~
+   |                       +++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/suggestions/invalid-bin-op.stderr b/src/test/ui/suggestions/invalid-bin-op.stderr
index d18c24e53d0..fe5e2b5816f 100644
--- a/src/test/ui/suggestions/invalid-bin-op.stderr
+++ b/src/test/ui/suggestions/invalid-bin-op.stderr
@@ -11,11 +11,14 @@ note: an implementation of `PartialEq<_>` might be missing for `S<T>`
    |
 LL | struct S<T>(T);
    | ^^^^^^^^^^^^^^^ must implement `PartialEq<_>`
-   = note: the trait `std::cmp::PartialEq` is not implemented for `S<T>`
 help: consider annotating `S<T>` with `#[derive(PartialEq)]`
    |
 LL | #[derive(PartialEq)]
    |
+help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
+   |
+LL | pub fn foo<T>(s: S<T>, t: S<T>) where S<T>: PartialEq {
+   |                                 +++++++++++++++++++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/suggestions/issue-21673.stderr b/src/test/ui/suggestions/issue-21673.stderr
index eda29f876d8..0a4aaa61bc7 100644
--- a/src/test/ui/suggestions/issue-21673.stderr
+++ b/src/test/ui/suggestions/issue-21673.stderr
@@ -7,8 +7,8 @@ LL |     x.method()
    = help: items from traits can only be used if the type parameter is bounded by the trait
 help: the following trait defines an item `method`, perhaps you need to restrict type parameter `T` with it:
    |
-LL | fn call_method<T: Foo + std::fmt::Debug>(x: &T) {
-   |                ~~~~~~~~
+LL | fn call_method<T: std::fmt::Debug + Foo>(x: &T) {
+   |                                   +++++
 
 error[E0599]: no method named `method` found for type parameter `T` in the current scope
   --> $DIR/issue-21673.rs:10:7
@@ -20,7 +20,7 @@ LL |     x.method()
 help: the following trait defines an item `method`, perhaps you need to restrict type parameter `T` with it:
    |
 LL | fn call_method_2<T: Foo>(x: T) {
-   |                  ~~~~~~
+   |                   +++++
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/issues/issue-53692.rs b/src/test/ui/suggestions/issue-53692.rs
index 30f344e4282..30f344e4282 100644
--- a/src/test/ui/issues/issue-53692.rs
+++ b/src/test/ui/suggestions/issue-53692.rs
diff --git a/src/test/ui/issues/issue-53692.stderr b/src/test/ui/suggestions/issue-53692.stderr
index 09c78da54bc..09c78da54bc 100644
--- a/src/test/ui/issues/issue-53692.stderr
+++ b/src/test/ui/suggestions/issue-53692.stderr
diff --git a/src/test/ui/suggestions/issue-81098.rs b/src/test/ui/suggestions/issue-81098.rs
index 7ca7380a7be..a601b5866f4 100644
--- a/src/test/ui/suggestions/issue-81098.rs
+++ b/src/test/ui/suggestions/issue-81098.rs
@@ -1,14 +1,12 @@
 // Don't suggest removing a semicolon if the last statement isn't an expression with semicolon
 // (#81098)
 fn wat() -> impl core::fmt::Display { //~ ERROR: `()` doesn't implement `std::fmt::Display`
-    //~^ ERROR: `()` doesn't implement `std::fmt::Display`
     fn why() {}
 }
 
 // Do it if the last statement is an expression with semicolon
 // (#54771)
 fn ok() -> impl core::fmt::Display { //~ ERROR: `()` doesn't implement `std::fmt::Display`
-    //~^ ERROR: `()` doesn't implement `std::fmt::Display`
     1;
 }
 
diff --git a/src/test/ui/suggestions/issue-81098.stderr b/src/test/ui/suggestions/issue-81098.stderr
index 8665f2e70a8..4dc47a20282 100644
--- a/src/test/ui/suggestions/issue-81098.stderr
+++ b/src/test/ui/suggestions/issue-81098.stderr
@@ -8,24 +8,10 @@ LL | fn wat() -> impl core::fmt::Display {
    = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
 
 error[E0277]: `()` doesn't implement `std::fmt::Display`
-  --> $DIR/issue-81098.rs:3:37
-   |
-LL |   fn wat() -> impl core::fmt::Display {
-   |  _____________________________________^
-LL | |
-LL | |     fn why() {}
-LL | | }
-   | |_^ `()` cannot be formatted with the default formatter
-   |
-   = help: the trait `std::fmt::Display` is not implemented for `()`
-   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
-
-error[E0277]: `()` doesn't implement `std::fmt::Display`
-  --> $DIR/issue-81098.rs:10:12
+  --> $DIR/issue-81098.rs:9:12
    |
 LL | fn ok() -> impl core::fmt::Display {
    |            ^^^^^^^^^^^^^^^^^^^^^^^ `()` cannot be formatted with the default formatter
-LL |
 LL |     1;
    |     -- help: remove this semicolon
    |     |
@@ -34,19 +20,6 @@ LL |     1;
    = help: the trait `std::fmt::Display` is not implemented for `()`
    = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
 
-error[E0277]: `()` doesn't implement `std::fmt::Display`
-  --> $DIR/issue-81098.rs:10:36
-   |
-LL |   fn ok() -> impl core::fmt::Display {
-   |  ____________________________________^
-LL | |
-LL | |     1;
-LL | | }
-   | |_^ `()` cannot be formatted with the default formatter
-   |
-   = help: the trait `std::fmt::Display` is not implemented for `()`
-   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
-
-error: aborting due to 4 previous errors
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature-2.nll.stderr b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature-2.nll.stderr
index 536494c7344..0212c2d712c 100644
--- a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature-2.nll.stderr
+++ b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature-2.nll.stderr
@@ -12,6 +12,18 @@ note: the parameter type `T` must be valid for the anonymous lifetime defined he
    |
 LL | fn func<T: Test>(foo: &Foo, t: T) {
    |                        ^^^
+note: ...so that the type `T` will meet its required lifetime bounds
+  --> $DIR/missing-lifetimes-in-signature-2.rs:20:5
+   |
+LL | /     foo.bar(move |_| {
+LL | |
+LL | |         t.test();
+LL | |     });
+   | |______^
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn func<T: Test + 'a>(foo: &Foo, t: T) {
+   |                 ++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature-2.stderr b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature-2.stderr
index adb928aa8a3..5d195e5ff32 100644
--- a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature-2.stderr
+++ b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature-2.stderr
@@ -1,8 +1,6 @@
 error[E0311]: the parameter type `T` may not live long enough
   --> $DIR/missing-lifetimes-in-signature-2.rs:20:9
    |
-LL | fn func<T: Test>(foo: &Foo, t: T) {
-   |         -- help: consider adding an explicit lifetime bound...: `T: 'a +`
 LL |     foo.bar(move |_| {
    |         ^^^
    |
@@ -21,6 +19,10 @@ note: ...that is required by this bound
    |
 LL |         F: 'a,
    |            ^^
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn func<T: Test + 'a>(foo: &Foo, t: T) {
+   |                 ++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.nll.stderr b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.nll.stderr
index 0ae629676fe..24eac64d334 100644
--- a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.nll.stderr
+++ b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.nll.stderr
@@ -1,5 +1,5 @@
 error[E0261]: use of undeclared lifetime name `'a`
-  --> $DIR/missing-lifetimes-in-signature.rs:38:11
+  --> $DIR/missing-lifetimes-in-signature.rs:37:11
    |
 LL | fn baz<G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_
    |        -  ^^ undeclared lifetime
@@ -24,7 +24,7 @@ LL | fn foo<G, T>(g: G, dest: &mut T) -> impl FnOnce() + '_
    |                                                   ++++
 
 error[E0311]: the parameter type `G` may not live long enough
-  --> $DIR/missing-lifetimes-in-signature.rs:32:5
+  --> $DIR/missing-lifetimes-in-signature.rs:31:5
    |
 LL | /     move || {
 LL | |         *dest = g.get();
@@ -36,9 +36,20 @@ note: the parameter type `G` must be valid for the anonymous lifetime defined he
    |
 LL | fn bar<G, T>(g: G, dest: &mut T) -> impl FnOnce() + '_
    |                          ^^^^^^
+note: ...so that the type `G` will meet its required lifetime bounds
+  --> $DIR/missing-lifetimes-in-signature.rs:31:5
+   |
+LL | /     move || {
+LL | |         *dest = g.get();
+LL | |     }
+   | |_____^
+help: consider adding an explicit lifetime bound...
+   |
+LL |     G: Get<T> + 'a,
+   |               ++++
 
 error[E0311]: the parameter type `G` may not live long enough
-  --> $DIR/missing-lifetimes-in-signature.rs:55:5
+  --> $DIR/missing-lifetimes-in-signature.rs:53:5
    |
 LL | /     move || {
 LL | |         *dest = g.get();
@@ -46,13 +57,24 @@ LL | |     }
    | |_____^
    |
 note: the parameter type `G` must be valid for the anonymous lifetime defined here...
-  --> $DIR/missing-lifetimes-in-signature.rs:49:34
+  --> $DIR/missing-lifetimes-in-signature.rs:48:34
    |
 LL | fn qux<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_
    |                                  ^^^^^^
+note: ...so that the type `G` will meet its required lifetime bounds
+  --> $DIR/missing-lifetimes-in-signature.rs:53:5
+   |
+LL | /     move || {
+LL | |         *dest = g.get();
+LL | |     }
+   | |_____^
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn qux<'a, G: 'a + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_
+   |                  ++++
 
 error[E0311]: the parameter type `G` may not live long enough
-  --> $DIR/missing-lifetimes-in-signature.rs:65:9
+  --> $DIR/missing-lifetimes-in-signature.rs:62:9
    |
 LL | /         move || {
 LL | |             *dest = g.get();
@@ -60,13 +82,24 @@ LL | |         }
    | |_________^
    |
 note: the parameter type `G` must be valid for the anonymous lifetime defined here...
-  --> $DIR/missing-lifetimes-in-signature.rs:62:47
+  --> $DIR/missing-lifetimes-in-signature.rs:60:47
    |
 LL |     fn qux<'b, G: Get<T> + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ {
    |                                               ^^^^^^
+note: ...so that the type `G` will meet its required lifetime bounds
+  --> $DIR/missing-lifetimes-in-signature.rs:62:9
+   |
+LL | /         move || {
+LL | |             *dest = g.get();
+LL | |         }
+   | |_________^
+help: consider adding an explicit lifetime bound...
+   |
+LL |     fn qux<'b, G: Get<T> + 'b + 'c, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ {
+   |                               ++++
 
 error[E0311]: the parameter type `G` may not live long enough
-  --> $DIR/missing-lifetimes-in-signature.rs:77:5
+  --> $DIR/missing-lifetimes-in-signature.rs:74:5
    |
 LL | /     move || {
 LL | |         *dest = g.get();
@@ -74,13 +107,24 @@ LL | |     }
    | |_____^
    |
 note: the parameter type `G` must be valid for the anonymous lifetime defined here...
-  --> $DIR/missing-lifetimes-in-signature.rs:72:34
+  --> $DIR/missing-lifetimes-in-signature.rs:69:34
    |
 LL | fn bat<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'a
    |                                  ^^^^^^
+note: ...so that the type `G` will meet its required lifetime bounds
+  --> $DIR/missing-lifetimes-in-signature.rs:74:5
+   |
+LL | /     move || {
+LL | |         *dest = g.get();
+LL | |     }
+   | |_____^
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn bat<'a, G: 'a + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'a
+   |                  ++++
 
 error[E0621]: explicit lifetime required in the type of `dest`
-  --> $DIR/missing-lifetimes-in-signature.rs:77:5
+  --> $DIR/missing-lifetimes-in-signature.rs:74:5
    |
 LL |   fn bat<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'a
    |                                    ------ help: add explicit lifetime `'a` to the type of `dest`: `&'a mut T`
@@ -91,14 +135,17 @@ LL | |     }
    | |_____^ lifetime `'a` required
 
 error[E0309]: the parameter type `G` may not live long enough
-  --> $DIR/missing-lifetimes-in-signature.rs:89:5
+  --> $DIR/missing-lifetimes-in-signature.rs:85:5
    |
 LL | /     move || {
 LL | |         *dest = g.get();
 LL | |     }
-   | |_____^
+   | |_____^ ...so that the type `G` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
    |
-   = help: consider adding an explicit lifetime bound `G: 'a`...
+LL |     G: Get<T> + 'a,
+   |               ++++
 
 error: aborting due to 8 previous errors
 
diff --git a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.rs b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.rs
index 647b343fe06..0a3e6b48163 100644
--- a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.rs
+++ b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.rs
@@ -28,7 +28,6 @@ fn bar<G, T>(g: G, dest: &mut T) -> impl FnOnce() + '_
 where
     G: Get<T>,
 {
-    //~^ ERROR the parameter type `G` may not live long enough
     move || {
         *dest = g.get();
     }
@@ -51,7 +50,6 @@ fn qux<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_
 where
     G: Get<T>,
 {
-    //~^ ERROR the parameter type `G` may not live long enough
     move || {
         *dest = g.get();
     }
@@ -61,7 +59,6 @@ where
 impl<'a> Foo {
     fn qux<'b, G: Get<T> + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ {
         //~^ ERROR the parameter type `G` may not live long enough
-        //~| ERROR the parameter type `G` may not live long enough
         move || {
             *dest = g.get();
         }
@@ -85,7 +82,6 @@ fn bak<'a, G, T>(g: G, dest: &'a mut T) -> impl FnOnce() + 'a
 where
     G: Get<T>,
 {
-    //~^ ERROR the parameter type `G` may not live long enough
     move || {
         *dest = g.get();
     }
diff --git a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr
index 6d538dfd609..ae9a020a099 100644
--- a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr
+++ b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr
@@ -1,5 +1,5 @@
 error[E0261]: use of undeclared lifetime name `'a`
-  --> $DIR/missing-lifetimes-in-signature.rs:38:11
+  --> $DIR/missing-lifetimes-in-signature.rs:37:11
    |
 LL | fn baz<G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_
    |        -  ^^ undeclared lifetime
@@ -34,172 +34,63 @@ note: the parameter type `G` must be valid for the anonymous lifetime defined he
    |
 LL | fn bar<G, T>(g: G, dest: &mut T) -> impl FnOnce() + '_
    |                          ^^^^^^
-note: ...so that the type `[closure@$DIR/missing-lifetimes-in-signature.rs:32:5: 34:6]` will meet its required lifetime bounds
+note: ...so that the type `[closure@$DIR/missing-lifetimes-in-signature.rs:31:5: 33:6]` will meet its required lifetime bounds
   --> $DIR/missing-lifetimes-in-signature.rs:26:37
    |
 LL | fn bar<G, T>(g: G, dest: &mut T) -> impl FnOnce() + '_
    |                                     ^^^^^^^^^^^^^^^^^^
 help: consider introducing an explicit lifetime bound
    |
-LL | fn bar<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'a
-   |        ~~~~~                                                   ++++
-
-error[E0311]: the parameter type `G` may not live long enough
-  --> $DIR/missing-lifetimes-in-signature.rs:30:1
-   |
-LL | / {
-LL | |
-LL | |     move || {
-LL | |         *dest = g.get();
-LL | |     }
-LL | | }
-   | |_^
-   |
-note: the parameter type `G` must be valid for the anonymous lifetime defined here...
-  --> $DIR/missing-lifetimes-in-signature.rs:26:26
-   |
-LL | fn bar<G, T>(g: G, dest: &mut T) -> impl FnOnce() + '_
-   |                          ^^^^^^
-note: ...so that the type `[closure@$DIR/missing-lifetimes-in-signature.rs:32:5: 34:6]` will meet its required lifetime bounds
-  --> $DIR/missing-lifetimes-in-signature.rs:30:1
-   |
-LL | / {
-LL | |
-LL | |     move || {
-LL | |         *dest = g.get();
-LL | |     }
-LL | | }
-   | |_^
-help: consider introducing an explicit lifetime bound
-   |
-LL ~ fn bar<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_
+LL ~ fn bar<'a, G, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'a
 LL |
 LL | where
-LL |     G: Get<T>,
-LL | {
-LL |
- ...
+LL ~     G: Get<T> + 'a,
+   |
 
 error[E0311]: the parameter type `G` may not live long enough
-  --> $DIR/missing-lifetimes-in-signature.rs:49:45
+  --> $DIR/missing-lifetimes-in-signature.rs:48:45
    |
 LL | fn qux<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_
    |                                             ^^^^^^^^^^^^^^^^^^
    |
 note: the parameter type `G` must be valid for the anonymous lifetime defined here...
-  --> $DIR/missing-lifetimes-in-signature.rs:49:34
+  --> $DIR/missing-lifetimes-in-signature.rs:48:34
    |
 LL | fn qux<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_
    |                                  ^^^^^^
-note: ...so that the type `[closure@$DIR/missing-lifetimes-in-signature.rs:55:5: 57:6]` will meet its required lifetime bounds
-  --> $DIR/missing-lifetimes-in-signature.rs:49:45
+note: ...so that the type `[closure@$DIR/missing-lifetimes-in-signature.rs:53:5: 55:6]` will meet its required lifetime bounds
+  --> $DIR/missing-lifetimes-in-signature.rs:48:45
    |
 LL | fn qux<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_
    |                                             ^^^^^^^^^^^^^^^^^^
 help: consider introducing an explicit lifetime bound
    |
-LL | fn qux<'b, 'a, G: 'b + 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'b
-   |        +++     ~~~~~~~                                                  ++++
-
-error[E0311]: the parameter type `G` may not live long enough
-  --> $DIR/missing-lifetimes-in-signature.rs:53:1
-   |
-LL | / {
-LL | |
-LL | |     move || {
-LL | |         *dest = g.get();
-LL | |     }
-LL | | }
-   | |_^
-   |
-note: the parameter type `G` must be valid for the anonymous lifetime defined here...
-  --> $DIR/missing-lifetimes-in-signature.rs:49:34
-   |
-LL | fn qux<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_
-   |                                  ^^^^^^
-note: ...so that the type `[closure@$DIR/missing-lifetimes-in-signature.rs:55:5: 57:6]` will meet its required lifetime bounds
-  --> $DIR/missing-lifetimes-in-signature.rs:53:1
-   |
-LL | / {
-LL | |
-LL | |     move || {
-LL | |         *dest = g.get();
-LL | |     }
-LL | | }
-   | |_^
-help: consider introducing an explicit lifetime bound
-   |
-LL ~ fn qux<'b, 'a, G: 'b + 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_
-LL |
-LL | where
-LL |     G: Get<T>,
-LL | {
-LL |
- ...
+LL | fn qux<'b, 'a, G: 'a + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'b
+   |        +++           ++++                                               ++++
 
 error[E0311]: the parameter type `G` may not live long enough
-  --> $DIR/missing-lifetimes-in-signature.rs:62:58
+  --> $DIR/missing-lifetimes-in-signature.rs:60:58
    |
 LL |     fn qux<'b, G: Get<T> + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ {
    |                                                          ^^^^^^^^^^^^^^^^^^
    |
 note: the parameter type `G` must be valid for the anonymous lifetime defined here...
-  --> $DIR/missing-lifetimes-in-signature.rs:62:47
+  --> $DIR/missing-lifetimes-in-signature.rs:60:47
    |
 LL |     fn qux<'b, G: Get<T> + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ {
    |                                               ^^^^^^
-note: ...so that the type `[closure@$DIR/missing-lifetimes-in-signature.rs:65:9: 67:10]` will meet its required lifetime bounds
-  --> $DIR/missing-lifetimes-in-signature.rs:62:58
+note: ...so that the type `[closure@$DIR/missing-lifetimes-in-signature.rs:62:9: 64:10]` will meet its required lifetime bounds
+  --> $DIR/missing-lifetimes-in-signature.rs:60:58
    |
 LL |     fn qux<'b, G: Get<T> + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ {
    |                                                          ^^^^^^^^^^^^^^^^^^
 help: consider introducing an explicit lifetime bound
    |
-LL |     fn qux<'c, 'b, G: 'c + Get<T> + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'c {
-   |            +++     ~~~~~~~                                                           ++++
-
-error[E0311]: the parameter type `G` may not live long enough
-  --> $DIR/missing-lifetimes-in-signature.rs:62:77
-   |
-LL |       fn qux<'b, G: Get<T> + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ {
-   |  _____________________________________________________________________________^
-LL | |
-LL | |
-LL | |         move || {
-LL | |             *dest = g.get();
-LL | |         }
-LL | |     }
-   | |_____^
-   |
-note: the parameter type `G` must be valid for the anonymous lifetime defined here...
-  --> $DIR/missing-lifetimes-in-signature.rs:62:47
-   |
-LL |     fn qux<'b, G: Get<T> + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ {
-   |                                               ^^^^^^
-note: ...so that the type `[closure@$DIR/missing-lifetimes-in-signature.rs:65:9: 67:10]` will meet its required lifetime bounds
-  --> $DIR/missing-lifetimes-in-signature.rs:62:77
-   |
-LL |       fn qux<'b, G: Get<T> + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ {
-   |  _____________________________________________________________________________^
-LL | |
-LL | |
-LL | |         move || {
-LL | |             *dest = g.get();
-LL | |         }
-LL | |     }
-   | |_____^
-help: consider introducing an explicit lifetime bound
-   |
-LL ~     fn qux<'c, 'b, G: 'c + Get<T> + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ {
-LL |
-LL |
-LL |         move || {
-LL |             *dest = g.get();
-LL |         }
- ...
+LL |     fn qux<'c, 'b, G: Get<T> + 'b + 'c, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'c {
+   |            +++                    ++++                                               ++++
 
 error[E0621]: explicit lifetime required in the type of `dest`
-  --> $DIR/missing-lifetimes-in-signature.rs:72:45
+  --> $DIR/missing-lifetimes-in-signature.rs:69:45
    |
 LL | fn bat<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'a
    |                                  ------     ^^^^^^^^^^^^^^^^^^^^^^^ lifetime `'a` required
@@ -207,28 +98,17 @@ LL | fn bat<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'a
    |                                  help: add explicit lifetime `'a` to the type of `dest`: `&'a mut T`
 
 error[E0309]: the parameter type `G` may not live long enough
-  --> $DIR/missing-lifetimes-in-signature.rs:83:44
+  --> $DIR/missing-lifetimes-in-signature.rs:80:44
    |
 LL | fn bak<'a, G, T>(g: G, dest: &'a mut T) -> impl FnOnce() + 'a
-   |            -                               ^^^^^^^^^^^^^^^^^^ ...so that the type `[closure@$DIR/missing-lifetimes-in-signature.rs:89:5: 91:6]` will meet its required lifetime bounds
-   |            |
-   |            help: consider adding an explicit lifetime bound...: `G: 'a`
-
-error[E0309]: the parameter type `G` may not live long enough
-  --> $DIR/missing-lifetimes-in-signature.rs:87:1
+   |                                            ^^^^^^^^^^^^^^^^^^ ...so that the type `[closure@$DIR/missing-lifetimes-in-signature.rs:85:5: 87:6]` will meet its required lifetime bounds
    |
-LL |   fn bak<'a, G, T>(g: G, dest: &'a mut T) -> impl FnOnce() + 'a
-   |              - help: consider adding an explicit lifetime bound...: `G: 'a`
-...
-LL | / {
-LL | |
-LL | |     move || {
-LL | |         *dest = g.get();
-LL | |     }
-LL | | }
-   | |_^ ...so that the type `[closure@$DIR/missing-lifetimes-in-signature.rs:89:5: 91:6]` will meet its required lifetime bounds
+help: consider adding an explicit lifetime bound...
+   |
+LL |     G: Get<T> + 'a,
+   |               ++++
 
-error: aborting due to 11 previous errors
+error: aborting due to 7 previous errors
 
 Some errors have detailed explanations: E0261, E0309, E0621, E0700.
 For more information about an error, try `rustc --explain E0261`.
diff --git a/src/test/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.nll.stderr b/src/test/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.nll.stderr
index 3ed3827b97d..6c65e4f0175 100644
--- a/src/test/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.nll.stderr
+++ b/src/test/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.nll.stderr
@@ -9,10 +9,14 @@ LL | |             remaining: self.0.iter(),
 LL | |         }
    | |_________^ returning this value requires that `'1` must outlive `'static`
    |
-help: to allow this `impl Trait` to capture borrowed data with lifetime `'1`, add `'_` as a bound
+help: to declare that the `impl Trait` captures data from argument `self`, you can add an explicit `'_` lifetime bound
    |
 LL |     fn iter(&self) -> impl Iterator<Item = Box<dyn Foo>> + '_ {
    |                                                          ++++
+help: to declare that the trait object captures data from argument `self`, you can add an explicit `'_` lifetime bound
+   |
+LL |     fn iter(&self) -> impl Iterator<Item = Box<dyn Foo + '_>> {
+   |                                                        ++++
 
 error: lifetime may not live long enough
   --> $DIR/trait-object-nested-in-impl-trait.rs:39:9
@@ -24,6 +28,11 @@ LL | |             current: None,
 LL | |             remaining: self.0.iter(),
 LL | |         }
    | |_________^ returning this value requires that `'1` must outlive `'static`
+   |
+help: to declare that the trait object captures data from argument `self`, you can add an explicit `'_` lifetime bound
+   |
+LL |     fn iter(&self) -> impl Iterator<Item = Box<dyn Foo + '_>> + '_ {
+   |                                                        ++++
 
 error: lifetime may not live long enough
   --> $DIR/trait-object-nested-in-impl-trait.rs:50:9
@@ -35,6 +44,11 @@ LL | |             current: None,
 LL | |             remaining: self.0.iter(),
 LL | |         }
    | |_________^ returning this value requires that `'a` must outlive `'static`
+   |
+help: to declare that the trait object captures data from argument `self`, you can add an explicit `'a` lifetime bound
+   |
+LL |     fn iter<'a>(&'a self) -> impl Iterator<Item = Box<dyn Foo + 'a>> + 'a {
+   |                                                               ++++
 
 error: lifetime may not live long enough
   --> $DIR/trait-object-nested-in-impl-trait.rs:61:9
@@ -47,10 +61,14 @@ LL | |             remaining: self.0.iter(),
 LL | |         }
    | |_________^ returning this value requires that `'a` must outlive `'static`
    |
-help: to allow this `impl Trait` to capture borrowed data with lifetime `'a`, add `'a` as a bound
+help: to declare that the `impl Trait` captures data from argument `self`, you can add an explicit `'a` lifetime bound
    |
 LL |     fn iter<'a>(&'a self) -> impl Iterator<Item = Box<dyn Foo>> + 'a {
    |                                                                 ++++
+help: to declare that the trait object captures data from argument `self`, you can add an explicit `'a` lifetime bound
+   |
+LL |     fn iter<'a>(&'a self) -> impl Iterator<Item = Box<dyn Foo + 'a>> {
+   |                                                               ++++
 
 error: aborting due to 4 previous errors
 
diff --git a/src/test/ui/suggestions/restrict-type-argument.stderr b/src/test/ui/suggestions/restrict-type-argument.stderr
index 551a7c5060f..01c2de79864 100644
--- a/src/test/ui/suggestions/restrict-type-argument.stderr
+++ b/src/test/ui/suggestions/restrict-type-argument.stderr
@@ -85,8 +85,8 @@ LL | fn is_send<T: Send>(val: T) {}
    |               ^^^^ required by this bound in `is_send`
 help: consider further restricting this bound
    |
-LL | fn use_bound_and_where<S: Sync>(val: S) where S: std::fmt::Debug + std::marker::Send {
-   |                                                                  +++++++++++++++++++
+LL | fn use_bound_and_where<S: Sync + std::marker::Send>(val: S) where S: std::fmt::Debug {
+   |                                +++++++++++++++++++
 
 error[E0277]: `S` cannot be sent between threads safely
   --> $DIR/restrict-type-argument.rs:28:13
diff --git a/src/test/ui/suggestions/suggest-impl-trait-lifetime.nll.stderr b/src/test/ui/suggestions/suggest-impl-trait-lifetime.nll.stderr
index f4eb9813c1a..cf912f4aac2 100644
--- a/src/test/ui/suggestions/suggest-impl-trait-lifetime.nll.stderr
+++ b/src/test/ui/suggestions/suggest-impl-trait-lifetime.nll.stderr
@@ -2,9 +2,12 @@ error[E0310]: the parameter type `impl Debug` may not live long enough
   --> $DIR/suggest-impl-trait-lifetime.rs:7:5
    |
 LL |     bar(d);
-   |     ^^^^^^
+   |     ^^^^^^ ...so that the type `impl Debug` will meet its required lifetime bounds
    |
-   = help: consider adding an explicit lifetime bound `impl Debug: 'static`...
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn foo(d: impl Debug + 'static) {
+   |                      +++++++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/suggestions/suggest-impl-trait-lifetime.stderr b/src/test/ui/suggestions/suggest-impl-trait-lifetime.stderr
index e4a247993c2..4a99c3a14d7 100644
--- a/src/test/ui/suggestions/suggest-impl-trait-lifetime.stderr
+++ b/src/test/ui/suggestions/suggest-impl-trait-lifetime.stderr
@@ -1,9 +1,6 @@
 error[E0310]: the parameter type `impl Debug` may not live long enough
   --> $DIR/suggest-impl-trait-lifetime.rs:7:5
    |
-LL | fn foo(d: impl Debug) {
-   |           ---------- help: consider adding an explicit lifetime bound...: `impl Debug + 'static`
-LL |
 LL |     bar(d);
    |     ^^^ ...so that the type `impl Debug` will meet its required lifetime bounds...
    |
@@ -12,6 +9,10 @@ note: ...that is required by this bound
    |
 LL | fn bar(d: impl Debug + 'static) {
    |                        ^^^^^^^
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn foo(d: impl Debug + 'static) {
+   |                      +++++++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/suggest-using-chars.rs b/src/test/ui/suggestions/suggest-using-chars.rs
index 95732881baf..95732881baf 100644
--- a/src/test/ui/suggest-using-chars.rs
+++ b/src/test/ui/suggestions/suggest-using-chars.rs
diff --git a/src/test/ui/suggest-using-chars.stderr b/src/test/ui/suggestions/suggest-using-chars.stderr
index 99bcfb08a08..99bcfb08a08 100644
--- a/src/test/ui/suggest-using-chars.stderr
+++ b/src/test/ui/suggestions/suggest-using-chars.stderr
diff --git a/src/test/ui/symbol-names/x86-stdcall.rs b/src/test/ui/symbol-names/x86-stdcall.rs
new file mode 100644
index 00000000000..9948488c0e9
--- /dev/null
+++ b/src/test/ui/symbol-names/x86-stdcall.rs
@@ -0,0 +1,13 @@
+// build-pass
+// only-x86-windows
+#![crate_type = "cdylib"]
+#![feature(abi_vectorcall)]
+
+#[no_mangle]
+extern "stdcall" fn foo(_: bool) {}
+
+#[no_mangle]
+extern "fastcall" fn bar(_: u8) {}
+
+#[no_mangle]
+extern "vectorcall" fn baz(_: u16) {}
diff --git a/src/test/ui/issues/issue-29488.rs b/src/test/ui/threads-sendsync/issue-29488.rs
index 3c9a6a80dbf..3c9a6a80dbf 100644
--- a/src/test/ui/issues/issue-29488.rs
+++ b/src/test/ui/threads-sendsync/issue-29488.rs
diff --git a/src/test/ui/threads-sendsync/issue-43733.mir.stderr b/src/test/ui/threads-sendsync/issue-43733.mir.stderr
index 8dc0e75f1af..1e21a6b37a9 100644
--- a/src/test/ui/threads-sendsync/issue-43733.mir.stderr
+++ b/src/test/ui/threads-sendsync/issue-43733.mir.stderr
@@ -1,5 +1,5 @@
 error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/issue-43733.rs:19:5
+  --> $DIR/issue-43733.rs:21:5
    |
 LL |     __KEY.get(Default::default)
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
@@ -7,7 +7,7 @@ LL |     __KEY.get(Default::default)
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
 error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/issue-43733.rs:22:42
+  --> $DIR/issue-43733.rs:26:42
    |
 LL | static FOO: std::thread::LocalKey<Foo> = std::thread::LocalKey::new(__getit);
    |                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
diff --git a/src/test/ui/threads-sendsync/issue-43733.rs b/src/test/ui/threads-sendsync/issue-43733.rs
index 9926ed09bb4..0ac6f588fb1 100644
--- a/src/test/ui/threads-sendsync/issue-43733.rs
+++ b/src/test/ui/threads-sendsync/issue-43733.rs
@@ -1,5 +1,7 @@
 // revisions: mir thir
 // [thir]compile-flags: -Z thir-unsafeck
+// normalize-stderr-test: "__FastLocalKeyInner::<T>::get" -> "$$LOCALKEYINNER::<T>::get"
+// normalize-stderr-test: "__OsLocalKeyInner::<T>::get" -> "$$LOCALKEYINNER::<T>::get"
 
 #![feature(thread_local)]
 #![feature(cfg_target_thread_local, thread_local_internals)]
@@ -16,11 +18,14 @@ static __KEY: std::thread::__FastLocalKeyInner<Foo> = std::thread::__FastLocalKe
 static __KEY: std::thread::__OsLocalKeyInner<Foo> = std::thread::__OsLocalKeyInner::new();
 
 fn __getit(_: Option<&mut Option<RefCell<String>>>) -> std::option::Option<&'static Foo> {
-    __KEY.get(Default::default) //~ ERROR call to unsafe function is unsafe
+    __KEY.get(Default::default)
+    //[mir]~^ ERROR call to unsafe function is unsafe
+    //[thir]~^^ ERROR call to unsafe function `__
 }
 
 static FOO: std::thread::LocalKey<Foo> = std::thread::LocalKey::new(__getit);
-//~^ ERROR call to unsafe function is unsafe
+//[mir]~^ ERROR call to unsafe function is unsafe
+//[thir]~^^ ERROR call to unsafe function `LocalKey::<T>::new`
 
 fn main() {
     FOO.with(|foo| println!("{}", foo.borrow()));
diff --git a/src/test/ui/threads-sendsync/issue-43733.thir.stderr b/src/test/ui/threads-sendsync/issue-43733.thir.stderr
index 8dc0e75f1af..ea7ff408048 100644
--- a/src/test/ui/threads-sendsync/issue-43733.thir.stderr
+++ b/src/test/ui/threads-sendsync/issue-43733.thir.stderr
@@ -1,13 +1,13 @@
-error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/issue-43733.rs:19:5
+error[E0133]: call to unsafe function `$LOCALKEYINNER::<T>::get` is unsafe and requires unsafe function or block
+  --> $DIR/issue-43733.rs:21:5
    |
 LL |     __KEY.get(Default::default)
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
    |
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
-error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/issue-43733.rs:22:42
+error[E0133]: call to unsafe function `LocalKey::<T>::new` is unsafe and requires unsafe function or block
+  --> $DIR/issue-43733.rs:26:42
    |
 LL | static FOO: std::thread::LocalKey<Foo> = std::thread::LocalKey::new(__getit);
    |                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
diff --git a/src/test/ui/traits/do-not-mention-type-params-by-name-in-suggestion-issue-96292.rs b/src/test/ui/traits/do-not-mention-type-params-by-name-in-suggestion-issue-96292.rs
new file mode 100644
index 00000000000..9a444be500c
--- /dev/null
+++ b/src/test/ui/traits/do-not-mention-type-params-by-name-in-suggestion-issue-96292.rs
@@ -0,0 +1,20 @@
+struct Thing<X>(X);
+
+trait Method<T> {
+    fn method(self, _: i32) -> T;
+}
+
+impl<X> Method<i32> for Thing<X> {
+    fn method(self, _: i32) -> i32 { 0 }
+}
+
+impl<X> Method<u32> for Thing<X> {
+    fn method(self, _: i32) -> u32 { 0 }
+}
+
+fn main() {
+    let thing = Thing(true);
+    thing.method(42);
+    //~^ ERROR type annotations needed
+    //~| ERROR type annotations needed
+}
diff --git a/src/test/ui/traits/do-not-mention-type-params-by-name-in-suggestion-issue-96292.stderr b/src/test/ui/traits/do-not-mention-type-params-by-name-in-suggestion-issue-96292.stderr
new file mode 100644
index 00000000000..0e52420ec43
--- /dev/null
+++ b/src/test/ui/traits/do-not-mention-type-params-by-name-in-suggestion-issue-96292.stderr
@@ -0,0 +1,37 @@
+error[E0282]: type annotations needed
+  --> $DIR/do-not-mention-type-params-by-name-in-suggestion-issue-96292.rs:17:11
+   |
+LL |     thing.method(42);
+   |     ------^^^^^^----
+   |     |     |
+   |     |     cannot infer type for type parameter `T` declared on the trait `Method`
+   |     this method call resolves to `T`
+
+error[E0283]: type annotations needed
+  --> $DIR/do-not-mention-type-params-by-name-in-suggestion-issue-96292.rs:17:11
+   |
+LL |     thing.method(42);
+   |     ------^^^^^^----
+   |     |     |
+   |     |     cannot infer type for type parameter `T` declared on the trait `Method`
+   |     this method call resolves to `T`
+   |
+note: multiple `impl`s satisfying `Thing<bool>: Method<_>` found
+  --> $DIR/do-not-mention-type-params-by-name-in-suggestion-issue-96292.rs:7:1
+   |
+LL | impl<X> Method<i32> for Thing<X> {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | impl<X> Method<u32> for Thing<X> {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: use the fully qualified path for the potential candidates
+   |
+LL |     <Thing<_> as Method<i32>>::method(thing, 42);
+   |     ++++++++++++++++++++++++++++++++++     ~
+LL |     <Thing<_> as Method<u32>>::method(thing, 42);
+   |     ++++++++++++++++++++++++++++++++++     ~
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0282, E0283.
+For more information about an error, try `rustc --explain E0282`.
diff --git a/src/test/ui/issues/issue-43132.rs b/src/test/ui/traits/issue-43132.rs
index c886f4b0a2d..c886f4b0a2d 100644
--- a/src/test/ui/issues/issue-43132.rs
+++ b/src/test/ui/traits/issue-43132.rs
diff --git a/src/test/ui/traits/issue-65284-suggest-generic-trait-bound.stderr b/src/test/ui/traits/issue-65284-suggest-generic-trait-bound.stderr
index cb1128fe5c6..35d41c62667 100644
--- a/src/test/ui/traits/issue-65284-suggest-generic-trait-bound.stderr
+++ b/src/test/ui/traits/issue-65284-suggest-generic-trait-bound.stderr
@@ -7,8 +7,8 @@ LL |     t.foo()
    = help: items from traits can only be used if the type parameter is bounded by the trait
 help: the following trait defines an item `foo`, perhaps you need to restrict type parameter `T` with it:
    |
-LL | fn do_stuff<T: Foo + Bar>(t : T) {
-   |             ~~~~~~~~
+LL | fn do_stuff<T : Bar + Foo>(t : T) {
+   |                     +++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issues/issue-71036.rs b/src/test/ui/traits/issue-71036.rs
index 3d2df6fe997..3d2df6fe997 100644
--- a/src/test/ui/issues/issue-71036.rs
+++ b/src/test/ui/traits/issue-71036.rs
diff --git a/src/test/ui/issues/issue-71036.stderr b/src/test/ui/traits/issue-71036.stderr
index db1f6946660..db1f6946660 100644
--- a/src/test/ui/issues/issue-71036.stderr
+++ b/src/test/ui/traits/issue-71036.stderr
diff --git a/src/test/ui/traits/issue-77982.stderr b/src/test/ui/traits/issue-77982.stderr
index 413225d45a6..63c1cb3791e 100644
--- a/src/test/ui/traits/issue-77982.stderr
+++ b/src/test/ui/traits/issue-77982.stderr
@@ -37,13 +37,13 @@ LL |     opts.get(opt.as_ref());
 help: use the fully qualified path for the potential candidates
    |
 LL |     opts.get(<String as AsRef<OsStr>>::as_ref(opt));
-   |              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   |              +++++++++++++++++++++++++++++++++   ~
 LL |     opts.get(<String as AsRef<Path>>::as_ref(opt));
-   |              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   |              ++++++++++++++++++++++++++++++++   ~
 LL |     opts.get(<String as AsRef<[u8]>>::as_ref(opt));
-   |              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   |              ++++++++++++++++++++++++++++++++   ~
 LL |     opts.get(<String as AsRef<str>>::as_ref(opt));
-   |              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   |              +++++++++++++++++++++++++++++++   ~
      and 4 other candidates
 
 error[E0283]: type annotations needed
diff --git a/src/test/ui/traits/issue-95898.stderr b/src/test/ui/traits/issue-95898.stderr
index d7d47905396..0a58ad4b663 100644
--- a/src/test/ui/traits/issue-95898.stderr
+++ b/src/test/ui/traits/issue-95898.stderr
@@ -8,7 +8,7 @@ LL |     t.clone();
 help: the following trait defines an item `clone`, perhaps you need to restrict type parameter `T` with it:
    |
 LL | fn foo<T: Clone>(t: T) {
-   |        ~~~~~~~~
+   |           +++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/traits/resolution-in-overloaded-op.stderr b/src/test/ui/traits/resolution-in-overloaded-op.stderr
index 049fffe165a..3ae6bf130cc 100644
--- a/src/test/ui/traits/resolution-in-overloaded-op.stderr
+++ b/src/test/ui/traits/resolution-in-overloaded-op.stderr
@@ -6,10 +6,10 @@ LL |     a * b
    |     |
    |     &T
    |
-help: consider further restricting this bound
+help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
    |
-LL | fn foo<T: MyMul<f64, f64> + std::ops::Mul<Output = f64>>(a: &T, b: f64) -> f64 {
-   |                           +++++++++++++++++++++++++++++
+LL | fn foo<T: MyMul<f64, f64>>(a: &T, b: f64) -> f64 where &T: Mul<f64> {
+   |                                                  ++++++++++++++++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/try-trait/bad-interconversion.stderr b/src/test/ui/try-trait/bad-interconversion.stderr
index 1a4105231dc..1dbf3ebdf82 100644
--- a/src/test/ui/try-trait/bad-interconversion.stderr
+++ b/src/test/ui/try-trait/bad-interconversion.stderr
@@ -31,7 +31,9 @@ LL | | }
    | |_- this function returns a `Result`
    |
    = help: the trait `FromResidual<Option<Infallible>>` is not implemented for `Result<u64, String>`
-   = help: the trait `FromResidual<Result<Infallible, E>>` is implemented for `Result<T, F>`
+   = help: the following other types implement trait `FromResidual<R>`:
+             <Result<T, F> as FromResidual<Result<Infallible, E>>>
+             <Result<T, F> as FromResidual<Yeet<E>>>
 
 error[E0277]: the `?` operator can only be used on `Result`s in a function that returns `Result`
   --> $DIR/bad-interconversion.rs:17:31
@@ -44,7 +46,9 @@ LL | | }
    | |_- this function returns a `Result`
    |
    = help: the trait `FromResidual<ControlFlow<{integer}, Infallible>>` is not implemented for `Result<u64, String>`
-   = help: the trait `FromResidual<Result<Infallible, E>>` is implemented for `Result<T, F>`
+   = help: the following other types implement trait `FromResidual<R>`:
+             <Result<T, F> as FromResidual<Result<Infallible, E>>>
+             <Result<T, F> as FromResidual<Yeet<E>>>
 
 error[E0277]: the `?` operator can only be used on `Option`s, not `Result`s, in a function that returns `Option`
   --> $DIR/bad-interconversion.rs:22:22
@@ -57,7 +61,9 @@ LL | | }
    | |_- this function returns an `Option`
    |
    = help: the trait `FromResidual<Result<Infallible, &str>>` is not implemented for `Option<u16>`
-   = help: the trait `FromResidual` is implemented for `Option<T>`
+   = help: the following other types implement trait `FromResidual<R>`:
+             <Option<T> as FromResidual<Yeet<()>>>
+             <Option<T> as FromResidual>
 
 error[E0277]: the `?` operator can only be used on `Option`s in a function that returns `Option`
   --> $DIR/bad-interconversion.rs:27:33
@@ -70,7 +76,9 @@ LL | | }
    | |_- this function returns an `Option`
    |
    = help: the trait `FromResidual<ControlFlow<{integer}, Infallible>>` is not implemented for `Option<u64>`
-   = help: the trait `FromResidual` is implemented for `Option<T>`
+   = help: the following other types implement trait `FromResidual<R>`:
+             <Option<T> as FromResidual<Yeet<()>>>
+             <Option<T> as FromResidual>
 
 error[E0277]: the `?` operator can only be used on `ControlFlow`s in a function that returns `ControlFlow`
   --> $DIR/bad-interconversion.rs:32:39
diff --git a/src/test/ui/try-trait/option-to-result.stderr b/src/test/ui/try-trait/option-to-result.stderr
index b0e4de8cb4b..ae5c3ad6282 100644
--- a/src/test/ui/try-trait/option-to-result.stderr
+++ b/src/test/ui/try-trait/option-to-result.stderr
@@ -10,7 +10,9 @@ LL | | }
    | |_- this function returns a `Result`
    |
    = help: the trait `FromResidual<Option<Infallible>>` is not implemented for `Result<(), ()>`
-   = help: the trait `FromResidual<Result<Infallible, E>>` is implemented for `Result<T, F>`
+   = help: the following other types implement trait `FromResidual<R>`:
+             <Result<T, F> as FromResidual<Result<Infallible, E>>>
+             <Result<T, F> as FromResidual<Yeet<E>>>
 
 error[E0277]: the `?` operator can only be used on `Option`s, not `Result`s, in a function that returns `Option`
   --> $DIR/option-to-result.rs:11:6
@@ -24,7 +26,9 @@ LL | | }
    | |_- this function returns an `Option`
    |
    = help: the trait `FromResidual<Result<Infallible, i32>>` is not implemented for `Option<i32>`
-   = help: the trait `FromResidual` is implemented for `Option<T>`
+   = help: the following other types implement trait `FromResidual<R>`:
+             <Option<T> as FromResidual<Yeet<()>>>
+             <Option<T> as FromResidual>
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/try-trait/try-on-option.stderr b/src/test/ui/try-trait/try-on-option.stderr
index 7b2a9a16f90..ba85a7cada2 100644
--- a/src/test/ui/try-trait/try-on-option.stderr
+++ b/src/test/ui/try-trait/try-on-option.stderr
@@ -10,7 +10,9 @@ LL | | }
    | |_- this function returns a `Result`
    |
    = help: the trait `FromResidual<Option<Infallible>>` is not implemented for `Result<u32, ()>`
-   = help: the trait `FromResidual<Result<Infallible, E>>` is implemented for `Result<T, F>`
+   = help: the following other types implement trait `FromResidual<R>`:
+             <Result<T, F> as FromResidual<Result<Infallible, E>>>
+             <Result<T, F> as FromResidual<Yeet<E>>>
 
 error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)
   --> $DIR/try-on-option.rs:11:6
diff --git a/src/test/ui/try-trait/yeet-for-option.rs b/src/test/ui/try-trait/yeet-for-option.rs
new file mode 100644
index 00000000000..753fbc1dee7
--- /dev/null
+++ b/src/test/ui/try-trait/yeet-for-option.rs
@@ -0,0 +1,11 @@
+// run-pass
+
+#![feature(yeet_expr)]
+
+fn always_yeet() -> Option<String> {
+    do yeet;
+}
+
+fn main() {
+    assert_eq!(always_yeet(), None);
+}
diff --git a/src/test/ui/try-trait/yeet-for-result.rs b/src/test/ui/try-trait/yeet-for-result.rs
new file mode 100644
index 00000000000..b7b113797cd
--- /dev/null
+++ b/src/test/ui/try-trait/yeet-for-result.rs
@@ -0,0 +1,11 @@
+// run-pass
+
+#![feature(yeet_expr)]
+
+fn always_yeet() -> Result<i32, String> {
+    do yeet "hello";
+}
+
+fn main() {
+    assert_eq!(always_yeet(), Err("hello".to_string()));
+}
diff --git a/src/test/ui/type-alias-impl-trait/bounds-are-checked.stderr b/src/test/ui/type-alias-impl-trait/bounds-are-checked.stderr
index d87ef2ec79c..920eef11da4 100644
--- a/src/test/ui/type-alias-impl-trait/bounds-are-checked.stderr
+++ b/src/test/ui/type-alias-impl-trait/bounds-are-checked.stderr
@@ -2,7 +2,7 @@ warning: unnecessary lifetime parameter `'a`
   --> $DIR/bounds-are-checked.rs:8:6
    |
 LL | fn f<'a: 'static>(t: &'a str) -> X<'a> {
-   |      ^^^^^^^^^^^
+   |      ^^
    |
    = help: you can use the `'static` lifetime directly, in place of `'a`
 
diff --git a/src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.base.stderr b/src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.base.stderr
index a3b410c2cfb..593fb8af32f 100644
--- a/src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.base.stderr
+++ b/src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.base.stderr
@@ -19,10 +19,13 @@ LL | type WrongGeneric<T> = impl 'static;
 error[E0310]: the parameter type `T` may not live long enough
   --> $DIR/generic_type_does_not_live_long_enough.rs:18:5
    |
-LL | fn wrong_generic<T>(t: T) -> WrongGeneric<T> {
-   |                  - help: consider adding an explicit lifetime bound...: `T: 'static`
 LL |     t
    |     ^ ...so that the type `T` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn wrong_generic<T: 'static>(t: T) -> WrongGeneric<T> {
+   |                   +++++++++
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.nll.stderr b/src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.nll.stderr
index db771d21132..593fb8af32f 100644
--- a/src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.nll.stderr
+++ b/src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.nll.stderr
@@ -20,9 +20,12 @@ error[E0310]: the parameter type `T` may not live long enough
   --> $DIR/generic_type_does_not_live_long_enough.rs:18:5
    |
 LL |     t
-   |     ^
+   |     ^ ...so that the type `T` will meet its required lifetime bounds
    |
-   = help: consider adding an explicit lifetime bound `T: 'static`...
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn wrong_generic<T: 'static>(t: T) -> WrongGeneric<T> {
+   |                   +++++++++
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/type-alias-impl-trait/issue-60662.stdout b/src/test/ui/type-alias-impl-trait/issue-60662.stdout
index a46047d9174..5b3d7375de0 100644
--- a/src/test/ui/type-alias-impl-trait/issue-60662.stdout
+++ b/src/test/ui/type-alias-impl-trait/issue-60662.stdout
@@ -10,5 +10,5 @@ extern crate std;
 trait Animal { }
 
 fn main() {
-        pub type ServeFut = /*impl Trait*/;
+        type ServeFut = /*impl Trait*/;
     }
diff --git a/src/test/ui/type-alias-impl-trait/nested-tait-inference.rs b/src/test/ui/type-alias-impl-trait/nested-tait-inference.rs
index 314e5362a8f..82248971692 100644
--- a/src/test/ui/type-alias-impl-trait/nested-tait-inference.rs
+++ b/src/test/ui/type-alias-impl-trait/nested-tait-inference.rs
@@ -11,7 +11,6 @@ impl Foo<()> for () { }
 
 fn foo() -> impl Foo<FooX> {
     //~^ ERROR: the trait bound `(): Foo<FooX>` is not satisfied
-    //~| ERROR: the trait bound `(): Foo<FooX>` is not satisfied
     // FIXME(type-alias-impl-trait): We could probably make this work.
     ()
 }
diff --git a/src/test/ui/type-alias-impl-trait/nested-tait-inference.stderr b/src/test/ui/type-alias-impl-trait/nested-tait-inference.stderr
index 0df2b57d373..f98da9f7f92 100644
--- a/src/test/ui/type-alias-impl-trait/nested-tait-inference.stderr
+++ b/src/test/ui/type-alias-impl-trait/nested-tait-inference.stderr
@@ -6,20 +6,6 @@ LL | fn foo() -> impl Foo<FooX> {
    |
    = help: the trait `Foo<()>` is implemented for `()`
 
-error[E0277]: the trait bound `(): Foo<FooX>` is not satisfied
-  --> $DIR/nested-tait-inference.rs:12:28
-   |
-LL |   fn foo() -> impl Foo<FooX> {
-   |  ____________________________^
-LL | |
-LL | |
-LL | |     // FIXME(type-alias-impl-trait): We could probably make this work.
-LL | |     ()
-LL | | }
-   | |_^ the trait `Foo<FooX>` is not implemented for `()`
-   |
-   = help: the trait `Foo<()>` is implemented for `()`
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/type-alias-impl-trait/nested-tait-inference2.rs b/src/test/ui/type-alias-impl-trait/nested-tait-inference2.rs
index 4dc30d9257b..0d7f5bad25f 100644
--- a/src/test/ui/type-alias-impl-trait/nested-tait-inference2.rs
+++ b/src/test/ui/type-alias-impl-trait/nested-tait-inference2.rs
@@ -12,7 +12,6 @@ impl Foo<u32> for () {}
 
 fn foo() -> impl Foo<FooX> {
     //~^ ERROR: the trait bound `(): Foo<FooX>` is not satisfied
-    //~| ERROR: the trait bound `(): Foo<FooX>` is not satisfied
     ()
 }
 
diff --git a/src/test/ui/type-alias-impl-trait/nested-tait-inference2.stderr b/src/test/ui/type-alias-impl-trait/nested-tait-inference2.stderr
index 264e8024fac..54f571ad3e3 100644
--- a/src/test/ui/type-alias-impl-trait/nested-tait-inference2.stderr
+++ b/src/test/ui/type-alias-impl-trait/nested-tait-inference2.stderr
@@ -8,21 +8,6 @@ LL | fn foo() -> impl Foo<FooX> {
              <() as Foo<()>>
              <() as Foo<u32>>
 
-error[E0277]: the trait bound `(): Foo<FooX>` is not satisfied
-  --> $DIR/nested-tait-inference2.rs:13:28
-   |
-LL |   fn foo() -> impl Foo<FooX> {
-   |  ____________________________^
-LL | |
-LL | |
-LL | |     ()
-LL | | }
-   | |_^ the trait `Foo<FooX>` is not implemented for `()`
-   |
-   = help: the following other types implement trait `Foo<A>`:
-             <() as Foo<()>>
-             <() as Foo<u32>>
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/type/type-check/missing_trait_impl.stderr b/src/test/ui/type/type-check/missing_trait_impl.stderr
index 59b8692dd4d..2b58cd4180b 100644
--- a/src/test/ui/type/type-check/missing_trait_impl.stderr
+++ b/src/test/ui/type/type-check/missing_trait_impl.stderr
@@ -8,8 +8,8 @@ LL |     let z = x + y;
    |
 help: consider restricting type parameter `T`
    |
-LL | fn foo<T: std::ops::Add<Output = T>>(x: T, y: T) {
-   |         +++++++++++++++++++++++++++
+LL | fn foo<T: std::ops::Add>(x: T, y: T) {
+   |         +++++++++++++++
 
 error[E0368]: binary assignment operation `+=` cannot be applied to type `T`
   --> $DIR/missing_trait_impl.rs:9:5
@@ -32,8 +32,8 @@ LL |     let y = -x;
    |
 help: consider restricting type parameter `T`
    |
-LL | fn baz<T: std::ops::Neg<Output = T>>(x: T) {
-   |         +++++++++++++++++++++++++++
+LL | fn baz<T: std::ops::Neg>(x: T) {
+   |         +++++++++++++++
 
 error[E0600]: cannot apply unary operator `!` to type `T`
   --> $DIR/missing_trait_impl.rs:14:13
@@ -43,8 +43,8 @@ LL |     let y = !x;
    |
 help: consider restricting type parameter `T`
    |
-LL | fn baz<T: std::ops::Not<Output = T>>(x: T) {
-   |         +++++++++++++++++++++++++++
+LL | fn baz<T: std::ops::Not>(x: T) {
+   |         +++++++++++++++
 
 error[E0614]: type `T` cannot be dereferenced
   --> $DIR/missing_trait_impl.rs:15:13
diff --git a/src/test/ui/typeck/issue-87181/empty-tuple-method.rs b/src/test/ui/typeck/issue-87181/empty-tuple-method.rs
new file mode 100644
index 00000000000..1875d8280cb
--- /dev/null
+++ b/src/test/ui/typeck/issue-87181/empty-tuple-method.rs
@@ -0,0 +1,14 @@
+struct Bar<T> {
+    bar: T
+}
+
+struct Foo();
+impl Foo {
+    fn foo() { }
+}
+
+fn main() {
+    let thing = Bar { bar: Foo };
+    thing.bar.foo();
+    //~^ ERROR no method named `foo` found for fn item `fn() -> Foo {Foo}` in the current scope [E0599]
+}
diff --git a/src/test/ui/typeck/issue-87181/empty-tuple-method.stderr b/src/test/ui/typeck/issue-87181/empty-tuple-method.stderr
new file mode 100644
index 00000000000..6ed70b301e4
--- /dev/null
+++ b/src/test/ui/typeck/issue-87181/empty-tuple-method.stderr
@@ -0,0 +1,16 @@
+error[E0599]: no method named `foo` found for fn item `fn() -> Foo {Foo}` in the current scope
+  --> $DIR/empty-tuple-method.rs:12:15
+   |
+LL |     thing.bar.foo();
+   |     --------- ^^^ method not found in `fn() -> Foo {Foo}`
+   |     |
+   |     this is the constructor of a struct
+   |
+help: call the constructor
+   |
+LL |     (thing.bar)().foo();
+   |     +         +++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0599`.
diff --git a/src/test/ui/typeck/issue-87181/enum-variant.rs b/src/test/ui/typeck/issue-87181/enum-variant.rs
new file mode 100644
index 00000000000..3b926b90f10
--- /dev/null
+++ b/src/test/ui/typeck/issue-87181/enum-variant.rs
@@ -0,0 +1,16 @@
+struct Bar<T> {
+    bar: T
+}
+
+enum Foo{
+    Tup()
+}
+impl Foo {
+    fn foo() { }
+}
+
+fn main() {
+    let thing = Bar { bar: Foo::Tup };
+    thing.bar.foo();
+    //~^ ERROR no method named `foo` found for fn item `fn() -> Foo {Foo::Tup}` in the current scope [E0599]
+}
diff --git a/src/test/ui/typeck/issue-87181/enum-variant.stderr b/src/test/ui/typeck/issue-87181/enum-variant.stderr
new file mode 100644
index 00000000000..a3a818696ab
--- /dev/null
+++ b/src/test/ui/typeck/issue-87181/enum-variant.stderr
@@ -0,0 +1,16 @@
+error[E0599]: no method named `foo` found for fn item `fn() -> Foo {Foo::Tup}` in the current scope
+  --> $DIR/enum-variant.rs:14:15
+   |
+LL |     thing.bar.foo();
+   |     --------- ^^^ method not found in `fn() -> Foo {Foo::Tup}`
+   |     |
+   |     this is the constructor of an enum variant
+   |
+help: call the constructor
+   |
+LL |     (thing.bar)().foo();
+   |     +         +++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0599`.
diff --git a/src/test/ui/typeck/issue-87181/tuple-field.rs b/src/test/ui/typeck/issue-87181/tuple-field.rs
new file mode 100644
index 00000000000..00e3b460ecf
--- /dev/null
+++ b/src/test/ui/typeck/issue-87181/tuple-field.rs
@@ -0,0 +1,14 @@
+struct Bar<T> {
+    bar: T
+}
+
+struct Foo(char, u16);
+impl Foo {
+    fn foo() { }
+}
+
+fn main() {
+    let thing = Bar { bar: Foo };
+    thing.bar.0;
+    //~^ ERROR no field `0` on type `fn(char, u16) -> Foo {Foo}` [E0609]
+}
diff --git a/src/test/ui/typeck/issue-87181/tuple-field.stderr b/src/test/ui/typeck/issue-87181/tuple-field.stderr
new file mode 100644
index 00000000000..4d22ada0247
--- /dev/null
+++ b/src/test/ui/typeck/issue-87181/tuple-field.stderr
@@ -0,0 +1,16 @@
+error[E0609]: no field `0` on type `fn(char, u16) -> Foo {Foo}`
+  --> $DIR/tuple-field.rs:12:15
+   |
+LL |     thing.bar.0;
+   |     --------- ^
+   |     |
+   |     this is the constructor of a struct
+   |
+help: call the constructor
+   |
+LL |     (thing.bar)(_, _).0;
+   |     +         +++++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0609`.
diff --git a/src/test/ui/typeck/issue-87181/tuple-method.rs b/src/test/ui/typeck/issue-87181/tuple-method.rs
new file mode 100644
index 00000000000..e88f642b070
--- /dev/null
+++ b/src/test/ui/typeck/issue-87181/tuple-method.rs
@@ -0,0 +1,14 @@
+struct Bar<T> {
+    bar: T
+}
+
+struct Foo(u8, i32);
+impl Foo {
+    fn foo() { }
+}
+
+fn main() {
+    let thing = Bar { bar: Foo };
+    thing.bar.foo();
+    //~^ ERROR no method named `foo` found for fn item `fn(u8, i32) -> Foo {Foo}` in the current scope [E0599]
+}
diff --git a/src/test/ui/typeck/issue-87181/tuple-method.stderr b/src/test/ui/typeck/issue-87181/tuple-method.stderr
new file mode 100644
index 00000000000..1e392e17984
--- /dev/null
+++ b/src/test/ui/typeck/issue-87181/tuple-method.stderr
@@ -0,0 +1,16 @@
+error[E0599]: no method named `foo` found for fn item `fn(u8, i32) -> Foo {Foo}` in the current scope
+  --> $DIR/tuple-method.rs:12:15
+   |
+LL |     thing.bar.foo();
+   |     --------- ^^^ method not found in `fn(u8, i32) -> Foo {Foo}`
+   |     |
+   |     this is the constructor of a struct
+   |
+help: call the constructor
+   |
+LL |     (thing.bar)(_, _).foo();
+   |     +         +++++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0599`.
diff --git a/src/test/ui/typeck/typeck-default-trait-impl-cross-crate-coherence.stderr b/src/test/ui/typeck/typeck-default-trait-impl-cross-crate-coherence.stderr
index cf5c15df705..fc3778b7967 100644
--- a/src/test/ui/typeck/typeck-default-trait-impl-cross-crate-coherence.stderr
+++ b/src/test/ui/typeck/typeck-default-trait-impl-cross-crate-coherence.stderr
@@ -26,7 +26,7 @@ error[E0321]: cross-crate traits with a default impl, like `DefaultedTrait`, can
 LL | impl DefaultedTrait for Box<C> { }
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't implement cross-crate trait for type in another crate
 
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate
   --> $DIR/typeck-default-trait-impl-cross-crate-coherence.rs:22:1
    |
 LL | impl DefaultedTrait for lib::Something<C> { }
diff --git a/src/test/ui/typeck/typeck_type_placeholder_item.rs b/src/test/ui/typeck/typeck_type_placeholder_item.rs
index ca0876be58d..22fedb22d66 100644
--- a/src/test/ui/typeck/typeck_type_placeholder_item.rs
+++ b/src/test/ui/typeck/typeck_type_placeholder_item.rs
@@ -57,7 +57,7 @@ unsafe fn test12(x: *const usize) -> *const *const _ {
 
 impl Clone for Test9 {
     fn clone(&self) -> _ { Test9 }
-    //~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types
+    //~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions
 
     fn clone_from(&mut self, other: _) { *self = Test9; }
     //~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions
@@ -113,7 +113,7 @@ pub fn main() {
 
     impl Clone for FnTest9 {
         fn clone(&self) -> _ { FnTest9 }
-        //~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types
+        //~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions
 
         fn clone_from(&mut self, other: _) { *self = FnTest9; }
         //~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions
diff --git a/src/test/ui/typeck/typeck_type_placeholder_item.stderr b/src/test/ui/typeck/typeck_type_placeholder_item.stderr
index c07b96f9a97..3ea317dfb1a 100644
--- a/src/test/ui/typeck/typeck_type_placeholder_item.stderr
+++ b/src/test/ui/typeck/typeck_type_placeholder_item.stderr
@@ -545,14 +545,16 @@ help: use type parameters instead
 LL |     fn test10<T>(&self, _x : T) { }
    |              +++             ~
 
-error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types
+error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions
   --> $DIR/typeck_type_placeholder_item.rs:59:24
    |
 LL |     fn clone(&self) -> _ { Test9 }
-   |                        ^
-   |                        |
-   |                        not allowed in type signatures
-   |                        help: replace with the correct return type: `Test9`
+   |                        ^ not allowed in type signatures
+   |
+help: try replacing `_` with the type in the corresponding trait method signature
+   |
+LL |     fn clone(&self) -> Test9 { Test9 }
+   |                        ~~~~~
 
 error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions
   --> $DIR/typeck_type_placeholder_item.rs:62:37
@@ -560,10 +562,10 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures
 LL |     fn clone_from(&mut self, other: _) { *self = Test9; }
    |                                     ^ not allowed in type signatures
    |
-help: use type parameters instead
+help: try replacing `_` with the type in the corresponding trait method signature
    |
-LL |     fn clone_from<T>(&mut self, other: T) { *self = Test9; }
-   |                  +++                   ~
+LL |     fn clone_from(&mut self, other: &Test9) { *self = Test9; }
+   |                                     ~~~~~~
 
 error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types
   --> $DIR/typeck_type_placeholder_item.rs:107:31
@@ -585,14 +587,16 @@ help: use type parameters instead
 LL |         fn fn_test10<T>(&self, _x : T) { }
    |                     +++             ~
 
-error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types
+error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions
   --> $DIR/typeck_type_placeholder_item.rs:115:28
    |
 LL |         fn clone(&self) -> _ { FnTest9 }
-   |                            ^
-   |                            |
-   |                            not allowed in type signatures
-   |                            help: replace with the correct return type: `FnTest9`
+   |                            ^ not allowed in type signatures
+   |
+help: try replacing `_` with the type in the corresponding trait method signature
+   |
+LL |         fn clone(&self) -> FnTest9 { FnTest9 }
+   |                            ~~~~~~~
 
 error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions
   --> $DIR/typeck_type_placeholder_item.rs:118:41
@@ -600,10 +604,10 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures
 LL |         fn clone_from(&mut self, other: _) { *self = FnTest9; }
    |                                         ^ not allowed in type signatures
    |
-help: use type parameters instead
+help: try replacing `_` with the type in the corresponding trait method signature
    |
-LL |         fn clone_from<T>(&mut self, other: T) { *self = FnTest9; }
-   |                      +++                   ~
+LL |         fn clone_from(&mut self, other: &FnTest9) { *self = FnTest9; }
+   |                                         ~~~~~~~~
 
 error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated types
   --> $DIR/typeck_type_placeholder_item.rs:201:14
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.stderr b/src/test/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.stderr
index 0b6d94e71f0..e9883903674 100644
--- a/src/test/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.stderr
+++ b/src/test/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.stderr
@@ -2,9 +2,9 @@ error[E0599]: no method named `call` found for closure `[closure@$DIR/unboxed-cl
   --> $DIR/unboxed-closures-static-call-wrong-trait.rs:7:10
    |
 LL |     mut_.call((0, ));
-   |          ^^^^ method not found in `[closure@$DIR/unboxed-closures-static-call-wrong-trait.rs:6:26: 6:31]`
-   |
-   = note: `mut_` is a function, perhaps you wish to call it
+   |     ---- ^^^^ method not found in `[closure@$DIR/unboxed-closures-static-call-wrong-trait.rs:6:26: 6:31]`
+   |     |
+   |     this is a function, perhaps you wish to call it
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/underscore-lifetime/dyn-trait-underscore.nll.stderr b/src/test/ui/underscore-lifetime/dyn-trait-underscore.nll.stderr
index 53d45f6a8f2..0ffb77cf021 100644
--- a/src/test/ui/underscore-lifetime/dyn-trait-underscore.nll.stderr
+++ b/src/test/ui/underscore-lifetime/dyn-trait-underscore.nll.stderr
@@ -6,6 +6,11 @@ LL | fn a<T>(items: &[T]) -> Box<dyn Iterator<Item=&T>> {
 LL |     //                      ^^^^^^^^^^^^^^^^^^^^^ bound *here* defaults to `'static`
 LL |     Box::new(items.iter())
    |     ^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static`
+   |
+help: to declare that the trait object captures data from argument `items`, you can add an explicit `'_` lifetime bound
+   |
+LL | fn a<T>(items: &[T]) -> Box<dyn Iterator<Item=&T> + '_> {
+   |                                                   ++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/underscore-lifetime/underscore-lifetime-elison-mismatch.nll.stderr b/src/test/ui/underscore-lifetime/underscore-lifetime-elison-mismatch.nll.stderr
index 8e10242cb13..a4dece320ec 100644
--- a/src/test/ui/underscore-lifetime/underscore-lifetime-elison-mismatch.nll.stderr
+++ b/src/test/ui/underscore-lifetime/underscore-lifetime-elison-mismatch.nll.stderr
@@ -6,6 +6,11 @@ LL | fn foo(x: &mut Vec<&'_ u8>, y: &'_ u8) { x.push(y); }
    |                    |           |
    |                    |           let's call the lifetime of this reference `'1`
    |                    let's call the lifetime of this reference `'2`
+   |
+help: consider introducing a named lifetime parameter
+   |
+LL | fn foo<'a>(x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); }
+   |       ++++              ~~          ~~
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/uninhabited/privately-uninhabited-mir-call.rs b/src/test/ui/uninhabited/privately-uninhabited-mir-call.rs
new file mode 100644
index 00000000000..b37ec2696de
--- /dev/null
+++ b/src/test/ui/uninhabited/privately-uninhabited-mir-call.rs
@@ -0,0 +1,29 @@
+// Verifies that MIR building for a call expression respects
+// privacy when checking if a call return type is uninhabited.
+
+pub mod widget {
+    enum Unimplemented {}
+    pub struct Widget(Unimplemented);
+
+    impl Widget {
+        pub fn new() -> Widget {
+            todo!();
+        }
+    }
+
+    pub fn f() {
+        let x: &mut u32;
+        Widget::new();
+        // Ok. Widget type returned from new is known to be uninhabited
+        // and the following code is considered unreachable.
+        *x = 1;
+    }
+}
+
+fn main() {
+    let y: &mut u32;
+    widget::Widget::new();
+    // Error. Widget type is not known to be uninhabited here,
+    // so the following code is considered reachable.
+    *y = 2; //~ ERROR use of possibly-uninitialized variable
+}
diff --git a/src/test/ui/uninhabited/privately-uninhabited-mir-call.stderr b/src/test/ui/uninhabited/privately-uninhabited-mir-call.stderr
new file mode 100644
index 00000000000..fb195341168
--- /dev/null
+++ b/src/test/ui/uninhabited/privately-uninhabited-mir-call.stderr
@@ -0,0 +1,9 @@
+error[E0381]: use of possibly-uninitialized variable: `y`
+  --> $DIR/privately-uninhabited-mir-call.rs:28:5
+   |
+LL |     *y = 2;
+   |     ^^^^^^ use of possibly-uninitialized `y`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0381`.
diff --git a/src/test/ui/unsafe/issue-45087-unreachable-unsafe.mir.stderr b/src/test/ui/unsafe/issue-45087-unreachable-unsafe.mir.stderr
index 33f762ccf63..e7960960774 100644
--- a/src/test/ui/unsafe/issue-45087-unreachable-unsafe.mir.stderr
+++ b/src/test/ui/unsafe/issue-45087-unreachable-unsafe.mir.stderr
@@ -1,11 +1,27 @@
 error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block
-  --> $DIR/issue-45087-unreachable-unsafe.rs:6:5
+  --> $DIR/issue-45087-unreachable-unsafe.rs:7:5
    |
 LL |     *(1 as *mut u32) = 42;
    |     ^^^^^^^^^^^^^^^^^^^^^ dereference of raw pointer
    |
    = note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
 
-error: aborting due to previous error
+error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block
+  --> $DIR/issue-45087-unreachable-unsafe.rs:17:5
+   |
+LL |     *a = 1;
+   |     ^^^^^^ dereference of raw pointer
+   |
+   = note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
+
+error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block
+  --> $DIR/issue-45087-unreachable-unsafe.rs:29:5
+   |
+LL |     *b = 1;
+   |     ^^^^^^ dereference of raw pointer
+   |
+   = note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
+
+error: aborting due to 3 previous errors
 
 For more information about this error, try `rustc --explain E0133`.
diff --git a/src/test/ui/unsafe/issue-45087-unreachable-unsafe.rs b/src/test/ui/unsafe/issue-45087-unreachable-unsafe.rs
index 071cea8fbd7..3e3da667c0b 100644
--- a/src/test/ui/unsafe/issue-45087-unreachable-unsafe.rs
+++ b/src/test/ui/unsafe/issue-45087-unreachable-unsafe.rs
@@ -1,3 +1,4 @@
+// Verify that unreachable code undergoes unsafety checks.
 // revisions: mir thir
 // [thir]compile-flags: -Z thir-unsafeck
 
@@ -6,3 +7,25 @@ fn main() {
     *(1 as *mut u32) = 42;
     //~^ ERROR dereference of raw pointer is unsafe
 }
+
+fn panic() -> ! {
+    panic!();
+}
+
+fn f(a: *mut u32) {
+    panic();
+    *a = 1;
+    //~^ ERROR dereference of raw pointer is unsafe
+}
+
+enum Void {}
+
+fn uninhabited() -> Void {
+    panic!();
+}
+
+fn g(b: *mut u32) {
+    uninhabited();
+    *b = 1;
+    //~^ ERROR dereference of raw pointer is unsafe
+}
diff --git a/src/test/ui/unsafe/issue-45087-unreachable-unsafe.thir.stderr b/src/test/ui/unsafe/issue-45087-unreachable-unsafe.thir.stderr
index 73a113652b8..e81adad4507 100644
--- a/src/test/ui/unsafe/issue-45087-unreachable-unsafe.thir.stderr
+++ b/src/test/ui/unsafe/issue-45087-unreachable-unsafe.thir.stderr
@@ -1,11 +1,27 @@
 error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block
-  --> $DIR/issue-45087-unreachable-unsafe.rs:6:5
+  --> $DIR/issue-45087-unreachable-unsafe.rs:7:5
    |
 LL |     *(1 as *mut u32) = 42;
    |     ^^^^^^^^^^^^^^^^ dereference of raw pointer
    |
    = note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
 
-error: aborting due to previous error
+error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block
+  --> $DIR/issue-45087-unreachable-unsafe.rs:17:5
+   |
+LL |     *a = 1;
+   |     ^^ dereference of raw pointer
+   |
+   = note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
+
+error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block
+  --> $DIR/issue-45087-unreachable-unsafe.rs:29:5
+   |
+LL |     *b = 1;
+   |     ^^ dereference of raw pointer
+   |
+   = note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
+
+error: aborting due to 3 previous errors
 
 For more information about this error, try `rustc --explain E0133`.
diff --git a/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.mir.stderr b/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.mir.stderr
index 163c101772c..fd58e1b1ebe 100644
--- a/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.mir.stderr
+++ b/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.mir.stderr
@@ -12,7 +12,7 @@ LL | #![deny(unsafe_op_in_unsafe_fn)]
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
 error: dereference of raw pointer is unsafe and requires unsafe block (error E0133)
-  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:14:5
+  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:15:5
    |
 LL |     *PTR;
    |     ^^^^ dereference of raw pointer
@@ -20,7 +20,7 @@ LL |     *PTR;
    = note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
 
 error: use of mutable static is unsafe and requires unsafe block (error E0133)
-  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:16:5
+  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:17:5
    |
 LL |     VOID = ();
    |     ^^^^^^^^^ use of mutable static
@@ -28,7 +28,7 @@ LL |     VOID = ();
    = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior
 
 error: unnecessary `unsafe` block
-  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:19:5
+  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:20:5
    |
 LL |     unsafe {}
    |     ^^^^^^ unnecessary `unsafe` block
@@ -40,13 +40,13 @@ LL | #![deny(unused_unsafe)]
    |         ^^^^^^^^^^^^^
 
 error: call to unsafe function is unsafe and requires unsafe block (error E0133)
-  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:27:5
+  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:28:5
    |
 LL |     unsf();
    |     ^^^^^^ call to unsafe function
    |
 note: the lint level is defined here
-  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:25:8
+  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:26:8
    |
 LL | #[deny(warnings)]
    |        ^^^^^^^^
@@ -54,7 +54,7 @@ LL | #[deny(warnings)]
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
 error: dereference of raw pointer is unsafe and requires unsafe block (error E0133)
-  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:29:5
+  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:31:5
    |
 LL |     *PTR;
    |     ^^^^ dereference of raw pointer
@@ -62,7 +62,7 @@ LL |     *PTR;
    = note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
 
 error: use of mutable static is unsafe and requires unsafe block (error E0133)
-  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:31:5
+  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:33:5
    |
 LL |     VOID = ();
    |     ^^^^^^^^^ use of mutable static
@@ -70,19 +70,19 @@ LL |     VOID = ();
    = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior
 
 error: unnecessary `unsafe` block
-  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:33:5
+  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:35:5
    |
 LL |     unsafe {}
    |     ^^^^^^ unnecessary `unsafe` block
 
 error: unnecessary `unsafe` block
-  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:47:5
+  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:49:5
    |
 LL |     unsafe { unsafe { unsf() } }
    |     ^^^^^^ unnecessary `unsafe` block
 
 error: unnecessary `unsafe` block
-  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:58:5
+  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:60:5
    |
 LL | unsafe fn allow_level() {
    | ----------------------- because it's nested under this `unsafe` fn
@@ -92,13 +92,13 @@ LL |     unsafe { unsf() }
    |
    = note: this `unsafe` block does contain unsafe operations, but those are already allowed in an `unsafe fn`
 note: the lint level is defined here
-  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:51:9
+  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:53:9
    |
 LL | #[allow(unsafe_op_in_unsafe_fn)]
    |         ^^^^^^^^^^^^^^^^^^^^^^
 
 error: unnecessary `unsafe` block
-  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:70:9
+  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:72:9
    |
 LL | unsafe fn nested_allow_level() {
    | ------------------------------ because it's nested under this `unsafe` fn
@@ -108,13 +108,13 @@ LL |         unsafe { unsf() }
    |
    = note: this `unsafe` block does contain unsafe operations, but those are already allowed in an `unsafe fn`
 note: the lint level is defined here
-  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:63:13
+  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:65:13
    |
 LL |     #[allow(unsafe_op_in_unsafe_fn)]
    |             ^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0133]: call to unsafe function is unsafe and requires unsafe block
-  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:76:5
+  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:78:5
    |
 LL |     unsf();
    |     ^^^^^^ call to unsafe function
@@ -122,7 +122,7 @@ LL |     unsf();
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
 error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:80:9
+  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:83:9
    |
 LL |         unsf();
    |         ^^^^^^ call to unsafe function
diff --git a/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs b/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs
index 7ca714b85c2..30b07234034 100644
--- a/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs
+++ b/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs
@@ -10,7 +10,8 @@ static mut VOID: () = ();
 
 unsafe fn deny_level() {
     unsf();
-    //~^ ERROR call to unsafe function is unsafe and requires unsafe block
+    //[mir]~^ ERROR call to unsafe function is unsafe and requires unsafe block
+    //[thir]~^^ ERROR call to unsafe function `unsf` is unsafe and requires unsafe block
     *PTR;
     //~^ ERROR dereference of raw pointer is unsafe and requires unsafe block
     VOID = ();
@@ -25,7 +26,8 @@ unsafe fn deny_level() {
 #[deny(warnings)]
 unsafe fn warning_level() {
     unsf();
-    //~^ ERROR call to unsafe function is unsafe and requires unsafe block
+    //[mir]~^ ERROR call to unsafe function is unsafe and requires unsafe block
+    //[thir]~^^ ERROR call to unsafe function `unsf` is unsafe and requires unsafe block
     *PTR;
     //~^ ERROR dereference of raw pointer is unsafe and requires unsafe block
     VOID = ();
@@ -74,10 +76,12 @@ unsafe fn nested_allow_level() {
 
 fn main() {
     unsf();
-    //~^ ERROR call to unsafe function is unsafe and requires unsafe block
+    //[mir]~^ ERROR call to unsafe function is unsafe and requires unsafe block
+    //[thir]~^^ ERROR call to unsafe function `unsf` is unsafe and requires unsafe block
     #[allow(unsafe_op_in_unsafe_fn)]
     {
         unsf();
-        //~^ ERROR call to unsafe function is unsafe and requires unsafe function or block
+        //[mir]~^ ERROR call to unsafe function is unsafe and requires unsafe function or block
+        //[thir]~^^ ERROR call to unsafe function `unsf` is unsafe and requires unsafe function or block
     }
 }
diff --git a/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.thir.stderr b/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.thir.stderr
index ad87690bb52..2ba6a72930d 100644
--- a/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.thir.stderr
+++ b/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.thir.stderr
@@ -1,4 +1,4 @@
-error: call to unsafe function is unsafe and requires unsafe block (error E0133)
+error: call to unsafe function `unsf` is unsafe and requires unsafe block (error E0133)
   --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:12:5
    |
 LL |     unsf();
@@ -12,7 +12,7 @@ LL | #![deny(unsafe_op_in_unsafe_fn)]
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
 error: dereference of raw pointer is unsafe and requires unsafe block (error E0133)
-  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:14:5
+  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:15:5
    |
 LL |     *PTR;
    |     ^^^^ dereference of raw pointer
@@ -20,7 +20,7 @@ LL |     *PTR;
    = note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
 
 error: use of mutable static is unsafe and requires unsafe block (error E0133)
-  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:16:5
+  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:17:5
    |
 LL |     VOID = ();
    |     ^^^^ use of mutable static
@@ -28,7 +28,7 @@ LL |     VOID = ();
    = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior
 
 error: unnecessary `unsafe` block
-  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:19:5
+  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:20:5
    |
 LL |     unsafe {}
    |     ^^^^^^ unnecessary `unsafe` block
@@ -39,14 +39,14 @@ note: the lint level is defined here
 LL | #![deny(unused_unsafe)]
    |         ^^^^^^^^^^^^^
 
-error: call to unsafe function is unsafe and requires unsafe block (error E0133)
-  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:27:5
+error: call to unsafe function `unsf` is unsafe and requires unsafe block (error E0133)
+  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:28:5
    |
 LL |     unsf();
    |     ^^^^^^ call to unsafe function
    |
 note: the lint level is defined here
-  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:25:8
+  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:26:8
    |
 LL | #[deny(warnings)]
    |        ^^^^^^^^
@@ -54,7 +54,7 @@ LL | #[deny(warnings)]
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
 error: dereference of raw pointer is unsafe and requires unsafe block (error E0133)
-  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:29:5
+  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:31:5
    |
 LL |     *PTR;
    |     ^^^^ dereference of raw pointer
@@ -62,7 +62,7 @@ LL |     *PTR;
    = note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
 
 error: use of mutable static is unsafe and requires unsafe block (error E0133)
-  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:31:5
+  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:33:5
    |
 LL |     VOID = ();
    |     ^^^^ use of mutable static
@@ -70,13 +70,13 @@ LL |     VOID = ();
    = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior
 
 error: unnecessary `unsafe` block
-  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:33:5
+  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:35:5
    |
 LL |     unsafe {}
    |     ^^^^^^ unnecessary `unsafe` block
 
 error: unnecessary `unsafe` block
-  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:47:14
+  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:49:14
    |
 LL |     unsafe { unsafe { unsf() } }
    |     ------   ^^^^^^ unnecessary `unsafe` block
@@ -84,7 +84,7 @@ LL |     unsafe { unsafe { unsf() } }
    |     because it's nested under this `unsafe` block
 
 error: unnecessary `unsafe` block
-  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:58:5
+  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:60:5
    |
 LL | unsafe fn allow_level() {
    | ----------------------- because it's nested under this `unsafe` fn
@@ -93,7 +93,7 @@ LL |     unsafe { unsf() }
    |     ^^^^^^ unnecessary `unsafe` block
 
 error: unnecessary `unsafe` block
-  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:70:9
+  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:72:9
    |
 LL | unsafe fn nested_allow_level() {
    | ------------------------------ because it's nested under this `unsafe` fn
@@ -101,16 +101,16 @@ LL | unsafe fn nested_allow_level() {
 LL |         unsafe { unsf() }
    |         ^^^^^^ unnecessary `unsafe` block
 
-error[E0133]: call to unsafe function is unsafe and requires unsafe block
-  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:76:5
+error[E0133]: call to unsafe function `unsf` is unsafe and requires unsafe block
+  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:78:5
    |
 LL |     unsf();
    |     ^^^^^^ call to unsafe function
    |
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
-error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:80:9
+error[E0133]: call to unsafe function `unsf` is unsafe and requires unsafe function or block
+  --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:83:9
    |
 LL |         unsf();
    |         ^^^^^^ call to unsafe function
diff --git a/src/test/ui/unsafe/unsafe-const-fn.thir.stderr b/src/test/ui/unsafe/unsafe-const-fn.thir.stderr
index 3031be720f0..1a77adf4459 100644
--- a/src/test/ui/unsafe/unsafe-const-fn.thir.stderr
+++ b/src/test/ui/unsafe/unsafe-const-fn.thir.stderr
@@ -1,4 +1,4 @@
-error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+error[E0133]: call to unsafe function `dummy` is unsafe and requires unsafe function or block
   --> $DIR/unsafe-const-fn.rs:10:18
    |
 LL | const VAL: u32 = dummy(0xFFFF);
diff --git a/src/test/ui/unsafe/unsafe-fn-called-from-safe.rs b/src/test/ui/unsafe/unsafe-fn-called-from-safe.rs
index df12e441516..55072dcc6c3 100644
--- a/src/test/ui/unsafe/unsafe-fn-called-from-safe.rs
+++ b/src/test/ui/unsafe/unsafe-fn-called-from-safe.rs
@@ -4,5 +4,7 @@
 unsafe fn f() { return; }
 
 fn main() {
-    f(); //~ ERROR call to unsafe function is unsafe
+    f();
+    //[mir]~^ ERROR call to unsafe function is unsafe
+    //[thir]~^^ ERROR call to unsafe function `f` is unsafe
 }
diff --git a/src/test/ui/unsafe/unsafe-fn-called-from-safe.thir.stderr b/src/test/ui/unsafe/unsafe-fn-called-from-safe.thir.stderr
index 1d6fa4cbf40..206dbd90a75 100644
--- a/src/test/ui/unsafe/unsafe-fn-called-from-safe.thir.stderr
+++ b/src/test/ui/unsafe/unsafe-fn-called-from-safe.thir.stderr
@@ -1,4 +1,4 @@
-error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+error[E0133]: call to unsafe function `f` is unsafe and requires unsafe function or block
   --> $DIR/unsafe-fn-called-from-safe.rs:7:5
    |
 LL |     f();
diff --git a/src/test/ui/unsafe/unsafe-fn-used-as-value.rs b/src/test/ui/unsafe/unsafe-fn-used-as-value.rs
index 2af0786617b..9517598c7ce 100644
--- a/src/test/ui/unsafe/unsafe-fn-used-as-value.rs
+++ b/src/test/ui/unsafe/unsafe-fn-used-as-value.rs
@@ -5,5 +5,7 @@ unsafe fn f() { return; }
 
 fn main() {
     let x = f;
-    x();    //~ ERROR call to unsafe function is unsafe
+    x();
+    //[mir]~^ ERROR call to unsafe function is unsafe
+    //[thir]~^^ ERROR call to unsafe function `f` is unsafe
 }
diff --git a/src/test/ui/unsafe/unsafe-fn-used-as-value.thir.stderr b/src/test/ui/unsafe/unsafe-fn-used-as-value.thir.stderr
index b08a7109dda..e81dd3b2b41 100644
--- a/src/test/ui/unsafe/unsafe-fn-used-as-value.thir.stderr
+++ b/src/test/ui/unsafe/unsafe-fn-used-as-value.thir.stderr
@@ -1,4 +1,4 @@
-error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+error[E0133]: call to unsafe function `f` is unsafe and requires unsafe function or block
   --> $DIR/unsafe-fn-used-as-value.rs:8:5
    |
 LL |     x();
diff --git a/src/test/ui/unused-crate-deps/deny-attr.rs b/src/test/ui/unused-crate-deps/deny-attr.rs
new file mode 100644
index 00000000000..e9ab18ff63f
--- /dev/null
+++ b/src/test/ui/unused-crate-deps/deny-attr.rs
@@ -0,0 +1,9 @@
+// Check for unused crate dep, no path
+
+// edition:2018
+// aux-crate:bar=bar.rs
+
+#![deny(unused_crate_dependencies)]
+//~^ ERROR external crate `bar` unused in
+
+fn main() {}
diff --git a/src/test/ui/unused-crate-deps/deny-attr.stderr b/src/test/ui/unused-crate-deps/deny-attr.stderr
new file mode 100644
index 00000000000..93694f6827f
--- /dev/null
+++ b/src/test/ui/unused-crate-deps/deny-attr.stderr
@@ -0,0 +1,14 @@
+error: external crate `bar` unused in `deny_attr`: remove the dependency or add `use bar as _;`
+  --> $DIR/deny-attr.rs:6:1
+   |
+LL | #![deny(unused_crate_dependencies)]
+   | ^
+   |
+note: the lint level is defined here
+  --> $DIR/deny-attr.rs:6:9
+   |
+LL | #![deny(unused_crate_dependencies)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/unused-crate-deps/deny-cmdline-json-silent.rs b/src/test/ui/unused-crate-deps/deny-cmdline-json-silent.rs
new file mode 100644
index 00000000000..fd9a61d6caa
--- /dev/null
+++ b/src/test/ui/unused-crate-deps/deny-cmdline-json-silent.rs
@@ -0,0 +1,8 @@
+// Check for unused crate dep, json event, deny but we're not reporting that in exit status
+
+// edition:2018
+// check-pass
+// compile-flags: -Dunused-crate-dependencies -Zunstable-options --json unused-externs-silent --error-format=json
+// aux-crate:bar=bar.rs
+
+fn main() {}
diff --git a/src/test/ui/unused-crate-deps/deny-cmdline-json-silent.stderr b/src/test/ui/unused-crate-deps/deny-cmdline-json-silent.stderr
new file mode 100644
index 00000000000..595619f3a8a
--- /dev/null
+++ b/src/test/ui/unused-crate-deps/deny-cmdline-json-silent.stderr
@@ -0,0 +1 @@
+{"lint_level":"deny","unused_extern_names":["bar"]}
diff --git a/src/test/ui/unused-crate-deps/deny-cmdline-json.rs b/src/test/ui/unused-crate-deps/deny-cmdline-json.rs
new file mode 100644
index 00000000000..2b369dee5a0
--- /dev/null
+++ b/src/test/ui/unused-crate-deps/deny-cmdline-json.rs
@@ -0,0 +1,7 @@
+// Check for unused crate dep, json event, deny, expect compile failure
+
+// edition:2018
+// compile-flags: -Dunused-crate-dependencies  -Zunstable-options --json unused-externs --error-format=json
+// aux-crate:bar=bar.rs
+
+fn main() {}
diff --git a/src/test/ui/unused-crate-deps/deny-cmdline-json.stderr b/src/test/ui/unused-crate-deps/deny-cmdline-json.stderr
new file mode 100644
index 00000000000..595619f3a8a
--- /dev/null
+++ b/src/test/ui/unused-crate-deps/deny-cmdline-json.stderr
@@ -0,0 +1 @@
+{"lint_level":"deny","unused_extern_names":["bar"]}
diff --git a/src/test/ui/unused-crate-deps/deny-cmdline.rs b/src/test/ui/unused-crate-deps/deny-cmdline.rs
new file mode 100644
index 00000000000..69e28b3319a
--- /dev/null
+++ b/src/test/ui/unused-crate-deps/deny-cmdline.rs
@@ -0,0 +1,8 @@
+// Check for unused crate dep, deny, expect failure
+
+// edition:2018
+// compile-flags: -Dunused-crate-dependencies
+// aux-crate:bar=bar.rs
+
+fn main() {}
+//~^ ERROR external crate `bar` unused in
diff --git a/src/test/ui/unused-crate-deps/deny-cmdline.stderr b/src/test/ui/unused-crate-deps/deny-cmdline.stderr
new file mode 100644
index 00000000000..0951dc670fe
--- /dev/null
+++ b/src/test/ui/unused-crate-deps/deny-cmdline.stderr
@@ -0,0 +1,10 @@
+error: external crate `bar` unused in `deny_cmdline`: remove the dependency or add `use bar as _;`
+  --> $DIR/deny-cmdline.rs:7:1
+   |
+LL | fn main() {}
+   | ^
+   |
+   = note: requested on the command line with `-D unused-crate-dependencies`
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/unused-crate-deps/warn-cmdline-json.rs b/src/test/ui/unused-crate-deps/warn-cmdline-json.rs
new file mode 100644
index 00000000000..4826c0062d0
--- /dev/null
+++ b/src/test/ui/unused-crate-deps/warn-cmdline-json.rs
@@ -0,0 +1,8 @@
+// Check for unused crate dep, warn, json event, expect pass
+
+// edition:2018
+// check-pass
+// compile-flags: -Wunused-crate-dependencies -Zunstable-options --json unused-externs --error-format=json
+// aux-crate:bar=bar.rs
+
+fn main() {}
diff --git a/src/test/ui/unused-crate-deps/warn-cmdline-json.stderr b/src/test/ui/unused-crate-deps/warn-cmdline-json.stderr
new file mode 100644
index 00000000000..98dbd763927
--- /dev/null
+++ b/src/test/ui/unused-crate-deps/warn-cmdline-json.stderr
@@ -0,0 +1 @@
+{"lint_level":"warn","unused_extern_names":["bar"]}
diff --git a/src/test/ui/wf/wf-impl-associated-type-region.stderr b/src/test/ui/wf/wf-impl-associated-type-region.stderr
index 3f324190b7b..b9d4857a3ef 100644
--- a/src/test/ui/wf/wf-impl-associated-type-region.stderr
+++ b/src/test/ui/wf/wf-impl-associated-type-region.stderr
@@ -1,10 +1,13 @@
 error[E0309]: the parameter type `T` may not live long enough
   --> $DIR/wf-impl-associated-type-region.rs:10:16
    |
-LL | impl<'a, T> Foo<'a> for T {
-   |          - help: consider adding an explicit lifetime bound...: `T: 'a`
 LL |     type Bar = &'a T;
    |                ^^^^^ ...so that the reference type `&'a T` does not outlive the data it points at
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL | impl<'a, T: 'a> Foo<'a> for T {
+   |           ++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/wf/wf-in-fn-type-static.stderr b/src/test/ui/wf/wf-in-fn-type-static.stderr
index 44cacf4ef4d..73fbb9ca670 100644
--- a/src/test/ui/wf/wf-in-fn-type-static.stderr
+++ b/src/test/ui/wf/wf-in-fn-type-static.stderr
@@ -1,20 +1,24 @@
 error[E0310]: the parameter type `T` may not live long enough
   --> $DIR/wf-in-fn-type-static.rs:13:8
    |
-LL | struct Foo<T> {
-   |            - help: consider adding an explicit lifetime bound...: `T: 'static`
-LL |     // needs T: 'static
 LL |     x: fn() -> &'static T
    |        ^^^^^^^^^^^^^^^^^^ ...so that the reference type `&'static T` does not outlive the data it points at
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL | struct Foo<T: 'static> {
+   |             +++++++++
 
 error[E0310]: the parameter type `T` may not live long enough
   --> $DIR/wf-in-fn-type-static.rs:18:8
    |
-LL | struct Bar<T> {
-   |            - help: consider adding an explicit lifetime bound...: `T: 'static`
-LL |     // needs T: Copy
 LL |     x: fn(&'static T)
    |        ^^^^^^^^^^^^^^ ...so that the reference type `&'static T` does not outlive the data it points at
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL | struct Bar<T: 'static> {
+   |             +++++++++
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/wf/wf-in-obj-type-static.stderr b/src/test/ui/wf/wf-in-obj-type-static.stderr
index c50a6bb6e4d..c3ad42dd5d5 100644
--- a/src/test/ui/wf/wf-in-obj-type-static.stderr
+++ b/src/test/ui/wf/wf-in-obj-type-static.stderr
@@ -1,11 +1,13 @@
 error[E0310]: the parameter type `T` may not live long enough
   --> $DIR/wf-in-obj-type-static.rs:14:8
    |
-LL | struct Foo<T> {
-   |            - help: consider adding an explicit lifetime bound...: `T: 'static`
-LL |     // needs T: 'static
 LL |     x: dyn Object<&'static T>
    |        ^^^^^^^^^^^^^^^^^^^^^^ ...so that the reference type `&'static T` does not outlive the data it points at
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL | struct Foo<T: 'static> {
+   |             +++++++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/wf/wf-outlives-ty-in-fn-or-trait.stderr b/src/test/ui/wf/wf-outlives-ty-in-fn-or-trait.stderr
index 68c1e9091d7..4d4d8b2ab4d 100644
--- a/src/test/ui/wf/wf-outlives-ty-in-fn-or-trait.stderr
+++ b/src/test/ui/wf/wf-outlives-ty-in-fn-or-trait.stderr
@@ -1,18 +1,24 @@
 error[E0309]: the parameter type `T` may not live long enough
   --> $DIR/wf-outlives-ty-in-fn-or-trait.rs:9:16
    |
-LL | impl<'a, T> Trait<'a, T> for usize {
-   |          - help: consider adding an explicit lifetime bound...: `T: 'a`
 LL |     type Out = &'a fn(T);
    |                ^^^^^^^^^ ...so that the reference type `&'a fn(T)` does not outlive the data it points at
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL | impl<'a, T: 'a> Trait<'a, T> for usize {
+   |           ++++
 
 error[E0309]: the parameter type `T` may not live long enough
   --> $DIR/wf-outlives-ty-in-fn-or-trait.rs:19:16
    |
-LL | impl<'a, T> Trait<'a, T> for u32 {
-   |          - help: consider adding an explicit lifetime bound...: `T: 'a`
 LL |     type Out = &'a dyn Baz<T>;
    |                ^^^^^^^^^^^^^^ ...so that the reference type `&'a (dyn Baz<T> + 'a)` does not outlive the data it points at
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL | impl<'a, T: 'a> Trait<'a, T> for u32 {
+   |           ++++
 
 error: aborting due to 2 previous errors
 
diff --git a/src/tools/cargo b/src/tools/cargo
-Subproject dba5baf4345858c591517b24801902a062c399f
+Subproject f63f23ff1f1a12ede8585bbd1bbf0c536e50293
diff --git a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs
index 85f95237549..2bf7f868905 100644
--- a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs
+++ b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs
@@ -82,7 +82,7 @@ impl CognitiveComplexity {
 
         if rust_cc > self.limit.limit() {
             let fn_span = match kind {
-                FnKind::ItemFn(ident, _, _, _) | FnKind::Method(ident, _, _) => ident.span,
+                FnKind::ItemFn(ident, _, _) | FnKind::Method(ident, _) => ident.span,
                 FnKind::Closure => {
                     let header_span = body_span.with_hi(decl.output.span().lo());
                     let pos = snippet_opt(cx, header_span).and_then(|snip| {
diff --git a/src/tools/clippy/clippy_lints/src/enum_variants.rs b/src/tools/clippy/clippy_lints/src/enum_variants.rs
index 1f4353fa4f7..346d03ca556 100644
--- a/src/tools/clippy/clippy_lints/src/enum_variants.rs
+++ b/src/tools/clippy/clippy_lints/src/enum_variants.rs
@@ -260,7 +260,7 @@ impl LateLintPass<'_> for EnumVariantNames {
                     }
                     // The `module_name_repetitions` lint should only trigger if the item has the module in its
                     // name. Having the same name is accepted.
-                    if item.vis.node.is_pub() && item_camel.len() > mod_camel.len() {
+                    if cx.tcx.visibility(item.def_id).is_public() && item_camel.len() > mod_camel.len() {
                         let matching = count_match_start(mod_camel, &item_camel);
                         let rmatching = count_match_end(mod_camel, &item_camel);
                         let nchars = mod_camel.chars().count();
diff --git a/src/tools/clippy/clippy_lints/src/exhaustive_items.rs b/src/tools/clippy/clippy_lints/src/exhaustive_items.rs
index b0f50b5c144..173d41b4b05 100644
--- a/src/tools/clippy/clippy_lints/src/exhaustive_items.rs
+++ b/src/tools/clippy/clippy_lints/src/exhaustive_items.rs
@@ -78,7 +78,10 @@ impl LateLintPass<'_> for ExhaustiveItems {
             if !attrs.iter().any(|a| a.has_name(sym::non_exhaustive));
             then {
                 let (lint, msg) = if let ItemKind::Struct(ref v, ..) = item.kind {
-                    if v.fields().iter().any(|f| !f.vis.node.is_pub()) {
+                    if v.fields().iter().any(|f| {
+                        let def_id = cx.tcx.hir().local_def_id(f.hir_id);
+                        !cx.tcx.visibility(def_id).is_public()
+                    }) {
                         // skip structs with private fields
                         return;
                     }
diff --git a/src/tools/clippy/clippy_lints/src/functions/must_use.rs b/src/tools/clippy/clippy_lints/src/functions/must_use.rs
index 0709580c8ad..5462d913fb4 100644
--- a/src/tools/clippy/clippy_lints/src/functions/must_use.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/must_use.rs
@@ -108,7 +108,7 @@ fn check_needless_must_use(
                 diag.span_suggestion(
                     attr.span,
                     "remove the attribute",
-                    "".into(),
+                    "",
                     Applicability::MachineApplicable,
                 );
             },
diff --git a/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs
index 830e3b32cfa..565a1c871d7 100644
--- a/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs
@@ -17,8 +17,8 @@ pub(super) fn check_fn<'tcx>(
     hir_id: hir::HirId,
 ) {
     let unsafety = match kind {
-        intravisit::FnKind::ItemFn(_, _, hir::FnHeader { unsafety, .. }, _) => unsafety,
-        intravisit::FnKind::Method(_, sig, _) => sig.header.unsafety,
+        intravisit::FnKind::ItemFn(_, _, hir::FnHeader { unsafety, .. }) => unsafety,
+        intravisit::FnKind::Method(_, sig) => sig.header.unsafety,
         intravisit::FnKind::Closure => return,
     };
 
diff --git a/src/tools/clippy/clippy_lints/src/functions/too_many_arguments.rs b/src/tools/clippy/clippy_lints/src/functions/too_many_arguments.rs
index 3af960491ed..5c8d8b8e755 100644
--- a/src/tools/clippy/clippy_lints/src/functions/too_many_arguments.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/too_many_arguments.rs
@@ -26,9 +26,8 @@ pub(super) fn check_fn(
                     header: hir::FnHeader { abi: Abi::Rust, .. },
                     ..
                 },
-                _,
             )
-            | intravisit::FnKind::ItemFn(_, _, hir::FnHeader { abi: Abi::Rust, .. }, _) => check_arg_number(
+            | intravisit::FnKind::ItemFn(_, _, hir::FnHeader { abi: Abi::Rust, .. }) => check_arg_number(
                 cx,
                 decl,
                 span.with_hi(decl.output.span().hi()),
diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs
index b09c23f31e9..662a561f171 100644
--- a/src/tools/clippy/clippy_lints/src/lifetimes.rs
+++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs
@@ -8,7 +8,7 @@ use rustc_hir::FnRetTy::Return;
 use rustc_hir::{
     BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem,
     ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, ParamName, PolyTraitRef, TraitBoundModifier,
-    TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WhereClause, WherePredicate,
+    TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate,
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -130,7 +130,7 @@ fn check_fn_inner<'tcx>(
     span: Span,
     report_extra_lifetimes: bool,
 ) {
-    if span.from_expansion() || has_where_lifetimes(cx, &generics.where_clause) {
+    if span.from_expansion() || has_where_lifetimes(cx, generics) {
         return;
     }
 
@@ -139,28 +139,35 @@ fn check_fn_inner<'tcx>(
         .iter()
         .filter(|param| matches!(param.kind, GenericParamKind::Type { .. }));
     for typ in types {
-        for bound in typ.bounds {
-            let mut visitor = RefVisitor::new(cx);
-            walk_param_bound(&mut visitor, bound);
-            if visitor.lts.iter().any(|lt| matches!(lt, RefLt::Named(_))) {
-                return;
+        for pred in generics.bounds_for_param(cx.tcx.hir().local_def_id(typ.hir_id)) {
+            if pred.in_where_clause {
+                // has_where_lifetimes checked that this predicate contains no lifetime.
+                continue;
             }
-            if let GenericBound::Trait(ref trait_ref, _) = *bound {
-                let params = &trait_ref
-                    .trait_ref
-                    .path
-                    .segments
-                    .last()
-                    .expect("a path must have at least one segment")
-                    .args;
-                if let Some(params) = *params {
-                    let lifetimes = params.args.iter().filter_map(|arg| match arg {
-                        GenericArg::Lifetime(lt) => Some(lt),
-                        _ => None,
-                    });
-                    for bound in lifetimes {
-                        if bound.name != LifetimeName::Static && !bound.is_elided() {
-                            return;
+
+            for bound in pred.bounds {
+                let mut visitor = RefVisitor::new(cx);
+                walk_param_bound(&mut visitor, bound);
+                if visitor.lts.iter().any(|lt| matches!(lt, RefLt::Named(_))) {
+                    return;
+                }
+                if let GenericBound::Trait(ref trait_ref, _) = *bound {
+                    let params = &trait_ref
+                        .trait_ref
+                        .path
+                        .segments
+                        .last()
+                        .expect("a path must have at least one segment")
+                        .args;
+                    if let Some(params) = *params {
+                        let lifetimes = params.args.iter().filter_map(|arg| match arg {
+                            GenericArg::Lifetime(lt) => Some(lt),
+                            _ => None,
+                        });
+                        for bound in lifetimes {
+                            if bound.name != LifetimeName::Static && !bound.is_elided() {
+                                return;
+                            }
                         }
                     }
                 }
@@ -322,9 +329,7 @@ fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet<RefLt> {
     let mut allowed_lts = FxHashSet::default();
     for par in named_generics.iter() {
         if let GenericParamKind::Lifetime { .. } = par.kind {
-            if par.bounds.is_empty() {
-                allowed_lts.insert(RefLt::Named(par.name.ident().name));
-            }
+            allowed_lts.insert(RefLt::Named(par.name.ident().name));
         }
     }
     allowed_lts.insert(RefLt::Unnamed);
@@ -445,8 +450,8 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
 
 /// Are any lifetimes mentioned in the `where` clause? If so, we don't try to
 /// reason about elision.
-fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereClause<'_>) -> bool {
-    for predicate in where_clause.predicates {
+fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_>) -> bool {
+    for predicate in generics.predicates {
         match *predicate {
             WherePredicate::RegionPredicate(..) => return true,
             WherePredicate::BoundPredicate(ref pred) => {
diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
index d29d07da7b0..4034079a90c 100644
--- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
@@ -85,7 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
         }
 
         match kind {
-            FnKind::ItemFn(.., header, _) => {
+            FnKind::ItemFn(.., header) => {
                 let attrs = cx.tcx.hir().attrs(hir_id);
                 if header.abi != Abi::Rust || requires_exact_signature(attrs) {
                     return;
@@ -241,7 +241,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
                                                 |x| Cow::from(format!("change `{}` to", x)),
                                             )
                                             .as_ref(),
-                                        suggestion.into(),
+                                        suggestion,
                                         Applicability::Unspecified,
                                     );
                                 }
@@ -271,7 +271,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
                                                 |x| Cow::from(format!("change `{}` to", x))
                                             )
                                             .as_ref(),
-                                        suggestion.into(),
+                                        suggestion,
                                         Applicability::Unspecified,
                                     );
                                 }
diff --git a/src/tools/clippy/clippy_lints/src/new_without_default.rs b/src/tools/clippy/clippy_lints/src/new_without_default.rs
index 9419056be14..96c00c205ff 100644
--- a/src/tools/clippy/clippy_lints/src/new_without_default.rs
+++ b/src/tools/clippy/clippy_lints/src/new_without_default.rs
@@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
             ..
         }) = item.kind
         {
-            for assoc_item in items {
+            for assoc_item in *items {
                 if assoc_item.kind == (hir::AssocItemKind::Fn { has_self: false }) {
                     let impl_item = cx.tcx.hir().impl_item(assoc_item.id);
                     if in_external_macro(cx.sess(), impl_item.span) {
diff --git a/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs
index e827cdaae87..1469cb434c0 100644
--- a/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs
+++ b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs
@@ -42,7 +42,7 @@ impl<'tcx> LateLintPass<'tcx> for PartialEqNeImpl {
             if let Some(eq_trait) = cx.tcx.lang_items().eq_trait();
             if trait_ref.path.res.def_id() == eq_trait;
             then {
-                for impl_item in impl_items {
+                for impl_item in *impl_items {
                     if impl_item.ident.name == sym::ne {
                         span_lint_hir(
                             cx,
diff --git a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
index d59249d7f13..9af3059a37f 100644
--- a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
+++ b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
@@ -251,7 +251,7 @@ impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue {
         }
 
         match kind {
-            FnKind::ItemFn(.., header, _) => {
+            FnKind::ItemFn(.., header) => {
                 if header.abi != Abi::Rust {
                     return;
                 }
diff --git a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs
index 2cee3c14d7f..e2e2400f8e2 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs
@@ -1,8 +1,10 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use rustc_errors::Applicability;
-use rustc_hir::{Item, ItemKind, VisibilityKind};
+use rustc_hir::{Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::def_id::CRATE_DEF_ID;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -41,7 +43,7 @@ impl_lint_pass!(RedundantPubCrate => [REDUNDANT_PUB_CRATE]);
 
 impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
-        if let VisibilityKind::Crate { .. } = item.vis.node {
+        if cx.tcx.visibility(item.def_id) == ty::Visibility::Restricted(CRATE_DEF_ID.to_def_id()) {
             if !cx.access_levels.is_exported(item.def_id) && self.is_exported.last() == Some(&false) {
                 let span = item.span.with_hi(item.ident.span.hi());
                 let descr = cx.tcx.def_kind(item.def_id).descr(item.def_id.to_def_id());
@@ -52,7 +54,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate {
                     &format!("pub(crate) {} inside private module", descr),
                     |diag| {
                         diag.span_suggestion(
-                            item.vis.span,
+                            item.vis_span,
                             "consider using",
                             "pub".to_string(),
                             Applicability::MachineApplicable,
diff --git a/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs b/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs
index 79f104eac0b..91e5e1e8b28 100644
--- a/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs
+++ b/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs
@@ -111,7 +111,7 @@ impl<'tcx> LateLintPass<'tcx> for ReturnSelfNotMustUse {
     ) {
         if_chain! {
             // We are only interested in methods, not in functions or associated functions.
-            if matches!(kind, FnKind::Method(_, _, _));
+            if matches!(kind, FnKind::Method(_, _));
             if let Some(fn_def) = cx.tcx.hir().opt_local_def_id(hir_id);
             if let Some(impl_def) = cx.tcx.impl_of_method(fn_def.to_def_id());
             // We don't want this method to be te implementation of a trait because the
diff --git a/src/tools/clippy/clippy_lints/src/serde_api.rs b/src/tools/clippy/clippy_lints/src/serde_api.rs
index 398e2c200de..fc1c2af9257 100644
--- a/src/tools/clippy/clippy_lints/src/serde_api.rs
+++ b/src/tools/clippy/clippy_lints/src/serde_api.rs
@@ -36,7 +36,7 @@ impl<'tcx> LateLintPass<'tcx> for SerdeApi {
                 if did == visit_did {
                     let mut seen_str = None;
                     let mut seen_string = None;
-                    for item in items {
+                    for item in *items {
                         match item.ident.as_str() {
                             "visit_str" => seen_str = Some(item.span),
                             "visit_string" => seen_string = Some(item.span),
diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs
index 43e0132a7ec..3d1b2ee925b 100644
--- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs
+++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs
@@ -8,8 +8,7 @@ use rustc_data_structures::unhash::UnhashMap;
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
 use rustc_hir::{
-    GenericBound, Generics, Item, ItemKind, Node, ParamName, Path, PathSegment, QPath, TraitItem, Ty, TyKind,
-    WherePredicate,
+    GenericBound, Generics, Item, ItemKind, Node, Path, PathSegment, QPath, TraitItem, Ty, TyKind, WherePredicate,
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
@@ -90,10 +89,9 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
     }
 
     fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'tcx>) {
-        let Generics { where_clause, .. } = &item.generics;
         let mut self_bounds_map = FxHashMap::default();
 
-        for predicate in where_clause.predicates {
+        for predicate in item.generics.predicates {
             if_chain! {
                 if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate;
                 if !bound_predicate.span.from_expansion();
@@ -166,7 +164,7 @@ impl TraitBounds {
         }
         let mut map: UnhashMap<SpanlessTy<'_, '_>, Vec<&GenericBound<'_>>> = UnhashMap::default();
         let mut applicability = Applicability::MaybeIncorrect;
-        for bound in gen.where_clause.predicates {
+        for bound in gen.predicates {
             if_chain! {
                 if let WherePredicate::BoundPredicate(ref p) = bound;
                 if p.bounds.len() as u64 <= self.max_trait_bounds;
@@ -216,34 +214,23 @@ impl TraitBounds {
 }
 
 fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
-    if gen.span.from_expansion() || gen.params.is_empty() || gen.where_clause.predicates.is_empty() {
+    if gen.span.from_expansion() || gen.params.is_empty() || gen.predicates.is_empty() {
         return;
     }
 
-    let mut map = FxHashMap::default();
-    for param in gen.params {
-        if let ParamName::Plain(ref ident) = param.name {
-            let res = param
-                .bounds
-                .iter()
-                .filter_map(get_trait_info_from_bound)
-                .collect::<Vec<_>>();
-            map.insert(*ident, res);
-        }
-    }
-
-    for predicate in gen.where_clause.predicates {
+    let mut map = FxHashMap::<_, Vec<_>>::default();
+    for predicate in gen.predicates {
         if_chain! {
             if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate;
             if !bound_predicate.span.from_expansion();
             if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind;
             if let Some(segment) = segments.first();
-            if let Some(trait_resolutions_direct) = map.get(&segment.ident);
             then {
-                for (res_where, _,  _) in bound_predicate.bounds.iter().filter_map(get_trait_info_from_bound) {
-                    if let Some((_, _, span_direct)) = trait_resolutions_direct
+                for (res_where, _, span_where) in bound_predicate.bounds.iter().filter_map(get_trait_info_from_bound) {
+                    let trait_resolutions_direct = map.entry(segment.ident).or_default();
+                    if let Some((_, span_direct)) = trait_resolutions_direct
                                                 .iter()
-                                                .find(|(res_direct, _, _)| *res_direct == res_where) {
+                                                .find(|(res_direct, _)| *res_direct == res_where) {
                         span_lint_and_help(
                             cx,
                             TRAIT_DUPLICATION_IN_BOUNDS,
@@ -253,6 +240,9 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
                             "consider removing this trait bound",
                         );
                     }
+                    else {
+                        trait_resolutions_direct.push((res_where, span_where))
+                    }
                 }
             }
         }
diff --git a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs
index 7c06906293b..f35f44eda56 100644
--- a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs
+++ b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs
@@ -104,8 +104,10 @@ fn get_bounds_if_impl_trait<'tcx>(cx: &LateContext<'tcx>, qpath: &QPath<'_>, id:
         if let Some(Node::GenericParam(generic_param)) = cx.tcx.hir().get_if_local(did);
         if let GenericParamKind::Type { synthetic, .. } = generic_param.kind;
         if synthetic;
+        if let Some(generics) = cx.tcx.hir().get_generics(id.owner);
+        if let Some(pred) = generics.bounds_for_param(did.expect_local()).next();
         then {
-            Some(generic_param.bounds)
+            Some(pred.bounds)
         } else {
             None
         }
diff --git a/src/tools/clippy/clippy_lints/src/unused_async.rs b/src/tools/clippy/clippy_lints/src/unused_async.rs
index 2b89398ecd6..41333bb2add 100644
--- a/src/tools/clippy/clippy_lints/src/unused_async.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_async.rs
@@ -67,7 +67,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync {
         span: Span,
         hir_id: HirId,
     ) {
-        if let FnKind::ItemFn(_, _, FnHeader { asyncness, .. }, _) = &fn_kind {
+        if let FnKind::ItemFn(_, _, FnHeader { asyncness, .. }) = &fn_kind {
             if matches!(asyncness, IsAsync::Async) {
                 let mut visitor = AsyncFnVisitor { cx, found_await: false };
                 walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), span, hir_id);
diff --git a/src/tools/clippy/clippy_lints/src/utils/inspector.rs b/src/tools/clippy/clippy_lints/src/utils/inspector.rs
index a04288e0a41..37b114a0cfb 100644
--- a/src/tools/clippy/clippy_lints/src/utils/inspector.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/inspector.rs
@@ -4,6 +4,7 @@ use clippy_utils::get_attr;
 use rustc_ast::ast::{Attribute, InlineAsmTemplatePiece};
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::ty;
 use rustc_session::Session;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
@@ -45,14 +46,16 @@ impl<'tcx> LateLintPass<'tcx> for DeepCodeInspector {
             return;
         }
         println!("impl item `{}`", item.ident.name);
-        match item.vis.node {
-            hir::VisibilityKind::Public => println!("public"),
-            hir::VisibilityKind::Crate(_) => println!("visible crate wide"),
-            hir::VisibilityKind::Restricted { path, .. } => println!(
-                "visible in module `{}`",
-                rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(path, false))
-            ),
-            hir::VisibilityKind::Inherited => println!("visibility inherited from outer item"),
+        match cx.tcx.visibility(item.def_id) {
+            ty::Visibility::Public => println!("public"),
+            ty::Visibility::Restricted(def_id) => {
+                if def_id.is_top_level_module() {
+                    println!("visible crate wide")
+                } else {
+                    println!("visible in module `{}`", cx.tcx.def_path_str(def_id))
+                }
+            },
+            ty::Visibility::Invisible => println!("invisible"),
         }
         match item.kind {
             hir::ImplItemKind::Const(_, body_id) => {
@@ -360,14 +363,16 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) {
 fn print_item(cx: &LateContext<'_>, item: &hir::Item<'_>) {
     let did = item.def_id;
     println!("item `{}`", item.ident.name);
-    match item.vis.node {
-        hir::VisibilityKind::Public => println!("public"),
-        hir::VisibilityKind::Crate(_) => println!("visible crate wide"),
-        hir::VisibilityKind::Restricted { path, .. } => println!(
-            "visible in module `{}`",
-            rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(path, false))
-        ),
-        hir::VisibilityKind::Inherited => println!("visibility inherited from outer item"),
+    match cx.tcx.visibility(item.def_id) {
+        ty::Visibility::Public => println!("public"),
+        ty::Visibility::Restricted(def_id) => {
+            if def_id.is_top_level_module() {
+                println!("visible crate wide")
+            } else {
+                println!("visible in module `{}`", cx.tcx.def_path_str(def_id))
+            }
+        },
+        ty::Visibility::Invisible => println!("invisible"),
     }
     match item.kind {
         hir::ItemKind::ExternCrate(ref _renamed_from) => {
diff --git a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
index 832da66a536..2f74eaf3cf5 100644
--- a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
+++ b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
@@ -8,6 +8,7 @@ use rustc_hir::{
     Item, ItemKind, PathSegment, UseKind,
 };
 use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::symbol::kw;
 use rustc_span::{sym, BytePos};
@@ -115,7 +116,8 @@ impl LateLintPass<'_> for WildcardImports {
         if is_test_module_or_function(cx.tcx, item) {
             self.test_modules_deep = self.test_modules_deep.saturating_add(1);
         }
-        if item.vis.node.is_pub() || item.vis.node.is_pub_restricted() {
+        let module = cx.tcx.parent_module_from_def_id(item.def_id);
+        if cx.tcx.visibility(item.def_id) != ty::Visibility::Restricted(module.to_def_id()) {
             return;
         }
         if_chain! {
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index a275bac4ce6..74978720424 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -1690,7 +1690,7 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>,
 
 /// Checks if the given function kind is an async function.
 pub fn is_async_fn(kind: FnKind<'_>) -> bool {
-    matches!(kind, FnKind::ItemFn(_, _, header, _) if header.asyncness == IsAsync::Async)
+    matches!(kind, FnKind::ItemFn(_, _, header) if header.asyncness == IsAsync::Async)
 }
 
 /// Peels away all the compiler generated code surrounding the body of an async function,
diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs
index 1fc9979f3dd..794d2e1026f 100644
--- a/src/tools/clippy/clippy_utils/src/sugg.rs
+++ b/src/tools/clippy/clippy_utils/src/sugg.rs
@@ -214,6 +214,7 @@ impl<'a> Sugg<'a> {
             | ast::ExprKind::Path(..)
             | ast::ExprKind::Repeat(..)
             | ast::ExprKind::Ret(..)
+            | ast::ExprKind::Yeet(..)
             | ast::ExprKind::Struct(..)
             | ast::ExprKind::Try(..)
             | ast::ExprKind::TryBlock(..)
diff --git a/src/tools/clippy/tests/ui/extra_unused_lifetimes.stderr b/src/tools/clippy/tests/ui/extra_unused_lifetimes.stderr
index ebdb8e74952..9143fb2c208 100644
--- a/src/tools/clippy/tests/ui/extra_unused_lifetimes.stderr
+++ b/src/tools/clippy/tests/ui/extra_unused_lifetimes.stderr
@@ -7,12 +7,6 @@ LL | fn unused_lt<'a>(x: u8) {}
    = note: `-D clippy::extra-unused-lifetimes` implied by `-D warnings`
 
 error: this lifetime isn't used in the function definition
-  --> $DIR/extra_unused_lifetimes.rs:16:25
-   |
-LL | fn unused_lt_transitive<'a, 'b: 'a>(x: &'b u8) {
-   |                         ^^
-
-error: this lifetime isn't used in the function definition
   --> $DIR/extra_unused_lifetimes.rs:41:10
    |
 LL |     fn x<'a>(&self) {}
@@ -24,5 +18,5 @@ error: this lifetime isn't used in the function definition
 LL |         fn unused_lt<'a>(x: u8) {}
    |                      ^^
 
-error: aborting due to 4 previous errors
+error: aborting due to 3 previous errors
 
diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.stderr b/src/tools/clippy/tests/ui/needless_lifetimes.stderr
index ffa152427a9..a488bc01fff 100644
--- a/src/tools/clippy/tests/ui/needless_lifetimes.stderr
+++ b/src/tools/clippy/tests/ui/needless_lifetimes.stderr
@@ -109,12 +109,6 @@ LL |         fn baz<'a>(&'a self) -> impl Foo + 'a {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
-  --> $DIR/needless_lifetimes.rs:307:5
-   |
-LL |     fn impl_trait_elidable_nested_named_lifetimes<'a>(i: &'a i32, f: impl for<'b> Fn(&'b i32) -> &'b i32) -> &'a i32 {
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
   --> $DIR/needless_lifetimes.rs:310:5
    |
 LL |     fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 {
@@ -192,5 +186,5 @@ error: explicit lifetimes given in parameter types where they could be elided (o
 LL |         fn lifetime_elsewhere_provided<'a>(self: Box<Self>, here: &'a ()) -> &'a () {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 32 previous errors
+error: aborting due to 31 previous errors
 
diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index 1bf6e6d011e..2cb368c6881 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -198,11 +198,8 @@ pub struct Config {
     /// The rust-demangler executable.
     pub rust_demangler_path: Option<PathBuf>,
 
-    /// The Python executable to use for LLDB.
-    pub lldb_python: String,
-
-    /// The Python executable to use for htmldocck.
-    pub docck_python: String,
+    /// The Python executable to use for LLDB and htmldocck.
+    pub python: String,
 
     /// The jsondocck executable.
     pub jsondocck_path: Option<String>,
diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs
index 858a576dcb4..e6f058569db 100644
--- a/src/tools/compiletest/src/header.rs
+++ b/src/tools/compiletest/src/header.rs
@@ -157,6 +157,8 @@ pub struct TestProps {
     pub should_ice: bool,
     // If true, the stderr is expected to be different across bit-widths.
     pub stderr_per_bitwidth: bool,
+    // The MIR opt to unit test, if any
+    pub mir_unit_test: Option<String>,
 }
 
 mod directives {
@@ -189,6 +191,7 @@ mod directives {
     pub const STDERR_PER_BITWIDTH: &'static str = "stderr-per-bitwidth";
     pub const INCREMENTAL: &'static str = "incremental";
     pub const KNOWN_BUG: &'static str = "known-bug";
+    pub const MIR_UNIT_TEST: &'static str = "unit-test";
     // This isn't a real directive, just one that is probably mistyped often
     pub const INCORRECT_COMPILER_FLAGS: &'static str = "compiler-flags";
 }
@@ -232,6 +235,7 @@ impl TestProps {
             assembly_output: None,
             should_ice: false,
             stderr_per_bitwidth: false,
+            mir_unit_test: None,
         }
     }
 
@@ -392,6 +396,9 @@ impl TestProps {
                 config.set_name_directive(ln, STDERR_PER_BITWIDTH, &mut self.stderr_per_bitwidth);
                 config.set_name_directive(ln, INCREMENTAL, &mut self.incremental);
                 config.set_name_directive(ln, KNOWN_BUG, &mut self.known_bug);
+                config.set_name_value_directive(ln, MIR_UNIT_TEST, &mut self.mir_unit_test, |s| {
+                    s.trim().to_string()
+                });
             });
         }
 
diff --git a/src/tools/compiletest/src/header/tests.rs b/src/tools/compiletest/src/header/tests.rs
index 5b144a1020f..a8fd4880f07 100644
--- a/src/tools/compiletest/src/header/tests.rs
+++ b/src/tools/compiletest/src/header/tests.rs
@@ -43,8 +43,7 @@ fn config() -> Config {
         "--compile-lib-path=",
         "--run-lib-path=",
         "--rustc-path=",
-        "--lldb-python=",
-        "--docck-python=",
+        "--python=",
         "--jsondocck-path=",
         "--src-base=",
         "--build-base=",
diff --git a/src/tools/compiletest/src/json.rs b/src/tools/compiletest/src/json.rs
index a5ff779a4ab..10726b98420 100644
--- a/src/tools/compiletest/src/json.rs
+++ b/src/tools/compiletest/src/json.rs
@@ -23,6 +23,14 @@ struct ArtifactNotification {
     artifact: PathBuf,
 }
 
+#[derive(Deserialize)]
+struct UnusedExternNotification {
+    #[allow(dead_code)]
+    lint_level: String,
+    #[allow(dead_code)]
+    unused_extern_names: Vec<String>,
+}
+
 #[derive(Deserialize, Clone)]
 struct DiagnosticSpan {
     file_name: String,
@@ -113,6 +121,9 @@ pub fn extract_rendered(output: &str) -> String {
                 } else if serde_json::from_str::<ArtifactNotification>(line).is_ok() {
                     // Ignore the notification.
                     None
+                } else if serde_json::from_str::<UnusedExternNotification>(line).is_ok() {
+                    // Ignore the notification.
+                    None
                 } else {
                     print!(
                         "failed to decode compiler output as json: line: {}\noutput: {}",
diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs
index 8c1f28f1407..3d11ea21acf 100644
--- a/src/tools/compiletest/src/main.rs
+++ b/src/tools/compiletest/src/main.rs
@@ -61,8 +61,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
         .reqopt("", "rustc-path", "path to rustc to use for compiling", "PATH")
         .optopt("", "rustdoc-path", "path to rustdoc to use for compiling", "PATH")
         .optopt("", "rust-demangler-path", "path to rust-demangler to use in tests", "PATH")
-        .reqopt("", "lldb-python", "path to python to use for doc tests", "PATH")
-        .reqopt("", "docck-python", "path to python to use for doc tests", "PATH")
+        .reqopt("", "python", "path to python to use for doc tests", "PATH")
         .optopt("", "jsondocck-path", "path to jsondocck to use for doc tests", "PATH")
         .optopt("", "valgrind-path", "path to Valgrind executable for Valgrind tests", "PROGRAM")
         .optflag("", "force-valgrind", "fail if Valgrind tests cannot be run under Valgrind")
@@ -222,8 +221,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
         rustc_path: opt_path(matches, "rustc-path"),
         rustdoc_path: matches.opt_str("rustdoc-path").map(PathBuf::from),
         rust_demangler_path: matches.opt_str("rust-demangler-path").map(PathBuf::from),
-        lldb_python: matches.opt_str("lldb-python").unwrap(),
-        docck_python: matches.opt_str("docck-python").unwrap(),
+        python: matches.opt_str("python").unwrap(),
         jsondocck_path: matches.opt_str("jsondocck-path"),
         valgrind_path: matches.opt_str("valgrind-path"),
         force_valgrind: matches.opt_present("force-valgrind"),
@@ -667,6 +665,40 @@ fn stamp(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> Path
     output_base_dir(config, testpaths, revision).join("stamp")
 }
 
+fn files_related_to_test(
+    config: &Config,
+    testpaths: &TestPaths,
+    props: &EarlyProps,
+    revision: Option<&str>,
+) -> Vec<PathBuf> {
+    let mut related = vec![];
+
+    if testpaths.file.is_dir() {
+        // run-make tests use their individual directory
+        for entry in WalkDir::new(&testpaths.file) {
+            let path = entry.unwrap().into_path();
+            if path.is_file() {
+                related.push(path);
+            }
+        }
+    } else {
+        related.push(testpaths.file.clone());
+    }
+
+    for aux in &props.aux {
+        let path = testpaths.file.parent().unwrap().join("auxiliary").join(aux);
+        related.push(path);
+    }
+
+    // UI test files.
+    for extension in UI_EXTENSIONS {
+        let path = expected_output_path(testpaths, revision, &config.compare_mode, extension);
+        related.push(path);
+    }
+
+    related
+}
+
 fn is_up_to_date(
     config: &Config,
     testpaths: &TestPaths,
@@ -688,20 +720,10 @@ fn is_up_to_date(
 
     // Check timestamps.
     let mut inputs = inputs.clone();
-    // Use `add_dir` to account for run-make tests, which use their individual directory
-    inputs.add_dir(&testpaths.file);
-
-    for aux in &props.aux {
-        let path = testpaths.file.parent().unwrap().join("auxiliary").join(aux);
+    for path in files_related_to_test(config, testpaths, props, revision) {
         inputs.add_path(&path);
     }
 
-    // UI test files.
-    for extension in UI_EXTENSIONS {
-        let path = &expected_output_path(testpaths, revision, &config.compare_mode, extension);
-        inputs.add_path(path);
-    }
-
     inputs < Stamp::from_path(&stamp_name)
 }
 
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 6b27d1ecbf5..6d94fe3ebb9 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -179,7 +179,7 @@ pub fn compute_stamp_hash(config: &Config) -> String {
         }
 
         Some(Debugger::Lldb) => {
-            config.lldb_python.hash(&mut hash);
+            config.python.hash(&mut hash);
             config.lldb_python_dir.hash(&mut hash);
             env::var_os("PATH").hash(&mut hash);
             env::var_os("PYTHONPATH").hash(&mut hash);
@@ -1141,7 +1141,7 @@ impl<'test> TestCx<'test> {
         // Prepare the lldb_batchmode which executes the debugger script
         let lldb_script_path = rust_src_root.join("src/etc/lldb_batchmode.py");
         self.cmd2procres(
-            Command::new(&self.config.lldb_python)
+            Command::new(&self.config.python)
                 .arg(&lldb_script_path)
                 .arg(test_executable)
                 .arg(debugger_script)
@@ -1856,10 +1856,14 @@ impl<'test> TestCx<'test> {
                 rustc.args(&[
                     "-Copt-level=1",
                     "-Zdump-mir=all",
-                    "-Zmir-opt-level=4",
                     "-Zvalidate-mir",
                     "-Zdump-mir-exclude-pass-number",
                 ]);
+                if let Some(pass) = &self.props.mir_unit_test {
+                    rustc.args(&["-Zmir-opt-level=0", &format!("-Zmir-enable-passes=+{}", pass)]);
+                } else {
+                    rustc.arg("-Zmir-opt-level=4");
+                }
 
                 let mir_dump_dir = self.get_mir_dump_dir();
                 let _ = fs::remove_dir_all(&mir_dump_dir);
@@ -2256,7 +2260,7 @@ impl<'test> TestCx<'test> {
             self.check_rustdoc_test_option(proc_res);
         } else {
             let root = self.config.find_rust_src_root().unwrap();
-            let mut cmd = Command::new(&self.config.docck_python);
+            let mut cmd = Command::new(&self.config.python);
             cmd.arg(root.join("src/etc/htmldocck.py")).arg(&out_dir).arg(&self.testpaths.file);
             if self.config.bless {
                 cmd.arg("--bless");
@@ -2457,7 +2461,7 @@ impl<'test> TestCx<'test> {
         let mut json_out = out_dir.join(self.testpaths.file.file_stem().unwrap());
         json_out.set_extension("json");
         let res = self.cmd2procres(
-            Command::new(&self.config.docck_python)
+            Command::new(&self.config.python)
                 .arg(root.join("src/etc/check_missing_items.py"))
                 .arg(&json_out),
         );
@@ -2852,7 +2856,7 @@ impl<'test> TestCx<'test> {
             .stdout(Stdio::piped())
             .stderr(Stdio::piped())
             .env("TARGET", &self.config.target)
-            .env("PYTHON", &self.config.docck_python)
+            .env("PYTHON", &self.config.python)
             .env("S", src_root)
             .env("RUST_BUILD_STAGE", &self.config.stage_id)
             .env("RUSTC", cwd.join(&self.config.rustc_path))
diff --git a/src/tools/miri b/src/tools/miri
-Subproject 1ef91e122775060acb1fbda2c9a366891af3ea8
+Subproject a71a0083937671d79e16bfac4c7b8cab9c8ab9b
diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js
index dbf5cf9650c..98d0f5dc656 100644
--- a/src/tools/rustdoc-js/tester.js
+++ b/src/tools/rustdoc-js/tester.js
@@ -58,7 +58,8 @@ function extractFunction(content, functionName) {
                 } while (pos < content.length && content[pos] !== '/' && content[pos - 1] !== '*');
 
             // Eat quoted strings
-            } else if (content[pos] === '"' || content[pos] === "'" || content[pos] === "`") {
+            } else if ((content[pos] === '"' || content[pos] === "'" || content[pos] === "`") &&
+                       (pos === 0 || content[pos - 1] !== '/')) {
                 stop = content[pos];
                 do {
                     if (content[pos] === '\\') {
@@ -84,8 +85,11 @@ function extractFunction(content, functionName) {
 }
 
 // Stupid function extractor for array.
-function extractArrayVariable(content, arrayName) {
-    var splitter = "var " + arrayName;
+function extractArrayVariable(content, arrayName, kind) {
+    if (typeof kind === "undefined") {
+        kind = "let ";
+    }
+    var splitter = kind + arrayName;
     while (true) {
         var start = content.indexOf(splitter);
         if (start === -1) {
@@ -125,12 +129,18 @@ function extractArrayVariable(content, arrayName) {
         }
         content = content.slice(start + 1);
     }
+    if (kind === "let ") {
+        return extractArrayVariable(content, arrayName, "const ");
+    }
     return null;
 }
 
 // Stupid function extractor for variable.
-function extractVariable(content, varName) {
-    var splitter = "var " + varName;
+function extractVariable(content, varName, kind) {
+    if (typeof kind === "undefined") {
+        kind = "let ";
+    }
+    var splitter = kind + varName;
     while (true) {
         var start = content.indexOf(splitter);
         if (start === -1) {
@@ -161,6 +171,9 @@ function extractVariable(content, varName) {
         }
         content = content.slice(start + 1);
     }
+    if (kind === "let ") {
+        return extractVariable(content, varName, "const ");
+    }
     return null;
 }
 
@@ -269,8 +282,13 @@ function loadSearchJsAndIndex(searchJs, searchIndex, storageJs, crate) {
     // execQuery last parameter is built in buildIndex.
     // buildIndex requires the hashmap from search-index.
     var functionsToLoad = ["buildHrefAndPath", "pathSplitter", "levenshtein", "validateResult",
-                           "handleAliases", "getQuery", "buildIndex", "execQuery", "execSearch",
-                           "removeEmptyStringsFromArray"];
+                           "buildIndex", "execQuery", "parseQuery", "createQueryResults",
+                           "isWhitespace", "isSpecialStartCharacter", "isStopCharacter",
+                           "parseInput", "getItemsBefore", "getNextElem", "createQueryElement",
+                           "isReturnArrow", "isPathStart", "getStringElem", "newParsedQuery",
+                           "itemTypeFromName", "isEndCharacter", "isErrorCharacter",
+                           "isIdentCharacter", "isSeparatorCharacter", "getIdentEndPosition",
+                           "checkExtraTypeFilterCharacters", "isWhitespaceCharacter"];
 
     const functions = ["hasOwnPropertyRustdoc", "onEach"];
     ALIASES = {};
@@ -286,12 +304,99 @@ function loadSearchJsAndIndex(searchJs, searchIndex, storageJs, crate) {
     return [loaded, index];
 }
 
+// This function checks if `expected` has all the required fields needed for the checks.
+function checkNeededFields(fullPath, expected, error_text, queryName, position) {
+    let fieldsToCheck;
+    if (fullPath.length === 0) {
+        fieldsToCheck = [
+            "foundElems",
+            "original",
+            "returned",
+            "typeFilter",
+            "userQuery",
+            "error",
+        ];
+    } else if (fullPath.endsWith("elems") || fullPath.endsWith("generics")) {
+        fieldsToCheck = [
+            "name",
+            "fullPath",
+            "pathWithoutLast",
+            "pathLast",
+            "generics",
+        ];
+    } else {
+        fieldsToCheck = [];
+    }
+    for (var i = 0; i < fieldsToCheck.length; ++i) {
+        const field = fieldsToCheck[i];
+        if (!expected.hasOwnProperty(field)) {
+            let text = `${queryName}==> Mandatory key \`${field}\` is not present`;
+            if (fullPath.length > 0) {
+                text += ` in field \`${fullPath}\``;
+                if (position != null) {
+                    text += ` (position ${position})`;
+                }
+            }
+            error_text.push(text);
+        }
+    }
+}
+
+function valueCheck(fullPath, expected, result, error_text, queryName) {
+    if (Array.isArray(expected)) {
+        for (var i = 0; i < expected.length; ++i) {
+            checkNeededFields(fullPath, expected[i], error_text, queryName, i);
+            if (i >= result.length) {
+                error_text.push(`${queryName}==> EXPECTED has extra value in array from field ` +
+                    `\`${fullPath}\` (position ${i}): \`${JSON.stringify(expected[i])}\``);
+            } else {
+                valueCheck(fullPath + '[' + i + ']', expected[i], result[i], error_text, queryName);
+            }
+        }
+        for (; i < result.length; ++i) {
+            error_text.push(`${queryName}==> RESULT has extra value in array from field ` +
+                `\`${fullPath}\` (position ${i}): \`${JSON.stringify(result[i])}\` ` +
+                'compared to EXPECTED');
+        }
+    } else if (expected !== null && typeof expected !== "undefined" &&
+               expected.constructor == Object)
+    {
+        for (const key in expected) {
+            if (!expected.hasOwnProperty(key)) {
+                continue;
+            }
+            if (!result.hasOwnProperty(key)) {
+                error_text.push('==> Unknown key "' + key + '"');
+                break;
+            }
+            const obj_path = fullPath + (fullPath.length > 0 ? '.' : '') + key;
+            valueCheck(obj_path, expected[key], result[key], error_text, queryName);
+        }
+    } else {
+        expectedValue = JSON.stringify(expected);
+        resultValue = JSON.stringify(result);
+        if (expectedValue != resultValue) {
+            error_text.push(`${queryName}==> Different values for field \`${fullPath}\`:\n` +
+                `EXPECTED: \`${expectedValue}\`\nRESULT:   \`${resultValue}\``);
+        }
+    }
+}
+
+function runParser(query, expected, loaded, loadedFile, queryName) {
+    var error_text = [];
+    checkNeededFields("", expected, error_text, queryName, null);
+    if (error_text.length === 0) {
+        valueCheck('', expected, loaded.parseQuery(query), error_text, queryName);
+    }
+    return error_text;
+}
+
 function runSearch(query, expected, index, loaded, loadedFile, queryName) {
     const filter_crate = loadedFile.FILTER_CRATE;
     const ignore_order = loadedFile.ignore_order;
     const exact_check = loadedFile.exact_check;
 
-    var results = loaded.execSearch(loaded.getQuery(query), index, filter_crate);
+    var results = loaded.execQuery(loaded.parseQuery(query), index, filter_crate);
     var error_text = [];
 
     for (var key in expected) {
@@ -353,40 +458,75 @@ function checkResult(error_text, loadedFile, displaySuccess) {
     return 1;
 }
 
-function runChecks(testFile, loaded, index) {
-    var testFileContent = readFile(testFile) + 'exports.QUERY = QUERY;exports.EXPECTED = EXPECTED;';
-    if (testFileContent.indexOf("FILTER_CRATE") !== -1) {
-        testFileContent += "exports.FILTER_CRATE = FILTER_CRATE;";
-    } else {
-        testFileContent += "exports.FILTER_CRATE = null;";
-    }
-    var loadedFile = loadContent(testFileContent);
-
-    const expected = loadedFile.EXPECTED;
+function runCheck(loadedFile, key, callback) {
+    const expected = loadedFile[key];
     const query = loadedFile.QUERY;
 
     if (Array.isArray(query)) {
         if (!Array.isArray(expected)) {
             console.log("FAILED");
-            console.log("==> If QUERY variable is an array, EXPECTED should be an array too");
+            console.log(`==> If QUERY variable is an array, ${key} should be an array too`);
             return 1;
         } else if (query.length !== expected.length) {
             console.log("FAILED");
-            console.log("==> QUERY variable should have the same length as EXPECTED");
+            console.log(`==> QUERY variable should have the same length as ${key}`);
             return 1;
         }
         for (var i = 0; i < query.length; ++i) {
-            var error_text = runSearch(query[i], expected[i], index, loaded, loadedFile,
-                "[ query `" + query[i] + "`]");
+            var error_text = callback(query[i], expected[i], "[ query `" + query[i] + "`]");
             if (checkResult(error_text, loadedFile, false) !== 0) {
                 return 1;
             }
         }
         console.log("OK");
-        return 0;
+    } else {
+        var error_text = callback(query, expected, "");
+        if (checkResult(error_text, loadedFile, true) !== 0) {
+            return 1;
+        }
+    }
+    return 0;
+}
+
+function runChecks(testFile, loaded, index) {
+    var checkExpected = false;
+    var checkParsed = false;
+    var testFileContent = readFile(testFile) + 'exports.QUERY = QUERY;';
+
+    if (testFileContent.indexOf("FILTER_CRATE") !== -1) {
+        testFileContent += "exports.FILTER_CRATE = FILTER_CRATE;";
+    } else {
+        testFileContent += "exports.FILTER_CRATE = null;";
+    }
+
+    if (testFileContent.indexOf("\nconst EXPECTED") !== -1) {
+        testFileContent += 'exports.EXPECTED = EXPECTED;';
+        checkExpected = true;
+    }
+    if (testFileContent.indexOf("\nconst PARSED") !== -1) {
+        testFileContent += 'exports.PARSED = PARSED;';
+        checkParsed = true;
+    }
+    if (!checkParsed && !checkExpected) {
+        console.log("FAILED");
+        console.log("==> At least `PARSED` or `EXPECTED` is needed!");
+        return 1;
+    }
+
+    const loadedFile = loadContent(testFileContent);
+    var res = 0;
+
+    if (checkExpected) {
+        res += runCheck(loadedFile, "EXPECTED", (query, expected, text) => {
+            return runSearch(query, expected, index, loaded, loadedFile, text);
+        });
+    }
+    if (checkParsed) {
+        res += runCheck(loadedFile, "PARSED", (query, expected, text) => {
+            return runParser(query, expected, loaded, loadedFile, text);
+        });
     }
-    var error_text = runSearch(query, expected, index, loaded, loadedFile, "");
-    return checkResult(error_text, loadedFile, true);
+    return res;
 }
 
 function load_files(doc_folder, resource_suffix, crate) {
diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs
index 4f333cd27ce..e4cc93026f1 100644
--- a/src/tools/rustfmt/src/expr.rs
+++ b/src/tools/rustfmt/src/expr.rs
@@ -2,7 +2,7 @@ use std::borrow::Cow;
 use std::cmp::min;
 
 use itertools::Itertools;
-use rustc_ast::token::{DelimToken, LitKind};
+use rustc_ast::token::{Delimiter, LitKind};
 use rustc_ast::{ast, ptr};
 use rustc_span::{BytePos, Span};
 
@@ -225,6 +225,10 @@ pub(crate) fn format_expr(
         ast::ExprKind::Ret(Some(ref expr)) => {
             rewrite_unary_prefix(context, "return ", &**expr, shape)
         }
+        ast::ExprKind::Yeet(None) => Some("do yeet".to_owned()),
+        ast::ExprKind::Yeet(Some(ref expr)) => {
+            rewrite_unary_prefix(context, "do yeet ", &**expr, shape)
+        }
         ast::ExprKind::Box(ref expr) => rewrite_unary_prefix(context, "box ", &**expr, shape),
         ast::ExprKind::AddrOf(borrow_kind, mutability, ref expr) => {
             rewrite_expr_addrof(context, borrow_kind, mutability, expr, shape)
@@ -412,7 +416,7 @@ pub(crate) fn rewrite_array<'a, T: 'a + IntoOverflowableItem<'a>>(
     context: &'a RewriteContext<'_>,
     shape: Shape,
     force_separator_tactic: Option<SeparatorTactic>,
-    delim_token: Option<DelimToken>,
+    delim_token: Option<Delimiter>,
 ) -> Option<String> {
     overflow::rewrite_with_square_brackets(
         context,
@@ -1325,7 +1329,7 @@ pub(crate) fn can_be_overflowed_expr(
         }
         ast::ExprKind::MacCall(ref mac) => {
             match (
-                rustc_ast::ast::MacDelimiter::from_token(mac.args.delim()),
+                rustc_ast::ast::MacDelimiter::from_token(mac.args.delim().unwrap()),
                 context.config.overflow_delimited_expr(),
             ) {
                 (Some(ast::MacDelimiter::Bracket), true)
diff --git a/src/tools/rustfmt/src/macros.rs b/src/tools/rustfmt/src/macros.rs
index 664f152e8be..26c429eb94f 100644
--- a/src/tools/rustfmt/src/macros.rs
+++ b/src/tools/rustfmt/src/macros.rs
@@ -12,7 +12,7 @@
 use std::collections::HashMap;
 use std::panic::{catch_unwind, AssertUnwindSafe};
 
-use rustc_ast::token::{BinOpToken, DelimToken, Token, TokenKind};
+use rustc_ast::token::{BinOpToken, Delimiter, Token, TokenKind};
 use rustc_ast::tokenstream::{Cursor, Spacing, TokenStream, TokenTree};
 use rustc_ast::{ast, ptr};
 use rustc_ast_pretty::pprust;
@@ -203,7 +203,7 @@ fn rewrite_macro_inner(
     let is_forced_bracket = FORCED_BRACKET_MACROS.contains(&&macro_name[..]);
 
     let style = if is_forced_bracket && !is_nested_macro {
-        DelimToken::Bracket
+        Delimiter::Bracket
     } else {
         original_style
     };
@@ -212,15 +212,15 @@ fn rewrite_macro_inner(
     let has_comment = contains_comment(context.snippet(mac.span()));
     if ts.is_empty() && !has_comment {
         return match style {
-            DelimToken::Paren if position == MacroPosition::Item => {
+            Delimiter::Parenthesis if position == MacroPosition::Item => {
                 Some(format!("{}();", macro_name))
             }
-            DelimToken::Bracket if position == MacroPosition::Item => {
+            Delimiter::Bracket if position == MacroPosition::Item => {
                 Some(format!("{}[];", macro_name))
             }
-            DelimToken::Paren => Some(format!("{}()", macro_name)),
-            DelimToken::Bracket => Some(format!("{}[]", macro_name)),
-            DelimToken::Brace => Some(format!("{} {{}}", macro_name)),
+            Delimiter::Parenthesis => Some(format!("{}()", macro_name)),
+            Delimiter::Bracket => Some(format!("{}[]", macro_name)),
+            Delimiter::Brace => Some(format!("{} {{}}", macro_name)),
             _ => unreachable!(),
         };
     }
@@ -260,7 +260,7 @@ fn rewrite_macro_inner(
     }
 
     match style {
-        DelimToken::Paren => {
+        Delimiter::Parenthesis => {
             // Handle special case: `vec!(expr; expr)`
             if vec_with_semi {
                 handle_vec_semi(context, shape, arg_vec, macro_name, style)
@@ -286,7 +286,7 @@ fn rewrite_macro_inner(
                 })
             }
         }
-        DelimToken::Bracket => {
+        Delimiter::Bracket => {
             // Handle special case: `vec![expr; expr]`
             if vec_with_semi {
                 handle_vec_semi(context, shape, arg_vec, macro_name, style)
@@ -323,7 +323,7 @@ fn rewrite_macro_inner(
                 Some(format!("{}{}", rewrite, comma))
             }
         }
-        DelimToken::Brace => {
+        Delimiter::Brace => {
             // For macro invocations with braces, always put a space between
             // the `macro_name!` and `{ /* macro_body */ }` but skip modifying
             // anything in between the braces (for now).
@@ -342,11 +342,11 @@ fn handle_vec_semi(
     shape: Shape,
     arg_vec: Vec<MacroArg>,
     macro_name: String,
-    delim_token: DelimToken,
+    delim_token: Delimiter,
 ) -> Option<String> {
     let (left, right) = match delim_token {
-        DelimToken::Paren => ("(", ")"),
-        DelimToken::Bracket => ("[", "]"),
+        Delimiter::Parenthesis => ("(", ")"),
+        Delimiter::Bracket => ("[", "]"),
         _ => unreachable!(),
     };
 
@@ -528,7 +528,7 @@ enum MacroArgKind {
     /// e.g., `$($foo: expr),*`
     Repeat(
         /// `()`, `[]` or `{}`.
-        DelimToken,
+        Delimiter,
         /// Inner arguments inside delimiters.
         Vec<ParsedMacroArg>,
         /// Something after the closing delimiter and the repeat token, if available.
@@ -537,7 +537,7 @@ enum MacroArgKind {
         Token,
     ),
     /// e.g., `[derive(Debug)]`
-    Delimited(DelimToken, Vec<ParsedMacroArg>),
+    Delimited(Delimiter, Vec<ParsedMacroArg>),
     /// A possible separator. e.g., `,` or `;`.
     Separator(String, String),
     /// Other random stuff that does not fit to other kinds.
@@ -547,22 +547,22 @@ enum MacroArgKind {
 
 fn delim_token_to_str(
     context: &RewriteContext<'_>,
-    delim_token: DelimToken,
+    delim_token: Delimiter,
     shape: Shape,
     use_multiple_lines: bool,
     inner_is_empty: bool,
 ) -> (String, String) {
     let (lhs, rhs) = match delim_token {
-        DelimToken::Paren => ("(", ")"),
-        DelimToken::Bracket => ("[", "]"),
-        DelimToken::Brace => {
+        Delimiter::Parenthesis => ("(", ")"),
+        Delimiter::Bracket => ("[", "]"),
+        Delimiter::Brace => {
             if inner_is_empty || use_multiple_lines {
                 ("{", "}")
             } else {
                 ("{ ", " }")
             }
         }
-        DelimToken::NoDelim => ("", ""),
+        Delimiter::Invisible => unreachable!(),
     };
     if use_multiple_lines {
         let indent_str = shape.indent.to_string_with_newline(context.config);
@@ -583,8 +583,8 @@ impl MacroArgKind {
     fn starts_with_brace(&self) -> bool {
         matches!(
             *self,
-            MacroArgKind::Repeat(DelimToken::Brace, _, _, _)
-                | MacroArgKind::Delimited(DelimToken::Brace, _)
+            MacroArgKind::Repeat(Delimiter::Brace, _, _, _)
+                | MacroArgKind::Delimited(Delimiter::Brace, _)
         )
     }
 
@@ -753,7 +753,7 @@ impl MacroArgParser {
         }
     }
 
-    fn add_delimited(&mut self, inner: Vec<ParsedMacroArg>, delim: DelimToken) {
+    fn add_delimited(&mut self, inner: Vec<ParsedMacroArg>, delim: Delimiter) {
         self.result.push(ParsedMacroArg {
             kind: MacroArgKind::Delimited(delim, inner),
         });
@@ -763,7 +763,7 @@ impl MacroArgParser {
     fn add_repeat(
         &mut self,
         inner: Vec<ParsedMacroArg>,
-        delim: DelimToken,
+        delim: Delimiter,
         iter: &mut Cursor,
     ) -> Option<()> {
         let mut buffer = String::new();
@@ -1083,18 +1083,18 @@ pub(crate) fn convert_try_mac(
     }
 }
 
-pub(crate) fn macro_style(mac: &ast::MacCall, context: &RewriteContext<'_>) -> DelimToken {
+pub(crate) fn macro_style(mac: &ast::MacCall, context: &RewriteContext<'_>) -> Delimiter {
     let snippet = context.snippet(mac.span());
     let paren_pos = snippet.find_uncommented("(").unwrap_or(usize::max_value());
     let bracket_pos = snippet.find_uncommented("[").unwrap_or(usize::max_value());
     let brace_pos = snippet.find_uncommented("{").unwrap_or(usize::max_value());
 
     if paren_pos < bracket_pos && paren_pos < brace_pos {
-        DelimToken::Paren
+        Delimiter::Parenthesis
     } else if bracket_pos < brace_pos {
-        DelimToken::Bracket
+        Delimiter::Bracket
     } else {
-        DelimToken::Brace
+        Delimiter::Brace
     }
 }
 
@@ -1174,7 +1174,7 @@ struct Macro {
 // rather than clone them, if we can make the borrowing work out.
 struct MacroBranch {
     span: Span,
-    args_paren_kind: DelimToken,
+    args_paren_kind: Delimiter,
     args: TokenStream,
     body: Span,
     whole_body: Span,
@@ -1188,7 +1188,7 @@ impl MacroBranch {
         multi_branch_style: bool,
     ) -> Option<String> {
         // Only attempt to format function-like macros.
-        if self.args_paren_kind != DelimToken::Paren {
+        if self.args_paren_kind != Delimiter::Parenthesis {
             // FIXME(#1539): implement for non-sugared macros.
             return None;
         }
@@ -1350,18 +1350,18 @@ fn rewrite_macro_with_items(
     items: &[MacroArg],
     macro_name: &str,
     shape: Shape,
-    style: DelimToken,
+    style: Delimiter,
     position: MacroPosition,
     span: Span,
 ) -> Option<String> {
     let (opener, closer) = match style {
-        DelimToken::Paren => ("(", ")"),
-        DelimToken::Bracket => ("[", "]"),
-        DelimToken::Brace => (" {", "}"),
+        Delimiter::Parenthesis => ("(", ")"),
+        Delimiter::Bracket => ("[", "]"),
+        Delimiter::Brace => (" {", "}"),
         _ => return None,
     };
     let trailing_semicolon = match style {
-        DelimToken::Paren | DelimToken::Bracket if position == MacroPosition::Item => ";",
+        Delimiter::Parenthesis | Delimiter::Bracket if position == MacroPosition::Item => ";",
         _ => "",
     };
 
diff --git a/src/tools/rustfmt/src/overflow.rs b/src/tools/rustfmt/src/overflow.rs
index 80aed998d73..f115e7d0261 100644
--- a/src/tools/rustfmt/src/overflow.rs
+++ b/src/tools/rustfmt/src/overflow.rs
@@ -3,7 +3,7 @@
 use std::cmp::min;
 
 use itertools::Itertools;
-use rustc_ast::token::DelimToken;
+use rustc_ast::token::Delimiter;
 use rustc_ast::{ast, ptr};
 use rustc_span::Span;
 
@@ -297,11 +297,11 @@ pub(crate) fn rewrite_with_square_brackets<'a, T: 'a + IntoOverflowableItem<'a>>
     shape: Shape,
     span: Span,
     force_separator_tactic: Option<SeparatorTactic>,
-    delim_token: Option<DelimToken>,
+    delim_token: Option<Delimiter>,
 ) -> Option<String> {
     let (lhs, rhs) = match delim_token {
-        Some(DelimToken::Paren) => ("(", ")"),
-        Some(DelimToken::Brace) => ("{", "}"),
+        Some(Delimiter::Parenthesis) => ("(", ")"),
+        Some(Delimiter::Brace) => ("{", "}"),
         _ => ("[", "]"),
     };
     Context::new(
diff --git a/src/tools/rustfmt/src/parse/macros/cfg_if.rs b/src/tools/rustfmt/src/parse/macros/cfg_if.rs
index 306b6bb745e..09b3e32df31 100644
--- a/src/tools/rustfmt/src/parse/macros/cfg_if.rs
+++ b/src/tools/rustfmt/src/parse/macros/cfg_if.rs
@@ -1,7 +1,7 @@
 use std::panic::{catch_unwind, AssertUnwindSafe};
 
 use rustc_ast::ast;
-use rustc_ast::token::{DelimToken, TokenKind};
+use rustc_ast::token::{Delimiter, TokenKind};
 use rustc_parse::parser::ForceCollect;
 use rustc_span::symbol::kw;
 
@@ -47,11 +47,11 @@ fn parse_cfg_if_inner<'a>(
                 .map_err(|_| "Failed to parse attributes")?;
         }
 
-        if !parser.eat(&TokenKind::OpenDelim(DelimToken::Brace)) {
+        if !parser.eat(&TokenKind::OpenDelim(Delimiter::Brace)) {
             return Err("Expected an opening brace");
         }
 
-        while parser.token != TokenKind::CloseDelim(DelimToken::Brace)
+        while parser.token != TokenKind::CloseDelim(Delimiter::Brace)
             && parser.token.kind != TokenKind::Eof
         {
             let item = match parser.parse_item(ForceCollect::No) {
@@ -70,7 +70,7 @@ fn parse_cfg_if_inner<'a>(
             }
         }
 
-        if !parser.eat(&TokenKind::CloseDelim(DelimToken::Brace)) {
+        if !parser.eat(&TokenKind::CloseDelim(Delimiter::Brace)) {
             return Err("Expected a closing brace");
         }
 
diff --git a/src/tools/rustfmt/src/parse/macros/mod.rs b/src/tools/rustfmt/src/parse/macros/mod.rs
index 3728f3a19b4..d4dbf21f8ca 100644
--- a/src/tools/rustfmt/src/parse/macros/mod.rs
+++ b/src/tools/rustfmt/src/parse/macros/mod.rs
@@ -1,4 +1,4 @@
-use rustc_ast::token::{DelimToken, TokenKind};
+use rustc_ast::token::{Delimiter, TokenKind};
 use rustc_ast::tokenstream::TokenStream;
 use rustc_ast::{ast, ptr};
 use rustc_parse::parser::{ForceCollect, Parser};
@@ -81,7 +81,7 @@ fn check_keyword<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option<MacroArg> {
             && parser.look_ahead(1, |t| {
                 t.kind == TokenKind::Eof
                     || t.kind == TokenKind::Comma
-                    || t.kind == TokenKind::CloseDelim(DelimToken::NoDelim)
+                    || t.kind == TokenKind::CloseDelim(Delimiter::Invisible)
             })
         {
             parser.bump();
@@ -97,7 +97,7 @@ fn check_keyword<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option<MacroArg> {
 pub(crate) fn parse_macro_args(
     context: &RewriteContext<'_>,
     tokens: TokenStream,
-    style: DelimToken,
+    style: Delimiter,
     forced_bracket: bool,
 ) -> Option<ParsedMacroArgs> {
     let mut parser = build_parser(context, tokens);
@@ -105,7 +105,7 @@ pub(crate) fn parse_macro_args(
     let mut vec_with_semi = false;
     let mut trailing_comma = false;
 
-    if DelimToken::Brace != style {
+    if Delimiter::Brace != style {
         loop {
             if let Some(arg) = check_keyword(&mut parser) {
                 args.push(arg);
diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs
index 35512e78fa6..ed418fb1fec 100644
--- a/src/tools/rustfmt/src/utils.rs
+++ b/src/tools/rustfmt/src/utils.rs
@@ -512,6 +512,7 @@ pub(crate) fn is_block_expr(context: &RewriteContext<'_>, expr: &ast::Expr, repr
         | ast::ExprKind::Range(..)
         | ast::ExprKind::Repeat(..)
         | ast::ExprKind::Ret(..)
+        | ast::ExprKind::Yeet(..)
         | ast::ExprKind::Tup(..)
         | ast::ExprKind::Type(..)
         | ast::ExprKind::Yield(None)
diff --git a/src/tools/rustfmt/src/visitor.rs b/src/tools/rustfmt/src/visitor.rs
index 1621eb406b1..f04fb2e0446 100644
--- a/src/tools/rustfmt/src/visitor.rs
+++ b/src/tools/rustfmt/src/visitor.rs
@@ -1,7 +1,7 @@
 use std::cell::{Cell, RefCell};
 use std::rc::Rc;
 
-use rustc_ast::{ast, token::DelimToken, visit, AstLike};
+use rustc_ast::{ast, token::Delimiter, visit, AstLike};
 use rustc_data_structures::sync::Lrc;
 use rustc_span::{symbol, BytePos, Pos, Span};
 
@@ -689,7 +689,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
         // with whitespace between the delimiters and trailing semi (i.e. `foo!(abc)     ;`)
         // are formatted correctly.
         let (span, rewrite) = match macro_style(mac, &self.get_context()) {
-            DelimToken::Bracket | DelimToken::Paren if MacroPosition::Item == pos => {
+            Delimiter::Bracket | Delimiter::Parenthesis if MacroPosition::Item == pos => {
                 let search_span = mk_sp(mac.span().hi(), self.snippet_provider.end_pos());
                 let hi = self.snippet_provider.span_before(search_span, ";");
                 let target_span = mk_sp(mac.span().lo(), hi + BytePos(1));
diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs
index ea6e01e577c..4bf932563aa 100644
--- a/src/tools/tidy/src/deps.rs
+++ b/src/tools/tidy/src/deps.rs
@@ -42,6 +42,9 @@ const EXCEPTIONS: &[(&str, &str)] = &[
     ("self_cell", "Apache-2.0"),      // rustc (fluent translations)
     // FIXME: this dependency violates the documentation comment above:
     ("fortanix-sgx-abi", "MPL-2.0"), // libstd but only for `sgx` target
+    ("dunce", "CC0-1.0"),            // cargo (dev dependency)
+    ("similar", "Apache-2.0"),       // cargo (dev dependency)
+    ("normalize-line-endings", "Apache-2.0"), // cargo (dev dependency)
 ];
 
 const EXCEPTIONS_CRANELIFT: &[(&str, &str)] = &[
@@ -174,7 +177,6 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[
     "rand_chacha",
     "rand_core",
     "rand_hc",
-    "rand_pcg",
     "rand_xorshift",
     "rand_xoshiro",
     "redox_syscall",
diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs
index 63a8909c662..6b715f727b2 100644
--- a/src/tools/tidy/src/ui_tests.rs
+++ b/src/tools/tidy/src/ui_tests.rs
@@ -7,8 +7,8 @@ use std::path::Path;
 
 const ENTRY_LIMIT: usize = 1000;
 // FIXME: The following limits should be reduced eventually.
-const ROOT_ENTRY_LIMIT: usize = 986;
-const ISSUES_ENTRY_LIMIT: usize = 2310;
+const ROOT_ENTRY_LIMIT: usize = 977;
+const ISSUES_ENTRY_LIMIT: usize = 2278;
 
 fn check_entries(path: &Path, bad: &mut bool) {
     let dirs = walkdir::WalkDir::new(&path.join("test/ui"))