about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock7
-rw-r--r--bootstrap.example.toml9
-rw-r--r--compiler/rustc_abi/src/canon_abi.rs4
-rw-r--r--compiler/rustc_abi/src/extern_abi.rs6
-rw-r--r--compiler/rustc_ast/src/ast.rs68
-rw-r--r--compiler/rustc_ast/src/visit.rs3
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs17
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs10
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs97
-rw-r--r--compiler/rustc_ast_lowering/src/stability.rs3
-rw-r--r--compiler/rustc_ast_passes/messages.ftl17
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs89
-rw-r--r--compiler/rustc_ast_passes/src/errors.rs64
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs14
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/mod.rs12
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state.rs8
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/expr.rs107
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/fixup.rs91
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/item.rs4
-rw-r--r--compiler/rustc_attr_data_structures/src/lib.rs2
-rw-r--r--compiler/rustc_attr_data_structures/src/lints.rs14
-rw-r--r--compiler/rustc_attr_data_structures/src/stability.rs1
-rw-r--r--compiler/rustc_attr_parsing/messages.ftl8
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs38
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/confusables.rs8
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/deprecation.rs25
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/inline.rs97
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/mod.rs177
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/repr.rs23
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/stability.rs54
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/transparency.rs19
-rw-r--r--compiler/rustc_attr_parsing/src/context.rs228
-rw-r--r--compiler/rustc_attr_parsing/src/lib.rs6
-rw-r--r--compiler/rustc_attr_parsing/src/lints.rs19
-rw-r--r--compiler/rustc_attr_parsing/src/parser.rs14
-rw-r--r--compiler/rustc_attr_parsing/src/session_diagnostics.rs13
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs14
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs7
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/generic/mod.rs2
-rw-r--r--compiler/rustc_codegen_cranelift/example/arbitrary_self_types_pointers_and_wrappers.rs4
-rw-r--r--compiler/rustc_codegen_cranelift/example/mini_core.rs4
-rw-r--r--compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs7
-rw-r--r--compiler/rustc_codegen_cranelift/src/abi/mod.rs5
-rw-r--r--compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs4
-rw-r--r--compiler/rustc_codegen_cranelift/src/unsize.rs2
-rw-r--r--compiler/rustc_codegen_cranelift/src/vtable.rs2
-rw-r--r--compiler/rustc_codegen_gcc/example/arbitrary_self_types_pointers_and_wrappers.rs4
-rw-r--r--compiler/rustc_codegen_gcc/example/mini_core.rs4
-rw-r--r--compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs6
-rw-r--r--compiler/rustc_codegen_gcc/messages.ftl7
-rw-r--r--compiler/rustc_codegen_gcc/src/abi.rs6
-rw-r--r--compiler/rustc_codegen_gcc/src/builder.rs2
-rw-r--r--compiler/rustc_codegen_gcc/src/errors.rs15
-rw-r--r--compiler/rustc_codegen_gcc/src/gcc_util.rs27
-rw-r--r--compiler/rustc_codegen_llvm/messages.ftl9
-rw-r--r--compiler/rustc_codegen_llvm/src/abi.rs4
-rw-r--r--compiler/rustc_codegen_llvm/src/builder.rs183
-rw-r--r--compiler/rustc_codegen_llvm/src/context.rs462
-rw-r--r--compiler/rustc_codegen_llvm/src/errors.rs17
-rw-r--r--compiler/rustc_codegen_llvm/src/intrinsic.rs591
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm/ffi.rs12
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm/mod.rs45
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm_util.rs25
-rw-r--r--compiler/rustc_codegen_llvm/src/type_.rs9
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/intrinsic.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/target_features.rs3
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/builder.rs4
-rw-r--r--compiler/rustc_const_eval/src/interpret/intrinsics.rs18
-rw-r--r--compiler/rustc_data_structures/src/thousands/mod.rs35
-rw-r--r--compiler/rustc_data_structures/src/thousands/tests.rs56
-rw-r--r--compiler/rustc_driver_impl/src/lib.rs20
-rw-r--r--compiler/rustc_errors/src/lib.rs16
-rw-r--r--compiler/rustc_expand/messages.ftl2
-rw-r--r--compiler/rustc_expand/src/base.rs6
-rw-r--r--compiler/rustc_expand/src/expand.rs190
-rw-r--r--compiler/rustc_expand/src/lib.rs1
-rw-r--r--compiler/rustc_expand/src/mbe/macro_check.rs25
-rw-r--r--compiler/rustc_expand/src/stats.rs171
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs212
-rw-r--r--compiler/rustc_feature/src/lib.rs13
-rw-r--r--compiler/rustc_feature/src/removed.rs2
-rw-r--r--compiler/rustc_feature/src/unstable.rs4
-rw-r--r--compiler/rustc_hir/src/hir.rs5
-rw-r--r--compiler/rustc_hir/src/lib.rs1
-rw-r--r--compiler/rustc_hir/src/lints.rs23
-rw-r--r--compiler/rustc_hir/src/stable_hash_impls.rs8
-rw-r--r--compiler/rustc_hir_analysis/Cargo.toml1
-rw-r--r--compiler/rustc_hir_analysis/messages.ftl6
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs14
-rw-r--r--compiler/rustc_hir_analysis/src/check/intrinsic.rs8
-rw-r--r--compiler/rustc_hir_analysis/src/check/mod.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/check/region.rs21
-rw-r--r--compiler/rustc_hir_analysis/src/collect.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/delegation.rs3
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs16
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs6
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs10
-rw-r--r--compiler/rustc_hir_analysis/src/lib.rs37
-rw-r--r--compiler/rustc_hir_typeck/messages.ftl4
-rw-r--r--compiler/rustc_hir_typeck/src/callee.rs18
-rw-r--r--compiler/rustc_hir_typeck/src/coercion.rs8
-rw-r--r--compiler/rustc_hir_typeck/src/errors.rs7
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs15
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs10
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/gather_locals.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/lib.rs6
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs125
-rw-r--r--compiler/rustc_hir_typeck/src/upvar.rs2
-rw-r--r--compiler/rustc_interface/src/passes.rs81
-rw-r--r--compiler/rustc_interface/src/tests.rs1
-rw-r--r--compiler/rustc_lint/messages.ftl5
-rw-r--r--compiler/rustc_lint/src/builtin.rs52
-rw-r--r--compiler/rustc_lint/src/early.rs16
-rw-r--r--compiler/rustc_lint/src/early/diagnostics.rs3
-rw-r--r--compiler/rustc_lint/src/impl_trait_overcaptures.rs5
-rw-r--r--compiler/rustc_lint/src/lib.rs6
-rw-r--r--compiler/rustc_lint/src/lifetime_syntax.rs40
-rw-r--r--compiler/rustc_lint/src/lints.rs30
-rw-r--r--compiler/rustc_lint/src/nonstandard_style.rs2
-rw-r--r--compiler/rustc_lint/src/types.rs3
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs48
-rw-r--r--compiler/rustc_lint_defs/src/lib.rs1
-rw-r--r--compiler/rustc_log/src/lib.rs33
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs6
-rw-r--r--compiler/rustc_middle/src/hir/mod.rs16
-rw-r--r--compiler/rustc_middle/src/middle/mod.rs2
-rw-r--r--compiler/rustc_middle/src/middle/stability.rs2
-rw-r--r--compiler/rustc_middle/src/mir/syntax.rs7
-rw-r--r--compiler/rustc_middle/src/query/mod.rs8
-rw-r--r--compiler/rustc_middle/src/ty/consts.rs4
-rw-r--r--compiler/rustc_middle/src/ty/context.rs24
-rw-r--r--compiler/rustc_middle/src/ty/generic_args.rs2
-rw-r--r--compiler/rustc_middle/src/ty/instance.rs2
-rw-r--r--compiler/rustc_middle/src/ty/layout.rs1
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs30
-rw-r--r--compiler/rustc_middle/src/ty/region.rs4
-rw-r--r--compiler/rustc_middle/src/ty/visit.rs4
-rw-r--r--compiler/rustc_mir_build/src/builder/expr/as_operand.rs6
-rw-r--r--compiler/rustc_mir_transform/src/coroutine.rs4
-rw-r--r--compiler/rustc_mir_transform/src/gvn.rs11
-rw-r--r--compiler/rustc_mir_transform/src/lower_intrinsics.rs4
-rw-r--r--compiler/rustc_next_trait_solver/src/canonicalizer.rs8
-rw-r--r--compiler/rustc_next_trait_solver/src/lib.rs1
-rw-r--r--compiler/rustc_next_trait_solver/src/placeholder.rs158
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs49
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs9
-rw-r--r--compiler/rustc_parse/messages.ftl2
-rw-r--r--compiler/rustc_parse/src/errors.rs8
-rw-r--r--compiler/rustc_parse/src/parser/item.rs81
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs65
-rw-r--r--compiler/rustc_parse_format/src/lib.rs299
-rw-r--r--compiler/rustc_parse_format/src/tests.rs42
-rw-r--r--compiler/rustc_passes/messages.ftl6
-rw-r--r--compiler/rustc_passes/src/dead.rs30
-rw-r--r--compiler/rustc_passes/src/errors.rs21
-rw-r--r--compiler/rustc_passes/src/input_stats.rs16
-rw-r--r--compiler/rustc_passes/src/lib_features.rs8
-rw-r--r--compiler/rustc_passes/src/stability.rs11
-rw-r--r--compiler/rustc_query_system/src/ich/hcx.rs8
-rw-r--r--compiler/rustc_resolve/src/build_reduced_graph.rs28
-rw-r--r--compiler/rustc_resolve/src/def_collector.rs8
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs134
-rw-r--r--compiler/rustc_resolve/src/ident.rs1
-rw-r--r--compiler/rustc_resolve/src/imports.rs4
-rw-r--r--compiler/rustc_resolve/src/late.rs2
-rw-r--r--compiler/rustc_resolve/src/lib.rs28
-rw-r--r--compiler/rustc_resolve/src/macros.rs33
-rw-r--r--compiler/rustc_session/messages.ftl8
-rw-r--r--compiler/rustc_session/src/config.rs9
-rw-r--r--compiler/rustc_session/src/errors.rs17
-rw-r--r--compiler/rustc_session/src/features.rs59
-rw-r--r--compiler/rustc_session/src/lib.rs1
-rw-r--r--compiler/rustc_session/src/options.rs15
-rw-r--r--compiler/rustc_session/src/parse.rs9
-rw-r--r--compiler/rustc_smir/src/rustc_internal/internal.rs1
-rw-r--r--compiler/rustc_smir/src/rustc_smir/convert/abi.rs1
-rw-r--r--compiler/rustc_smir/src/rustc_smir/convert/ty.rs1
-rw-r--r--compiler/rustc_smir/src/stable_mir/abi.rs2
-rw-r--r--compiler/rustc_smir/src/stable_mir/ty.rs1
-rw-r--r--compiler/rustc_span/src/caching_source_map_view.rs8
-rw-r--r--compiler/rustc_span/src/hygiene.rs2
-rw-r--r--compiler/rustc_span/src/lib.rs4
-rw-r--r--compiler/rustc_span/src/symbol.rs9
-rw-r--r--compiler/rustc_target/src/asm/loongarch.rs10
-rw-r--r--compiler/rustc_target/src/spec/abi_map.rs2
-rw-r--r--compiler/rustc_target/src/target_features.rs39
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs41
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_condition.rs2
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs87
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs3
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect/analyse.rs99
-rw-r--r--compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/normalize.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs13
-rw-r--r--compiler/rustc_trait_selection/src/traits/util.rs153
-rw-r--r--compiler/rustc_ty_utils/src/ty.rs5
-rw-r--r--compiler/rustc_type_ir/Cargo.toml2
-rw-r--r--compiler/rustc_type_ir/src/binder.rs6
-rw-r--r--compiler/rustc_type_ir/src/fast_reject.rs7
-rw-r--r--compiler/rustc_type_ir/src/inherent.rs15
-rw-r--r--compiler/rustc_type_ir/src/interner.rs16
-rw-r--r--compiler/rustc_type_ir/src/search_graph/global_cache.rs18
-rw-r--r--compiler/rustc_type_ir/src/search_graph/mod.rs114
-rw-r--r--compiler/rustc_type_ir/src/search_graph/stack.rs113
-rw-r--r--compiler/rustc_type_ir/src/ty_kind/closure.rs2
-rw-r--r--compiler/rustc_type_ir/src/visit.rs8
-rw-r--r--library/Cargo.lock69
-rw-r--r--library/alloc/src/collections/btree/set.rs8
-rw-r--r--library/alloc/src/ffi/c_str.rs2
-rw-r--r--library/alloc/src/vec/mod.rs32
-rw-r--r--library/alloc/src/vec/peek_mut.rs55
-rw-r--r--library/alloctests/tests/lib.rs1
-rw-r--r--library/alloctests/tests/slice.rs13
-rw-r--r--library/alloctests/tests/vec.rs17
-rw-r--r--library/compiler-builtins/.release-plz.toml13
-rw-r--r--library/compiler-builtins/Cargo.toml8
-rw-r--r--library/compiler-builtins/builtins-shim/Cargo.toml63
-rw-r--r--library/compiler-builtins/builtins-test-intrinsics/Cargo.toml2
-rw-r--r--library/compiler-builtins/builtins-test/Cargo.toml2
-rw-r--r--library/compiler-builtins/builtins-test/tests/lse.rs3
-rwxr-xr-xlibrary/compiler-builtins/ci/bench-icount.sh2
-rw-r--r--library/compiler-builtins/compiler-builtins/Cargo.toml20
-rw-r--r--library/compiler-builtins/compiler-builtins/build.rs62
-rw-r--r--library/compiler-builtins/compiler-builtins/src/aarch64_linux.rs74
-rw-r--r--library/compiler-builtins/compiler-builtins/src/lib.rs1
-rw-r--r--library/compiler-builtins/libm/Cargo.toml10
-rw-r--r--library/compiler-builtins/libm/src/math/fmin_fmax.rs122
-rw-r--r--library/compiler-builtins/libm/src/math/fminimum_fmaximum.rs126
-rw-r--r--library/compiler-builtins/libm/src/math/fminimum_fmaximum_num.rs138
-rw-r--r--library/compiler-builtins/libm/src/math/generic/fmax.rs3
-rw-r--r--library/compiler-builtins/libm/src/math/generic/fmaximum.rs5
-rw-r--r--library/compiler-builtins/libm/src/math/generic/fmaximum_num.rs17
-rw-r--r--library/compiler-builtins/libm/src/math/generic/fmin.rs3
-rw-r--r--library/compiler-builtins/libm/src/math/generic/fminimum.rs5
-rw-r--r--library/compiler-builtins/libm/src/math/generic/fminimum_num.rs17
-rw-r--r--library/compiler-builtins/libm/src/math/support/float_traits.rs9
-rw-r--r--library/compiler-builtins/libm/src/math/support/macros.rs4
-rw-r--r--library/compiler-builtins/rust-version2
-rw-r--r--library/core/src/ffi/c_str.rs4
-rw-r--r--library/core/src/intrinsics/mod.rs17
-rw-r--r--library/core/src/mem/mod.rs43
-rw-r--r--library/core/src/num/int_macros.rs1
-rw-r--r--library/core/src/num/mod.rs1
-rw-r--r--library/core/src/ops/control_flow.rs4
-rw-r--r--library/core/src/ops/mod.rs2
-rw-r--r--library/core/src/ops/try_trait.rs14
-rw-r--r--library/core/src/option.rs4
-rw-r--r--library/core/src/primitive_docs.rs3
-rw-r--r--library/core/src/result.rs4
-rw-r--r--library/core/src/slice/ascii.rs1
-rw-r--r--library/core/src/slice/iter.rs7
-rw-r--r--library/core/src/sync/atomic.rs179
-rw-r--r--library/core/src/task/poll.rs8
-rw-r--r--library/core/src/task/wake.rs1
-rw-r--r--library/coretests/tests/ffi/cstr.rs2
-rw-r--r--library/coretests/tests/floats/f128.rs122
-rw-r--r--library/coretests/tests/floats/f16.rs122
-rw-r--r--library/coretests/tests/floats/f32.rs112
-rw-r--r--library/coretests/tests/floats/f64.rs100
-rw-r--r--library/coretests/tests/floats/mod.rs528
-rw-r--r--library/coretests/tests/ptr.rs1
-rw-r--r--library/std/Cargo.toml6
-rw-r--r--library/std/src/fs.rs5
-rw-r--r--library/std/src/os/unix/net/addr.rs9
-rw-r--r--library/std/src/os/unix/net/tests.rs9
m---------library/stdarch0
-rw-r--r--library/unwind/Cargo.toml2
-rw-r--r--src/bootstrap/src/core/build_steps/tool.rs15
-rw-r--r--src/bootstrap/src/core/config/config.rs7
-rw-r--r--src/bootstrap/src/core/config/toml/build.rs11
-rw-r--r--src/bootstrap/src/core/sanity.rs14
-rw-r--r--src/bootstrap/src/lib.rs32
-rw-r--r--src/bootstrap/src/utils/change_tracker.rs5
-rw-r--r--src/bootstrap/src/utils/helpers.rs19
-rw-r--r--src/ci/docker/host-x86_64/mingw-check-tidy/Dockerfile6
-rw-r--r--src/ci/github-actions/jobs.yml2
-rwxr-xr-xsrc/ci/run.sh5
-rw-r--r--src/doc/rustc-dev-guide/rust-version2
-rw-r--r--src/doc/rustc-dev-guide/src/git.md3
-rw-r--r--src/doc/rustc-dev-guide/src/implementing_new_features.md4
-rw-r--r--src/doc/rustc-dev-guide/src/tests/compiletest.md2
-rw-r--r--src/doc/rustc/src/SUMMARY.md1
-rw-r--r--src/doc/rustc/src/platform-support.md6
-rw-r--r--src/doc/rustc/src/platform-support/windows-msvc.md69
-rw-r--r--src/doc/rustdoc/src/unstable-features.md24
-rw-r--r--src/doc/unstable-book/src/compiler-flags/macro-stats.md24
-rw-r--r--src/doc/unstable-book/src/language-features/asm-experimental-arch.md5
-rw-r--r--src/doc/unstable-book/src/language-features/unsized-locals.md175
-rw-r--r--src/librustdoc/clean/cfg.rs29
-rw-r--r--src/librustdoc/clean/types.rs2
-rw-r--r--src/librustdoc/doctest.rs4
-rw-r--r--src/librustdoc/doctest/extracted.rs55
-rw-r--r--src/librustdoc/doctest/make.rs148
-rw-r--r--src/librustdoc/doctest/tests.rs59
-rw-r--r--src/librustdoc/formats/renderer.rs16
-rw-r--r--src/librustdoc/html/markdown.rs3
-rw-r--r--src/librustdoc/html/render/context.rs4
-rw-r--r--src/librustdoc/json/conversions.rs232
-rw-r--r--src/librustdoc/json/mod.rs53
-rw-r--r--src/tools/clippy/CHANGELOG.md5
-rw-r--r--src/tools/clippy/clippy_dev/src/update_lints.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs18
-rw-r--r--src/tools/clippy/clippy_lints/src/booleans.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs19
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_precision_loss.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/utils.rs29
-rw-r--r--src/tools/clippy/clippy_lints/src/checked_conversions.rs60
-rw-r--r--src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/coerce_container_to_any.rs108
-rw-r--r--src/tools/clippy/clippy_lints/src/copies.rs51
-rw-r--r--src/tools/clippy/clippy_lints/src/create_dir.rs23
-rw-r--r--src/tools/clippy/clippy_lints/src/ctfe.rs26
-rw-r--r--src/tools/clippy/clippy_lints/src/declared_lints.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/default.rs18
-rw-r--r--src/tools/clippy/clippy_lints/src/disallowed_names.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/disallowed_types.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/doc/doc_suspicious_footnotes.rs113
-rw-r--r--src/tools/clippy/clippy_lints/src/doc/mod.rs40
-rw-r--r--src/tools/clippy/clippy_lints/src/endian_bytes.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/format_args.rs124
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/mod.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/if_let_mutex.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/infallible_try_from.rs76
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs38
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_clamp.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_string_new.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/match_result_ok.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs18
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs17
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/ip_constant.rs52
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/mod.rs39
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/needless_collect.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/open_options.rs18
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/str_splitn.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/non_canonical_impls.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/ptr.rs26
-rw-r--r--src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/semicolon_block.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/serde_api.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/std_instead_of_core.rs79
-rw-r--r--src/tools/clippy/clippy_lints/src/strings.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/swap.rs17
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/types/borrowed_box.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs124
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_io_amount.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_result_ok.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_unit.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/vec_init_then_push.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/wildcard_imports.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/write.rs64
-rw-r--r--src/tools/clippy/clippy_lints/src/zombie_processes.rs26
-rw-r--r--src/tools/clippy/clippy_lints_internal/src/almost_standard_lint_formulation.rs2
-rw-r--r--src/tools/clippy/clippy_lints_internal/src/lint_without_lint_pass.rs2
-rw-r--r--src/tools/clippy/clippy_utils/README.md2
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs15
-rw-r--r--src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs12
-rw-r--r--src/tools/clippy/clippy_utils/src/sym.rs22
-rw-r--r--src/tools/clippy/clippy_utils/src/ty/mod.rs41
-rw-r--r--src/tools/clippy/rust-toolchain.toml2
-rw-r--r--src/tools/clippy/tests/compile-test.rs8
-rw-r--r--src/tools/clippy/tests/symbols-used.rs81
-rw-r--r--src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs1
-rw-r--r--src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr6
-rw-r--r--src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.rs37
-rw-r--r--src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.stderr17
-rw-r--r--src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.rs31
-rw-r--r--src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.stderr17
-rw-r--r--src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs39
-rw-r--r--src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr28
-rw-r--r--src/tools/clippy/tests/ui/cmp_null.fixed6
-rw-r--r--src/tools/clippy/tests/ui/cmp_null.rs6
-rw-r--r--src/tools/clippy/tests/ui/cmp_null.stderr8
-rw-r--r--src/tools/clippy/tests/ui/coerce_container_to_any.fixed26
-rw-r--r--src/tools/clippy/tests/ui/coerce_container_to_any.rs26
-rw-r--r--src/tools/clippy/tests/ui/coerce_container_to_any.stderr23
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-14935.rs27
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-9463.rs7
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-9463.stderr29
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-rust-107877.rs1
-rw-r--r--src/tools/clippy/tests/ui/create_dir.fixed23
-rw-r--r--src/tools/clippy/tests/ui/create_dir.rs19
-rw-r--r--src/tools/clippy/tests/ui/create_dir.stderr43
-rw-r--r--src/tools/clippy/tests/ui/disallowed_names.rs15
-rw-r--r--src/tools/clippy/tests/ui/disallowed_names.stderr28
-rw-r--r--src/tools/clippy/tests/ui/doc_suspicious_footnotes.fixed186
-rw-r--r--src/tools/clippy/tests/ui/doc_suspicious_footnotes.rs162
-rw-r--r--src/tools/clippy/tests/ui/doc_suspicious_footnotes.stderr179
-rw-r--r--src/tools/clippy/tests/ui/doc_suspicious_footnotes_include.rs4
-rw-r--r--src/tools/clippy/tests/ui/doc_suspicious_footnotes_include.stderr17
-rw-r--r--src/tools/clippy/tests/ui/doc_suspicious_footnotes_include.txt13
-rw-r--r--src/tools/clippy/tests/ui/format_args.fixed10
-rw-r--r--src/tools/clippy/tests/ui/format_args.rs10
-rw-r--r--src/tools/clippy/tests/ui/indexing_slicing_index.rs1
-rw-r--r--src/tools/clippy/tests/ui/indexing_slicing_index.stderr31
-rw-r--r--src/tools/clippy/tests/ui/infallible_try_from.rs33
-rw-r--r--src/tools/clippy/tests/ui/infallible_try_from.stderr23
-rw-r--r--src/tools/clippy/tests/ui/ip_constant.fixed107
-rw-r--r--src/tools/clippy/tests/ui/ip_constant.rs127
-rw-r--r--src/tools/clippy/tests/ui/ip_constant.stderr338
-rw-r--r--src/tools/clippy/tests/ui/ip_constant_from_external.rs12
-rw-r--r--src/tools/clippy/tests/ui/ip_constant_from_external.stderr16
-rw-r--r--src/tools/clippy/tests/ui/large_stack_frames.rs8
-rw-r--r--src/tools/clippy/tests/ui/large_stack_frames.stderr8
-rw-r--r--src/tools/clippy/tests/ui/localhost.txt1
-rw-r--r--src/tools/clippy/tests/ui/manual_flatten.fixed148
-rw-r--r--src/tools/clippy/tests/ui/manual_flatten.rs39
-rw-r--r--src/tools/clippy/tests/ui/manual_flatten.stderr139
-rw-r--r--src/tools/clippy/tests/ui/manual_swap_auto_fix.fixed11
-rw-r--r--src/tools/clippy/tests/ui/manual_swap_auto_fix.rs14
-rw-r--r--src/tools/clippy/tests/ui/manual_swap_auto_fix.stderr11
-rw-r--r--src/tools/clippy/tests/ui/match_single_binding.fixed16
-rw-r--r--src/tools/clippy/tests/ui/match_single_binding.rs18
-rw-r--r--src/tools/clippy/tests/ui/match_single_binding.stderr35
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.fixed36
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.rs36
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.stderr17
-rw-r--r--src/tools/clippy/tests/ui/needless_lifetimes.fixed2
-rw-r--r--src/tools/clippy/tests/ui/needless_lifetimes.rs2
-rw-r--r--src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.fixed16
-rw-r--r--src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.rs18
-rw-r--r--src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.stderr15
-rw-r--r--src/tools/clippy/tests/ui/pointer_format.rs66
-rw-r--r--src/tools/clippy/tests/ui/pointer_format.stderr47
-rw-r--r--src/tools/clippy/tests/ui/print_literal.fixed11
-rw-r--r--src/tools/clippy/tests/ui/print_literal.rs11
-rw-r--r--src/tools/clippy/tests/ui/print_literal.stderr50
-rw-r--r--src/tools/clippy/tests/ui/semicolon_outside_block.fixed25
-rw-r--r--src/tools/clippy/tests/ui/semicolon_outside_block.rs25
-rw-r--r--src/tools/clippy/tests/ui/std_instead_of_core.fixed6
-rw-r--r--src/tools/clippy/tests/ui/std_instead_of_core.rs6
-rw-r--r--src/tools/clippy/tests/ui/unit_arg.rs24
-rw-r--r--src/tools/clippy/tests/ui/unit_arg.stderr23
-rw-r--r--src/tools/clippy/tests/ui/unit_arg_empty_blocks.fixed34
-rw-r--r--src/tools/clippy/tests/ui/unit_arg_empty_blocks.rs31
-rw-r--r--src/tools/clippy/tests/ui/unit_arg_empty_blocks.stderr46
-rw-r--r--src/tools/clippy/tests/ui/unit_arg_fixable.fixed78
-rw-r--r--src/tools/clippy/tests/ui/unit_arg_fixable.rs71
-rw-r--r--src/tools/clippy/tests/ui/unit_arg_fixable.stderr110
-rw-r--r--src/tools/clippy/tests/ui/unused_unit.edition2021.fixed8
-rw-r--r--src/tools/clippy/tests/ui/unused_unit.edition2024.fixed8
-rw-r--r--src/tools/clippy/tests/ui/unused_unit.rs8
-rw-r--r--src/tools/clippy/tests/ui/write_literal.fixed12
-rw-r--r--src/tools/clippy/tests/ui/write_literal.rs12
-rw-r--r--src/tools/clippy/tests/ui/write_literal.stderr50
-rw-r--r--src/tools/clippy/tests/ui/zombie_processes.rs10
-rw-r--r--src/tools/clippy/util/gh-pages/script.js2
-rw-r--r--src/tools/compiletest/src/util.rs1
-rw-r--r--src/tools/miri/README.md5
-rw-r--r--src/tools/miri/src/bin/miri.rs2
-rw-r--r--src/tools/miri/src/eval.rs3
-rw-r--r--src/tools/miri/src/intrinsics/mod.rs16
-rw-r--r--src/tools/miri/src/intrinsics/simd.rs7
-rw-r--r--src/tools/miri/src/machine.rs5
-rw-r--r--src/tools/miri/src/math.rs4
-rw-r--r--src/tools/miri/src/operator.rs9
-rw-r--r--src/tools/miri/tests/fail/unsized-local.rs23
-rw-r--r--src/tools/miri/tests/fail/unsized-local.stderr14
-rw-r--r--src/tools/tidy/src/issues.txt1
-rw-r--r--tests/assembly/stack-protector/stack-protector-heuristics-effect-windows-32bit.rs28
-rw-r--r--tests/assembly/stack-protector/stack-protector-heuristics-effect-windows-64bit.rs27
-rw-r--r--tests/assembly/stack-protector/stack-protector-heuristics-effect.rs23
-rw-r--r--tests/codegen/autovec/dont-shuffle-bswaps-opt2.rs31
-rw-r--r--tests/codegen/autovec/dont-shuffle-bswaps-opt3.rs (renamed from tests/codegen/dont-shuffle-bswaps.rs)30
-rw-r--r--tests/codegen/deduced-param-attrs.rs4
-rw-r--r--tests/codegen/retpoline.rs27
-rw-r--r--tests/codegen/virtual-function-elimination.rs2
-rw-r--r--tests/crashes/137188.rs6
-rw-r--r--tests/crashes/138166.rs8
-rw-r--r--tests/crashes/138240.rs9
-rw-r--r--tests/crashes/138266.rs7
-rw-r--r--tests/crashes/138359.rs8
-rw-r--r--tests/crashes/79409.rs16
-rw-r--r--tests/mir-opt/lower_intrinsics.align_of.LowerIntrinsics.panic-abort.diff2
-rw-r--r--tests/mir-opt/lower_intrinsics.align_of.LowerIntrinsics.panic-unwind.diff2
-rw-r--r--tests/mir-opt/lower_intrinsics.rs2
-rw-r--r--tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir2
-rw-r--r--tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir2
-rw-r--r--tests/pretty/postfix-match/precedence.pp2
-rw-r--r--tests/rustdoc-ui/extract-doctests-result.rs11
-rw-r--r--tests/rustdoc-ui/extract-doctests-result.stdout1
-rw-r--r--tests/rustdoc-ui/extract-doctests.stdout2
-rw-r--r--tests/rustdoc/cfg-bool.rs10
-rw-r--r--tests/rustdoc/macro/macro-generated-macro.macro_morestuff_pre.html4
-rw-r--r--tests/rustdoc/macro/macro-generated-macro.rs2
-rw-r--r--tests/ui-fulldeps/pprust-parenthesis-insertion.rs4
-rw-r--r--tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr14
-rw-r--r--tests/ui/abi/bad-custom.rs121
-rw-r--r--tests/ui/abi/bad-custom.stderr299
-rw-r--r--tests/ui/abi/custom.rs88
-rw-r--r--tests/ui/abi/unsupported.aarch64.stderr48
-rw-r--r--tests/ui/abi/unsupported.arm.stderr48
-rw-r--r--tests/ui/abi/unsupported.riscv32.stderr48
-rw-r--r--tests/ui/abi/unsupported.riscv64.stderr48
-rw-r--r--tests/ui/abi/unsupported.x64.stderr48
-rw-r--r--tests/ui/abi/unsupported.x64_win.stderr108
-rw-r--r--tests/ui/associated-types/associated-type-call.fixed22
-rw-r--r--tests/ui/associated-types/associated-type-call.rs22
-rw-r--r--tests/ui/associated-types/associated-type-call.stderr15
-rw-r--r--tests/ui/associated-types/associated-types-unsized.stderr1
-rw-r--r--tests/ui/async-await/awaiting-unsized-param.rs5
-rw-r--r--tests/ui/async-await/awaiting-unsized-param.stderr19
-rw-r--r--tests/ui/async-await/unsized-across-await.rs3
-rw-r--r--tests/ui/async-await/unsized-across-await.stderr20
-rw-r--r--tests/ui/borrowck/issue-47646.stderr2
-rw-r--r--tests/ui/borrowck/issue-85581.stderr2
-rw-r--r--tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.rs6
-rw-r--r--tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.stderr6
-rw-r--r--tests/ui/c-variadic/variadic-ffi-1.rs3
-rw-r--r--tests/ui/c-variadic/variadic-ffi-1.stderr26
-rw-r--r--tests/ui/c-variadic/variadic-ffi-2.rs3
-rw-r--r--tests/ui/c-variadic/variadic-ffi-2.stderr2
-rw-r--r--tests/ui/check-cfg/target_feature.stderr3
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs2
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr2
-rw-r--r--tests/ui/const-generics/mgca/missing_generic_params.rs16
-rw-r--r--tests/ui/const-generics/mgca/missing_generic_params.stderr19
-rw-r--r--tests/ui/consts/auxiliary/unstable_intrinsic.rs2
-rw-r--r--tests/ui/consts/const-adt-align-mismatch.rs2
-rw-r--r--tests/ui/consts/const-mut-refs/mut_ref_in_final.rs2
-rw-r--r--tests/ui/consts/const-mut-refs/mut_ref_in_final.stderr10
-rw-r--r--tests/ui/consts/const-size_of_val-align_of_val-extern-type.rs4
-rw-r--r--tests/ui/consts/const-size_of_val-align_of_val-extern-type.stderr4
-rw-r--r--tests/ui/consts/const-unstable-intrinsic.rs6
-rw-r--r--tests/ui/consts/const-unstable-intrinsic.stderr14
-rw-r--r--tests/ui/consts/gate-do-not-const-check.rs4
-rw-r--r--tests/ui/consts/gate-do-not-const-check.stderr5
-rw-r--r--tests/ui/consts/issue-54224.rs12
-rw-r--r--tests/ui/consts/issue-54224.stderr23
-rw-r--r--tests/ui/consts/promote-not.rs3
-rw-r--r--tests/ui/consts/promote-not.stderr66
-rw-r--r--tests/ui/coroutine/auto-trait-regions.rs4
-rw-r--r--tests/ui/coroutine/auto-trait-regions.stderr44
-rw-r--r--tests/ui/coroutine/unsized-capture-across-yield.rs5
-rw-r--r--tests/ui/coroutine/unsized-capture-across-yield.stderr25
-rw-r--r--tests/ui/coroutine/unsized-local-across-yield.rs2
-rw-r--r--tests/ui/coroutine/unsized-local-across-yield.stderr19
-rw-r--r--tests/ui/diagnostic_namespace/multiline_spans.rs55
-rw-r--r--tests/ui/diagnostic_namespace/multiline_spans.stderr71
-rw-r--r--tests/ui/diagnostic_namespace/on_unimplemented/broken_format.rs19
-rw-r--r--tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr114
-rw-r--r--tests/ui/enum/dead-code-associated-function.rs20
-rw-r--r--tests/ui/enum/dead-code-associated-function.stderr30
-rw-r--r--tests/ui/error-codes/E0045.stderr2
-rw-r--r--tests/ui/error-codes/E0161.rs11
-rw-r--r--tests/ui/error-codes/E0161.stderr (renamed from tests/ui/error-codes/E0161.base.stderr)2
-rw-r--r--tests/ui/extern/unsized-extern-derefmove.rs15
-rw-r--r--tests/ui/extern/unsized-extern-derefmove.stderr16
-rw-r--r--tests/ui/feature-gates/feature-gate-abi-custom.rs51
-rw-r--r--tests/ui/feature-gates/feature-gate-abi-custom.stderr117
-rw-r--r--tests/ui/feature-gates/feature-gate-abi_gpu_kernel.AMDGPU.stderr73
-rw-r--r--tests/ui/feature-gates/feature-gate-abi_gpu_kernel.HOST.stderr (renamed from tests/ui/feature-gates/feature-gate-abi_gpu_kernel.stderr)28
-rw-r--r--tests/ui/feature-gates/feature-gate-abi_gpu_kernel.NVPTX.stderr73
-rw-r--r--tests/ui/feature-gates/feature-gate-abi_gpu_kernel.rs19
-rw-r--r--tests/ui/feature-gates/feature-gate-extern_system_varargs.rs2
-rw-r--r--tests/ui/feature-gates/feature-gate-extern_system_varargs.stderr2
-rw-r--r--tests/ui/feature-gates/feature-gate-pattern-complexity-limit.rs4
-rw-r--r--tests/ui/feature-gates/feature-gate-pattern-complexity-limit.stderr5
-rw-r--r--tests/ui/feature-gates/feature-gate-rustc-attrs-1.rs12
-rw-r--r--tests/ui/feature-gates/feature-gate-rustc-attrs-1.stderr14
-rw-r--r--tests/ui/feature-gates/feature-gate-rustc-attrs.rs6
-rw-r--r--tests/ui/feature-gates/feature-gate-rustc-attrs.stderr15
-rw-r--r--tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs6
-rw-r--r--tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr75
-rw-r--r--tests/ui/force-inlining/gate.rs8
-rw-r--r--tests/ui/force-inlining/gate.stderr12
-rw-r--r--tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-1.rs2
-rw-r--r--tests/ui/impl-trait/auto-trait-selection-freeze.next.stderr21
-rw-r--r--tests/ui/impl-trait/auto-trait-selection.next.stderr21
-rw-r--r--tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.next.stderr2
-rw-r--r--tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.rs2
-rw-r--r--tests/ui/impl-trait/recursive-in-exhaustiveness.next.stderr8
-rw-r--r--tests/ui/impl-trait/recursive-in-exhaustiveness.rs2
-rw-r--r--tests/ui/impl-trait/two_tait_defining_each_other2.next.stderr8
-rw-r--r--tests/ui/impl-trait/two_tait_defining_each_other2.rs2
-rw-r--r--tests/ui/intrinsics/intrinsic-alignment.rs10
-rw-r--r--tests/ui/invalid/invalid-rustc_legacy_const_generics-issue-123077.stderr4
-rw-r--r--tests/ui/issues/issue-15756.stderr1
-rw-r--r--tests/ui/issues/issue-28561.rs1
-rw-r--r--tests/ui/iterators/collect-into-slice.stderr1
-rw-r--r--tests/ui/label/label_misspelled.stderr24
-rw-r--r--tests/ui/layout/invalid-unsized-in-always-sized-tail.stderr2
-rw-r--r--tests/ui/lifetimes/mismatched-lifetime-syntaxes.rs39
-rw-r--r--tests/ui/lifetimes/mismatched-lifetime-syntaxes.stderr67
-rw-r--r--tests/ui/lifetimes/temporary-lifetime-extension-tuple-ctor.rs36
-rw-r--r--tests/ui/lifetimes/temporary-lifetime-extension-tuple-ctor.stderr19
-rw-r--r--tests/ui/linkage-attr/raw-dylib/windows/unsupported-abi.rs12
-rw-r--r--tests/ui/linkage-attr/raw-dylib/windows/unsupported-abi.stderr21
-rw-r--r--tests/ui/lint/expansion-time.rs4
-rw-r--r--tests/ui/lint/expansion-time.stderr33
-rw-r--r--tests/ui/lint/fn-ptr-comparisons-some.rs2
-rw-r--r--tests/ui/lint/fn-ptr-comparisons-some.stderr13
-rw-r--r--tests/ui/lint/fn-ptr-comparisons-weird.rs22
-rw-r--r--tests/ui/lint/fn-ptr-comparisons-weird.stderr47
-rw-r--r--tests/ui/lint/fn-ptr-comparisons.fixed2
-rw-r--r--tests/ui/lint/fn-ptr-comparisons.rs2
-rw-r--r--tests/ui/lint/fn-ptr-comparisons.stderr24
-rw-r--r--tests/ui/lint/future-incompatible-lint-group.rs19
-rw-r--r--tests/ui/lint/future-incompatible-lint-group.stderr44
-rw-r--r--tests/ui/macros/auxiliary/serde.rs19
-rw-r--r--tests/ui/macros/issue-39404.rs6
-rw-r--r--tests/ui/macros/issue-39404.stderr26
-rw-r--r--tests/ui/macros/macro-match-nonterminal.rs2
-rw-r--r--tests/ui/macros/macro-match-nonterminal.stderr39
-rw-r--r--tests/ui/macros/macro-missing-fragment-deduplication.rs6
-rw-r--r--tests/ui/macros/macro-missing-fragment-deduplication.stderr26
-rw-r--r--tests/ui/macros/macro-missing-fragment.e2015.stderr85
-rw-r--r--tests/ui/macros/macro-missing-fragment.rs24
-rw-r--r--tests/ui/macros/macro-missing-fragment.stderr (renamed from tests/ui/macros/macro-missing-fragment.e2024.stderr)14
-rw-r--r--tests/ui/macros/missing-derive-1.rs33
-rw-r--r--tests/ui/macros/missing-derive-1.stderr47
-rw-r--r--tests/ui/macros/missing-derive-2.rs26
-rw-r--r--tests/ui/macros/missing-derive-2.stderr47
-rw-r--r--tests/ui/macros/missing-derive-3.rs24
-rw-r--r--tests/ui/macros/missing-derive-3.stderr32
-rw-r--r--tests/ui/methods/method-deref-to-same-trait-object-with-separate-params.rs3
-rw-r--r--tests/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr29
-rw-r--r--tests/ui/methods/missing-bound-on-tuple.rs39
-rw-r--r--tests/ui/methods/missing-bound-on-tuple.stderr58
-rw-r--r--tests/ui/mir/mir_refs_correct.rs2
-rw-r--r--tests/ui/moves/move-out-of-slice-2.rs14
-rw-r--r--tests/ui/moves/move-out-of-slice-2.stderr85
-rw-r--r--tests/ui/nll/sugg-mut-for-binding-issue-137486.fixed23
-rw-r--r--tests/ui/nll/sugg-mut-for-binding-issue-137486.rs21
-rw-r--r--tests/ui/nll/sugg-mut-for-binding-issue-137486.stderr37
-rw-r--r--tests/ui/nullable-pointer-iotareduction.rs2
-rw-r--r--tests/ui/on-unimplemented/feature-gate-on-unimplemented.rs7
-rw-r--r--tests/ui/on-unimplemented/feature-gate-on-unimplemented.stderr5
-rw-r--r--tests/ui/parser/bad-fn-ptr-qualifier.fixed8
-rw-r--r--tests/ui/parser/bad-fn-ptr-qualifier.rs8
-rw-r--r--tests/ui/parser/bad-fn-ptr-qualifier.stderr139
-rw-r--r--tests/ui/parser/macro/issue-33569.rs1
-rw-r--r--tests/ui/parser/macro/issue-33569.stderr24
-rw-r--r--tests/ui/parser/recover/recover-const-async-fn-ptr.rs8
-rw-r--r--tests/ui/parser/recover/recover-const-async-fn-ptr.stderr141
-rw-r--r--tests/ui/print-calling-conventions.stdout1
-rw-r--r--tests/ui/proc-macro/derive-helper-legacy-limits.stderr6
-rw-r--r--tests/ui/proc-macro/derive-helper-shadowing.stderr2
-rw-r--r--tests/ui/proc-macro/disappearing-resolution.stderr6
-rw-r--r--tests/ui/rustdoc/feature-gate-doc_primitive.rs4
-rw-r--r--tests/ui/rustdoc/feature-gate-doc_primitive.stderr5
-rw-r--r--tests/ui/self/arbitrary_self_types_pointers_and_wrappers.rs4
-rw-r--r--tests/ui/simd/size-align.rs8
-rw-r--r--tests/ui/sized/unsized-binding.stderr1
-rw-r--r--tests/ui/sized/unsized-str-in-return-expr-arg-and-local.rs1
-rw-r--r--tests/ui/sized/unsized-str-in-return-expr-arg-and-local.stderr5
-rw-r--r--tests/ui/stability-attribute/renamed_feature.rs3
-rw-r--r--tests/ui/stability-attribute/renamed_feature.stderr9
-rw-r--r--tests/ui/static/static-drop-scope.rs6
-rw-r--r--tests/ui/static/static-drop-scope.stderr30
-rw-r--r--tests/ui/stats/auxiliary/include.rs3
-rw-r--r--tests/ui/stats/input-stats.stderr4
-rw-r--r--tests/ui/stats/macro-stats.rs130
-rw-r--r--tests/ui/stats/macro-stats.stderr26
-rw-r--r--tests/ui/str/str-array-assignment.stderr1
-rw-r--r--tests/ui/structs-enums/enum-alignment.rs2
-rw-r--r--tests/ui/structs-enums/rec-align-u32.rs4
-rw-r--r--tests/ui/structs-enums/rec-align-u64.rs4
-rw-r--r--tests/ui/structs-enums/tag-align-dyn-u64.rs2
-rw-r--r--tests/ui/structs-enums/tag-align-dyn-variants.rs2
-rw-r--r--tests/ui/structs-enums/tag-align-u64.rs2
-rw-r--r--tests/ui/target-feature/retpoline-target-feature-flag.by_feature.stderr7
-rw-r--r--tests/ui/target-feature/retpoline-target-feature-flag.by_feature1.stderr7
-rw-r--r--tests/ui/target-feature/retpoline-target-feature-flag.by_feature2.stderr7
-rw-r--r--tests/ui/target-feature/retpoline-target-feature-flag.by_feature3.stderr7
-rw-r--r--tests/ui/target-feature/retpoline-target-feature-flag.rs21
-rw-r--r--tests/ui/tool-attributes/diagnostic_item.rs4
-rw-r--r--tests/ui/tool-attributes/diagnostic_item.stderr4
-rw-r--r--tests/ui/traits/associated_type_bound/hrtb-associated.rs2
-rw-r--r--tests/ui/traits/default-method/rustc_must_implement_one_of_gated.rs4
-rw-r--r--tests/ui/traits/default-method/rustc_must_implement_one_of_gated.stderr5
-rw-r--r--tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.rs6
-rw-r--r--tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.stderr15
-rw-r--r--tests/ui/traits/negative-bounds/opaque-type-unsatisfied-fn-bound.rs2
-rw-r--r--tests/ui/traits/negative-bounds/opaque-type-unsatisfied-fn-bound.stderr6
-rw-r--r--tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.is_send.stderr13
-rw-r--r--tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.not_send.stderr13
-rw-r--r--tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.rs2
-rw-r--r--tests/ui/traits/object/no-incomplete-inference.current.stderr16
-rw-r--r--tests/ui/traits/object/no-incomplete-inference.next.stderr16
-rw-r--r--tests/ui/traits/object/no-incomplete-inference.rs18
-rw-r--r--tests/ui/type-alias-impl-trait/issue-84660-unsoundness.next.stderr6
-rw-r--r--tests/ui/type-alias-impl-trait/issue-84660-unsoundness.rs2
-rw-r--r--tests/ui/type-alias-impl-trait/nested-tait-inference2.next.stderr6
-rw-r--r--tests/ui/type-alias-impl-trait/nested-tait-inference2.rs2
-rw-r--r--tests/ui/unpretty/exhaustive.expanded.stdout2
-rw-r--r--tests/ui/unsized-locals/align.rs5
-rw-r--r--tests/ui/unsized-locals/align.stderr17
-rw-r--r--tests/ui/unsized-locals/autoderef.rs11
-rw-r--r--tests/ui/unsized-locals/autoderef.stderr45
-rw-r--r--tests/ui/unsized-locals/auxiliary/ufuncs.rs2
-rw-r--r--tests/ui/unsized-locals/borrow-after-move.rs12
-rw-r--r--tests/ui/unsized-locals/borrow-after-move.stderr90
-rw-r--r--tests/ui/unsized-locals/by-value-trait-dyn-compatibility-rpass.rs7
-rw-r--r--tests/ui/unsized-locals/by-value-trait-dyn-compatibility-rpass.stderr17
-rw-r--r--tests/ui/unsized-locals/by-value-trait-dyn-compatibility-with-default.rs8
-rw-r--r--tests/ui/unsized-locals/by-value-trait-dyn-compatibility-with-default.stderr17
-rw-r--r--tests/ui/unsized-locals/by-value-trait-dyn-compatibility.rs5
-rw-r--r--tests/ui/unsized-locals/by-value-trait-dyn-compatibility.stderr28
-rw-r--r--tests/ui/unsized-locals/double-move.rs23
-rw-r--r--tests/ui/unsized-locals/double-move.stderr125
-rw-r--r--tests/ui/unsized-locals/ice-size_and_align_of-closure-not-supported-88212.rs7
-rw-r--r--tests/ui/unsized-locals/ice-size_and_align_of-closure-not-supported-88212.stderr26
-rw-r--r--tests/ui/unsized-locals/issue-30276-feature-flagged.rs3
-rw-r--r--tests/ui/unsized-locals/issue-30276-feature-flagged.stderr13
-rw-r--r--tests/ui/unsized-locals/issue-50940-with-feature.rs3
-rw-r--r--tests/ui/unsized-locals/issue-50940-with-feature.stderr15
-rw-r--r--tests/ui/unsized-locals/reference-unsized-locals.rs7
-rw-r--r--tests/ui/unsized-locals/reference-unsized-locals.stderr16
-rw-r--r--tests/ui/unsized-locals/simple-unsized-locals.rs7
-rw-r--r--tests/ui/unsized-locals/simple-unsized-locals.stderr16
-rw-r--r--tests/ui/unsized-locals/suggest-borrow.stderr2
-rw-r--r--tests/ui/unsized-locals/unsized-exprs-rpass.rs4
-rw-r--r--tests/ui/unsized-locals/unsized-locals-using-unsized-fn-params.stderr3
-rw-r--r--tests/ui/unsized-locals/yote.rs4
-rw-r--r--tests/ui/unsized-locals/yote.stderr12
-rw-r--r--tests/ui/unsized/unsized6.stderr8
-rw-r--r--triagebot.toml9
734 files changed, 13304 insertions, 6405 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 21ec1fbaae8..df2842bddb3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3234,9 +3234,9 @@ dependencies = [
 
 [[package]]
 name = "rustc_apfloat"
-version = "0.2.2+llvm-462a31f5a5ab"
+version = "0.2.3+llvm-462a31f5a5ab"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "121e2195ff969977a4e2b5c9965ea867fce7e4cb5aee5b09dee698a7932d574f"
+checksum = "486c2179b4796f65bfe2ee33679acf0927ac83ecf583ad6c91c3b4570911b9ad"
 dependencies = [
  "bitflags",
  "smallvec",
@@ -3793,6 +3793,7 @@ dependencies = [
  "rustc_arena",
  "rustc_ast",
  "rustc_attr_data_structures",
+ "rustc_attr_parsing",
  "rustc_data_structures",
  "rustc_errors",
  "rustc_feature",
@@ -4617,7 +4618,7 @@ dependencies = [
  "derive-where",
  "ena",
  "indexmap",
- "rustc-hash 1.1.0",
+ "rustc-hash 2.1.1",
  "rustc_ast_ir",
  "rustc_data_structures",
  "rustc_index",
diff --git a/bootstrap.example.toml b/bootstrap.example.toml
index 1371fd6442f..19cf360b0fb 100644
--- a/bootstrap.example.toml
+++ b/bootstrap.example.toml
@@ -381,6 +381,15 @@
 #    "miri", "cargo-miri" # for dev/nightly channels
 #]
 
+# Specify build configuration specific for some tool, such as enabled features.
+# This option has no effect on which tools are enabled: refer to the `tools` option for that.
+#
+# For example, to build Miri with tracing support, use `tool.miri.features = ["tracing"]`
+#
+# The default value for the `features` array is `[]`. However, please note that other flags in
+# `bootstrap.toml` might influence the features enabled for some tools.
+#tool.TOOL_NAME.features = [FEATURE1, FEATURE2]
+
 # Verbosity level: 0 == not verbose, 1 == verbose, 2 == very verbose, 3 == print environment variables on each rustc invocation
 #verbose = 0
 
diff --git a/compiler/rustc_abi/src/canon_abi.rs b/compiler/rustc_abi/src/canon_abi.rs
index 03eeb645489..7c020be6761 100644
--- a/compiler/rustc_abi/src/canon_abi.rs
+++ b/compiler/rustc_abi/src/canon_abi.rs
@@ -28,6 +28,9 @@ pub enum CanonAbi {
     Rust,
     RustCold,
 
+    /// An ABI that rustc does not know how to call or define.
+    Custom,
+
     /// ABIs relevant to 32-bit Arm targets
     Arm(ArmCall),
     /// ABI relevant to GPUs: the entry point for a GPU kernel
@@ -57,6 +60,7 @@ impl fmt::Display for CanonAbi {
             CanonAbi::C => ExternAbi::C { unwind: false },
             CanonAbi::Rust => ExternAbi::Rust,
             CanonAbi::RustCold => ExternAbi::RustCold,
+            CanonAbi::Custom => ExternAbi::Custom,
             CanonAbi::Arm(arm_call) => match arm_call {
                 ArmCall::Aapcs => ExternAbi::Aapcs { unwind: false },
                 ArmCall::CCmseNonSecureCall => ExternAbi::CCmseNonSecureCall,
diff --git a/compiler/rustc_abi/src/extern_abi.rs b/compiler/rustc_abi/src/extern_abi.rs
index 0bc1c8a0930..7457ae1f033 100644
--- a/compiler/rustc_abi/src/extern_abi.rs
+++ b/compiler/rustc_abi/src/extern_abi.rs
@@ -40,6 +40,11 @@ pub enum ExternAbi {
     /// Even normally-compatible Rust types can become ABI-incompatible with this ABI!
     Unadjusted,
 
+    /// An ABI that rustc does not know how to call or define. Functions with this ABI can
+    /// only be created using `#[naked]` functions or `extern "custom"` blocks, and can only
+    /// be called from inline assembly.
+    Custom,
+
     /// UEFI ABI, usually an alias of C, but sometimes an arch-specific alias
     /// and only valid on platforms that have a UEFI standard
     EfiApi,
@@ -141,6 +146,7 @@ abi_impls! {
             AvrNonBlockingInterrupt =><= "avr-non-blocking-interrupt",
             Cdecl { unwind: false } =><= "cdecl",
             Cdecl { unwind: true } =><= "cdecl-unwind",
+            Custom =><= "custom",
             EfiApi =><= "efiapi",
             Fastcall { unwind: false } =><= "fastcall",
             Fastcall { unwind: true } =><= "fastcall-unwind",
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index cf40c3f7f6f..6b51cbd7fbe 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -97,12 +97,12 @@ pub struct Path {
     pub tokens: Option<LazyAttrTokenStream>,
 }
 
+// Succeeds if the path has a single segment that is arg-free and matches the given symbol.
 impl PartialEq<Symbol> for Path {
     #[inline]
     fn eq(&self, name: &Symbol) -> bool {
         if let [segment] = self.segments.as_ref()
-            && segment.args.is_none()
-            && segment.ident.name == *name
+            && segment == name
         {
             true
         } else {
@@ -111,6 +111,15 @@ impl PartialEq<Symbol> for Path {
     }
 }
 
+// Succeeds if the path has segments that are arg-free and match the given symbols.
+impl PartialEq<&[Symbol]> for Path {
+    #[inline]
+    fn eq(&self, names: &&[Symbol]) -> bool {
+        self.segments.len() == names.len()
+            && self.segments.iter().zip(names.iter()).all(|(s1, s2)| s1 == s2)
+    }
+}
+
 impl<CTX: rustc_span::HashStableContext> HashStable<CTX> for Path {
     fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
         self.segments.len().hash_stable(hcx, hasher);
@@ -166,6 +175,14 @@ pub struct PathSegment {
     pub args: Option<P<GenericArgs>>,
 }
 
+// Succeeds if the path segment is arg-free and matches the given symbol.
+impl PartialEq<Symbol> for PathSegment {
+    #[inline]
+    fn eq(&self, name: &Symbol) -> bool {
+        self.args.is_none() && self.ident.name == *name
+    }
+}
+
 impl PathSegment {
     pub fn from_ident(ident: Ident) -> Self {
         PathSegment { ident, id: DUMMY_NODE_ID, args: None }
@@ -1441,11 +1458,15 @@ impl Expr {
                 }
             }
 
-            ExprKind::Break(..)
-            | ExprKind::Ret(..)
-            | ExprKind::Yield(..)
-            | ExprKind::Yeet(..)
-            | ExprKind::Become(..) => ExprPrecedence::Jump,
+            ExprKind::Break(_ /*label*/, value)
+            | ExprKind::Ret(value)
+            | ExprKind::Yield(YieldKind::Prefix(value))
+            | ExprKind::Yeet(value) => match value {
+                Some(_) => ExprPrecedence::Jump,
+                None => ExprPrecedence::Unambiguous,
+            },
+
+            ExprKind::Become(_) => ExprPrecedence::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
@@ -1502,6 +1523,7 @@ impl Expr {
             | ExprKind::Underscore
             | ExprKind::UnsafeBinderCast(..)
             | ExprKind::While(..)
+            | ExprKind::Yield(YieldKind::Postfix(..))
             | ExprKind::Err(_)
             | ExprKind::Dummy => ExprPrecedence::Unambiguous,
         }
@@ -3520,6 +3542,38 @@ impl FnHeader {
             || matches!(constness, Const::Yes(_))
             || !matches!(ext, Extern::None)
     }
+
+    /// Return a span encompassing the header, or none if all options are default.
+    pub fn span(&self) -> Option<Span> {
+        fn append(a: &mut Option<Span>, b: Span) {
+            *a = match a {
+                None => Some(b),
+                Some(x) => Some(x.to(b)),
+            }
+        }
+
+        let mut full_span = None;
+
+        match self.safety {
+            Safety::Unsafe(span) | Safety::Safe(span) => append(&mut full_span, span),
+            Safety::Default => {}
+        };
+
+        if let Some(coroutine_kind) = self.coroutine_kind {
+            append(&mut full_span, coroutine_kind.span());
+        }
+
+        if let Const::Yes(span) = self.constness {
+            append(&mut full_span, span);
+        }
+
+        match self.ext {
+            Extern::Implicit(span) | Extern::Explicit(_, span) => append(&mut full_span, span),
+            Extern::None => {}
+        }
+
+        full_span
+    }
 }
 
 impl Default for FnHeader {
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index c88aa5c33ea..1449a4a5fb3 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -1700,7 +1700,8 @@ fn visit_nested_use_tree<'a, V: Visitor<'a>>(
 }
 
 pub fn walk_stmt<'a, V: Visitor<'a>>(visitor: &mut V, statement: &'a Stmt) -> V::Result {
-    let Stmt { id: _, kind, span: _ } = statement;
+    let Stmt { id, kind, span: _ } = statement;
+    try_visit!(visit_id(visitor, id));
     match kind {
         StmtKind::Let(local) => try_visit!(visitor.visit_local(local)),
         StmtKind::Item(item) => try_visit!(visitor.visit_item(item)),
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 537d4a2a6af..718edad0cc6 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -73,16 +73,15 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     // Merge attributes into the inner expression.
                     if !e.attrs.is_empty() {
                         let old_attrs = self.attrs.get(&ex.hir_id.local_id).copied().unwrap_or(&[]);
-                        let attrs = &*self.arena.alloc_from_iter(
-                            self.lower_attrs_vec(&e.attrs, e.span)
-                                .into_iter()
-                                .chain(old_attrs.iter().cloned()),
-                        );
-                        if attrs.is_empty() {
+                        let new_attrs = self
+                            .lower_attrs_vec(&e.attrs, e.span, ex.hir_id)
+                            .into_iter()
+                            .chain(old_attrs.iter().cloned());
+                        let new_attrs = &*self.arena.alloc_from_iter(new_attrs);
+                        if new_attrs.is_empty() {
                             return ex;
                         }
-
-                        self.attrs.insert(ex.hir_id.local_id, attrs);
+                        self.attrs.insert(ex.hir_id.local_id, new_attrs);
                     }
                     return ex;
                 }
@@ -2035,7 +2034,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 let ret_expr = self.checked_return(Some(from_residual_expr));
                 self.arena.alloc(self.expr(try_span, ret_expr))
             };
-            self.lower_attrs(ret_expr.hir_id, &attrs, ret_expr.span);
+            self.lower_attrs(ret_expr.hir_id, &attrs, span);
 
             let break_pat = self.pat_cf_break(try_span, residual_local);
             self.arm(break_pat, ret_expr)
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index d1a2ddbdb34..e2d291a536d 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -63,7 +63,7 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> {
 
         for (def_id, info) in lctx.children {
             let owner = self.owners.ensure_contains_elem(def_id, || hir::MaybeOwner::Phantom);
-            debug_assert!(
+            assert!(
                 matches!(owner, hir::MaybeOwner::Phantom),
                 "duplicate copy of {def_id:?} in lctx.children"
             );
@@ -78,7 +78,7 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> {
             match node {
                 AstOwner::NonOwner => {}
                 AstOwner::Crate(c) => {
-                    debug_assert_eq!(self.resolver.node_id_to_def_id[&CRATE_NODE_ID], CRATE_DEF_ID);
+                    assert_eq!(self.resolver.node_id_to_def_id[&CRATE_NODE_ID], CRATE_DEF_ID);
                     self.with_lctx(CRATE_NODE_ID, |lctx| {
                         let module = lctx.lower_mod(&c.items, &c.spans);
                         // FIXME(jdonszelman): is dummy span ever a problem here?
@@ -1160,7 +1160,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
     ) -> hir::BodyId {
         let body = hir::Body { params, value: self.arena.alloc(value) };
         let id = body.id();
-        debug_assert_eq!(id.hir_id.owner, self.current_hir_id_owner);
+        assert_eq!(id.hir_id.owner, self.current_hir_id_owner);
         self.bodies.push((id.hir_id.local_id, self.arena.alloc(body)));
         id
     }
@@ -1673,8 +1673,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
         itctx: ImplTraitContext,
         f: impl FnOnce(&mut Self) -> T,
     ) -> (&'hir hir::Generics<'hir>, T) {
-        debug_assert!(self.impl_trait_defs.is_empty());
-        debug_assert!(self.impl_trait_bounds.is_empty());
+        assert!(self.impl_trait_defs.is_empty());
+        assert!(self.impl_trait_bounds.is_empty());
 
         // 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
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index b99df8bd7e5..3b99a653417 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -51,6 +51,7 @@ use rustc_data_structures::tagged_ptr::TaggedRef;
 use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle, StashKey};
 use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res};
 use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId};
+use rustc_hir::lints::DelayedLint;
 use rustc_hir::{
     self as hir, AngleBrackets, ConstArg, GenericArg, HirId, ItemLocalMap, LangItem,
     LifetimeSource, LifetimeSyntax, ParamName, TraitCandidate,
@@ -141,6 +142,8 @@ struct LoweringContext<'a, 'hir> {
     allow_for_await: Arc<[Symbol]>,
     allow_async_fn_traits: Arc<[Symbol]>,
 
+    delayed_lints: Vec<DelayedLint>,
+
     attribute_parser: AttributeParser<'hir>,
 }
 
@@ -190,6 +193,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             allow_async_iterator: [sym::gen_future, sym::async_iterator].into(),
 
             attribute_parser: AttributeParser::new(tcx.sess, tcx.features(), registered_tools),
+            delayed_lints: Vec::new(),
         }
     }
 
@@ -198,6 +202,22 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
     }
 }
 
+struct SpanLowerer {
+    is_incremental: bool,
+    def_id: LocalDefId,
+}
+
+impl SpanLowerer {
+    fn lower(&self, span: Span) -> Span {
+        if self.is_incremental {
+            span.with_parent(Some(self.def_id))
+        } else {
+            // Do not make spans relative when not using incremental compilation.
+            span
+        }
+    }
+}
+
 #[extension(trait ResolverAstLoweringExt)]
 impl ResolverAstLowering {
     fn legacy_const_generic_args(&self, expr: &Expr) -> Option<Vec<usize>> {
@@ -503,7 +523,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         span: Span,
     ) -> LocalDefId {
         let parent = self.current_hir_id_owner.def_id;
-        debug_assert_ne!(node_id, ast::DUMMY_NODE_ID);
+        assert_ne!(node_id, ast::DUMMY_NODE_ID);
         assert!(
             self.opt_local_def_id(node_id).is_none(),
             "adding a def'n for node-id {:?} and def kind {:?} but a previous def'n exists: {:?}",
@@ -573,6 +593,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             std::mem::replace(&mut self.item_local_id_counter, hir::ItemLocalId::new(1));
         let current_impl_trait_defs = std::mem::take(&mut self.impl_trait_defs);
         let current_impl_trait_bounds = std::mem::take(&mut self.impl_trait_bounds);
+        let current_delayed_lints = std::mem::take(&mut self.delayed_lints);
 
         // Do not reset `next_node_id` and `node_id_to_def_id`:
         // we want `f` to be able to refer to the `LocalDefId`s that the caller created.
@@ -586,10 +607,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         }
 
         let item = f(self);
-        debug_assert_eq!(owner_id, item.def_id());
+        assert_eq!(owner_id, item.def_id());
         // `f` should have consumed all the elements in these vectors when constructing `item`.
-        debug_assert!(self.impl_trait_defs.is_empty());
-        debug_assert!(self.impl_trait_bounds.is_empty());
+        assert!(self.impl_trait_defs.is_empty());
+        assert!(self.impl_trait_bounds.is_empty());
         let info = self.make_owner_info(item);
 
         self.attrs = current_attrs;
@@ -606,6 +627,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         self.item_local_id_counter = current_local_counter;
         self.impl_trait_defs = current_impl_trait_defs;
         self.impl_trait_bounds = current_impl_trait_bounds;
+        self.delayed_lints = current_delayed_lints;
 
         debug_assert!(!self.children.iter().any(|(id, _)| id == &owner_id.def_id));
         self.children.push((owner_id.def_id, hir::MaybeOwner::Owner(info)));
@@ -616,6 +638,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         let mut bodies = std::mem::take(&mut self.bodies);
         let define_opaque = std::mem::take(&mut self.define_opaque);
         let trait_map = std::mem::take(&mut self.trait_map);
+        let delayed_lints = std::mem::take(&mut self.delayed_lints).into_boxed_slice();
 
         #[cfg(debug_assertions)]
         for (id, attrs) in attrs.iter() {
@@ -629,14 +652,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         let bodies = SortedMap::from_presorted_elements(bodies);
 
         // Don't hash unless necessary, because it's expensive.
-        let (opt_hash_including_bodies, attrs_hash) =
-            self.tcx.hash_owner_nodes(node, &bodies, &attrs, define_opaque);
+        let (opt_hash_including_bodies, attrs_hash, delayed_lints_hash) =
+            self.tcx.hash_owner_nodes(node, &bodies, &attrs, &delayed_lints, define_opaque);
         let num_nodes = self.item_local_id_counter.as_usize();
         let (nodes, parenting) = index::index_hir(self.tcx, node, &bodies, num_nodes);
         let nodes = hir::OwnerNodes { opt_hash_including_bodies, nodes, bodies };
         let attrs = hir::AttributeMap { map: attrs, opt_hash: attrs_hash, define_opaque };
+        let delayed_lints =
+            hir::lints::DelayedLints { lints: delayed_lints, opt_hash: delayed_lints_hash };
 
-        self.arena.alloc(hir::OwnerInfo { nodes, parenting, attrs, trait_map })
+        self.arena.alloc(hir::OwnerInfo { nodes, parenting, attrs, trait_map, delayed_lints })
     }
 
     /// This method allocates a new `HirId` for the given `NodeId`.
@@ -759,15 +784,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         })
     }
 
+    fn span_lowerer(&self) -> SpanLowerer {
+        SpanLowerer {
+            is_incremental: self.tcx.sess.opts.incremental.is_some(),
+            def_id: self.current_hir_id_owner.def_id,
+        }
+    }
+
     /// Intercept all spans entering HIR.
     /// Mark a span as relative to the current owning item.
     fn lower_span(&self, span: Span) -> Span {
-        if self.tcx.sess.opts.incremental.is_some() {
-            span.with_parent(Some(self.current_hir_id_owner.def_id))
-        } else {
-            // Do not make spans relative when not using incremental compilation.
-            span
-        }
+        self.span_lowerer().lower(span)
     }
 
     fn lower_ident(&self, ident: Ident) -> Ident {
@@ -889,9 +916,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         if attrs.is_empty() {
             &[]
         } else {
-            let lowered_attrs = self.lower_attrs_vec(attrs, self.lower_span(target_span));
+            let lowered_attrs = self.lower_attrs_vec(attrs, self.lower_span(target_span), id);
 
-            debug_assert_eq!(id.owner, self.current_hir_id_owner);
+            assert_eq!(id.owner, self.current_hir_id_owner);
             let ret = self.arena.alloc_from_iter(lowered_attrs);
 
             // this is possible if an item contained syntactical attribute,
@@ -909,16 +936,30 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         }
     }
 
-    fn lower_attrs_vec(&self, attrs: &[Attribute], target_span: Span) -> Vec<hir::Attribute> {
-        self.attribute_parser
-            .parse_attribute_list(attrs, target_span, OmitDoc::Lower, |s| self.lower_span(s))
+    fn lower_attrs_vec(
+        &mut self,
+        attrs: &[Attribute],
+        target_span: Span,
+        target_hir_id: HirId,
+    ) -> Vec<hir::Attribute> {
+        let l = self.span_lowerer();
+        self.attribute_parser.parse_attribute_list(
+            attrs,
+            target_span,
+            target_hir_id,
+            OmitDoc::Lower,
+            |s| l.lower(s),
+            |l| {
+                self.delayed_lints.push(DelayedLint::AttributeParsing(l));
+            },
+        )
     }
 
     fn alias_attrs(&mut self, id: HirId, target_id: HirId) {
-        debug_assert_eq!(id.owner, self.current_hir_id_owner);
-        debug_assert_eq!(target_id.owner, self.current_hir_id_owner);
+        assert_eq!(id.owner, self.current_hir_id_owner);
+        assert_eq!(target_id.owner, self.current_hir_id_owner);
         if let Some(&a) = self.attrs.get(&target_id.local_id) {
-            debug_assert!(!a.is_empty());
+            assert!(!a.is_empty());
             self.attrs.insert(id.local_id, a);
         }
     }
@@ -1397,7 +1438,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 let id = if let Some(LifetimeRes::ElidedAnchor { start, end }) =
                     self.resolver.get_lifetime_res(t.id)
                 {
-                    debug_assert_eq!(start.plus(1), end);
+                    assert_eq!(start.plus(1), end);
                     start
                 } else {
                     self.next_node_id()
@@ -1805,16 +1846,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         let res = match res {
             LifetimeRes::Param { param, .. } => hir::LifetimeKind::Param(param),
             LifetimeRes::Fresh { param, .. } => {
-                debug_assert_eq!(ident.name, kw::UnderscoreLifetime);
+                assert_eq!(ident.name, kw::UnderscoreLifetime);
                 let param = self.local_def_id(param);
                 hir::LifetimeKind::Param(param)
             }
             LifetimeRes::Infer => {
-                debug_assert_eq!(ident.name, kw::UnderscoreLifetime);
+                assert_eq!(ident.name, kw::UnderscoreLifetime);
                 hir::LifetimeKind::Infer
             }
             LifetimeRes::Static { .. } => {
-                debug_assert!(matches!(ident.name, kw::StaticLifetime | kw::UnderscoreLifetime));
+                assert!(matches!(ident.name, kw::StaticLifetime | kw::UnderscoreLifetime));
                 hir::LifetimeKind::Static
             }
             LifetimeRes::Error => hir::LifetimeKind::Error,
@@ -2106,7 +2147,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 ty_id,
                 &None,
                 path,
-                ParamMode::Optional,
+                ParamMode::Explicit,
                 AllowReturnTypeNotation::No,
                 // FIXME(mgca): update for `fn foo() -> Bar<FOO<impl Trait>>` support
                 ImplTraitContext::Disallowed(ImplTraitPosition::Path),
@@ -2178,7 +2219,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 expr.id,
                 qself,
                 path,
-                ParamMode::Optional,
+                ParamMode::Explicit,
                 AllowReturnTypeNotation::No,
                 // FIXME(mgca): update for `fn foo() -> Bar<FOO<impl Trait>>` support
                 ImplTraitContext::Disallowed(ImplTraitPosition::Path),
@@ -2244,7 +2285,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
     ) -> hir::Stmt<'hir> {
         let hir_id = self.next_id();
         if let Some(a) = attrs {
-            debug_assert!(!a.is_empty());
+            assert!(!a.is_empty());
             self.attrs.insert(hir_id.local_id, a);
         }
         let local = hir::LetStmt {
diff --git a/compiler/rustc_ast_lowering/src/stability.rs b/compiler/rustc_ast_lowering/src/stability.rs
index eb052ba1c6d..b8fa2dd3dd6 100644
--- a/compiler/rustc_ast_lowering/src/stability.rs
+++ b/compiler/rustc_ast_lowering/src/stability.rs
@@ -134,5 +134,8 @@ pub fn extern_abi_stability(abi: ExternAbi) -> Result<(), UnstableAbi> {
             feature: sym::cmse_nonsecure_entry,
             explain: GateReason::Experimental,
         }),
+        ExternAbi::Custom => {
+            Err(UnstableAbi { abi, feature: sym::abi_custom, explain: GateReason::Experimental })
+        }
     }
 }
diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl
index 80754a8f65a..9a267501230 100644
--- a/compiler/rustc_ast_passes/messages.ftl
+++ b/compiler/rustc_ast_passes/messages.ftl
@@ -1,3 +1,20 @@
+ast_passes_abi_custom_coroutine =
+    functions with the `"custom"` ABI cannot be `{$coroutine_kind_str}`
+    .suggestion = remove the `{$coroutine_kind_str}` keyword from this definiton
+
+ast_passes_abi_custom_invalid_signature =
+    invalid signature for `extern "custom"` function
+    .note = functions with the `"custom"` ABI cannot have any parameters or return type
+    .suggestion = remove the parameters and return type
+
+ast_passes_abi_custom_safe_foreign_function =
+    foreign functions with the `"custom"` ABI cannot be safe
+    .suggestion = remove the `safe` keyword from this definition
+
+ast_passes_abi_custom_safe_function =
+    functions with the `"custom"` ABI must be unsafe
+    .suggestion = add the `unsafe` keyword to this definition
+
 ast_passes_assoc_const_without_body =
     associated constant in `impl` without body
     .suggestion = provide a definition for the constant
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index d6fe04d2994..018887d0e8e 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -18,6 +18,7 @@
 
 use std::mem;
 use std::ops::{Deref, DerefMut};
+use std::str::FromStr;
 
 use itertools::{Either, Itertools};
 use rustc_abi::ExternAbi;
@@ -81,6 +82,7 @@ struct AstValidator<'a> {
 
     /// Used to ban explicit safety on foreign items when the extern block is not marked as unsafe.
     extern_mod_safety: Option<Safety>,
+    extern_mod_abi: Option<ExternAbi>,
 
     lint_node_id: NodeId,
 
@@ -121,10 +123,17 @@ impl<'a> AstValidator<'a> {
         self.outer_trait_or_trait_impl = old;
     }
 
-    fn with_in_extern_mod(&mut self, extern_mod_safety: Safety, f: impl FnOnce(&mut Self)) {
-        let old = mem::replace(&mut self.extern_mod_safety, Some(extern_mod_safety));
+    fn with_in_extern_mod(
+        &mut self,
+        extern_mod_safety: Safety,
+        abi: Option<ExternAbi>,
+        f: impl FnOnce(&mut Self),
+    ) {
+        let old_safety = mem::replace(&mut self.extern_mod_safety, Some(extern_mod_safety));
+        let old_abi = mem::replace(&mut self.extern_mod_abi, abi);
         f(self);
-        self.extern_mod_safety = old;
+        self.extern_mod_safety = old_safety;
+        self.extern_mod_abi = old_abi;
     }
 
     fn with_tilde_const(
@@ -370,6 +379,65 @@ impl<'a> AstValidator<'a> {
         }
     }
 
+    /// An `extern "custom"` function must be unsafe, and must not have any parameters or return
+    /// type.
+    fn check_custom_abi(&self, ctxt: FnCtxt, ident: &Ident, sig: &FnSig) {
+        let dcx = self.dcx();
+
+        // An `extern "custom"` function must be unsafe.
+        match sig.header.safety {
+            Safety::Unsafe(_) => { /* all good */ }
+            Safety::Safe(safe_span) => {
+                let safe_span =
+                    self.sess.psess.source_map().span_until_non_whitespace(safe_span.to(sig.span));
+                dcx.emit_err(errors::AbiCustomSafeForeignFunction { span: sig.span, safe_span });
+            }
+            Safety::Default => match ctxt {
+                FnCtxt::Foreign => { /* all good */ }
+                FnCtxt::Free | FnCtxt::Assoc(_) => {
+                    self.dcx().emit_err(errors::AbiCustomSafeFunction {
+                        span: sig.span,
+                        unsafe_span: sig.span.shrink_to_lo(),
+                    });
+                }
+            },
+        }
+
+        // An `extern "custom"` function cannot be `async` and/or `gen`.
+        if let Some(coroutine_kind) = sig.header.coroutine_kind {
+            let coroutine_kind_span = self
+                .sess
+                .psess
+                .source_map()
+                .span_until_non_whitespace(coroutine_kind.span().to(sig.span));
+
+            self.dcx().emit_err(errors::AbiCustomCoroutine {
+                span: sig.span,
+                coroutine_kind_span,
+                coroutine_kind_str: coroutine_kind.as_str(),
+            });
+        }
+
+        // An `extern "custom"` function must not have any parameters or return type.
+        let mut spans: Vec<_> = sig.decl.inputs.iter().map(|p| p.span).collect();
+        if let FnRetTy::Ty(ref ret_ty) = sig.decl.output {
+            spans.push(ret_ty.span);
+        }
+
+        if !spans.is_empty() {
+            let header_span = sig.header.span().unwrap_or(sig.span.shrink_to_lo());
+            let suggestion_span = header_span.shrink_to_hi().to(sig.decl.output.span());
+            let padding = if header_span.is_empty() { "" } else { " " };
+
+            self.dcx().emit_err(errors::AbiCustomInvalidSignature {
+                spans,
+                symbol: ident.name,
+                suggestion_span,
+                padding,
+            });
+        }
+    }
+
     /// This ensures that items can only be `unsafe` (or unmarked) outside of extern
     /// blocks.
     ///
@@ -1005,7 +1073,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 if abi.is_none() {
                     self.handle_missing_abi(*extern_span, item.id);
                 }
-                self.with_in_extern_mod(*safety, |this| {
+
+                let extern_abi = abi.and_then(|abi| ExternAbi::from_str(abi.symbol.as_str()).ok());
+                self.with_in_extern_mod(*safety, extern_abi, |this| {
                     visit::walk_item(this, item);
                 });
                 self.extern_mod_span = old_item;
@@ -1145,6 +1215,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 self.check_foreign_fn_bodyless(*ident, body.as_deref());
                 self.check_foreign_fn_headerless(sig.header);
                 self.check_foreign_item_ascii_only(*ident);
+                if self.extern_mod_abi == Some(ExternAbi::Custom) {
+                    self.check_custom_abi(FnCtxt::Foreign, ident, sig);
+                }
             }
             ForeignItemKind::TyAlias(box TyAlias {
                 defaultness,
@@ -1352,6 +1425,13 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
             self.check_item_safety(span, safety);
         }
 
+        if let FnKind::Fn(ctxt, _, fun) = fk
+            && let Extern::Explicit(str_lit, _) = fun.sig.header.ext
+            && let Ok(ExternAbi::Custom) = ExternAbi::from_str(str_lit.symbol.as_str())
+        {
+            self.check_custom_abi(ctxt, &fun.ident, &fun.sig);
+        }
+
         self.check_c_variadic_type(fk);
 
         // Functions cannot both be `const async` or `const gen`
@@ -1703,6 +1783,7 @@ pub fn check_crate(
         outer_impl_trait_span: None,
         disallow_tilde_const: Some(TildeConstReason::Item),
         extern_mod_safety: None,
+        extern_mod_abi: None,
         lint_node_id: CRATE_NODE_ID,
         is_sdylib_interface,
         lint_buffer: lints,
diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs
index 6f9737e0831..c437e62f4d3 100644
--- a/compiler/rustc_ast_passes/src/errors.rs
+++ b/compiler/rustc_ast_passes/src/errors.rs
@@ -824,3 +824,67 @@ pub(crate) struct MissingAbi {
     #[suggestion(code = "extern \"<abi>\"", applicability = "has-placeholders")]
     pub span: Span,
 }
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_abi_custom_safe_foreign_function)]
+pub(crate) struct AbiCustomSafeForeignFunction {
+    #[primary_span]
+    pub span: Span,
+
+    #[suggestion(
+        ast_passes_suggestion,
+        applicability = "maybe-incorrect",
+        code = "",
+        style = "verbose"
+    )]
+    pub safe_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_abi_custom_safe_function)]
+pub(crate) struct AbiCustomSafeFunction {
+    #[primary_span]
+    pub span: Span,
+
+    #[suggestion(
+        ast_passes_suggestion,
+        applicability = "maybe-incorrect",
+        code = "unsafe ",
+        style = "verbose"
+    )]
+    pub unsafe_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_abi_custom_coroutine)]
+pub(crate) struct AbiCustomCoroutine {
+    #[primary_span]
+    pub span: Span,
+
+    #[suggestion(
+        ast_passes_suggestion,
+        applicability = "maybe-incorrect",
+        code = "",
+        style = "verbose"
+    )]
+    pub coroutine_kind_span: Span,
+    pub coroutine_kind_str: &'static str,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_abi_custom_invalid_signature)]
+#[note]
+pub(crate) struct AbiCustomInvalidSignature {
+    #[primary_span]
+    pub spans: Vec<Span>,
+
+    #[suggestion(
+        ast_passes_suggestion,
+        applicability = "maybe-incorrect",
+        code = "{padding}fn {symbol}()",
+        style = "verbose"
+    )]
+    pub suggestion_span: Span,
+    pub symbol: Symbol,
+    pub padding: &'static str,
+}
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 3682d25d341..1ec56868f37 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -36,6 +36,16 @@ macro_rules! gate_alt {
             feature_err(&$visitor.sess, $name, $span, $explain).emit();
         }
     }};
+    ($visitor:expr, $has_feature:expr, $name:expr, $span:expr, $explain:expr, $notes: expr) => {{
+        if !$has_feature && !$span.allows_unstable($name) {
+            #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
+            let mut diag = feature_err(&$visitor.sess, $name, $span, $explain);
+            for note in $notes {
+                diag.note(*note);
+            }
+            diag.emit();
+        }
+    }};
 }
 
 /// The case involving a multispan.
@@ -154,11 +164,11 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
         let attr_info = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name));
         // Check feature gates for built-in attributes.
         if let Some(BuiltinAttribute {
-            gate: AttributeGate::Gated(_, name, descr, has_feature),
+            gate: AttributeGate::Gated { feature, message, check, notes, .. },
             ..
         }) = attr_info
         {
-            gate_alt!(self, has_feature(self.features), *name, attr.span, *descr);
+            gate_alt!(self, check(self.features), *feature, attr.span, *message, *notes);
         }
         // Check unstable flavors of the `#[doc]` attribute.
         if attr.has_name(sym::doc) {
diff --git a/compiler/rustc_ast_pretty/src/pprust/mod.rs b/compiler/rustc_ast_pretty/src/pprust/mod.rs
index a05e2bd6a5d..a766e2006e5 100644
--- a/compiler/rustc_ast_pretty/src/pprust/mod.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/mod.rs
@@ -53,6 +53,18 @@ pub fn item_to_string(i: &ast::Item) -> String {
     State::new().item_to_string(i)
 }
 
+pub fn assoc_item_to_string(i: &ast::AssocItem) -> String {
+    State::new().assoc_item_to_string(i)
+}
+
+pub fn foreign_item_to_string(i: &ast::ForeignItem) -> String {
+    State::new().foreign_item_to_string(i)
+}
+
+pub fn stmt_to_string(s: &ast::Stmt) -> String {
+    State::new().stmt_to_string(s)
+}
+
 pub fn path_to_string(p: &ast::Path) -> String {
     State::new().path_to_string(p)
 }
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index 0990c9b27eb..3d738fa31f2 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -1063,6 +1063,14 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
         Self::to_string(|s| s.print_item(i))
     }
 
+    fn assoc_item_to_string(&self, i: &ast::AssocItem) -> String {
+        Self::to_string(|s| s.print_assoc_item(i))
+    }
+
+    fn foreign_item_to_string(&self, i: &ast::ForeignItem) -> String {
+        Self::to_string(|s| s.print_foreign_item(i))
+    }
+
     fn path_to_string(&self, p: &ast::Path) -> String {
         Self::to_string(|s| s.print_path(p, false, 0))
     }
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
index c9a7e2aebd0..ee49246a4bb 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
@@ -7,8 +7,8 @@ use rustc_ast::util::classify;
 use rustc_ast::util::literal::escape_byte_str_symbol;
 use rustc_ast::util::parser::{self, ExprPrecedence, Fixity};
 use rustc_ast::{
-    self as ast, BlockCheckMode, FormatAlignment, FormatArgPosition, FormatArgsPiece, FormatCount,
-    FormatDebugHex, FormatSign, FormatTrait, YieldKind, token,
+    self as ast, BinOpKind, BlockCheckMode, FormatAlignment, FormatArgPosition, FormatArgsPiece,
+    FormatCount, FormatDebugHex, FormatSign, FormatTrait, YieldKind, token,
 };
 
 use crate::pp::Breaks::Inconsistent;
@@ -214,13 +214,6 @@ impl<'a> State<'a> {
     }
 
     fn print_expr_call(&mut self, func: &ast::Expr, args: &[P<ast::Expr>], fixup: FixupContext) {
-        let needs_paren = match func.kind {
-            // In order to call a named field, needs parens: `(self.fun)()`
-            // But not for an unnamed field: `self.0()`
-            ast::ExprKind::Field(_, name) => !name.is_numeric(),
-            _ => func.precedence() < ExprPrecedence::Unambiguous,
-        };
-
         // Independent of parenthesization related to precedence, we must
         // parenthesize `func` if this is a statement context in which without
         // parentheses, a statement boundary would occur inside `func` or
@@ -237,8 +230,16 @@ impl<'a> State<'a> {
         // because the latter is valid syntax but with the incorrect meaning.
         // It's a match-expression followed by tuple-expression, not a function
         // call.
-        self.print_expr_cond_paren(func, needs_paren, fixup.leftmost_subexpression());
+        let func_fixup = fixup.leftmost_subexpression_with_operator(true);
+
+        let needs_paren = match func.kind {
+            // In order to call a named field, needs parens: `(self.fun)()`
+            // But not for an unnamed field: `self.0()`
+            ast::ExprKind::Field(_, name) => !name.is_numeric(),
+            _ => func_fixup.precedence(func) < ExprPrecedence::Unambiguous,
+        };
 
+        self.print_expr_cond_paren(func, needs_paren, func_fixup);
         self.print_call_post(args)
     }
 
@@ -281,9 +282,24 @@ impl<'a> State<'a> {
         rhs: &ast::Expr,
         fixup: FixupContext,
     ) {
+        let operator_can_begin_expr = match op {
+            | BinOpKind::Sub     // -x
+            | BinOpKind::Mul     // *x
+            | BinOpKind::And     // &&x
+            | BinOpKind::Or      // || x
+            | BinOpKind::BitAnd  // &x
+            | BinOpKind::BitOr   // |x| x
+            | BinOpKind::Shl     // <<T as Trait>::Type as Trait>::CONST
+            | BinOpKind::Lt      // <T as Trait>::CONST
+              => true,
+            _ => false,
+        };
+
+        let left_fixup = fixup.leftmost_subexpression_with_operator(operator_can_begin_expr);
+
         let binop_prec = op.precedence();
-        let left_prec = lhs.precedence();
-        let right_prec = rhs.precedence();
+        let left_prec = left_fixup.precedence(lhs);
+        let right_prec = fixup.precedence(rhs);
 
         let (mut left_needs_paren, right_needs_paren) = match op.fixity() {
             Fixity::Left => (left_prec < binop_prec, right_prec <= binop_prec),
@@ -312,18 +328,18 @@ impl<'a> State<'a> {
             _ => {}
         }
 
-        self.print_expr_cond_paren(lhs, left_needs_paren, fixup.leftmost_subexpression());
+        self.print_expr_cond_paren(lhs, left_needs_paren, left_fixup);
         self.space();
         self.word_space(op.as_str());
-        self.print_expr_cond_paren(rhs, right_needs_paren, fixup.subsequent_subexpression());
+        self.print_expr_cond_paren(rhs, right_needs_paren, fixup.rightmost_subexpression());
     }
 
     fn print_expr_unary(&mut self, op: ast::UnOp, expr: &ast::Expr, fixup: FixupContext) {
         self.word(op.as_str());
         self.print_expr_cond_paren(
             expr,
-            expr.precedence() < ExprPrecedence::Prefix,
-            fixup.subsequent_subexpression(),
+            fixup.precedence(expr) < ExprPrecedence::Prefix,
+            fixup.rightmost_subexpression(),
         );
     }
 
@@ -344,8 +360,8 @@ impl<'a> State<'a> {
         }
         self.print_expr_cond_paren(
             expr,
-            expr.precedence() < ExprPrecedence::Prefix,
-            fixup.subsequent_subexpression(),
+            fixup.precedence(expr) < ExprPrecedence::Prefix,
+            fixup.rightmost_subexpression(),
         );
     }
 
@@ -590,8 +606,8 @@ impl<'a> State<'a> {
                 self.word_space("=");
                 self.print_expr_cond_paren(
                     rhs,
-                    rhs.precedence() < ExprPrecedence::Assign,
-                    fixup.subsequent_subexpression(),
+                    fixup.precedence(rhs) < ExprPrecedence::Assign,
+                    fixup.rightmost_subexpression(),
                 );
             }
             ast::ExprKind::AssignOp(op, lhs, rhs) => {
@@ -604,8 +620,8 @@ impl<'a> State<'a> {
                 self.word_space(op.node.as_str());
                 self.print_expr_cond_paren(
                     rhs,
-                    rhs.precedence() < ExprPrecedence::Assign,
-                    fixup.subsequent_subexpression(),
+                    fixup.precedence(rhs) < ExprPrecedence::Assign,
+                    fixup.rightmost_subexpression(),
                 );
             }
             ast::ExprKind::Field(expr, ident) => {
@@ -618,10 +634,11 @@ impl<'a> State<'a> {
                 self.print_ident(*ident);
             }
             ast::ExprKind::Index(expr, index, _) => {
+                let expr_fixup = fixup.leftmost_subexpression_with_operator(true);
                 self.print_expr_cond_paren(
                     expr,
-                    expr.precedence() < ExprPrecedence::Unambiguous,
-                    fixup.leftmost_subexpression(),
+                    expr_fixup.precedence(expr) < ExprPrecedence::Unambiguous,
+                    expr_fixup,
                 );
                 self.word("[");
                 self.print_expr(index, FixupContext::default());
@@ -634,10 +651,11 @@ impl<'a> State<'a> {
                 // a "normal" binop gets parenthesized. (`LOr` is the lowest-precedence binop.)
                 let fake_prec = ExprPrecedence::LOr;
                 if let Some(e) = start {
+                    let start_fixup = fixup.leftmost_subexpression_with_operator(true);
                     self.print_expr_cond_paren(
                         e,
-                        e.precedence() < fake_prec,
-                        fixup.leftmost_subexpression(),
+                        start_fixup.precedence(e) < fake_prec,
+                        start_fixup,
                     );
                 }
                 match limits {
@@ -647,8 +665,8 @@ impl<'a> State<'a> {
                 if let Some(e) = end {
                     self.print_expr_cond_paren(
                         e,
-                        e.precedence() < fake_prec,
-                        fixup.subsequent_subexpression(),
+                        fixup.precedence(e) < fake_prec,
+                        fixup.rightmost_subexpression(),
                     );
                 }
             }
@@ -665,11 +683,10 @@ impl<'a> State<'a> {
                     self.space();
                     self.print_expr_cond_paren(
                         expr,
-                        // Parenthesize if required by precedence, or in the
-                        // case of `break 'inner: loop { break 'inner 1 } + 1`
-                        expr.precedence() < ExprPrecedence::Jump
-                            || (opt_label.is_none() && classify::leading_labeled_expr(expr)),
-                        fixup.subsequent_subexpression(),
+                        // Parenthesize `break 'inner: loop { break 'inner 1 } + 1`
+                        //                     ^---------------------------------^
+                        opt_label.is_none() && classify::leading_labeled_expr(expr),
+                        fixup.rightmost_subexpression(),
                     );
                 }
             }
@@ -684,11 +701,7 @@ impl<'a> State<'a> {
                 self.word("return");
                 if let Some(expr) = result {
                     self.word(" ");
-                    self.print_expr_cond_paren(
-                        expr,
-                        expr.precedence() < ExprPrecedence::Jump,
-                        fixup.subsequent_subexpression(),
-                    );
+                    self.print_expr(expr, fixup.rightmost_subexpression());
                 }
             }
             ast::ExprKind::Yeet(result) => {
@@ -697,21 +710,13 @@ impl<'a> State<'a> {
                 self.word("yeet");
                 if let Some(expr) = result {
                     self.word(" ");
-                    self.print_expr_cond_paren(
-                        expr,
-                        expr.precedence() < ExprPrecedence::Jump,
-                        fixup.subsequent_subexpression(),
-                    );
+                    self.print_expr(expr, fixup.rightmost_subexpression());
                 }
             }
             ast::ExprKind::Become(result) => {
                 self.word("become");
                 self.word(" ");
-                self.print_expr_cond_paren(
-                    result,
-                    result.precedence() < ExprPrecedence::Jump,
-                    fixup.subsequent_subexpression(),
-                );
+                self.print_expr(result, fixup.rightmost_subexpression());
             }
             ast::ExprKind::InlineAsm(a) => {
                 // FIXME: Print `builtin # asm` once macro `asm` uses `builtin_syntax`.
@@ -761,11 +766,7 @@ impl<'a> State<'a> {
 
                 if let Some(expr) = e {
                     self.space();
-                    self.print_expr_cond_paren(
-                        expr,
-                        expr.precedence() < ExprPrecedence::Jump,
-                        fixup.subsequent_subexpression(),
-                    );
+                    self.print_expr(expr, fixup.rightmost_subexpression());
                 }
             }
             ast::ExprKind::Yield(YieldKind::Postfix(e)) => {
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/fixup.rs b/compiler/rustc_ast_pretty/src/pprust/state/fixup.rs
index 3ef21f5cb29..eb5ac8b78a8 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/fixup.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/fixup.rs
@@ -1,5 +1,6 @@
-use rustc_ast::Expr;
-use rustc_ast::util::{classify, parser};
+use rustc_ast::util::classify;
+use rustc_ast::util::parser::{self, ExprPrecedence};
+use rustc_ast::{Expr, ExprKind, YieldKind};
 
 // The default amount of fixing is minimal fixing, so all fixups are set to `false` by `Default`.
 // Fixups should be turned on in a targeted fashion where needed.
@@ -93,6 +94,24 @@ pub(crate) struct FixupContext {
     /// }
     /// ```
     parenthesize_exterior_struct_lit: bool,
+
+    /// This is the difference between:
+    ///
+    /// ```ignore (illustrative)
+    /// let _ = (return) - 1;  // without paren, this would return -1
+    ///
+    /// let _ = return + 1;  // no paren because '+' cannot begin expr
+    /// ```
+    next_operator_can_begin_expr: bool,
+
+    /// This is the difference between:
+    ///
+    /// ```ignore (illustrative)
+    /// let _ = 1 + return 1;  // no parens if rightmost subexpression
+    ///
+    /// let _ = 1 + (return 1) + 1;  // needs parens
+    /// ```
+    next_operator_can_continue_expr: bool,
 }
 
 impl FixupContext {
@@ -134,6 +153,8 @@ impl FixupContext {
             match_arm: false,
             leftmost_subexpression_in_match_arm: self.match_arm
                 || self.leftmost_subexpression_in_match_arm,
+            next_operator_can_begin_expr: false,
+            next_operator_can_continue_expr: true,
             ..self
         }
     }
@@ -148,19 +169,34 @@ impl FixupContext {
             leftmost_subexpression_in_stmt: false,
             match_arm: self.match_arm || self.leftmost_subexpression_in_match_arm,
             leftmost_subexpression_in_match_arm: false,
+            next_operator_can_begin_expr: false,
+            next_operator_can_continue_expr: true,
             ..self
         }
     }
 
-    /// Transform this fixup into the one that should apply when printing any
-    /// subexpression that is neither a leftmost subexpression nor surrounded in
-    /// delimiters.
+    /// Transform this fixup into the one that should apply when printing a
+    /// leftmost subexpression followed by punctuation that is legal as the
+    /// first token of an expression.
+    pub(crate) fn leftmost_subexpression_with_operator(
+        self,
+        next_operator_can_begin_expr: bool,
+    ) -> Self {
+        FixupContext { next_operator_can_begin_expr, ..self.leftmost_subexpression() }
+    }
+
+    /// Transform this fixup into the one that should apply when printing the
+    /// rightmost subexpression of the current expression.
+    ///
+    /// The rightmost subexpression is any subexpression that has a different
+    /// first token than the current expression, but has the same last token.
+    ///
+    /// For example in `$a + $b` and `-$b`, the subexpression `$b` is a
+    /// rightmost subexpression.
     ///
-    /// This is for any subexpression that has a different first token than the
-    /// current expression, and is not surrounded by a paren/bracket/brace. For
-    /// example the `$b` in `$a + $b` and `-$b`, but not the one in `[$b]` or
-    /// `$a.f($b)`.
-    pub(crate) fn subsequent_subexpression(self) -> Self {
+    /// Not every expression has a rightmost subexpression. For example neither
+    /// `[$b]` nor `$a.f($b)` have one.
+    pub(crate) fn rightmost_subexpression(self) -> Self {
         FixupContext {
             stmt: false,
             leftmost_subexpression_in_stmt: false,
@@ -193,6 +229,39 @@ impl FixupContext {
     ///     "let chain".
     pub(crate) fn needs_par_as_let_scrutinee(self, expr: &Expr) -> bool {
         self.parenthesize_exterior_struct_lit && parser::contains_exterior_struct_lit(expr)
-            || parser::needs_par_as_let_scrutinee(expr.precedence())
+            || parser::needs_par_as_let_scrutinee(self.precedence(expr))
+    }
+
+    /// Determines the effective precedence of a subexpression. Some expressions
+    /// have higher or lower precedence when adjacent to particular operators.
+    pub(crate) fn precedence(self, expr: &Expr) -> ExprPrecedence {
+        if self.next_operator_can_begin_expr {
+            // Decrease precedence of value-less jumps when followed by an
+            // operator that would otherwise get interpreted as beginning a
+            // value for the jump.
+            if let ExprKind::Break(..)
+            | ExprKind::Ret(..)
+            | ExprKind::Yeet(..)
+            | ExprKind::Yield(YieldKind::Prefix(..)) = expr.kind
+            {
+                return ExprPrecedence::Jump;
+            }
+        }
+
+        if !self.next_operator_can_continue_expr {
+            // Increase precedence of expressions that extend to the end of
+            // current statement or group.
+            if let ExprKind::Break(..)
+            | ExprKind::Closure(..)
+            | ExprKind::Ret(..)
+            | ExprKind::Yeet(..)
+            | ExprKind::Yield(YieldKind::Prefix(..))
+            | ExprKind::Range(None, ..) = expr.kind
+            {
+                return ExprPrecedence::Prefix;
+            }
+        }
+
+        expr.precedence()
     }
 }
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs
index 3638eb31c61..6c442553976 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs
@@ -28,7 +28,7 @@ impl<'a> State<'a> {
         }
     }
 
-    fn print_foreign_item(&mut self, item: &ast::ForeignItem) {
+    pub(crate) fn print_foreign_item(&mut self, item: &ast::ForeignItem) {
         let ast::Item { id, span, ref attrs, ref kind, ref vis, tokens: _ } = *item;
         self.ann.pre(self, AnnNode::SubItem(id));
         self.hardbreak_if_not_bol();
@@ -548,7 +548,7 @@ impl<'a> State<'a> {
         }
     }
 
-    fn print_assoc_item(&mut self, item: &ast::AssocItem) {
+    pub(crate) fn print_assoc_item(&mut self, item: &ast::AssocItem) {
         let ast::Item { id, span, ref attrs, ref kind, ref vis, tokens: _ } = *item;
         self.ann.pre(self, AnnNode::SubItem(id));
         self.hardbreak_if_not_bol();
diff --git a/compiler/rustc_attr_data_structures/src/lib.rs b/compiler/rustc_attr_data_structures/src/lib.rs
index dbfc95b047a..b0fc19d1cd7 100644
--- a/compiler/rustc_attr_data_structures/src/lib.rs
+++ b/compiler/rustc_attr_data_structures/src/lib.rs
@@ -8,6 +8,8 @@ mod attributes;
 mod stability;
 mod version;
 
+pub mod lints;
+
 use std::num::NonZero;
 
 pub use attributes::*;
diff --git a/compiler/rustc_attr_data_structures/src/lints.rs b/compiler/rustc_attr_data_structures/src/lints.rs
new file mode 100644
index 00000000000..7e3664b2263
--- /dev/null
+++ b/compiler/rustc_attr_data_structures/src/lints.rs
@@ -0,0 +1,14 @@
+use rustc_macros::HashStable_Generic;
+use rustc_span::Span;
+
+#[derive(Clone, Debug, HashStable_Generic)]
+pub struct AttributeLint<Id> {
+    pub id: Id,
+    pub span: Span,
+    pub kind: AttributeLintKind,
+}
+
+#[derive(Clone, Debug, HashStable_Generic)]
+pub enum AttributeLintKind {
+    UnusedDuplicate { this: Span, other: Span, warning: bool },
+}
diff --git a/compiler/rustc_attr_data_structures/src/stability.rs b/compiler/rustc_attr_data_structures/src/stability.rs
index c0ca08a60f8..218e771c745 100644
--- a/compiler/rustc_attr_data_structures/src/stability.rs
+++ b/compiler/rustc_attr_data_structures/src/stability.rs
@@ -132,6 +132,7 @@ pub enum StabilityLevel {
         /// fn foobar() {}
         /// ```
         implied_by: Option<Symbol>,
+        old_name: Option<Symbol>,
     },
     /// `#[stable]`
     Stable {
diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl
index 45174c9582d..c9443feb021 100644
--- a/compiler/rustc_attr_parsing/messages.ftl
+++ b/compiler/rustc_attr_parsing/messages.ftl
@@ -131,7 +131,15 @@ attr_parsing_unsupported_literal_generic =
 attr_parsing_unsupported_literal_suggestion =
     consider removing the prefix
 
+attr_parsing_unused_duplicate =
+    unused attribute
+    .suggestion = remove this attribute
+    .note = attribute also specified here
+    .warn = {-passes_previously_accepted}
 attr_parsing_unused_multiple =
     multiple `{$name}` attributes
     .suggestion = remove this attribute
     .note = attribute also specified here
+
+-attr_parsing_perviously_accepted =
+    this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
diff --git a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs
index d0465546b73..81192f902a2 100644
--- a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs
@@ -4,41 +4,43 @@ use rustc_attr_data_structures::AttributeKind;
 use rustc_span::{Span, Symbol, sym};
 
 use super::{CombineAttributeParser, ConvertFn};
-use crate::context::AcceptContext;
+use crate::context::{AcceptContext, Stage};
 use crate::parser::ArgParser;
 use crate::session_diagnostics;
 
 pub(crate) struct AllowInternalUnstableParser;
-impl CombineAttributeParser for AllowInternalUnstableParser {
-    const PATH: &'static [Symbol] = &[sym::allow_internal_unstable];
+impl<S: Stage> CombineAttributeParser<S> for AllowInternalUnstableParser {
+    const PATH: &[Symbol] = &[sym::allow_internal_unstable];
     type Item = (Symbol, Span);
     const CONVERT: ConvertFn<Self::Item> = AttributeKind::AllowInternalUnstable;
 
-    fn extend<'a>(
-        cx: &'a AcceptContext<'a>,
-        args: &'a ArgParser<'a>,
-    ) -> impl IntoIterator<Item = Self::Item> + 'a {
-        parse_unstable(cx, args, Self::PATH[0]).into_iter().zip(iter::repeat(cx.attr_span))
+    fn extend<'c>(
+        cx: &'c mut AcceptContext<'_, '_, S>,
+        args: &'c ArgParser<'_>,
+    ) -> impl IntoIterator<Item = Self::Item> {
+        parse_unstable(cx, args, <Self as CombineAttributeParser<S>>::PATH[0])
+            .into_iter()
+            .zip(iter::repeat(cx.attr_span))
     }
 }
 
 pub(crate) struct AllowConstFnUnstableParser;
-impl CombineAttributeParser for AllowConstFnUnstableParser {
-    const PATH: &'static [Symbol] = &[sym::rustc_allow_const_fn_unstable];
+impl<S: Stage> CombineAttributeParser<S> for AllowConstFnUnstableParser {
+    const PATH: &[Symbol] = &[sym::rustc_allow_const_fn_unstable];
     type Item = Symbol;
     const CONVERT: ConvertFn<Self::Item> = AttributeKind::AllowConstFnUnstable;
 
-    fn extend<'a>(
-        cx: &'a AcceptContext<'a>,
-        args: &'a ArgParser<'a>,
-    ) -> impl IntoIterator<Item = Self::Item> + 'a {
-        parse_unstable(cx, args, Self::PATH[0])
+    fn extend<'c>(
+        cx: &'c mut AcceptContext<'_, '_, S>,
+        args: &'c ArgParser<'_>,
+    ) -> impl IntoIterator<Item = Self::Item> + 'c {
+        parse_unstable(cx, args, <Self as CombineAttributeParser<S>>::PATH[0])
     }
 }
 
-fn parse_unstable<'a>(
-    cx: &AcceptContext<'_>,
-    args: &'a ArgParser<'a>,
+fn parse_unstable<S: Stage>(
+    cx: &AcceptContext<'_, '_, S>,
+    args: &ArgParser<'_>,
     symbol: Symbol,
 ) -> impl IntoIterator<Item = Symbol> {
     let mut res = Vec::new();
diff --git a/compiler/rustc_attr_parsing/src/attributes/confusables.rs b/compiler/rustc_attr_parsing/src/attributes/confusables.rs
index 6cff952fcf2..afd3c012f05 100644
--- a/compiler/rustc_attr_parsing/src/attributes/confusables.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/confusables.rs
@@ -3,7 +3,7 @@ use rustc_span::{Span, Symbol, sym};
 use thin_vec::ThinVec;
 
 use super::{AcceptMapping, AttributeParser};
-use crate::context::FinalizeContext;
+use crate::context::{FinalizeContext, Stage};
 use crate::session_diagnostics;
 
 #[derive(Default)]
@@ -12,8 +12,8 @@ pub(crate) struct ConfusablesParser {
     first_span: Option<Span>,
 }
 
-impl AttributeParser for ConfusablesParser {
-    const ATTRIBUTES: AcceptMapping<Self> = &[(&[sym::rustc_confusables], |this, cx, args| {
+impl<S: Stage> AttributeParser<S> for ConfusablesParser {
+    const ATTRIBUTES: AcceptMapping<Self, S> = &[(&[sym::rustc_confusables], |this, cx, args| {
         let Some(list) = args.list() else {
             // FIXME(jdonszelmann): error when not a list? Bring validation code here.
             //       NOTE: currently subsequent attributes are silently ignored using
@@ -45,7 +45,7 @@ impl AttributeParser for ConfusablesParser {
         this.first_span.get_or_insert(cx.attr_span);
     })];
 
-    fn finalize(self, _cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
+    fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
         if self.confusables.is_empty() {
             return None;
         }
diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs
index 006c1fe3b9c..1faee41c2a9 100644
--- a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs
@@ -1,17 +1,17 @@
 use rustc_attr_data_structures::{AttributeKind, DeprecatedSince, Deprecation};
 use rustc_span::{Span, Symbol, sym};
 
-use super::SingleAttributeParser;
 use super::util::parse_version;
-use crate::context::AcceptContext;
+use super::{AttributeOrder, OnDuplicate, SingleAttributeParser};
+use crate::context::{AcceptContext, Stage};
 use crate::parser::ArgParser;
 use crate::session_diagnostics;
 use crate::session_diagnostics::UnsupportedLiteralReason;
 
 pub(crate) struct DeprecationParser;
 
-fn get(
-    cx: &AcceptContext<'_>,
+fn get<S: Stage>(
+    cx: &AcceptContext<'_, '_, S>,
     name: Symbol,
     param_span: Span,
     arg: &ArgParser<'_>,
@@ -41,19 +41,12 @@ fn get(
     }
 }
 
-impl SingleAttributeParser for DeprecationParser {
-    const PATH: &'static [Symbol] = &[sym::deprecated];
+impl<S: Stage> SingleAttributeParser<S> for DeprecationParser {
+    const PATH: &[Symbol] = &[sym::deprecated];
+    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
+    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
 
-    fn on_duplicate(cx: &AcceptContext<'_>, first_span: Span) {
-        // FIXME(jdonszelmann): merge with errors from check_attrs.rs
-        cx.emit_err(session_diagnostics::UnusedMultiple {
-            this: cx.attr_span,
-            other: first_span,
-            name: sym::deprecated,
-        });
-    }
-
-    fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option<AttributeKind> {
+    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
         let features = cx.features();
 
         let mut since = None;
diff --git a/compiler/rustc_attr_parsing/src/attributes/inline.rs b/compiler/rustc_attr_parsing/src/attributes/inline.rs
new file mode 100644
index 00000000000..c7f82082c2e
--- /dev/null
+++ b/compiler/rustc_attr_parsing/src/attributes/inline.rs
@@ -0,0 +1,97 @@
+// FIXME(jdonszelmann): merge these two parsers and error when both attributes are present here.
+//                      note: need to model better how duplicate attr errors work when not using
+//                      SingleAttributeParser which is what we have two of here.
+
+use rustc_attr_data_structures::lints::AttributeLintKind;
+use rustc_attr_data_structures::{AttributeKind, InlineAttr};
+use rustc_feature::{AttributeTemplate, template};
+use rustc_span::{Symbol, sym};
+
+use super::{AcceptContext, AttributeOrder, OnDuplicate};
+use crate::attributes::SingleAttributeParser;
+use crate::context::Stage;
+use crate::parser::ArgParser;
+
+pub(crate) struct InlineParser;
+
+impl<S: Stage> SingleAttributeParser<S> for InlineParser {
+    const PATH: &'static [Symbol] = &[sym::inline];
+    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast;
+    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
+    const TEMPLATE: AttributeTemplate = template!(Word, List: "always|never");
+
+    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
+        match args {
+            ArgParser::NoArgs => Some(AttributeKind::Inline(InlineAttr::Hint, cx.attr_span)),
+            ArgParser::List(list) => {
+                let Some(l) = list.single() else {
+                    cx.expected_single_argument(list.span);
+                    return None;
+                };
+
+                match l.meta_item().and_then(|i| i.word_without_args().map(|i| i.name)) {
+                    Some(sym::always) => {
+                        Some(AttributeKind::Inline(InlineAttr::Always, cx.attr_span))
+                    }
+                    Some(sym::never) => {
+                        Some(AttributeKind::Inline(InlineAttr::Never, cx.attr_span))
+                    }
+                    _ => {
+                        cx.expected_specific_argument(l.span(), vec!["always", "never"]);
+                        return None;
+                    }
+                }
+            }
+            ArgParser::NameValue(_) => {
+                let suggestions =
+                    <Self as SingleAttributeParser<S>>::TEMPLATE.suggestions(false, "inline");
+                cx.emit_lint(
+                    AttributeLintKind::IllFormedAttributeInput { suggestions },
+                    cx.attr_span,
+                );
+                return None;
+            }
+        }
+    }
+}
+
+pub(crate) struct RustcForceInlineParser;
+
+impl<S: Stage> SingleAttributeParser<S> for RustcForceInlineParser {
+    const PATH: &'static [Symbol] = &[sym::rustc_force_inline];
+    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast;
+    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
+    const TEMPLATE: AttributeTemplate = template!(Word, List: "reason", NameValueStr: "reason");
+
+    fn convert(cx: &AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
+        let reason = match args {
+            ArgParser::NoArgs => None,
+            ArgParser::List(list) => {
+                let Some(l) = list.single() else {
+                    cx.expected_single_argument(list.span);
+                    return None;
+                };
+
+                let Some(reason) = l.lit().and_then(|i| i.kind.str()) else {
+                    cx.expected_string_literal(l.span());
+                    return None;
+                };
+
+                Some(reason)
+            }
+            ArgParser::NameValue(v) => {
+                let Some(reason) = v.value_as_str() else {
+                    cx.expected_string_literal(v.value_span);
+                    return None;
+                };
+
+                Some(reason)
+            }
+        };
+
+        Some(AttributeKind::Inline(
+            InlineAttr::Force { attr_span: cx.attr_span, reason },
+            cx.attr_span,
+        ))
+    }
+}
diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs
index 7cceca3c24d..caf55e6685e 100644
--- a/compiler/rustc_attr_parsing/src/attributes/mod.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs
@@ -12,16 +12,18 @@
 //! - [`CombineAttributeParser`]: makes it easy to implement an attribute which should combine the
 //! contents of attributes, if an attribute appear multiple times in a list
 //!
-//! Attributes should be added to [`ATTRIBUTE_PARSERS`](crate::context::ATTRIBUTE_PARSERS) to be parsed.
+//! Attributes should be added to `crate::context::ATTRIBUTE_PARSERS` to be parsed.
 
 use std::marker::PhantomData;
 
 use rustc_attr_data_structures::AttributeKind;
+use rustc_attr_data_structures::lints::AttributeLintKind;
 use rustc_span::{Span, Symbol};
 use thin_vec::ThinVec;
 
-use crate::context::{AcceptContext, FinalizeContext};
+use crate::context::{AcceptContext, FinalizeContext, Stage};
 use crate::parser::ArgParser;
+use crate::session_diagnostics::UnusedMultiple;
 
 pub(crate) mod allow_unstable;
 pub(crate) mod cfg;
@@ -32,8 +34,8 @@ pub(crate) mod stability;
 pub(crate) mod transparency;
 pub(crate) mod util;
 
-type AcceptFn<T> = fn(&mut T, &AcceptContext<'_>, &ArgParser<'_>);
-type AcceptMapping<T> = &'static [(&'static [Symbol], AcceptFn<T>)];
+type AcceptFn<T, S> = for<'sess> fn(&mut T, &mut AcceptContext<'_, 'sess, S>, &ArgParser<'_>);
+type AcceptMapping<T, S> = &'static [(&'static [Symbol], AcceptFn<T, S>)];
 
 /// An [`AttributeParser`] is a type which searches for syntactic attributes.
 ///
@@ -54,11 +56,11 @@ type AcceptMapping<T> = &'static [(&'static [Symbol], AcceptFn<T>)];
 ///
 /// For a simpler attribute parsing interface, consider using [`SingleAttributeParser`]
 /// or [`CombineAttributeParser`] instead.
-pub(crate) trait AttributeParser: Default + 'static {
+pub(crate) trait AttributeParser<S: Stage>: Default + 'static {
     /// The symbols for the attributes that this parser is interested in.
     ///
     /// If an attribute has this symbol, the `accept` function will be called on it.
-    const ATTRIBUTES: AcceptMapping<Self>;
+    const ATTRIBUTES: AcceptMapping<Self, S>;
 
     /// The parser has gotten a chance to accept the attributes on an item,
     /// here it can produce an attribute.
@@ -68,7 +70,7 @@ pub(crate) trait AttributeParser: Default + 'static {
     /// that'd be equivalent to unconditionally applying an attribute to
     /// every single syntax item that could have attributes applied to it.
     /// Your accept mappings should determine whether this returns something.
-    fn finalize(self, cx: &FinalizeContext<'_>) -> Option<AttributeKind>;
+    fn finalize(self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind>;
 }
 
 /// Alternative to [`AttributeParser`] that automatically handles state management.
@@ -80,43 +82,130 @@ pub(crate) trait AttributeParser: Default + 'static {
 ///
 /// [`SingleAttributeParser`] can only convert attributes one-to-one, and cannot combine multiple
 /// attributes together like is necessary for `#[stable()]` and `#[unstable()]` for example.
-pub(crate) trait SingleAttributeParser: 'static {
-    const PATH: &'static [Symbol];
-
-    /// Called when a duplicate attribute is found.
-    ///
-    /// `first_span` is the span of the first occurrence of this attribute.
-    // FIXME(jdonszelmann): default error
-    fn on_duplicate(cx: &AcceptContext<'_>, first_span: Span);
+pub(crate) trait SingleAttributeParser<S: Stage>: 'static {
+    const PATH: &[Symbol];
+    const ATTRIBUTE_ORDER: AttributeOrder;
+    const ON_DUPLICATE: OnDuplicate<S>;
 
     /// Converts a single syntactical attribute to a single semantic attribute, or [`AttributeKind`]
-    fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option<AttributeKind>;
+    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind>;
 }
 
-pub(crate) struct Single<T: SingleAttributeParser>(PhantomData<T>, Option<(AttributeKind, Span)>);
+pub(crate) struct Single<T: SingleAttributeParser<S>, S: Stage>(
+    PhantomData<(S, T)>,
+    Option<(AttributeKind, Span)>,
+);
 
-impl<T: SingleAttributeParser> Default for Single<T> {
+impl<T: SingleAttributeParser<S>, S: Stage> Default for Single<T, S> {
     fn default() -> Self {
         Self(Default::default(), Default::default())
     }
 }
 
-impl<T: SingleAttributeParser> AttributeParser for Single<T> {
-    const ATTRIBUTES: AcceptMapping<Self> = &[(T::PATH, |group: &mut Single<T>, cx, args| {
-        if let Some((_, s)) = group.1 {
-            T::on_duplicate(cx, s);
-            return;
-        }
+impl<T: SingleAttributeParser<S>, S: Stage> AttributeParser<S> for Single<T, S> {
+    const ATTRIBUTES: AcceptMapping<Self, S> =
+        &[(T::PATH, |group: &mut Single<T, S>, cx, args| {
+            if let Some(pa) = T::convert(cx, args) {
+                match T::ATTRIBUTE_ORDER {
+                    // keep the first and report immediately. ignore this attribute
+                    AttributeOrder::KeepFirst => {
+                        if let Some((_, unused)) = group.1 {
+                            T::ON_DUPLICATE.exec::<T>(cx, cx.attr_span, unused);
+                            return;
+                        }
+                    }
+                    // keep the new one and warn about the previous,
+                    // then replace
+                    AttributeOrder::KeepLast => {
+                        if let Some((_, used)) = group.1 {
+                            T::ON_DUPLICATE.exec::<T>(cx, used, cx.attr_span);
+                        }
+                    }
+                }
+
+                group.1 = Some((pa, cx.attr_span));
+            }
+        })];
+
+    fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
+        Some(self.1?.0)
+    }
+}
 
-        if let Some(pa) = T::convert(cx, args) {
-            group.1 = Some((pa, cx.attr_span));
-        }
-    })];
+// FIXME(jdonszelmann): logic is implemented but the attribute parsers needing
+// them will be merged in another PR
+#[allow(unused)]
+pub(crate) enum OnDuplicate<S: Stage> {
+    /// Give a default warning
+    Warn,
 
-    fn finalize(self, _cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
-        Some(self.1?.0)
+    /// Duplicates will be a warning, with a note that this will be an error in the future.
+    WarnButFutureError,
+
+    /// Give a default error
+    Error,
+
+    /// Ignore duplicates
+    Ignore,
+
+    /// Custom function called when a duplicate attribute is found.
+    ///
+    /// - `unused` is the span of the attribute that was unused or bad because of some
+    ///   duplicate reason (see [`AttributeOrder`])
+    /// - `used` is the span of the attribute that was used in favor of the unused attribute
+    Custom(fn(cx: &AcceptContext<'_, '_, S>, used: Span, unused: Span)),
+}
+
+impl<S: Stage> OnDuplicate<S> {
+    fn exec<P: SingleAttributeParser<S>>(
+        &self,
+        cx: &mut AcceptContext<'_, '_, S>,
+        used: Span,
+        unused: Span,
+    ) {
+        match self {
+            OnDuplicate::Warn => cx.emit_lint(
+                AttributeLintKind::UnusedDuplicate { this: unused, other: used, warning: false },
+                unused,
+            ),
+            OnDuplicate::WarnButFutureError => cx.emit_lint(
+                AttributeLintKind::UnusedDuplicate { this: unused, other: used, warning: true },
+                unused,
+            ),
+            OnDuplicate::Error => {
+                cx.emit_err(UnusedMultiple {
+                    this: used,
+                    other: unused,
+                    name: Symbol::intern(
+                        &P::PATH.into_iter().map(|i| i.to_string()).collect::<Vec<_>>().join(".."),
+                    ),
+                });
+            }
+            OnDuplicate::Ignore => {}
+            OnDuplicate::Custom(f) => f(cx, used, unused),
+        }
     }
 }
+//
+// FIXME(jdonszelmann): logic is implemented but the attribute parsers needing
+// them will be merged in another PR
+#[allow(unused)]
+pub(crate) enum AttributeOrder {
+    /// Duplicates after the first attribute will be an error.
+    ///
+    /// This should be used where duplicates would be ignored, but carry extra
+    /// meaning that could cause confusion. For example, `#[stable(since="1.0")]
+    /// #[stable(since="2.0")]`, which version should be used for `stable`?
+    KeepFirst,
+
+    /// Duplicates preceding the last instance of the attribute will be a
+    /// warning, with a note that this will be an error in the future.
+    ///
+    /// This is the same as `FutureWarnFollowing`, except the last attribute is
+    /// the one that is "used". Ideally these can eventually migrate to
+    /// `ErrorPreceding`.
+    KeepLast,
+}
 
 type ConvertFn<E> = fn(ThinVec<E>) -> AttributeKind;
 
@@ -127,35 +216,35 @@ type ConvertFn<E> = fn(ThinVec<E>) -> AttributeKind;
 ///
 /// [`CombineAttributeParser`] can only convert a single kind of attribute, and cannot combine multiple
 /// attributes together like is necessary for `#[stable()]` and `#[unstable()]` for example.
-pub(crate) trait CombineAttributeParser: 'static {
-    const PATH: &'static [Symbol];
+pub(crate) trait CombineAttributeParser<S: Stage>: 'static {
+    const PATH: &[rustc_span::Symbol];
 
     type Item;
     const CONVERT: ConvertFn<Self::Item>;
 
     /// Converts a single syntactical attribute to a number of elements of the semantic attribute, or [`AttributeKind`]
-    fn extend<'a>(
-        cx: &'a AcceptContext<'a>,
-        args: &'a ArgParser<'a>,
-    ) -> impl IntoIterator<Item = Self::Item> + 'a;
+    fn extend<'c>(
+        cx: &'c mut AcceptContext<'_, '_, S>,
+        args: &'c ArgParser<'_>,
+    ) -> impl IntoIterator<Item = Self::Item> + 'c;
 }
 
-pub(crate) struct Combine<T: CombineAttributeParser>(
-    PhantomData<T>,
-    ThinVec<<T as CombineAttributeParser>::Item>,
+pub(crate) struct Combine<T: CombineAttributeParser<S>, S: Stage>(
+    PhantomData<(S, T)>,
+    ThinVec<<T as CombineAttributeParser<S>>::Item>,
 );
 
-impl<T: CombineAttributeParser> Default for Combine<T> {
+impl<T: CombineAttributeParser<S>, S: Stage> Default for Combine<T, S> {
     fn default() -> Self {
         Self(Default::default(), Default::default())
     }
 }
 
-impl<T: CombineAttributeParser> AttributeParser for Combine<T> {
-    const ATTRIBUTES: AcceptMapping<Self> =
-        &[(T::PATH, |group: &mut Combine<T>, cx, args| group.1.extend(T::extend(cx, args)))];
+impl<T: CombineAttributeParser<S>, S: Stage> AttributeParser<S> for Combine<T, S> {
+    const ATTRIBUTES: AcceptMapping<Self, S> =
+        &[(T::PATH, |group: &mut Combine<T, S>, cx, args| group.1.extend(T::extend(cx, args)))];
 
-    fn finalize(self, _cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
+    fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
         if self.1.is_empty() { None } else { Some(T::CONVERT(self.1)) }
     }
 }
diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs
index 69316541e19..753b2366b41 100644
--- a/compiler/rustc_attr_parsing/src/attributes/repr.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs
@@ -4,7 +4,7 @@ use rustc_attr_data_structures::{AttributeKind, IntType, ReprAttr};
 use rustc_span::{DUMMY_SP, Span, Symbol, sym};
 
 use super::{CombineAttributeParser, ConvertFn};
-use crate::context::AcceptContext;
+use crate::context::{AcceptContext, Stage};
 use crate::parser::{ArgParser, MetaItemListParser, MetaItemParser};
 use crate::session_diagnostics;
 use crate::session_diagnostics::IncorrectReprFormatGenericCause;
@@ -19,15 +19,15 @@ use crate::session_diagnostics::IncorrectReprFormatGenericCause;
 // FIXME(jdonszelmann): is a vec the right representation here even? isn't it just a struct?
 pub(crate) struct ReprParser;
 
-impl CombineAttributeParser for ReprParser {
+impl<S: Stage> CombineAttributeParser<S> for ReprParser {
     type Item = (ReprAttr, Span);
-    const PATH: &'static [Symbol] = &[sym::repr];
+    const PATH: &[Symbol] = &[sym::repr];
     const CONVERT: ConvertFn<Self::Item> = AttributeKind::Repr;
 
-    fn extend<'a>(
-        cx: &'a AcceptContext<'a>,
-        args: &'a ArgParser<'a>,
-    ) -> impl IntoIterator<Item = Self::Item> + 'a {
+    fn extend<'c>(
+        cx: &'c mut AcceptContext<'_, '_, S>,
+        args: &'c ArgParser<'_>,
+    ) -> impl IntoIterator<Item = Self::Item> + 'c {
         let mut reprs = Vec::new();
 
         let Some(list) = args.list() else {
@@ -91,7 +91,10 @@ fn int_type_of_word(s: Symbol) -> Option<IntType> {
     }
 }
 
-fn parse_repr(cx: &AcceptContext<'_>, param: &MetaItemParser<'_>) -> Option<ReprAttr> {
+fn parse_repr<S: Stage>(
+    cx: &AcceptContext<'_, '_, S>,
+    param: &MetaItemParser<'_>,
+) -> Option<ReprAttr> {
     use ReprAttr::*;
 
     // FIXME(jdonszelmann): invert the parsing here to match on the word first and then the
@@ -180,8 +183,8 @@ enum AlignKind {
     Align,
 }
 
-fn parse_repr_align(
-    cx: &AcceptContext<'_>,
+fn parse_repr_align<S: Stage>(
+    cx: &AcceptContext<'_, '_, S>,
     list: &MetaItemListParser<'_>,
     param_span: Span,
     align_kind: AlignKind,
diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs
index ce69a54513d..6589a51db2b 100644
--- a/compiler/rustc_attr_parsing/src/attributes/stability.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs
@@ -8,8 +8,8 @@ use rustc_errors::ErrorGuaranteed;
 use rustc_span::{Span, Symbol, sym};
 
 use super::util::parse_version;
-use super::{AcceptMapping, AttributeParser, SingleAttributeParser};
-use crate::context::{AcceptContext, FinalizeContext};
+use super::{AcceptMapping, AttributeOrder, AttributeParser, OnDuplicate, SingleAttributeParser};
+use crate::context::{AcceptContext, FinalizeContext, Stage};
 use crate::parser::{ArgParser, MetaItemParser};
 use crate::session_diagnostics::{self, UnsupportedLiteralReason};
 
@@ -31,7 +31,7 @@ pub(crate) struct StabilityParser {
 
 impl StabilityParser {
     /// Checks, and emits an error when a stability (or unstability) was already set, which would be a duplicate.
-    fn check_duplicate(&self, cx: &AcceptContext<'_>) -> bool {
+    fn check_duplicate<S: Stage>(&self, cx: &AcceptContext<'_, '_, S>) -> bool {
         if let Some((_, _)) = self.stability {
             cx.emit_err(session_diagnostics::MultipleStabilityLevels { span: cx.attr_span });
             true
@@ -41,8 +41,8 @@ impl StabilityParser {
     }
 }
 
-impl AttributeParser for StabilityParser {
-    const ATTRIBUTES: AcceptMapping<Self> = &[
+impl<S: Stage> AttributeParser<S> for StabilityParser {
+    const ATTRIBUTES: AcceptMapping<Self, S> = &[
         (&[sym::stable], |this, cx, args| {
             reject_outside_std!(cx);
             if !this.check_duplicate(cx)
@@ -65,7 +65,7 @@ impl AttributeParser for StabilityParser {
         }),
     ];
 
-    fn finalize(mut self, cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
+    fn finalize(mut self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
         if let Some(atum) = self.allowed_through_unstable_modules {
             if let Some((
                 Stability {
@@ -95,8 +95,8 @@ pub(crate) struct BodyStabilityParser {
     stability: Option<(DefaultBodyStability, Span)>,
 }
 
-impl AttributeParser for BodyStabilityParser {
-    const ATTRIBUTES: AcceptMapping<Self> =
+impl<S: Stage> AttributeParser<S> for BodyStabilityParser {
+    const ATTRIBUTES: AcceptMapping<Self, S> =
         &[(&[sym::rustc_default_body_unstable], |this, cx, args| {
             reject_outside_std!(cx);
             if this.stability.is_some() {
@@ -107,7 +107,7 @@ impl AttributeParser for BodyStabilityParser {
             }
         })];
 
-    fn finalize(self, _cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
+    fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
         let (stability, span) = self.stability?;
 
         Some(AttributeKind::BodyStability { stability, span })
@@ -116,13 +116,12 @@ impl AttributeParser for BodyStabilityParser {
 
 pub(crate) struct ConstStabilityIndirectParser;
 // FIXME(jdonszelmann): single word attribute group when we have these
-impl SingleAttributeParser for ConstStabilityIndirectParser {
-    const PATH: &'static [Symbol] = &[sym::rustc_const_stable_indirect];
+impl<S: Stage> SingleAttributeParser<S> for ConstStabilityIndirectParser {
+    const PATH: &[Symbol] = &[sym::rustc_const_stable_indirect];
+    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
+    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Ignore;
 
-    // ignore
-    fn on_duplicate(_cx: &AcceptContext<'_>, _first_span: Span) {}
-
-    fn convert(_cx: &AcceptContext<'_>, _args: &ArgParser<'_>) -> Option<AttributeKind> {
+    fn convert(_cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option<AttributeKind> {
         Some(AttributeKind::ConstStabilityIndirect)
     }
 }
@@ -135,7 +134,7 @@ pub(crate) struct ConstStabilityParser {
 
 impl ConstStabilityParser {
     /// Checks, and emits an error when a stability (or unstability) was already set, which would be a duplicate.
-    fn check_duplicate(&self, cx: &AcceptContext<'_>) -> bool {
+    fn check_duplicate<S: Stage>(&self, cx: &AcceptContext<'_, '_, S>) -> bool {
         if let Some((_, _)) = self.stability {
             cx.emit_err(session_diagnostics::MultipleStabilityLevels { span: cx.attr_span });
             true
@@ -145,8 +144,8 @@ impl ConstStabilityParser {
     }
 }
 
-impl AttributeParser for ConstStabilityParser {
-    const ATTRIBUTES: AcceptMapping<Self> = &[
+impl<S: Stage> AttributeParser<S> for ConstStabilityParser {
+    const ATTRIBUTES: AcceptMapping<Self, S> = &[
         (&[sym::rustc_const_stable], |this, cx, args| {
             reject_outside_std!(cx);
 
@@ -176,7 +175,7 @@ impl AttributeParser for ConstStabilityParser {
         }),
     ];
 
-    fn finalize(mut self, cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
+    fn finalize(mut self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
         if self.promotable {
             if let Some((ref mut stab, _)) = self.stability {
                 stab.promotable = true;
@@ -196,8 +195,8 @@ impl AttributeParser for ConstStabilityParser {
 ///
 /// Emits an error when either the option was already Some, or the arguments weren't of form
 /// `name = value`
-fn insert_value_into_option_or_error(
-    cx: &AcceptContext<'_>,
+fn insert_value_into_option_or_error<S: Stage>(
+    cx: &AcceptContext<'_, '_, S>,
     param: &MetaItemParser<'_>,
     item: &mut Option<Symbol>,
 ) -> Option<()> {
@@ -223,8 +222,8 @@ fn insert_value_into_option_or_error(
 
 /// Read the content of a `stable`/`rustc_const_stable` attribute, and return the feature name and
 /// its stability information.
-pub(crate) fn parse_stability(
-    cx: &AcceptContext<'_>,
+pub(crate) fn parse_stability<S: Stage>(
+    cx: &AcceptContext<'_, '_, S>,
     args: &ArgParser<'_>,
 ) -> Option<(Symbol, StabilityLevel)> {
     let mut feature = None;
@@ -289,8 +288,8 @@ pub(crate) fn parse_stability(
 
 // Read the content of a `unstable`/`rustc_const_unstable`/`rustc_default_body_unstable`
 /// attribute, and return the feature name and its stability information.
-pub(crate) fn parse_unstability(
-    cx: &AcceptContext<'_>,
+pub(crate) fn parse_unstability<S: Stage>(
+    cx: &AcceptContext<'_, '_, S>,
     args: &ArgParser<'_>,
 ) -> Option<(Symbol, StabilityLevel)> {
     let mut feature = None;
@@ -299,6 +298,7 @@ pub(crate) fn parse_unstability(
     let mut issue_num = None;
     let mut is_soft = false;
     let mut implied_by = None;
+    let mut old_name = None;
     for param in args.list()?.mixed() {
         let Some(param) = param.meta_item() else {
             cx.emit_err(session_diagnostics::UnsupportedLiteral {
@@ -346,11 +346,12 @@ pub(crate) fn parse_unstability(
             Some(sym::implied_by) => {
                 insert_value_into_option_or_error(cx, &param, &mut implied_by)?
             }
+            Some(sym::old_name) => insert_value_into_option_or_error(cx, &param, &mut old_name)?,
             _ => {
                 cx.emit_err(session_diagnostics::UnknownMetaItem {
                     span: param.span(),
                     item: param.path().to_string(),
-                    expected: &["feature", "reason", "issue", "soft", "implied_by"],
+                    expected: &["feature", "reason", "issue", "soft", "implied_by", "old_name"],
                 });
                 return None;
             }
@@ -375,6 +376,7 @@ pub(crate) fn parse_unstability(
                 issue: issue_num,
                 is_soft,
                 implied_by,
+                old_name,
             };
             Some((feature, level))
         }
diff --git a/compiler/rustc_attr_parsing/src/attributes/transparency.rs b/compiler/rustc_attr_parsing/src/attributes/transparency.rs
index d229fc09740..16ad9d03e50 100644
--- a/compiler/rustc_attr_parsing/src/attributes/transparency.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/transparency.rs
@@ -1,8 +1,9 @@
 use rustc_attr_data_structures::AttributeKind;
 use rustc_span::hygiene::Transparency;
-use rustc_span::{Span, Symbol, sym};
+use rustc_span::{Symbol, sym};
 
-use super::{AcceptContext, SingleAttributeParser};
+use super::{AttributeOrder, OnDuplicate, SingleAttributeParser};
+use crate::context::{AcceptContext, Stage};
 use crate::parser::ArgParser;
 
 pub(crate) struct TransparencyParser;
@@ -10,14 +11,14 @@ pub(crate) struct TransparencyParser;
 // FIXME(jdonszelmann): make these proper diagnostics
 #[allow(rustc::untranslatable_diagnostic)]
 #[allow(rustc::diagnostic_outside_of_impl)]
-impl SingleAttributeParser for TransparencyParser {
-    const PATH: &'static [Symbol] = &[sym::rustc_macro_transparency];
+impl<S: Stage> SingleAttributeParser<S> for TransparencyParser {
+    const PATH: &[Symbol] = &[sym::rustc_macro_transparency];
+    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
+    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Custom(|cx, used, unused| {
+        cx.dcx().span_err(vec![used, unused], "multiple macro transparency attributes");
+    });
 
-    fn on_duplicate(cx: &crate::context::AcceptContext<'_>, first_span: Span) {
-        cx.dcx().span_err(vec![first_span, cx.attr_span], "multiple macro transparency attributes");
-    }
-
-    fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option<AttributeKind> {
+    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
         match args.name_value().and_then(|nv| nv.value_as_str()) {
             Some(sym::transparent) => Some(Transparency::Transparent),
             Some(sym::semiopaque | sym::semitransparent) => Some(Transparency::SemiOpaque),
diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs
index 35fb768ad0b..47f72232828 100644
--- a/compiler/rustc_attr_parsing/src/context.rs
+++ b/compiler/rustc_attr_parsing/src/context.rs
@@ -1,13 +1,17 @@
 use std::cell::RefCell;
 use std::collections::BTreeMap;
-use std::ops::Deref;
+use std::marker::PhantomData;
+use std::ops::{Deref, DerefMut};
 use std::sync::LazyLock;
 
+use private::Sealed;
 use rustc_ast as ast;
+use rustc_ast::NodeId;
 use rustc_attr_data_structures::AttributeKind;
+use rustc_attr_data_structures::lints::{AttributeLint, AttributeLintKind};
 use rustc_errors::{DiagCtxtHandle, Diagnostic};
 use rustc_feature::Features;
-use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId};
+use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, HirId};
 use rustc_session::Session;
 use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
 
@@ -22,20 +26,40 @@ use crate::attributes::transparency::TransparencyParser;
 use crate::attributes::{AttributeParser as _, Combine, Single};
 use crate::parser::{ArgParser, MetaItemParser};
 
+macro_rules! group_type {
+    ($stage: ty) => {
+         LazyLock<(
+            BTreeMap<&'static [Symbol], Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, $stage>, &ArgParser<'a>) + Send + Sync>>,
+            Vec<Box<dyn Send + Sync + Fn(&mut FinalizeContext<'_, '_, $stage>) -> Option<AttributeKind>>>
+        )>
+    };
+}
+
 macro_rules! attribute_parsers {
     (
         pub(crate) static $name: ident = [$($names: ty),* $(,)?];
     ) => {
-        type Accepts = BTreeMap<
-            &'static [Symbol],
-            Box<dyn Send + Sync + Fn(&AcceptContext<'_>, &ArgParser<'_>)>
-        >;
-        type Finalizes = Vec<
-            Box<dyn Send + Sync + Fn(&FinalizeContext<'_>) -> Option<AttributeKind>>
-        >;
-        pub(crate) static $name: LazyLock<(Accepts, Finalizes)> = LazyLock::new(|| {
-            let mut accepts = Accepts::new();
-            let mut finalizes = Finalizes::new();
+        mod early {
+            use super::*;
+            type Combine<T> = super::Combine<T, Early>;
+            type Single<T> = super::Single<T, Early>;
+
+            attribute_parsers!(@[Early] pub(crate) static $name = [$($names),*];);
+        }
+        mod late {
+            use super::*;
+            type Combine<T> = super::Combine<T, Late>;
+            type Single<T> = super::Single<T, Late>;
+
+            attribute_parsers!(@[Late] pub(crate) static $name = [$($names),*];);
+        }
+    };
+    (
+        @[$ty: ty] pub(crate) static $name: ident = [$($names: ty),* $(,)?];
+    ) => {
+        pub(crate) static $name: group_type!($ty) = LazyLock::new(|| {
+            let mut accepts = BTreeMap::<_, Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, $ty>, &ArgParser<'a>) + Send + Sync>>::new();
+            let mut finalizes = Vec::<Box<dyn Send + Sync + Fn(&mut FinalizeContext<'_, '_, $ty>) -> Option<AttributeKind>>>::new();
             $(
                 {
                     thread_local! {
@@ -62,7 +86,6 @@ macro_rules! attribute_parsers {
         });
     };
 }
-
 attribute_parsers!(
     pub(crate) static ATTRIBUTE_PARSERS = [
         // tidy-alphabetical-start
@@ -86,50 +109,114 @@ attribute_parsers!(
     ];
 );
 
+mod private {
+    pub trait Sealed {}
+    impl Sealed for super::Early {}
+    impl Sealed for super::Late {}
+}
+
+// allow because it's a sealed trait
+#[allow(private_interfaces)]
+pub trait Stage: Sized + 'static + Sealed {
+    type Id: Copy;
+
+    fn parsers() -> &'static group_type!(Self);
+
+    fn emit_err<'sess>(sess: &'sess Session, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed;
+}
+
+// allow because it's a sealed trait
+#[allow(private_interfaces)]
+impl Stage for Early {
+    type Id = NodeId;
+
+    fn parsers() -> &'static group_type!(Self) {
+        &early::ATTRIBUTE_PARSERS
+    }
+    fn emit_err<'sess>(sess: &'sess Session, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed {
+        sess.dcx().create_err(diag).delay_as_bug()
+    }
+}
+
+// allow because it's a sealed trait
+#[allow(private_interfaces)]
+impl Stage for Late {
+    type Id = HirId;
+
+    fn parsers() -> &'static group_type!(Self) {
+        &late::ATTRIBUTE_PARSERS
+    }
+    fn emit_err<'sess>(tcx: &'sess Session, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed {
+        tcx.dcx().emit_err(diag)
+    }
+}
+
+/// used when parsing attributes for miscelaneous things *before* ast lowering
+pub struct Early;
+/// used when parsing attributes during ast lowering
+pub struct Late;
+
 /// Context given to every attribute parser when accepting
 ///
 /// Gives [`AttributeParser`]s enough information to create errors, for example.
-pub(crate) struct AcceptContext<'a> {
-    pub(crate) finalize_cx: &'a FinalizeContext<'a>,
+pub(crate) struct AcceptContext<'f, 'sess, S: Stage> {
+    pub(crate) finalize_cx: FinalizeContext<'f, 'sess, S>,
     /// The span of the attribute currently being parsed
     pub(crate) attr_span: Span,
 }
 
-impl<'a> AcceptContext<'a> {
-    pub(crate) fn emit_err(&self, diag: impl Diagnostic<'a>) -> ErrorGuaranteed {
-        if self.limit_diagnostics {
-            self.dcx().create_err(diag).delay_as_bug()
-        } else {
-            self.dcx().emit_err(diag)
-        }
+impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
+    pub(crate) fn emit_err(&self, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed {
+        S::emit_err(&self.sess, diag)
+    }
+
+    pub(crate) fn emit_lint(&mut self, lint: AttributeLintKind, span: Span) {
+        let id = self.target_id;
+        (self.emit_lint)(AttributeLint { id, span, kind: lint });
     }
 }
 
-impl<'a> Deref for AcceptContext<'a> {
-    type Target = FinalizeContext<'a>;
+impl<'f, 'sess, S: Stage> Deref for AcceptContext<'f, 'sess, S> {
+    type Target = FinalizeContext<'f, 'sess, S>;
 
     fn deref(&self) -> &Self::Target {
         &self.finalize_cx
     }
 }
 
+impl<'f, 'sess, S: Stage> DerefMut for AcceptContext<'f, 'sess, S> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.finalize_cx
+    }
+}
+
 /// Context given to every attribute parser during finalization.
 ///
 /// Gives [`AttributeParser`](crate::attributes::AttributeParser)s enough information to create
 /// errors, for example.
-pub(crate) struct FinalizeContext<'a> {
+pub(crate) struct FinalizeContext<'p, 'sess, S: Stage> {
     /// The parse context, gives access to the session and the
     /// diagnostics context.
-    pub(crate) cx: &'a AttributeParser<'a>,
+    pub(crate) cx: &'p mut AttributeParser<'sess, S>,
     /// The span of the syntactical component this attribute was applied to
     pub(crate) target_span: Span,
+    /// The id ([`NodeId`] if `S` is `Early`, [`HirId`] if `S` is `Late`) of the syntactical component this attribute was applied to
+    pub(crate) target_id: S::Id,
+
+    pub(crate) emit_lint: &'p mut dyn FnMut(AttributeLint<S::Id>),
 }
 
-impl<'a> Deref for FinalizeContext<'a> {
-    type Target = AttributeParser<'a>;
+impl<'p, 'sess: 'p, S: Stage> Deref for FinalizeContext<'p, 'sess, S> {
+    type Target = AttributeParser<'sess, S>;
 
     fn deref(&self) -> &Self::Target {
-        &self.cx
+        self.cx
+    }
+}
+
+impl<'p, 'sess: 'p, S: Stage> DerefMut for FinalizeContext<'p, 'sess, S> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        self.cx
     }
 }
 
@@ -141,23 +228,20 @@ pub enum OmitDoc {
 
 /// Context created once, for example as part of the ast lowering
 /// context, through which all attributes can be lowered.
-pub struct AttributeParser<'sess> {
+pub struct AttributeParser<'sess, S: Stage = Late> {
     #[expect(dead_code)] // FIXME(jdonszelmann): needed later to verify we parsed all attributes
     tools: Vec<Symbol>,
-    sess: &'sess Session,
     features: Option<&'sess Features>,
+    sess: &'sess Session,
+    stage: PhantomData<S>,
 
     /// *Only* parse attributes with this symbol.
     ///
     /// Used in cases where we want the lowering infrastructure for parse just a single attribute.
     parse_only: Option<Symbol>,
-
-    /// Can be used to instruct parsers to reduce the number of diagnostics it emits.
-    /// Useful when using `parse_limited` and you know the attr will be reparsed later.
-    pub(crate) limit_diagnostics: bool,
 }
 
-impl<'sess> AttributeParser<'sess> {
+impl<'sess> AttributeParser<'sess, Early> {
     /// This method allows you to parse attributes *before* you have access to features or tools.
     /// One example where this is necessary, is to parse `feature` attributes themselves for
     /// example.
@@ -168,33 +252,53 @@ impl<'sess> AttributeParser<'sess> {
     ///
     /// To make sure use is limited, supply a `Symbol` you'd like to parse. Only attributes with
     /// that symbol are picked out of the list of instructions and parsed. Those are returned.
+    ///
+    /// No diagnostics will be emitted when parsing limited. Lints are not emitted at all, while
+    /// errors will be emitted as a delayed bugs. in other words, we *expect* attributes parsed
+    /// with `parse_limited` to be reparsed later during ast lowering where we *do* emit the errors
     pub fn parse_limited(
         sess: &'sess Session,
         attrs: &[ast::Attribute],
         sym: Symbol,
         target_span: Span,
-        limit_diagnostics: bool,
+        target_node_id: NodeId,
     ) -> Option<Attribute> {
-        let mut parsed = Self {
-            sess,
+        let mut p = Self {
             features: None,
             tools: Vec::new(),
             parse_only: Some(sym),
-            limit_diagnostics,
-        }
-        .parse_attribute_list(attrs, target_span, OmitDoc::Skip, std::convert::identity);
-
+            sess,
+            stage: PhantomData,
+        };
+        let mut parsed = p.parse_attribute_list(
+            attrs,
+            target_span,
+            target_node_id,
+            OmitDoc::Skip,
+            std::convert::identity,
+            |_lint| {
+                panic!("can't emit lints here for now (nothing uses this atm)");
+            },
+        );
         assert!(parsed.len() <= 1);
 
         parsed.pop()
     }
 
+    pub fn new_early(sess: &'sess Session, features: &'sess Features, tools: Vec<Symbol>) -> Self {
+        Self { features: Some(features), tools, parse_only: None, sess, stage: PhantomData }
+    }
+}
+
+impl<'sess> AttributeParser<'sess, Late> {
     pub fn new(sess: &'sess Session, features: &'sess Features, tools: Vec<Symbol>) -> Self {
-        Self { sess, features: Some(features), tools, parse_only: None, limit_diagnostics: false }
+        Self { features: Some(features), tools, parse_only: None, sess, stage: PhantomData }
     }
+}
 
+impl<'sess, S: Stage> AttributeParser<'sess, S> {
     pub(crate) fn sess(&self) -> &'sess Session {
-        self.sess
+        &self.sess
     }
 
     pub(crate) fn features(&self) -> &'sess Features {
@@ -202,25 +306,25 @@ impl<'sess> AttributeParser<'sess> {
     }
 
     pub(crate) fn dcx(&self) -> DiagCtxtHandle<'sess> {
-        self.sess.dcx()
+        self.sess().dcx()
     }
 
     /// Parse a list of attributes.
     ///
     /// `target_span` is the span of the thing this list of attributes is applied to,
     /// and when `omit_doc` is set, doc attributes are filtered out.
-    pub fn parse_attribute_list<'a>(
-        &'a self,
-        attrs: &'a [ast::Attribute],
+    pub fn parse_attribute_list(
+        &mut self,
+        attrs: &[ast::Attribute],
         target_span: Span,
+        target_id: S::Id,
         omit_doc: OmitDoc,
 
         lower_span: impl Copy + Fn(Span) -> Span,
+        mut emit_lint: impl FnMut(AttributeLint<S::Id>),
     ) -> Vec<Attribute> {
         let mut attributes = Vec::new();
 
-        let finalize_cx = FinalizeContext { cx: self, target_span };
-
         for attr in attrs {
             // If we're only looking for a single attribute, skip all the ones we don't care about.
             if let Some(expected) = self.parse_only {
@@ -268,13 +372,18 @@ impl<'sess> AttributeParser<'sess> {
                     let args = parser.args();
                     let parts = path.segments().map(|i| i.name).collect::<Vec<_>>();
 
-                    if let Some(accept) = ATTRIBUTE_PARSERS.0.get(parts.as_slice()) {
-                        let cx = AcceptContext {
-                            finalize_cx: &finalize_cx,
+                    if let Some(accept) = S::parsers().0.get(parts.as_slice()) {
+                        let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext {
+                            finalize_cx: FinalizeContext {
+                                cx: self,
+                                target_span,
+                                target_id,
+                                emit_lint: &mut emit_lint,
+                            },
                             attr_span: lower_span(attr.span),
                         };
 
-                        accept(&cx, &args)
+                        accept(&mut cx, args)
                     } else {
                         // If we're here, we must be compiling a tool attribute... Or someone
                         // forgot to parse their fancy new attribute. Let's warn them in any case.
@@ -304,8 +413,13 @@ impl<'sess> AttributeParser<'sess> {
         }
 
         let mut parsed_attributes = Vec::new();
-        for f in &ATTRIBUTE_PARSERS.1 {
-            if let Some(attr) = f(&finalize_cx) {
+        for f in &S::parsers().1 {
+            if let Some(attr) = f(&mut FinalizeContext {
+                cx: self,
+                target_span,
+                target_id,
+                emit_lint: &mut emit_lint,
+            }) {
                 parsed_attributes.push(Attribute::Parsed(attr));
             }
         }
diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs
index 15037e802ff..47eeb63bad3 100644
--- a/compiler/rustc_attr_parsing/src/lib.rs
+++ b/compiler/rustc_attr_parsing/src/lib.rs
@@ -85,7 +85,8 @@
 
 #[macro_use]
 mod attributes;
-mod context;
+pub(crate) mod context;
+mod lints;
 pub mod parser;
 mod session_diagnostics;
 
@@ -93,6 +94,7 @@ pub use attributes::cfg::*;
 pub use attributes::util::{
     find_crate_name, is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version,
 };
-pub use context::{AttributeParser, OmitDoc};
+pub use context::{AttributeParser, Early, Late, OmitDoc};
+pub use lints::emit_attribute_lint;
 
 rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
diff --git a/compiler/rustc_attr_parsing/src/lints.rs b/compiler/rustc_attr_parsing/src/lints.rs
new file mode 100644
index 00000000000..d0d112446b4
--- /dev/null
+++ b/compiler/rustc_attr_parsing/src/lints.rs
@@ -0,0 +1,19 @@
+use rustc_attr_data_structures::lints::{AttributeLint, AttributeLintKind};
+use rustc_errors::LintEmitter;
+use rustc_hir::HirId;
+
+use crate::session_diagnostics;
+
+pub fn emit_attribute_lint<L: LintEmitter>(lint: &AttributeLint<HirId>, lint_emitter: L) {
+    let AttributeLint { id, span, kind } = lint;
+
+    match kind {
+        &AttributeLintKind::UnusedDuplicate { this, other, warning } => lint_emitter
+            .emit_node_span_lint(
+                rustc_session::lint::builtin::UNUSED_ATTRIBUTES,
+                *id,
+                *span,
+                session_diagnostics::UnusedDuplicate { this, other, warning },
+            ),
+    }
+}
diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs
index e10e3b511db..1edbe3a9d27 100644
--- a/compiler/rustc_attr_parsing/src/parser.rs
+++ b/compiler/rustc_attr_parsing/src/parser.rs
@@ -115,7 +115,7 @@ impl<'a> ArgParser<'a> {
         }
     }
 
-    pub fn from_attr_args(value: &'a AttrArgs, dcx: DiagCtxtHandle<'a>) -> Self {
+    pub fn from_attr_args<'sess>(value: &'a AttrArgs, dcx: DiagCtxtHandle<'sess>) -> Self {
         match value {
             AttrArgs::Empty => Self::NoArgs,
             AttrArgs::Delimited(args) if args.delim == Delimiter::Parenthesis => {
@@ -235,7 +235,7 @@ impl<'a> Debug for MetaItemParser<'a> {
 impl<'a> MetaItemParser<'a> {
     /// Create a new parser from a [`NormalAttr`], which is stored inside of any
     /// [`ast::Attribute`](rustc_ast::Attribute)
-    pub fn from_attr(attr: &'a NormalAttr, dcx: DiagCtxtHandle<'a>) -> Self {
+    pub fn from_attr<'sess>(attr: &'a NormalAttr, dcx: DiagCtxtHandle<'sess>) -> Self {
         Self {
             path: PathParser::Ast(&attr.item.path),
             args: ArgParser::from_attr_args(&attr.item.args, dcx),
@@ -320,13 +320,13 @@ fn expr_to_lit(dcx: DiagCtxtHandle<'_>, expr: &Expr, span: Span) -> MetaItemLit
     }
 }
 
-struct MetaItemListParserContext<'a> {
+struct MetaItemListParserContext<'a, 'sess> {
     // the tokens inside the delimiters, so `#[some::attr(a b c)]` would have `a b c` inside
     inside_delimiters: Peekable<TokenStreamIter<'a>>,
-    dcx: DiagCtxtHandle<'a>,
+    dcx: DiagCtxtHandle<'sess>,
 }
 
-impl<'a> MetaItemListParserContext<'a> {
+impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> {
     fn done(&mut self) -> bool {
         self.inside_delimiters.peek().is_none()
     }
@@ -507,11 +507,11 @@ pub struct MetaItemListParser<'a> {
 }
 
 impl<'a> MetaItemListParser<'a> {
-    fn new(delim: &'a DelimArgs, dcx: DiagCtxtHandle<'a>) -> MetaItemListParser<'a> {
+    fn new<'sess>(delim: &'a DelimArgs, dcx: DiagCtxtHandle<'sess>) -> Self {
         MetaItemListParser::new_tts(delim.tokens.iter(), delim.dspan.entire(), dcx)
     }
 
-    fn new_tts(tts: TokenStreamIter<'a>, span: Span, dcx: DiagCtxtHandle<'a>) -> Self {
+    fn new_tts<'sess>(tts: TokenStreamIter<'a>, span: Span, dcx: DiagCtxtHandle<'sess>) -> Self {
         MetaItemListParserContext { inside_delimiters: tts.peekable(), dcx }.parse(span)
     }
 
diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs
index 2c434175b4b..7f847d3dd4c 100644
--- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs
+++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs
@@ -3,7 +3,7 @@ use std::num::IntErrorKind;
 use rustc_ast as ast;
 use rustc_errors::codes::*;
 use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level};
-use rustc_macros::{Diagnostic, Subdiagnostic};
+use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
 use rustc_span::{Span, Symbol};
 
 use crate::fluent_generated as fluent;
@@ -451,6 +451,17 @@ pub(crate) struct UnusedMultiple {
     pub name: Symbol,
 }
 
+#[derive(LintDiagnostic)]
+#[diag(attr_parsing_unused_duplicate)]
+pub(crate) struct UnusedDuplicate {
+    #[suggestion(code = "", applicability = "machine-applicable")]
+    pub this: Span,
+    #[note]
+    pub other: Span,
+    #[warning]
+    pub warning: bool,
+}
+
 #[derive(Diagnostic)]
 #[diag(attr_parsing_stability_outside_std, code = E0734)]
 pub(crate) struct StabilityOutsideStd {
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index 1b4bb11d87b..34d36849939 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -3229,8 +3229,20 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                                     Applicability::MaybeIncorrect,
                                 );
                             }
+
+                            let mutability = if matches!(borrow.kind(), BorrowKind::Mut { .. }) {
+                                "mut "
+                            } else {
+                                ""
+                            };
+
                             if !is_format_arguments_item {
-                                let addition = format!("let binding = {};\n{}", s, " ".repeat(p));
+                                let addition = format!(
+                                    "let {}binding = {};\n{}",
+                                    mutability,
+                                    s,
+                                    " ".repeat(p)
+                                );
                                 err.multipart_suggestion_verbose(
                                     msg,
                                     vec![
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 4f75dd7e992..9b6dcfd17c6 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -373,8 +373,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
     }
 
     fn unsized_feature_enabled(&self) -> bool {
-        let features = self.tcx().features();
-        features.unsized_locals() || features.unsized_fn_params()
+        self.tcx().features().unsized_fn_params()
     }
 
     /// Equate the inferred type and the annotated type for user type annotations
@@ -957,7 +956,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
             }
         }
 
-        // When `unsized_fn_params` or `unsized_locals` is enabled, only function calls
+        // When `unsized_fn_params` is enabled, only function calls
         // and nullary ops are checked in `check_call_dest`.
         if !self.unsized_feature_enabled() {
             match self.body.local_kind(local) {
@@ -1941,7 +1940,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                     );
                 }
 
-                // When `unsized_fn_params` and `unsized_locals` are both not enabled,
+                // When `unsized_fn_params` is not enabled,
                 // this check is done at `check_local`.
                 if self.unsized_feature_enabled() {
                     let span = term.source_info.span;
diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
index 6dd3adf750d..d642851bb77 100644
--- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
@@ -484,7 +484,7 @@ impl<'a> TraitDef<'a> {
         match item {
             Annotatable::Item(item) => {
                 let is_packed = matches!(
-                    AttributeParser::parse_limited(cx.sess, &item.attrs, sym::repr, item.span, true),
+                    AttributeParser::parse_limited(cx.sess, &item.attrs, sym::repr, item.span, item.id),
                     Some(Attribute::Parsed(AttributeKind::Repr(r))) if r.iter().any(|(x, _)| matches!(x, ReprPacked(..)))
                 );
 
diff --git a/compiler/rustc_codegen_cranelift/example/arbitrary_self_types_pointers_and_wrappers.rs b/compiler/rustc_codegen_cranelift/example/arbitrary_self_types_pointers_and_wrappers.rs
index 5479b0c617b..f0d1f6e2215 100644
--- a/compiler/rustc_codegen_cranelift/example/arbitrary_self_types_pointers_and_wrappers.rs
+++ b/compiler/rustc_codegen_cranelift/example/arbitrary_self_types_pointers_and_wrappers.rs
@@ -32,10 +32,6 @@ impl<T: CoerceUnsized<U>, U> CoerceUnsized<Wrapper<U>> for Wrapper<T> {}
 impl<T: DispatchFromDyn<U>, U> DispatchFromDyn<Wrapper<U>> for Wrapper<T> {}
 
 trait Trait {
-    // This method isn't object-safe yet. Unsized by-value `self` is object-safe (but not callable
-    // without unsized_locals), but wrappers around `Self` currently are not.
-    // FIXME (mikeyhew) uncomment this when unsized rvalues object-safety is implemented
-    // fn wrapper(self: Wrapper<Self>) -> i32;
     fn ptr_wrapper(self: Ptr<Wrapper<Self>>) -> i32;
     fn wrapper_ptr(self: Wrapper<Ptr<Self>>) -> i32;
     fn wrapper_ptr_wrapper(self: Wrapper<Ptr<Wrapper<Self>>>) -> i32;
diff --git a/compiler/rustc_codegen_cranelift/example/mini_core.rs b/compiler/rustc_codegen_cranelift/example/mini_core.rs
index 1dc799c0aee..012e4dbc3ec 100644
--- a/compiler/rustc_codegen_cranelift/example/mini_core.rs
+++ b/compiler/rustc_codegen_cranelift/example/mini_core.rs
@@ -644,9 +644,9 @@ pub mod intrinsics {
     #[rustc_intrinsic]
     pub unsafe fn size_of_val<T: ?::Sized>(val: *const T) -> usize;
     #[rustc_intrinsic]
-    pub fn min_align_of<T>() -> usize;
+    pub fn align_of<T>() -> usize;
     #[rustc_intrinsic]
-    pub unsafe fn min_align_of_val<T: ?::Sized>(val: *const T) -> usize;
+    pub unsafe fn align_of_val<T: ?::Sized>(val: *const T) -> usize;
     #[rustc_intrinsic]
     pub unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize);
     #[rustc_intrinsic]
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 93ca2e0e421..1499f948deb 100644
--- a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs
+++ b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs
@@ -204,11 +204,8 @@ fn main() {
         assert_eq!(intrinsics::size_of_val(a) as u8, 16);
         assert_eq!(intrinsics::size_of_val(&0u32) as u8, 4);
 
-        assert_eq!(intrinsics::min_align_of::<u16>() as u8, 2);
-        assert_eq!(
-            intrinsics::min_align_of_val(&a) as u8,
-            intrinsics::min_align_of::<&str>() as u8
-        );
+        assert_eq!(intrinsics::align_of::<u16>() as u8, 2);
+        assert_eq!(intrinsics::align_of_val(&a) as u8, intrinsics::align_of::<&str>() as u8);
 
         assert!(!intrinsics::needs_drop::<u8>());
         assert!(!intrinsics::needs_drop::<[u8]>());
diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs
index fe5b220117f..4c6fd907815 100644
--- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs
+++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs
@@ -51,6 +51,11 @@ pub(crate) fn conv_to_call_conv(
         CanonAbi::Rust | CanonAbi::C => default_call_conv,
         CanonAbi::RustCold => CallConv::Cold,
 
+        // Functions with this calling convention can only be called from assembly, but it is
+        // possible to declare an `extern "custom"` block, so the backend still needs a calling
+        // convention for declaring foreign functions.
+        CanonAbi::Custom => default_call_conv,
+
         CanonAbi::X86(x86_call) => match x86_call {
             X86Call::SysV64 => CallConv::SystemV,
             X86Call::Win64 => CallConv::WindowsFastcall,
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
index 1d1cf884e48..df5748c34d1 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
@@ -586,7 +586,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
             let (size, _align) = crate::unsize::size_and_align_of(fx, layout, meta);
             ret.write_cvalue(fx, CValue::by_val(size, usize_layout));
         }
-        sym::min_align_of_val => {
+        sym::align_of_val => {
             intrinsic_args!(fx, args => (ptr); intrinsic);
 
             let layout = fx.layout_of(generic_args.type_at(0));
@@ -613,7 +613,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
             intrinsic_args!(fx, args => (vtable); intrinsic);
             let vtable = vtable.load_scalar(fx);
 
-            let align = crate::vtable::min_align_of_obj(fx, vtable);
+            let align = crate::vtable::align_of_obj(fx, vtable);
             ret.write_cvalue(fx, CValue::by_val(align, usize_layout));
         }
 
diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs
index 662546e4999..df60b05c463 100644
--- a/compiler/rustc_codegen_cranelift/src/unsize.rs
+++ b/compiler/rustc_codegen_cranelift/src/unsize.rs
@@ -212,7 +212,7 @@ pub(crate) fn size_and_align_of<'tcx>(
             // load size/align from vtable
             (
                 crate::vtable::size_of_obj(fx, info.unwrap()),
-                crate::vtable::min_align_of_obj(fx, info.unwrap()),
+                crate::vtable::align_of_obj(fx, info.unwrap()),
             )
         }
         ty::Slice(_) | ty::Str => {
diff --git a/compiler/rustc_codegen_cranelift/src/vtable.rs b/compiler/rustc_codegen_cranelift/src/vtable.rs
index 05a8e3c3342..1fae56949bc 100644
--- a/compiler/rustc_codegen_cranelift/src/vtable.rs
+++ b/compiler/rustc_codegen_cranelift/src/vtable.rs
@@ -31,7 +31,7 @@ pub(crate) fn size_of_obj(fx: &mut FunctionCx<'_, '_, '_>, vtable: Value) -> Val
     )
 }
 
-pub(crate) fn min_align_of_obj(fx: &mut FunctionCx<'_, '_, '_>, vtable: Value) -> Value {
+pub(crate) fn align_of_obj(fx: &mut FunctionCx<'_, '_, '_>, vtable: Value) -> Value {
     let usize_size = fx.layout_of(fx.tcx.types.usize).size.bytes() as usize;
     fx.bcx.ins().load(
         fx.pointer_type,
diff --git a/compiler/rustc_codegen_gcc/example/arbitrary_self_types_pointers_and_wrappers.rs b/compiler/rustc_codegen_gcc/example/arbitrary_self_types_pointers_and_wrappers.rs
index b299aa87974..c26606f0bdd 100644
--- a/compiler/rustc_codegen_gcc/example/arbitrary_self_types_pointers_and_wrappers.rs
+++ b/compiler/rustc_codegen_gcc/example/arbitrary_self_types_pointers_and_wrappers.rs
@@ -37,10 +37,6 @@ impl<T: DispatchFromDyn<U>, U> DispatchFromDyn<Wrapper<U>> for Wrapper<T> {}
 
 
 trait Trait {
-    // This method isn't object-safe yet. Unsized by-value `self` is object-safe (but not callable
-    // without unsized_locals), but wrappers around `Self` currently are not.
-    // FIXME (mikeyhew) uncomment this when unsized rvalues object-safety is implemented
-    // fn wrapper(self: Wrapper<Self>) -> i32;
     fn ptr_wrapper(self: Ptr<Wrapper<Self>>) -> i32;
     fn wrapper_ptr(self: Wrapper<Ptr<Self>>) -> i32;
     fn wrapper_ptr_wrapper(self: Wrapper<Ptr<Wrapper<Self>>>) -> i32;
diff --git a/compiler/rustc_codegen_gcc/example/mini_core.rs b/compiler/rustc_codegen_gcc/example/mini_core.rs
index d1d8e8fd5bc..aca1f0080ef 100644
--- a/compiler/rustc_codegen_gcc/example/mini_core.rs
+++ b/compiler/rustc_codegen_gcc/example/mini_core.rs
@@ -655,9 +655,9 @@ pub mod intrinsics {
     #[rustc_intrinsic]
     pub unsafe fn size_of_val<T: ?::Sized>(val: *const T) -> usize;
     #[rustc_intrinsic]
-    pub fn min_align_of<T>() -> usize;
+    pub fn align_of<T>() -> usize;
     #[rustc_intrinsic]
-    pub unsafe fn min_align_of_val<T: ?::Sized>(val: *const T) -> usize;
+    pub unsafe fn align_of_val<T: ?::Sized>(val: *const T) -> usize;
     #[rustc_intrinsic]
     pub unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize);
     #[rustc_intrinsic]
diff --git a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs
index 4cbe66c5e4c..c3bd62e5897 100644
--- a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs
+++ b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs
@@ -153,7 +153,7 @@ fn main() {
     let slice = &[0, 1] as &[i32];
     let slice_ptr = slice as *const [i32] as *const i32;
 
-    let align = intrinsics::min_align_of::<*const i32>();
+    let align = intrinsics::align_of::<*const i32>();
     assert_eq!(slice_ptr as usize % align, 0);
 
     //return;
@@ -194,8 +194,8 @@ fn main() {
         assert_eq!(intrinsics::size_of_val(a) as u8, 8);
         assert_eq!(intrinsics::size_of_val(&0u32) as u8, 4);
 
-        assert_eq!(intrinsics::min_align_of::<u16>() as u8, 2);
-        assert_eq!(intrinsics::min_align_of_val(&a) as u8, intrinsics::min_align_of::<&str>() as u8);
+        assert_eq!(intrinsics::align_of::<u16>() as u8, 2);
+        assert_eq!(intrinsics::align_of_val(&a) as u8, intrinsics::align_of::<&str>() as u8);
 
         assert!(!intrinsics::needs_drop::<u8>());
         assert!(!intrinsics::needs_drop::<[u8]>());
diff --git a/compiler/rustc_codegen_gcc/messages.ftl b/compiler/rustc_codegen_gcc/messages.ftl
index 546bfc87b68..18a8a5a1e04 100644
--- a/compiler/rustc_codegen_gcc/messages.ftl
+++ b/compiler/rustc_codegen_gcc/messages.ftl
@@ -2,9 +2,6 @@ codegen_gcc_unknown_ctarget_feature_prefix =
     unknown feature specified for `-Ctarget-feature`: `{$feature}`
     .note = features must begin with a `+` to enable or `-` to disable it
 
-codegen_gcc_forbidden_ctarget_feature =
-    target feature `{$feature}` cannot be toggled with `-Ctarget-feature`: {$reason}
-
 codegen_gcc_unwinding_inline_asm =
     GCC backend does not support unwinding from inline asm
 
@@ -26,10 +23,6 @@ codegen_gcc_unknown_ctarget_feature =
     .possible_feature = you might have meant: `{$rust_feature}`
     .consider_filing_feature_request = consider filing a feature request
 
-codegen_gcc_unstable_ctarget_feature =
-    unstable feature specified for `-Ctarget-feature`: `{$feature}`
-    .note = this feature is not stably supported; its behavior can change in the future
-
 codegen_gcc_missing_features =
     add the missing features in a `target_feature` attribute
 
diff --git a/compiler/rustc_codegen_gcc/src/abi.rs b/compiler/rustc_codegen_gcc/src/abi.rs
index 3d0c258f576..08f3d281904 100644
--- a/compiler/rustc_codegen_gcc/src/abi.rs
+++ b/compiler/rustc_codegen_gcc/src/abi.rs
@@ -239,12 +239,16 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
 pub fn conv_to_fn_attribute<'gcc>(conv: CanonAbi, arch: &str) -> Option<FnAttribute<'gcc>> {
     let attribute = match conv {
         CanonAbi::C | CanonAbi::Rust => return None,
+        CanonAbi::RustCold => FnAttribute::Cold,
+        // Functions with this calling convention can only be called from assembly, but it is
+        // possible to declare an `extern "custom"` block, so the backend still needs a calling
+        // convention for declaring foreign functions.
+        CanonAbi::Custom => return None,
         CanonAbi::Arm(arm_call) => match arm_call {
             ArmCall::CCmseNonSecureCall => FnAttribute::ArmCmseNonsecureCall,
             ArmCall::CCmseNonSecureEntry => FnAttribute::ArmCmseNonsecureEntry,
             ArmCall::Aapcs => FnAttribute::ArmPcs("aapcs"),
         },
-        CanonAbi::RustCold => FnAttribute::Cold,
         CanonAbi::GpuKernel => {
             if arch == "amdgpu" {
                 FnAttribute::GcnAmdGpuHsaKernel
diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs
index d1fb8d8f9d6..1bd89212100 100644
--- a/compiler/rustc_codegen_gcc/src/builder.rs
+++ b/compiler/rustc_codegen_gcc/src/builder.rs
@@ -897,7 +897,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
     fn checked_binop(
         &mut self,
         oop: OverflowOp,
-        typ: Ty<'_>,
+        typ: Ty<'tcx>,
         lhs: Self::Value,
         rhs: Self::Value,
     ) -> (Self::Value, Self::Value) {
diff --git a/compiler/rustc_codegen_gcc/src/errors.rs b/compiler/rustc_codegen_gcc/src/errors.rs
index ccd9abe3804..7786be9ae5d 100644
--- a/compiler/rustc_codegen_gcc/src/errors.rs
+++ b/compiler/rustc_codegen_gcc/src/errors.rs
@@ -17,21 +17,6 @@ pub(crate) struct UnknownCTargetFeature<'a> {
     pub rust_feature: PossibleFeature<'a>,
 }
 
-#[derive(Diagnostic)]
-#[diag(codegen_gcc_unstable_ctarget_feature)]
-#[note]
-pub(crate) struct UnstableCTargetFeature<'a> {
-    pub feature: &'a str,
-}
-
-#[derive(Diagnostic)]
-#[diag(codegen_gcc_forbidden_ctarget_feature)]
-pub(crate) struct ForbiddenCTargetFeature<'a> {
-    pub feature: &'a str,
-    pub enabled: &'a str,
-    pub reason: &'a str,
-}
-
 #[derive(Subdiagnostic)]
 pub(crate) enum PossibleFeature<'a> {
     #[help(codegen_gcc_possible_feature)]
diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs
index 2b053abdd19..d90e66aea31 100644
--- a/compiler/rustc_codegen_gcc/src/gcc_util.rs
+++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs
@@ -5,13 +5,17 @@ use rustc_codegen_ssa::errors::TargetFeatureDisableOrEnable;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::unord::UnordSet;
 use rustc_session::Session;
+use rustc_session::features::{StabilityExt, retpoline_features_by_flags};
 use rustc_target::target_features::RUSTC_SPECIFIC_FEATURES;
 use smallvec::{SmallVec, smallvec};
 
-use crate::errors::{
-    ForbiddenCTargetFeature, PossibleFeature, UnknownCTargetFeature, UnknownCTargetFeaturePrefix,
-    UnstableCTargetFeature,
-};
+use crate::errors::{PossibleFeature, UnknownCTargetFeature, UnknownCTargetFeaturePrefix};
+
+fn gcc_features_by_flags(sess: &Session) -> Vec<&str> {
+    let mut features: Vec<&str> = Vec::new();
+    retpoline_features_by_flags(sess, &mut features);
+    features
+}
 
 /// The list of GCC features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`,
 /// `--target` and similar).
@@ -45,7 +49,7 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec<Stri
 
     // Compute implied features
     let mut all_rust_features = vec![];
-    for feature in sess.opts.cg.target_feature.split(',') {
+    for feature in sess.opts.cg.target_feature.split(',').chain(gcc_features_by_flags(sess)) {
         if let Some(feature) = feature.strip_prefix('+') {
             all_rust_features.extend(
                 UnordSet::from(sess.target.implied_target_features(feature))
@@ -94,18 +98,7 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec<Stri
                     sess.dcx().emit_warn(unknown_feature);
                 }
                 Some(&(_, stability, _)) => {
-                    if let Err(reason) = stability.toggle_allowed() {
-                        sess.dcx().emit_warn(ForbiddenCTargetFeature {
-                            feature,
-                            enabled: if enable { "enabled" } else { "disabled" },
-                            reason,
-                        });
-                    } else if stability.requires_nightly().is_some() {
-                        // An unstable feature. Warn about using it. (It makes little sense
-                        // to hard-error here since we just warn about fully unknown
-                        // features above).
-                        sess.dcx().emit_warn(UnstableCTargetFeature { feature });
-                    }
+                    stability.verify_feature_enabled_by_flag(sess, enable, feature);
                 }
             }
 
diff --git a/compiler/rustc_codegen_llvm/messages.ftl b/compiler/rustc_codegen_llvm/messages.ftl
index bda121c67fb..3faeb9b3b22 100644
--- a/compiler/rustc_codegen_llvm/messages.ftl
+++ b/compiler/rustc_codegen_llvm/messages.ftl
@@ -10,11 +10,6 @@ codegen_llvm_dynamic_linking_with_lto =
 
 codegen_llvm_fixed_x18_invalid_arch = the `-Zfixed-x18` flag is not supported on the `{$arch}` architecture
 
-codegen_llvm_forbidden_ctarget_feature =
-    target feature `{$feature}` cannot be {$enabled} with `-Ctarget-feature`: {$reason}
-    .note = this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-codegen_llvm_forbidden_ctarget_feature_issue = for more information, see issue #116344 <https://github.com/rust-lang/rust/issues/116344>
-
 codegen_llvm_from_llvm_diag = {$message}
 
 codegen_llvm_from_llvm_optimization_diag = {$filename}:{$line}:{$column} {$pass_name} ({$kind}): {$message}
@@ -76,10 +71,6 @@ codegen_llvm_unknown_ctarget_feature_prefix =
 
 codegen_llvm_unknown_debuginfo_compression = unknown debuginfo compression algorithm {$algorithm} - will fall back to uncompressed debuginfo
 
-codegen_llvm_unstable_ctarget_feature =
-    unstable feature specified for `-Ctarget-feature`: `{$feature}`
-    .note = this feature is not stably supported; its behavior can change in the future
-
 codegen_llvm_write_bytecode = failed to write bytecode to {$path}: {$err}
 
 codegen_llvm_write_ir = failed to write LLVM IR to {$path}
diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs
index 119cd634f98..aba63d75f1d 100644
--- a/compiler/rustc_codegen_llvm/src/abi.rs
+++ b/compiler/rustc_codegen_llvm/src/abi.rs
@@ -649,6 +649,10 @@ impl llvm::CallConv {
         match conv {
             CanonAbi::C | CanonAbi::Rust => llvm::CCallConv,
             CanonAbi::RustCold => llvm::PreserveMost,
+            // Functions with this calling convention can only be called from assembly, but it is
+            // possible to declare an `extern "custom"` block, so the backend still needs a calling
+            // convention for declaring foreign functions.
+            CanonAbi::Custom => llvm::CCallConv,
             CanonAbi::GpuKernel => {
                 if arch == "amdgpu" {
                     llvm::AmdgpuKernel
diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs
index ec006b59192..09cb74d9dcb 100644
--- a/compiler/rustc_codegen_llvm/src/builder.rs
+++ b/compiler/rustc_codegen_llvm/src/builder.rs
@@ -14,7 +14,6 @@ use rustc_codegen_ssa::mir::place::PlaceRef;
 use rustc_codegen_ssa::traits::*;
 use rustc_data_structures::small_c_str::SmallCStr;
 use rustc_hir::def_id::DefId;
-use rustc_middle::bug;
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
 use rustc_middle::ty::layout::{
     FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasTypingEnv, LayoutError, LayoutOfHelpers,
@@ -484,73 +483,31 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
     fn checked_binop(
         &mut self,
         oop: OverflowOp,
-        ty: Ty<'_>,
+        ty: Ty<'tcx>,
         lhs: Self::Value,
         rhs: Self::Value,
     ) -> (Self::Value, Self::Value) {
-        use rustc_middle::ty::IntTy::*;
-        use rustc_middle::ty::UintTy::*;
-        use rustc_middle::ty::{Int, Uint};
-
-        let new_kind = match ty.kind() {
-            Int(t @ Isize) => Int(t.normalize(self.tcx.sess.target.pointer_width)),
-            Uint(t @ Usize) => Uint(t.normalize(self.tcx.sess.target.pointer_width)),
-            t @ (Uint(_) | Int(_)) => *t,
-            _ => panic!("tried to get overflow intrinsic for op applied to non-int type"),
+        let (size, signed) = ty.int_size_and_signed(self.tcx);
+        let width = size.bits();
+
+        if oop == OverflowOp::Sub && !signed {
+            // Emit sub and icmp instead of llvm.usub.with.overflow. LLVM considers these
+            // to be the canonical form. It will attempt to reform llvm.usub.with.overflow
+            // in the backend if profitable.
+            let sub = self.sub(lhs, rhs);
+            let cmp = self.icmp(IntPredicate::IntULT, lhs, rhs);
+            return (sub, cmp);
+        }
+
+        let oop_str = match oop {
+            OverflowOp::Add => "add",
+            OverflowOp::Sub => "sub",
+            OverflowOp::Mul => "mul",
         };
 
-        let name = match oop {
-            OverflowOp::Add => match new_kind {
-                Int(I8) => "llvm.sadd.with.overflow.i8",
-                Int(I16) => "llvm.sadd.with.overflow.i16",
-                Int(I32) => "llvm.sadd.with.overflow.i32",
-                Int(I64) => "llvm.sadd.with.overflow.i64",
-                Int(I128) => "llvm.sadd.with.overflow.i128",
-
-                Uint(U8) => "llvm.uadd.with.overflow.i8",
-                Uint(U16) => "llvm.uadd.with.overflow.i16",
-                Uint(U32) => "llvm.uadd.with.overflow.i32",
-                Uint(U64) => "llvm.uadd.with.overflow.i64",
-                Uint(U128) => "llvm.uadd.with.overflow.i128",
-
-                _ => unreachable!(),
-            },
-            OverflowOp::Sub => match new_kind {
-                Int(I8) => "llvm.ssub.with.overflow.i8",
-                Int(I16) => "llvm.ssub.with.overflow.i16",
-                Int(I32) => "llvm.ssub.with.overflow.i32",
-                Int(I64) => "llvm.ssub.with.overflow.i64",
-                Int(I128) => "llvm.ssub.with.overflow.i128",
-
-                Uint(_) => {
-                    // Emit sub and icmp instead of llvm.usub.with.overflow. LLVM considers these
-                    // to be the canonical form. It will attempt to reform llvm.usub.with.overflow
-                    // in the backend if profitable.
-                    let sub = self.sub(lhs, rhs);
-                    let cmp = self.icmp(IntPredicate::IntULT, lhs, rhs);
-                    return (sub, cmp);
-                }
-
-                _ => unreachable!(),
-            },
-            OverflowOp::Mul => match new_kind {
-                Int(I8) => "llvm.smul.with.overflow.i8",
-                Int(I16) => "llvm.smul.with.overflow.i16",
-                Int(I32) => "llvm.smul.with.overflow.i32",
-                Int(I64) => "llvm.smul.with.overflow.i64",
-                Int(I128) => "llvm.smul.with.overflow.i128",
-
-                Uint(U8) => "llvm.umul.with.overflow.i8",
-                Uint(U16) => "llvm.umul.with.overflow.i16",
-                Uint(U32) => "llvm.umul.with.overflow.i32",
-                Uint(U64) => "llvm.umul.with.overflow.i64",
-                Uint(U128) => "llvm.umul.with.overflow.i128",
-
-                _ => unreachable!(),
-            },
-        };
+        let name = format!("llvm.{}{oop_str}.with.overflow", if signed { 's' } else { 'u' });
 
-        let res = self.call_intrinsic(name, &[lhs, rhs]);
+        let res = self.call_intrinsic(&name, &[self.type_ix(width)], &[lhs, rhs]);
         (self.extract_value(res, 0), self.extract_value(res, 1))
     }
 
@@ -954,11 +911,11 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
     }
 
     fn fptoui_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value {
-        self.fptoint_sat(false, val, dest_ty)
+        self.call_intrinsic("llvm.fptoui.sat", &[dest_ty, self.val_ty(val)], &[val])
     }
 
     fn fptosi_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value {
-        self.fptoint_sat(true, val, dest_ty)
+        self.call_intrinsic("llvm.fptosi.sat", &[dest_ty, self.val_ty(val)], &[val])
     }
 
     fn fptoui(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value {
@@ -981,15 +938,12 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
             if self.cx.type_kind(src_ty) != TypeKind::Vector {
                 let float_width = self.cx.float_width(src_ty);
                 let int_width = self.cx.int_width(dest_ty);
-                let name = match (int_width, float_width) {
-                    (32, 32) => Some("llvm.wasm.trunc.unsigned.i32.f32"),
-                    (32, 64) => Some("llvm.wasm.trunc.unsigned.i32.f64"),
-                    (64, 32) => Some("llvm.wasm.trunc.unsigned.i64.f32"),
-                    (64, 64) => Some("llvm.wasm.trunc.unsigned.i64.f64"),
-                    _ => None,
-                };
-                if let Some(name) = name {
-                    return self.call_intrinsic(name, &[val]);
+                if matches!((int_width, float_width), (32 | 64, 32 | 64)) {
+                    return self.call_intrinsic(
+                        "llvm.wasm.trunc.unsigned",
+                        &[dest_ty, src_ty],
+                        &[val],
+                    );
                 }
             }
         }
@@ -1003,15 +957,12 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
             if self.cx.type_kind(src_ty) != TypeKind::Vector {
                 let float_width = self.cx.float_width(src_ty);
                 let int_width = self.cx.int_width(dest_ty);
-                let name = match (int_width, float_width) {
-                    (32, 32) => Some("llvm.wasm.trunc.signed.i32.f32"),
-                    (32, 64) => Some("llvm.wasm.trunc.signed.i32.f64"),
-                    (64, 32) => Some("llvm.wasm.trunc.signed.i64.f32"),
-                    (64, 64) => Some("llvm.wasm.trunc.signed.i64.f64"),
-                    _ => None,
-                };
-                if let Some(name) = name {
-                    return self.call_intrinsic(name, &[val]);
+                if matches!((int_width, float_width), (32 | 64, 32 | 64)) {
+                    return self.call_intrinsic(
+                        "llvm.wasm.trunc.signed",
+                        &[dest_ty, src_ty],
+                        &[val],
+                    );
                 }
             }
         }
@@ -1084,22 +1035,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
             return None;
         }
 
-        let name = match (ty.is_signed(), ty.primitive_size(self.tcx).bits()) {
-            (true, 8) => "llvm.scmp.i8.i8",
-            (true, 16) => "llvm.scmp.i8.i16",
-            (true, 32) => "llvm.scmp.i8.i32",
-            (true, 64) => "llvm.scmp.i8.i64",
-            (true, 128) => "llvm.scmp.i8.i128",
-
-            (false, 8) => "llvm.ucmp.i8.i8",
-            (false, 16) => "llvm.ucmp.i8.i16",
-            (false, 32) => "llvm.ucmp.i8.i32",
-            (false, 64) => "llvm.ucmp.i8.i64",
-            (false, 128) => "llvm.ucmp.i8.i128",
+        let size = ty.primitive_size(self.tcx);
+        let name = if ty.is_signed() { "llvm.scmp" } else { "llvm.ucmp" };
 
-            _ => bug!("three-way compare unsupported for type {ty:?}"),
-        };
-        Some(self.call_intrinsic(name, &[lhs, rhs]))
+        Some(self.call_intrinsic(&name, &[self.type_i8(), self.type_ix(size.bits())], &[lhs, rhs]))
     }
 
     /* Miscellaneous instructions */
@@ -1385,11 +1324,11 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
     }
 
     fn lifetime_start(&mut self, ptr: &'ll Value, size: Size) {
-        self.call_lifetime_intrinsic("llvm.lifetime.start.p0i8", ptr, size);
+        self.call_lifetime_intrinsic("llvm.lifetime.start", ptr, size);
     }
 
     fn lifetime_end(&mut self, ptr: &'ll Value, size: Size) {
-        self.call_lifetime_intrinsic("llvm.lifetime.end.p0i8", ptr, size);
+        self.call_lifetime_intrinsic("llvm.lifetime.end", ptr, size);
     }
 
     fn call(
@@ -1454,7 +1393,7 @@ impl<'ll> StaticBuilderMethods for Builder<'_, 'll, '_> {
         // Forward to the `get_static` method of `CodegenCx`
         let global = self.cx().get_static(def_id);
         if self.cx().tcx.is_thread_local_static(def_id) {
-            let pointer = self.call_intrinsic("llvm.threadlocal.address", &[global]);
+            let pointer = self.call_intrinsic("llvm.threadlocal.address", &[], &[global]);
             // Cast to default address space if globals are in a different addrspace
             self.pointercast(pointer, self.type_ptr())
         } else {
@@ -1649,8 +1588,13 @@ impl<'a, 'll, CX: Borrow<SCx<'ll>>> GenericBuilder<'a, 'll, CX> {
 }
 
 impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
-    pub(crate) fn call_intrinsic(&mut self, intrinsic: &str, args: &[&'ll Value]) -> &'ll Value {
-        let (ty, f) = self.cx.get_intrinsic(intrinsic);
+    pub(crate) fn call_intrinsic(
+        &mut self,
+        base_name: &str,
+        type_params: &[&'ll Type],
+        args: &[&'ll Value],
+    ) -> &'ll Value {
+        let (ty, f) = self.cx.get_intrinsic(base_name, type_params);
         self.call(ty, None, None, f, args, None, None)
     }
 
@@ -1664,7 +1608,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
             return;
         }
 
-        self.call_intrinsic(intrinsic, &[self.cx.const_u64(size), ptr]);
+        self.call_intrinsic(intrinsic, &[self.type_ptr()], &[self.cx.const_u64(size), ptr]);
     }
 }
 impl<'a, 'll, CX: Borrow<SCx<'ll>>> GenericBuilder<'a, 'll, CX> {
@@ -1689,31 +1633,6 @@ impl<'a, 'll, CX: Borrow<SCx<'ll>>> GenericBuilder<'a, 'll, CX> {
     }
 }
 impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
-    fn fptoint_sat(&mut self, signed: bool, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value {
-        let src_ty = self.cx.val_ty(val);
-        let (float_ty, int_ty, vector_length) = if self.cx.type_kind(src_ty) == TypeKind::Vector {
-            assert_eq!(self.cx.vector_length(src_ty), self.cx.vector_length(dest_ty));
-            (
-                self.cx.element_type(src_ty),
-                self.cx.element_type(dest_ty),
-                Some(self.cx.vector_length(src_ty)),
-            )
-        } else {
-            (src_ty, dest_ty, None)
-        };
-        let float_width = self.cx.float_width(float_ty);
-        let int_width = self.cx.int_width(int_ty);
-
-        let instr = if signed { "fptosi" } else { "fptoui" };
-        let name = if let Some(vector_length) = vector_length {
-            format!("llvm.{instr}.sat.v{vector_length}i{int_width}.v{vector_length}f{float_width}")
-        } else {
-            format!("llvm.{instr}.sat.i{int_width}.f{float_width}")
-        };
-        let f = self.declare_cfn(&name, llvm::UnnamedAddr::No, self.type_func(&[src_ty], dest_ty));
-        self.call(self.type_func(&[src_ty], dest_ty), None, None, f, &[val], None, None)
-    }
-
     pub(crate) fn landing_pad(
         &mut self,
         ty: &'ll Type,
@@ -1819,7 +1738,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
             // llvm.type.test intrinsic. The LowerTypeTests link-time optimization pass replaces
             // calls to this intrinsic with code to test type membership.
             let typeid = self.get_metadata_value(typeid_metadata);
-            let cond = self.call_intrinsic("llvm.type.test", &[llfn, typeid]);
+            let cond = self.call_intrinsic("llvm.type.test", &[], &[llfn, typeid]);
             let bb_pass = self.append_sibling_block("type_test.pass");
             let bb_fail = self.append_sibling_block("type_test.fail");
             self.cond_br(cond, bb_pass, bb_fail);
@@ -1887,7 +1806,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
         num_counters: &'ll Value,
         index: &'ll Value,
     ) {
-        self.call_intrinsic("llvm.instrprof.increment", &[fn_name, hash, num_counters, index]);
+        self.call_intrinsic("llvm.instrprof.increment", &[], &[fn_name, hash, num_counters, index]);
     }
 
     /// Emits a call to `llvm.instrprof.mcdc.parameters`.
@@ -1906,7 +1825,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
         hash: &'ll Value,
         bitmap_bits: &'ll Value,
     ) {
-        self.call_intrinsic("llvm.instrprof.mcdc.parameters", &[fn_name, hash, bitmap_bits]);
+        self.call_intrinsic("llvm.instrprof.mcdc.parameters", &[], &[fn_name, hash, bitmap_bits]);
     }
 
     #[instrument(level = "debug", skip(self))]
@@ -1918,7 +1837,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
         mcdc_temp: &'ll Value,
     ) {
         let args = &[fn_name, hash, bitmap_index, mcdc_temp];
-        self.call_intrinsic("llvm.instrprof.mcdc.tvbitmap.update", args);
+        self.call_intrinsic("llvm.instrprof.mcdc.tvbitmap.update", &[], args);
     }
 
     #[instrument(level = "debug", skip(self))]
diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs
index 8d6e1d8941b..f36f3b2b16b 100644
--- a/compiler/rustc_codegen_llvm/src/context.rs
+++ b/compiler/rustc_codegen_llvm/src/context.rs
@@ -137,7 +137,8 @@ pub(crate) struct FullCx<'ll, 'tcx> {
     eh_catch_typeinfo: Cell<Option<&'ll Value>>,
     pub rust_try_fn: Cell<Option<(&'ll Type, &'ll Value)>>,
 
-    intrinsics: RefCell<FxHashMap<&'static str, (&'ll Type, &'ll Value)>>,
+    intrinsics:
+        RefCell<FxHashMap<(&'static str, SmallVec<[&'ll Type; 2]>), (&'ll Type, &'ll Value)>>,
 
     /// A counter that is used for generating local symbol names
     local_gen_sym_counter: Cell<usize>,
@@ -842,17 +843,24 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
 }
 
 impl<'ll> CodegenCx<'ll, '_> {
-    pub(crate) fn get_intrinsic(&self, key: &str) -> (&'ll Type, &'ll Value) {
-        if let Some(v) = self.intrinsics.borrow().get(key).cloned() {
-            return v;
+    pub(crate) fn get_intrinsic(
+        &self,
+        base_name: &str,
+        type_params: &[&'ll Type],
+    ) -> (&'ll Type, &'ll Value) {
+        if let Some(v) =
+            self.intrinsics.borrow().get(&(base_name, SmallVec::from_slice(type_params)))
+        {
+            return *v;
         }
 
-        self.declare_intrinsic(key).unwrap_or_else(|| bug!("unknown intrinsic '{}'", key))
+        self.declare_intrinsic(base_name, type_params)
     }
 
     fn insert_intrinsic(
         &self,
-        name: &'static str,
+        base_name: &'static str,
+        type_params: &[&'ll Type],
         args: Option<&[&'ll llvm::Type]>,
         ret: &'ll llvm::Type,
     ) -> (&'ll llvm::Type, &'ll llvm::Value) {
@@ -861,372 +869,153 @@ impl<'ll> CodegenCx<'ll, '_> {
         } else {
             self.type_variadic_func(&[], ret)
         };
-        let f = self.declare_cfn(name, llvm::UnnamedAddr::No, fn_ty);
-        self.intrinsics.borrow_mut().insert(name, (fn_ty, f));
+
+        let intrinsic = llvm::Intrinsic::lookup(base_name.as_bytes())
+            .expect("Unknown LLVM intrinsic `{base_name}`");
+
+        let full_name = if intrinsic.is_overloaded() {
+            &intrinsic.overloaded_name(self.llmod, type_params)
+        } else {
+            base_name
+        };
+
+        let f = self.declare_cfn(full_name, llvm::UnnamedAddr::No, fn_ty);
+        self.intrinsics
+            .borrow_mut()
+            .insert((base_name, SmallVec::from_slice(type_params)), (fn_ty, f));
         (fn_ty, f)
     }
 
-    fn declare_intrinsic(&self, key: &str) -> Option<(&'ll Type, &'ll Value)> {
+    fn declare_intrinsic(
+        &self,
+        base_name: &str,
+        type_params: &[&'ll Type],
+    ) -> (&'ll Type, &'ll Value) {
+        macro_rules! param {
+            ($index:literal) => {
+                type_params[$index]
+            };
+            ($other:expr) => {
+                $other
+            };
+        }
         macro_rules! ifn {
-            ($name:expr, fn() -> $ret:expr) => (
-                if key == $name {
-                    return Some(self.insert_intrinsic($name, Some(&[]), $ret));
-                }
-            );
             ($name:expr, fn(...) -> $ret:expr) => (
-                if key == $name {
-                    return Some(self.insert_intrinsic($name, None, $ret));
+                if base_name == $name {
+                    return self.insert_intrinsic($name, type_params, None, param!($ret));
                 }
             );
             ($name:expr, fn($($arg:expr),*) -> $ret:expr) => (
-                if key == $name {
-                    return Some(self.insert_intrinsic($name, Some(&[$($arg),*]), $ret));
+                if base_name == $name {
+                    return self.insert_intrinsic($name, type_params, Some(&[$(param!($arg)),*]), param!($ret));
                 }
             );
         }
         macro_rules! mk_struct {
-            ($($field_ty:expr),*) => (self.type_struct( &[$($field_ty),*], false))
+            ($($field_ty:expr),*) => (self.type_struct( &[$(param!($field_ty)),*], false))
         }
 
+        let same_width_vector = |index, element_ty| {
+            self.type_vector(element_ty, self.vector_length(type_params[index]) as u64)
+        };
+
         let ptr = self.type_ptr();
         let void = self.type_void();
         let i1 = self.type_i1();
-        let t_i8 = self.type_i8();
-        let t_i16 = self.type_i16();
         let t_i32 = self.type_i32();
         let t_i64 = self.type_i64();
-        let t_i128 = self.type_i128();
         let t_isize = self.type_isize();
-        let t_f16 = self.type_f16();
-        let t_f32 = self.type_f32();
-        let t_f64 = self.type_f64();
-        let t_f128 = self.type_f128();
         let t_metadata = self.type_metadata();
         let t_token = self.type_token();
 
         ifn!("llvm.wasm.get.exception", fn(t_token) -> ptr);
         ifn!("llvm.wasm.get.ehselector", fn(t_token) -> t_i32);
 
-        ifn!("llvm.wasm.trunc.unsigned.i32.f32", fn(t_f32) -> t_i32);
-        ifn!("llvm.wasm.trunc.unsigned.i32.f64", fn(t_f64) -> t_i32);
-        ifn!("llvm.wasm.trunc.unsigned.i64.f32", fn(t_f32) -> t_i64);
-        ifn!("llvm.wasm.trunc.unsigned.i64.f64", fn(t_f64) -> t_i64);
-        ifn!("llvm.wasm.trunc.signed.i32.f32", fn(t_f32) -> t_i32);
-        ifn!("llvm.wasm.trunc.signed.i32.f64", fn(t_f64) -> t_i32);
-        ifn!("llvm.wasm.trunc.signed.i64.f32", fn(t_f32) -> t_i64);
-        ifn!("llvm.wasm.trunc.signed.i64.f64", fn(t_f64) -> t_i64);
-
-        ifn!("llvm.fptosi.sat.i8.f32", fn(t_f32) -> t_i8);
-        ifn!("llvm.fptosi.sat.i16.f32", fn(t_f32) -> t_i16);
-        ifn!("llvm.fptosi.sat.i32.f32", fn(t_f32) -> t_i32);
-        ifn!("llvm.fptosi.sat.i64.f32", fn(t_f32) -> t_i64);
-        ifn!("llvm.fptosi.sat.i128.f32", fn(t_f32) -> t_i128);
-        ifn!("llvm.fptosi.sat.i8.f64", fn(t_f64) -> t_i8);
-        ifn!("llvm.fptosi.sat.i16.f64", fn(t_f64) -> t_i16);
-        ifn!("llvm.fptosi.sat.i32.f64", fn(t_f64) -> t_i32);
-        ifn!("llvm.fptosi.sat.i64.f64", fn(t_f64) -> t_i64);
-        ifn!("llvm.fptosi.sat.i128.f64", fn(t_f64) -> t_i128);
-
-        ifn!("llvm.fptoui.sat.i8.f32", fn(t_f32) -> t_i8);
-        ifn!("llvm.fptoui.sat.i16.f32", fn(t_f32) -> t_i16);
-        ifn!("llvm.fptoui.sat.i32.f32", fn(t_f32) -> t_i32);
-        ifn!("llvm.fptoui.sat.i64.f32", fn(t_f32) -> t_i64);
-        ifn!("llvm.fptoui.sat.i128.f32", fn(t_f32) -> t_i128);
-        ifn!("llvm.fptoui.sat.i8.f64", fn(t_f64) -> t_i8);
-        ifn!("llvm.fptoui.sat.i16.f64", fn(t_f64) -> t_i16);
-        ifn!("llvm.fptoui.sat.i32.f64", fn(t_f64) -> t_i32);
-        ifn!("llvm.fptoui.sat.i64.f64", fn(t_f64) -> t_i64);
-        ifn!("llvm.fptoui.sat.i128.f64", fn(t_f64) -> t_i128);
+        ifn!("llvm.wasm.trunc.unsigned", fn(1) -> 0);
+        ifn!("llvm.wasm.trunc.signed", fn(1) -> 0);
+        ifn!("llvm.fptosi.sat", fn(1) -> 0);
+        ifn!("llvm.fptoui.sat", fn(1) -> 0);
 
         ifn!("llvm.trap", fn() -> void);
         ifn!("llvm.debugtrap", fn() -> void);
         ifn!("llvm.frameaddress", fn(t_i32) -> ptr);
 
-        ifn!("llvm.powi.f16.i32", fn(t_f16, t_i32) -> t_f16);
-        ifn!("llvm.powi.f32.i32", fn(t_f32, t_i32) -> t_f32);
-        ifn!("llvm.powi.f64.i32", fn(t_f64, t_i32) -> t_f64);
-        ifn!("llvm.powi.f128.i32", fn(t_f128, t_i32) -> t_f128);
-
-        ifn!("llvm.pow.f16", fn(t_f16, t_f16) -> t_f16);
-        ifn!("llvm.pow.f32", fn(t_f32, t_f32) -> t_f32);
-        ifn!("llvm.pow.f64", fn(t_f64, t_f64) -> t_f64);
-        ifn!("llvm.pow.f128", fn(t_f128, t_f128) -> t_f128);
-
-        ifn!("llvm.sqrt.f16", fn(t_f16) -> t_f16);
-        ifn!("llvm.sqrt.f32", fn(t_f32) -> t_f32);
-        ifn!("llvm.sqrt.f64", fn(t_f64) -> t_f64);
-        ifn!("llvm.sqrt.f128", fn(t_f128) -> t_f128);
-
-        ifn!("llvm.sin.f16", fn(t_f16) -> t_f16);
-        ifn!("llvm.sin.f32", fn(t_f32) -> t_f32);
-        ifn!("llvm.sin.f64", fn(t_f64) -> t_f64);
-        ifn!("llvm.sin.f128", fn(t_f128) -> t_f128);
-
-        ifn!("llvm.cos.f16", fn(t_f16) -> t_f16);
-        ifn!("llvm.cos.f32", fn(t_f32) -> t_f32);
-        ifn!("llvm.cos.f64", fn(t_f64) -> t_f64);
-        ifn!("llvm.cos.f128", fn(t_f128) -> t_f128);
-
-        ifn!("llvm.exp.f16", fn(t_f16) -> t_f16);
-        ifn!("llvm.exp.f32", fn(t_f32) -> t_f32);
-        ifn!("llvm.exp.f64", fn(t_f64) -> t_f64);
-        ifn!("llvm.exp.f128", fn(t_f128) -> t_f128);
-
-        ifn!("llvm.exp2.f16", fn(t_f16) -> t_f16);
-        ifn!("llvm.exp2.f32", fn(t_f32) -> t_f32);
-        ifn!("llvm.exp2.f64", fn(t_f64) -> t_f64);
-        ifn!("llvm.exp2.f128", fn(t_f128) -> t_f128);
-
-        ifn!("llvm.log.f16", fn(t_f16) -> t_f16);
-        ifn!("llvm.log.f32", fn(t_f32) -> t_f32);
-        ifn!("llvm.log.f64", fn(t_f64) -> t_f64);
-        ifn!("llvm.log.f128", fn(t_f128) -> t_f128);
-
-        ifn!("llvm.log10.f16", fn(t_f16) -> t_f16);
-        ifn!("llvm.log10.f32", fn(t_f32) -> t_f32);
-        ifn!("llvm.log10.f64", fn(t_f64) -> t_f64);
-        ifn!("llvm.log10.f128", fn(t_f128) -> t_f128);
-
-        ifn!("llvm.log2.f16", fn(t_f16) -> t_f16);
-        ifn!("llvm.log2.f32", fn(t_f32) -> t_f32);
-        ifn!("llvm.log2.f64", fn(t_f64) -> t_f64);
-        ifn!("llvm.log2.f128", fn(t_f128) -> t_f128);
-
-        ifn!("llvm.fma.f16", fn(t_f16, t_f16, t_f16) -> t_f16);
-        ifn!("llvm.fma.f32", fn(t_f32, t_f32, t_f32) -> t_f32);
-        ifn!("llvm.fma.f64", fn(t_f64, t_f64, t_f64) -> t_f64);
-        ifn!("llvm.fma.f128", fn(t_f128, t_f128, t_f128) -> t_f128);
-
-        ifn!("llvm.fmuladd.f16", fn(t_f16, t_f16, t_f16) -> t_f16);
-        ifn!("llvm.fmuladd.f32", fn(t_f32, t_f32, t_f32) -> t_f32);
-        ifn!("llvm.fmuladd.f64", fn(t_f64, t_f64, t_f64) -> t_f64);
-        ifn!("llvm.fmuladd.f128", fn(t_f128, t_f128, t_f128) -> t_f128);
-
-        ifn!("llvm.fabs.f16", fn(t_f16) -> t_f16);
-        ifn!("llvm.fabs.f32", fn(t_f32) -> t_f32);
-        ifn!("llvm.fabs.f64", fn(t_f64) -> t_f64);
-        ifn!("llvm.fabs.f128", fn(t_f128) -> t_f128);
-
-        ifn!("llvm.minnum.f16", fn(t_f16, t_f16) -> t_f16);
-        ifn!("llvm.minnum.f32", fn(t_f32, t_f32) -> t_f32);
-        ifn!("llvm.minnum.f64", fn(t_f64, t_f64) -> t_f64);
-        ifn!("llvm.minnum.f128", fn(t_f128, t_f128) -> t_f128);
-
-        ifn!("llvm.minimum.f16", fn(t_f16, t_f16) -> t_f16);
-        ifn!("llvm.minimum.f32", fn(t_f32, t_f32) -> t_f32);
-        ifn!("llvm.minimum.f64", fn(t_f64, t_f64) -> t_f64);
-        // There are issues on x86_64 and aarch64 with the f128 variant.
-        //  - https://github.com/llvm/llvm-project/issues/139380
-        //  - https://github.com/llvm/llvm-project/issues/139381
-        // ifn!("llvm.minimum.f128", fn(t_f128, t_f128) -> t_f128);
-
-        ifn!("llvm.maxnum.f16", fn(t_f16, t_f16) -> t_f16);
-        ifn!("llvm.maxnum.f32", fn(t_f32, t_f32) -> t_f32);
-        ifn!("llvm.maxnum.f64", fn(t_f64, t_f64) -> t_f64);
-        ifn!("llvm.maxnum.f128", fn(t_f128, t_f128) -> t_f128);
-
-        ifn!("llvm.maximum.f16", fn(t_f16, t_f16) -> t_f16);
-        ifn!("llvm.maximum.f32", fn(t_f32, t_f32) -> t_f32);
-        ifn!("llvm.maximum.f64", fn(t_f64, t_f64) -> t_f64);
-        // There are issues on x86_64 and aarch64 with the f128 variant.
-        //  - https://github.com/llvm/llvm-project/issues/139380
-        //  - https://github.com/llvm/llvm-project/issues/139381
-        // ifn!("llvm.maximum.f128", fn(t_f128, t_f128) -> t_f128);
-
-        ifn!("llvm.floor.f16", fn(t_f16) -> t_f16);
-        ifn!("llvm.floor.f32", fn(t_f32) -> t_f32);
-        ifn!("llvm.floor.f64", fn(t_f64) -> t_f64);
-        ifn!("llvm.floor.f128", fn(t_f128) -> t_f128);
-
-        ifn!("llvm.ceil.f16", fn(t_f16) -> t_f16);
-        ifn!("llvm.ceil.f32", fn(t_f32) -> t_f32);
-        ifn!("llvm.ceil.f64", fn(t_f64) -> t_f64);
-        ifn!("llvm.ceil.f128", fn(t_f128) -> t_f128);
-
-        ifn!("llvm.trunc.f16", fn(t_f16) -> t_f16);
-        ifn!("llvm.trunc.f32", fn(t_f32) -> t_f32);
-        ifn!("llvm.trunc.f64", fn(t_f64) -> t_f64);
-        ifn!("llvm.trunc.f128", fn(t_f128) -> t_f128);
-
-        ifn!("llvm.copysign.f16", fn(t_f16, t_f16) -> t_f16);
-        ifn!("llvm.copysign.f32", fn(t_f32, t_f32) -> t_f32);
-        ifn!("llvm.copysign.f64", fn(t_f64, t_f64) -> t_f64);
-        ifn!("llvm.copysign.f128", fn(t_f128, t_f128) -> t_f128);
-
-        ifn!("llvm.round.f16", fn(t_f16) -> t_f16);
-        ifn!("llvm.round.f32", fn(t_f32) -> t_f32);
-        ifn!("llvm.round.f64", fn(t_f64) -> t_f64);
-        ifn!("llvm.round.f128", fn(t_f128) -> t_f128);
-
-        ifn!("llvm.roundeven.f16", fn(t_f16) -> t_f16);
-        ifn!("llvm.roundeven.f32", fn(t_f32) -> t_f32);
-        ifn!("llvm.roundeven.f64", fn(t_f64) -> t_f64);
-        ifn!("llvm.roundeven.f128", fn(t_f128) -> t_f128);
-
-        ifn!("llvm.rint.f16", fn(t_f16) -> t_f16);
-        ifn!("llvm.rint.f32", fn(t_f32) -> t_f32);
-        ifn!("llvm.rint.f64", fn(t_f64) -> t_f64);
-        ifn!("llvm.rint.f128", fn(t_f128) -> t_f128);
-
-        ifn!("llvm.nearbyint.f16", fn(t_f16) -> t_f16);
-        ifn!("llvm.nearbyint.f32", fn(t_f32) -> t_f32);
-        ifn!("llvm.nearbyint.f64", fn(t_f64) -> t_f64);
-        ifn!("llvm.nearbyint.f128", fn(t_f128) -> t_f128);
-
-        ifn!("llvm.ctpop.i8", fn(t_i8) -> t_i8);
-        ifn!("llvm.ctpop.i16", fn(t_i16) -> t_i16);
-        ifn!("llvm.ctpop.i32", fn(t_i32) -> t_i32);
-        ifn!("llvm.ctpop.i64", fn(t_i64) -> t_i64);
-        ifn!("llvm.ctpop.i128", fn(t_i128) -> t_i128);
-
-        ifn!("llvm.ctlz.i8", fn(t_i8, i1) -> t_i8);
-        ifn!("llvm.ctlz.i16", fn(t_i16, i1) -> t_i16);
-        ifn!("llvm.ctlz.i32", fn(t_i32, i1) -> t_i32);
-        ifn!("llvm.ctlz.i64", fn(t_i64, i1) -> t_i64);
-        ifn!("llvm.ctlz.i128", fn(t_i128, i1) -> t_i128);
-
-        ifn!("llvm.cttz.i8", fn(t_i8, i1) -> t_i8);
-        ifn!("llvm.cttz.i16", fn(t_i16, i1) -> t_i16);
-        ifn!("llvm.cttz.i32", fn(t_i32, i1) -> t_i32);
-        ifn!("llvm.cttz.i64", fn(t_i64, i1) -> t_i64);
-        ifn!("llvm.cttz.i128", fn(t_i128, i1) -> t_i128);
-
-        ifn!("llvm.bswap.i16", fn(t_i16) -> t_i16);
-        ifn!("llvm.bswap.i32", fn(t_i32) -> t_i32);
-        ifn!("llvm.bswap.i64", fn(t_i64) -> t_i64);
-        ifn!("llvm.bswap.i128", fn(t_i128) -> t_i128);
-
-        ifn!("llvm.bitreverse.i8", fn(t_i8) -> t_i8);
-        ifn!("llvm.bitreverse.i16", fn(t_i16) -> t_i16);
-        ifn!("llvm.bitreverse.i32", fn(t_i32) -> t_i32);
-        ifn!("llvm.bitreverse.i64", fn(t_i64) -> t_i64);
-        ifn!("llvm.bitreverse.i128", fn(t_i128) -> t_i128);
-
-        ifn!("llvm.fshl.i8", fn(t_i8, t_i8, t_i8) -> t_i8);
-        ifn!("llvm.fshl.i16", fn(t_i16, t_i16, t_i16) -> t_i16);
-        ifn!("llvm.fshl.i32", fn(t_i32, t_i32, t_i32) -> t_i32);
-        ifn!("llvm.fshl.i64", fn(t_i64, t_i64, t_i64) -> t_i64);
-        ifn!("llvm.fshl.i128", fn(t_i128, t_i128, t_i128) -> t_i128);
-
-        ifn!("llvm.fshr.i8", fn(t_i8, t_i8, t_i8) -> t_i8);
-        ifn!("llvm.fshr.i16", fn(t_i16, t_i16, t_i16) -> t_i16);
-        ifn!("llvm.fshr.i32", fn(t_i32, t_i32, t_i32) -> t_i32);
-        ifn!("llvm.fshr.i64", fn(t_i64, t_i64, t_i64) -> t_i64);
-        ifn!("llvm.fshr.i128", fn(t_i128, t_i128, t_i128) -> t_i128);
-
-        ifn!("llvm.sadd.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct! {t_i8, i1});
-        ifn!("llvm.sadd.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct! {t_i16, i1});
-        ifn!("llvm.sadd.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct! {t_i32, i1});
-        ifn!("llvm.sadd.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct! {t_i64, i1});
-        ifn!("llvm.sadd.with.overflow.i128", fn(t_i128, t_i128) -> mk_struct! {t_i128, i1});
-
-        ifn!("llvm.uadd.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct! {t_i8, i1});
-        ifn!("llvm.uadd.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct! {t_i16, i1});
-        ifn!("llvm.uadd.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct! {t_i32, i1});
-        ifn!("llvm.uadd.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct! {t_i64, i1});
-        ifn!("llvm.uadd.with.overflow.i128", fn(t_i128, t_i128) -> mk_struct! {t_i128, i1});
-
-        ifn!("llvm.ssub.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct! {t_i8, i1});
-        ifn!("llvm.ssub.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct! {t_i16, i1});
-        ifn!("llvm.ssub.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct! {t_i32, i1});
-        ifn!("llvm.ssub.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct! {t_i64, i1});
-        ifn!("llvm.ssub.with.overflow.i128", fn(t_i128, t_i128) -> mk_struct! {t_i128, i1});
-
-        ifn!("llvm.usub.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct! {t_i8, i1});
-        ifn!("llvm.usub.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct! {t_i16, i1});
-        ifn!("llvm.usub.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct! {t_i32, i1});
-        ifn!("llvm.usub.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct! {t_i64, i1});
-        ifn!("llvm.usub.with.overflow.i128", fn(t_i128, t_i128) -> mk_struct! {t_i128, i1});
-
-        ifn!("llvm.smul.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct! {t_i8, i1});
-        ifn!("llvm.smul.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct! {t_i16, i1});
-        ifn!("llvm.smul.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct! {t_i32, i1});
-        ifn!("llvm.smul.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct! {t_i64, i1});
-        ifn!("llvm.smul.with.overflow.i128", fn(t_i128, t_i128) -> mk_struct! {t_i128, i1});
-
-        ifn!("llvm.umul.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct! {t_i8, i1});
-        ifn!("llvm.umul.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct! {t_i16, i1});
-        ifn!("llvm.umul.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct! {t_i32, i1});
-        ifn!("llvm.umul.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct! {t_i64, i1});
-        ifn!("llvm.umul.with.overflow.i128", fn(t_i128, t_i128) -> mk_struct! {t_i128, i1});
-
-        ifn!("llvm.sadd.sat.i8", fn(t_i8, t_i8) -> t_i8);
-        ifn!("llvm.sadd.sat.i16", fn(t_i16, t_i16) -> t_i16);
-        ifn!("llvm.sadd.sat.i32", fn(t_i32, t_i32) -> t_i32);
-        ifn!("llvm.sadd.sat.i64", fn(t_i64, t_i64) -> t_i64);
-        ifn!("llvm.sadd.sat.i128", fn(t_i128, t_i128) -> t_i128);
-
-        ifn!("llvm.uadd.sat.i8", fn(t_i8, t_i8) -> t_i8);
-        ifn!("llvm.uadd.sat.i16", fn(t_i16, t_i16) -> t_i16);
-        ifn!("llvm.uadd.sat.i32", fn(t_i32, t_i32) -> t_i32);
-        ifn!("llvm.uadd.sat.i64", fn(t_i64, t_i64) -> t_i64);
-        ifn!("llvm.uadd.sat.i128", fn(t_i128, t_i128) -> t_i128);
-
-        ifn!("llvm.ssub.sat.i8", fn(t_i8, t_i8) -> t_i8);
-        ifn!("llvm.ssub.sat.i16", fn(t_i16, t_i16) -> t_i16);
-        ifn!("llvm.ssub.sat.i32", fn(t_i32, t_i32) -> t_i32);
-        ifn!("llvm.ssub.sat.i64", fn(t_i64, t_i64) -> t_i64);
-        ifn!("llvm.ssub.sat.i128", fn(t_i128, t_i128) -> t_i128);
-
-        ifn!("llvm.usub.sat.i8", fn(t_i8, t_i8) -> t_i8);
-        ifn!("llvm.usub.sat.i16", fn(t_i16, t_i16) -> t_i16);
-        ifn!("llvm.usub.sat.i32", fn(t_i32, t_i32) -> t_i32);
-        ifn!("llvm.usub.sat.i64", fn(t_i64, t_i64) -> t_i64);
-        ifn!("llvm.usub.sat.i128", fn(t_i128, t_i128) -> t_i128);
-
-        ifn!("llvm.scmp.i8.i8", fn(t_i8, t_i8) -> t_i8);
-        ifn!("llvm.scmp.i8.i16", fn(t_i16, t_i16) -> t_i8);
-        ifn!("llvm.scmp.i8.i32", fn(t_i32, t_i32) -> t_i8);
-        ifn!("llvm.scmp.i8.i64", fn(t_i64, t_i64) -> t_i8);
-        ifn!("llvm.scmp.i8.i128", fn(t_i128, t_i128) -> t_i8);
-
-        ifn!("llvm.ucmp.i8.i8", fn(t_i8, t_i8) -> t_i8);
-        ifn!("llvm.ucmp.i8.i16", fn(t_i16, t_i16) -> t_i8);
-        ifn!("llvm.ucmp.i8.i32", fn(t_i32, t_i32) -> t_i8);
-        ifn!("llvm.ucmp.i8.i64", fn(t_i64, t_i64) -> t_i8);
-        ifn!("llvm.ucmp.i8.i128", fn(t_i128, t_i128) -> t_i8);
-
-        ifn!("llvm.lifetime.start.p0i8", fn(t_i64, ptr) -> void);
-        ifn!("llvm.lifetime.end.p0i8", fn(t_i64, ptr) -> void);
-
-        // FIXME: This is an infinitesimally small portion of the types you can
-        // pass to this intrinsic, if we can ever lazily register intrinsics we
-        // should register these when they're used, that way any type can be
-        // passed.
-        ifn!("llvm.is.constant.i1", fn(i1) -> i1);
-        ifn!("llvm.is.constant.i8", fn(t_i8) -> i1);
-        ifn!("llvm.is.constant.i16", fn(t_i16) -> i1);
-        ifn!("llvm.is.constant.i32", fn(t_i32) -> i1);
-        ifn!("llvm.is.constant.i64", fn(t_i64) -> i1);
-        ifn!("llvm.is.constant.i128", fn(t_i128) -> i1);
-        ifn!("llvm.is.constant.isize", fn(t_isize) -> i1);
-        ifn!("llvm.is.constant.f16", fn(t_f16) -> i1);
-        ifn!("llvm.is.constant.f32", fn(t_f32) -> i1);
-        ifn!("llvm.is.constant.f64", fn(t_f64) -> i1);
-        ifn!("llvm.is.constant.f128", fn(t_f128) -> i1);
-        ifn!("llvm.is.constant.ptr", fn(ptr) -> i1);
-
-        ifn!("llvm.expect.i1", fn(i1, i1) -> i1);
-        ifn!("llvm.eh.typeid.for", fn(ptr) -> t_i32);
+        ifn!("llvm.powi", fn(0, 1) -> 0);
+        ifn!("llvm.pow", fn(0, 0) -> 0);
+        ifn!("llvm.sqrt", fn(0) -> 0);
+        ifn!("llvm.sin", fn(0) -> 0);
+        ifn!("llvm.cos", fn(0) -> 0);
+        ifn!("llvm.exp", fn(0) -> 0);
+        ifn!("llvm.exp2", fn(0) -> 0);
+        ifn!("llvm.log", fn(0) -> 0);
+        ifn!("llvm.log10", fn(0) -> 0);
+        ifn!("llvm.log2", fn(0) -> 0);
+        ifn!("llvm.fma", fn(0, 0, 0) -> 0);
+        ifn!("llvm.fmuladd", fn(0, 0, 0) -> 0);
+        ifn!("llvm.fabs", fn(0) -> 0);
+        ifn!("llvm.minnum", fn(0, 0) -> 0);
+        ifn!("llvm.minimum", fn(0, 0) -> 0);
+        ifn!("llvm.maxnum", fn(0, 0) -> 0);
+        ifn!("llvm.maximum", fn(0, 0) -> 0);
+        ifn!("llvm.floor", fn(0) -> 0);
+        ifn!("llvm.ceil", fn(0) -> 0);
+        ifn!("llvm.trunc", fn(0) -> 0);
+        ifn!("llvm.copysign", fn(0, 0) -> 0);
+        ifn!("llvm.round", fn(0) -> 0);
+        ifn!("llvm.rint", fn(0) -> 0);
+        ifn!("llvm.nearbyint", fn(0) -> 0);
+
+        ifn!("llvm.ctpop", fn(0) -> 0);
+        ifn!("llvm.ctlz", fn(0, i1) -> 0);
+        ifn!("llvm.cttz", fn(0, i1) -> 0);
+        ifn!("llvm.bswap", fn(0) -> 0);
+        ifn!("llvm.bitreverse", fn(0) -> 0);
+        ifn!("llvm.fshl", fn(0, 0, 0) -> 0);
+        ifn!("llvm.fshr", fn(0, 0, 0) -> 0);
+
+        ifn!("llvm.sadd.with.overflow", fn(0, 0) -> mk_struct! {0, i1});
+        ifn!("llvm.uadd.with.overflow", fn(0, 0) -> mk_struct! {0, i1});
+        ifn!("llvm.ssub.with.overflow", fn(0, 0) -> mk_struct! {0, i1});
+        ifn!("llvm.usub.with.overflow", fn(0, 0) -> mk_struct! {0, i1});
+        ifn!("llvm.smul.with.overflow", fn(0, 0) -> mk_struct! {0, i1});
+        ifn!("llvm.umul.with.overflow", fn(0, 0) -> mk_struct! {0, i1});
+
+        ifn!("llvm.sadd.sat", fn(0, 0) -> 0);
+        ifn!("llvm.uadd.sat", fn(0, 0) -> 0);
+        ifn!("llvm.ssub.sat", fn(0, 0) -> 0);
+        ifn!("llvm.usub.sat", fn(0, 0) -> 0);
+
+        ifn!("llvm.scmp", fn(1, 1) -> 0);
+        ifn!("llvm.ucmp", fn(1, 1) -> 0);
+
+        ifn!("llvm.lifetime.start", fn(t_i64, 0) -> void);
+        ifn!("llvm.lifetime.end", fn(t_i64, 0) -> void);
+
+        ifn!("llvm.is.constant", fn(0) -> i1);
+        ifn!("llvm.expect", fn(0, 0) -> 0);
+
+        ifn!("llvm.eh.typeid.for", fn(0) -> t_i32);
         ifn!("llvm.localescape", fn(...) -> void);
         ifn!("llvm.localrecover", fn(ptr, ptr, t_i32) -> ptr);
-        ifn!("llvm.x86.seh.recoverfp", fn(ptr, ptr) -> ptr);
 
         ifn!("llvm.assume", fn(i1) -> void);
-        ifn!("llvm.prefetch", fn(ptr, t_i32, t_i32, t_i32) -> void);
+        ifn!("llvm.prefetch", fn(0, t_i32, t_i32, t_i32) -> void);
 
         // This isn't an "LLVM intrinsic", but LLVM's optimization passes
         // recognize it like one (including turning it into `bcmp` sometimes)
         // and we use it to implement intrinsics like `raw_eq` and `compare_bytes`
-        match self.sess().target.arch.as_ref() {
-            "avr" | "msp430" => ifn!("memcmp", fn(ptr, ptr, t_isize) -> t_i16),
-            _ => ifn!("memcmp", fn(ptr, ptr, t_isize) -> t_i32),
+        if base_name == "memcmp" {
+            let fn_ty = self.type_func(&[ptr, ptr, t_isize], self.type_int());
+            let f = self.declare_cfn("memcmp", llvm::UnnamedAddr::No, fn_ty);
+            self.intrinsics.borrow_mut().insert(("memcmp", SmallVec::new()), (fn_ty, f));
+
+            return (fn_ty, f);
         }
 
         // variadic intrinsics
-        ifn!("llvm.va_start", fn(ptr) -> void);
-        ifn!("llvm.va_end", fn(ptr) -> void);
-        ifn!("llvm.va_copy", fn(ptr, ptr) -> void);
+        ifn!("llvm.va_start", fn(0) -> void);
+        ifn!("llvm.va_end", fn(0) -> void);
+        ifn!("llvm.va_copy", fn(0, 0) -> void);
 
         if self.sess().instrument_coverage() {
             ifn!("llvm.instrprof.increment", fn(ptr, t_i64, t_i32, t_i32) -> void);
@@ -1238,14 +1027,19 @@ impl<'ll> CodegenCx<'ll, '_> {
         ifn!("llvm.type.checked.load", fn(ptr, t_i32, t_metadata) -> mk_struct! {ptr, i1});
 
         if self.sess().opts.debuginfo != DebugInfo::None {
-            ifn!("llvm.dbg.declare", fn(t_metadata, t_metadata) -> void);
-            ifn!("llvm.dbg.value", fn(t_metadata, t_i64, t_metadata) -> void);
+            ifn!("llvm.dbg.declare", fn(t_metadata, t_metadata, t_metadata) -> void);
+            ifn!("llvm.dbg.value", fn(t_metadata, t_metadata, t_metadata) -> void);
         }
 
-        ifn!("llvm.ptrmask", fn(ptr, t_isize) -> ptr);
+        ifn!("llvm.ptrmask", fn(0, 1) -> 0);
         ifn!("llvm.threadlocal.address", fn(ptr) -> ptr);
 
-        None
+        ifn!("llvm.masked.load", fn(1, t_i32, same_width_vector(0, i1), 0) -> 0);
+        ifn!("llvm.masked.store", fn(0, 1, t_i32, same_width_vector(0, i1)) -> void);
+        ifn!("llvm.masked.gather", fn(1, t_i32, same_width_vector(0, i1), 0) -> 0);
+        ifn!("llvm.masked.scatter", fn(0, 1, t_i32, same_width_vector(0, i1)) -> void);
+
+        bug!("Unknown intrinsic: `{base_name}`")
     }
 
     pub(crate) fn eh_catch_typeinfo(&self) -> &'ll Value {
diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs
index eaafc680712..8bc74fbec7e 100644
--- a/compiler/rustc_codegen_llvm/src/errors.rs
+++ b/compiler/rustc_codegen_llvm/src/errors.rs
@@ -24,23 +24,6 @@ pub(crate) struct UnknownCTargetFeature<'a> {
     pub rust_feature: PossibleFeature<'a>,
 }
 
-#[derive(Diagnostic)]
-#[diag(codegen_llvm_unstable_ctarget_feature)]
-#[note]
-pub(crate) struct UnstableCTargetFeature<'a> {
-    pub feature: &'a str,
-}
-
-#[derive(Diagnostic)]
-#[diag(codegen_llvm_forbidden_ctarget_feature)]
-#[note]
-#[note(codegen_llvm_forbidden_ctarget_feature_issue)]
-pub(crate) struct ForbiddenCTargetFeature<'a> {
-    pub feature: &'a str,
-    pub enabled: &'a str,
-    pub reason: &'a str,
-}
-
 #[derive(Subdiagnostic)]
 pub(crate) enum PossibleFeature<'a> {
     #[help(codegen_llvm_possible_feature)]
diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs
index 10697b9a71f..497c31706ec 100644
--- a/compiler/rustc_codegen_llvm/src/intrinsic.rs
+++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs
@@ -15,7 +15,7 @@ use rustc_middle::ty::{self, GenericArgsRef, Ty};
 use rustc_middle::{bug, span_bug};
 use rustc_span::{Span, Symbol, sym};
 use rustc_symbol_mangling::mangle_internal_symbol;
-use rustc_target::spec::{HasTargetSpec, PanicStrategy};
+use rustc_target::spec::PanicStrategy;
 use tracing::debug;
 
 use crate::abi::FnAbiLlvmExt;
@@ -27,137 +27,142 @@ use crate::type_of::LayoutLlvmExt;
 use crate::va_arg::emit_va_arg;
 use crate::value::Value;
 
-fn get_simple_intrinsic<'ll>(
-    cx: &CodegenCx<'ll, '_>,
+fn call_simple_intrinsic<'ll, 'tcx>(
+    bx: &mut Builder<'_, 'll, 'tcx>,
     name: Symbol,
-) -> Option<(&'ll Type, &'ll Value)> {
-    let llvm_name = match name {
-        sym::sqrtf16 => "llvm.sqrt.f16",
-        sym::sqrtf32 => "llvm.sqrt.f32",
-        sym::sqrtf64 => "llvm.sqrt.f64",
-        sym::sqrtf128 => "llvm.sqrt.f128",
-
-        sym::powif16 => "llvm.powi.f16.i32",
-        sym::powif32 => "llvm.powi.f32.i32",
-        sym::powif64 => "llvm.powi.f64.i32",
-        sym::powif128 => "llvm.powi.f128.i32",
-
-        sym::sinf16 => "llvm.sin.f16",
-        sym::sinf32 => "llvm.sin.f32",
-        sym::sinf64 => "llvm.sin.f64",
-        sym::sinf128 => "llvm.sin.f128",
-
-        sym::cosf16 => "llvm.cos.f16",
-        sym::cosf32 => "llvm.cos.f32",
-        sym::cosf64 => "llvm.cos.f64",
-        sym::cosf128 => "llvm.cos.f128",
-
-        sym::powf16 => "llvm.pow.f16",
-        sym::powf32 => "llvm.pow.f32",
-        sym::powf64 => "llvm.pow.f64",
-        sym::powf128 => "llvm.pow.f128",
-
-        sym::expf16 => "llvm.exp.f16",
-        sym::expf32 => "llvm.exp.f32",
-        sym::expf64 => "llvm.exp.f64",
-        sym::expf128 => "llvm.exp.f128",
-
-        sym::exp2f16 => "llvm.exp2.f16",
-        sym::exp2f32 => "llvm.exp2.f32",
-        sym::exp2f64 => "llvm.exp2.f64",
-        sym::exp2f128 => "llvm.exp2.f128",
-
-        sym::logf16 => "llvm.log.f16",
-        sym::logf32 => "llvm.log.f32",
-        sym::logf64 => "llvm.log.f64",
-        sym::logf128 => "llvm.log.f128",
-
-        sym::log10f16 => "llvm.log10.f16",
-        sym::log10f32 => "llvm.log10.f32",
-        sym::log10f64 => "llvm.log10.f64",
-        sym::log10f128 => "llvm.log10.f128",
-
-        sym::log2f16 => "llvm.log2.f16",
-        sym::log2f32 => "llvm.log2.f32",
-        sym::log2f64 => "llvm.log2.f64",
-        sym::log2f128 => "llvm.log2.f128",
-
-        sym::fmaf16 => "llvm.fma.f16",
-        sym::fmaf32 => "llvm.fma.f32",
-        sym::fmaf64 => "llvm.fma.f64",
-        sym::fmaf128 => "llvm.fma.f128",
-
-        sym::fmuladdf16 => "llvm.fmuladd.f16",
-        sym::fmuladdf32 => "llvm.fmuladd.f32",
-        sym::fmuladdf64 => "llvm.fmuladd.f64",
-        sym::fmuladdf128 => "llvm.fmuladd.f128",
-
-        sym::fabsf16 => "llvm.fabs.f16",
-        sym::fabsf32 => "llvm.fabs.f32",
-        sym::fabsf64 => "llvm.fabs.f64",
-        sym::fabsf128 => "llvm.fabs.f128",
-
-        sym::minnumf16 => "llvm.minnum.f16",
-        sym::minnumf32 => "llvm.minnum.f32",
-        sym::minnumf64 => "llvm.minnum.f64",
-        sym::minnumf128 => "llvm.minnum.f128",
-
-        sym::minimumf16 => "llvm.minimum.f16",
-        sym::minimumf32 => "llvm.minimum.f32",
-        sym::minimumf64 => "llvm.minimum.f64",
+    args: &[OperandRef<'tcx, &'ll Value>],
+) -> Option<&'ll Value> {
+    let (base_name, type_params): (&'static str, &[&'ll Type]) = match name {
+        sym::sqrtf16 => ("llvm.sqrt", &[bx.type_f16()]),
+        sym::sqrtf32 => ("llvm.sqrt", &[bx.type_f32()]),
+        sym::sqrtf64 => ("llvm.sqrt", &[bx.type_f64()]),
+        sym::sqrtf128 => ("llvm.sqrt", &[bx.type_f128()]),
+
+        sym::powif16 => ("llvm.powi", &[bx.type_f16(), bx.type_i32()]),
+        sym::powif32 => ("llvm.powi", &[bx.type_f32(), bx.type_i32()]),
+        sym::powif64 => ("llvm.powi", &[bx.type_f64(), bx.type_i32()]),
+        sym::powif128 => ("llvm.powi", &[bx.type_f128(), bx.type_i32()]),
+
+        sym::sinf16 => ("llvm.sin", &[bx.type_f16()]),
+        sym::sinf32 => ("llvm.sin", &[bx.type_f32()]),
+        sym::sinf64 => ("llvm.sin", &[bx.type_f64()]),
+        sym::sinf128 => ("llvm.sin", &[bx.type_f128()]),
+
+        sym::cosf16 => ("llvm.cos", &[bx.type_f16()]),
+        sym::cosf32 => ("llvm.cos", &[bx.type_f32()]),
+        sym::cosf64 => ("llvm.cos", &[bx.type_f64()]),
+        sym::cosf128 => ("llvm.cos", &[bx.type_f128()]),
+
+        sym::powf16 => ("llvm.pow", &[bx.type_f16()]),
+        sym::powf32 => ("llvm.pow", &[bx.type_f32()]),
+        sym::powf64 => ("llvm.pow", &[bx.type_f64()]),
+        sym::powf128 => ("llvm.pow", &[bx.type_f128()]),
+
+        sym::expf16 => ("llvm.exp", &[bx.type_f16()]),
+        sym::expf32 => ("llvm.exp", &[bx.type_f32()]),
+        sym::expf64 => ("llvm.exp", &[bx.type_f64()]),
+        sym::expf128 => ("llvm.exp", &[bx.type_f128()]),
+
+        sym::exp2f16 => ("llvm.exp2", &[bx.type_f16()]),
+        sym::exp2f32 => ("llvm.exp2", &[bx.type_f32()]),
+        sym::exp2f64 => ("llvm.exp2", &[bx.type_f64()]),
+        sym::exp2f128 => ("llvm.exp2", &[bx.type_f128()]),
+
+        sym::logf16 => ("llvm.log", &[bx.type_f16()]),
+        sym::logf32 => ("llvm.log", &[bx.type_f32()]),
+        sym::logf64 => ("llvm.log", &[bx.type_f64()]),
+        sym::logf128 => ("llvm.log", &[bx.type_f128()]),
+
+        sym::log10f16 => ("llvm.log10", &[bx.type_f16()]),
+        sym::log10f32 => ("llvm.log10", &[bx.type_f32()]),
+        sym::log10f64 => ("llvm.log10", &[bx.type_f64()]),
+        sym::log10f128 => ("llvm.log10", &[bx.type_f128()]),
+
+        sym::log2f16 => ("llvm.log2", &[bx.type_f16()]),
+        sym::log2f32 => ("llvm.log2", &[bx.type_f32()]),
+        sym::log2f64 => ("llvm.log2", &[bx.type_f64()]),
+        sym::log2f128 => ("llvm.log2", &[bx.type_f128()]),
+
+        sym::fmaf16 => ("llvm.fma", &[bx.type_f16()]),
+        sym::fmaf32 => ("llvm.fma", &[bx.type_f32()]),
+        sym::fmaf64 => ("llvm.fma", &[bx.type_f64()]),
+        sym::fmaf128 => ("llvm.fma", &[bx.type_f128()]),
+
+        sym::fmuladdf16 => ("llvm.fmuladd", &[bx.type_f16()]),
+        sym::fmuladdf32 => ("llvm.fmuladd", &[bx.type_f32()]),
+        sym::fmuladdf64 => ("llvm.fmuladd", &[bx.type_f64()]),
+        sym::fmuladdf128 => ("llvm.fmuladd", &[bx.type_f128()]),
+
+        sym::fabsf16 => ("llvm.fabs", &[bx.type_f16()]),
+        sym::fabsf32 => ("llvm.fabs", &[bx.type_f32()]),
+        sym::fabsf64 => ("llvm.fabs", &[bx.type_f64()]),
+        sym::fabsf128 => ("llvm.fabs", &[bx.type_f128()]),
+
+        sym::minnumf16 => ("llvm.minnum", &[bx.type_f16()]),
+        sym::minnumf32 => ("llvm.minnum", &[bx.type_f32()]),
+        sym::minnumf64 => ("llvm.minnum", &[bx.type_f64()]),
+        sym::minnumf128 => ("llvm.minnum", &[bx.type_f128()]),
+
+        sym::minimumf16 => ("llvm.minimum", &[bx.type_f16()]),
+        sym::minimumf32 => ("llvm.minimum", &[bx.type_f32()]),
+        sym::minimumf64 => ("llvm.minimum", &[bx.type_f64()]),
         // There are issues on x86_64 and aarch64 with the f128 variant,
         // let's instead use the instrinsic fallback body.
-        // sym::minimumf128 => "llvm.minimum.f128",
-        sym::maxnumf16 => "llvm.maxnum.f16",
-        sym::maxnumf32 => "llvm.maxnum.f32",
-        sym::maxnumf64 => "llvm.maxnum.f64",
-        sym::maxnumf128 => "llvm.maxnum.f128",
-
-        sym::maximumf16 => "llvm.maximum.f16",
-        sym::maximumf32 => "llvm.maximum.f32",
-        sym::maximumf64 => "llvm.maximum.f64",
+        // sym::minimumf128 => ("llvm.minimum", &[cx.type_f128()]),
+        sym::maxnumf16 => ("llvm.maxnum", &[bx.type_f16()]),
+        sym::maxnumf32 => ("llvm.maxnum", &[bx.type_f32()]),
+        sym::maxnumf64 => ("llvm.maxnum", &[bx.type_f64()]),
+        sym::maxnumf128 => ("llvm.maxnum", &[bx.type_f128()]),
+
+        sym::maximumf16 => ("llvm.maximum", &[bx.type_f16()]),
+        sym::maximumf32 => ("llvm.maximum", &[bx.type_f32()]),
+        sym::maximumf64 => ("llvm.maximum", &[bx.type_f64()]),
         // There are issues on x86_64 and aarch64 with the f128 variant,
         // let's instead use the instrinsic fallback body.
-        // sym::maximumf128 => "llvm.maximum.f128",
-        sym::copysignf16 => "llvm.copysign.f16",
-        sym::copysignf32 => "llvm.copysign.f32",
-        sym::copysignf64 => "llvm.copysign.f64",
-        sym::copysignf128 => "llvm.copysign.f128",
-
-        sym::floorf16 => "llvm.floor.f16",
-        sym::floorf32 => "llvm.floor.f32",
-        sym::floorf64 => "llvm.floor.f64",
-        sym::floorf128 => "llvm.floor.f128",
-
-        sym::ceilf16 => "llvm.ceil.f16",
-        sym::ceilf32 => "llvm.ceil.f32",
-        sym::ceilf64 => "llvm.ceil.f64",
-        sym::ceilf128 => "llvm.ceil.f128",
-
-        sym::truncf16 => "llvm.trunc.f16",
-        sym::truncf32 => "llvm.trunc.f32",
-        sym::truncf64 => "llvm.trunc.f64",
-        sym::truncf128 => "llvm.trunc.f128",
+        // sym::maximumf128 => ("llvm.maximum", &[cx.type_f128()]),
+        sym::copysignf16 => ("llvm.copysign", &[bx.type_f16()]),
+        sym::copysignf32 => ("llvm.copysign", &[bx.type_f32()]),
+        sym::copysignf64 => ("llvm.copysign", &[bx.type_f64()]),
+        sym::copysignf128 => ("llvm.copysign", &[bx.type_f128()]),
+
+        sym::floorf16 => ("llvm.floor", &[bx.type_f16()]),
+        sym::floorf32 => ("llvm.floor", &[bx.type_f32()]),
+        sym::floorf64 => ("llvm.floor", &[bx.type_f64()]),
+        sym::floorf128 => ("llvm.floor", &[bx.type_f128()]),
+
+        sym::ceilf16 => ("llvm.ceil", &[bx.type_f16()]),
+        sym::ceilf32 => ("llvm.ceil", &[bx.type_f32()]),
+        sym::ceilf64 => ("llvm.ceil", &[bx.type_f64()]),
+        sym::ceilf128 => ("llvm.ceil", &[bx.type_f128()]),
+
+        sym::truncf16 => ("llvm.trunc", &[bx.type_f16()]),
+        sym::truncf32 => ("llvm.trunc", &[bx.type_f32()]),
+        sym::truncf64 => ("llvm.trunc", &[bx.type_f64()]),
+        sym::truncf128 => ("llvm.trunc", &[bx.type_f128()]),
 
         // We could use any of `rint`, `nearbyint`, or `roundeven`
         // for this -- they are all identical in semantics when
         // assuming the default FP environment.
         // `rint` is what we used for $forever.
-        sym::round_ties_even_f16 => "llvm.rint.f16",
-        sym::round_ties_even_f32 => "llvm.rint.f32",
-        sym::round_ties_even_f64 => "llvm.rint.f64",
-        sym::round_ties_even_f128 => "llvm.rint.f128",
+        sym::round_ties_even_f16 => ("llvm.rint", &[bx.type_f16()]),
+        sym::round_ties_even_f32 => ("llvm.rint", &[bx.type_f32()]),
+        sym::round_ties_even_f64 => ("llvm.rint", &[bx.type_f64()]),
+        sym::round_ties_even_f128 => ("llvm.rint", &[bx.type_f128()]),
 
-        sym::roundf16 => "llvm.round.f16",
-        sym::roundf32 => "llvm.round.f32",
-        sym::roundf64 => "llvm.round.f64",
-        sym::roundf128 => "llvm.round.f128",
+        sym::roundf16 => ("llvm.round", &[bx.type_f16()]),
+        sym::roundf32 => ("llvm.round", &[bx.type_f32()]),
+        sym::roundf64 => ("llvm.round", &[bx.type_f64()]),
+        sym::roundf128 => ("llvm.round", &[bx.type_f128()]),
 
-        sym::ptr_mask => "llvm.ptrmask",
+        sym::ptr_mask => ("llvm.ptrmask", &[bx.type_ptr(), bx.type_isize()]),
 
         _ => return None,
     };
-    Some(cx.get_intrinsic(llvm_name))
+    Some(bx.call_intrinsic(
+        base_name,
+        type_params,
+        &args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(),
+    ))
 }
 
 impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
@@ -173,36 +178,16 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
         let name = tcx.item_name(instance.def_id());
         let fn_args = instance.args;
 
-        let simple = get_simple_intrinsic(self, name);
+        let simple = call_simple_intrinsic(self, name, args);
         let llval = match name {
-            _ if simple.is_some() => {
-                let (simple_ty, simple_fn) = simple.unwrap();
-                self.call(
-                    simple_ty,
-                    None,
-                    None,
-                    simple_fn,
-                    &args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(),
-                    None,
-                    Some(instance),
-                )
-            }
+            _ if simple.is_some() => simple.unwrap(),
             sym::is_val_statically_known => {
-                let intrinsic_type = args[0].layout.immediate_llvm_type(self.cx);
-                let kind = self.type_kind(intrinsic_type);
-                let intrinsic_name = match kind {
-                    TypeKind::Pointer | TypeKind::Integer => {
-                        Some(format!("llvm.is.constant.{intrinsic_type:?}"))
-                    }
-                    // LLVM float types' intrinsic names differ from their type names.
-                    TypeKind::Half => Some(format!("llvm.is.constant.f16")),
-                    TypeKind::Float => Some(format!("llvm.is.constant.f32")),
-                    TypeKind::Double => Some(format!("llvm.is.constant.f64")),
-                    TypeKind::FP128 => Some(format!("llvm.is.constant.f128")),
-                    _ => None,
-                };
-                if let Some(intrinsic_name) = intrinsic_name {
-                    self.call_intrinsic(&intrinsic_name, &[args[0].immediate()])
+                if let OperandValue::Immediate(imm) = args[0].val {
+                    self.call_intrinsic(
+                        "llvm.is.constant",
+                        &[args[0].layout.immediate_llvm_type(self.cx)],
+                        &[imm],
+                    )
                 } else {
                     self.const_bool(false)
                 }
@@ -246,10 +231,12 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
                 );
                 return Ok(());
             }
-            sym::breakpoint => self.call_intrinsic("llvm.debugtrap", &[]),
-            sym::va_copy => {
-                self.call_intrinsic("llvm.va_copy", &[args[0].immediate(), args[1].immediate()])
-            }
+            sym::breakpoint => self.call_intrinsic("llvm.debugtrap", &[], &[]),
+            sym::va_copy => self.call_intrinsic(
+                "llvm.va_copy",
+                &[self.type_ptr()],
+                &[args[0].immediate(), args[1].immediate()],
+            ),
             sym::va_arg => {
                 match result.layout.backend_repr {
                     BackendRepr::Scalar(scalar) => {
@@ -324,6 +311,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
                 };
                 self.call_intrinsic(
                     "llvm.prefetch",
+                    &[self.type_ptr()],
                     &[
                         args[0].immediate(),
                         self.const_i32(rw),
@@ -385,11 +373,13 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
                 }
                 let (size, signed) = ty.int_size_and_signed(self.tcx);
                 let width = size.bits();
+                let llty = self.type_ix(width);
                 match name {
                     sym::ctlz | sym::cttz => {
                         let y = self.const_bool(false);
                         let ret = self.call_intrinsic(
-                            &format!("llvm.{name}.i{width}"),
+                            &format!("llvm.{name}"),
+                            &[llty],
                             &[args[0].immediate(), y],
                         );
 
@@ -397,62 +387,54 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
                     }
                     sym::ctlz_nonzero => {
                         let y = self.const_bool(true);
-                        let llvm_name = &format!("llvm.ctlz.i{width}");
-                        let ret = self.call_intrinsic(llvm_name, &[args[0].immediate(), y]);
+                        let ret =
+                            self.call_intrinsic("llvm.ctlz", &[llty], &[args[0].immediate(), y]);
                         self.intcast(ret, result.layout.llvm_type(self), false)
                     }
                     sym::cttz_nonzero => {
                         let y = self.const_bool(true);
-                        let llvm_name = &format!("llvm.cttz.i{width}");
-                        let ret = self.call_intrinsic(llvm_name, &[args[0].immediate(), y]);
+                        let ret =
+                            self.call_intrinsic("llvm.cttz", &[llty], &[args[0].immediate(), y]);
                         self.intcast(ret, result.layout.llvm_type(self), false)
                     }
                     sym::ctpop => {
-                        let ret = self.call_intrinsic(
-                            &format!("llvm.ctpop.i{width}"),
-                            &[args[0].immediate()],
-                        );
+                        let ret =
+                            self.call_intrinsic("llvm.ctpop", &[llty], &[args[0].immediate()]);
                         self.intcast(ret, result.layout.llvm_type(self), false)
                     }
                     sym::bswap => {
                         if width == 8 {
                             args[0].immediate() // byte swap a u8/i8 is just a no-op
                         } else {
-                            self.call_intrinsic(
-                                &format!("llvm.bswap.i{width}"),
-                                &[args[0].immediate()],
-                            )
+                            self.call_intrinsic("llvm.bswap", &[llty], &[args[0].immediate()])
                         }
                     }
-                    sym::bitreverse => self.call_intrinsic(
-                        &format!("llvm.bitreverse.i{width}"),
-                        &[args[0].immediate()],
-                    ),
+                    sym::bitreverse => {
+                        self.call_intrinsic("llvm.bitreverse", &[llty], &[args[0].immediate()])
+                    }
                     sym::rotate_left | sym::rotate_right => {
                         let is_left = name == sym::rotate_left;
                         let val = args[0].immediate();
                         let raw_shift = args[1].immediate();
                         // rotate = funnel shift with first two args the same
-                        let llvm_name =
-                            &format!("llvm.fsh{}.i{}", if is_left { 'l' } else { 'r' }, width);
+                        let llvm_name = format!("llvm.fsh{}", if is_left { 'l' } else { 'r' });
 
                         // llvm expects shift to be the same type as the values, but rust
                         // always uses `u32`.
                         let raw_shift = self.intcast(raw_shift, self.val_ty(val), false);
 
-                        self.call_intrinsic(llvm_name, &[val, val, raw_shift])
+                        self.call_intrinsic(&llvm_name, &[llty], &[val, val, raw_shift])
                     }
                     sym::saturating_add | sym::saturating_sub => {
                         let is_add = name == sym::saturating_add;
                         let lhs = args[0].immediate();
                         let rhs = args[1].immediate();
-                        let llvm_name = &format!(
-                            "llvm.{}{}.sat.i{}",
+                        let llvm_name = format!(
+                            "llvm.{}{}.sat",
                             if signed { 's' } else { 'u' },
                             if is_add { "add" } else { "sub" },
-                            width
                         );
-                        self.call_intrinsic(llvm_name, &[lhs, rhs])
+                        self.call_intrinsic(&llvm_name, &[llty], &[lhs, rhs])
                     }
                     _ => bug!(),
                 }
@@ -484,11 +466,8 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
                     self.icmp(IntPredicate::IntEQ, a_val, b_val)
                 } else {
                     let n = self.const_usize(layout.size().bytes());
-                    let cmp = self.call_intrinsic("memcmp", &[a, b, n]);
-                    match self.cx.sess().target.arch.as_ref() {
-                        "avr" | "msp430" => self.icmp(IntPredicate::IntEQ, cmp, self.const_i16(0)),
-                        _ => self.icmp(IntPredicate::IntEQ, cmp, self.const_i32(0)),
-                    }
+                    let cmp = self.call_intrinsic("memcmp", &[], &[a, b, n]);
+                    self.icmp(IntPredicate::IntEQ, cmp, self.const_int(self.type_int(), 0))
                 }
             }
 
@@ -496,6 +475,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
                 // Here we assume that the `memcmp` provided by the target is a NOP for size 0.
                 let cmp = self.call_intrinsic(
                     "memcmp",
+                    &[],
                     &[args[0].immediate(), args[1].immediate(), args[2].immediate()],
                 );
                 // Some targets have `memcmp` returning `i16`, but the intrinsic is always `i32`.
@@ -619,18 +599,22 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
     }
 
     fn abort(&mut self) {
-        self.call_intrinsic("llvm.trap", &[]);
+        self.call_intrinsic("llvm.trap", &[], &[]);
     }
 
     fn assume(&mut self, val: Self::Value) {
         if self.cx.sess().opts.optimize != rustc_session::config::OptLevel::No {
-            self.call_intrinsic("llvm.assume", &[val]);
+            self.call_intrinsic("llvm.assume", &[], &[val]);
         }
     }
 
     fn expect(&mut self, cond: Self::Value, expected: bool) -> Self::Value {
         if self.cx.sess().opts.optimize != rustc_session::config::OptLevel::No {
-            self.call_intrinsic("llvm.expect.i1", &[cond, self.const_bool(expected)])
+            self.call_intrinsic(
+                "llvm.expect",
+                &[self.type_i1()],
+                &[cond, self.const_bool(expected)],
+            )
         } else {
             cond
         }
@@ -644,17 +628,20 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
     ) -> Self::Value {
         let typeid = self.get_metadata_value(typeid);
         let vtable_byte_offset = self.const_i32(vtable_byte_offset as i32);
-        let type_checked_load =
-            self.call_intrinsic("llvm.type.checked.load", &[llvtable, vtable_byte_offset, typeid]);
+        let type_checked_load = self.call_intrinsic(
+            "llvm.type.checked.load",
+            &[],
+            &[llvtable, vtable_byte_offset, typeid],
+        );
         self.extract_value(type_checked_load, 0)
     }
 
     fn va_start(&mut self, va_list: &'ll Value) -> &'ll Value {
-        self.call_intrinsic("llvm.va_start", &[va_list])
+        self.call_intrinsic("llvm.va_start", &[self.type_ptr()], &[va_list])
     }
 
     fn va_end(&mut self, va_list: &'ll Value) -> &'ll Value {
-        self.call_intrinsic("llvm.va_end", &[va_list])
+        self.call_intrinsic("llvm.va_end", &[self.type_ptr()], &[va_list])
     }
 }
 
@@ -893,8 +880,8 @@ fn codegen_wasm_try<'ll, 'tcx>(
         let null = bx.const_null(bx.type_ptr());
         let funclet = bx.catch_pad(cs, &[null]);
 
-        let ptr = bx.call_intrinsic("llvm.wasm.get.exception", &[funclet.cleanuppad()]);
-        let _sel = bx.call_intrinsic("llvm.wasm.get.ehselector", &[funclet.cleanuppad()]);
+        let ptr = bx.call_intrinsic("llvm.wasm.get.exception", &[], &[funclet.cleanuppad()]);
+        let _sel = bx.call_intrinsic("llvm.wasm.get.ehselector", &[], &[funclet.cleanuppad()]);
 
         let catch_ty = bx.type_func(&[bx.type_ptr(), bx.type_ptr()], bx.type_void());
         bx.call(catch_ty, None, None, catch_func, &[data, ptr], Some(&funclet), None);
@@ -1031,7 +1018,7 @@ fn codegen_emcc_try<'ll, 'tcx>(
         let selector = bx.extract_value(vals, 1);
 
         // Check if the typeid we got is the one for a Rust panic.
-        let rust_typeid = bx.call_intrinsic("llvm.eh.typeid.for", &[tydesc]);
+        let rust_typeid = bx.call_intrinsic("llvm.eh.typeid.for", &[bx.type_ptr()], &[tydesc]);
         let is_rust_panic = bx.icmp(IntPredicate::IntEQ, selector, rust_typeid);
         let is_rust_panic = bx.zext(is_rust_panic, bx.type_bool());
 
@@ -1522,56 +1509,37 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
             }};
         }
 
-        let (elem_ty_str, elem_ty) = if let ty::Float(f) = in_elem.kind() {
-            let elem_ty = bx.cx.type_float_from_ty(*f);
-            match f.bit_width() {
-                16 => ("f16", elem_ty),
-                32 => ("f32", elem_ty),
-                64 => ("f64", elem_ty),
-                128 => ("f128", elem_ty),
-                _ => return_error!(InvalidMonomorphization::FloatingPointVector {
-                    span,
-                    name,
-                    f_ty: *f,
-                    in_ty,
-                }),
-            }
+        let elem_ty = if let ty::Float(f) = in_elem.kind() {
+            bx.cx.type_float_from_ty(*f)
         } else {
             return_error!(InvalidMonomorphization::FloatingPointType { span, name, in_ty });
         };
 
         let vec_ty = bx.type_vector(elem_ty, in_len);
 
-        let (intr_name, fn_ty) = match name {
-            sym::simd_ceil => ("ceil", bx.type_func(&[vec_ty], vec_ty)),
-            sym::simd_fabs => ("fabs", bx.type_func(&[vec_ty], vec_ty)),
-            sym::simd_fcos => ("cos", bx.type_func(&[vec_ty], vec_ty)),
-            sym::simd_fexp2 => ("exp2", bx.type_func(&[vec_ty], vec_ty)),
-            sym::simd_fexp => ("exp", bx.type_func(&[vec_ty], vec_ty)),
-            sym::simd_flog10 => ("log10", bx.type_func(&[vec_ty], vec_ty)),
-            sym::simd_flog2 => ("log2", bx.type_func(&[vec_ty], vec_ty)),
-            sym::simd_flog => ("log", bx.type_func(&[vec_ty], vec_ty)),
-            sym::simd_floor => ("floor", bx.type_func(&[vec_ty], vec_ty)),
-            sym::simd_fma => ("fma", bx.type_func(&[vec_ty, vec_ty, vec_ty], vec_ty)),
-            sym::simd_relaxed_fma => ("fmuladd", bx.type_func(&[vec_ty, vec_ty, vec_ty], vec_ty)),
-            sym::simd_fsin => ("sin", bx.type_func(&[vec_ty], vec_ty)),
-            sym::simd_fsqrt => ("sqrt", bx.type_func(&[vec_ty], vec_ty)),
-            sym::simd_round => ("round", bx.type_func(&[vec_ty], vec_ty)),
-            sym::simd_trunc => ("trunc", bx.type_func(&[vec_ty], vec_ty)),
+        let intr_name = match name {
+            sym::simd_ceil => "llvm.ceil",
+            sym::simd_fabs => "llvm.fabs",
+            sym::simd_fcos => "llvm.cos",
+            sym::simd_fexp2 => "llvm.exp2",
+            sym::simd_fexp => "llvm.exp",
+            sym::simd_flog10 => "llvm.log10",
+            sym::simd_flog2 => "llvm.log2",
+            sym::simd_flog => "llvm.log",
+            sym::simd_floor => "llvm.floor",
+            sym::simd_fma => "llvm.fma",
+            sym::simd_relaxed_fma => "llvm.fmuladd",
+            sym::simd_fsin => "llvm.sin",
+            sym::simd_fsqrt => "llvm.sqrt",
+            sym::simd_round => "llvm.round",
+            sym::simd_trunc => "llvm.trunc",
             _ => return_error!(InvalidMonomorphization::UnrecognizedIntrinsic { span, name }),
         };
-        let llvm_name = &format!("llvm.{intr_name}.v{in_len}{elem_ty_str}");
-        let f = bx.declare_cfn(llvm_name, llvm::UnnamedAddr::No, fn_ty);
-        let c = bx.call(
-            fn_ty,
-            None,
-            None,
-            f,
+        Ok(bx.call_intrinsic(
+            intr_name,
+            &[vec_ty],
             &args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(),
-            None,
-            None,
-        );
-        Ok(c)
+        ))
     }
 
     if std::matches!(
@@ -1595,29 +1563,6 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
         return simd_simple_float_intrinsic(name, in_elem, in_ty, in_len, bx, span, args);
     }
 
-    // FIXME: use:
-    //  https://github.com/llvm-mirror/llvm/blob/master/include/llvm/IR/Function.h#L182
-    //  https://github.com/llvm-mirror/llvm/blob/master/include/llvm/IR/Intrinsics.h#L81
-    fn llvm_vector_str(bx: &Builder<'_, '_, '_>, elem_ty: Ty<'_>, vec_len: u64) -> String {
-        match *elem_ty.kind() {
-            ty::Int(v) => format!(
-                "v{}i{}",
-                vec_len,
-                // Normalize to prevent crash if v: IntTy::Isize
-                v.normalize(bx.target_spec().pointer_width).bit_width().unwrap()
-            ),
-            ty::Uint(v) => format!(
-                "v{}i{}",
-                vec_len,
-                // Normalize to prevent crash if v: UIntTy::Usize
-                v.normalize(bx.target_spec().pointer_width).bit_width().unwrap()
-            ),
-            ty::Float(v) => format!("v{}f{}", vec_len, v.bit_width()),
-            ty::RawPtr(_, _) => format!("v{}p0", vec_len),
-            _ => unreachable!(),
-        }
-    }
-
     fn llvm_vector_ty<'ll>(cx: &CodegenCx<'ll, '_>, elem_ty: Ty<'_>, vec_len: u64) -> &'ll Type {
         let elem_ty = match *elem_ty.kind() {
             ty::Int(v) => cx.type_int_from_ty(v),
@@ -1698,38 +1643,22 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
         );
 
         // Alignment of T, must be a constant integer value:
-        let alignment_ty = bx.type_i32();
         let alignment = bx.const_i32(bx.align_of(in_elem).bytes() as i32);
 
         // Truncate the mask vector to a vector of i1s:
         let mask = vector_mask_to_bitmask(bx, args[2].immediate(), mask_elem_bitwidth, in_len);
-        let mask_ty = bx.type_vector(bx.type_i1(), in_len);
 
         // Type of the vector of pointers:
         let llvm_pointer_vec_ty = llvm_vector_ty(bx, element_ty1, in_len);
-        let llvm_pointer_vec_str = llvm_vector_str(bx, element_ty1, in_len);
 
         // Type of the vector of elements:
         let llvm_elem_vec_ty = llvm_vector_ty(bx, element_ty0, in_len);
-        let llvm_elem_vec_str = llvm_vector_str(bx, element_ty0, in_len);
 
-        let llvm_intrinsic =
-            format!("llvm.masked.gather.{llvm_elem_vec_str}.{llvm_pointer_vec_str}");
-        let fn_ty = bx.type_func(
-            &[llvm_pointer_vec_ty, alignment_ty, mask_ty, llvm_elem_vec_ty],
-            llvm_elem_vec_ty,
-        );
-        let f = bx.declare_cfn(&llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty);
-        let v = bx.call(
-            fn_ty,
-            None,
-            None,
-            f,
+        return Ok(bx.call_intrinsic(
+            "llvm.masked.gather",
+            &[llvm_elem_vec_ty, llvm_pointer_vec_ty],
             &[args[1].immediate(), alignment, mask, args[0].immediate()],
-            None,
-            None,
-        );
-        return Ok(v);
+        ));
     }
 
     if name == sym::simd_masked_load {
@@ -1795,32 +1724,20 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
         );
 
         let mask = vector_mask_to_bitmask(bx, args[0].immediate(), m_elem_bitwidth, mask_len);
-        let mask_ty = bx.type_vector(bx.type_i1(), mask_len);
 
         // Alignment of T, must be a constant integer value:
-        let alignment_ty = bx.type_i32();
         let alignment = bx.const_i32(bx.align_of(values_elem).bytes() as i32);
 
         let llvm_pointer = bx.type_ptr();
 
         // Type of the vector of elements:
         let llvm_elem_vec_ty = llvm_vector_ty(bx, values_elem, values_len);
-        let llvm_elem_vec_str = llvm_vector_str(bx, values_elem, values_len);
-
-        let llvm_intrinsic = format!("llvm.masked.load.{llvm_elem_vec_str}.p0");
-        let fn_ty = bx
-            .type_func(&[llvm_pointer, alignment_ty, mask_ty, llvm_elem_vec_ty], llvm_elem_vec_ty);
-        let f = bx.declare_cfn(&llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty);
-        let v = bx.call(
-            fn_ty,
-            None,
-            None,
-            f,
+
+        return Ok(bx.call_intrinsic(
+            "llvm.masked.load",
+            &[llvm_elem_vec_ty, llvm_pointer],
             &[args[1].immediate(), alignment, mask, args[2].immediate()],
-            None,
-            None,
-        );
-        return Ok(v);
+        ));
     }
 
     if name == sym::simd_masked_store {
@@ -1880,33 +1797,20 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
         );
 
         let mask = vector_mask_to_bitmask(bx, args[0].immediate(), m_elem_bitwidth, mask_len);
-        let mask_ty = bx.type_vector(bx.type_i1(), mask_len);
 
         // Alignment of T, must be a constant integer value:
-        let alignment_ty = bx.type_i32();
         let alignment = bx.const_i32(bx.align_of(values_elem).bytes() as i32);
 
-        let ret_t = bx.type_void();
-
         let llvm_pointer = bx.type_ptr();
 
         // Type of the vector of elements:
         let llvm_elem_vec_ty = llvm_vector_ty(bx, values_elem, values_len);
-        let llvm_elem_vec_str = llvm_vector_str(bx, values_elem, values_len);
-
-        let llvm_intrinsic = format!("llvm.masked.store.{llvm_elem_vec_str}.p0");
-        let fn_ty = bx.type_func(&[llvm_elem_vec_ty, llvm_pointer, alignment_ty, mask_ty], ret_t);
-        let f = bx.declare_cfn(&llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty);
-        let v = bx.call(
-            fn_ty,
-            None,
-            None,
-            f,
+
+        return Ok(bx.call_intrinsic(
+            "llvm.masked.store",
+            &[llvm_elem_vec_ty, llvm_pointer],
             &[args[2].immediate(), args[1].immediate(), alignment, mask],
-            None,
-            None,
-        );
-        return Ok(v);
+        ));
     }
 
     if name == sym::simd_scatter {
@@ -1971,38 +1875,22 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
         );
 
         // Alignment of T, must be a constant integer value:
-        let alignment_ty = bx.type_i32();
         let alignment = bx.const_i32(bx.align_of(in_elem).bytes() as i32);
 
         // Truncate the mask vector to a vector of i1s:
         let mask = vector_mask_to_bitmask(bx, args[2].immediate(), mask_elem_bitwidth, in_len);
-        let mask_ty = bx.type_vector(bx.type_i1(), in_len);
-
-        let ret_t = bx.type_void();
 
         // Type of the vector of pointers:
         let llvm_pointer_vec_ty = llvm_vector_ty(bx, element_ty1, in_len);
-        let llvm_pointer_vec_str = llvm_vector_str(bx, element_ty1, in_len);
 
         // Type of the vector of elements:
         let llvm_elem_vec_ty = llvm_vector_ty(bx, element_ty0, in_len);
-        let llvm_elem_vec_str = llvm_vector_str(bx, element_ty0, in_len);
-
-        let llvm_intrinsic =
-            format!("llvm.masked.scatter.{llvm_elem_vec_str}.{llvm_pointer_vec_str}");
-        let fn_ty =
-            bx.type_func(&[llvm_elem_vec_ty, llvm_pointer_vec_ty, alignment_ty, mask_ty], ret_t);
-        let f = bx.declare_cfn(&llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty);
-        let v = bx.call(
-            fn_ty,
-            None,
-            None,
-            f,
+
+        return Ok(bx.call_intrinsic(
+            "llvm.masked.scatter",
+            &[llvm_elem_vec_ty, llvm_pointer_vec_ty],
             &[args[0].immediate(), args[1].immediate(), alignment, mask],
-            None,
-            None,
-        );
-        return Ok(v);
+        ));
     }
 
     macro_rules! arith_red {
@@ -2431,40 +2319,31 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
             },
             in_len as u64,
         );
-        let intrinsic_name = match name {
-            sym::simd_bswap => "bswap",
-            sym::simd_bitreverse => "bitreverse",
-            sym::simd_ctlz => "ctlz",
-            sym::simd_ctpop => "ctpop",
-            sym::simd_cttz => "cttz",
+        let llvm_intrinsic = match name {
+            sym::simd_bswap => "llvm.bswap",
+            sym::simd_bitreverse => "llvm.bitreverse",
+            sym::simd_ctlz => "llvm.ctlz",
+            sym::simd_ctpop => "llvm.ctpop",
+            sym::simd_cttz => "llvm.cttz",
             _ => unreachable!(),
         };
         let int_size = in_elem.int_size_and_signed(bx.tcx()).0.bits();
-        let llvm_intrinsic = &format!("llvm.{}.v{}i{}", intrinsic_name, in_len, int_size,);
 
         return match name {
             // byte swap is no-op for i8/u8
             sym::simd_bswap if int_size == 8 => Ok(args[0].immediate()),
             sym::simd_ctlz | sym::simd_cttz => {
                 // for the (int, i1 immediate) pair, the second arg adds `(0, true) => poison`
-                let fn_ty = bx.type_func(&[vec_ty, bx.type_i1()], vec_ty);
                 let dont_poison_on_zero = bx.const_int(bx.type_i1(), 0);
-                let f = bx.declare_cfn(llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty);
-                Ok(bx.call(
-                    fn_ty,
-                    None,
-                    None,
-                    f,
+                Ok(bx.call_intrinsic(
+                    llvm_intrinsic,
+                    &[vec_ty],
                     &[args[0].immediate(), dont_poison_on_zero],
-                    None,
-                    None,
                 ))
             }
             sym::simd_bswap | sym::simd_bitreverse | sym::simd_ctpop => {
                 // simple unary argument cases
-                let fn_ty = bx.type_func(&[vec_ty], vec_ty);
-                let f = bx.declare_cfn(llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty);
-                Ok(bx.call(fn_ty, None, None, f, &[args[0].immediate()], None, None))
+                Ok(bx.call_intrinsic(llvm_intrinsic, &[vec_ty], &[args[0].immediate()]))
             }
             _ => unreachable!(),
         };
@@ -2495,10 +2374,9 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
         let lhs = args[0].immediate();
         let rhs = args[1].immediate();
         let is_add = name == sym::simd_saturating_add;
-        let ptr_bits = bx.tcx().data_layout.pointer_size.bits() as _;
-        let (signed, elem_width, elem_ty) = match *in_elem.kind() {
-            ty::Int(i) => (true, i.bit_width().unwrap_or(ptr_bits), bx.cx.type_int_from_ty(i)),
-            ty::Uint(i) => (false, i.bit_width().unwrap_or(ptr_bits), bx.cx.type_uint_from_ty(i)),
+        let (signed, elem_ty) = match *in_elem.kind() {
+            ty::Int(i) => (true, bx.cx.type_int_from_ty(i)),
+            ty::Uint(i) => (false, bx.cx.type_uint_from_ty(i)),
             _ => {
                 return_error!(InvalidMonomorphization::ExpectedVectorElementType {
                     span,
@@ -2508,19 +2386,14 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
                 });
             }
         };
-        let llvm_intrinsic = &format!(
-            "llvm.{}{}.sat.v{}i{}",
+        let llvm_intrinsic = format!(
+            "llvm.{}{}.sat",
             if signed { 's' } else { 'u' },
             if is_add { "add" } else { "sub" },
-            in_len,
-            elem_width
         );
         let vec_ty = bx.cx.type_vector(elem_ty, in_len as u64);
 
-        let fn_ty = bx.type_func(&[vec_ty, vec_ty], vec_ty);
-        let f = bx.declare_cfn(llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty);
-        let v = bx.call(fn_ty, None, None, f, &[lhs, rhs], None, None);
-        return Ok(v);
+        return Ok(bx.call_intrinsic(&llvm_intrinsic, &[vec_ty], &[lhs, rhs]));
     }
 
     span_bug!(span, "unknown SIMD intrinsic");
diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
index e27fbf94f34..59c61db5fcd 100644
--- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
@@ -15,6 +15,7 @@
 
 use std::fmt::Debug;
 use std::marker::PhantomData;
+use std::num::NonZero;
 use std::ptr;
 
 use bitflags::bitflags;
@@ -1195,6 +1196,17 @@ unsafe extern "C" {
     // Operations on functions
     pub(crate) fn LLVMSetFunctionCallConv(Fn: &Value, CC: c_uint);
 
+    // Operations about llvm intrinsics
+    pub(crate) fn LLVMLookupIntrinsicID(Name: *const c_char, NameLen: size_t) -> c_uint;
+    pub(crate) fn LLVMIntrinsicIsOverloaded(ID: NonZero<c_uint>) -> Bool;
+    pub(crate) fn LLVMIntrinsicCopyOverloadedName2<'a>(
+        Mod: &'a Module,
+        ID: NonZero<c_uint>,
+        ParamTypes: *const &'a Type,
+        ParamCount: size_t,
+        NameLength: *mut size_t,
+    ) -> *mut c_char;
+
     // Operations on parameters
     pub(crate) fn LLVMIsAArgument(Val: &Value) -> Option<&Value>;
     pub(crate) safe fn LLVMCountParams(Fn: &Value) -> c_uint;
diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs
index ed23f911930..bc3538c768d 100644
--- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs
@@ -1,9 +1,10 @@
 #![allow(non_snake_case)]
 
 use std::ffi::{CStr, CString};
-use std::ptr;
+use std::num::NonZero;
 use std::str::FromStr;
 use std::string::FromUtf8Error;
+use std::{ptr, slice};
 
 use libc::c_uint;
 use rustc_abi::{Align, Size, WrappingRange};
@@ -327,6 +328,48 @@ pub(crate) fn get_value_name(value: &Value) -> &[u8] {
     }
 }
 
+#[derive(Debug, Copy, Clone)]
+pub(crate) struct Intrinsic {
+    id: NonZero<c_uint>,
+}
+
+impl Intrinsic {
+    pub(crate) fn lookup(name: &[u8]) -> Option<Self> {
+        let id = unsafe { LLVMLookupIntrinsicID(name.as_c_char_ptr(), name.len()) };
+        NonZero::new(id).map(|id| Self { id })
+    }
+
+    pub(crate) fn is_overloaded(self) -> bool {
+        unsafe { LLVMIntrinsicIsOverloaded(self.id) == True }
+    }
+
+    pub(crate) fn overloaded_name<'ll>(
+        self,
+        llmod: &'ll Module,
+        type_params: &[&'ll Type],
+    ) -> String {
+        let mut len = 0;
+        let ptr = unsafe {
+            LLVMIntrinsicCopyOverloadedName2(
+                llmod,
+                self.id,
+                type_params.as_ptr(),
+                type_params.len(),
+                &mut len,
+            )
+        };
+
+        let slice = unsafe { slice::from_raw_parts_mut(ptr.cast(), len) };
+        let copied = str::from_utf8(slice).expect("Non-UTF8 intrinsic name").to_string();
+
+        unsafe {
+            libc::free(ptr.cast());
+        }
+
+        copied
+    }
+}
+
 /// Safe wrapper for `LLVMSetValueName2` from a byte slice
 pub(crate) fn set_value_name(value: &Value, name: &[u8]) {
     unsafe {
diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs
index 9718c95f38a..0e77bc43df8 100644
--- a/compiler/rustc_codegen_llvm/src/llvm_util.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs
@@ -16,6 +16,7 @@ use rustc_fs_util::path_to_c_string;
 use rustc_middle::bug;
 use rustc_session::Session;
 use rustc_session::config::{PrintKind, PrintRequest};
+use rustc_session::features::{StabilityExt, retpoline_features_by_flags};
 use rustc_span::Symbol;
 use rustc_target::spec::{MergeFunctions, PanicStrategy, SmallDataThresholdSupport};
 use rustc_target::target_features::{RUSTC_SPECIAL_FEATURES, RUSTC_SPECIFIC_FEATURES};
@@ -23,8 +24,7 @@ use smallvec::{SmallVec, smallvec};
 
 use crate::back::write::create_informational_target_machine;
 use crate::errors::{
-    FixedX18InvalidArch, ForbiddenCTargetFeature, PossibleFeature, UnknownCTargetFeature,
-    UnknownCTargetFeaturePrefix, UnstableCTargetFeature,
+    FixedX18InvalidArch, PossibleFeature, UnknownCTargetFeature, UnknownCTargetFeaturePrefix,
 };
 use crate::llvm;
 
@@ -707,6 +707,12 @@ pub(crate) fn target_cpu(sess: &Session) -> &str {
     handle_native(cpu_name)
 }
 
+fn llvm_features_by_flags(sess: &Session) -> Vec<&str> {
+    let mut features: Vec<&str> = Vec::new();
+    retpoline_features_by_flags(sess, &mut features);
+    features
+}
+
 /// The list of LLVM features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`,
 /// `--target` and similar).
 pub(crate) fn global_llvm_features(
@@ -787,7 +793,7 @@ pub(crate) fn global_llvm_features(
 
         // Compute implied features
         let mut all_rust_features = vec![];
-        for feature in sess.opts.cg.target_feature.split(',') {
+        for feature in sess.opts.cg.target_feature.split(',').chain(llvm_features_by_flags(sess)) {
             if let Some(feature) = feature.strip_prefix('+') {
                 all_rust_features.extend(
                     UnordSet::from(sess.target.implied_target_features(feature))
@@ -840,18 +846,7 @@ pub(crate) fn global_llvm_features(
                         sess.dcx().emit_warn(unknown_feature);
                     }
                     Some((_, stability, _)) => {
-                        if let Err(reason) = stability.toggle_allowed() {
-                            sess.dcx().emit_warn(ForbiddenCTargetFeature {
-                                feature,
-                                enabled: if enable { "enabled" } else { "disabled" },
-                                reason,
-                            });
-                        } else if stability.requires_nightly().is_some() {
-                            // An unstable feature. Warn about using it. It makes little sense
-                            // to hard-error here since we just warn about fully unknown
-                            // features above.
-                            sess.dcx().emit_warn(UnstableCTargetFeature { feature });
-                        }
+                        stability.verify_feature_enabled_by_flag(sess, enable, feature);
                     }
                 }
 
diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs
index 169036f5152..2364ad0c9d2 100644
--- a/compiler/rustc_codegen_llvm/src/type_.rs
+++ b/compiler/rustc_codegen_llvm/src/type_.rs
@@ -1,4 +1,5 @@
 use std::borrow::Borrow;
+use std::hash::{Hash, Hasher};
 use std::{fmt, ptr};
 
 use libc::{c_char, c_uint};
@@ -25,6 +26,14 @@ impl PartialEq for Type {
     }
 }
 
+impl Eq for Type {}
+
+impl Hash for Type {
+    fn hash<H: Hasher>(&self, state: &mut H) {
+        ptr::hash(self, state);
+    }
+}
+
 impl fmt::Debug for Type {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         f.write_str(
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index 168077260a6..18b76b33757 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -1065,11 +1065,11 @@ fn link_natively(
         match strip {
             Strip::Debuginfo => {
                 // FIXME: AIX's strip utility only offers option to strip line number information.
-                strip_with_external_utility(sess, stripcmd, out_filename, &["-X32_64", "-l"])
+                strip_with_external_utility(sess, stripcmd, temp_filename, &["-X32_64", "-l"])
             }
             Strip::Symbols => {
                 // Must be noted this option might remove symbol __aix_rust_metadata and thus removes .info section which contains metadata.
-                strip_with_external_utility(sess, stripcmd, out_filename, &["-X32_64", "-r"])
+                strip_with_external_utility(sess, stripcmd, temp_filename, &["-X32_64", "-r"])
             }
             Strip::None => {}
         }
diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
index e217c09939e..27fcab8ed2d 100644
--- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
@@ -118,7 +118,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 let (llsize, _) = size_of_val::size_and_align_of_dst(bx, tp_ty, meta);
                 llsize
             }
-            sym::min_align_of_val => {
+            sym::align_of_val => {
                 let tp_ty = fn_args.type_at(0);
                 let (_, meta) = args[0].val.pointer_parts();
                 let (_, llalign) = size_of_val::size_and_align_of_dst(bx, tp_ty, meta);
diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs
index 6bb3150c1c5..640d197c219 100644
--- a/compiler/rustc_codegen_ssa/src/target_features.rs
+++ b/compiler/rustc_codegen_ssa/src/target_features.rs
@@ -8,6 +8,7 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
 use rustc_middle::middle::codegen_fn_attrs::TargetFeature;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::TyCtxt;
+use rustc_session::features::StabilityExt;
 use rustc_session::lint::builtin::AARCH64_SOFTFLOAT_NEON;
 use rustc_session::parse::feature_err;
 use rustc_span::{Span, Symbol, sym};
@@ -66,7 +67,7 @@ pub(crate) fn from_target_feature_attr(
 
             // Only allow target features whose feature gates have been enabled
             // and which are permitted to be toggled.
-            if let Err(reason) = stability.toggle_allowed() {
+            if let Err(reason) = stability.is_toggle_permitted(tcx.sess) {
                 tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr {
                     span: item.span(),
                     feature,
diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs
index 7f78bc75695..f35f551d590 100644
--- a/compiler/rustc_codegen_ssa/src/traits/builder.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs
@@ -23,7 +23,7 @@ use crate::common::{AtomicRmwBinOp, IntPredicate, RealPredicate, Synchronization
 use crate::mir::operand::{OperandRef, OperandValue};
 use crate::mir::place::{PlaceRef, PlaceValue};
 
-#[derive(Copy, Clone, Debug)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
 pub enum OverflowOp {
     Add,
     Sub,
@@ -215,7 +215,7 @@ pub trait BuilderMethods<'a, 'tcx>:
     fn checked_binop(
         &mut self,
         oop: OverflowOp,
-        ty: Ty<'_>,
+        ty: Ty<'tcx>,
         lhs: Self::Value,
         rhs: Self::Value,
     ) -> (Self::Value, Self::Value);
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index ab27182c211..96c39c7bb32 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -120,7 +120,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
                 self.copy_op(&val, dest)?;
             }
 
-            sym::min_align_of_val | sym::size_of_val => {
+            sym::align_of_val | sym::size_of_val => {
                 // Avoid `deref_pointer` -- this is not a deref, the ptr does not have to be
                 // dereferenceable!
                 let place = self.ref_to_mplace(&self.read_immediate(&args[0])?)?;
@@ -129,7 +129,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
                     .ok_or_else(|| err_unsup_format!("`extern type` does not have known layout"))?;
 
                 let result = match intrinsic_name {
-                    sym::min_align_of_val => align.bytes(),
+                    sym::align_of_val => align.bytes(),
                     sym::size_of_val => size.bytes(),
                     _ => bug!(),
                 };
@@ -139,13 +139,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
 
             sym::needs_drop | sym::type_id | sym::type_name | sym::variant_count => {
                 let gid = GlobalId { instance, promoted: None };
-                let ty = match intrinsic_name {
-                    sym::variant_count => self.tcx.types.usize,
-                    sym::needs_drop => self.tcx.types.bool,
-                    sym::type_id => self.tcx.types.u128,
-                    sym::type_name => Ty::new_static_str(self.tcx.tcx),
-                    _ => bug!(),
-                };
+                let ty = self
+                    .tcx
+                    .fn_sig(instance.def_id())
+                    .instantiate(self.tcx.tcx, instance.args)
+                    .output()
+                    .no_bound_vars()
+                    .unwrap();
                 let val = self
                     .ctfe_query(|tcx| tcx.const_eval_global_id(self.typing_env, gid, tcx.span))?;
                 let val = self.const_val_to_op(val, ty, Some(dest.layout))?;
diff --git a/compiler/rustc_data_structures/src/thousands/mod.rs b/compiler/rustc_data_structures/src/thousands/mod.rs
index e7ab7ec2932..b251ebe58f6 100644
--- a/compiler/rustc_data_structures/src/thousands/mod.rs
+++ b/compiler/rustc_data_structures/src/thousands/mod.rs
@@ -1,16 +1,37 @@
-//! This is an extremely bare-bones alternative to the `thousands` crate on
-//! crates.io, for printing large numbers in a readable fashion.
+//! This is a bare-bones alternative to the `thousands` crate on crates.io, for
+//! printing large numbers in a readable fashion.
 
 #[cfg(test)]
 mod tests;
 
-// Converts the number to a string, with underscores as the thousands separator.
-pub fn format_with_underscores(n: usize) -> String {
-    let mut s = n.to_string();
-    let mut i = s.len();
-    while i > 3 {
+fn format_with_underscores(mut s: String) -> String {
+    // Ignore a leading '-'.
+    let start = if s.starts_with('-') { 1 } else { 0 };
+
+    // Stop after the first non-digit, e.g. '.' or 'e' for floats.
+    let non_digit = s[start..].find(|c: char| !c.is_digit(10));
+    let end = if let Some(non_digit) = non_digit { start + non_digit } else { s.len() };
+
+    // Insert underscores within `start..end`.
+    let mut i = end;
+    while i > start + 3 {
         i -= 3;
         s.insert(i, '_');
     }
     s
 }
+
+/// Print a `usize` with underscore separators.
+pub fn usize_with_underscores(n: usize) -> String {
+    format_with_underscores(format!("{n}"))
+}
+
+/// Print an `isize` with underscore separators.
+pub fn isize_with_underscores(n: isize) -> String {
+    format_with_underscores(format!("{n}"))
+}
+
+/// Print an `f64` with precision 1 (one decimal place) and underscore separators.
+pub fn f64p1_with_underscores(n: f64) -> String {
+    format_with_underscores(format!("{n:.1}"))
+}
diff --git a/compiler/rustc_data_structures/src/thousands/tests.rs b/compiler/rustc_data_structures/src/thousands/tests.rs
index 906605d9a93..0f9a648802b 100644
--- a/compiler/rustc_data_structures/src/thousands/tests.rs
+++ b/compiler/rustc_data_structures/src/thousands/tests.rs
@@ -2,13 +2,51 @@ use super::*;
 
 #[test]
 fn test_format_with_underscores() {
-    assert_eq!("0", format_with_underscores(0));
-    assert_eq!("1", format_with_underscores(1));
-    assert_eq!("99", format_with_underscores(99));
-    assert_eq!("345", format_with_underscores(345));
-    assert_eq!("1_000", format_with_underscores(1_000));
-    assert_eq!("12_001", format_with_underscores(12_001));
-    assert_eq!("999_999", format_with_underscores(999_999));
-    assert_eq!("1_000_000", format_with_underscores(1_000_000));
-    assert_eq!("12_345_678", format_with_underscores(12_345_678));
+    assert_eq!("", format_with_underscores("".to_string()));
+    assert_eq!("0", format_with_underscores("0".to_string()));
+    assert_eq!("12_345.67e14", format_with_underscores("12345.67e14".to_string()));
+    assert_eq!("-1_234.5678e10", format_with_underscores("-1234.5678e10".to_string()));
+    assert_eq!("------", format_with_underscores("------".to_string()));
+    assert_eq!("abcdefgh", format_with_underscores("abcdefgh".to_string()));
+    assert_eq!("-1b", format_with_underscores("-1b".to_string()));
+    assert_eq!("-3_456xyz", format_with_underscores("-3456xyz".to_string()));
+}
+
+#[test]
+fn test_usize_with_underscores() {
+    assert_eq!("0", usize_with_underscores(0));
+    assert_eq!("1", usize_with_underscores(1));
+    assert_eq!("99", usize_with_underscores(99));
+    assert_eq!("345", usize_with_underscores(345));
+    assert_eq!("1_000", usize_with_underscores(1_000));
+    assert_eq!("12_001", usize_with_underscores(12_001));
+    assert_eq!("999_999", usize_with_underscores(999_999));
+    assert_eq!("1_000_000", usize_with_underscores(1_000_000));
+    assert_eq!("12_345_678", usize_with_underscores(12_345_678));
+}
+
+#[test]
+fn test_isize_with_underscores() {
+    assert_eq!("0", isize_with_underscores(0));
+    assert_eq!("-1", isize_with_underscores(-1));
+    assert_eq!("99", isize_with_underscores(99));
+    assert_eq!("345", isize_with_underscores(345));
+    assert_eq!("-1_000", isize_with_underscores(-1_000));
+    assert_eq!("12_001", isize_with_underscores(12_001));
+    assert_eq!("-999_999", isize_with_underscores(-999_999));
+    assert_eq!("1_000_000", isize_with_underscores(1_000_000));
+    assert_eq!("-12_345_678", isize_with_underscores(-12_345_678));
+}
+
+#[test]
+fn test_f64p1_with_underscores() {
+    assert_eq!("0.0", f64p1_with_underscores(0f64));
+    assert_eq!("0.0", f64p1_with_underscores(0.00000001));
+    assert_eq!("-0.0", f64p1_with_underscores(-0.00000001));
+    assert_eq!("1.0", f64p1_with_underscores(0.9999999));
+    assert_eq!("-1.0", f64p1_with_underscores(-0.9999999));
+    assert_eq!("345.5", f64p1_with_underscores(345.4999999));
+    assert_eq!("-100_000.0", f64p1_with_underscores(-100_000f64));
+    assert_eq!("123_456_789.1", f64p1_with_underscores(123456789.123456789));
+    assert_eq!("-123_456_789.1", f64p1_with_underscores(-123456789.123456789));
 }
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index 54a331a4904..0cd9e36a927 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -1500,13 +1500,31 @@ pub fn init_rustc_env_logger(early_dcx: &EarlyDiagCtxt) {
 
 /// This allows tools to enable rust logging without having to magically match rustc's
 /// tracing crate version. In contrast to `init_rustc_env_logger` it allows you to choose
-/// the values directly rather than having to set an environment variable.
+/// the logger config directly rather than having to set an environment variable.
 pub fn init_logger(early_dcx: &EarlyDiagCtxt, cfg: rustc_log::LoggerConfig) {
     if let Err(error) = rustc_log::init_logger(cfg) {
         early_dcx.early_fatal(error.to_string());
     }
 }
 
+/// This allows tools to enable rust logging without having to magically match rustc's
+/// tracing crate version. In contrast to `init_rustc_env_logger`, it allows you to
+/// choose the logger config directly rather than having to set an environment variable.
+/// Moreover, in contrast to `init_logger`, it allows you to add a custom tracing layer
+/// via `build_subscriber`, for example `|| Registry::default().with(custom_layer)`.
+pub fn init_logger_with_additional_layer<F, T>(
+    early_dcx: &EarlyDiagCtxt,
+    cfg: rustc_log::LoggerConfig,
+    build_subscriber: F,
+) where
+    F: FnOnce() -> T,
+    T: rustc_log::BuildSubscriberRet,
+{
+    if let Err(error) = rustc_log::init_logger_with_additional_layer(cfg, build_subscriber) {
+        early_dcx.early_fatal(error.to_string());
+    }
+}
+
 /// Install our usual `ctrlc` handler, which sets [`rustc_const_eval::CTRL_C_RECEIVED`].
 /// Making this handler optional lets tools can install a different handler, if they wish.
 pub fn install_ctrlc_handler() {
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 133bd361ee7..9f72fc4705a 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -60,8 +60,9 @@ pub use rustc_error_messages::{
     SubdiagMessage, fallback_fluent_bundle, fluent_bundle,
 };
 use rustc_hashes::Hash128;
-use rustc_lint_defs::LintExpectationId;
+use rustc_hir::HirId;
 pub use rustc_lint_defs::{Applicability, listify, pluralize};
+use rustc_lint_defs::{Lint, LintExpectationId};
 use rustc_macros::{Decodable, Encodable};
 pub use rustc_span::ErrorGuaranteed;
 pub use rustc_span::fatal_error::{FatalError, FatalErrorMarker};
@@ -101,6 +102,19 @@ rustc_data_structures::static_assert_size!(PResult<'_, ()>, 24);
 #[cfg(target_pointer_width = "64")]
 rustc_data_structures::static_assert_size!(PResult<'_, bool>, 24);
 
+/// Used to avoid depending on `rustc_middle` in `rustc_attr_parsing`.
+/// Always the `TyCtxt`.
+pub trait LintEmitter: Copy {
+    #[track_caller]
+    fn emit_node_span_lint(
+        self,
+        lint: &'static Lint,
+        hir_id: HirId,
+        span: impl Into<MultiSpan>,
+        decorator: impl for<'a> LintDiagnostic<'a, ()>,
+    );
+}
+
 #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Encodable, Decodable)]
 pub enum SuggestionStyle {
     /// Hide the suggested code when displaying this suggestion inline.
diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl
index 08b7a362083..8b7c47dad99 100644
--- a/compiler/rustc_expand/messages.ftl
+++ b/compiler/rustc_expand/messages.ftl
@@ -113,7 +113,7 @@ expand_meta_var_expr_unrecognized_var =
     variable `{$key}` is not recognized in meta-variable expression
 
 expand_missing_fragment_specifier = missing fragment specifier
-    .note = fragment specifiers must be specified in the 2024 edition
+    .note = fragment specifiers must be provided
     .suggestion_add_fragspec = try adding a specifier here
     .valid = {$valid}
 
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index c7b975d8f3e..311dadb53c4 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -12,7 +12,7 @@ use rustc_ast::tokenstream::TokenStream;
 use rustc_ast::visit::{AssocCtxt, Visitor};
 use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind};
 use rustc_attr_data_structures::{AttributeKind, Deprecation, Stability, find_attr};
-use rustc_data_structures::fx::FxIndexMap;
+use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
 use rustc_data_structures::sync;
 use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, PResult};
 use rustc_feature::Features;
@@ -727,6 +727,7 @@ pub enum SyntaxExtensionKind {
     /// A trivial attribute "macro" that does nothing,
     /// only keeps the attribute and marks it as inert,
     /// thus making it ineligible for further expansion.
+    /// E.g. `#[default]`, `#[rustfmt::skip]`.
     NonMacroAttr,
 
     /// A token-based derive macro.
@@ -1189,6 +1190,8 @@ pub struct ExtCtxt<'a> {
     /// in the AST, but insert it here so that we know
     /// not to expand it again.
     pub(super) expanded_inert_attrs: MarkedAttrs,
+    /// `-Zmacro-stats` data.
+    pub macro_stats: FxHashMap<(Symbol, MacroKind), crate::stats::MacroStat>, // njn: quals
 }
 
 impl<'a> ExtCtxt<'a> {
@@ -1218,6 +1221,7 @@ impl<'a> ExtCtxt<'a> {
             expansions: FxIndexMap::default(),
             expanded_inert_attrs: MarkedAttrs::new(),
             buffered_early_lint: vec![],
+            macro_stats: Default::default(),
         }
     }
 
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index 569165a64e5..3cfeb01ea47 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -42,13 +42,22 @@ use crate::module::{
     DirOwnership, ParsedExternalMod, mod_dir_path, mod_file_path_from_attr, parse_external_mod,
 };
 use crate::placeholders::{PlaceholderExpander, placeholder};
+use crate::stats::*;
 
 macro_rules! ast_fragments {
     (
         $($Kind:ident($AstTy:ty) {
             $kind_name:expr;
-            $(one fn $mut_visit_ast:ident; fn $visit_ast:ident;)?
-            $(many fn $flat_map_ast_elt:ident; fn $visit_ast_elt:ident($($args:tt)*);)?
+            $(one
+                fn $mut_visit_ast:ident;
+                fn $visit_ast:ident;
+                fn $ast_to_string:path;
+            )?
+            $(many
+                fn $flat_map_ast_elt:ident;
+                fn $visit_ast_elt:ident($($args:tt)*);
+                fn $ast_to_string_elt:path;
+            )?
             fn $make_ast:ident;
         })*
     ) => {
@@ -61,7 +70,7 @@ macro_rules! ast_fragments {
         }
 
         /// "Discriminant" of an AST fragment.
-        #[derive(Copy, Clone, PartialEq, Eq)]
+        #[derive(Copy, Clone, Debug, PartialEq, Eq)]
         pub enum AstFragmentKind {
             OptExpr,
             MethodReceiverExpr,
@@ -151,6 +160,21 @@ macro_rules! ast_fragments {
                 }
                 V::Result::output()
             }
+
+            pub(crate) fn to_string(&self) -> String {
+                match self {
+                    AstFragment::OptExpr(Some(expr)) => pprust::expr_to_string(expr),
+                    AstFragment::OptExpr(None) => unreachable!(),
+                    AstFragment::MethodReceiverExpr(expr) => pprust::expr_to_string(expr),
+                    $($(AstFragment::$Kind(ast) => $ast_to_string(ast),)?)*
+                    $($(
+                        AstFragment::$Kind(ast) => {
+                            // The closure unwraps a `P` if present, or does nothing otherwise.
+                            elems_to_string(&*ast, |ast| $ast_to_string_elt(&*ast))
+                        }
+                    )?)*
+                }
+            }
         }
 
         impl<'a> MacResult for crate::mbe::macro_rules::ParserAnyMacro<'a> {
@@ -163,76 +187,98 @@ macro_rules! ast_fragments {
 }
 
 ast_fragments! {
-    Expr(P<ast::Expr>) { "expression"; one fn visit_expr; fn visit_expr; fn make_expr; }
-    Pat(P<ast::Pat>) { "pattern"; one fn visit_pat; fn visit_pat; fn make_pat; }
-    Ty(P<ast::Ty>) { "type"; one fn visit_ty; fn visit_ty; fn make_ty; }
+    Expr(P<ast::Expr>) {
+        "expression";
+        one fn visit_expr; fn visit_expr; fn pprust::expr_to_string;
+        fn make_expr;
+    }
+    Pat(P<ast::Pat>) {
+        "pattern";
+        one fn visit_pat; fn visit_pat; fn pprust::pat_to_string;
+        fn make_pat;
+    }
+    Ty(P<ast::Ty>) {
+        "type";
+        one fn visit_ty; fn visit_ty; fn pprust::ty_to_string;
+        fn make_ty;
+    }
     Stmts(SmallVec<[ast::Stmt; 1]>) {
-        "statement"; many fn flat_map_stmt; fn visit_stmt(); fn make_stmts;
+        "statement";
+        many fn flat_map_stmt; fn visit_stmt(); fn pprust::stmt_to_string;
+        fn make_stmts;
     }
     Items(SmallVec<[P<ast::Item>; 1]>) {
-        "item"; many fn flat_map_item; fn visit_item(); fn make_items;
+        "item";
+        many fn flat_map_item; fn visit_item(); fn pprust::item_to_string;
+        fn make_items;
     }
     TraitItems(SmallVec<[P<ast::AssocItem>; 1]>) {
         "trait item";
-        many fn flat_map_assoc_item;
-        fn visit_assoc_item(AssocCtxt::Trait);
+        many fn flat_map_assoc_item; fn visit_assoc_item(AssocCtxt::Trait);
+            fn pprust::assoc_item_to_string;
         fn make_trait_items;
     }
     ImplItems(SmallVec<[P<ast::AssocItem>; 1]>) {
         "impl item";
-        many fn flat_map_assoc_item;
-        fn visit_assoc_item(AssocCtxt::Impl { of_trait: false });
+        many fn flat_map_assoc_item; fn visit_assoc_item(AssocCtxt::Impl { of_trait: false });
+            fn pprust::assoc_item_to_string;
         fn make_impl_items;
     }
     TraitImplItems(SmallVec<[P<ast::AssocItem>; 1]>) {
         "impl item";
-        many fn flat_map_assoc_item;
-        fn visit_assoc_item(AssocCtxt::Impl { of_trait: true });
+        many fn flat_map_assoc_item; fn visit_assoc_item(AssocCtxt::Impl { of_trait: true });
+            fn pprust::assoc_item_to_string;
         fn make_trait_impl_items;
     }
     ForeignItems(SmallVec<[P<ast::ForeignItem>; 1]>) {
         "foreign item";
-        many fn flat_map_foreign_item;
-        fn visit_foreign_item();
+        many fn flat_map_foreign_item; fn visit_foreign_item(); fn pprust::foreign_item_to_string;
         fn make_foreign_items;
     }
     Arms(SmallVec<[ast::Arm; 1]>) {
-        "match arm"; many fn flat_map_arm; fn visit_arm(); fn make_arms;
+        "match arm";
+        many fn flat_map_arm; fn visit_arm(); fn unreachable_to_string;
+        fn make_arms;
     }
     ExprFields(SmallVec<[ast::ExprField; 1]>) {
-        "field expression"; many fn flat_map_expr_field; fn visit_expr_field(); fn make_expr_fields;
+        "field expression";
+        many fn flat_map_expr_field; fn visit_expr_field(); fn unreachable_to_string;
+        fn make_expr_fields;
     }
     PatFields(SmallVec<[ast::PatField; 1]>) {
         "field pattern";
-        many fn flat_map_pat_field;
-        fn visit_pat_field();
+        many fn flat_map_pat_field; fn visit_pat_field(); fn unreachable_to_string;
         fn make_pat_fields;
     }
     GenericParams(SmallVec<[ast::GenericParam; 1]>) {
         "generic parameter";
-        many fn flat_map_generic_param;
-        fn visit_generic_param();
+        many fn flat_map_generic_param; fn visit_generic_param(); fn unreachable_to_string;
         fn make_generic_params;
     }
     Params(SmallVec<[ast::Param; 1]>) {
-        "function parameter"; many fn flat_map_param; fn visit_param(); fn make_params;
+        "function parameter";
+        many fn flat_map_param; fn visit_param(); fn unreachable_to_string;
+        fn make_params;
     }
     FieldDefs(SmallVec<[ast::FieldDef; 1]>) {
         "field";
-        many fn flat_map_field_def;
-        fn visit_field_def();
+        many fn flat_map_field_def; fn visit_field_def(); fn unreachable_to_string;
         fn make_field_defs;
     }
     Variants(SmallVec<[ast::Variant; 1]>) {
-        "variant"; many fn flat_map_variant; fn visit_variant(); fn make_variants;
+        "variant"; many fn flat_map_variant; fn visit_variant(); fn unreachable_to_string;
+        fn make_variants;
     }
     WherePredicates(SmallVec<[ast::WherePredicate; 1]>) {
         "where predicate";
-        many fn flat_map_where_predicate;
-        fn visit_where_predicate();
+        many fn flat_map_where_predicate; fn visit_where_predicate(); fn unreachable_to_string;
         fn make_where_predicates;
-     }
-    Crate(ast::Crate) { "crate"; one fn visit_crate; fn visit_crate; fn make_crate; }
+    }
+    Crate(ast::Crate) {
+        "crate";
+        one fn visit_crate; fn visit_crate; fn unreachable_to_string;
+        fn make_crate;
+    }
 }
 
 pub enum SupportsMacroExpansion {
@@ -270,7 +316,7 @@ impl AstFragmentKind {
         }
     }
 
-    fn expect_from_annotatables<I: IntoIterator<Item = Annotatable>>(
+    pub(crate) fn expect_from_annotatables<I: IntoIterator<Item = Annotatable>>(
         self,
         items: I,
     ) -> AstFragment {
@@ -686,13 +732,26 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
             return ExpandResult::Ready(invoc.fragment_kind.dummy(invoc.span(), guar));
         }
 
+        let macro_stats = self.cx.sess.opts.unstable_opts.macro_stats;
+
         let (fragment_kind, span) = (invoc.fragment_kind, invoc.span());
         ExpandResult::Ready(match invoc.kind {
             InvocationKind::Bang { mac, span } => match ext {
                 SyntaxExtensionKind::Bang(expander) => {
                     match expander.expand(self.cx, span, mac.args.tokens.clone()) {
                         Ok(tok_result) => {
-                            self.parse_ast_fragment(tok_result, fragment_kind, &mac.path, span)
+                            let fragment =
+                                self.parse_ast_fragment(tok_result, fragment_kind, &mac.path, span);
+                            if macro_stats {
+                                update_bang_macro_stats(
+                                    self.cx,
+                                    fragment_kind,
+                                    span,
+                                    mac,
+                                    &fragment,
+                                );
+                            }
+                            fragment
                         }
                         Err(guar) => return ExpandResult::Ready(fragment_kind.dummy(span, guar)),
                     }
@@ -708,13 +767,15 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                             });
                         }
                     };
-                    let result = if let Some(result) = fragment_kind.make_from(tok_result) {
-                        result
+                    if let Some(fragment) = fragment_kind.make_from(tok_result) {
+                        if macro_stats {
+                            update_bang_macro_stats(self.cx, fragment_kind, span, mac, &fragment);
+                        }
+                        fragment
                     } else {
                         let guar = self.error_wrong_fragment_kind(fragment_kind, &mac, span);
                         fragment_kind.dummy(span, guar)
-                    };
-                    result
+                    }
                 }
                 _ => unreachable!(),
             },
@@ -746,24 +807,39 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                         }
                         _ => item.to_tokens(),
                     };
-                    let attr_item = attr.unwrap_normal_item();
+                    let attr_item = attr.get_normal_item();
                     if let AttrArgs::Eq { .. } = attr_item.args {
                         self.cx.dcx().emit_err(UnsupportedKeyValue { span });
                     }
                     let inner_tokens = attr_item.args.inner_tokens();
                     match expander.expand(self.cx, span, inner_tokens, tokens) {
-                        Ok(tok_result) => self.parse_ast_fragment(
-                            tok_result,
-                            fragment_kind,
-                            &attr_item.path,
-                            span,
-                        ),
+                        Ok(tok_result) => {
+                            let fragment = self.parse_ast_fragment(
+                                tok_result,
+                                fragment_kind,
+                                &attr_item.path,
+                                span,
+                            );
+                            if macro_stats {
+                                update_attr_macro_stats(
+                                    self.cx,
+                                    fragment_kind,
+                                    span,
+                                    &attr_item.path,
+                                    &attr,
+                                    item,
+                                    &fragment,
+                                );
+                            }
+                            fragment
+                        }
                         Err(guar) => return ExpandResult::Ready(fragment_kind.dummy(span, guar)),
                     }
                 }
                 SyntaxExtensionKind::LegacyAttr(expander) => {
                     match validate_attr::parse_meta(&self.cx.sess.psess, &attr) {
                         Ok(meta) => {
+                            let item_clone = macro_stats.then(|| item.clone());
                             let items = match expander.expand(self.cx, span, &meta, item, false) {
                                 ExpandResult::Ready(items) => items,
                                 ExpandResult::Retry(item) => {
@@ -782,7 +858,19 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                                 let guar = self.cx.dcx().emit_err(RemoveExprNotSupported { span });
                                 fragment_kind.dummy(span, guar)
                             } else {
-                                fragment_kind.expect_from_annotatables(items)
+                                let fragment = fragment_kind.expect_from_annotatables(items);
+                                if macro_stats {
+                                    update_attr_macro_stats(
+                                        self.cx,
+                                        fragment_kind,
+                                        span,
+                                        &meta.path,
+                                        &attr,
+                                        item_clone.unwrap(),
+                                        &fragment,
+                                    );
+                                }
+                                fragment
                             }
                         }
                         Err(err) => {
@@ -792,6 +880,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                     }
                 }
                 SyntaxExtensionKind::NonMacroAttr => {
+                    // `-Zmacro-stats` ignores these because they don't do any real expansion.
                     self.cx.expanded_inert_attrs.mark(&attr);
                     item.visit_attrs(|attrs| attrs.insert(pos, attr));
                     fragment_kind.expect_from_annotatables(iter::once(item))
@@ -822,7 +911,17 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                             });
                         }
                     };
-                    fragment_kind.expect_from_annotatables(items)
+                    let fragment = fragment_kind.expect_from_annotatables(items);
+                    if macro_stats {
+                        update_derive_macro_stats(
+                            self.cx,
+                            fragment_kind,
+                            span,
+                            &meta.path,
+                            &fragment,
+                        );
+                    }
+                    fragment
                 }
                 _ => unreachable!(),
             },
@@ -852,6 +951,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                 let single_delegations = build_single_delegations::<Node>(
                     self.cx, deleg, &item, &suffixes, item.span, true,
                 );
+                // `-Zmacro-stats` ignores these because they don't seem important.
                 fragment_kind.expect_from_annotatables(
                     single_delegations
                         .map(|item| Annotatable::AssocItem(P(item), AssocCtxt::Impl { of_trait })),
diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs
index 515d82296ca..64be7649775 100644
--- a/compiler/rustc_expand/src/lib.rs
+++ b/compiler/rustc_expand/src/lib.rs
@@ -20,6 +20,7 @@ mod errors;
 mod mbe;
 mod placeholders;
 mod proc_macro_server;
+mod stats;
 
 pub use mbe::macro_rules::compile_declarative_macro;
 pub mod base;
diff --git a/compiler/rustc_expand/src/mbe/macro_check.rs b/compiler/rustc_expand/src/mbe/macro_check.rs
index 1a2db233b7a..3cd803c3e84 100644
--- a/compiler/rustc_expand/src/mbe/macro_check.rs
+++ b/compiler/rustc_expand/src/mbe/macro_check.rs
@@ -112,9 +112,8 @@ use rustc_ast::{DUMMY_NODE_ID, NodeId};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::MultiSpan;
 use rustc_lint_defs::BuiltinLintDiag;
-use rustc_session::lint::builtin::{META_VARIABLE_MISUSE, MISSING_FRAGMENT_SPECIFIER};
+use rustc_session::lint::builtin::META_VARIABLE_MISUSE;
 use rustc_session::parse::ParseSess;
-use rustc_span::edition::Edition;
 use rustc_span::{ErrorGuaranteed, MacroRulesNormalizedIdent, Span, kw};
 use smallvec::SmallVec;
 
@@ -266,23 +265,11 @@ fn check_binders(
         // Similarly, this can only happen when checking a toplevel macro.
         TokenTree::MetaVarDecl(span, name, kind) => {
             if kind.is_none() && node_id != DUMMY_NODE_ID {
-                // FIXME: Report this as a hard error eventually and remove equivalent errors from
-                // `parse_tt_inner` and `nameize`. Until then the error may be reported twice, once
-                // as a hard error and then once as a buffered lint.
-                if span.edition() >= Edition::Edition2024 {
-                    psess.dcx().emit_err(errors::MissingFragmentSpecifier {
-                        span,
-                        add_span: span.shrink_to_hi(),
-                        valid: VALID_FRAGMENT_NAMES_MSG,
-                    });
-                } else {
-                    psess.buffer_lint(
-                        MISSING_FRAGMENT_SPECIFIER,
-                        span,
-                        node_id,
-                        BuiltinLintDiag::MissingFragmentSpecifier,
-                    );
-                }
+                psess.dcx().emit_err(errors::MissingFragmentSpecifier {
+                    span,
+                    add_span: span.shrink_to_hi(),
+                    valid: VALID_FRAGMENT_NAMES_MSG,
+                });
             }
             if !macros.is_empty() {
                 psess.dcx().span_bug(span, "unexpected MetaVarDecl in nested lhs");
diff --git a/compiler/rustc_expand/src/stats.rs b/compiler/rustc_expand/src/stats.rs
new file mode 100644
index 00000000000..6b2ad30dffd
--- /dev/null
+++ b/compiler/rustc_expand/src/stats.rs
@@ -0,0 +1,171 @@
+use std::iter;
+
+use rustc_ast::ptr::P;
+use rustc_ast::{self as ast, DUMMY_NODE_ID, Expr, ExprKind};
+use rustc_ast_pretty::pprust;
+use rustc_span::hygiene::{ExpnKind, MacroKind};
+use rustc_span::{Span, Symbol, kw, sym};
+use smallvec::SmallVec;
+
+use crate::base::{Annotatable, ExtCtxt};
+use crate::expand::{AstFragment, AstFragmentKind};
+
+#[derive(Default)]
+pub struct MacroStat {
+    /// Number of uses of the macro.
+    pub uses: usize,
+
+    /// Net increase in number of lines of code (when pretty-printed), i.e.
+    /// `lines(output) - lines(invocation)`. Can be negative because a macro
+    /// output may be smaller than the invocation.
+    pub lines: isize,
+
+    /// Net increase in number of lines of code (when pretty-printed), i.e.
+    /// `bytes(output) - bytes(invocation)`. Can be negative because a macro
+    /// output may be smaller than the invocation.
+    pub bytes: isize,
+}
+
+pub(crate) fn elems_to_string<T>(elems: &SmallVec<[T; 1]>, f: impl Fn(&T) -> String) -> String {
+    let mut s = String::new();
+    for (i, elem) in elems.iter().enumerate() {
+        if i > 0 {
+            s.push('\n');
+        }
+        s.push_str(&f(elem));
+    }
+    s
+}
+
+pub(crate) fn unreachable_to_string<T>(_: &T) -> String {
+    unreachable!()
+}
+
+pub(crate) fn update_bang_macro_stats(
+    ecx: &mut ExtCtxt<'_>,
+    fragment_kind: AstFragmentKind,
+    span: Span,
+    mac: P<ast::MacCall>,
+    fragment: &AstFragment,
+) {
+    // Does this path match any of the include macros, e.g. `include!`?
+    // Ignore them. They would have large numbers but are entirely
+    // unsurprising and uninteresting.
+    let is_include_path = mac.path == sym::include
+        || mac.path == sym::include_bytes
+        || mac.path == sym::include_str
+        || mac.path == [sym::std, sym::include].as_slice() // std::include
+        || mac.path == [sym::std, sym::include_bytes].as_slice() // std::include_bytes
+        || mac.path == [sym::std, sym::include_str].as_slice(); // std::include_str
+    if is_include_path {
+        return;
+    }
+
+    // The call itself (e.g. `println!("hi")`) is the input. Need to wrap
+    // `mac` in something printable; `ast::Expr` is as good as anything
+    // else.
+    let expr = Expr {
+        id: DUMMY_NODE_ID,
+        kind: ExprKind::MacCall(mac),
+        span: Default::default(),
+        attrs: Default::default(),
+        tokens: None,
+    };
+    let input = pprust::expr_to_string(&expr);
+
+    // Get `mac` back out of `expr`.
+    let ast::Expr { kind: ExprKind::MacCall(mac), .. } = expr else { unreachable!() };
+
+    update_macro_stats(ecx, MacroKind::Bang, fragment_kind, span, &mac.path, &input, fragment);
+}
+
+pub(crate) fn update_attr_macro_stats(
+    ecx: &mut ExtCtxt<'_>,
+    fragment_kind: AstFragmentKind,
+    span: Span,
+    path: &ast::Path,
+    attr: &ast::Attribute,
+    item: Annotatable,
+    fragment: &AstFragment,
+) {
+    // Does this path match `#[derive(...)]` in any of its forms? If so,
+    // ignore it because the individual derives will go through the
+    // `Invocation::Derive` handling separately.
+    let is_derive_path = *path == sym::derive
+        // ::core::prelude::v1::derive
+        || *path == [kw::PathRoot, sym::core, sym::prelude, sym::v1, sym::derive].as_slice();
+    if is_derive_path {
+        return;
+    }
+
+    // The attribute plus the item itself constitute the input, which we
+    // measure.
+    let input = format!(
+        "{}\n{}",
+        pprust::attribute_to_string(attr),
+        fragment_kind.expect_from_annotatables(iter::once(item)).to_string(),
+    );
+    update_macro_stats(ecx, MacroKind::Attr, fragment_kind, span, path, &input, fragment);
+}
+
+pub(crate) fn update_derive_macro_stats(
+    ecx: &mut ExtCtxt<'_>,
+    fragment_kind: AstFragmentKind,
+    span: Span,
+    path: &ast::Path,
+    fragment: &AstFragment,
+) {
+    // Use something like `#[derive(Clone)]` for the measured input, even
+    // though it may have actually appeared in a multi-derive attribute
+    // like `#[derive(Clone, Copy, Debug)]`.
+    let input = format!("#[derive({})]", pprust::path_to_string(path));
+    update_macro_stats(ecx, MacroKind::Derive, fragment_kind, span, path, &input, fragment);
+}
+
+pub(crate) fn update_macro_stats(
+    ecx: &mut ExtCtxt<'_>,
+    macro_kind: MacroKind,
+    fragment_kind: AstFragmentKind,
+    span: Span,
+    path: &ast::Path,
+    input: &str,
+    fragment: &AstFragment,
+) {
+    fn lines_and_bytes(s: &str) -> (usize, usize) {
+        (s.trim_end().split('\n').count(), s.len())
+    }
+
+    // Measure the size of the output by pretty-printing it and counting
+    // the lines and bytes.
+    let name = Symbol::intern(&pprust::path_to_string(path));
+    let output = fragment.to_string();
+    let (in_l, in_b) = lines_and_bytes(input);
+    let (out_l, out_b) = lines_and_bytes(&output);
+
+    // This code is useful for debugging `-Zmacro-stats`. For every
+    // invocation it prints the full input and output.
+    if false {
+        let name = ExpnKind::Macro(macro_kind, name).descr();
+        let crate_name = &ecx.ecfg.crate_name;
+        let span = ecx
+            .sess
+            .source_map()
+            .span_to_string(span, rustc_span::FileNameDisplayPreference::Local);
+        eprint!(
+            "\
+            -------------------------------\n\
+            {name}: [{crate_name}] ({fragment_kind:?}) {span}\n\
+            -------------------------------\n\
+            {input}\n\
+            -- ({in_l} lines, {in_b} bytes) --> ({out_l} lines, {out_b} bytes) --\n\
+            {output}\n\
+        "
+        );
+    }
+
+    // The recorded size is the difference between the input and the output.
+    let entry = ecx.macro_stats.entry((name, macro_kind)).or_insert(MacroStat::default());
+    entry.uses += 1;
+    entry.lines += out_l as isize - in_l as isize;
+    entry.bytes += out_b as isize - in_b as isize;
+}
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index c117e0fcf7c..73a21789c5d 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -9,7 +9,7 @@ use rustc_data_structures::fx::FxHashMap;
 use rustc_span::edition::Edition;
 use rustc_span::{Symbol, sym};
 
-use crate::{Features, Stability};
+use crate::Features;
 
 type GateFn = fn(&Features) -> bool;
 
@@ -94,34 +94,23 @@ pub enum AttributeSafety {
     Unsafe { unsafe_since: Option<Edition> },
 }
 
-#[derive(Clone, Copy)]
+#[derive(Clone, Debug, Copy)]
 pub enum AttributeGate {
-    /// Is gated by a given feature gate, reason
-    /// and function to check if enabled
-    Gated(Stability, Symbol, &'static str, fn(&Features) -> bool),
-
+    /// A gated attribute which requires a feature gate to be enabled.
+    Gated {
+        /// The feature gate, for example `#![feature(rustc_attrs)]` for rustc_* attributes.
+        feature: Symbol,
+        /// The error message displayed when an attempt is made to use the attribute without its feature gate.
+        message: &'static str,
+        /// Check function to be called during the `PostExpansionVisitor` pass.
+        check: fn(&Features) -> bool,
+        /// Notes to be displayed when an attempt is made to use the attribute without its feature gate.
+        notes: &'static [&'static str],
+    },
     /// Ungated attribute, can be used on all release channels
     Ungated,
 }
 
-// fn() is not Debug
-impl std::fmt::Debug for AttributeGate {
-    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        match *self {
-            Self::Gated(ref stab, name, expl, _) => {
-                write!(fmt, "Gated({stab:?}, {name}, {expl})")
-            }
-            Self::Ungated => write!(fmt, "Ungated"),
-        }
-    }
-}
-
-impl AttributeGate {
-    fn is_deprecated(&self) -> bool {
-        matches!(*self, Self::Gated(Stability::Deprecated(_, _), ..))
-    }
-}
-
 /// A template that the attribute input must match.
 /// Only top-level shape (`#[attr]` vs `#[attr(...)]` vs `#[attr = ...]`) is considered now.
 #[derive(Clone, Copy, Default)]
@@ -247,7 +236,7 @@ macro_rules! ungated {
 }
 
 macro_rules! gated {
-    (unsafe $attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $gate:ident, $msg:expr $(,)?) => {
+    (unsafe $attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $gate:ident, $message:expr $(,)?) => {
         BuiltinAttribute {
             name: sym::$attr,
             encode_cross_crate: $encode_cross_crate,
@@ -255,10 +244,15 @@ macro_rules! gated {
             safety: AttributeSafety::Unsafe { unsafe_since: None },
             template: $tpl,
             duplicates: $duplicates,
-            gate: Gated(Stability::Unstable, sym::$gate, $msg, Features::$gate),
+            gate: Gated {
+                feature: sym::$gate,
+                message: $message,
+                check: Features::$gate,
+                notes: &[],
+            },
         }
     };
-    (unsafe $attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $msg:expr $(,)?) => {
+    (unsafe $attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $message:expr $(,)?) => {
         BuiltinAttribute {
             name: sym::$attr,
             encode_cross_crate: $encode_cross_crate,
@@ -266,10 +260,15 @@ macro_rules! gated {
             safety: AttributeSafety::Unsafe { unsafe_since: None },
             template: $tpl,
             duplicates: $duplicates,
-            gate: Gated(Stability::Unstable, sym::$attr, $msg, Features::$attr),
+            gate: Gated {
+                feature: sym::$attr,
+                message: $message,
+                check: Features::$attr,
+                notes: &[],
+            },
         }
     };
-    ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $gate:ident, $msg:expr $(,)?) => {
+    ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $gate:ident, $message:expr $(,)?) => {
         BuiltinAttribute {
             name: sym::$attr,
             encode_cross_crate: $encode_cross_crate,
@@ -277,10 +276,15 @@ macro_rules! gated {
             safety: AttributeSafety::Normal,
             template: $tpl,
             duplicates: $duplicates,
-            gate: Gated(Stability::Unstable, sym::$gate, $msg, Features::$gate),
+            gate: Gated {
+                feature: sym::$gate,
+                message: $message,
+                check: Features::$gate,
+                notes: &[],
+            },
         }
     };
-    ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $msg:expr $(,)?) => {
+    ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $message:expr $(,)?) => {
         BuiltinAttribute {
             name: sym::$attr,
             encode_cross_crate: $encode_cross_crate,
@@ -288,7 +292,12 @@ macro_rules! gated {
             safety: AttributeSafety::Normal,
             template: $tpl,
             duplicates: $duplicates,
-            gate: Gated(Stability::Unstable, sym::$attr, $msg, Features::$attr),
+            gate: Gated {
+                feature: sym::$attr,
+                message: $message,
+                check: Features::$attr,
+                notes: &[],
+            },
         }
     };
 }
@@ -304,12 +313,11 @@ macro_rules! rustc_attr {
             concat!(
                 "the `#[",
                 stringify!($attr),
-                "]` attribute is just used for rustc unit tests \
-                and will never be stable",
+                "]` attribute is used for rustc unit tests"
             ),
         )
     };
-    ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $msg:expr $(,)?) => {
+    ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $($notes:expr),* $(,)?) => {
         BuiltinAttribute {
             name: sym::$attr,
             encode_cross_crate: $encode_cross_crate,
@@ -317,7 +325,17 @@ macro_rules! rustc_attr {
             safety: AttributeSafety::Normal,
             template: $tpl,
             duplicates: $duplicates,
-            gate: Gated(Stability::Unstable, sym::rustc_attrs, $msg, Features::rustc_attrs),
+            gate: Gated {
+                feature: sym::rustc_attrs,
+                message: "use of an internal attribute",
+                check: Features::rustc_attrs,
+                notes: &[
+                    concat!("the `#[",
+                    stringify!($attr),
+                    "]` attribute is an internal implementation detail that will never be stable"),
+                    $($notes),*
+                    ]
+            },
         }
     };
 }
@@ -328,9 +346,6 @@ macro_rules! experimental {
     };
 }
 
-const IMPL_DETAIL: &str = "internal implementation detail";
-const INTERNAL_UNSTABLE: &str = "this is an internal attribute that will never be stable";
-
 #[derive(PartialEq)]
 pub enum EncodeCrossCrate {
     Yes,
@@ -668,7 +683,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     rustc_attr!(
         rustc_deprecated_safe_2024, Normal, template!(List: r#"audit_that = "...""#),
         ErrorFollowing, EncodeCrossCrate::Yes,
-        "rustc_deprecated_safe_2024 is supposed to be used in libstd only",
+        "`#[rustc_deprecated_safe_2024]` is used to declare functions unsafe across the edition 2024 boundary",
     ),
     rustc_attr!(
         rustc_pub_transparent, Normal, template!(Word),
@@ -695,7 +710,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         ErrorFollowing,
         EncodeCrossCrate::No,
         "`rustc_never_type_options` is used to experiment with never type fallback and work on \
-         never type stabilization, and will never be stable"
+         never type stabilization"
     ),
 
     // ==========================================================================
@@ -704,23 +719,23 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
 
     rustc_attr!(
         rustc_allocator, Normal, template!(Word), WarnFollowing,
-        EncodeCrossCrate::No, IMPL_DETAIL
+        EncodeCrossCrate::No,
     ),
     rustc_attr!(
         rustc_nounwind, Normal, template!(Word), WarnFollowing,
-        EncodeCrossCrate::No, IMPL_DETAIL
+        EncodeCrossCrate::No,
     ),
     rustc_attr!(
         rustc_reallocator, Normal, template!(Word), WarnFollowing,
-        EncodeCrossCrate::No, IMPL_DETAIL
+        EncodeCrossCrate::No,
     ),
     rustc_attr!(
         rustc_deallocator, Normal, template!(Word), WarnFollowing,
-        EncodeCrossCrate::No, IMPL_DETAIL
+        EncodeCrossCrate::No,
     ),
     rustc_attr!(
         rustc_allocator_zeroed, Normal, template!(Word), WarnFollowing,
-        EncodeCrossCrate::No, IMPL_DETAIL
+        EncodeCrossCrate::No,
     ),
     gated!(
         default_lib_allocator, Normal, template!(Word), WarnFollowing,
@@ -762,7 +777,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     ),
     rustc_attr!(
         rustc_std_internal_symbol, Normal, template!(Word), WarnFollowing,
-        EncodeCrossCrate::No, INTERNAL_UNSTABLE
+        EncodeCrossCrate::No,
     ),
 
     // ==========================================================================
@@ -772,11 +787,11 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     rustc_attr!(
         rustc_builtin_macro, Normal,
         template!(Word, List: "name, /*opt*/ attributes(name1, name2, ...)"), ErrorFollowing,
-        EncodeCrossCrate::Yes, IMPL_DETAIL
+        EncodeCrossCrate::Yes,
     ),
     rustc_attr!(
         rustc_proc_macro_decls, Normal, template!(Word), WarnFollowing,
-        EncodeCrossCrate::No, INTERNAL_UNSTABLE
+        EncodeCrossCrate::No,
     ),
     rustc_attr!(
         rustc_macro_transparency, Normal,
@@ -786,7 +801,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     rustc_attr!(
         rustc_autodiff, Normal,
         template!(Word, List: r#""...""#), DuplicatesOk,
-        EncodeCrossCrate::Yes, INTERNAL_UNSTABLE
+        EncodeCrossCrate::Yes,
     ),
     // Traces that are left when `cfg` and `cfg_attr` attributes are expanded.
     // The attributes are not gated, to avoid stability errors, but they cannot be used in stable
@@ -812,54 +827,53 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
             NameValueStr: "message"
         ),
         ErrorFollowing, EncodeCrossCrate::Yes,
-        INTERNAL_UNSTABLE
+        "see `#[diagnostic::on_unimplemented]` for the stable equivalent of this attribute"
     ),
     rustc_attr!(
         rustc_confusables, Normal,
         template!(List: r#""name1", "name2", ..."#),
         ErrorFollowing, EncodeCrossCrate::Yes,
-        INTERNAL_UNSTABLE,
     ),
     // Enumerates "identity-like" conversion methods to suggest on type mismatch.
     rustc_attr!(
         rustc_conversion_suggestion, Normal, template!(Word),
-        WarnFollowing, EncodeCrossCrate::Yes, INTERNAL_UNSTABLE
+        WarnFollowing, EncodeCrossCrate::Yes,
     ),
     // Prevents field reads in the marked trait or method to be considered
     // during dead code analysis.
     rustc_attr!(
         rustc_trivial_field_reads, Normal, template!(Word),
-        WarnFollowing, EncodeCrossCrate::Yes, INTERNAL_UNSTABLE
+        WarnFollowing, EncodeCrossCrate::Yes,
     ),
     // Used by the `rustc::potential_query_instability` lint to warn methods which
     // might not be stable during incremental compilation.
     rustc_attr!(
         rustc_lint_query_instability, Normal, template!(Word),
-        WarnFollowing, EncodeCrossCrate::Yes, INTERNAL_UNSTABLE
+        WarnFollowing, EncodeCrossCrate::Yes,
     ),
     // Used by the `rustc::untracked_query_information` lint to warn methods which
     // might not be stable during incremental compilation.
     rustc_attr!(
         rustc_lint_untracked_query_information, Normal, template!(Word),
-        WarnFollowing, EncodeCrossCrate::Yes, INTERNAL_UNSTABLE
+        WarnFollowing, EncodeCrossCrate::Yes,
     ),
     // Used by the `rustc::diagnostic_outside_of_impl` lints to assist in changes to diagnostic
     // APIs. Any function with this attribute will be checked by that lint.
     rustc_attr!(
         rustc_lint_diagnostics, Normal, template!(Word),
-        WarnFollowing, EncodeCrossCrate::Yes, INTERNAL_UNSTABLE
+        WarnFollowing, EncodeCrossCrate::Yes,
     ),
     // Used by the `rustc::bad_opt_access` lint to identify `DebuggingOptions` and `CodegenOptions`
     // types (as well as any others in future).
     rustc_attr!(
         rustc_lint_opt_ty, Normal, template!(Word),
-        WarnFollowing, EncodeCrossCrate::Yes, INTERNAL_UNSTABLE
+        WarnFollowing, EncodeCrossCrate::Yes,
     ),
     // Used by the `rustc::bad_opt_access` lint on fields
     // types (as well as any others in future).
     rustc_attr!(
         rustc_lint_opt_deny_field_access, Normal, template!(List: "message"),
-        WarnFollowing, EncodeCrossCrate::Yes, INTERNAL_UNSTABLE
+        WarnFollowing, EncodeCrossCrate::Yes,
     ),
 
     // ==========================================================================
@@ -868,28 +882,30 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
 
     rustc_attr!(
         rustc_promotable, Normal, template!(Word), WarnFollowing,
-        EncodeCrossCrate::No, IMPL_DETAIL),
+        EncodeCrossCrate::No, ),
     rustc_attr!(
         rustc_legacy_const_generics, Normal, template!(List: "N"), ErrorFollowing,
-        EncodeCrossCrate::Yes, INTERNAL_UNSTABLE
+        EncodeCrossCrate::Yes,
     ),
     // Do not const-check this function's body. It will always get replaced during CTFE.
     rustc_attr!(
         rustc_do_not_const_check, Normal, template!(Word), WarnFollowing,
-        EncodeCrossCrate::Yes, INTERNAL_UNSTABLE
+        EncodeCrossCrate::Yes, "`#[rustc_do_not_const_check]` skips const-check for this function's body",
     ),
-    // Ensure the argument to this function is &&str during const-check.
     rustc_attr!(
         rustc_const_panic_str, Normal, template!(Word), WarnFollowing,
-        EncodeCrossCrate::Yes, INTERNAL_UNSTABLE
+        EncodeCrossCrate::Yes, "`#[rustc_const_panic_str]` ensures the argument to this function is &&str during const-check",
     ),
     rustc_attr!(
         rustc_const_stable_indirect, Normal,
-        template!(Word), WarnFollowing, EncodeCrossCrate::No, IMPL_DETAIL,
+        template!(Word),
+        WarnFollowing,
+        EncodeCrossCrate::No,
+        "this is an internal implementation detail",
     ),
     rustc_attr!(
         rustc_intrinsic_const_stable_indirect, Normal,
-        template!(Word), WarnFollowing, EncodeCrossCrate::No, IMPL_DETAIL,
+        template!(Word), WarnFollowing, EncodeCrossCrate::No,  "this is an internal implementation detail",
     ),
     gated!(
         rustc_allow_const_fn_unstable, Normal,
@@ -905,21 +921,21 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         rustc_layout_scalar_valid_range_start, Normal, template!(List: "value"), ErrorFollowing,
         EncodeCrossCrate::Yes,
         "the `#[rustc_layout_scalar_valid_range_start]` attribute is just used to enable \
-        niche optimizations in libcore and libstd and will never be stable",
+        niche optimizations in the standard library",
     ),
     rustc_attr!(
         rustc_layout_scalar_valid_range_end, Normal, template!(List: "value"), ErrorFollowing,
         EncodeCrossCrate::Yes,
         "the `#[rustc_layout_scalar_valid_range_end]` attribute is just used to enable \
-        niche optimizations in libcore and libstd and will never be stable",
+        niche optimizations in the standard library",
     ),
     rustc_attr!(
         rustc_nonnull_optimization_guaranteed, Normal, template!(Word), WarnFollowing,
         EncodeCrossCrate::Yes,
         "the `#[rustc_nonnull_optimization_guaranteed]` attribute is just used to document \
-        guaranteed niche optimizations in libcore and libstd and will never be stable\n\
-        (note that the compiler does not even check whether the type indeed is being non-null-optimized; \
-        it is your responsibility to ensure that the attribute is only used on types that are optimized)",
+        guaranteed niche optimizations in the standard library",
+        "the compiler does not even check whether the type indeed is being non-null-optimized; \
+        it is your responsibility to ensure that the attribute is only used on types that are optimized",
     ),
 
     // ==========================================================================
@@ -932,17 +948,17 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     rustc_attr!(
         rustc_as_ptr, Normal, template!(Word), ErrorFollowing,
         EncodeCrossCrate::Yes,
-        "#[rustc_as_ptr] is used to mark functions returning pointers to their inner allocations."
+        "`#[rustc_as_ptr]` is used to mark functions returning pointers to their inner allocations."
     ),
     rustc_attr!(
         rustc_pass_by_value, Normal, template!(Word), ErrorFollowing,
         EncodeCrossCrate::Yes,
-        "#[rustc_pass_by_value] is used to mark types that must be passed by value instead of reference."
+        "`#[rustc_pass_by_value]` is used to mark types that must be passed by value instead of reference."
     ),
     rustc_attr!(
         rustc_never_returns_null_ptr, Normal, template!(Word), ErrorFollowing,
         EncodeCrossCrate::Yes,
-        "#[rustc_never_returns_null_ptr] is used to mark functions returning non-null pointers."
+        "`#[rustc_never_returns_null_ptr]` is used to mark functions returning non-null pointers."
     ),
     rustc_attr!(
         rustc_no_implicit_autorefs, AttributeType::Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::Yes,
@@ -950,15 +966,15 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     ),
     rustc_attr!(
         rustc_coherence_is_core, AttributeType::CrateLevel, template!(Word), ErrorFollowing, EncodeCrossCrate::No,
-        "#![rustc_coherence_is_core] allows inherent methods on builtin types, only intended to be used in `core`."
+        "`#![rustc_coherence_is_core]` allows inherent methods on builtin types, only intended to be used in `core`."
     ),
     rustc_attr!(
         rustc_coinductive, AttributeType::Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No,
-        "#![rustc_coinductive] changes a trait to be coinductive, allowing cycles in the trait solver."
+        "`#[rustc_coinductive]` changes a trait to be coinductive, allowing cycles in the trait solver."
     ),
     rustc_attr!(
         rustc_allow_incoherent_impl, AttributeType::Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::No,
-        "#[rustc_allow_incoherent_impl] has to be added to all impl items of an incoherent inherent impl."
+        "`#[rustc_allow_incoherent_impl]` has to be added to all impl items of an incoherent inherent impl."
     ),
     rustc_attr!(
         rustc_preserve_ub_checks, AttributeType::CrateLevel, template!(Word), ErrorFollowing, EncodeCrossCrate::No,
@@ -970,7 +986,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         template!(Word),
         ErrorFollowing,
         EncodeCrossCrate::No,
-        "#[rustc_deny_explicit_impl] enforces that a trait can have no user-provided impls"
+        "`#[rustc_deny_explicit_impl]` enforces that a trait can have no user-provided impls"
     ),
     rustc_attr!(
         rustc_do_not_implement_via_object,
@@ -978,14 +994,14 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         template!(Word),
         ErrorFollowing,
         EncodeCrossCrate::No,
-        "#[rustc_do_not_implement_via_object] opts out of the automatic trait impl for trait objects \
+        "`#[rustc_do_not_implement_via_object]` opts out of the automatic trait impl for trait objects \
         (`impl Trait for dyn Trait`)"
     ),
     rustc_attr!(
         rustc_has_incoherent_inherent_impls, AttributeType::Normal, template!(Word),
         ErrorFollowing, EncodeCrossCrate::Yes,
-        "#[rustc_has_incoherent_inherent_impls] allows the addition of incoherent inherent impls for \
-         the given type by annotating all impl items with #[rustc_allow_incoherent_impl]."
+        "`#[rustc_has_incoherent_inherent_impls]` allows the addition of incoherent inherent impls for \
+         the given type by annotating all impl items with `#[rustc_allow_incoherent_impl]`."
     ),
 
     BuiltinAttribute {
@@ -996,12 +1012,13 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         safety: AttributeSafety::Normal,
         template: template!(NameValueStr: "name"),
         duplicates: ErrorFollowing,
-        gate: Gated(
-            Stability::Unstable,
-            sym::rustc_attrs,
-            "diagnostic items compiler internal support for linting",
-            Features::rustc_attrs,
-        ),
+        gate: Gated{
+            feature: sym::rustc_attrs,
+            message: "use of an internal attribute",
+            check: Features::rustc_attrs,
+            notes: &["the `#[rustc_diagnostic_item]` attribute allows the compiler to reference types \
+            from the standard library for diagnostic purposes"],
+        },
     },
     gated!(
         // Used in resolve:
@@ -1015,14 +1032,14 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     rustc_attr!(
         rustc_inherit_overflow_checks, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No,
         "the `#[rustc_inherit_overflow_checks]` attribute is just used to control \
-        overflow checking behavior of several libcore functions that are inlined \
-        across crates and will never be stable",
+        overflow checking behavior of several functions in the standard library that are inlined \
+        across crates",
     ),
     rustc_attr!(
         rustc_reservation_impl, Normal,
         template!(NameValueStr: "reservation message"), ErrorFollowing, EncodeCrossCrate::Yes,
         "the `#[rustc_reservation_impl]` attribute is internally used \
-         for reserving for `for<T> From<!> for T` impl"
+        for reserving `impl<T> From<!> for T` as part of the effort to stabilize `!`"
     ),
     rustc_attr!(
         rustc_test_marker, Normal, template!(NameValueStr: "name"), WarnFollowing,
@@ -1053,12 +1070,13 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         rustc_must_implement_one_of, Normal, template!(List: "function1, function2, ..."),
         ErrorFollowing, EncodeCrossCrate::No,
         "the `#[rustc_must_implement_one_of]` attribute is used to change minimal complete \
-        definition of a trait, it's currently in experimental form and should be changed before \
-        being exposed outside of the std"
+        definition of a trait. Its syntax and semantics are highly experimental and will be \
+        subject to change before stabilization",
     ),
     rustc_attr!(
         rustc_doc_primitive, Normal, template!(NameValueStr: "primitive name"), ErrorFollowing,
-        EncodeCrossCrate::Yes, r#"`rustc_doc_primitive` is a rustc internal attribute"#,
+        EncodeCrossCrate::Yes, "the `#[rustc_doc_primitive]` attribute is used by the standard library \
+        to provide a way to generate documentation for primitive types",
     ),
     gated!(
         rustc_intrinsic, Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::Yes, intrinsics,
@@ -1066,11 +1084,11 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     ),
     rustc_attr!(
         rustc_no_mir_inline, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes,
-        "#[rustc_no_mir_inline] prevents the MIR inliner from inlining a function while not affecting codegen"
+        "`#[rustc_no_mir_inline]` prevents the MIR inliner from inlining a function while not affecting codegen"
     ),
     rustc_attr!(
         rustc_force_inline, Normal, template!(Word, NameValueStr: "reason"), WarnFollowing, EncodeCrossCrate::Yes,
-        "#[rustc_force_inline] forces a free function to be inlined"
+        "`#[rustc_force_inline]` forces a free function to be inlined"
     ),
 
     // ==========================================================================
@@ -1209,10 +1227,6 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     ),
 ];
 
-pub fn deprecated_attributes() -> Vec<&'static BuiltinAttribute> {
-    BUILTIN_ATTRIBUTES.iter().filter(|attr| attr.gate.is_deprecated()).collect()
-}
-
 pub fn is_builtin_attr_name(name: Symbol) -> bool {
     BUILTIN_ATTRIBUTE_MAP.get(&name).is_some()
 }
diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs
index 25764755a8f..dbc0daa3d83 100644
--- a/compiler/rustc_feature/src/lib.rs
+++ b/compiler/rustc_feature/src/lib.rs
@@ -40,14 +40,6 @@ pub struct Feature {
     issue: Option<NonZero<u32>>,
 }
 
-#[derive(Copy, Clone, Debug)]
-pub enum Stability {
-    Unstable,
-    // First argument is tracking issue link; second argument is an optional
-    // help message, which defaults to "remove this attribute".
-    Deprecated(&'static str, Option<&'static str>),
-}
-
 #[derive(Clone, Copy, Debug, Hash)]
 pub enum UnstableFeatures {
     /// Disallow use of unstable features, as on beta/stable channels.
@@ -144,9 +136,8 @@ pub fn find_feature_issue(feature: Symbol, issue: GateIssue) -> Option<NonZero<u
 pub use accepted::ACCEPTED_LANG_FEATURES;
 pub use builtin_attrs::{
     AttributeDuplicates, AttributeGate, AttributeSafety, AttributeTemplate, AttributeType,
-    BUILTIN_ATTRIBUTE_MAP, BUILTIN_ATTRIBUTES, BuiltinAttribute, GatedCfg, deprecated_attributes,
-    encode_cross_crate, find_gated_cfg, is_builtin_attr_name, is_stable_diagnostic_attribute,
-    is_valid_for_get_attr,
+    BUILTIN_ATTRIBUTE_MAP, BUILTIN_ATTRIBUTES, BuiltinAttribute, GatedCfg, encode_cross_crate,
+    find_gated_cfg, is_builtin_attr_name, is_stable_diagnostic_attribute, is_valid_for_get_attr,
 };
 pub use removed::REMOVED_LANG_FEATURES;
 pub use unstable::{
diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs
index 9738f169595..0cd090b25a4 100644
--- a/compiler/rustc_feature/src/removed.rs
+++ b/compiler/rustc_feature/src/removed.rs
@@ -263,6 +263,8 @@ declare_features! (
     /// Allows unnamed fields of struct and union type
     (removed, unnamed_fields, "1.83.0", Some(49804), Some("feature needs redesign"), 131045),
     (removed, unsafe_no_drop_flag, "1.0.0", None, None),
+    /// Allows unsized rvalues at arguments and parameters.
+    (removed, unsized_locals, "CURRENT_RUSTC_VERSION", Some(48055), Some("removed due to implementation concerns; see https://github.com/rust-lang/rust/issues/111942")),
     (removed, unsized_tuple_coercion, "1.87.0", Some(42877),
      Some("The feature restricts possible layouts for tuples, and this restriction is not worth it."), 137728),
     /// Allows `union` fields that don't implement `Copy` as long as they don't have any drop glue.
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index 594021d78d2..bd6ea850147 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -353,6 +353,8 @@ declare_features! (
     (unstable, abi_avr_interrupt, "1.45.0", Some(69664)),
     /// Allows `extern "C-cmse-nonsecure-call" fn()`.
     (unstable, abi_c_cmse_nonsecure_call, "1.51.0", Some(81391)),
+    /// Allows `extern "custom" fn()`.
+    (unstable, abi_custom, "CURRENT_RUSTC_VERSION", Some(140829)),
     /// Allows `extern "gpu-kernel" fn()`.
     (unstable, abi_gpu_kernel, "1.86.0", Some(135467)),
     /// Allows `extern "msp430-interrupt" fn()`.
@@ -665,8 +667,6 @@ declare_features! (
     (incomplete, unsized_const_params, "1.82.0", Some(95174)),
     /// Allows unsized fn parameters.
     (internal, unsized_fn_params, "1.49.0", Some(48055)),
-    /// Allows unsized rvalues at arguments and parameters.
-    (incomplete, unsized_locals, "1.30.0", Some(48055)),
     /// Allows using the `#[used(linker)]` (or `#[used(compiler)]`) attribute.
     (unstable, used_with_arg, "1.60.0", Some(93798)),
     /// Allows use of attributes in `where` clauses.
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 6f288bb39b9..336bf0d93fd 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -34,6 +34,7 @@ use crate::def::{CtorKind, DefKind, PerNS, Res};
 use crate::def_id::{DefId, LocalDefIdMap};
 pub(crate) use crate::hir_id::{HirId, ItemLocalId, ItemLocalMap, OwnerId};
 use crate::intravisit::{FnKind, VisitorExt};
+use crate::lints::DelayedLints;
 
 #[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable_Generic)]
 pub enum AngleBrackets {
@@ -1526,6 +1527,10 @@ pub struct OwnerInfo<'hir> {
     /// Map indicating what traits are in scope for places where this
     /// is relevant; generated by resolve.
     pub trait_map: ItemLocalMap<Box<[TraitCandidate]>>,
+
+    /// Lints delayed during ast lowering to be emitted
+    /// after hir has completely built
+    pub delayed_lints: DelayedLints,
 }
 
 impl<'tcx> OwnerInfo<'tcx> {
diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs
index c6fe475b460..dafd31336fb 100644
--- a/compiler/rustc_hir/src/lib.rs
+++ b/compiler/rustc_hir/src/lib.rs
@@ -27,6 +27,7 @@ mod hir;
 pub mod hir_id;
 pub mod intravisit;
 pub mod lang_items;
+pub mod lints;
 pub mod pat_util;
 mod stable_hash_impls;
 mod target;
diff --git a/compiler/rustc_hir/src/lints.rs b/compiler/rustc_hir/src/lints.rs
new file mode 100644
index 00000000000..7be6c32e57e
--- /dev/null
+++ b/compiler/rustc_hir/src/lints.rs
@@ -0,0 +1,23 @@
+use rustc_attr_data_structures::lints::AttributeLint;
+use rustc_data_structures::fingerprint::Fingerprint;
+use rustc_macros::HashStable_Generic;
+
+use crate::HirId;
+
+/// During ast lowering, no lints can be emitted.
+/// That is because lints attach to nodes either in the AST, or on the built HIR.
+/// When attached to AST nodes, they're emitted just before building HIR,
+/// and then there's a gap where no lints can be emitted until HIR is done.
+/// The variants in this enum represent lints that are temporarily stashed during
+/// AST lowering to be emitted once HIR is built.
+#[derive(Clone, Debug, HashStable_Generic)]
+pub enum DelayedLint {
+    AttributeParsing(AttributeLint<HirId>),
+}
+
+#[derive(Debug)]
+pub struct DelayedLints {
+    pub lints: Box<[DelayedLint]>,
+    // Only present when the crate hash is needed.
+    pub opt_hash: Option<Fingerprint>,
+}
diff --git a/compiler/rustc_hir/src/stable_hash_impls.rs b/compiler/rustc_hir/src/stable_hash_impls.rs
index 91ea88cae47..6acf1524b60 100644
--- a/compiler/rustc_hir/src/stable_hash_impls.rs
+++ b/compiler/rustc_hir/src/stable_hash_impls.rs
@@ -6,6 +6,7 @@ use crate::hir::{
     AttributeMap, BodyId, Crate, ForeignItemId, ImplItemId, ItemId, OwnerNodes, TraitItemId,
 };
 use crate::hir_id::{HirId, ItemLocalId};
+use crate::lints::DelayedLints;
 
 /// Requirements for a `StableHashingContext` to be used in this crate.
 /// This is a hack to allow using the `HashStable_Generic` derive macro
@@ -102,6 +103,13 @@ impl<'tcx, HirCtx: crate::HashStableContext> HashStable<HirCtx> for OwnerNodes<'
     }
 }
 
+impl<HirCtx: crate::HashStableContext> HashStable<HirCtx> for DelayedLints {
+    fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) {
+        let DelayedLints { opt_hash, .. } = *self;
+        opt_hash.unwrap().hash_stable(hcx, hasher);
+    }
+}
+
 impl<'tcx, HirCtx: crate::HashStableContext> HashStable<HirCtx> for AttributeMap<'tcx> {
     fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) {
         // We ignore the `map` since it refers to information included in `opt_hash` which is
diff --git a/compiler/rustc_hir_analysis/Cargo.toml b/compiler/rustc_hir_analysis/Cargo.toml
index 899370b34e4..5d6c49ee862 100644
--- a/compiler/rustc_hir_analysis/Cargo.toml
+++ b/compiler/rustc_hir_analysis/Cargo.toml
@@ -14,6 +14,7 @@ rustc_abi = { path = "../rustc_abi" }
 rustc_arena = { path = "../rustc_arena" }
 rustc_ast = { path = "../rustc_ast" }
 rustc_attr_data_structures = { path = "../rustc_attr_data_structures" }
+rustc_attr_parsing = { path = "../rustc_attr_parsing" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
 rustc_feature = { path = "../rustc_feature" }
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index 4fcd9f8a646..f768bd157ab 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -1,3 +1,7 @@
+hir_analysis_abi_custom_clothed_function =
+    items with the `"custom"` ABI can only be declared externally or defined via naked functions
+    .suggestion = convert this to an `#[unsafe(naked)]` function
+
 hir_analysis_ambiguous_assoc_item = ambiguous associated {$assoc_kind} `{$assoc_ident}` in bounds of `{$qself}`
     .label = ambiguous associated {$assoc_kind} `{$assoc_ident}`
 
@@ -595,7 +599,7 @@ hir_analysis_value_of_associated_struct_already_specified =
     .label = re-bound here
     .previous_bound_label = `{$item_name}` bound here first
 
-hir_analysis_variadic_function_compatible_convention = C-variadic function must have a compatible calling convention, like {$conventions}
+hir_analysis_variadic_function_compatible_convention = C-variadic functions with the {$convention} calling convention are not supported
     .label = C-variadic function must have a compatible calling convention
 
 hir_analysis_variances_of = {$variances}
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 60ca0155bdd..32fec0604c0 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -21,7 +21,7 @@ use rustc_middle::ty::error::TypeErrorToStringExt;
 use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES};
 use rustc_middle::ty::util::Discr;
 use rustc_middle::ty::{
-    AdtDef, BottomUpFolder, GenericArgKind, RegionKind, TypeFoldable, TypeSuperVisitable,
+    AdtDef, BottomUpFolder, FnSig, GenericArgKind, RegionKind, TypeFoldable, TypeSuperVisitable,
     TypeVisitable, TypeVisitableExt, fold_regions,
 };
 use rustc_session::lint::builtin::UNINHABITED_STATIC;
@@ -100,6 +100,18 @@ pub fn check_abi_fn_ptr(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Ex
     }
 }
 
+pub fn check_custom_abi(tcx: TyCtxt<'_>, def_id: LocalDefId, fn_sig: FnSig<'_>, fn_sig_span: Span) {
+    if fn_sig.abi == ExternAbi::Custom {
+        // Function definitions that use `extern "custom"` must be naked functions.
+        if !tcx.has_attr(def_id, sym::naked) {
+            tcx.dcx().emit_err(crate::errors::AbiCustomClothedFunction {
+                span: fn_sig_span,
+                naked_span: tcx.def_span(def_id).shrink_to_lo(),
+            });
+        }
+    }
+}
+
 fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId) {
     let def = tcx.adt_def(def_id);
     let span = tcx.def_span(def_id);
diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
index 481cdaa4c6c..060fc51b7bd 100644
--- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs
+++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
@@ -71,7 +71,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi
         | sym::box_new
         | sym::breakpoint
         | sym::size_of
-        | sym::min_align_of
+        | sym::align_of
         | sym::needs_drop
         | sym::caller_location
         | sym::add_with_overflow
@@ -200,10 +200,8 @@ pub(crate) fn check_intrinsic_type(
         sym::abort => (0, 0, vec![], tcx.types.never),
         sym::unreachable => (0, 0, vec![], tcx.types.never),
         sym::breakpoint => (0, 0, vec![], tcx.types.unit),
-        sym::size_of | sym::pref_align_of | sym::min_align_of | sym::variant_count => {
-            (1, 0, vec![], tcx.types.usize)
-        }
-        sym::size_of_val | sym::min_align_of_val => {
+        sym::size_of | sym::align_of | sym::variant_count => (1, 0, vec![], tcx.types.usize),
+        sym::size_of_val | sym::align_of_val => {
             (1, 0, vec![Ty::new_imm_ptr(tcx, param(0))], tcx.types.usize)
         }
         sym::rustc_peek => (1, 0, vec![param(0)], param(0)),
diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs
index fad8abf5fae..c5c7e6b2aa7 100644
--- a/compiler/rustc_hir_analysis/src/check/mod.rs
+++ b/compiler/rustc_hir_analysis/src/check/mod.rs
@@ -72,7 +72,7 @@ pub mod wfcheck;
 
 use std::num::NonZero;
 
-pub use check::{check_abi, check_abi_fn_ptr};
+pub use check::{check_abi, check_abi_fn_ptr, check_custom_abi};
 use rustc_abi::{ExternAbi, VariantIdx};
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
 use rustc_errors::{Diag, ErrorGuaranteed, pluralize, struct_span_code_err};
diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs
index 7cb31a64e13..c458878da15 100644
--- a/compiler/rustc_hir_analysis/src/check/region.rs
+++ b/compiler/rustc_hir_analysis/src/check/region.rs
@@ -10,6 +10,7 @@ use std::mem;
 
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir as hir;
+use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::{self, Visitor};
 use rustc_hir::{Arm, Block, Expr, LetStmt, Pat, PatKind, Stmt};
@@ -752,13 +753,19 @@ fn resolve_local<'tcx>(
                     record_rvalue_scope_if_borrow_expr(visitor, arm.body, blk_id);
                 }
             }
-            hir::ExprKind::Call(..) | hir::ExprKind::MethodCall(..) => {
-                // FIXME(@dingxiangfei2009): choose call arguments here
-                // for candidacy for extended parameter rule application
-            }
-            hir::ExprKind::Index(..) => {
-                // FIXME(@dingxiangfei2009): select the indices
-                // as candidate for rvalue scope rules
+            hir::ExprKind::Call(func, args) => {
+                // Recurse into tuple constructors, such as `Some(&temp())`.
+                //
+                // That way, there is no difference between `Some(..)` and `Some { 0: .. }`,
+                // even though the former is syntactically a function call.
+                if let hir::ExprKind::Path(path) = &func.kind
+                    && let hir::QPath::Resolved(None, path) = path
+                    && let Res::SelfCtor(_) | Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) = path.res
+                {
+                    for arg in args {
+                        record_rvalue_scope_if_borrow_expr(visitor, arg, blk_id);
+                    }
+                }
             }
             _ => {}
         }
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index e1df0d60452..6e22ac5a28a 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -494,7 +494,7 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
 
                 // Only visit the type looking for `_` if we didn't fix the type above
                 visitor.visit_ty_unambig(a);
-                self.lowerer().lower_arg_ty(a, None)
+                self.lowerer().lower_ty(a)
             })
             .collect();
 
diff --git a/compiler/rustc_hir_analysis/src/delegation.rs b/compiler/rustc_hir_analysis/src/delegation.rs
index b907ec090e5..f5821aed03f 100644
--- a/compiler/rustc_hir_analysis/src/delegation.rs
+++ b/compiler/rustc_hir_analysis/src/delegation.rs
@@ -276,8 +276,7 @@ fn create_generic_args<'tcx>(
                 tcx.impl_trait_header(parent).unwrap().trait_ref.instantiate_identity().args;
 
             let trait_args = ty::GenericArgs::identity_for_item(tcx, sig_id);
-            let method_args =
-                tcx.mk_args_from_iter(trait_args.iter().skip(callee_generics.parent_count));
+            let method_args = tcx.mk_args(&trait_args[callee_generics.parent_count..]);
             let method_args = build_generic_args(tcx, sig_id, def_id, method_args);
 
             tcx.mk_args_from_iter(parent_args.iter().chain(method_args))
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index a27d1ed6c53..8de2aec95a7 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -633,7 +633,7 @@ pub(crate) struct VariadicFunctionCompatibleConvention<'a> {
     #[primary_span]
     #[label]
     pub span: Span,
-    pub conventions: &'a str,
+    pub convention: &'a str,
 }
 
 #[derive(Diagnostic)]
@@ -1698,3 +1698,17 @@ pub(crate) struct SelfInTypeAlias {
     #[label]
     pub span: Span,
 }
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_abi_custom_clothed_function)]
+pub(crate) struct AbiCustomClothedFunction {
+    #[primary_span]
+    pub span: Span,
+    #[suggestion(
+        hir_analysis_suggestion,
+        applicability = "maybe-incorrect",
+        code = "#[unsafe(naked)]\n",
+        style = "short"
+    )]
+    pub naked_span: Span,
+}
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
index bdc42c7a2d9..106420faa4c 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
@@ -9,8 +9,8 @@ use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_middle::bug;
 use rustc_middle::ty::{
-    self as ty, IsSuggestable, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable,
-    TypeVisitableExt, TypeVisitor, Upcast,
+    self as ty, IsSuggestable, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
+    TypeVisitor, Upcast,
 };
 use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, sym};
 use rustc_trait_selection::traits;
@@ -927,7 +927,7 @@ struct GenericParamAndBoundVarCollector<'a, 'tcx> {
 impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GenericParamAndBoundVarCollector<'_, 'tcx> {
     type Result = ControlFlow<ErrorGuaranteed>;
 
-    fn visit_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
+    fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
         &mut self,
         binder: &ty::Binder<'tcx, T>,
     ) -> Self::Result {
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
index 4deb47dfff8..bf407cbaccb 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -2708,16 +2708,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         }
     }
 
-    pub fn lower_arg_ty(&self, ty: &hir::Ty<'tcx>, expected_ty: Option<Ty<'tcx>>) -> Ty<'tcx> {
-        match ty.kind {
-            hir::TyKind::Infer(()) if let Some(expected_ty) = expected_ty => {
-                self.record_ty(ty.hir_id, expected_ty, ty.span);
-                expected_ty
-            }
-            _ => self.lower_ty(ty),
-        }
-    }
-
     /// Lower a function type from the HIR to our internal notion of a function signature.
     #[instrument(level = "debug", skip(self, hir_id, safety, abi, decl, generics, hir_ty), ret)]
     pub fn lower_fn_ty(
diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs
index a92ee89186c..4f699462ac3 100644
--- a/compiler/rustc_hir_analysis/src/lib.rs
+++ b/compiler/rustc_hir_analysis/src/lib.rs
@@ -92,8 +92,9 @@ mod variance;
 
 pub use errors::NoVariantNamed;
 use rustc_abi::ExternAbi;
-use rustc_hir as hir;
 use rustc_hir::def::DefKind;
+use rustc_hir::lints::DelayedLint;
+use rustc_hir::{self as hir};
 use rustc_middle::middle;
 use rustc_middle::mir::interpret::GlobalId;
 use rustc_middle::query::Providers;
@@ -114,12 +115,6 @@ fn require_c_abi_if_c_variadic(
     abi: ExternAbi,
     span: Span,
 ) {
-    const CONVENTIONS_UNSTABLE: &str =
-        "`C`, `cdecl`, `system`, `aapcs`, `win64`, `sysv64` or `efiapi`";
-    const CONVENTIONS_STABLE: &str = "`C` or `cdecl`";
-    const UNSTABLE_EXPLAIN: &str =
-        "using calling conventions other than `C` or `cdecl` for varargs functions is unstable";
-
     // ABIs which can stably use varargs
     if !decl.c_variadic || matches!(abi, ExternAbi::C { .. } | ExternAbi::Cdecl { .. }) {
         return;
@@ -139,20 +134,18 @@ fn require_c_abi_if_c_variadic(
 
     // Looks like we need to pick an error to emit.
     // Is there any feature which we could have enabled to make this work?
+    let unstable_explain =
+        format!("C-variadic functions with the {abi} calling convention are unstable");
     match abi {
         ExternAbi::System { .. } => {
-            feature_err(&tcx.sess, sym::extern_system_varargs, span, UNSTABLE_EXPLAIN)
+            feature_err(&tcx.sess, sym::extern_system_varargs, span, unstable_explain)
         }
         abi if abi.supports_varargs() => {
-            feature_err(&tcx.sess, sym::extended_varargs_abi_support, span, UNSTABLE_EXPLAIN)
+            feature_err(&tcx.sess, sym::extended_varargs_abi_support, span, unstable_explain)
         }
         _ => tcx.dcx().create_err(errors::VariadicFunctionCompatibleConvention {
             span,
-            conventions: if tcx.sess.opts.unstable_features.is_nightly_build() {
-                CONVENTIONS_UNSTABLE
-            } else {
-                CONVENTIONS_STABLE
-            },
+            convention: &format!("{abi}"),
         }),
     }
     .emit();
@@ -174,6 +167,14 @@ pub fn provide(providers: &mut Providers) {
     };
 }
 
+fn emit_delayed_lint(lint: &DelayedLint, tcx: TyCtxt<'_>) {
+    match lint {
+        DelayedLint::AttributeParsing(attribute_lint) => {
+            rustc_attr_parsing::emit_attribute_lint(attribute_lint, tcx)
+        }
+    }
+}
+
 pub fn check_crate(tcx: TyCtxt<'_>) {
     let _prof_timer = tcx.sess.timer("type_check_crate");
 
@@ -192,6 +193,14 @@ pub fn check_crate(tcx: TyCtxt<'_>) {
         let _: R = tcx.ensure_ok().crate_inherent_impls_overlap_check(());
     });
 
+    for owner_id in tcx.hir_crate_items(()).owners() {
+        if let Some(delayed_lints) = tcx.opt_ast_lowering_delayed_lints(owner_id) {
+            for lint in &delayed_lints.lints {
+                emit_delayed_lint(lint, tcx);
+            }
+        }
+    }
+
     tcx.par_hir_body_owners(|item_def_id| {
         let def_kind = tcx.def_kind(item_def_id);
         // Make sure we evaluate all static and (non-associated) const items, even if unused.
diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl
index 3bdd1b48666..ac7ff65528d 100644
--- a/compiler/rustc_hir_typeck/messages.ftl
+++ b/compiler/rustc_hir_typeck/messages.ftl
@@ -1,3 +1,7 @@
+hir_typeck_abi_custom_call =
+    functions with the `"custom"` ABI cannot be called
+    .note = an `extern "custom"` function can only be called from within inline assembly
+
 hir_typeck_add_missing_parentheses_in_range = you must surround the range in parentheses to call its `{$func_name}` function
 
 hir_typeck_add_return_type_add = try adding a return type
diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs
index d173fe7c2c2..e915b4fc626 100644
--- a/compiler/rustc_hir_typeck/src/callee.rs
+++ b/compiler/rustc_hir_typeck/src/callee.rs
@@ -1,5 +1,6 @@
 use std::iter;
 
+use rustc_abi::ExternAbi;
 use rustc_ast::util::parser::ExprPrecedence;
 use rustc_errors::{Applicability, Diag, ErrorGuaranteed, StashKey};
 use rustc_hir::def::{self, CtorKind, Namespace, Res};
@@ -83,6 +84,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         while result.is_none() && autoderef.next().is_some() {
             result = self.try_overloaded_call_step(call_expr, callee_expr, arg_exprs, &autoderef);
         }
+        self.check_call_custom_abi(autoderef.final_ty(false), call_expr.span);
         self.register_predicates(autoderef.into_obligations());
 
         let output = match result {
@@ -135,6 +137,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         output
     }
 
+    /// Functions of type `extern "custom" fn(/* ... */)` cannot be called using `ExprKind::Call`.
+    ///
+    /// These functions have a calling convention that is unknown to rust, hence it cannot generate
+    /// code for the call. The only way to execute such a function is via inline assembly.
+    fn check_call_custom_abi(&self, callee_ty: Ty<'tcx>, span: Span) {
+        let abi = match callee_ty.kind() {
+            ty::FnDef(def_id, _) => self.tcx.fn_sig(def_id).skip_binder().skip_binder().abi,
+            ty::FnPtr(_, header) => header.abi,
+            _ => return,
+        };
+
+        if let ExternAbi::Custom = abi {
+            self.tcx.dcx().emit_err(errors::AbiCustomCall { span });
+        }
+    }
+
     #[instrument(level = "debug", skip(self, call_expr, callee_expr, arg_exprs, autoderef), ret)]
     fn try_overloaded_call_step(
         &self,
diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index d9fa56fefeb..24092c01125 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -1662,9 +1662,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
                             blk_id,
                             expression,
                         );
-                        if !fcx.tcx.features().unsized_locals() {
-                            unsized_return = self.is_return_ty_definitely_unsized(fcx);
-                        }
+                        unsized_return = self.is_return_ty_definitely_unsized(fcx);
                     }
                     ObligationCauseCode::ReturnValue(return_expr_id) => {
                         err = self.report_return_mismatched_types(
@@ -1676,9 +1674,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
                             return_expr_id,
                             expression,
                         );
-                        if !fcx.tcx.features().unsized_locals() {
-                            unsized_return = self.is_return_ty_definitely_unsized(fcx);
-                        }
+                        unsized_return = self.is_return_ty_definitely_unsized(fcx);
                     }
                     ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
                         arm_span,
diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs
index 774815015d5..abb8cdc1cdf 100644
--- a/compiler/rustc_hir_typeck/src/errors.rs
+++ b/compiler/rustc_hir_typeck/src/errors.rs
@@ -1163,3 +1163,10 @@ pub(crate) struct NakedFunctionsMustNakedAsm {
     #[label]
     pub span: Span,
 }
+
+#[derive(Diagnostic)]
+#[diag(hir_typeck_abi_custom_call)]
+pub(crate) struct AbiCustomCall {
+    #[primary_span]
+    pub span: Span,
+}
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index dfc7935d02b..30bf557dc93 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -5,7 +5,7 @@
 //!
 //! See [`rustc_hir_analysis::check`] for more context on type checking in general.
 
-use rustc_abi::{FIRST_VARIANT, FieldIdx};
+use rustc_abi::{ExternAbi, FIRST_VARIANT, FieldIdx};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_data_structures::unord::UnordMap;
@@ -809,9 +809,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     );
                 }
             }
-            // Here we want to prevent struct constructors from returning unsized types.
-            // There were two cases this happened: fn pointer coercion in stable
-            // and usual function call in presence of unsized_locals.
+            // Here we want to prevent struct constructors from returning unsized types,
+            // which can happen with fn pointer coercion on stable.
             // Also, as we just want to check sizedness, instead of introducing
             // placeholder lifetimes with probing, we just replace higher lifetimes
             // with fresh vars.
@@ -1627,6 +1626,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     Some(method.def_id),
                 );
 
+                // Functions of type `extern "custom" fn(/* ... */)` cannot be called using
+                // `ExprKind::MethodCall`. These functions have a calling convention that is
+                // unknown to rust, hence it cannot generate code for the call. The only way
+                // to execute such a function is via inline assembly.
+                if let ExternAbi::Custom = method.sig.abi {
+                    self.tcx.dcx().emit_err(crate::errors::AbiCustomCall { span: expr.span });
+                }
+
                 method.sig.output()
             }
             Err(error) => {
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs
index 7f1f3c3c802..e4c62bf027b 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs
@@ -120,15 +120,7 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for NestedObligationsForSelfTy<'a, 'tcx> {
     fn visit_goal(&mut self, inspect_goal: &InspectGoal<'_, 'tcx>) {
         // No need to walk into goal subtrees that certainly hold, since they
         // wouldn't then be stalled on an infer var.
-        // FIXME: We also walk into normalizes-to goals since their certainty
-        // is forced to `Certainty::Yes` since they pass down ambiguous subgoals
-        // to their parent.
-        if inspect_goal.result() == Ok(Certainty::Yes)
-            && !matches!(
-                inspect_goal.goal().predicate.kind().skip_binder(),
-                ty::PredicateKind::NormalizesTo(_)
-            )
-        {
+        if inspect_goal.result() == Ok(Certainty::Yes) {
             return;
         }
 
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
index 393556928af..e979798a402 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
@@ -382,7 +382,7 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> {
         _hir_id: rustc_hir::HirId,
         _hir_ty: Option<&hir::Ty<'_>>,
     ) -> (Vec<Ty<'tcx>>, Ty<'tcx>) {
-        let input_tys = decl.inputs.iter().map(|a| self.lowerer().lower_arg_ty(a, None)).collect();
+        let input_tys = decl.inputs.iter().map(|a| self.lowerer().lower_ty(a)).collect();
 
         let output_ty = match decl.output {
             hir::FnRetTy::Return(output) => self.lowerer().lower_ty(output),
diff --git a/compiler/rustc_hir_typeck/src/gather_locals.rs b/compiler/rustc_hir_typeck/src/gather_locals.rs
index 7d99b0e7869..f1d6476a0f3 100644
--- a/compiler/rustc_hir_typeck/src/gather_locals.rs
+++ b/compiler/rustc_hir_typeck/src/gather_locals.rs
@@ -202,7 +202,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
                         ),
                     );
                 }
-            } else if !self.fcx.tcx.features().unsized_locals() {
+            } else {
                 self.fcx.require_type_is_sized(
                     var_ty,
                     p.span,
diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs
index a45a7715340..043a687914b 100644
--- a/compiler/rustc_hir_typeck/src/lib.rs
+++ b/compiler/rustc_hir_typeck/src/lib.rs
@@ -48,7 +48,7 @@ use rustc_errors::{Applicability, ErrorGuaranteed, pluralize, struct_span_code_e
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{HirId, HirIdMap, Node};
-use rustc_hir_analysis::check::check_abi;
+use rustc_hir_analysis::check::{check_abi, check_custom_abi};
 use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
 use rustc_infer::traits::{ObligationCauseCode, ObligationInspector, WellFormedLoc};
 use rustc_middle::query::Providers;
@@ -138,7 +138,7 @@ fn typeck_with_inspect<'tcx>(
         // for visit the asm expr of the body.
         let ty = fcx.check_expr(body.value);
         fcx.write_ty(id, ty);
-    } else if let Some(hir::FnSig { header, decl, .. }) = node.fn_sig() {
+    } else if let Some(hir::FnSig { header, decl, span: fn_sig_span }) = node.fn_sig() {
         let fn_sig = if decl.output.is_suggestable_infer_ty().is_some() {
             // In the case that we're recovering `fn() -> W<_>` or some other return
             // type that has an infer in it, lower the type directly so that it'll
@@ -150,6 +150,8 @@ fn typeck_with_inspect<'tcx>(
         };
 
         check_abi(tcx, id, span, fn_sig.abi());
+        check_custom_abi(tcx, def_id, fn_sig.skip_binder(), *fn_sig_span);
+
         loops::check(tcx, def_id, body);
 
         // Compute the function signature from point of view of inside the fn.
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 2fac13b7201..288d915e85c 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -14,7 +14,9 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_data_structures::sorted_map::SortedMap;
 use rustc_data_structures::unord::UnordSet;
 use rustc_errors::codes::*;
-use rustc_errors::{Applicability, Diag, MultiSpan, StashKey, pluralize, struct_span_code_err};
+use rustc_errors::{
+    Applicability, Diag, DiagStyledString, MultiSpan, StashKey, pluralize, struct_span_code_err,
+};
 use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::{self, Visitor};
@@ -722,7 +724,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             {
                 let def_path = tcx.def_path_str(adt_def.did());
                 err.span_suggestion(
-                    ty.span.to(item_ident.span),
+                    sugg_span,
                     format!("to construct a value of type `{}`, use the explicit path", def_path),
                     def_path,
                     Applicability::MachineApplicable,
@@ -1569,7 +1571,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             );
         }
 
-        if rcvr_ty.is_numeric() && rcvr_ty.is_fresh() || restrict_type_params || suggested_derive {
+        if rcvr_ty.is_numeric() && rcvr_ty.is_fresh()
+            || restrict_type_params
+            || suggested_derive
+            || self.lookup_alternative_tuple_impls(&mut err, &unsatisfied_predicates)
+        {
         } else {
             self.suggest_traits_to_import(
                 &mut err,
@@ -1744,6 +1750,119 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         err.emit()
     }
 
+    /// If the predicate failure is caused by an unmet bound on a tuple, recheck if the bound would
+    /// succeed if all the types on the tuple had no borrows. This is a common problem for libraries
+    /// like Bevy and ORMs, which rely heavily on traits being implemented on tuples.
+    fn lookup_alternative_tuple_impls(
+        &self,
+        err: &mut Diag<'_>,
+        unsatisfied_predicates: &[(
+            ty::Predicate<'tcx>,
+            Option<ty::Predicate<'tcx>>,
+            Option<ObligationCause<'tcx>>,
+        )],
+    ) -> bool {
+        let mut found_tuple = false;
+        for (pred, root, _ob) in unsatisfied_predicates {
+            let mut preds = vec![pred];
+            if let Some(root) = root {
+                // We will look at both the current predicate and the root predicate that caused it
+                // to be needed. If calling something like `<(A, &B)>::default()`, then `pred` is
+                // `&B: Default` and `root` is `(A, &B): Default`, which is the one we are checking
+                // for further down, so we check both.
+                preds.push(root);
+            }
+            for pred in preds {
+                if let Some(clause) = pred.as_clause()
+                    && let Some(clause) = clause.as_trait_clause()
+                    && let ty = clause.self_ty().skip_binder()
+                    && let ty::Tuple(types) = ty.kind()
+                {
+                    let path = clause.skip_binder().trait_ref.print_only_trait_path();
+                    let def_id = clause.def_id();
+                    let ty = Ty::new_tup(
+                        self.tcx,
+                        self.tcx.mk_type_list_from_iter(types.iter().map(|ty| ty.peel_refs())),
+                    );
+                    let args = ty::GenericArgs::for_item(self.tcx, def_id, |param, _| {
+                        if param.index == 0 {
+                            ty.into()
+                        } else {
+                            self.infcx.var_for_def(DUMMY_SP, param)
+                        }
+                    });
+                    if self
+                        .infcx
+                        .type_implements_trait(def_id, args, self.param_env)
+                        .must_apply_modulo_regions()
+                    {
+                        // "`Trait` is implemented for `(A, B)` but not for `(A, &B)`"
+                        let mut msg = DiagStyledString::normal(format!("`{path}` "));
+                        msg.push_highlighted("is");
+                        msg.push_normal(" implemented for `(");
+                        let len = types.len();
+                        for (i, t) in types.iter().enumerate() {
+                            msg.push(
+                                format!("{}", with_forced_trimmed_paths!(t.peel_refs())),
+                                t.peel_refs() != t,
+                            );
+                            if i < len - 1 {
+                                msg.push_normal(", ");
+                            }
+                        }
+                        msg.push_normal(")` but ");
+                        msg.push_highlighted("not");
+                        msg.push_normal(" for `(");
+                        for (i, t) in types.iter().enumerate() {
+                            msg.push(
+                                format!("{}", with_forced_trimmed_paths!(t)),
+                                t.peel_refs() != t,
+                            );
+                            if i < len - 1 {
+                                msg.push_normal(", ");
+                            }
+                        }
+                        msg.push_normal(")`");
+
+                        // Find the span corresponding to the impl that was found to point at it.
+                        if let Some(impl_span) = self
+                            .tcx
+                            .all_impls(def_id)
+                            .filter(|&impl_def_id| {
+                                let header = self.tcx.impl_trait_header(impl_def_id).unwrap();
+                                let trait_ref = header.trait_ref.instantiate(
+                                    self.tcx,
+                                    self.infcx.fresh_args_for_item(DUMMY_SP, impl_def_id),
+                                );
+
+                                let value = ty::fold_regions(self.tcx, ty, |_, _| {
+                                    self.tcx.lifetimes.re_erased
+                                });
+                                // FIXME: Don't bother dealing with non-lifetime binders here...
+                                if value.has_escaping_bound_vars() {
+                                    return false;
+                                }
+                                self.infcx.can_eq(ty::ParamEnv::empty(), trait_ref.self_ty(), value)
+                                    && header.polarity == ty::ImplPolarity::Positive
+                            })
+                            .map(|impl_def_id| self.tcx.def_span(impl_def_id))
+                            .next()
+                        {
+                            err.highlighted_span_note(impl_span, msg.0);
+                        } else {
+                            err.highlighted_note(msg.0);
+                        }
+                        found_tuple = true;
+                    }
+                    // If `pred` was already on the tuple, we don't need to look at the root
+                    // obligation too.
+                    break;
+                }
+            }
+        }
+        found_tuple
+    }
+
     /// If an appropriate error source is not found, check method chain for possible candidates
     fn lookup_segments_chain_for_no_match_method(
         &self,
diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs
index 5b5253c7e7e..982cfa2e42b 100644
--- a/compiler/rustc_hir_typeck/src/upvar.rs
+++ b/compiler/rustc_hir_typeck/src/upvar.rs
@@ -492,7 +492,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let final_upvar_tys = self.final_upvar_tys(closure_def_id);
         debug!(?closure_hir_id, ?args, ?final_upvar_tys);
 
-        if self.tcx.features().unsized_locals() || self.tcx.features().unsized_fn_params() {
+        if self.tcx.features().unsized_fn_params() {
             for capture in
                 self.typeck_results.borrow().closure_min_captures_flattened(closure_def_id)
             {
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index 99520a3fea3..0238d6a3947 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -8,9 +8,9 @@ use std::{env, fs, iter};
 use rustc_ast as ast;
 use rustc_codegen_ssa::traits::CodegenBackend;
 use rustc_data_structures::jobserver::Proxy;
-use rustc_data_structures::parallel;
 use rustc_data_structures::steal::Steal;
 use rustc_data_structures::sync::{AppendOnlyIndexVec, FreezeLock, WorkerLocal};
+use rustc_data_structures::{parallel, thousands};
 use rustc_expand::base::{ExtCtxt, LintStoreExpand};
 use rustc_feature::Features;
 use rustc_fs_util::try_canonicalize;
@@ -35,7 +35,8 @@ use rustc_session::parse::feature_err;
 use rustc_session::search_paths::PathKind;
 use rustc_session::{Limit, Session};
 use rustc_span::{
-    DUMMY_SP, ErrorGuaranteed, FileName, SourceFileHash, SourceFileHashAlgorithm, Span, Symbol, sym,
+    DUMMY_SP, ErrorGuaranteed, ExpnKind, FileName, SourceFileHash, SourceFileHashAlgorithm, Span,
+    Symbol, sym,
 };
 use rustc_target::spec::PanicStrategy;
 use rustc_trait_selection::traits;
@@ -205,7 +206,7 @@ fn configure_and_expand(
         // Expand macros now!
         let krate = sess.time("expand_crate", || ecx.monotonic_expander().expand_crate(krate));
 
-        // The rest is error reporting
+        // The rest is error reporting and stats
 
         sess.psess.buffered_lints.with_lock(|buffered_lints: &mut Vec<BufferedEarlyLint>| {
             buffered_lints.append(&mut ecx.buffered_early_lint);
@@ -228,6 +229,10 @@ fn configure_and_expand(
             }
         }
 
+        if ecx.sess.opts.unstable_opts.macro_stats {
+            print_macro_stats(&ecx);
+        }
+
         krate
     });
 
@@ -288,6 +293,76 @@ fn configure_and_expand(
     krate
 }
 
+fn print_macro_stats(ecx: &ExtCtxt<'_>) {
+    use std::fmt::Write;
+
+    // No instability because we immediately sort the produced vector.
+    #[allow(rustc::potential_query_instability)]
+    let mut macro_stats: Vec<_> = ecx
+        .macro_stats
+        .iter()
+        .map(|((name, kind), stat)| {
+            // This gives the desired sort order: sort by bytes, then lines, etc.
+            (stat.bytes, stat.lines, stat.uses, name, *kind)
+        })
+        .collect();
+    macro_stats.sort_unstable();
+    macro_stats.reverse(); // bigger items first
+
+    let prefix = "macro-stats";
+    let name_w = 32;
+    let uses_w = 7;
+    let lines_w = 11;
+    let avg_lines_w = 11;
+    let bytes_w = 11;
+    let avg_bytes_w = 11;
+    let banner_w = name_w + uses_w + lines_w + avg_lines_w + bytes_w + avg_bytes_w;
+
+    // We write all the text into a string and print it with a single
+    // `eprint!`. This is an attempt to minimize interleaved text if multiple
+    // rustc processes are printing macro-stats at the same time (e.g. with
+    // `RUSTFLAGS='-Zmacro-stats' cargo build`). It still doesn't guarantee
+    // non-interleaving, though.
+    let mut s = String::new();
+    _ = writeln!(s, "{prefix} {}", "=".repeat(banner_w));
+    _ = writeln!(s, "{prefix} MACRO EXPANSION STATS: {}", ecx.ecfg.crate_name);
+    _ = writeln!(
+        s,
+        "{prefix} {:<name_w$}{:>uses_w$}{:>lines_w$}{:>avg_lines_w$}{:>bytes_w$}{:>avg_bytes_w$}",
+        "Macro Name", "Uses", "Lines", "Avg Lines", "Bytes", "Avg Bytes",
+    );
+    _ = writeln!(s, "{prefix} {}", "-".repeat(banner_w));
+    // It's helpful to print something when there are no entries, otherwise it
+    // might look like something went wrong.
+    if macro_stats.is_empty() {
+        _ = writeln!(s, "{prefix} (none)");
+    }
+    for (bytes, lines, uses, name, kind) in macro_stats {
+        let mut name = ExpnKind::Macro(kind, *name).descr();
+        let avg_lines = lines as f64 / uses as f64;
+        let avg_bytes = bytes as f64 / uses as f64;
+        if name.len() >= name_w {
+            // If the name is long, print it on a line by itself, then
+            // set the name to empty and print things normally, to show the
+            // stats on the next line.
+            _ = writeln!(s, "{prefix} {:<name_w$}", name);
+            name = String::new();
+        }
+        _ = writeln!(
+            s,
+            "{prefix} {:<name_w$}{:>uses_w$}{:>lines_w$}{:>avg_lines_w$}{:>bytes_w$}{:>avg_bytes_w$}",
+            name,
+            thousands::usize_with_underscores(uses),
+            thousands::isize_with_underscores(lines),
+            thousands::f64p1_with_underscores(avg_lines),
+            thousands::isize_with_underscores(bytes),
+            thousands::f64p1_with_underscores(avg_bytes),
+        );
+    }
+    _ = writeln!(s, "{prefix} {}", "=".repeat(banner_w));
+    eprint!("{s}");
+}
+
 fn early_lint_checks(tcx: TyCtxt<'_>, (): ()) {
     let sess = tcx.sess;
     let (resolver, krate) = &*tcx.resolver_for_lowering().borrow();
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index 558f13a832c..5419d688caa 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -709,6 +709,7 @@ fn test_unstable_options_tracking_hash() {
     untracked!(llvm_time_trace, true);
     untracked!(ls, vec!["all".to_owned()]);
     untracked!(macro_backtrace, true);
+    untracked!(macro_stats, true);
     untracked!(meta_stats, true);
     untracked!(mir_include_spans, MirIncludeSpans::On);
     untracked!(nll_facts, true);
diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl
index fd2e2ba39ac..46ae4146528 100644
--- a/compiler/rustc_lint/messages.ftl
+++ b/compiler/rustc_lint/messages.ftl
@@ -72,9 +72,6 @@ lint_builtin_const_no_mangle = const items should never be `#[no_mangle]`
 lint_builtin_decl_unsafe_fn = declaration of an `unsafe` function
 lint_builtin_decl_unsafe_method = declaration of an `unsafe` method
 
-lint_builtin_deprecated_attr_link = use of deprecated attribute `{$name}`: {$reason}. See {$link}
-    .msg_suggestion = {$msg}
-    .default_suggestion = remove this attribute
 lint_builtin_deref_nullptr = dereferencing a null pointer
     .label = this code causes undefined behavior when executed
 
@@ -533,8 +530,6 @@ lint_mismatched_lifetime_syntaxes_suggestion_implicit =
 lint_mismatched_lifetime_syntaxes_suggestion_mixed =
     one option is to remove the lifetime for references and use the anonymous lifetime for paths
 
-lint_missing_fragment_specifier = missing fragment specifier
-
 lint_missing_unsafe_on_extern = extern blocks should be unsafe
     .suggestion = needs `unsafe` before the extern keyword
 
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 69e9f8e1b2c..dedea54f8e0 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -22,7 +22,7 @@ use rustc_ast::visit::{FnCtxt, FnKind};
 use rustc_ast::{self as ast, *};
 use rustc_ast_pretty::pprust::expr_to_string;
 use rustc_errors::{Applicability, LintDiagnostic};
-use rustc_feature::{AttributeGate, BuiltinAttribute, GateIssue, Stability, deprecated_attributes};
+use rustc_feature::GateIssue;
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
@@ -48,8 +48,7 @@ use rustc_trait_selection::traits::{self};
 
 use crate::errors::BuiltinEllipsisInclusiveRangePatterns;
 use crate::lints::{
-    BuiltinAnonymousParams, BuiltinConstNoMangle, BuiltinDeprecatedAttrLink,
-    BuiltinDeprecatedAttrLinkSuggestion, BuiltinDerefNullptr, BuiltinDoubleNegations,
+    BuiltinAnonymousParams, BuiltinConstNoMangle, BuiltinDerefNullptr, BuiltinDoubleNegations,
     BuiltinDoubleNegationsAddParens, BuiltinEllipsisInclusiveRangePatternsLint,
     BuiltinExplicitOutlives, BuiltinExplicitOutlivesSuggestion, BuiltinFeatureIssueNote,
     BuiltinIncompleteFeatures, BuiltinIncompleteFeaturesHelp, BuiltinInternalFeatures,
@@ -799,53 +798,6 @@ impl EarlyLintPass for AnonymousParameters {
     }
 }
 
-/// Check for use of attributes which have been deprecated.
-#[derive(Clone)]
-pub struct DeprecatedAttr {
-    // This is not free to compute, so we want to keep it around, rather than
-    // compute it for every attribute.
-    depr_attrs: Vec<&'static BuiltinAttribute>,
-}
-
-impl_lint_pass!(DeprecatedAttr => []);
-
-impl Default for DeprecatedAttr {
-    fn default() -> Self {
-        DeprecatedAttr { depr_attrs: deprecated_attributes() }
-    }
-}
-
-impl EarlyLintPass for DeprecatedAttr {
-    fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) {
-        for BuiltinAttribute { name, gate, .. } in &self.depr_attrs {
-            if attr.ident().map(|ident| ident.name) == Some(*name) {
-                if let &AttributeGate::Gated(
-                    Stability::Deprecated(link, suggestion),
-                    name,
-                    reason,
-                    _,
-                ) = gate
-                {
-                    let suggestion = match suggestion {
-                        Some(msg) => {
-                            BuiltinDeprecatedAttrLinkSuggestion::Msg { suggestion: attr.span, msg }
-                        }
-                        None => {
-                            BuiltinDeprecatedAttrLinkSuggestion::Default { suggestion: attr.span }
-                        }
-                    };
-                    cx.emit_span_lint(
-                        DEPRECATED,
-                        attr.span,
-                        BuiltinDeprecatedAttrLink { name, reason, link, suggestion },
-                    );
-                }
-                return;
-            }
-        }
-    }
-}
-
 fn warn_if_doc(cx: &EarlyContext<'_>, node_span: Span, node_kind: &str, attrs: &[ast::Attribute]) {
     use rustc_ast::token::CommentKind;
 
diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs
index 12666d383f9..48e3bbb79fa 100644
--- a/compiler/rustc_lint/src/early.rs
+++ b/compiler/rustc_lint/src/early.rs
@@ -33,10 +33,8 @@ pub struct EarlyContextAndPass<'ecx, 'tcx, T: EarlyLintPass> {
 }
 
 impl<'ecx, 'tcx, T: EarlyLintPass> EarlyContextAndPass<'ecx, 'tcx, T> {
-    // This always-inlined function is for the hot call site.
-    #[inline(always)]
     #[allow(rustc::diagnostic_outside_of_impl)]
-    fn inlined_check_id(&mut self, id: ast::NodeId) {
+    fn check_id(&mut self, id: ast::NodeId) {
         for early_lint in self.context.buffered.take(id) {
             let BufferedEarlyLint { span, node_id: _, lint_id, diagnostic } = early_lint;
             self.context.opt_span_lint(lint_id.lint, span, |diag| {
@@ -45,11 +43,6 @@ impl<'ecx, 'tcx, T: EarlyLintPass> EarlyContextAndPass<'ecx, 'tcx, T> {
         }
     }
 
-    // This non-inlined function is for the cold call sites.
-    fn check_id(&mut self, id: ast::NodeId) {
-        self.inlined_check_id(id)
-    }
-
     /// Merge the lints specified by any lint attributes into the
     /// current lint context, call the provided function, then reset the
     /// lints in effect to their previous state.
@@ -61,7 +54,6 @@ impl<'ecx, 'tcx, T: EarlyLintPass> EarlyContextAndPass<'ecx, 'tcx, T> {
         debug!(?id);
         let push = self.context.builder.push(attrs, is_crate_node, None);
 
-        self.inlined_check_id(id);
         debug!("early context: enter_attrs({:?})", attrs);
         lint_callback!(self, check_attributes, attrs);
         ensure_sufficient_stack(|| f(self));
@@ -136,12 +128,8 @@ impl<'ast, 'ecx, 'tcx, T: EarlyLintPass> ast_visit::Visitor<'ast>
         // the AST struct that they wrap (e.g. an item)
         self.with_lint_attrs(s.id, s.attrs(), |cx| {
             lint_callback!(cx, check_stmt, s);
+            ast_visit::walk_stmt(cx, s);
         });
-        // The visitor for the AST struct wrapped
-        // by the statement (e.g. `Item`) will call
-        // `with_lint_attrs`, so do this walk
-        // outside of the above `with_lint_attrs` call
-        ast_visit::walk_stmt(self, s);
     }
 
     fn visit_fn(&mut self, fk: ast_visit::FnKind<'ast>, span: Span, id: ast::NodeId) {
diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs
index 60c477dd6c7..3b0a36186b6 100644
--- a/compiler/rustc_lint/src/early/diagnostics.rs
+++ b/compiler/rustc_lint/src/early/diagnostics.rs
@@ -432,9 +432,6 @@ pub fn decorate_builtin_lint(
         BuiltinLintDiag::CfgAttrNoAttributes => {
             lints::CfgAttrNoAttributes.decorate_lint(diag);
         }
-        BuiltinLintDiag::MissingFragmentSpecifier => {
-            lints::MissingFragmentSpecifier.decorate_lint(diag);
-        }
         BuiltinLintDiag::MetaVariableStillRepeating(name) => {
             lints::MetaVariableStillRepeating { name }.decorate_lint(diag);
         }
diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs
index 8124d7f7c86..aa6f36a67f0 100644
--- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs
+++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs
@@ -15,8 +15,7 @@ use rustc_middle::ty::relate::{
     Relate, RelateResult, TypeRelation, structurally_relate_consts, structurally_relate_tys,
 };
 use rustc_middle::ty::{
-    self, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
-    TypeVisitor,
+    self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
 };
 use rustc_middle::{bug, span_bug};
 use rustc_session::lint::FutureIncompatibilityReason;
@@ -210,7 +209,7 @@ where
     VarFn: FnOnce() -> FxHashMap<DefId, ty::Variance>,
     OutlivesFn: FnOnce() -> OutlivesEnvironment<'tcx>,
 {
-    fn visit_binder<T: TypeFoldable<TyCtxt<'tcx>>>(&mut self, t: &ty::Binder<'tcx, T>) {
+    fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &ty::Binder<'tcx, T>) {
         // When we get into a binder, we need to add its own bound vars to the scope.
         let mut added = vec![];
         for arg in t.bound_vars() {
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index 72bfeaddbf1..e84cdb581b5 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -174,7 +174,6 @@ early_lint_methods!(
             AnonymousParameters: AnonymousParameters,
             EllipsisInclusiveRangePatterns: EllipsisInclusiveRangePatterns::default(),
             NonCamelCaseTypes: NonCamelCaseTypes,
-            DeprecatedAttr: DeprecatedAttr::default(),
             WhileTrue: WhileTrue,
             NonAsciiIdents: NonAsciiIdents,
             IncompleteInternalFeatures: IncompleteInternalFeatures,
@@ -619,6 +618,11 @@ fn register_builtins(store: &mut LintStore) {
         "converted into hard error, \
          see <https://github.com/rust-lang/rust/issues/116558> for more information",
     );
+    store.register_removed(
+        "missing_fragment_specifier",
+        "converted into hard error, \
+         see <https://github.com/rust-lang/rust/issues/40107> for more information",
+    );
 }
 
 fn register_internals(store: &mut LintStore) {
diff --git a/compiler/rustc_lint/src/lifetime_syntax.rs b/compiler/rustc_lint/src/lifetime_syntax.rs
index 31b038e6a46..95b7b69bd5a 100644
--- a/compiler/rustc_lint/src/lifetime_syntax.rs
+++ b/compiler/rustc_lint/src/lifetime_syntax.rs
@@ -84,19 +84,45 @@ impl<'tcx> LateLintPass<'tcx> for LifetimeSyntax {
         _: rustc_span::Span,
         _: rustc_span::def_id::LocalDefId,
     ) {
-        let mut input_map = Default::default();
-        let mut output_map = Default::default();
+        check_fn_like(cx, fd);
+    }
 
-        for input in fd.inputs {
-            LifetimeInfoCollector::collect(input, &mut input_map);
+    #[instrument(skip_all)]
+    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, ti: &'tcx hir::TraitItem<'tcx>) {
+        match ti.kind {
+            hir::TraitItemKind::Const(..) => {}
+            hir::TraitItemKind::Fn(fn_sig, _trait_fn) => check_fn_like(cx, fn_sig.decl),
+            hir::TraitItemKind::Type(..) => {}
         }
+    }
 
-        if let hir::FnRetTy::Return(output) = fd.output {
-            LifetimeInfoCollector::collect(output, &mut output_map);
+    #[instrument(skip_all)]
+    fn check_foreign_item(
+        &mut self,
+        cx: &LateContext<'tcx>,
+        fi: &'tcx rustc_hir::ForeignItem<'tcx>,
+    ) {
+        match fi.kind {
+            hir::ForeignItemKind::Fn(fn_sig, _idents, _generics) => check_fn_like(cx, fn_sig.decl),
+            hir::ForeignItemKind::Static(..) => {}
+            hir::ForeignItemKind::Type => {}
         }
+    }
+}
+
+fn check_fn_like<'tcx>(cx: &LateContext<'tcx>, fd: &'tcx hir::FnDecl<'tcx>) {
+    let mut input_map = Default::default();
+    let mut output_map = Default::default();
 
-        report_mismatches(cx, &input_map, &output_map);
+    for input in fd.inputs {
+        LifetimeInfoCollector::collect(input, &mut input_map);
     }
+
+    if let hir::FnRetTy::Return(output) = fd.output {
+        LifetimeInfoCollector::collect(output, &mut output_map);
+    }
+
+    report_mismatches(cx, &input_map, &output_map);
 }
 
 #[instrument(skip_all)]
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index 9d3c74a9a2b..3d17dfbc451 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -199,32 +199,6 @@ pub(crate) struct BuiltinAnonymousParams<'a> {
     pub ty_snip: &'a str,
 }
 
-// FIXME(davidtwco) translatable deprecated attr
-#[derive(LintDiagnostic)]
-#[diag(lint_builtin_deprecated_attr_link)]
-pub(crate) struct BuiltinDeprecatedAttrLink<'a> {
-    pub name: Symbol,
-    pub reason: &'a str,
-    pub link: &'a str,
-    #[subdiagnostic]
-    pub suggestion: BuiltinDeprecatedAttrLinkSuggestion<'a>,
-}
-
-#[derive(Subdiagnostic)]
-pub(crate) enum BuiltinDeprecatedAttrLinkSuggestion<'a> {
-    #[suggestion(lint_msg_suggestion, code = "", applicability = "machine-applicable")]
-    Msg {
-        #[primary_span]
-        suggestion: Span,
-        msg: &'a str,
-    },
-    #[suggestion(lint_default_suggestion, code = "", applicability = "machine-applicable")]
-    Default {
-        #[primary_span]
-        suggestion: Span,
-    },
-}
-
 #[derive(LintDiagnostic)]
 #[diag(lint_builtin_unused_doc_comment)]
 pub(crate) struct BuiltinUnusedDocComment<'a> {
@@ -2617,10 +2591,6 @@ pub(crate) struct DuplicateMacroAttribute;
 pub(crate) struct CfgAttrNoAttributes;
 
 #[derive(LintDiagnostic)]
-#[diag(lint_missing_fragment_specifier)]
-pub(crate) struct MissingFragmentSpecifier;
-
-#[derive(LintDiagnostic)]
 #[diag(lint_metavariable_still_repeating)]
 pub(crate) struct MetaVariableStillRepeating {
     pub name: MacroRulesNormalizedIdent,
diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs
index 31c18074466..1b60466a589 100644
--- a/compiler/rustc_lint/src/nonstandard_style.rs
+++ b/compiler/rustc_lint/src/nonstandard_style.rs
@@ -164,7 +164,7 @@ impl NonCamelCaseTypes {
 impl EarlyLintPass for NonCamelCaseTypes {
     fn check_item(&mut self, cx: &EarlyContext<'_>, it: &ast::Item) {
         let has_repr_c = matches!(
-            AttributeParser::parse_limited(cx.sess(), &it.attrs, sym::repr, it.span, true),
+            AttributeParser::parse_limited(cx.sess(), &it.attrs, sym::repr, it.span, it.id),
             Some(Attribute::Parsed(AttributeKind::Repr(r))) if r.iter().any(|(r, _)| r == &ReprAttr::ReprC)
         );
 
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index fec23354c91..aaba0c14b1c 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -196,7 +196,8 @@ declare_lint! {
     /// same address after being merged together.
     UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
     Warn,
-    "detects unpredictable function pointer comparisons"
+    "detects unpredictable function pointer comparisons",
+    report_in_external_macro
 }
 
 #[derive(Copy, Clone, Default)]
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index 777118e69fb..295dd82fead 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -65,7 +65,6 @@ declare_lint_pass! {
         MACRO_USE_EXTERN_CRATE,
         META_VARIABLE_MISUSE,
         MISSING_ABI,
-        MISSING_FRAGMENT_SPECIFIER,
         MISSING_UNSAFE_ON_EXTERN,
         MUST_NOT_SUSPEND,
         NAMED_ARGUMENTS_USED_POSITIONALLY,
@@ -1418,51 +1417,6 @@ declare_lint! {
 }
 
 declare_lint! {
-    /// The `missing_fragment_specifier` lint is issued when an unused pattern in a
-    /// `macro_rules!` macro definition has a meta-variable (e.g. `$e`) that is not
-    /// followed by a fragment specifier (e.g. `:expr`).
-    ///
-    /// This warning can always be fixed by removing the unused pattern in the
-    /// `macro_rules!` macro definition.
-    ///
-    /// ### Example
-    ///
-    /// ```rust,compile_fail,edition2021
-    /// macro_rules! foo {
-    ///    () => {};
-    ///    ($name) => { };
-    /// }
-    ///
-    /// fn main() {
-    ///    foo!();
-    /// }
-    /// ```
-    ///
-    /// {{produces}}
-    ///
-    /// ### Explanation
-    ///
-    /// To fix this, remove the unused pattern from the `macro_rules!` macro definition:
-    ///
-    /// ```rust
-    /// macro_rules! foo {
-    ///     () => {};
-    /// }
-    /// fn main() {
-    ///     foo!();
-    /// }
-    /// ```
-    pub MISSING_FRAGMENT_SPECIFIER,
-    Deny,
-    "detects missing fragment specifiers in unused `macro_rules!` patterns",
-    @future_incompatible = FutureIncompatibleInfo {
-        reason: FutureIncompatibilityReason::FutureReleaseError,
-        reference: "issue #40107 <https://github.com/rust-lang/rust/issues/40107>",
-        report_in_deps: true,
-    };
-}
-
-declare_lint! {
     /// The `late_bound_lifetime_arguments` lint detects generic lifetime
     /// arguments in path segments with late bound lifetime parameters.
     ///
@@ -3664,7 +3618,7 @@ declare_lint! {
     "use of unsupported calling convention",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::FutureReleaseError,
-        report_in_deps: true,
+        report_in_deps: false,
         reference: "issue #137018 <https://github.com/rust-lang/rust/issues/137018>",
     };
 }
diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs
index 1d9b7a7fcb9..cd402c9234f 100644
--- a/compiler/rustc_lint_defs/src/lib.rs
+++ b/compiler/rustc_lint_defs/src/lib.rs
@@ -778,7 +778,6 @@ pub enum BuiltinLintDiag {
     UnnameableTestItems,
     DuplicateMacroAttribute,
     CfgAttrNoAttributes,
-    MissingFragmentSpecifier,
     MetaVariableStillRepeating(MacroRulesNormalizedIdent),
     MetaVariableWrongOperator,
     DuplicateMatcherBinding,
diff --git a/compiler/rustc_log/src/lib.rs b/compiler/rustc_log/src/lib.rs
index 1bb502ca3d0..df648bbd489 100644
--- a/compiler/rustc_log/src/lib.rs
+++ b/compiler/rustc_log/src/lib.rs
@@ -43,6 +43,7 @@ use tracing_subscriber::filter::{Directive, EnvFilter, LevelFilter};
 use tracing_subscriber::fmt::FmtContext;
 use tracing_subscriber::fmt::format::{self, FormatEvent, FormatFields};
 use tracing_subscriber::layer::SubscriberExt;
+use tracing_subscriber::{Layer, Registry};
 
 /// The values of all the environment variables that matter for configuring a logger.
 /// Errors are explicitly preserved so that we can share error handling.
@@ -72,6 +73,36 @@ impl LoggerConfig {
 
 /// Initialize the logger with the given values for the filter, coloring, and other options env variables.
 pub fn init_logger(cfg: LoggerConfig) -> Result<(), Error> {
+    init_logger_with_additional_layer(cfg, || Registry::default())
+}
+
+/// Trait alias for the complex return type of `build_subscriber` in
+/// [init_logger_with_additional_layer]. A [Registry] with any composition of [tracing::Subscriber]s
+/// (e.g. `Registry::default().with(custom_layer)`) should be compatible with this type.
+/// Having an alias is also useful so rustc_driver_impl does not need to explicitly depend on
+/// `tracing_subscriber`.
+pub trait BuildSubscriberRet:
+    tracing::Subscriber + for<'span> tracing_subscriber::registry::LookupSpan<'span> + Send + Sync
+{
+}
+
+impl<
+    T: tracing::Subscriber + for<'span> tracing_subscriber::registry::LookupSpan<'span> + Send + Sync,
+> BuildSubscriberRet for T
+{
+}
+
+/// Initialize the logger with the given values for the filter, coloring, and other options env variables.
+/// Additionally add a custom layer to collect logging and tracing events via `build_subscriber`,
+/// for example: `|| Registry::default().with(custom_layer)`.
+pub fn init_logger_with_additional_layer<F, T>(
+    cfg: LoggerConfig,
+    build_subscriber: F,
+) -> Result<(), Error>
+where
+    F: FnOnce() -> T,
+    T: BuildSubscriberRet,
+{
     let filter = match cfg.filter {
         Ok(env) => EnvFilter::new(env),
         _ => EnvFilter::default().add_directive(Directive::from(LevelFilter::WARN)),
@@ -124,7 +155,7 @@ pub fn init_logger(cfg: LoggerConfig) -> Result<(), Error> {
         Err(_) => {} // no wraptree
     }
 
-    let subscriber = tracing_subscriber::Registry::default().with(filter).with(layer);
+    let subscriber = build_subscriber().with(layer.with_filter(filter));
     match cfg.backtrace {
         Ok(backtrace_target) => {
             let fmt_layer = tracing_subscriber::fmt::layer()
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index 3ab989d2d3b..00bd32eb0eb 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -10,7 +10,7 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_data_structures::memmap::{Mmap, MmapMut};
 use rustc_data_structures::sync::{join, par_for_each_in};
 use rustc_data_structures::temp_dir::MaybeTempDir;
-use rustc_data_structures::thousands::format_with_underscores;
+use rustc_data_structures::thousands::usize_with_underscores;
 use rustc_feature::Features;
 use rustc_hir as hir;
 use rustc_hir::def_id::{CRATE_DEF_ID, CRATE_DEF_INDEX, LOCAL_CRATE, LocalDefId, LocalDefIdSet};
@@ -789,7 +789,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
                     "{} {:<23}{:>10} ({:4.1}%)",
                     prefix,
                     label,
-                    format_with_underscores(size),
+                    usize_with_underscores(size),
                     perc(size)
                 );
             }
@@ -798,7 +798,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
                 "{} {:<23}{:>10} (of which {:.1}% are zero bytes)",
                 prefix,
                 "Total",
-                format_with_underscores(total_bytes),
+                usize_with_underscores(total_bytes),
                 perc(zero_bytes)
             );
             eprintln!("{prefix}");
diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs
index d1f5caaafb2..cb4760c18de 100644
--- a/compiler/rustc_middle/src/hir/mod.rs
+++ b/compiler/rustc_middle/src/hir/mod.rs
@@ -12,6 +12,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::sync::{DynSend, DynSync, try_par_for_each_in};
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId};
+use rustc_hir::lints::DelayedLint;
 use rustc_hir::*;
 use rustc_macros::{Decodable, Encodable, HashStable};
 use rustc_span::{ErrorGuaranteed, ExpnId, Span};
@@ -161,8 +162,9 @@ impl<'tcx> TyCtxt<'tcx> {
         node: OwnerNode<'_>,
         bodies: &SortedMap<ItemLocalId, &Body<'_>>,
         attrs: &SortedMap<ItemLocalId, &[Attribute]>,
+        delayed_lints: &[DelayedLint],
         define_opaque: Option<&[(Span, LocalDefId)]>,
-    ) -> (Option<Fingerprint>, Option<Fingerprint>) {
+    ) -> (Option<Fingerprint>, Option<Fingerprint>, Option<Fingerprint>) {
         if self.needs_crate_hash() {
             self.with_stable_hashing_context(|mut hcx| {
                 let mut stable_hasher = StableHasher::new();
@@ -178,10 +180,16 @@ impl<'tcx> TyCtxt<'tcx> {
                 define_opaque.hash_stable(&mut hcx, &mut stable_hasher);
 
                 let h2 = stable_hasher.finish();
-                (Some(h1), Some(h2))
+
+                // hash lints emitted during ast lowering
+                let mut stable_hasher = StableHasher::new();
+                delayed_lints.hash_stable(&mut hcx, &mut stable_hasher);
+                let h3 = stable_hasher.finish();
+
+                (Some(h1), Some(h2), Some(h3))
             })
         } else {
-            (None, None)
+            (None, None, None)
         }
     }
 }
@@ -214,6 +222,8 @@ pub fn provide(providers: &mut Providers) {
     providers.hir_attr_map = |tcx, id| {
         tcx.hir_crate(()).owners[id.def_id].as_owner().map_or(AttributeMap::EMPTY, |o| &o.attrs)
     };
+    providers.opt_ast_lowering_delayed_lints =
+        |tcx, id| tcx.hir_crate(()).owners[id.def_id].as_owner().map(|o| &o.delayed_lints);
     providers.def_span = |tcx, def_id| tcx.hir_span(tcx.local_def_id_to_hir_id(def_id));
     providers.def_ident_span = |tcx, def_id| {
         let hir_id = tcx.local_def_id_to_hir_id(def_id);
diff --git a/compiler/rustc_middle/src/middle/mod.rs b/compiler/rustc_middle/src/middle/mod.rs
index 4587dcaddc4..6ca1e620704 100644
--- a/compiler/rustc_middle/src/middle/mod.rs
+++ b/compiler/rustc_middle/src/middle/mod.rs
@@ -12,7 +12,7 @@ pub mod lib_features {
     #[derive(HashStable, TyEncodable, TyDecodable)]
     pub enum FeatureStability {
         AcceptedSince(Symbol),
-        Unstable,
+        Unstable { old_name: Option<Symbol> },
     }
 
     #[derive(HashStable, Debug, Default)]
diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs
index 454ab8c107f..ab6a65ed526 100644
--- a/compiler/rustc_middle/src/middle/stability.rs
+++ b/compiler/rustc_middle/src/middle/stability.rs
@@ -411,7 +411,7 @@ impl<'tcx> TyCtxt<'tcx> {
 
         match stability {
             Some(Stability {
-                level: attrs::StabilityLevel::Unstable { reason, issue, is_soft, implied_by },
+                level: attrs::StabilityLevel::Unstable { reason, issue, is_soft, implied_by, .. },
                 feature,
                 ..
             }) => {
diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs
index f2f975a6968..56f19d7929d 100644
--- a/compiler/rustc_middle/src/mir/syntax.rs
+++ b/compiler/rustc_middle/src/mir/syntax.rs
@@ -1133,13 +1133,6 @@ pub type AssertMessage<'tcx> = AssertKind<Operand<'tcx>>;
 /// Each local naturally corresponds to the place `Place { local, projection: [] }`. This place has
 /// the address of the local's allocation and the type of the local.
 ///
-/// **Needs clarification:** Unsized locals seem to present a bit of an issue. Their allocation
-/// can't actually be created on `StorageLive`, because it's unclear how big to make the allocation.
-/// Furthermore, MIR produces assignments to unsized locals, although that is not permitted under
-/// `#![feature(unsized_locals)]` in Rust. Besides just putting "unsized locals are special and
-/// different" in a bunch of places, I (JakobDegen) don't know how to incorporate this behavior into
-/// the current MIR semantics in a clean way - possibly this needs some design work first.
-///
 /// For places that are not locals, ie they have a non-empty list of projections, we define the
 /// values as a function of the parent place, that is the place with its last [`ProjectionElem`]
 /// stripped. The way this is computed of course depends on the kind of that last projection
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index d8c927e00db..15e8c1ef3cc 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -221,6 +221,14 @@ rustc_queries! {
         feedable
     }
 
+    /// Gives access to lints emitted during ast lowering.
+    ///
+    /// This can be conveniently accessed by `tcx.hir_*` methods.
+    /// Avoid calling this query directly.
+    query opt_ast_lowering_delayed_lints(key: hir::OwnerId) -> Option<&'tcx hir::lints::DelayedLints> {
+        desc { |tcx| "getting AST lowering delayed lints in `{}`", tcx.def_path_str(key) }
+    }
+
     /// Returns the *default* of the const pararameter given by `DefId`.
     ///
     /// E.g., given `struct Ty<const N: usize = 3>;` this returns `3` for `N`.
diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs
index 455ac660412..2fbaa2221a1 100644
--- a/compiler/rustc_middle/src/ty/consts.rs
+++ b/compiler/rustc_middle/src/ty/consts.rs
@@ -163,6 +163,10 @@ impl<'tcx> rustc_type_ir::inherent::Const<TyCtxt<'tcx>> for Const<'tcx> {
         Const::new_bound(tcx, debruijn, var)
     }
 
+    fn new_placeholder(tcx: TyCtxt<'tcx>, placeholder: ty::PlaceholderConst) -> Self {
+        Const::new_placeholder(tcx, placeholder)
+    }
+
     fn new_unevaluated(interner: TyCtxt<'tcx>, uv: ty::UnevaluatedConst<'tcx>) -> Self {
         Const::new_unevaluated(interner, uv)
     }
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index cb132ae5573..b4d8b2e7176 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -4,7 +4,7 @@
 
 pub mod tls;
 
-use std::assert_matches::{assert_matches, debug_assert_matches};
+use std::assert_matches::debug_assert_matches;
 use std::borrow::Borrow;
 use std::cmp::Ordering;
 use std::env::VarError;
@@ -30,7 +30,7 @@ use rustc_data_structures::sync::{
     self, DynSend, DynSync, FreezeReadGuard, Lock, RwLock, WorkerLocal,
 };
 use rustc_errors::{
-    Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, LintDiagnostic, MultiSpan,
+    Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, LintDiagnostic, LintEmitter, MultiSpan,
 };
 use rustc_hir::def::{CtorKind, DefKind};
 use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId};
@@ -283,9 +283,9 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
         def_id: DefId,
         args: ty::GenericArgsRef<'tcx>,
     ) -> (ty::TraitRef<'tcx>, &'tcx [ty::GenericArg<'tcx>]) {
-        assert_matches!(self.def_kind(def_id), DefKind::AssocTy | DefKind::AssocConst);
+        debug_assert_matches!(self.def_kind(def_id), DefKind::AssocTy | DefKind::AssocConst);
         let trait_def_id = self.parent(def_id);
-        assert_matches!(self.def_kind(trait_def_id), DefKind::Trait);
+        debug_assert_matches!(self.def_kind(trait_def_id), DefKind::Trait);
         let trait_generics = self.generics_of(trait_def_id);
         (
             ty::TraitRef::new_from_args(self, trait_def_id, args.truncate_to(self, trait_generics)),
@@ -1350,8 +1350,8 @@ impl<'tcx> TyCtxtFeed<'tcx, LocalDefId> {
         let bodies = Default::default();
         let attrs = hir::AttributeMap::EMPTY;
 
-        let (opt_hash_including_bodies, _) =
-            self.tcx.hash_owner_nodes(node, &bodies, &attrs.map, attrs.define_opaque);
+        let (opt_hash_including_bodies, _, _) =
+            self.tcx.hash_owner_nodes(node, &bodies, &attrs.map, &[], attrs.define_opaque);
         let node = node.into();
         self.opt_hir_owner_nodes(Some(self.tcx.arena.alloc(hir::OwnerNodes {
             opt_hash_including_bodies,
@@ -1389,6 +1389,18 @@ pub struct TyCtxt<'tcx> {
     gcx: &'tcx GlobalCtxt<'tcx>,
 }
 
+impl<'tcx> LintEmitter for TyCtxt<'tcx> {
+    fn emit_node_span_lint(
+        self,
+        lint: &'static Lint,
+        hir_id: HirId,
+        span: impl Into<MultiSpan>,
+        decorator: impl for<'a> LintDiagnostic<'a, ()>,
+    ) {
+        self.emit_node_span_lint(lint, hir_id, span, decorator);
+    }
+}
+
 // Explicitly implement `DynSync` and `DynSend` for `TyCtxt` to short circuit trait resolution. Its
 // field are asserted to implement these traits below, so this is trivially safe, and it greatly
 // speeds-up compilation of this crate and its dependents.
diff --git a/compiler/rustc_middle/src/ty/generic_args.rs b/compiler/rustc_middle/src/ty/generic_args.rs
index 5e038f91675..7d34d8df3f3 100644
--- a/compiler/rustc_middle/src/ty/generic_args.rs
+++ b/compiler/rustc_middle/src/ty/generic_args.rs
@@ -588,7 +588,7 @@ impl<'tcx> GenericArgs<'tcx> {
     }
 
     pub fn truncate_to(&self, tcx: TyCtxt<'tcx>, generics: &ty::Generics) -> GenericArgsRef<'tcx> {
-        tcx.mk_args_from_iter(self.iter().take(generics.count()))
+        tcx.mk_args(&self[..generics.count()])
     }
 
     pub fn print_as_list(&self) -> String {
diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs
index 5ba4e5446e9..68adfb3cdb3 100644
--- a/compiler/rustc_middle/src/ty/instance.rs
+++ b/compiler/rustc_middle/src/ty/instance.rs
@@ -79,7 +79,7 @@ pub enum InstanceKind<'tcx> {
     Intrinsic(DefId),
 
     /// `<T as Trait>::method` where `method` receives unsizeable `self: Self` (part of the
-    /// `unsized_locals` feature).
+    /// `unsized_fn_params` feature).
     ///
     /// The generated shim will take `Self` via `*mut Self` - conceptually this is `&owned Self` -
     /// and dereference the argument to call the original function.
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index c2ae6b06192..9bce2845680 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -1266,6 +1266,7 @@ pub fn fn_can_unwind(tcx: TyCtxt<'_>, fn_def_id: Option<DefId>, abi: ExternAbi)
         | RiscvInterruptS
         | CCmseNonSecureCall
         | CCmseNonSecureEntry
+        | Custom
         | Unadjusted => false,
         Rust | RustCall | RustCold => tcx.sess.panic_strategy() == PanicStrategy::Unwind,
     }
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index dc3f2844e5a..0402d098822 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -933,7 +933,9 @@ impl Placeholder<BoundVar> {
 
 pub type PlaceholderRegion = Placeholder<BoundRegion>;
 
-impl rustc_type_ir::inherent::PlaceholderLike for PlaceholderRegion {
+impl<'tcx> rustc_type_ir::inherent::PlaceholderLike<TyCtxt<'tcx>> for PlaceholderRegion {
+    type Bound = BoundRegion;
+
     fn universe(self) -> UniverseIndex {
         self.universe
     }
@@ -946,14 +948,20 @@ impl rustc_type_ir::inherent::PlaceholderLike for PlaceholderRegion {
         Placeholder { universe: ui, ..self }
     }
 
-    fn new(ui: UniverseIndex, var: BoundVar) -> Self {
+    fn new(ui: UniverseIndex, bound: BoundRegion) -> Self {
+        Placeholder { universe: ui, bound }
+    }
+
+    fn new_anon(ui: UniverseIndex, var: BoundVar) -> Self {
         Placeholder { universe: ui, bound: BoundRegion { var, kind: BoundRegionKind::Anon } }
     }
 }
 
 pub type PlaceholderType = Placeholder<BoundTy>;
 
-impl rustc_type_ir::inherent::PlaceholderLike for PlaceholderType {
+impl<'tcx> rustc_type_ir::inherent::PlaceholderLike<TyCtxt<'tcx>> for PlaceholderType {
+    type Bound = BoundTy;
+
     fn universe(self) -> UniverseIndex {
         self.universe
     }
@@ -966,7 +974,11 @@ impl rustc_type_ir::inherent::PlaceholderLike for PlaceholderType {
         Placeholder { universe: ui, ..self }
     }
 
-    fn new(ui: UniverseIndex, var: BoundVar) -> Self {
+    fn new(ui: UniverseIndex, bound: BoundTy) -> Self {
+        Placeholder { universe: ui, bound }
+    }
+
+    fn new_anon(ui: UniverseIndex, var: BoundVar) -> Self {
         Placeholder { universe: ui, bound: BoundTy { var, kind: BoundTyKind::Anon } }
     }
 }
@@ -980,7 +992,9 @@ pub struct BoundConst<'tcx> {
 
 pub type PlaceholderConst = Placeholder<BoundVar>;
 
-impl rustc_type_ir::inherent::PlaceholderLike for PlaceholderConst {
+impl<'tcx> rustc_type_ir::inherent::PlaceholderLike<TyCtxt<'tcx>> for PlaceholderConst {
+    type Bound = BoundVar;
+
     fn universe(self) -> UniverseIndex {
         self.universe
     }
@@ -993,7 +1007,11 @@ impl rustc_type_ir::inherent::PlaceholderLike for PlaceholderConst {
         Placeholder { universe: ui, ..self }
     }
 
-    fn new(ui: UniverseIndex, var: BoundVar) -> Self {
+    fn new(ui: UniverseIndex, bound: BoundVar) -> Self {
+        Placeholder { universe: ui, bound }
+    }
+
+    fn new_anon(ui: UniverseIndex, var: BoundVar) -> Self {
         Placeholder { universe: ui, bound: var }
     }
 }
diff --git a/compiler/rustc_middle/src/ty/region.rs b/compiler/rustc_middle/src/ty/region.rs
index 3e4f7a79d53..cc25cd16567 100644
--- a/compiler/rustc_middle/src/ty/region.rs
+++ b/compiler/rustc_middle/src/ty/region.rs
@@ -148,6 +148,10 @@ impl<'tcx> rustc_type_ir::inherent::Region<TyCtxt<'tcx>> for Region<'tcx> {
         Region::new_bound(tcx, debruijn, ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon })
     }
 
+    fn new_placeholder(tcx: TyCtxt<'tcx>, placeholder: ty::PlaceholderRegion) -> Self {
+        Region::new_placeholder(tcx, placeholder)
+    }
+
     fn new_static(tcx: TyCtxt<'tcx>) -> Self {
         tcx.lifetimes.re_static
     }
diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs
index f8042174599..3853a804a92 100644
--- a/compiler/rustc_middle/src/ty/visit.rs
+++ b/compiler/rustc_middle/src/ty/visit.rs
@@ -66,7 +66,7 @@ impl<'tcx> TyCtxt<'tcx> {
         {
             type Result = ControlFlow<()>;
 
-            fn visit_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
+            fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
                 &mut self,
                 t: &Binder<'tcx, T>,
             ) -> Self::Result {
@@ -168,7 +168,7 @@ impl LateBoundRegionsCollector {
 }
 
 impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for LateBoundRegionsCollector {
-    fn visit_binder<T: TypeFoldable<TyCtxt<'tcx>>>(&mut self, t: &Binder<'tcx, T>) {
+    fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &Binder<'tcx, T>) {
         self.current_index.shift_in(1);
         t.super_visit_with(self);
         self.current_index.shift_out(1);
diff --git a/compiler/rustc_mir_build/src/builder/expr/as_operand.rs b/compiler/rustc_mir_build/src/builder/expr/as_operand.rs
index 2059610ee47..982e7aa8246 100644
--- a/compiler/rustc_mir_build/src/builder/expr/as_operand.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/as_operand.rs
@@ -55,9 +55,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
     /// local variable of unsized type. For example, consider this program:
     ///
     /// ```
-    /// #![feature(unsized_locals, unsized_fn_params)]
+    /// #![feature(unsized_fn_params)]
     /// # use core::fmt::Debug;
-    /// fn foo(p: dyn Debug) { dbg!(p); }
+    /// fn foo(_p: dyn Debug) { /* ... */ }
     ///
     /// fn bar(box_p: Box<dyn Debug>) { foo(*box_p); }
     /// ```
@@ -84,7 +84,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
     /// will actually provide a pointer to the interior of the box, and not move the `dyn Debug`
     /// value to the stack.
     ///
-    /// See #68304 for more details.
+    /// See <https://github.com/rust-lang/rust/issues/68304> for more details.
     pub(crate) fn as_local_call_operand(
         &mut self,
         block: BasicBlock,
diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs
index d5d0d56f528..06c6b46a9c2 100644
--- a/compiler/rustc_mir_transform/src/coroutine.rs
+++ b/compiler/rustc_mir_transform/src/coroutine.rs
@@ -1417,9 +1417,9 @@ fn check_field_tys_sized<'tcx>(
     coroutine_layout: &CoroutineLayout<'tcx>,
     def_id: LocalDefId,
 ) {
-    // No need to check if unsized_locals/unsized_fn_params is disabled,
+    // No need to check if unsized_fn_params is disabled,
     // since we will error during typeck.
-    if !tcx.features().unsized_locals() && !tcx.features().unsized_fn_params() {
+    if !tcx.features().unsized_fn_params() {
         return;
     }
 
diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs
index 92c30d239b5..bda71ceaa55 100644
--- a/compiler/rustc_mir_transform/src/gvn.rs
+++ b/compiler/rustc_mir_transform/src/gvn.rs
@@ -240,8 +240,6 @@ struct VnState<'body, 'tcx> {
     next_opaque: usize,
     /// Cache the deref values.
     derefs: Vec<VnIndex>,
-    /// Cache the value of the `unsized_locals` features, to avoid fetching it repeatedly in a loop.
-    feature_unsized_locals: bool,
     ssa: &'body SsaLocals,
     dominators: Dominators<BasicBlock>,
     reused_locals: DenseBitSet<Local>,
@@ -273,7 +271,6 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
             evaluated: IndexVec::with_capacity(num_values),
             next_opaque: 1,
             derefs: Vec::new(),
-            feature_unsized_locals: tcx.features().unsized_locals(),
             ssa,
             dominators,
             reused_locals: DenseBitSet::new_empty(local_decls.len()),
@@ -329,13 +326,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
     fn assign(&mut self, local: Local, value: VnIndex) {
         debug_assert!(self.ssa.is_ssa(local));
         self.locals[local] = Some(value);
-
-        // Only register the value if its type is `Sized`, as we will emit copies of it.
-        let is_sized = !self.feature_unsized_locals
-            || self.local_decls[local].ty.is_sized(self.tcx, self.typing_env());
-        if is_sized {
-            self.rev_locals[value].push(local);
-        }
+        self.rev_locals[value].push(local);
     }
 
     fn insert_constant(&mut self, value: Const<'tcx>) -> VnIndex {
diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs
index 52f4c39c09b..fa29ab985b7 100644
--- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs
+++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs
@@ -150,12 +150,12 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics {
                         });
                         terminator.kind = TerminatorKind::Goto { target };
                     }
-                    sym::size_of | sym::min_align_of => {
+                    sym::size_of | sym::align_of => {
                         let target = target.unwrap();
                         let tp_ty = generic_args.type_at(0);
                         let null_op = match intrinsic.name {
                             sym::size_of => NullOp::SizeOf,
-                            sym::min_align_of => NullOp::AlignOf,
+                            sym::align_of => NullOp::AlignOf,
                             _ => bug!("unexpected intrinsic"),
                         };
                         block.statements.push(Statement {
diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs
index cea77533178..47ed9e87244 100644
--- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs
+++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs
@@ -435,13 +435,13 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
             },
             ty::Placeholder(placeholder) => match self.canonicalize_mode {
                 CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderTy(
-                    PlaceholderLike::new(placeholder.universe(), self.variables.len().into()),
+                    PlaceholderLike::new_anon(placeholder.universe(), self.variables.len().into()),
                 ),
                 CanonicalizeMode::Response { .. } => CanonicalVarKind::PlaceholderTy(placeholder),
             },
             ty::Param(_) => match self.canonicalize_mode {
                 CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderTy(
-                    PlaceholderLike::new(ty::UniverseIndex::ROOT, self.variables.len().into()),
+                    PlaceholderLike::new_anon(ty::UniverseIndex::ROOT, self.variables.len().into()),
                 ),
                 CanonicalizeMode::Response { .. } => panic!("param ty in response: {t:?}"),
             },
@@ -594,7 +594,7 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz
             },
             ty::ConstKind::Placeholder(placeholder) => match self.canonicalize_mode {
                 CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderConst(
-                    PlaceholderLike::new(placeholder.universe(), self.variables.len().into()),
+                    PlaceholderLike::new_anon(placeholder.universe(), self.variables.len().into()),
                 ),
                 CanonicalizeMode::Response { .. } => {
                     CanonicalVarKind::PlaceholderConst(placeholder)
@@ -602,7 +602,7 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz
             },
             ty::ConstKind::Param(_) => match self.canonicalize_mode {
                 CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderConst(
-                    PlaceholderLike::new(ty::UniverseIndex::ROOT, self.variables.len().into()),
+                    PlaceholderLike::new_anon(ty::UniverseIndex::ROOT, self.variables.len().into()),
                 ),
                 CanonicalizeMode::Response { .. } => panic!("param ty in response: {c:?}"),
             },
diff --git a/compiler/rustc_next_trait_solver/src/lib.rs b/compiler/rustc_next_trait_solver/src/lib.rs
index 92cdc28a37b..77f098e6f26 100644
--- a/compiler/rustc_next_trait_solver/src/lib.rs
+++ b/compiler/rustc_next_trait_solver/src/lib.rs
@@ -12,5 +12,6 @@
 pub mod canonicalizer;
 pub mod coherence;
 pub mod delegate;
+pub mod placeholder;
 pub mod resolve;
 pub mod solve;
diff --git a/compiler/rustc_next_trait_solver/src/placeholder.rs b/compiler/rustc_next_trait_solver/src/placeholder.rs
new file mode 100644
index 00000000000..c88fb8defae
--- /dev/null
+++ b/compiler/rustc_next_trait_solver/src/placeholder.rs
@@ -0,0 +1,158 @@
+use core::panic;
+
+use rustc_type_ir::data_structures::IndexMap;
+use rustc_type_ir::inherent::*;
+use rustc_type_ir::{
+    self as ty, InferCtxtLike, Interner, TypeFoldable, TypeFolder, TypeSuperFoldable,
+    TypeVisitableExt,
+};
+
+pub struct BoundVarReplacer<'a, Infcx, I = <Infcx as InferCtxtLike>::Interner>
+where
+    Infcx: InferCtxtLike<Interner = I>,
+    I: Interner,
+{
+    infcx: &'a Infcx,
+    // These three maps track the bound variable that were replaced by placeholders. It might be
+    // nice to remove these since we already have the `kind` in the placeholder; we really just need
+    // the `var` (but we *could* bring that into scope if we were to track them as we pass them).
+    mapped_regions: IndexMap<I::PlaceholderRegion, I::BoundRegion>,
+    mapped_types: IndexMap<I::PlaceholderTy, I::BoundTy>,
+    mapped_consts: IndexMap<I::PlaceholderConst, I::BoundConst>,
+    // The current depth relative to *this* folding, *not* the entire normalization. In other words,
+    // the depth of binders we've passed here.
+    current_index: ty::DebruijnIndex,
+    // The `UniverseIndex` of the binding levels above us. These are optional, since we are lazy:
+    // we don't actually create a universe until we see a bound var we have to replace.
+    universe_indices: &'a mut Vec<Option<ty::UniverseIndex>>,
+}
+
+impl<'a, Infcx, I> BoundVarReplacer<'a, Infcx, I>
+where
+    Infcx: InferCtxtLike<Interner = I>,
+    I: Interner,
+{
+    /// Returns `Some` if we *were* able to replace bound vars. If there are any bound vars that
+    /// use a binding level above `universe_indices.len()`, we fail.
+    pub fn replace_bound_vars<T: TypeFoldable<I>>(
+        infcx: &'a Infcx,
+        universe_indices: &'a mut Vec<Option<ty::UniverseIndex>>,
+        value: T,
+    ) -> (
+        T,
+        IndexMap<I::PlaceholderRegion, I::BoundRegion>,
+        IndexMap<I::PlaceholderTy, I::BoundTy>,
+        IndexMap<I::PlaceholderConst, I::BoundConst>,
+    ) {
+        let mut replacer = BoundVarReplacer {
+            infcx,
+            mapped_regions: Default::default(),
+            mapped_types: Default::default(),
+            mapped_consts: Default::default(),
+            current_index: ty::INNERMOST,
+            universe_indices,
+        };
+
+        let value = value.fold_with(&mut replacer);
+
+        (value, replacer.mapped_regions, replacer.mapped_types, replacer.mapped_consts)
+    }
+
+    fn universe_for(&mut self, debruijn: ty::DebruijnIndex) -> ty::UniverseIndex {
+        let infcx = self.infcx;
+        let index =
+            self.universe_indices.len() + self.current_index.as_usize() - debruijn.as_usize() - 1;
+        let universe = self.universe_indices[index].unwrap_or_else(|| {
+            for i in self.universe_indices.iter_mut().take(index + 1) {
+                *i = i.or_else(|| Some(infcx.create_next_universe()))
+            }
+            self.universe_indices[index].unwrap()
+        });
+        universe
+    }
+}
+
+impl<Infcx, I> TypeFolder<I> for BoundVarReplacer<'_, Infcx, I>
+where
+    Infcx: InferCtxtLike<Interner = I>,
+    I: Interner,
+{
+    fn cx(&self) -> I {
+        self.infcx.cx()
+    }
+
+    fn fold_binder<T: TypeFoldable<I>>(&mut self, t: ty::Binder<I, T>) -> ty::Binder<I, T> {
+        self.current_index.shift_in(1);
+        let t = t.super_fold_with(self);
+        self.current_index.shift_out(1);
+        t
+    }
+
+    fn fold_region(&mut self, r: I::Region) -> I::Region {
+        match r.kind() {
+            ty::ReBound(debruijn, _)
+                if debruijn.as_usize()
+                    >= self.current_index.as_usize() + self.universe_indices.len() =>
+            {
+                panic!(
+                    "Bound vars {r:#?} outside of `self.universe_indices`: {:#?}",
+                    self.universe_indices
+                );
+            }
+            ty::ReBound(debruijn, br) if debruijn >= self.current_index => {
+                let universe = self.universe_for(debruijn);
+                let p = PlaceholderLike::new(universe, br);
+                self.mapped_regions.insert(p, br);
+                Region::new_placeholder(self.cx(), p)
+            }
+            _ => r,
+        }
+    }
+
+    fn fold_ty(&mut self, t: I::Ty) -> I::Ty {
+        match t.kind() {
+            ty::Bound(debruijn, _)
+                if debruijn.as_usize() + 1
+                    > self.current_index.as_usize() + self.universe_indices.len() =>
+            {
+                panic!(
+                    "Bound vars {t:#?} outside of `self.universe_indices`: {:#?}",
+                    self.universe_indices
+                );
+            }
+            ty::Bound(debruijn, bound_ty) if debruijn >= self.current_index => {
+                let universe = self.universe_for(debruijn);
+                let p = PlaceholderLike::new(universe, bound_ty);
+                self.mapped_types.insert(p, bound_ty);
+                Ty::new_placeholder(self.cx(), p)
+            }
+            _ if t.has_vars_bound_at_or_above(self.current_index) => t.super_fold_with(self),
+            _ => t,
+        }
+    }
+
+    fn fold_const(&mut self, ct: I::Const) -> I::Const {
+        match ct.kind() {
+            ty::ConstKind::Bound(debruijn, _)
+                if debruijn.as_usize() + 1
+                    > self.current_index.as_usize() + self.universe_indices.len() =>
+            {
+                panic!(
+                    "Bound vars {ct:#?} outside of `self.universe_indices`: {:#?}",
+                    self.universe_indices
+                );
+            }
+            ty::ConstKind::Bound(debruijn, bound_const) if debruijn >= self.current_index => {
+                let universe = self.universe_for(debruijn);
+                let p = PlaceholderLike::new(universe, bound_const);
+                self.mapped_consts.insert(p, bound_const);
+                Const::new_placeholder(self.cx(), p)
+            }
+            _ => ct.super_fold_with(self),
+        }
+    }
+
+    fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate {
+        if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p }
+    }
+}
diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
index 542e212e1bf..0c267feefbe 100644
--- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
@@ -1014,7 +1014,11 @@ where
             return Ok(CandidateSource::ParamEnv(ParamEnvSource::NonGlobal));
         }
 
-        match assumption.visit_with(&mut FindParamInClause { ecx: self, param_env }) {
+        match assumption.visit_with(&mut FindParamInClause {
+            ecx: self,
+            param_env,
+            universes: vec![],
+        }) {
             ControlFlow::Break(Err(NoSolution)) => Err(NoSolution),
             ControlFlow::Break(Ok(())) => Ok(CandidateSource::ParamEnv(ParamEnvSource::NonGlobal)),
             ControlFlow::Continue(()) => Ok(CandidateSource::ParamEnv(ParamEnvSource::Global)),
@@ -1025,6 +1029,7 @@ where
 struct FindParamInClause<'a, 'b, D: SolverDelegate<Interner = I>, I: Interner> {
     ecx: &'a mut EvalCtxt<'b, D>,
     param_env: I::ParamEnv,
+    universes: Vec<Option<ty::UniverseIndex>>,
 }
 
 impl<D, I> TypeVisitor<I> for FindParamInClause<'_, '_, D, I>
@@ -1034,31 +1039,42 @@ where
 {
     type Result = ControlFlow<Result<(), NoSolution>>;
 
-    fn visit_binder<T: TypeFoldable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result {
-        self.ecx.enter_forall(t.clone(), |ecx, v| {
-            v.visit_with(&mut FindParamInClause { ecx, param_env: self.param_env })
-        })
+    fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result {
+        self.universes.push(None);
+        t.super_visit_with(self)?;
+        self.universes.pop();
+        ControlFlow::Continue(())
     }
 
     fn visit_ty(&mut self, ty: I::Ty) -> Self::Result {
+        let ty = self.ecx.replace_bound_vars(ty, &mut self.universes);
         let Ok(ty) = self.ecx.structurally_normalize_ty(self.param_env, ty) else {
             return ControlFlow::Break(Err(NoSolution));
         };
 
-        if let ty::Placeholder(_) = ty.kind() {
-            ControlFlow::Break(Ok(()))
+        if let ty::Placeholder(p) = ty.kind() {
+            if p.universe() == ty::UniverseIndex::ROOT {
+                ControlFlow::Break(Ok(()))
+            } else {
+                ControlFlow::Continue(())
+            }
         } else {
             ty.super_visit_with(self)
         }
     }
 
     fn visit_const(&mut self, ct: I::Const) -> Self::Result {
+        let ct = self.ecx.replace_bound_vars(ct, &mut self.universes);
         let Ok(ct) = self.ecx.structurally_normalize_const(self.param_env, ct) else {
             return ControlFlow::Break(Err(NoSolution));
         };
 
-        if let ty::ConstKind::Placeholder(_) = ct.kind() {
-            ControlFlow::Break(Ok(()))
+        if let ty::ConstKind::Placeholder(p) = ct.kind() {
+            if p.universe() == ty::UniverseIndex::ROOT {
+                ControlFlow::Break(Ok(()))
+            } else {
+                ControlFlow::Continue(())
+            }
         } else {
             ct.super_visit_with(self)
         }
@@ -1066,10 +1082,17 @@ where
 
     fn visit_region(&mut self, r: I::Region) -> Self::Result {
         match self.ecx.eager_resolve_region(r).kind() {
-            ty::ReStatic | ty::ReError(_) => ControlFlow::Continue(()),
-            ty::ReVar(_) | ty::RePlaceholder(_) => ControlFlow::Break(Ok(())),
-            ty::ReErased | ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::ReBound(..) => {
-                unreachable!()
+            ty::ReStatic | ty::ReError(_) | ty::ReBound(..) => ControlFlow::Continue(()),
+            ty::RePlaceholder(p) => {
+                if p.universe() == ty::UniverseIndex::ROOT {
+                    ControlFlow::Break(Ok(()))
+                } else {
+                    ControlFlow::Continue(())
+                }
+            }
+            ty::ReVar(_) => ControlFlow::Break(Ok(())),
+            ty::ReErased | ty::ReEarlyParam(_) | ty::ReLateParam(_) => {
+                unreachable!("unexpected region in param-env clause")
             }
         }
     }
diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
index 345ece20b7e..7ead0a6d6b7 100644
--- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
@@ -19,6 +19,7 @@ use tracing::{debug, instrument, trace};
 use super::has_only_region_constraints;
 use crate::coherence;
 use crate::delegate::SolverDelegate;
+use crate::placeholder::BoundVarReplacer;
 use crate::solve::inspect::{self, ProofTreeBuilder};
 use crate::solve::search_graph::SearchGraph;
 use crate::solve::{
@@ -1232,6 +1233,14 @@ where
     ) -> Result<Certainty, NoSolution> {
         self.delegate.is_transmutable(dst, src, assume)
     }
+
+    pub(super) fn replace_bound_vars<T: TypeFoldable<I>>(
+        &self,
+        t: T,
+        universes: &mut Vec<Option<ty::UniverseIndex>>,
+    ) -> T {
+        BoundVarReplacer::replace_bound_vars(&**self.delegate, universes, t).0
+    }
 }
 
 /// Eagerly replace aliases with inference variables, emitting `AliasRelate`
diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index 1f221b4bf78..f6e0b08b140 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -299,10 +299,12 @@ parse_float_literal_unsupported_base = {$base} float literal is not supported
 parse_fn_pointer_cannot_be_async = an `fn` pointer type cannot be `async`
     .label = `async` because of this
     .suggestion = remove the `async` qualifier
+    .note = allowed qualifiers are: `unsafe` and `extern`
 
 parse_fn_pointer_cannot_be_const = an `fn` pointer type cannot be `const`
     .label = `const` because of this
     .suggestion = remove the `const` qualifier
+    .note = allowed qualifiers are: `unsafe` and `extern`
 
 parse_fn_ptr_with_generics = function pointer types may not have generic parameters
     .suggestion = consider moving the lifetime {$arity ->
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index 2dba568a258..0f0c5434800 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -2938,22 +2938,22 @@ pub(crate) struct DynAfterMut {
 
 #[derive(Diagnostic)]
 #[diag(parse_fn_pointer_cannot_be_const)]
+#[note]
 pub(crate) struct FnPointerCannotBeConst {
     #[primary_span]
-    pub span: Span,
     #[label]
-    pub qualifier: Span,
+    pub span: Span,
     #[suggestion(code = "", applicability = "maybe-incorrect", style = "verbose")]
     pub suggestion: Span,
 }
 
 #[derive(Diagnostic)]
 #[diag(parse_fn_pointer_cannot_be_async)]
+#[note]
 pub(crate) struct FnPointerCannotBeAsync {
     #[primary_span]
-    pub span: Span,
     #[label]
-    pub qualifier: Span,
+    pub span: Span,
     #[suggestion(code = "", applicability = "maybe-incorrect", style = "verbose")]
     pub suggestion: Span,
 }
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index a325c2a57ab..658ed4bd41c 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -23,7 +23,7 @@ use super::{
     AttrWrapper, ExpKeywordPair, ExpTokenPair, FollowedByType, ForceCollect, Parser, PathStyle,
     Recovered, Trailing, UsePreAttrPos,
 };
-use crate::errors::{self, MacroExpandsToAdtField};
+use crate::errors::{self, FnPointerCannotBeAsync, FnPointerCannotBeConst, MacroExpandsToAdtField};
 use crate::{exp, fluent_generated as fluent};
 
 impl<'a> Parser<'a> {
@@ -2402,7 +2402,7 @@ impl<'a> Parser<'a> {
         case: Case,
     ) -> PResult<'a, (Ident, FnSig, Generics, Option<P<FnContract>>, Option<P<Block>>)> {
         let fn_span = self.token.span;
-        let header = self.parse_fn_front_matter(vis, case)?; // `const ... fn`
+        let header = self.parse_fn_front_matter(vis, case, FrontMatterParsingMode::Function)?; // `const ... fn`
         let ident = self.parse_ident()?; // `foo`
         let mut generics = self.parse_generics()?; // `<'a, T, ...>`
         let decl = match self.parse_fn_decl(
@@ -2658,16 +2658,37 @@ impl<'a> Parser<'a> {
     ///
     /// `vis` represents the visibility that was already parsed, if any. Use
     /// `Visibility::Inherited` when no visibility is known.
+    ///
+    /// If `parsing_mode` is `FrontMatterParsingMode::FunctionPtrType`, we error on `const` and `async` qualifiers,
+    /// which are not allowed in function pointer types.
     pub(super) fn parse_fn_front_matter(
         &mut self,
         orig_vis: &Visibility,
         case: Case,
+        parsing_mode: FrontMatterParsingMode,
     ) -> PResult<'a, FnHeader> {
         let sp_start = self.token.span;
         let constness = self.parse_constness(case);
+        if parsing_mode == FrontMatterParsingMode::FunctionPtrType
+            && let Const::Yes(const_span) = constness
+        {
+            self.dcx().emit_err(FnPointerCannotBeConst {
+                span: const_span,
+                suggestion: const_span.until(self.token.span),
+            });
+        }
 
         let async_start_sp = self.token.span;
         let coroutine_kind = self.parse_coroutine_kind(case);
+        if parsing_mode == FrontMatterParsingMode::FunctionPtrType
+            && let Some(ast::CoroutineKind::Async { span: async_span, .. }) = coroutine_kind
+        {
+            self.dcx().emit_err(FnPointerCannotBeAsync {
+                span: async_span,
+                suggestion: async_span.until(self.token.span),
+            });
+        }
+        // FIXME(gen_blocks): emit a similar error for `gen fn()`
 
         let unsafe_start_sp = self.token.span;
         let safety = self.parse_safety(case);
@@ -2703,6 +2724,11 @@ impl<'a> Parser<'a> {
                     enum WrongKw {
                         Duplicated(Span),
                         Misplaced(Span),
+                        /// `MisplacedDisallowedQualifier` is only used instead of `Misplaced`,
+                        /// when the misplaced keyword is disallowed by the current `FrontMatterParsingMode`.
+                        /// In this case, we avoid generating the suggestion to swap around the keywords,
+                        /// as we already generated a suggestion to remove the keyword earlier.
+                        MisplacedDisallowedQualifier,
                     }
 
                     // We may be able to recover
@@ -2716,7 +2742,21 @@ impl<'a> Parser<'a> {
                             Const::Yes(sp) => Some(WrongKw::Duplicated(sp)),
                             Const::No => {
                                 recover_constness = Const::Yes(self.token.span);
-                                Some(WrongKw::Misplaced(async_start_sp))
+                                match parsing_mode {
+                                    FrontMatterParsingMode::Function => {
+                                        Some(WrongKw::Misplaced(async_start_sp))
+                                    }
+                                    FrontMatterParsingMode::FunctionPtrType => {
+                                        self.dcx().emit_err(FnPointerCannotBeConst {
+                                            span: self.token.span,
+                                            suggestion: self
+                                                .token
+                                                .span
+                                                .with_lo(self.prev_token.span.hi()),
+                                        });
+                                        Some(WrongKw::MisplacedDisallowedQualifier)
+                                    }
+                                }
                             }
                         }
                     } else if self.check_keyword(exp!(Async)) {
@@ -2742,7 +2782,21 @@ impl<'a> Parser<'a> {
                                     closure_id: DUMMY_NODE_ID,
                                     return_impl_trait_id: DUMMY_NODE_ID,
                                 });
-                                Some(WrongKw::Misplaced(unsafe_start_sp))
+                                match parsing_mode {
+                                    FrontMatterParsingMode::Function => {
+                                        Some(WrongKw::Misplaced(async_start_sp))
+                                    }
+                                    FrontMatterParsingMode::FunctionPtrType => {
+                                        self.dcx().emit_err(FnPointerCannotBeAsync {
+                                            span: self.token.span,
+                                            suggestion: self
+                                                .token
+                                                .span
+                                                .with_lo(self.prev_token.span.hi()),
+                                        });
+                                        Some(WrongKw::MisplacedDisallowedQualifier)
+                                    }
+                                }
                             }
                         }
                     } else if self.check_keyword(exp!(Unsafe)) {
@@ -2840,14 +2894,20 @@ impl<'a> Parser<'a> {
 
                     // FIXME(gen_blocks): add keyword recovery logic for genness
 
-                    if wrong_kw.is_some()
+                    if let Some(wrong_kw) = wrong_kw
                         && self.may_recover()
                         && self.look_ahead(1, |tok| tok.is_keyword_case(kw::Fn, case))
                     {
                         // Advance past the misplaced keyword and `fn`
                         self.bump();
                         self.bump();
-                        err.emit();
+                        // When we recover from a `MisplacedDisallowedQualifier`, we already emitted an error for the disallowed qualifier
+                        // So we don't emit another error that the qualifier is unexpected.
+                        if matches!(wrong_kw, WrongKw::MisplacedDisallowedQualifier) {
+                            err.cancel();
+                        } else {
+                            err.emit();
+                        }
                         return Ok(FnHeader {
                             constness: recover_constness,
                             safety: recover_safety,
@@ -3194,3 +3254,12 @@ enum IsMacroRulesItem {
     Yes { has_bang: bool },
     No,
 }
+
+#[derive(Copy, Clone, PartialEq, Eq)]
+pub(super) enum FrontMatterParsingMode {
+    /// Parse the front matter of a function declaration
+    Function,
+    /// Parse the front matter of a function pointet type.
+    /// For function pointer types, the `const` and `async` keywords are not permitted.
+    FunctionPtrType,
+}
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index 9ddfc179e9b..620a34044d1 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -15,10 +15,11 @@ use thin_vec::{ThinVec, thin_vec};
 use super::{Parser, PathStyle, SeqSep, TokenType, Trailing};
 use crate::errors::{
     self, DynAfterMut, ExpectedFnPathFoundFnKeyword, ExpectedMutOrConstInRawPointerType,
-    FnPointerCannotBeAsync, FnPointerCannotBeConst, FnPtrWithGenerics, FnPtrWithGenericsSugg,
-    HelpUseLatestEdition, InvalidDynKeyword, LifetimeAfterMut, NeedPlusAfterTraitObjectLifetime,
-    NestedCVariadicType, ReturnTypesUseThinArrow,
+    FnPtrWithGenerics, FnPtrWithGenericsSugg, HelpUseLatestEdition, InvalidDynKeyword,
+    LifetimeAfterMut, NeedPlusAfterTraitObjectLifetime, NestedCVariadicType,
+    ReturnTypesUseThinArrow,
 };
+use crate::parser::item::FrontMatterParsingMode;
 use crate::{exp, maybe_recover_from_interpolated_ty_qpath};
 
 /// Signals whether parsing a type should allow `+`.
@@ -669,62 +670,16 @@ impl<'a> Parser<'a> {
             tokens: None,
         };
         let span_start = self.token.span;
-        let ast::FnHeader { ext, safety, constness, coroutine_kind } =
-            self.parse_fn_front_matter(&inherited_vis, Case::Sensitive)?;
-        let fn_start_lo = self.prev_token.span.lo();
+        let ast::FnHeader { ext, safety, .. } = self.parse_fn_front_matter(
+            &inherited_vis,
+            Case::Sensitive,
+            FrontMatterParsingMode::FunctionPtrType,
+        )?;
         if self.may_recover() && self.token == TokenKind::Lt {
             self.recover_fn_ptr_with_generics(lo, &mut params, param_insertion_point)?;
         }
         let decl = self.parse_fn_decl(|_| false, AllowPlus::No, recover_return_sign)?;
-        let whole_span = lo.to(self.prev_token.span);
-
-        // Order/parsing of "front matter" follows:
-        // `<constness> <coroutine_kind> <safety> <extern> fn()`
-        //  ^           ^                ^        ^        ^
-        //  |           |                |        |        fn_start_lo
-        //  |           |                |        ext_sp.lo
-        //  |           |                safety_sp.lo
-        //  |           coroutine_sp.lo
-        //  const_sp.lo
-        if let ast::Const::Yes(const_span) = constness {
-            let next_token_lo = if let Some(
-                ast::CoroutineKind::Async { span, .. }
-                | ast::CoroutineKind::Gen { span, .. }
-                | ast::CoroutineKind::AsyncGen { span, .. },
-            ) = coroutine_kind
-            {
-                span.lo()
-            } else if let ast::Safety::Unsafe(span) | ast::Safety::Safe(span) = safety {
-                span.lo()
-            } else if let ast::Extern::Implicit(span) | ast::Extern::Explicit(_, span) = ext {
-                span.lo()
-            } else {
-                fn_start_lo
-            };
-            let sugg_span = const_span.with_hi(next_token_lo);
-            self.dcx().emit_err(FnPointerCannotBeConst {
-                span: whole_span,
-                qualifier: const_span,
-                suggestion: sugg_span,
-            });
-        }
-        if let Some(ast::CoroutineKind::Async { span: async_span, .. }) = coroutine_kind {
-            let next_token_lo = if let ast::Safety::Unsafe(span) | ast::Safety::Safe(span) = safety
-            {
-                span.lo()
-            } else if let ast::Extern::Implicit(span) | ast::Extern::Explicit(_, span) = ext {
-                span.lo()
-            } else {
-                fn_start_lo
-            };
-            let sugg_span = async_span.with_hi(next_token_lo);
-            self.dcx().emit_err(FnPointerCannotBeAsync {
-                span: whole_span,
-                qualifier: async_span,
-                suggestion: sugg_span,
-            });
-        }
-        // FIXME(gen_blocks): emit a similar error for `gen fn()`
+
         let decl_span = span_start.to(self.prev_token.span);
         Ok(TyKind::BareFn(P(BareFnTy { ext, safety, generic_params: params, decl, decl_span })))
     }
diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs
index 9dd064aca66..42bd0f5d847 100644
--- a/compiler/rustc_parse_format/src/lib.rs
+++ b/compiler/rustc_parse_format/src/lib.rs
@@ -29,58 +29,45 @@ pub enum ParseMode {
     Format,
     /// An inline assembly template string for `asm!`.
     InlineAsm,
+    /// A format string for use in diagnostic attributes.
+    ///
+    /// Similar to `format_args!`, however only named ("captured") arguments
+    /// are allowed, and no format modifiers are permitted.
+    Diagnostic,
 }
 
 /// A piece is a portion of the format string which represents the next part
 /// to emit. These are emitted as a stream by the `Parser` class.
 #[derive(Clone, Debug, PartialEq)]
-pub enum Piece<'a> {
+pub enum Piece<'input> {
     /// A literal string which should directly be emitted
-    Lit(&'a str),
+    Lit(&'input str),
     /// This describes that formatting should process the next argument (as
     /// specified inside) for emission.
-    NextArgument(Box<Argument<'a>>),
+    NextArgument(Box<Argument<'input>>),
 }
 
 /// Representation of an argument specification.
 #[derive(Clone, Debug, PartialEq)]
-pub struct Argument<'a> {
+pub struct Argument<'input> {
     /// Where to find this argument
-    pub position: Position<'a>,
+    pub position: Position<'input>,
     /// The span of the position indicator. Includes any whitespace in implicit
     /// positions (`{  }`).
     pub position_span: Range<usize>,
     /// How to format the argument
-    pub format: FormatSpec<'a>,
+    pub format: FormatSpec<'input>,
 }
 
-impl<'a> Argument<'a> {
+impl<'input> Argument<'input> {
     pub fn is_identifier(&self) -> bool {
-        matches!(self.position, Position::ArgumentNamed(_))
-            && matches!(
-                self.format,
-                FormatSpec {
-                    fill: None,
-                    fill_span: None,
-                    align: AlignUnknown,
-                    sign: None,
-                    alternate: false,
-                    zero_pad: false,
-                    debug_hex: None,
-                    precision: CountImplied,
-                    precision_span: None,
-                    width: CountImplied,
-                    width_span: None,
-                    ty: "",
-                    ty_span: None,
-                },
-            )
+        matches!(self.position, Position::ArgumentNamed(_)) && self.format == FormatSpec::default()
     }
 }
 
 /// Specification for the formatting of an argument in the format string.
-#[derive(Clone, Debug, PartialEq)]
-pub struct FormatSpec<'a> {
+#[derive(Clone, Debug, PartialEq, Default)]
+pub struct FormatSpec<'input> {
     /// Optionally specified character to fill alignment with.
     pub fill: Option<char>,
     /// Span of the optionally specified fill character.
@@ -96,30 +83,30 @@ pub struct FormatSpec<'a> {
     /// The `x` or `X` flag. (Only for `Debug`.)
     pub debug_hex: Option<DebugHex>,
     /// The integer precision to use.
-    pub precision: Count<'a>,
+    pub precision: Count<'input>,
     /// The span of the precision formatting flag (for diagnostics).
     pub precision_span: Option<Range<usize>>,
     /// The string width requested for the resulting format.
-    pub width: Count<'a>,
+    pub width: Count<'input>,
     /// The span of the width formatting flag (for diagnostics).
     pub width_span: Option<Range<usize>>,
     /// The descriptor string representing the name of the format desired for
     /// this argument, this can be empty or any number of characters, although
     /// it is required to be one word.
-    pub ty: &'a str,
+    pub ty: &'input str,
     /// The span of the descriptor string (for diagnostics).
     pub ty_span: Option<Range<usize>>,
 }
 
 /// Enum describing where an argument for a format can be located.
 #[derive(Clone, Debug, PartialEq)]
-pub enum Position<'a> {
+pub enum Position<'input> {
     /// The argument is implied to be located at an index
     ArgumentImplicitlyIs(usize),
     /// The argument is located at a specific index given in the format,
     ArgumentIs(usize),
     /// The argument has a name.
-    ArgumentNamed(&'a str),
+    ArgumentNamed(&'input str),
 }
 
 impl Position<'_> {
@@ -132,7 +119,7 @@ impl Position<'_> {
 }
 
 /// Enum of alignments which are supported.
-#[derive(Copy, Clone, Debug, PartialEq)]
+#[derive(Copy, Clone, Debug, PartialEq, Default)]
 pub enum Alignment {
     /// The value will be aligned to the left.
     AlignLeft,
@@ -141,6 +128,7 @@ pub enum Alignment {
     /// The value will be aligned in the center.
     AlignCenter,
     /// The value will take on a default alignment.
+    #[default]
     AlignUnknown,
 }
 
@@ -164,17 +152,18 @@ pub enum DebugHex {
 
 /// A count is used for the precision and width parameters of an integer, and
 /// can reference either an argument or a literal integer.
-#[derive(Clone, Debug, PartialEq)]
-pub enum Count<'a> {
+#[derive(Clone, Debug, PartialEq, Default)]
+pub enum Count<'input> {
     /// The count is specified explicitly.
     CountIs(u16),
     /// The count is specified by the argument with the given name.
-    CountIsName(&'a str, Range<usize>),
+    CountIsName(&'input str, Range<usize>),
     /// The count is specified by the argument at the given index.
     CountIsParam(usize),
     /// The count is specified by a star (like in `{:.*}`) that refers to the argument at the given index.
     CountIsStar(usize),
     /// The count is implied and cannot be explicitly specified.
+    #[default]
     CountImplied,
 }
 
@@ -208,10 +197,10 @@ pub enum Suggestion {
 ///
 /// This is a recursive-descent parser for the sake of simplicity, and if
 /// necessary there's probably lots of room for improvement performance-wise.
-pub struct Parser<'a> {
+pub struct Parser<'input> {
     mode: ParseMode,
     /// Input to be parsed
-    input: &'a str,
+    input: &'input str,
     /// Tuples of the span in the code snippet (input as written before being unescaped), the pos in input, and the char in input
     input_vec: Vec<(Range<usize>, usize, char)>,
     /// Index into input_vec
@@ -237,15 +226,15 @@ pub struct Parser<'a> {
     pub line_spans: Vec<Range<usize>>,
 }
 
-impl<'a> Iterator for Parser<'a> {
-    type Item = Piece<'a>;
+impl<'input> Iterator for Parser<'input> {
+    type Item = Piece<'input>;
 
-    fn next(&mut self) -> Option<Piece<'a>> {
-        if let Some(&(Range { start, end }, idx, ch)) = self.input_vec.get(self.input_vec_index) {
+    fn next(&mut self) -> Option<Piece<'input>> {
+        if let Some((Range { start, end }, idx, ch)) = self.peek() {
             match ch {
                 '{' => {
                     self.input_vec_index += 1;
-                    if let Some(&(_, i, '{')) = self.input_vec.get(self.input_vec_index) {
+                    if let Some((_, i, '{')) = self.peek() {
                         self.input_vec_index += 1;
                         // double open brace escape: "{{"
                         // next state after this is either end-of-input or seen-a-brace
@@ -254,25 +243,21 @@ impl<'a> Iterator for Parser<'a> {
                         // single open brace
                         self.last_open_brace = Some(start..end);
                         let arg = self.argument();
-                        if let Some(close_brace_range) = self.consume_closing_brace(&arg) {
+                        self.ws();
+                        if let Some((close_brace_range, _)) = self.consume_pos('}') {
                             if self.is_source_literal {
                                 self.arg_places.push(start..close_brace_range.end);
                             }
-                        } else if let Some(&(_, _, c)) = self.input_vec.get(self.input_vec_index) {
-                            match c {
-                                '?' => self.suggest_format_debug(),
-                                '<' | '^' | '>' => self.suggest_format_align(c),
-                                _ => {
-                                    self.suggest_positional_arg_instead_of_captured_arg(arg.clone())
-                                }
-                            }
+                        } else {
+                            self.missing_closing_brace(&arg);
                         }
+
                         Some(Piece::NextArgument(Box::new(arg)))
                     }
                 }
                 '}' => {
                     self.input_vec_index += 1;
-                    if let Some(&(_, i, '}')) = self.input_vec.get(self.input_vec_index) {
+                    if let Some((_, i, '}')) = self.peek() {
                         self.input_vec_index += 1;
                         // double close brace escape: "}}"
                         // next state after this is either end-of-input or start
@@ -307,14 +292,14 @@ impl<'a> Iterator for Parser<'a> {
     }
 }
 
-impl<'a> Parser<'a> {
+impl<'input> Parser<'input> {
     /// Creates a new parser for the given unescaped input string and
     /// optional code snippet (the input as written before being unescaped),
     /// where `style` is `Some(nr_hashes)` when the snippet is a raw string with that many hashes.
     /// If the input comes via `println` or `panic`, then it has a newline already appended,
     /// which is reflected in the `appended_newline` parameter.
     pub fn new(
-        input: &'a str,
+        input: &'input str,
         style: Option<usize>,
         snippet: Option<String>,
         appended_newline: bool,
@@ -406,6 +391,16 @@ impl<'a> Parser<'a> {
         }
     }
 
+    /// Peeks at the current position, without incrementing the pointer.
+    pub fn peek(&self) -> Option<(Range<usize>, usize, char)> {
+        self.input_vec.get(self.input_vec_index).cloned()
+    }
+
+    /// Peeks at the current position + 1, without incrementing the pointer.
+    pub fn peek_ahead(&self) -> Option<(Range<usize>, usize, char)> {
+        self.input_vec.get(self.input_vec_index + 1).cloned()
+    }
+
     /// Optionally consumes the specified character. If the character is not at
     /// the current position, then the current iterator isn't moved and `false` is
     /// returned, otherwise the character is consumed and `true` is returned.
@@ -418,27 +413,19 @@ impl<'a> Parser<'a> {
     /// returned, otherwise the character is consumed and the current position is
     /// returned.
     fn consume_pos(&mut self, ch: char) -> Option<(Range<usize>, usize)> {
-        if let Some((r, i, c)) = self.input_vec.get(self.input_vec_index) {
-            if ch == *c {
-                self.input_vec_index += 1;
-                return Some((r.clone(), *i));
-            }
+        if let Some((r, i, c)) = self.peek()
+            && ch == c
+        {
+            self.input_vec_index += 1;
+            return Some((r, i));
         }
+
         None
     }
 
-    /// Forces consumption of the specified character. If the character is not
-    /// found, an error is emitted.
-    fn consume_closing_brace(&mut self, arg: &Argument<'_>) -> Option<Range<usize>> {
-        self.ws();
-
-        let (range, description) = if let Some((r, _, c)) = self.input_vec.get(self.input_vec_index)
-        {
-            if *c == '}' {
-                self.input_vec_index += 1;
-                return Some(r.clone());
-            }
-            // or r.clone()?
+    /// Called if a closing brace was not found.
+    fn missing_closing_brace(&mut self, arg: &Argument<'_>) {
+        let (range, description) = if let Some((r, _, c)) = self.peek() {
             (r.start..r.start, format!("expected `}}`, found `{}`", c.escape_debug()))
         } else {
             (
@@ -471,7 +458,13 @@ impl<'a> Parser<'a> {
             suggestion: Suggestion::None,
         });
 
-        None
+        if let Some((_, _, c)) = self.peek() {
+            match c {
+                '?' => self.suggest_format_debug(),
+                '<' | '^' | '>' => self.suggest_format_align(c),
+                _ => self.suggest_positional_arg_instead_of_captured_arg(arg),
+            }
+        }
     }
 
     /// Consumes all whitespace characters until the first non-whitespace character
@@ -483,11 +476,11 @@ impl<'a> Parser<'a> {
 
     /// Parses all of a string which is to be considered a "raw literal" in a
     /// format string. This is everything outside of the braces.
-    fn string(&mut self, start: usize) -> &'a str {
-        while let Some((r, i, c)) = self.input_vec.get(self.input_vec_index) {
+    fn string(&mut self, start: usize) -> &'input str {
+        while let Some((r, i, c)) = self.peek() {
             match c {
                 '{' | '}' => {
-                    return &self.input[start..*i];
+                    return &self.input[start..i];
                 }
                 '\n' if self.is_source_literal => {
                     self.input_vec_index += 1;
@@ -507,7 +500,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parses an `Argument` structure, or what's contained within braces inside the format string.
-    fn argument(&mut self) -> Argument<'a> {
+    fn argument(&mut self) -> Argument<'input> {
         let start_idx = self.input_vec_index;
 
         let position = self.position();
@@ -518,6 +511,7 @@ impl<'a> Parser<'a> {
         let format = match self.mode {
             ParseMode::Format => self.format(),
             ParseMode::InlineAsm => self.inline_asm(),
+            ParseMode::Diagnostic => self.diagnostic(),
         };
 
         // Resolve position after parsing format spec.
@@ -536,31 +530,27 @@ impl<'a> Parser<'a> {
     /// integer index of an argument, a named argument, or a blank string.
     /// Returns `Some(parsed_position)` if the position is not implicitly
     /// consuming a macro argument, `None` if it's the case.
-    fn position(&mut self) -> Option<Position<'a>> {
+    fn position(&mut self) -> Option<Position<'input>> {
         if let Some(i) = self.integer() {
             Some(ArgumentIs(i.into()))
         } else {
-            match self.input_vec.get(self.input_vec_index) {
-                Some((range, _, c)) if rustc_lexer::is_id_start(*c) => {
+            match self.peek() {
+                Some((range, _, c)) if rustc_lexer::is_id_start(c) => {
                     let start = range.start;
                     let word = self.word();
 
                     // Recover from `r#ident` in format strings.
-                    // FIXME: use a let chain
-                    if word == "r" {
-                        if let Some((r, _, '#')) = self.input_vec.get(self.input_vec_index) {
-                            if self
-                                .input_vec
-                                .get(self.input_vec_index + 1)
-                                .is_some_and(|(_, _, c)| rustc_lexer::is_id_start(*c))
-                            {
-                                self.input_vec_index += 1;
-                                let prefix_end = r.end;
-                                let word = self.word();
-                                let prefix_span = start..prefix_end;
-                                let full_span =
-                                    start..self.input_vec_index2range(self.input_vec_index).start;
-                                self.errors.insert(0, ParseError {
+                    if word == "r"
+                        && let Some((r, _, '#')) = self.peek()
+                        && self.peek_ahead().is_some_and(|(_, _, c)| rustc_lexer::is_id_start(c))
+                    {
+                        self.input_vec_index += 1;
+                        let prefix_end = r.end;
+                        let word = self.word();
+                        let prefix_span = start..prefix_end;
+                        let full_span =
+                            start..self.input_vec_index2range(self.input_vec_index).start;
+                        self.errors.insert(0, ParseError {
                                     description: "raw identifiers are not supported".to_owned(),
                                     note: Some("identifiers in format strings can be keywords and don't need to be prefixed with `r#`".to_string()),
                                     label: "raw identifier used here".to_owned(),
@@ -568,9 +558,7 @@ impl<'a> Parser<'a> {
                                     secondary_label: None,
                                     suggestion: Suggestion::RemoveRawIdent(prefix_span),
                                 });
-                                return Some(ArgumentNamed(word));
-                            }
-                        }
+                        return Some(ArgumentNamed(word));
                     }
 
                     Some(ArgumentNamed(word))
@@ -584,7 +572,7 @@ impl<'a> Parser<'a> {
     }
 
     fn input_vec_index2pos(&self, index: usize) -> usize {
-        if let Some(&(_, pos, _)) = self.input_vec.get(index) { pos } else { self.input.len() }
+        if let Some((_, pos, _)) = self.input_vec.get(index) { *pos } else { self.input.len() }
     }
 
     fn input_vec_index2range(&self, index: usize) -> Range<usize> {
@@ -597,33 +585,18 @@ impl<'a> Parser<'a> {
 
     /// Parses a format specifier at the current position, returning all of the
     /// relevant information in the `FormatSpec` struct.
-    fn format(&mut self) -> FormatSpec<'a> {
-        let mut spec = FormatSpec {
-            fill: None,
-            fill_span: None,
-            align: AlignUnknown,
-            sign: None,
-            alternate: false,
-            zero_pad: false,
-            debug_hex: None,
-            precision: CountImplied,
-            precision_span: None,
-            width: CountImplied,
-            width_span: None,
-            ty: &self.input[..0],
-            ty_span: None,
-        };
+    fn format(&mut self) -> FormatSpec<'input> {
+        let mut spec = FormatSpec::default();
+
         if !self.consume(':') {
             return spec;
         }
 
         // fill character
-        if let Some(&(ref r, _, c)) = self.input_vec.get(self.input_vec_index) {
-            if let Some((_, _, '>' | '<' | '^')) = self.input_vec.get(self.input_vec_index + 1) {
-                self.input_vec_index += 1;
-                spec.fill = Some(c);
-                spec.fill_span = Some(r.clone());
-            }
+        if let (Some((r, _, c)), Some((_, _, '>' | '<' | '^'))) = (self.peek(), self.peek_ahead()) {
+            self.input_vec_index += 1;
+            spec.fill = Some(c);
+            spec.fill_span = Some(r);
         }
         // Alignment
         if self.consume('<') {
@@ -701,24 +674,21 @@ impl<'a> Parser<'a> {
             }
         } else if let Some((range, _)) = self.consume_pos('?') {
             spec.ty = "?";
-            if let Some((r, _, c)) = self.input_vec.get(self.input_vec_index) {
-                match c {
-                    '#' | 'x' | 'X' => self.errors.insert(
-                        0,
-                        ParseError {
-                            description: format!("expected `}}`, found `{c}`"),
-                            note: None,
-                            label: "expected `'}'`".into(),
-                            span: r.clone(),
-                            secondary_label: None,
-                            suggestion: Suggestion::ReorderFormatParameter(
-                                range.start..r.end,
-                                format!("{c}?"),
-                            ),
-                        },
-                    ),
-                    _ => (),
-                }
+            if let Some((r, _, c @ ('#' | 'x' | 'X'))) = self.peek() {
+                self.errors.insert(
+                    0,
+                    ParseError {
+                        description: format!("expected `}}`, found `{c}`"),
+                        note: None,
+                        label: "expected `'}'`".into(),
+                        span: r.clone(),
+                        secondary_label: None,
+                        suggestion: Suggestion::ReorderFormatParameter(
+                            range.start..r.end,
+                            format!("{c}?"),
+                        ),
+                    },
+                );
             }
         } else {
             spec.ty = self.word();
@@ -733,22 +703,9 @@ impl<'a> Parser<'a> {
 
     /// Parses an inline assembly template modifier at the current position, returning the modifier
     /// in the `ty` field of the `FormatSpec` struct.
-    fn inline_asm(&mut self) -> FormatSpec<'a> {
-        let mut spec = FormatSpec {
-            fill: None,
-            fill_span: None,
-            align: AlignUnknown,
-            sign: None,
-            alternate: false,
-            zero_pad: false,
-            debug_hex: None,
-            precision: CountImplied,
-            precision_span: None,
-            width: CountImplied,
-            width_span: None,
-            ty: &self.input[..0],
-            ty_span: None,
-        };
+    fn inline_asm(&mut self) -> FormatSpec<'input> {
+        let mut spec = FormatSpec::default();
+
         if !self.consume(':') {
             return spec;
         }
@@ -764,10 +721,26 @@ impl<'a> Parser<'a> {
         spec
     }
 
+    /// Always returns an empty `FormatSpec`
+    fn diagnostic(&mut self) -> FormatSpec<'input> {
+        let mut spec = FormatSpec::default();
+
+        let Some((Range { start, .. }, start_idx)) = self.consume_pos(':') else {
+            return spec;
+        };
+
+        spec.ty = self.string(start_idx);
+        spec.ty_span = {
+            let end = self.input_vec_index2range(self.input_vec_index).start;
+            Some(start..end)
+        };
+        spec
+    }
+
     /// Parses a `Count` parameter at the current position. This does not check
     /// for 'CountIsNextParam' because that is only used in precision, not
     /// width.
-    fn count(&mut self) -> Count<'a> {
+    fn count(&mut self) -> Count<'input> {
         if let Some(i) = self.integer() {
             if self.consume('$') { CountIsParam(i.into()) } else { CountIs(i) }
         } else {
@@ -786,10 +759,10 @@ impl<'a> Parser<'a> {
 
     /// Parses a word starting at the current position. A word is the same as a
     /// Rust identifier, except that it can't start with `_` character.
-    fn word(&mut self) -> &'a str {
+    fn word(&mut self) -> &'input str {
         let index = self.input_vec_index;
-        match self.input_vec.get(self.input_vec_index) {
-            Some(&(ref r, i, c)) if rustc_lexer::is_id_start(c) => {
+        match self.peek() {
+            Some((ref r, i, c)) if rustc_lexer::is_id_start(c) => {
                 self.input_vec_index += 1;
                 (r.start, i)
             }
@@ -798,7 +771,7 @@ impl<'a> Parser<'a> {
             }
         };
         let (err_end, end): (usize, usize) = loop {
-            if let Some(&(ref r, i, c)) = self.input_vec.get(self.input_vec_index) {
+            if let Some((ref r, i, c)) = self.peek() {
                 if rustc_lexer::is_id_continue(c) {
                     self.input_vec_index += 1;
                 } else {
@@ -828,7 +801,7 @@ impl<'a> Parser<'a> {
         let mut found = false;
         let mut overflow = false;
         let start_index = self.input_vec_index;
-        while let Some(&(_, _, c)) = self.input_vec.get(self.input_vec_index) {
+        while let Some((_, _, c)) = self.peek() {
             if let Some(i) = c.to_digit(10) {
                 self.input_vec_index += 1;
                 let (tmp, mul_overflow) = cur.overflowing_mul(10);
@@ -897,7 +870,7 @@ impl<'a> Parser<'a> {
         }
     }
 
-    fn suggest_positional_arg_instead_of_captured_arg(&mut self, arg: Argument<'a>) {
+    fn suggest_positional_arg_instead_of_captured_arg(&mut self, arg: &Argument<'_>) {
         // If the argument is not an identifier, it is not a field access.
         if !arg.is_identifier() {
             return;
diff --git a/compiler/rustc_parse_format/src/tests.rs b/compiler/rustc_parse_format/src/tests.rs
index e6a7f24034a..a6c7e1890ab 100644
--- a/compiler/rustc_parse_format/src/tests.rs
+++ b/compiler/rustc_parse_format/src/tests.rs
@@ -553,3 +553,45 @@ fn asm_concat() {
     assert_eq!(parser.by_ref().collect::<Vec<Piece<'static>>>(), &[Lit(asm)]);
     assert_eq!(parser.line_spans, &[]);
 }
+
+#[test]
+fn diagnostic_format_flags() {
+    let lit = "{thing:blah}";
+    let mut parser = Parser::new(lit, None, None, false, ParseMode::Diagnostic);
+    assert!(!parser.is_source_literal);
+
+    let [NextArgument(arg)] = &*parser.by_ref().collect::<Vec<Piece<'static>>>() else { panic!() };
+
+    assert_eq!(
+        **arg,
+        Argument {
+            position: ArgumentNamed("thing"),
+            position_span: 2..7,
+            format: FormatSpec { ty: ":blah", ty_span: Some(7..12), ..Default::default() },
+        }
+    );
+
+    assert_eq!(parser.line_spans, &[]);
+    assert!(parser.errors.is_empty());
+}
+
+#[test]
+fn diagnostic_format_mod() {
+    let lit = "{thing:+}";
+    let mut parser = Parser::new(lit, None, None, false, ParseMode::Diagnostic);
+    assert!(!parser.is_source_literal);
+
+    let [NextArgument(arg)] = &*parser.by_ref().collect::<Vec<Piece<'static>>>() else { panic!() };
+
+    assert_eq!(
+        **arg,
+        Argument {
+            position: ArgumentNamed("thing"),
+            position_span: 2..7,
+            format: FormatSpec { ty: ":+", ty_span: Some(7..9), ..Default::default() },
+        }
+    );
+
+    assert_eq!(parser.line_spans, &[]);
+    assert!(parser.errors.is_empty());
+}
diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl
index a4ef065ea2c..7c237d708c0 100644
--- a/compiler/rustc_passes/messages.ftl
+++ b/compiler/rustc_passes/messages.ftl
@@ -290,6 +290,9 @@ passes_duplicate_lang_item_crate_depends =
     .first_definition_path = first definition in `{$orig_crate_name}` loaded from {$orig_path}
     .second_definition_path = second definition in `{$crate_name}` loaded from {$path}
 
+passes_enum_variant_same_name =
+    it is impossible to refer to the {$descr} `{$dead_name}` because it is shadowed by this enum variant with the same name
+
 passes_export_name =
     attribute should be applied to a free function, impl method or static
     .label = not a free function, impl method or static
@@ -716,6 +719,9 @@ passes_unknown_external_lang_item =
 passes_unknown_feature =
     unknown feature `{$feature}`
 
+passes_unknown_feature_alias =
+    feature `{$alias}` has been renamed to `{$feature}`
+
 passes_unknown_lang_item =
     definition of an unknown lang item: `{$name}`
     .label = definition of unknown lang item `{$name}`
diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs
index e597c819a3a..4257d8e8d16 100644
--- a/compiler/rustc_passes/src/dead.rs
+++ b/compiler/rustc_passes/src/dead.rs
@@ -14,7 +14,7 @@ use rustc_errors::MultiSpan;
 use rustc_hir::def::{CtorOf, DefKind, Res};
 use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId};
 use rustc_hir::intravisit::{self, Visitor};
-use rustc_hir::{self as hir, Node, PatKind, QPath, TyKind};
+use rustc_hir::{self as hir, ImplItem, ImplItemKind, Node, PatKind, QPath, TyKind};
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use rustc_middle::middle::privacy::Level;
 use rustc_middle::query::Providers;
@@ -936,7 +936,9 @@ enum ShouldWarnAboutField {
 
 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
 enum ReportOn {
+    /// Report on something that hasn't got a proper name to refer to
     TupleField,
+    /// Report on something that has got a name, which could be a field but also a method
     NamedField,
 }
 
@@ -1061,6 +1063,31 @@ impl<'tcx> DeadVisitor<'tcx> {
                 None
             };
 
+        let enum_variants_with_same_name = dead_codes
+            .iter()
+            .filter_map(|dead_item| {
+                if let Node::ImplItem(ImplItem {
+                    kind: ImplItemKind::Fn(..) | ImplItemKind::Const(..),
+                    ..
+                }) = tcx.hir_node_by_def_id(dead_item.def_id)
+                    && let Some(impl_did) = tcx.opt_parent(dead_item.def_id.to_def_id())
+                    && let DefKind::Impl { of_trait: false } = tcx.def_kind(impl_did)
+                    && let ty::Adt(maybe_enum, _) = tcx.type_of(impl_did).skip_binder().kind()
+                    && maybe_enum.is_enum()
+                    && let Some(variant) =
+                        maybe_enum.variants().iter().find(|i| i.name == dead_item.name)
+                {
+                    Some(crate::errors::EnumVariantSameName {
+                        descr: tcx.def_descr(dead_item.def_id.to_def_id()),
+                        dead_name: dead_item.name,
+                        variant_span: tcx.def_span(variant.def_id),
+                    })
+                } else {
+                    None
+                }
+            })
+            .collect();
+
         let diag = match report_on {
             ReportOn::TupleField => {
                 let tuple_fields = if let Some(parent_id) = parent_item
@@ -1114,6 +1141,7 @@ impl<'tcx> DeadVisitor<'tcx> {
                 name_list,
                 parent_info,
                 ignored_derived_impls,
+                enum_variants_with_same_name,
             },
         };
 
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index 74ce92624bd..f0d4b610f63 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -1435,6 +1435,15 @@ pub(crate) struct UnknownFeature {
 }
 
 #[derive(Diagnostic)]
+#[diag(passes_unknown_feature_alias, code = E0635)]
+pub(crate) struct RenamedFeature {
+    #[primary_span]
+    pub span: Span,
+    pub feature: Symbol,
+    pub alias: Symbol,
+}
+
+#[derive(Diagnostic)]
 #[diag(passes_implied_feature_not_exist)]
 pub(crate) struct ImpliedFeatureNotExist {
     #[primary_span]
@@ -1478,6 +1487,9 @@ pub(crate) enum MultipleDeadCodes<'tcx> {
         participle: &'tcx str,
         name_list: DiagSymbolList,
         #[subdiagnostic]
+        // only on DeadCodes since it's never a problem for tuple struct fields
+        enum_variants_with_same_name: Vec<EnumVariantSameName<'tcx>>,
+        #[subdiagnostic]
         parent_info: Option<ParentInfo<'tcx>>,
         #[subdiagnostic]
         ignored_derived_impls: Option<IgnoredDerivedImpls>,
@@ -1499,6 +1511,15 @@ pub(crate) enum MultipleDeadCodes<'tcx> {
 }
 
 #[derive(Subdiagnostic)]
+#[note(passes_enum_variant_same_name)]
+pub(crate) struct EnumVariantSameName<'tcx> {
+    #[primary_span]
+    pub variant_span: Span,
+    pub dead_name: Symbol,
+    pub descr: &'tcx str,
+}
+
+#[derive(Subdiagnostic)]
 #[label(passes_parent_info)]
 pub(crate) struct ParentInfo<'tcx> {
     pub num: usize,
diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs
index 6852153a2e7..46e6c0bf7da 100644
--- a/compiler/rustc_passes/src/input_stats.rs
+++ b/compiler/rustc_passes/src/input_stats.rs
@@ -5,7 +5,7 @@
 use rustc_ast::visit::BoundKind;
 use rustc_ast::{self as ast, NodeId, visit as ast_visit};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_data_structures::thousands::format_with_underscores;
+use rustc_data_structures::thousands::usize_with_underscores;
 use rustc_hir::{self as hir, AmbigArg, HirId, intravisit as hir_visit};
 use rustc_middle::ty::TyCtxt;
 use rustc_span::Span;
@@ -140,10 +140,10 @@ impl<'k> StatCollector<'k> {
                 "{} {:<18}{:>10} ({:4.1}%){:>14}{:>14}",
                 prefix,
                 label,
-                format_with_underscores(size),
+                usize_with_underscores(size),
                 percent(size, total_size),
-                format_with_underscores(node.stats.count),
-                format_with_underscores(node.stats.size)
+                usize_with_underscores(node.stats.count),
+                usize_with_underscores(node.stats.size)
             );
             if !node.subnodes.is_empty() {
                 // We will soon sort, so the initial order does not matter.
@@ -159,9 +159,9 @@ impl<'k> StatCollector<'k> {
                         "{} - {:<18}{:>10} ({:4.1}%){:>14}",
                         prefix,
                         label,
-                        format_with_underscores(size),
+                        usize_with_underscores(size),
                         percent(size, total_size),
-                        format_with_underscores(subnode.count),
+                        usize_with_underscores(subnode.count),
                     );
                 }
             }
@@ -171,8 +171,8 @@ impl<'k> StatCollector<'k> {
             "{} {:<18}{:>10}        {:>14}",
             prefix,
             "Total",
-            format_with_underscores(total_size),
-            format_with_underscores(total_count),
+            usize_with_underscores(total_size),
+            usize_with_underscores(total_count),
         );
         eprintln!("{prefix}");
     }
diff --git a/compiler/rustc_passes/src/lib_features.rs b/compiler/rustc_passes/src/lib_features.rs
index b3e6ee9512c..127e0df1332 100644
--- a/compiler/rustc_passes/src/lib_features.rs
+++ b/compiler/rustc_passes/src/lib_features.rs
@@ -40,7 +40,7 @@ impl<'tcx> LibFeatureCollector<'tcx> {
         };
 
         let feature_stability = match level {
-            StabilityLevel::Unstable { .. } => FeatureStability::Unstable,
+            StabilityLevel::Unstable { old_name, .. } => FeatureStability::Unstable { old_name },
             StabilityLevel::Stable { since, .. } => FeatureStability::AcceptedSince(match since {
                 StableSince::Version(v) => Symbol::intern(&v.to_string()),
                 StableSince::Current => sym::env_CFG_RELEASE,
@@ -71,7 +71,7 @@ impl<'tcx> LibFeatureCollector<'tcx> {
                     });
                 }
             }
-            (FeatureStability::AcceptedSince(_), Some((FeatureStability::Unstable, _))) => {
+            (FeatureStability::AcceptedSince(_), Some((FeatureStability::Unstable { .. }, _))) => {
                 self.tcx.dcx().emit_err(FeaturePreviouslyDeclared {
                     span,
                     feature,
@@ -79,7 +79,7 @@ impl<'tcx> LibFeatureCollector<'tcx> {
                     prev_declared: "unstable",
                 });
             }
-            (FeatureStability::Unstable, Some((FeatureStability::AcceptedSince(_), _))) => {
+            (FeatureStability::Unstable { .. }, Some((FeatureStability::AcceptedSince(_), _))) => {
                 self.tcx.dcx().emit_err(FeaturePreviouslyDeclared {
                     span,
                     feature,
@@ -88,7 +88,7 @@ impl<'tcx> LibFeatureCollector<'tcx> {
                 });
             }
             // duplicate `unstable` feature is ok.
-            (FeatureStability::Unstable, Some((FeatureStability::Unstable, _))) => {}
+            (FeatureStability::Unstable { .. }, Some((FeatureStability::Unstable { .. }, _))) => {}
         }
     }
 }
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index 45e26c8999a..56d9f5bf785 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -718,6 +718,7 @@ fn stability_index(tcx: TyCtxt<'_>, (): ()) -> Index {
                     issue: NonZero::new(27812),
                     is_soft: false,
                     implied_by: None,
+                    old_name: None,
                 },
                 feature: sym::rustc_private,
             };
@@ -1161,8 +1162,8 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
         defined_features: &LibFeatures,
         all_implications: &UnordMap<Symbol, Symbol>,
     ) {
-        for (feature, since) in defined_features.to_sorted_vec() {
-            if let FeatureStability::AcceptedSince(since) = since
+        for (feature, stability) in defined_features.to_sorted_vec() {
+            if let FeatureStability::AcceptedSince(since) = stability
                 && let Some(span) = remaining_lib_features.get(&feature)
             {
                 // Warn if the user has enabled an already-stable lib feature.
@@ -1181,6 +1182,12 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
             // implications from this crate.
             remaining_implications.remove(&feature);
 
+            if let FeatureStability::Unstable { old_name: Some(alias) } = stability {
+                if let Some(span) = remaining_lib_features.swap_remove(&alias) {
+                    tcx.dcx().emit_err(errors::RenamedFeature { span, feature, alias });
+                }
+            }
+
             if remaining_lib_features.is_empty() && remaining_implications.is_empty() {
                 break;
             }
diff --git a/compiler/rustc_query_system/src/ich/hcx.rs b/compiler/rustc_query_system/src/ich/hcx.rs
index e1b6adc6cc1..96d0c02c4a6 100644
--- a/compiler/rustc_query_system/src/ich/hcx.rs
+++ b/compiler/rustc_query_system/src/ich/hcx.rs
@@ -1,5 +1,3 @@
-use std::sync::Arc;
-
 use rustc_ast as ast;
 use rustc_data_structures::stable_hasher::{HashStable, HashingControls, StableHasher};
 use rustc_hir::def_id::{DefId, LocalDefId};
@@ -7,7 +5,9 @@ use rustc_hir::definitions::DefPathHash;
 use rustc_session::Session;
 use rustc_session::cstore::Untracked;
 use rustc_span::source_map::SourceMap;
-use rustc_span::{BytePos, CachingSourceMapView, DUMMY_SP, SourceFile, Span, SpanData, Symbol};
+use rustc_span::{
+    BytePos, CachingSourceMapView, DUMMY_SP, Span, SpanData, StableSourceFileId, Symbol,
+};
 
 use crate::ich;
 
@@ -118,7 +118,7 @@ impl<'a> rustc_span::HashStableContext for StableHashingContext<'a> {
     fn span_data_to_lines_and_cols(
         &mut self,
         span: &SpanData,
-    ) -> Option<(Arc<SourceFile>, usize, BytePos, usize, BytePos)> {
+    ) -> Option<(StableSourceFileId, usize, BytePos, usize, BytePos)> {
         self.source_map().span_data_to_lines_and_cols(span)
     }
 
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index c30ed781f35..650a827ba56 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -1098,22 +1098,20 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
             self.r.potentially_unused_imports.push(import);
             module.for_each_child(self, |this, ident, ns, binding| {
                 if ns == MacroNS {
-                    let imported_binding =
-                        if this.r.is_accessible_from(binding.vis, this.parent_scope.module) {
-                            this.r.import(binding, import)
-                        } else if !this.r.is_builtin_macro(binding.res())
-                            && !this.r.macro_use_prelude.contains_key(&ident.name)
-                        {
-                            // - `!r.is_builtin_macro(res)` excluding the built-in macros such as `Debug` or `Hash`.
-                            // - `!r.macro_use_prelude.contains_key(name)` excluding macros defined in other extern
-                            //    crates such as `std`.
-                            // FIXME: This branch should eventually be removed.
-                            let import = macro_use_import(this, span, true);
-                            this.r.import(binding, import)
-                        } else {
+                    let import = if this.r.is_accessible_from(binding.vis, this.parent_scope.module)
+                    {
+                        import
+                    } else {
+                        // FIXME: This branch is used for reporting the `private_macro_use` lint
+                        // and should eventually be removed.
+                        if this.r.macro_use_prelude.contains_key(&ident.name) {
+                            // Do not override already existing entries with compatibility entries.
                             return;
-                        };
-                    this.add_macro_use_binding(ident.name, imported_binding, span, allow_shadowing);
+                        }
+                        macro_use_import(this, span, true)
+                    };
+                    let import_binding = this.r.import(binding, import);
+                    this.add_macro_use_binding(ident.name, import_binding, span, allow_shadowing);
                 }
             });
         } else {
diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs
index dc16fe212b1..f8e0a6936a0 100644
--- a/compiler/rustc_resolve/src/def_collector.rs
+++ b/compiler/rustc_resolve/src/def_collector.rs
@@ -128,7 +128,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
                 // FIXME(jdonszelmann) make one of these in the resolver?
                 // FIXME(jdonszelmann) don't care about tools here maybe? Just parse what we can.
                 // Does that prevents errors from happening? maybe
-                let parser = AttributeParser::new(
+                let mut parser = AttributeParser::new_early(
                     &self.resolver.tcx.sess,
                     self.resolver.tcx.features(),
                     Vec::new(),
@@ -136,8 +136,14 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
                 let attrs = parser.parse_attribute_list(
                     &i.attrs,
                     i.span,
+                    i.id,
                     OmitDoc::Skip,
                     std::convert::identity,
+                    |_l| {
+                        // FIXME(jdonszelmann): emit lints here properly
+                        // NOTE that before new attribute parsing, they didn't happen either
+                        // but it would be nice if we could change that.
+                    },
                 );
 
                 let macro_data =
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index 201b1c0a493..9149974a617 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -7,7 +7,7 @@ use rustc_ast::{
 use rustc_ast_pretty::pprust;
 use rustc_attr_data_structures::{self as attr, Stability};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_data_structures::unord::UnordSet;
+use rustc_data_structures::unord::{UnordMap, UnordSet};
 use rustc_errors::codes::*;
 use rustc_errors::{
     Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, MultiSpan, SuggestionStyle,
@@ -1054,6 +1054,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                                 false,
                                 false,
                                 None,
+                                None,
                             ) else {
                                 continue;
                             };
@@ -1482,7 +1483,35 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         parent_scope: &ParentScope<'ra>,
         ident: Ident,
         krate: &Crate,
+        sugg_span: Option<Span>,
     ) {
+        // Bring imported but unused `derive` macros into `macro_map` so we ensure they can be used
+        // for suggestions.
+        self.visit_scopes(
+            ScopeSet::Macro(MacroKind::Derive),
+            &parent_scope,
+            ident.span.ctxt(),
+            |this, scope, _use_prelude, _ctxt| {
+                let Scope::Module(m, _) = scope else {
+                    return None;
+                };
+                for (_, resolution) in this.resolutions(m).borrow().iter() {
+                    let Some(binding) = resolution.borrow().binding else {
+                        continue;
+                    };
+                    let Res::Def(DefKind::Macro(MacroKind::Derive | MacroKind::Attr), def_id) =
+                        binding.res()
+                    else {
+                        continue;
+                    };
+                    // By doing this all *imported* macros get added to the `macro_map` even if they
+                    // are *unused*, which makes the later suggestions find them and work.
+                    let _ = this.get_macro_by_def_id(def_id);
+                }
+                None::<()>
+            },
+        );
+
         let is_expected = &|res: Res| res.macro_kind() == Some(macro_kind);
         let suggestion = self.early_lookup_typo_candidate(
             ScopeSet::Macro(macro_kind),
@@ -1490,7 +1519,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
             ident,
             is_expected,
         );
-        self.add_typo_suggestion(err, suggestion, ident.span);
+        if !self.add_typo_suggestion(err, suggestion, ident.span) {
+            self.detect_derive_attribute(err, ident, parent_scope, sugg_span);
+        }
 
         let import_suggestions =
             self.lookup_import_candidates(ident, Namespace::MacroNS, parent_scope, is_expected);
@@ -1623,6 +1654,105 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         }
     }
 
+    /// Given an attribute macro that failed to be resolved, look for `derive` macros that could
+    /// provide it, either as-is or with small typos.
+    fn detect_derive_attribute(
+        &self,
+        err: &mut Diag<'_>,
+        ident: Ident,
+        parent_scope: &ParentScope<'ra>,
+        sugg_span: Option<Span>,
+    ) {
+        // Find all of the `derive`s in scope and collect their corresponding declared
+        // attributes.
+        // FIXME: this only works if the crate that owns the macro that has the helper_attr
+        // has already been imported.
+        let mut derives = vec![];
+        let mut all_attrs: UnordMap<Symbol, Vec<_>> = UnordMap::default();
+        // We're collecting these in a hashmap, and handle ordering the output further down.
+        #[allow(rustc::potential_query_instability)]
+        for (def_id, data) in &self.macro_map {
+            for helper_attr in &data.ext.helper_attrs {
+                let item_name = self.tcx.item_name(*def_id);
+                all_attrs.entry(*helper_attr).or_default().push(item_name);
+                if helper_attr == &ident.name {
+                    derives.push(item_name);
+                }
+            }
+        }
+        let kind = MacroKind::Derive.descr();
+        if !derives.is_empty() {
+            // We found an exact match for the missing attribute in a `derive` macro. Suggest it.
+            let mut derives: Vec<String> = derives.into_iter().map(|d| d.to_string()).collect();
+            derives.sort();
+            derives.dedup();
+            let msg = match &derives[..] {
+                [derive] => format!(" `{derive}`"),
+                [start @ .., last] => format!(
+                    "s {} and `{last}`",
+                    start.iter().map(|d| format!("`{d}`")).collect::<Vec<_>>().join(", ")
+                ),
+                [] => unreachable!("we checked for this to be non-empty 10 lines above!?"),
+            };
+            let msg = format!(
+                "`{}` is an attribute that can be used by the {kind}{msg}, you might be \
+                     missing a `derive` attribute",
+                ident.name,
+            );
+            let sugg_span = if let ModuleKind::Def(DefKind::Enum, id, _) = parent_scope.module.kind
+            {
+                let span = self.def_span(id);
+                if span.from_expansion() {
+                    None
+                } else {
+                    // For enum variants sugg_span is empty but we can get the enum's Span.
+                    Some(span.shrink_to_lo())
+                }
+            } else {
+                // For items this `Span` will be populated, everything else it'll be None.
+                sugg_span
+            };
+            match sugg_span {
+                Some(span) => {
+                    err.span_suggestion_verbose(
+                        span,
+                        msg,
+                        format!("#[derive({})]\n", derives.join(", ")),
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+                None => {
+                    err.note(msg);
+                }
+            }
+        } else {
+            // We didn't find an exact match. Look for close matches. If any, suggest fixing typo.
+            let all_attr_names = all_attrs.keys().map(|s| *s).into_sorted_stable_ord();
+            if let Some(best_match) = find_best_match_for_name(&all_attr_names, ident.name, None)
+                && let Some(macros) = all_attrs.get(&best_match)
+            {
+                let mut macros: Vec<String> = macros.into_iter().map(|d| d.to_string()).collect();
+                macros.sort();
+                macros.dedup();
+                let msg = match &macros[..] {
+                    [] => return,
+                    [name] => format!(" `{name}` accepts"),
+                    [start @ .., end] => format!(
+                        "s {} and `{end}` accept",
+                        start.iter().map(|m| format!("`{m}`")).collect::<Vec<_>>().join(", "),
+                    ),
+                };
+                let msg = format!("the {kind}{msg} the similarly named `{best_match}` attribute");
+                err.span_suggestion_verbose(
+                    ident.span,
+                    msg,
+                    best_match,
+                    Applicability::MaybeIncorrect,
+                );
+            }
+        }
+    }
+
     pub(crate) fn add_typo_suggestion(
         &self,
         err: &mut Diag<'_>,
diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs
index 180d6af219d..68fbe48ebcb 100644
--- a/compiler/rustc_resolve/src/ident.rs
+++ b/compiler/rustc_resolve/src/ident.rs
@@ -460,6 +460,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                                 true,
                                 force,
                                 ignore_import,
+                                None,
                             ) {
                                 Ok((Some(ext), _)) => {
                                     if ext.helper_attrs.contains(&ident.name) {
diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs
index 816efd0d5fa..e989209e177 100644
--- a/compiler/rustc_resolve/src/imports.rs
+++ b/compiler/rustc_resolve/src/imports.rs
@@ -133,7 +133,9 @@ impl<'ra> std::fmt::Debug for ImportKind<'ra> {
                 .field("target", target)
                 .field("id", id)
                 .finish(),
-            MacroUse { .. } => f.debug_struct("MacroUse").finish(),
+            MacroUse { warn_private } => {
+                f.debug_struct("MacroUse").field("warn_private", warn_private).finish()
+            }
             MacroExport => f.debug_struct("MacroExport").finish(),
         }
     }
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 3dc285fdab6..fa4b024c422 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -4509,7 +4509,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
             let path_seg = |seg: &Segment| PathSegment::from_ident(seg.ident);
             let path = Path { segments: path.iter().map(path_seg).collect(), span, tokens: None };
             if let Ok((_, res)) =
-                self.r.resolve_macro_path(&path, None, &self.parent_scope, false, false, None)
+                self.r.resolve_macro_path(&path, None, &self.parent_scope, false, false, None, None)
             {
                 return Ok(Some(PartialRes::new(res)));
             }
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 9ba70abd4d9..f0540725416 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -1139,7 +1139,7 @@ pub struct Resolver<'ra, 'tcx> {
     proc_macro_stubs: FxHashSet<LocalDefId>,
     /// Traces collected during macro resolution and validated when it's complete.
     single_segment_macro_resolutions:
-        Vec<(Ident, MacroKind, ParentScope<'ra>, Option<NameBinding<'ra>>)>,
+        Vec<(Ident, MacroKind, ParentScope<'ra>, Option<NameBinding<'ra>>, Option<Span>)>,
     multi_segment_macro_resolutions:
         Vec<(Vec<Segment>, Span, MacroKind, ParentScope<'ra>, Option<Res>, Namespace)>,
     builtin_attrs: Vec<(Ident, ParentScope<'ra>)>,
@@ -1934,12 +1934,26 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         }
         if let NameBindingKind::Import { import, binding } = used_binding.kind {
             if let ImportKind::MacroUse { warn_private: true } = import.kind {
-                self.lint_buffer().buffer_lint(
-                    PRIVATE_MACRO_USE,
-                    import.root_id,
-                    ident.span,
-                    BuiltinLintDiag::MacroIsPrivate(ident),
-                );
+                // Do not report the lint if the macro name resolves in stdlib prelude
+                // even without the problematic `macro_use` import.
+                let found_in_stdlib_prelude = self.prelude.is_some_and(|prelude| {
+                    self.maybe_resolve_ident_in_module(
+                        ModuleOrUniformRoot::Module(prelude),
+                        ident,
+                        MacroNS,
+                        &ParentScope::module(self.empty_module, self),
+                        None,
+                    )
+                    .is_ok()
+                });
+                if !found_in_stdlib_prelude {
+                    self.lint_buffer().buffer_lint(
+                        PRIVATE_MACRO_USE,
+                        import.root_id,
+                        ident.span,
+                        BuiltinLintDiag::MacroIsPrivate(ident),
+                    );
+                }
             }
             // Avoid marking `extern crate` items that refer to a name from extern prelude,
             // but not introduce it, as used if they are accessed from lexical scope.
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index ee905065b96..1b82e9c9799 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -12,7 +12,8 @@ use rustc_attr_data_structures::StabilityLevel;
 use rustc_data_structures::intern::Interned;
 use rustc_errors::{Applicability, DiagCtxtHandle, StashKey};
 use rustc_expand::base::{
-    DeriveResolution, Indeterminate, ResolverExpand, SyntaxExtension, SyntaxExtensionKind,
+    Annotatable, DeriveResolution, Indeterminate, ResolverExpand, SyntaxExtension,
+    SyntaxExtensionKind,
 };
 use rustc_expand::compile_declarative_macro;
 use rustc_expand::expand::{
@@ -294,6 +295,14 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> {
                     && self.tcx.def_kind(mod_def_id) == DefKind::Mod
             })
             .map(|&InvocationParent { parent_def: mod_def_id, .. }| mod_def_id);
+        let sugg_span = match &invoc.kind {
+            InvocationKind::Attr { item: Annotatable::Item(item), .. }
+                if !item.span.from_expansion() =>
+            {
+                Some(item.span.shrink_to_lo())
+            }
+            _ => None,
+        };
         let (ext, res) = self.smart_resolve_macro_path(
             path,
             kind,
@@ -304,6 +313,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> {
             force,
             deleg_impl,
             looks_like_invoc_in_mod_inert_attr,
+            sugg_span,
         )?;
 
         let span = invoc.span();
@@ -386,6 +396,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> {
                         true,
                         force,
                         None,
+                        None,
                     ) {
                         Ok((Some(ext), _)) => {
                             if !ext.helper_attrs.is_empty() {
@@ -528,6 +539,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         force: bool,
         deleg_impl: Option<LocalDefId>,
         invoc_in_mod_inert_attr: Option<LocalDefId>,
+        suggestion_span: Option<Span>,
     ) -> Result<(Arc<SyntaxExtension>, Res), Indeterminate> {
         let (ext, res) = match self.resolve_macro_or_delegation_path(
             path,
@@ -538,6 +550,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
             deleg_impl,
             invoc_in_mod_inert_attr.map(|def_id| (def_id, node_id)),
             None,
+            suggestion_span,
         ) {
             Ok((Some(ext), res)) => (ext, res),
             Ok((None, res)) => (self.dummy_ext(kind), res),
@@ -681,6 +694,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         trace: bool,
         force: bool,
         ignore_import: Option<Import<'ra>>,
+        suggestion_span: Option<Span>,
     ) -> Result<(Option<Arc<SyntaxExtension>>, Res), Determinacy> {
         self.resolve_macro_or_delegation_path(
             path,
@@ -691,6 +705,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
             None,
             None,
             ignore_import,
+            suggestion_span,
         )
     }
 
@@ -704,6 +719,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         deleg_impl: Option<LocalDefId>,
         invoc_in_mod_inert_attr: Option<(LocalDefId, NodeId)>,
         ignore_import: Option<Import<'ra>>,
+        suggestion_span: Option<Span>,
     ) -> Result<(Option<Arc<SyntaxExtension>>, Res), Determinacy> {
         let path_span = ast_path.span;
         let mut path = Segment::from_path(ast_path);
@@ -768,6 +784,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                     kind,
                     *parent_scope,
                     binding.ok(),
+                    suggestion_span,
                 ));
             }
 
@@ -905,7 +922,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         }
 
         let macro_resolutions = mem::take(&mut self.single_segment_macro_resolutions);
-        for (ident, kind, parent_scope, initial_binding) in macro_resolutions {
+        for (ident, kind, parent_scope, initial_binding, sugg_span) in macro_resolutions {
             match self.early_resolve_ident_in_lexical_scope(
                 ident,
                 ScopeSet::Macro(kind),
@@ -946,7 +963,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                         expected,
                         ident,
                     });
-                    self.unresolved_macro_suggestions(&mut err, kind, &parent_scope, ident, krate);
+                    self.unresolved_macro_suggestions(
+                        &mut err,
+                        kind,
+                        &parent_scope,
+                        ident,
+                        krate,
+                        sugg_span,
+                    );
                     err.emit();
                 }
             }
@@ -974,7 +998,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
     ) {
         let span = path.span;
         if let Some(stability) = &ext.stability {
-            if let StabilityLevel::Unstable { reason, issue, is_soft, implied_by } = stability.level
+            if let StabilityLevel::Unstable { reason, issue, is_soft, implied_by, .. } =
+                stability.level
             {
                 let feature = stability.feature;
 
diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl
index 528c52eace7..61953614c77 100644
--- a/compiler/rustc_session/messages.ftl
+++ b/compiler/rustc_session/messages.ftl
@@ -40,6 +40,11 @@ session_file_is_not_writeable = output file {$file} is not writeable -- check it
 
 session_file_write_fail = failed to write `{$path}` due to error `{$err}`
 
+session_forbidden_ctarget_feature =
+    target feature `{$feature}` cannot be {$enabled} with `-Ctarget-feature`: {$reason}
+    .note = this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+session_forbidden_ctarget_feature_issue = for more information, see issue #116344 <https://github.com/rust-lang/rust/issues/116344>
+
 session_function_return_requires_x86_or_x86_64 = `-Zfunction-return` (except `keep`) is only supported on x86 and x86_64
 
 session_function_return_thunk_extern_requires_non_large_code_model = `-Zfunction-return=thunk-extern` is only supported on non-large code models
@@ -132,6 +137,9 @@ session_target_stack_protector_not_supported = `-Z stack-protector={$stack_prote
 session_unleashed_feature_help_named = skipping check for `{$gate}` feature
 session_unleashed_feature_help_unnamed = skipping check that does not even have a feature gate
 
+session_unstable_ctarget_feature =
+    unstable feature specified for `-Ctarget-feature`: `{$feature}`
+    .note = this feature is not stably supported; its behavior can change in the future
 session_unstable_virtual_function_elimination = `-Zvirtual-function-elimination` requires `-Clto`
 
 session_unsupported_crate_type_for_target =
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 60e1b465ba9..8984634e5ec 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -2649,6 +2649,15 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
 
     let prints = collect_print_requests(early_dcx, &mut cg, &unstable_opts, matches);
 
+    // -Zretpoline-external-thunk also requires -Zretpoline
+    if unstable_opts.retpoline_external_thunk {
+        unstable_opts.retpoline = true;
+        target_modifiers.insert(
+            OptionsTargetModifiers::UnstableOptions(UnstableOptionsTargetModifiers::retpoline),
+            "true".to_string(),
+        );
+    }
+
     let cg = cg;
 
     let sysroot_opt = matches.opt_str("sysroot").map(|m| PathBuf::from(&m));
diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs
index bf95014843d..9c591dcf619 100644
--- a/compiler/rustc_session/src/errors.rs
+++ b/compiler/rustc_session/src/errors.rs
@@ -501,3 +501,20 @@ pub(crate) struct SoftFloatIgnored;
 #[note]
 #[note(session_soft_float_deprecated_issue)]
 pub(crate) struct SoftFloatDeprecated;
+
+#[derive(Diagnostic)]
+#[diag(session_forbidden_ctarget_feature)]
+#[note]
+#[note(session_forbidden_ctarget_feature_issue)]
+pub(crate) struct ForbiddenCTargetFeature<'a> {
+    pub feature: &'a str,
+    pub enabled: &'a str,
+    pub reason: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(session_unstable_ctarget_feature)]
+#[note]
+pub(crate) struct UnstableCTargetFeature<'a> {
+    pub feature: &'a str,
+}
diff --git a/compiler/rustc_session/src/features.rs b/compiler/rustc_session/src/features.rs
new file mode 100644
index 00000000000..70a088a236f
--- /dev/null
+++ b/compiler/rustc_session/src/features.rs
@@ -0,0 +1,59 @@
+use rustc_target::target_features::Stability;
+
+use crate::Session;
+use crate::errors::{ForbiddenCTargetFeature, UnstableCTargetFeature};
+
+pub trait StabilityExt {
+    /// Returns whether the feature may be toggled via `#[target_feature]` or `-Ctarget-feature`.
+    /// Otherwise, some features also may only be enabled by flag (target modifier).
+    /// (It might still be nightly-only even if this returns `true`, so make sure to also check
+    /// `requires_nightly`.)
+    fn is_toggle_permitted(&self, sess: &Session) -> Result<(), &'static str>;
+
+    /// Check that feature is correctly enabled/disabled by command line flag (emits warnings)
+    fn verify_feature_enabled_by_flag(&self, sess: &Session, enable: bool, feature: &str);
+}
+
+impl StabilityExt for Stability {
+    fn is_toggle_permitted(&self, sess: &Session) -> Result<(), &'static str> {
+        match self {
+            Stability::Forbidden { reason } => Err(reason),
+            Stability::TargetModifierOnly { reason, flag } => {
+                if !sess.opts.target_feature_flag_enabled(*flag) { Err(reason) } else { Ok(()) }
+            }
+            _ => Ok(()),
+        }
+    }
+    fn verify_feature_enabled_by_flag(&self, sess: &Session, enable: bool, feature: &str) {
+        if let Err(reason) = self.is_toggle_permitted(sess) {
+            sess.dcx().emit_warn(ForbiddenCTargetFeature {
+                feature,
+                enabled: if enable { "enabled" } else { "disabled" },
+                reason,
+            });
+        } else if self.requires_nightly().is_some() {
+            // An unstable feature. Warn about using it. It makes little sense
+            // to hard-error here since we just warn about fully unknown
+            // features above.
+            sess.dcx().emit_warn(UnstableCTargetFeature { feature });
+        }
+    }
+}
+
+pub fn retpoline_features_by_flags(sess: &Session, features: &mut Vec<&str>) {
+    // -Zretpoline without -Zretpoline-external-thunk enables
+    // retpoline-indirect-branches and retpoline-indirect-calls target features
+    let unstable_opts = &sess.opts.unstable_opts;
+    if unstable_opts.retpoline && !unstable_opts.retpoline_external_thunk {
+        features.push("+retpoline-indirect-branches");
+        features.push("+retpoline-indirect-calls");
+    }
+    // -Zretpoline-external-thunk (maybe, with -Zretpoline too) enables
+    // retpoline-external-thunk, retpoline-indirect-branches and
+    // retpoline-indirect-calls target features
+    if unstable_opts.retpoline_external_thunk {
+        features.push("+retpoline-external-thunk");
+        features.push("+retpoline-indirect-branches");
+        features.push("+retpoline-indirect-calls");
+    }
+}
diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs
index 5e5872ee068..4added19e56 100644
--- a/compiler/rustc_session/src/lib.rs
+++ b/compiler/rustc_session/src/lib.rs
@@ -29,6 +29,7 @@ pub use session::*;
 pub mod output;
 
 pub use getopts;
+pub mod features;
 
 rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
 
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 12fa05118ca..6218521d4f0 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -290,6 +290,14 @@ macro_rules! top_level_options {
                 mods.sort_by(|a, b| a.opt.cmp(&b.opt));
                 mods
             }
+
+            pub fn target_feature_flag_enabled(&self, flag: &str) -> bool {
+                match flag {
+                    "retpoline" => self.unstable_opts.retpoline,
+                    "retpoline-external-thunk" => self.unstable_opts.retpoline_external_thunk,
+                    _ => false,
+                }
+            }
         }
     );
 }
@@ -2305,6 +2313,8 @@ options! {
         (space separated)"),
     macro_backtrace: bool = (false, parse_bool, [UNTRACKED],
         "show macro backtraces (default: no)"),
+    macro_stats: bool = (false, parse_bool, [UNTRACKED],
+        "print some statistics about macro expansions (default: no)"),
     maximal_hir_to_mir_coverage: bool = (false, parse_bool, [TRACKED],
         "save as much information as possible about the correspondence between MIR and HIR \
         as source scopes (default: no)"),
@@ -2446,6 +2456,11 @@ options! {
     remark_dir: Option<PathBuf> = (None, parse_opt_pathbuf, [UNTRACKED],
         "directory into which to write optimization remarks (if not specified, they will be \
 written to standard error output)"),
+    retpoline: bool = (false, parse_bool, [TRACKED TARGET_MODIFIER],
+        "enables retpoline-indirect-branches and retpoline-indirect-calls target features (default: no)"),
+    retpoline_external_thunk: bool = (false, parse_bool, [TRACKED TARGET_MODIFIER],
+        "enables retpoline-external-thunk, retpoline-indirect-branches and retpoline-indirect-calls \
+        target features (default: no)"),
     sanitizer: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED],
         "use a sanitizer"),
     sanitizer_cfi_canonical_jump_tables: Option<bool> = (Some(true), parse_opt_bool, [TRACKED],
diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs
index 1fe521bd32d..87c848cf857 100644
--- a/compiler/rustc_session/src/parse.rs
+++ b/compiler/rustc_session/src/parse.rs
@@ -17,7 +17,7 @@ use rustc_feature::{GateIssue, UnstableFeatures, find_feature_issue};
 use rustc_span::edition::Edition;
 use rustc_span::hygiene::ExpnId;
 use rustc_span::source_map::{FilePathMapping, SourceMap};
-use rustc_span::{Span, Symbol};
+use rustc_span::{Span, Symbol, sym};
 
 use crate::Session;
 use crate::config::{Cfg, CheckCfg};
@@ -192,8 +192,11 @@ pub fn add_feature_diagnostics_for_issue<G: EmissionGuarantee>(
         } else {
             err.subdiagnostic(FeatureDiagnosticHelp { feature });
         }
-
-        if sess.opts.unstable_opts.ui_testing {
+        if feature == sym::rustc_attrs {
+            // We're unlikely to stabilize something out of `rustc_attrs`
+            // without at least renaming it, so pointing out how old
+            // the compiler is will do little good.
+        } else if sess.opts.unstable_opts.ui_testing {
             err.subdiagnostic(SuggestUpgradeCompiler::ui_testing());
         } else if let Some(suggestion) = SuggestUpgradeCompiler::new() {
             err.subdiagnostic(suggestion);
diff --git a/compiler/rustc_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs
index 6e13b87c41d..a4c6f186222 100644
--- a/compiler/rustc_smir/src/rustc_internal/internal.rs
+++ b/compiler/rustc_smir/src/rustc_internal/internal.rs
@@ -496,6 +496,7 @@ impl RustcInternal for Abi {
             Abi::RustCold => rustc_abi::ExternAbi::RustCold,
             Abi::RiscvInterruptM => rustc_abi::ExternAbi::RiscvInterruptM,
             Abi::RiscvInterruptS => rustc_abi::ExternAbi::RiscvInterruptS,
+            Abi::Custom => rustc_abi::ExternAbi::Custom,
         }
     }
 }
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/abi.rs b/compiler/rustc_smir/src/rustc_smir/convert/abi.rs
index 46f1ca61cec..35d5b7fb89a 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/abi.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/abi.rs
@@ -101,6 +101,7 @@ impl<'tcx> Stable<'tcx> for CanonAbi {
             CanonAbi::C => CallConvention::C,
             CanonAbi::Rust => CallConvention::Rust,
             CanonAbi::RustCold => CallConvention::Cold,
+            CanonAbi::Custom => CallConvention::Custom,
             CanonAbi::Arm(arm_call) => match arm_call {
                 ArmCall::Aapcs => CallConvention::ArmAapcs,
                 ArmCall::CCmseNonSecureCall => CallConvention::CCmseNonSecureCall,
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
index b0c9dba78a6..6a26f5f7997 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
@@ -879,6 +879,7 @@ impl<'tcx> Stable<'tcx> for rustc_abi::ExternAbi {
             ExternAbi::RustCold => Abi::RustCold,
             ExternAbi::RiscvInterruptM => Abi::RiscvInterruptM,
             ExternAbi::RiscvInterruptS => Abi::RiscvInterruptS,
+            ExternAbi::Custom => Abi::Custom,
         }
     }
 }
diff --git a/compiler/rustc_smir/src/stable_mir/abi.rs b/compiler/rustc_smir/src/stable_mir/abi.rs
index 347c6ed16a2..d8a2b97662c 100644
--- a/compiler/rustc_smir/src/stable_mir/abi.rs
+++ b/compiler/rustc_smir/src/stable_mir/abi.rs
@@ -430,6 +430,8 @@ pub enum CallConvention {
     PreserveMost,
     PreserveAll,
 
+    Custom,
+
     // Target-specific calling conventions.
     ArmAapcs,
     CCmseNonSecureCall,
diff --git a/compiler/rustc_smir/src/stable_mir/ty.rs b/compiler/rustc_smir/src/stable_mir/ty.rs
index 45936857d33..2934af31cd5 100644
--- a/compiler/rustc_smir/src/stable_mir/ty.rs
+++ b/compiler/rustc_smir/src/stable_mir/ty.rs
@@ -1111,6 +1111,7 @@ pub enum Abi {
     RustCold,
     RiscvInterruptM,
     RiscvInterruptS,
+    Custom,
 }
 
 /// A binder represents a possibly generic type and its bound vars.
diff --git a/compiler/rustc_span/src/caching_source_map_view.rs b/compiler/rustc_span/src/caching_source_map_view.rs
index d8a4cc2f2e2..a887b50ec1e 100644
--- a/compiler/rustc_span/src/caching_source_map_view.rs
+++ b/compiler/rustc_span/src/caching_source_map_view.rs
@@ -2,7 +2,7 @@ use std::ops::Range;
 use std::sync::Arc;
 
 use crate::source_map::SourceMap;
-use crate::{BytePos, Pos, RelativeBytePos, SourceFile, SpanData};
+use crate::{BytePos, Pos, RelativeBytePos, SourceFile, SpanData, StableSourceFileId};
 
 #[derive(Clone)]
 struct CacheEntry {
@@ -114,7 +114,7 @@ impl<'sm> CachingSourceMapView<'sm> {
     pub fn span_data_to_lines_and_cols(
         &mut self,
         span_data: &SpanData,
-    ) -> Option<(Arc<SourceFile>, usize, BytePos, usize, BytePos)> {
+    ) -> Option<(StableSourceFileId, usize, BytePos, usize, BytePos)> {
         self.time_stamp += 1;
 
         // Check if lo and hi are in the cached lines.
@@ -132,7 +132,7 @@ impl<'sm> CachingSourceMapView<'sm> {
                 }
 
                 (
-                    Arc::clone(&lo.file),
+                    lo.file.stable_id,
                     lo.line_number,
                     span_data.lo - lo.line.start,
                     hi.line_number,
@@ -226,7 +226,7 @@ impl<'sm> CachingSourceMapView<'sm> {
         assert_eq!(lo.file_index, hi.file_index);
 
         Some((
-            Arc::clone(&lo.file),
+            lo.file.stable_id,
             lo.line_number,
             span_data.lo - lo.line.start,
             hi.line_number,
diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs
index b621920d62b..315dedec107 100644
--- a/compiler/rustc_span/src/hygiene.rs
+++ b/compiler/rustc_span/src/hygiene.rs
@@ -1135,7 +1135,7 @@ impl ExpnKind {
 }
 
 /// The kind of macro invocation or definition.
-#[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Debug)]
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Encodable, Decodable, Hash, Debug)]
 #[derive(HashStable_Generic)]
 pub enum MacroKind {
     /// A bang macro `foo!()`.
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index ed74dea5f1e..c8a29a2f68f 100644
--- a/compiler/rustc_span/src/lib.rs
+++ b/compiler/rustc_span/src/lib.rs
@@ -2600,7 +2600,7 @@ pub trait HashStableContext {
     fn span_data_to_lines_and_cols(
         &mut self,
         span: &SpanData,
-    ) -> Option<(Arc<SourceFile>, usize, BytePos, usize, BytePos)>;
+    ) -> Option<(StableSourceFileId, usize, BytePos, usize, BytePos)>;
     fn hashing_controls(&self) -> HashingControls;
 }
 
@@ -2657,7 +2657,7 @@ where
         };
 
         Hash::hash(&TAG_VALID_SPAN, hasher);
-        Hash::hash(&file.stable_id, hasher);
+        Hash::hash(&file, hasher);
 
         // Hash both the length and the end location (line/column) of a span. If we
         // hash only the length, for example, then two otherwise equal spans with
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index d66f98871b9..cb9ccf4cc3f 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -407,6 +407,7 @@ symbols! {
         abi_amdgpu_kernel,
         abi_avr_interrupt,
         abi_c_cmse_nonsecure_call,
+        abi_custom,
         abi_efiapi,
         abi_gpu_kernel,
         abi_msp430_interrupt,
@@ -429,6 +430,8 @@ symbols! {
         aggregate_raw_ptr,
         alias,
         align,
+        align_of,
+        align_of_val,
         alignment,
         all,
         alloc,
@@ -1353,8 +1356,6 @@ symbols! {
         message,
         meta,
         metadata_type,
-        min_align_of,
-        min_align_of_val,
         min_const_fn,
         min_const_generics,
         min_const_unsafe_fn,
@@ -1512,6 +1513,7 @@ symbols! {
         offset_of_nested,
         offset_of_slice,
         ok_or_else,
+        old_name,
         omit_gdb_pretty_printer_section,
         on,
         on_unimplemented,
@@ -2288,6 +2290,7 @@ symbols! {
         usize_legacy_fn_max_value,
         usize_legacy_fn_min_value,
         usize_legacy_mod,
+        v1,
         v8plus,
         va_arg,
         va_copy,
@@ -2675,7 +2678,7 @@ impl Interner {
         assert_eq!(
             strings.len(),
             init.len() + extra.len(),
-            "`init` or `extra` contain duplicate symbols",
+            "there are duplicate symbols in the rustc symbol list and the extra symbols added by the driver",
         );
         Interner(Lock::new(InternerInner { arena: Default::default(), strings }))
     }
diff --git a/compiler/rustc_target/src/asm/loongarch.rs b/compiler/rustc_target/src/asm/loongarch.rs
index b4ea6fc592a..8783d3953b1 100644
--- a/compiler/rustc_target/src/asm/loongarch.rs
+++ b/compiler/rustc_target/src/asm/loongarch.rs
@@ -34,11 +34,13 @@ impl LoongArchInlineAsmRegClass {
 
     pub fn supported_types(
         self,
-        _arch: InlineAsmArch,
+        arch: InlineAsmArch,
     ) -> &'static [(InlineAsmType, Option<Symbol>)] {
-        match self {
-            Self::reg => types! { _: I8, I16, I32, I64, F32, F64; },
-            Self::freg => types! { f: F32; d: F64; },
+        match (self, arch) {
+            (Self::reg, InlineAsmArch::LoongArch64) => types! { _: I8, I16, I32, I64, F32, F64; },
+            (Self::reg, InlineAsmArch::LoongArch32) => types! { _: I8, I16, I32, F32; },
+            (Self::freg, _) => types! { f: F32; d: F64; },
+            _ => unreachable!("unsupported register class"),
         }
     }
 }
diff --git a/compiler/rustc_target/src/spec/abi_map.rs b/compiler/rustc_target/src/spec/abi_map.rs
index c4978a8e52a..4659bbdb890 100644
--- a/compiler/rustc_target/src/spec/abi_map.rs
+++ b/compiler/rustc_target/src/spec/abi_map.rs
@@ -71,6 +71,8 @@ impl AbiMap {
             (ExternAbi::RustCold, _) if self.os == OsKind::Windows => CanonAbi::Rust,
             (ExternAbi::RustCold, _) => CanonAbi::RustCold,
 
+            (ExternAbi::Custom, _) => CanonAbi::Custom,
+
             (ExternAbi::System { .. }, Arch::X86) if os == OsKind::Windows && !has_c_varargs => {
                 CanonAbi::X86(X86Call::Stdcall)
             }
diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs
index 5d1182fce48..a1eac1fba25 100644
--- a/compiler/rustc_target/src/target_features.rs
+++ b/compiler/rustc_target/src/target_features.rs
@@ -34,6 +34,9 @@ pub enum Stability {
     /// particular for features are actually ABI configuration flags (not all targets are as nice as
     /// RISC-V and have an explicit way to set the ABI separate from target features).
     Forbidden { reason: &'static str },
+    /// This feature can not be set via `-Ctarget-feature` or `#[target_feature]`, it can only be set
+    /// by target modifier flag. Target modifier flags are tracked to be consistent in linked modules.
+    TargetModifierOnly { reason: &'static str, flag: &'static str },
 }
 use Stability::*;
 
@@ -49,6 +52,7 @@ impl<CTX> HashStable<CTX> for Stability {
             Stability::Forbidden { reason } => {
                 reason.hash_stable(hcx, hasher);
             }
+            Stability::TargetModifierOnly { .. } => {}
         }
     }
 }
@@ -74,16 +78,7 @@ impl Stability {
             Stability::Unstable(nightly_feature) => Some(nightly_feature),
             Stability::Stable { .. } => None,
             Stability::Forbidden { .. } => panic!("forbidden features should not reach this far"),
-        }
-    }
-
-    /// Returns whether the feature may be toggled via `#[target_feature]` or `-Ctarget-feature`.
-    /// (It might still be nightly-only even if this returns `true`, so make sure to also check
-    /// `requires_nightly`.)
-    pub fn toggle_allowed(&self) -> Result<(), &'static str> {
-        match self {
-            Stability::Forbidden { reason } => Err(reason),
-            _ => Ok(()),
+            Stability::TargetModifierOnly { .. } => None,
         }
     }
 }
@@ -453,6 +448,30 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
     ("prfchw", Unstable(sym::prfchw_target_feature), &[]),
     ("rdrand", Stable, &[]),
     ("rdseed", Stable, &[]),
+    (
+        "retpoline-external-thunk",
+        Stability::TargetModifierOnly {
+            reason: "use `retpoline-external-thunk` target modifier flag instead",
+            flag: "retpoline-external-thunk",
+        },
+        &[],
+    ),
+    (
+        "retpoline-indirect-branches",
+        Stability::TargetModifierOnly {
+            reason: "use `retpoline` target modifier flag instead",
+            flag: "retpoline",
+        },
+        &[],
+    ),
+    (
+        "retpoline-indirect-calls",
+        Stability::TargetModifierOnly {
+            reason: "use `retpoline` target modifier flag instead",
+            flag: "retpoline",
+        },
+        &[],
+    ),
     ("rtm", Unstable(sym::rtm_target_feature), &[]),
     ("sha", Stable, &["sse2"]),
     ("sha512", Stable, &["avx2"]),
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs
index 37968386e9a..89dab90dc68 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs
@@ -810,7 +810,8 @@ impl<'tcx> OnUnimplementedFormatString {
 
         let mut result = Ok(());
 
-        match FormatString::parse(self.symbol, self.span, &ctx) {
+        let snippet = tcx.sess.source_map().span_to_snippet(self.span).ok();
+        match FormatString::parse(self.symbol, snippet, self.span, &ctx) {
             // Warnings about format specifiers, deprecated parameters, wrong parameters etc.
             // In other words we'd like to let the author know, but we can still try to format the string later
             Ok(FormatString { warnings, .. }) => {
@@ -848,34 +849,27 @@ impl<'tcx> OnUnimplementedFormatString {
                     }
                 }
             }
-            // Errors from the underlying `rustc_parse_format::Parser`
-            Err(errors) => {
+            // Error from the underlying `rustc_parse_format::Parser`
+            Err(e) => {
                 // we cannot return errors from processing the format string as hard error here
                 // as the diagnostic namespace guarantees that malformed input cannot cause an error
                 //
                 // if we encounter any error while processing we nevertheless want to show it as warning
                 // so that users are aware that something is not correct
-                for e in errors {
-                    if self.is_diagnostic_namespace_variant {
-                        if let Some(trait_def_id) = trait_def_id.as_local() {
-                            tcx.emit_node_span_lint(
-                                UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
-                                tcx.local_def_id_to_hir_id(trait_def_id),
-                                self.span,
-                                WrappedParserError { description: e.description, label: e.label },
-                            );
-                        }
-                    } else {
-                        let reported = struct_span_code_err!(
-                            tcx.dcx(),
+                if self.is_diagnostic_namespace_variant {
+                    if let Some(trait_def_id) = trait_def_id.as_local() {
+                        tcx.emit_node_span_lint(
+                            UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
+                            tcx.local_def_id_to_hir_id(trait_def_id),
                             self.span,
-                            E0231,
-                            "{}",
-                            e.description,
-                        )
-                        .emit();
-                        result = Err(reported);
+                            WrappedParserError { description: e.description, label: e.label },
+                        );
                     }
+                } else {
+                    let reported =
+                        struct_span_code_err!(tcx.dcx(), self.span, E0231, "{}", e.description,)
+                            .emit();
+                    result = Err(reported);
                 }
             }
         }
@@ -896,7 +890,8 @@ impl<'tcx> OnUnimplementedFormatString {
             Ctx::RustcOnUnimplemented { tcx, trait_def_id }
         };
 
-        if let Ok(s) = FormatString::parse(self.symbol, self.span, &ctx) {
+        // No point passing a snippet here, we already did that in `verify`
+        if let Ok(s) = FormatString::parse(self.symbol, None, self.span, &ctx) {
             s.format(args)
         } else {
             // we cannot return errors from processing the format string as hard error here
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_condition.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_condition.rs
index e8ea9f2d23e..171d05230d4 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_condition.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_condition.rs
@@ -198,7 +198,7 @@ enum LitOrArg {
 
 impl FilterFormatString {
     fn parse(input: Symbol) -> Self {
-        let pieces = Parser::new(input.as_str(), None, None, false, ParseMode::Format)
+        let pieces = Parser::new(input.as_str(), None, None, false, ParseMode::Diagnostic)
             .map(|p| match p {
                 Piece::Lit(s) => LitOrArg::Lit(s.to_owned()),
                 // We just ignore formatspecs here
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs
index 7c1dfc1728f..3e8b906fa93 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs
@@ -5,12 +5,11 @@ use errors::*;
 use rustc_middle::ty::print::TraitRefPrintSugared;
 use rustc_middle::ty::{GenericParamDefKind, TyCtxt};
 use rustc_parse_format::{
-    Alignment, Argument, Count, FormatSpec, ParseError, ParseMode, Parser, Piece as RpfPiece,
-    Position,
+    Argument, FormatSpec, ParseError, ParseMode, Parser, Piece as RpfPiece, Position,
 };
 use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES;
 use rustc_span::def_id::DefId;
-use rustc_span::{BytePos, Pos, Span, Symbol, kw, sym};
+use rustc_span::{InnerSpan, Span, Symbol, kw, sym};
 
 /// Like [std::fmt::Arguments] this is a string that has been parsed into "pieces",
 /// either as string pieces or dynamic arguments.
@@ -160,32 +159,32 @@ impl FormatString {
 
     pub fn parse<'tcx>(
         input: Symbol,
+        snippet: Option<String>,
         span: Span,
         ctx: &Ctx<'tcx>,
-    ) -> Result<Self, Vec<ParseError>> {
+    ) -> Result<Self, ParseError> {
         let s = input.as_str();
-        let mut parser = Parser::new(s, None, None, false, ParseMode::Format);
-        let mut pieces = Vec::new();
+        let mut parser = Parser::new(s, None, snippet, false, ParseMode::Diagnostic);
+        let pieces: Vec<_> = parser.by_ref().collect();
+
+        if let Some(err) = parser.errors.into_iter().next() {
+            return Err(err);
+        }
         let mut warnings = Vec::new();
 
-        for piece in &mut parser {
-            match piece {
-                RpfPiece::Lit(lit) => {
-                    pieces.push(Piece::Lit(lit.into()));
-                }
+        let pieces = pieces
+            .into_iter()
+            .map(|piece| match piece {
+                RpfPiece::Lit(lit) => Piece::Lit(lit.into()),
                 RpfPiece::NextArgument(arg) => {
-                    warn_on_format_spec(arg.format.clone(), &mut warnings, span);
-                    let arg = parse_arg(&arg, ctx, &mut warnings, span);
-                    pieces.push(Piece::Arg(arg));
+                    warn_on_format_spec(&arg.format, &mut warnings, span, parser.is_source_literal);
+                    let arg = parse_arg(&arg, ctx, &mut warnings, span, parser.is_source_literal);
+                    Piece::Arg(arg)
                 }
-            }
-        }
+            })
+            .collect();
 
-        if parser.errors.is_empty() {
-            Ok(FormatString { input, pieces, span, warnings })
-        } else {
-            Err(parser.errors)
-        }
+        Ok(FormatString { input, pieces, span, warnings })
     }
 
     pub fn format(&self, args: &FormatArgs<'_>) -> String {
@@ -229,11 +228,12 @@ fn parse_arg<'tcx>(
     ctx: &Ctx<'tcx>,
     warnings: &mut Vec<FormatWarning>,
     input_span: Span,
+    is_source_literal: bool,
 ) -> FormatArg {
     let (Ctx::RustcOnUnimplemented { tcx, trait_def_id }
     | Ctx::DiagnosticOnUnimplemented { tcx, trait_def_id }) = ctx;
 
-    let span = slice_span(input_span, arg.position_span.clone());
+    let span = slice_span(input_span, arg.position_span.clone(), is_source_literal);
 
     match arg.position {
         // Something like "hello {name}"
@@ -283,39 +283,24 @@ fn parse_arg<'tcx>(
 
 /// `#[rustc_on_unimplemented]` and `#[diagnostic::...]` don't actually do anything
 /// with specifiers, so emit a warning if they are used.
-fn warn_on_format_spec(spec: FormatSpec<'_>, warnings: &mut Vec<FormatWarning>, input_span: Span) {
-    if !matches!(
-        spec,
-        FormatSpec {
-            fill: None,
-            fill_span: None,
-            align: Alignment::AlignUnknown,
-            sign: None,
-            alternate: false,
-            zero_pad: false,
-            debug_hex: None,
-            precision: Count::CountImplied,
-            precision_span: None,
-            width: Count::CountImplied,
-            width_span: None,
-            ty: _,
-            ty_span: _,
-        },
-    ) {
-        let span = spec.ty_span.map(|inner| slice_span(input_span, inner)).unwrap_or(input_span);
+fn warn_on_format_spec(
+    spec: &FormatSpec<'_>,
+    warnings: &mut Vec<FormatWarning>,
+    input_span: Span,
+    is_source_literal: bool,
+) {
+    if spec.ty != "" {
+        let span = spec
+            .ty_span
+            .as_ref()
+            .map(|inner| slice_span(input_span, inner.clone(), is_source_literal))
+            .unwrap_or(input_span);
         warnings.push(FormatWarning::InvalidSpecifier { span, name: spec.ty.into() })
     }
 }
 
-fn slice_span(input: Span, range: Range<usize>) -> Span {
-    let span = input.data();
-
-    Span::new(
-        span.lo + BytePos::from_usize(range.start),
-        span.lo + BytePos::from_usize(range.end),
-        span.ctxt,
-        span.parent,
-    )
+fn slice_span(input: Span, Range { start, end }: Range<usize>, is_source_literal: bool) -> Span {
+    if is_source_literal { input.from_inner(InnerSpan { start, end }) } else { input }
 }
 
 pub mod errors {
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
index 68bd9440538..ee5a5b247ce 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
@@ -2995,9 +2995,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 if local {
                     err.note("all local variables must have a statically known size");
                 }
-                if !tcx.features().unsized_locals() {
-                    err.help("unsized locals are gated as an unstable feature");
-                }
             }
             ObligationCauseCode::SizedArgumentType(hir_id) => {
                 let mut ty = None;
diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
index d5d318ee490..b0c8fa1f217 100644
--- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
+++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
@@ -11,7 +11,8 @@
 
 use std::assert_matches::assert_matches;
 
-use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk};
+use rustc_infer::infer::InferCtxt;
+use rustc_infer::traits::Obligation;
 use rustc_macros::extension;
 use rustc_middle::traits::ObligationCause;
 use rustc_middle::traits::solve::{Certainty, Goal, GoalSource, NoSolution, QueryResult};
@@ -20,7 +21,7 @@ use rustc_middle::{bug, ty};
 use rustc_next_trait_solver::resolve::eager_resolve_vars;
 use rustc_next_trait_solver::solve::inspect::{self, instantiate_canonical_state};
 use rustc_next_trait_solver::solve::{GenerateProofTree, MaybeCause, SolverDelegateEvalExt as _};
-use rustc_span::{DUMMY_SP, Span};
+use rustc_span::Span;
 use tracing::instrument;
 
 use crate::solve::delegate::SolverDelegate;
@@ -60,28 +61,29 @@ impl<'tcx> NormalizesToTermHack<'tcx> {
     /// Relate the `term` with the new `unconstrained_term` created
     /// when computing the proof tree for this `NormalizesTo` goals.
     /// This handles nested obligations.
-    fn constrain(
-        self,
+    fn constrain_and(
+        &self,
         infcx: &InferCtxt<'tcx>,
         span: Span,
         param_env: ty::ParamEnv<'tcx>,
+        f: impl FnOnce(&ObligationCtxt<'_, 'tcx>),
     ) -> Result<Certainty, NoSolution> {
-        infcx
-            .at(&ObligationCause::dummy_with_span(span), param_env)
-            .eq(DefineOpaqueTypes::Yes, self.term, self.unconstrained_term)
-            .map_err(|_| NoSolution)
-            .and_then(|InferOk { value: (), obligations }| {
-                let ocx = ObligationCtxt::new(infcx);
-                ocx.register_obligations(obligations);
-                let errors = ocx.select_all_or_error();
-                if errors.is_empty() {
-                    Ok(Certainty::Yes)
-                } else if errors.iter().all(|e| !e.is_true_error()) {
-                    Ok(Certainty::AMBIGUOUS)
-                } else {
-                    Err(NoSolution)
-                }
-            })
+        let ocx = ObligationCtxt::new(infcx);
+        ocx.eq(
+            &ObligationCause::dummy_with_span(span),
+            param_env,
+            self.term,
+            self.unconstrained_term,
+        )?;
+        f(&ocx);
+        let errors = ocx.select_all_or_error();
+        if errors.is_empty() {
+            Ok(Certainty::Yes)
+        } else if errors.iter().all(|e| !e.is_true_error()) {
+            Ok(Certainty::AMBIGUOUS)
+        } else {
+            Err(NoSolution)
+        }
     }
 }
 
@@ -160,11 +162,11 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> {
         let () =
             instantiate_canonical_state(infcx, span, param_env, &mut orig_values, self.final_state);
 
-        if let Some(term_hack) = self.goal.normalizes_to_term_hack {
+        if let Some(term_hack) = &self.goal.normalizes_to_term_hack {
             // FIXME: We ignore the expected term of `NormalizesTo` goals
             // when computing the result of its candidates. This is
             // scuffed.
-            let _ = term_hack.constrain(infcx, span, param_env);
+            let _ = term_hack.constrain_and(infcx, span, param_env, |_| {});
         }
 
         instantiated_goals
@@ -240,13 +242,39 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> {
                 // building their proof tree, the expected term was unconstrained, but when
                 // instantiating the candidate it is already constrained to the result of another
                 // candidate.
-                let proof_tree = infcx
-                    .probe(|_| infcx.evaluate_root_goal_raw(goal, GenerateProofTree::Yes, None).1);
+                let normalizes_to_term_hack = NormalizesToTermHack { term, unconstrained_term };
+                let (proof_tree, nested_goals_result) = infcx.probe(|_| {
+                    // Here, if we have any nested goals, then we make sure to apply them
+                    // considering the constrained RHS, and pass the resulting certainty to
+                    // `InspectGoal::new` so that the goal has the right result (and maintains
+                    // the impression that we don't do this normalizes-to infer hack at all).
+                    let (nested, proof_tree) =
+                        infcx.evaluate_root_goal_raw(goal, GenerateProofTree::Yes, None);
+                    let proof_tree = proof_tree.unwrap();
+                    let nested_goals_result = nested.and_then(|(nested, _)| {
+                        normalizes_to_term_hack.constrain_and(
+                            infcx,
+                            span,
+                            proof_tree.uncanonicalized_goal.param_env,
+                            |ocx| {
+                                ocx.register_obligations(nested.0.into_iter().map(|(_, goal)| {
+                                    Obligation::new(
+                                        infcx.tcx,
+                                        ObligationCause::dummy_with_span(span),
+                                        goal.param_env,
+                                        goal.predicate,
+                                    )
+                                }));
+                            },
+                        )
+                    });
+                    (proof_tree, nested_goals_result)
+                });
                 InspectGoal::new(
                     infcx,
                     self.goal.depth + 1,
-                    proof_tree.unwrap(),
-                    Some(NormalizesToTermHack { term, unconstrained_term }),
+                    proof_tree,
+                    Some((normalizes_to_term_hack, nested_goals_result)),
                     source,
                 )
             }
@@ -393,20 +421,21 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
         infcx: &'a InferCtxt<'tcx>,
         depth: usize,
         root: inspect::GoalEvaluation<TyCtxt<'tcx>>,
-        normalizes_to_term_hack: Option<NormalizesToTermHack<'tcx>>,
+        term_hack_and_nested_certainty: Option<(
+            NormalizesToTermHack<'tcx>,
+            Result<Certainty, NoSolution>,
+        )>,
         source: GoalSource,
     ) -> Self {
         let infcx = <&SolverDelegate<'tcx>>::from(infcx);
 
         let inspect::GoalEvaluation { uncanonicalized_goal, orig_values, evaluation } = root;
+        // If there's a normalizes-to goal, AND the evaluation result with the result of
+        // constraining the normalizes-to RHS and computing the nested goals.
         let result = evaluation.result.and_then(|ok| {
-            if let Some(term_hack) = normalizes_to_term_hack {
-                infcx
-                    .probe(|_| term_hack.constrain(infcx, DUMMY_SP, uncanonicalized_goal.param_env))
-                    .map(|certainty| ok.value.certainty.and(certainty))
-            } else {
-                Ok(ok.value.certainty)
-            }
+            let nested_goals_certainty =
+                term_hack_and_nested_certainty.map_or(Ok(Certainty::Yes), |(_, c)| c)?;
+            Ok(ok.value.certainty.and(nested_goals_certainty))
         });
 
         InspectGoal {
@@ -416,7 +445,7 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
             goal: eager_resolve_vars(infcx, uncanonicalized_goal),
             result,
             evaluation_kind: evaluation.kind,
-            normalizes_to_term_hack,
+            normalizes_to_term_hack: term_hack_and_nested_certainty.map(|(n, _)| n),
             source,
         }
     }
diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
index 220a847cc23..47d207e8d41 100644
--- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
+++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
@@ -414,8 +414,8 @@ fn virtual_call_violations_for_method<'tcx>(
 
     let receiver_ty = tcx.liberate_late_bound_regions(method.def_id, sig.input(0));
 
-    // Until `unsized_locals` is fully implemented, `self: Self` can't be dispatched on.
-    // However, this is already considered dyn compatible. We allow it as a special case here.
+    // `self: Self` can't be dispatched on.
+    // However, this is considered dyn compatible. We allow it as a special case here.
     // FIXME(mikeyhew) get rid of this `if` statement once `receiver_is_dispatchable` allows
     // `Receiver: Unsize<Receiver[Self => dyn Trait]>`.
     if receiver_ty != tcx.types.self_param {
diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
index eb34cb10c68..a54eb80fedc 100644
--- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
@@ -9,7 +9,7 @@ use rustc_macros::extension;
 pub use rustc_middle::traits::query::NormalizationResult;
 use rustc_middle::ty::{
     self, FallibleTypeFolder, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable,
-    TypeVisitableExt, TypeVisitor, TypingMode,
+    TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode,
 };
 use rustc_span::DUMMY_SP;
 use tracing::{debug, info, instrument};
@@ -127,7 +127,7 @@ struct MaxEscapingBoundVarVisitor {
 }
 
 impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for MaxEscapingBoundVarVisitor {
-    fn visit_binder<T: TypeFoldable<TyCtxt<'tcx>>>(&mut self, t: &ty::Binder<'tcx, T>) {
+    fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &ty::Binder<'tcx, T>) {
         self.outer_index.shift_in(1);
         t.super_visit_with(self);
         self.outer_index.shift_out(1);
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 3a2f9e8ca17..1b9b68fa980 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -1919,12 +1919,23 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
         // impl `impl<T: ?Sized> Any for T { .. }`. This really shouldn't exist but is
         // necessary due to #57893. We again arbitrarily prefer the applicable candidate
         // with the lowest index.
+        //
+        // We do not want to use these impls to guide inference in case a user-written impl
+        // may also apply.
         let object_bound = candidates
             .iter()
             .filter_map(|c| if let ObjectCandidate(i) = c.candidate { Some(i) } else { None })
             .try_reduce(|c1, c2| if has_non_region_infer { None } else { Some(c1.min(c2)) });
         match object_bound {
-            Some(Some(index)) => return Some(ObjectCandidate(index)),
+            Some(Some(index)) => {
+                return if has_non_region_infer
+                    && candidates.iter().any(|c| matches!(c.candidate, ImplCandidate(_)))
+                {
+                    None
+                } else {
+                    Some(ObjectCandidate(index))
+                };
+            }
             Some(None) => {}
             None => return None,
         }
diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs
index 035fd38c48a..0723aebd5d2 100644
--- a/compiler/rustc_trait_selection/src/traits/util.rs
+++ b/compiler/rustc_trait_selection/src/traits/util.rs
@@ -1,4 +1,4 @@
-use std::collections::{BTreeMap, VecDeque};
+use std::collections::VecDeque;
 
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
 use rustc_hir::LangItem;
@@ -9,6 +9,7 @@ use rustc_middle::bug;
 use rustc_middle::ty::{
     self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
 };
+pub use rustc_next_trait_solver::placeholder::BoundVarReplacer;
 use rustc_span::Span;
 use smallvec::{SmallVec, smallvec};
 use tracing::debug;
@@ -212,158 +213,12 @@ pub fn with_replaced_escaping_bound_vars<
     }
 }
 
-pub struct BoundVarReplacer<'a, 'tcx> {
-    infcx: &'a InferCtxt<'tcx>,
-    // These three maps track the bound variable that were replaced by placeholders. It might be
-    // nice to remove these since we already have the `kind` in the placeholder; we really just need
-    // the `var` (but we *could* bring that into scope if we were to track them as we pass them).
-    mapped_regions: FxIndexMap<ty::PlaceholderRegion, ty::BoundRegion>,
-    mapped_types: FxIndexMap<ty::PlaceholderType, ty::BoundTy>,
-    mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>,
-    // The current depth relative to *this* folding, *not* the entire normalization. In other words,
-    // the depth of binders we've passed here.
-    current_index: ty::DebruijnIndex,
-    // The `UniverseIndex` of the binding levels above us. These are optional, since we are lazy:
-    // we don't actually create a universe until we see a bound var we have to replace.
-    universe_indices: &'a mut Vec<Option<ty::UniverseIndex>>,
-}
-
-impl<'a, 'tcx> BoundVarReplacer<'a, 'tcx> {
-    /// Returns `Some` if we *were* able to replace bound vars. If there are any bound vars that
-    /// use a binding level above `universe_indices.len()`, we fail.
-    pub fn replace_bound_vars<T: TypeFoldable<TyCtxt<'tcx>>>(
-        infcx: &'a InferCtxt<'tcx>,
-        universe_indices: &'a mut Vec<Option<ty::UniverseIndex>>,
-        value: T,
-    ) -> (
-        T,
-        FxIndexMap<ty::PlaceholderRegion, ty::BoundRegion>,
-        FxIndexMap<ty::PlaceholderType, ty::BoundTy>,
-        BTreeMap<ty::PlaceholderConst, ty::BoundVar>,
-    ) {
-        let mapped_regions: FxIndexMap<ty::PlaceholderRegion, ty::BoundRegion> =
-            FxIndexMap::default();
-        let mapped_types: FxIndexMap<ty::PlaceholderType, ty::BoundTy> = FxIndexMap::default();
-        let mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar> = BTreeMap::new();
-
-        let mut replacer = BoundVarReplacer {
-            infcx,
-            mapped_regions,
-            mapped_types,
-            mapped_consts,
-            current_index: ty::INNERMOST,
-            universe_indices,
-        };
-
-        let value = value.fold_with(&mut replacer);
-
-        (value, replacer.mapped_regions, replacer.mapped_types, replacer.mapped_consts)
-    }
-
-    fn universe_for(&mut self, debruijn: ty::DebruijnIndex) -> ty::UniverseIndex {
-        let infcx = self.infcx;
-        let index =
-            self.universe_indices.len() + self.current_index.as_usize() - debruijn.as_usize() - 1;
-        let universe = self.universe_indices[index].unwrap_or_else(|| {
-            for i in self.universe_indices.iter_mut().take(index + 1) {
-                *i = i.or_else(|| Some(infcx.create_next_universe()))
-            }
-            self.universe_indices[index].unwrap()
-        });
-        universe
-    }
-}
-
-impl<'tcx> TypeFolder<TyCtxt<'tcx>> for BoundVarReplacer<'_, 'tcx> {
-    fn cx(&self) -> TyCtxt<'tcx> {
-        self.infcx.tcx
-    }
-
-    fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
-        &mut self,
-        t: ty::Binder<'tcx, T>,
-    ) -> ty::Binder<'tcx, T> {
-        self.current_index.shift_in(1);
-        let t = t.super_fold_with(self);
-        self.current_index.shift_out(1);
-        t
-    }
-
-    fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
-        match r.kind() {
-            ty::ReBound(debruijn, _)
-                if debruijn.as_usize()
-                    >= self.current_index.as_usize() + self.universe_indices.len() =>
-            {
-                bug!(
-                    "Bound vars {r:#?} outside of `self.universe_indices`: {:#?}",
-                    self.universe_indices
-                );
-            }
-            ty::ReBound(debruijn, br) if debruijn >= self.current_index => {
-                let universe = self.universe_for(debruijn);
-                let p = ty::PlaceholderRegion { universe, bound: br };
-                self.mapped_regions.insert(p, br);
-                ty::Region::new_placeholder(self.infcx.tcx, p)
-            }
-            _ => r,
-        }
-    }
-
-    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
-        match *t.kind() {
-            ty::Bound(debruijn, _)
-                if debruijn.as_usize() + 1
-                    > self.current_index.as_usize() + self.universe_indices.len() =>
-            {
-                bug!(
-                    "Bound vars {t:#?} outside of `self.universe_indices`: {:#?}",
-                    self.universe_indices
-                );
-            }
-            ty::Bound(debruijn, bound_ty) if debruijn >= self.current_index => {
-                let universe = self.universe_for(debruijn);
-                let p = ty::PlaceholderType { universe, bound: bound_ty };
-                self.mapped_types.insert(p, bound_ty);
-                Ty::new_placeholder(self.infcx.tcx, p)
-            }
-            _ if t.has_vars_bound_at_or_above(self.current_index) => t.super_fold_with(self),
-            _ => t,
-        }
-    }
-
-    fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
-        match ct.kind() {
-            ty::ConstKind::Bound(debruijn, _)
-                if debruijn.as_usize() + 1
-                    > self.current_index.as_usize() + self.universe_indices.len() =>
-            {
-                bug!(
-                    "Bound vars {ct:#?} outside of `self.universe_indices`: {:#?}",
-                    self.universe_indices
-                );
-            }
-            ty::ConstKind::Bound(debruijn, bound_const) if debruijn >= self.current_index => {
-                let universe = self.universe_for(debruijn);
-                let p = ty::PlaceholderConst { universe, bound: bound_const };
-                self.mapped_consts.insert(p, bound_const);
-                ty::Const::new_placeholder(self.infcx.tcx, p)
-            }
-            _ => ct.super_fold_with(self),
-        }
-    }
-
-    fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
-        if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p }
-    }
-}
-
 /// The inverse of [`BoundVarReplacer`]: replaces placeholders with the bound vars from which they came.
 pub struct PlaceholderReplacer<'a, 'tcx> {
     infcx: &'a InferCtxt<'tcx>,
     mapped_regions: FxIndexMap<ty::PlaceholderRegion, ty::BoundRegion>,
     mapped_types: FxIndexMap<ty::PlaceholderType, ty::BoundTy>,
-    mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>,
+    mapped_consts: FxIndexMap<ty::PlaceholderConst, ty::BoundVar>,
     universe_indices: &'a [Option<ty::UniverseIndex>],
     current_index: ty::DebruijnIndex,
 }
@@ -373,7 +228,7 @@ impl<'a, 'tcx> PlaceholderReplacer<'a, 'tcx> {
         infcx: &'a InferCtxt<'tcx>,
         mapped_regions: FxIndexMap<ty::PlaceholderRegion, ty::BoundRegion>,
         mapped_types: FxIndexMap<ty::PlaceholderType, ty::BoundTy>,
-        mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>,
+        mapped_consts: FxIndexMap<ty::PlaceholderConst, ty::BoundVar>,
         universe_indices: &'a [Option<ty::UniverseIndex>],
         value: T,
     ) -> T {
diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs
index 79ac622df32..11becea998c 100644
--- a/compiler/rustc_ty_utils/src/ty.rs
+++ b/compiler/rustc_ty_utils/src/ty.rs
@@ -7,8 +7,7 @@ use rustc_infer::infer::TyCtxtInferExt;
 use rustc_middle::bug;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::{
-    self, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor, Upcast,
-    fold_regions,
+    self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, Upcast, fold_regions,
 };
 use rustc_span::DUMMY_SP;
 use rustc_span::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
@@ -186,7 +185,7 @@ struct ImplTraitInTraitFinder<'a, 'tcx> {
 }
 
 impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ImplTraitInTraitFinder<'_, 'tcx> {
-    fn visit_binder<T: TypeFoldable<TyCtxt<'tcx>>>(&mut self, binder: &ty::Binder<'tcx, T>) {
+    fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, binder: &ty::Binder<'tcx, T>) {
         self.depth.shift_in(1);
         binder.super_visit_with(self);
         self.depth.shift_out(1);
diff --git a/compiler/rustc_type_ir/Cargo.toml b/compiler/rustc_type_ir/Cargo.toml
index 83d3d78298e..4bd7bfe79be 100644
--- a/compiler/rustc_type_ir/Cargo.toml
+++ b/compiler/rustc_type_ir/Cargo.toml
@@ -9,7 +9,7 @@ bitflags = "2.4.1"
 derive-where = "1.2.7"
 ena = "0.14.3"
 indexmap = "2.0.0"
-rustc-hash = "1.1.0"
+rustc-hash = "2.0.0"
 rustc_ast_ir = { path = "../rustc_ast_ir", default-features = false }
 rustc_data_structures = { path = "../rustc_data_structures", optional = true }
 rustc_index = { path = "../rustc_index", default-features = false }
diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs
index 55c0a3bba9f..927a2ce84ea 100644
--- a/compiler/rustc_type_ir/src/binder.rs
+++ b/compiler/rustc_type_ir/src/binder.rs
@@ -128,7 +128,7 @@ impl<I: Interner, T: TypeFoldable<I>> TypeFoldable<I> for Binder<I, T> {
     }
 }
 
-impl<I: Interner, T: TypeFoldable<I>> TypeVisitable<I> for Binder<I, T> {
+impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for Binder<I, T> {
     fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result {
         visitor.visit_binder(self)
     }
@@ -147,7 +147,7 @@ impl<I: Interner, T: TypeFoldable<I>> TypeSuperFoldable<I> for Binder<I, T> {
     }
 }
 
-impl<I: Interner, T: TypeFoldable<I>> TypeSuperVisitable<I> for Binder<I, T> {
+impl<I: Interner, T: TypeVisitable<I>> TypeSuperVisitable<I> for Binder<I, T> {
     fn super_visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result {
         self.as_ref().skip_binder().visit_with(visitor)
     }
@@ -292,7 +292,7 @@ impl<I: Interner> ValidateBoundVars<I> {
 impl<I: Interner> TypeVisitor<I> for ValidateBoundVars<I> {
     type Result = ControlFlow<()>;
 
-    fn visit_binder<T: TypeFoldable<I>>(&mut self, t: &Binder<I, T>) -> Self::Result {
+    fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &Binder<I, T>) -> Self::Result {
         self.binder_index.shift_in(1);
         let result = t.super_visit_with(self);
         self.binder_index.shift_out(1);
diff --git a/compiler/rustc_type_ir/src/fast_reject.rs b/compiler/rustc_type_ir/src/fast_reject.rs
index 615d07707b9..fa5e8d43702 100644
--- a/compiler/rustc_type_ir/src/fast_reject.rs
+++ b/compiler/rustc_type_ir/src/fast_reject.rs
@@ -237,9 +237,6 @@ impl<I: Interner, const INSTANTIATE_LHS_WITH_INFER: bool, const INSTANTIATE_RHS_
     }
 
     pub fn types_may_unify(self, lhs: I::Ty, rhs: I::Ty) -> bool {
-        if lhs == rhs {
-            return true;
-        }
         self.types_may_unify_inner(lhs, rhs, Self::STARTING_DEPTH)
     }
 
@@ -268,6 +265,10 @@ impl<I: Interner, const INSTANTIATE_LHS_WITH_INFER: bool, const INSTANTIATE_RHS_
     }
 
     fn types_may_unify_inner(self, lhs: I::Ty, rhs: I::Ty, depth: usize) -> bool {
+        if lhs == rhs {
+            return true;
+        }
+
         match rhs.kind() {
             // Start by checking whether the `rhs` type may unify with
             // pretty much everything. Just return `true` in that case.
diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs
index fa88bcb891a..436ab9f80b6 100644
--- a/compiler/rustc_type_ir/src/inherent.rs
+++ b/compiler/rustc_type_ir/src/inherent.rs
@@ -228,6 +228,8 @@ pub trait Region<I: Interner<Region = Self>>:
 
     fn new_static(interner: I) -> Self;
 
+    fn new_placeholder(interner: I, var: I::PlaceholderRegion) -> Self;
+
     fn is_bound(self) -> bool {
         matches!(self.kind(), ty::ReBound(..))
     }
@@ -254,6 +256,8 @@ pub trait Const<I: Interner<Const = Self>>:
 
     fn new_anon_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self;
 
+    fn new_placeholder(interner: I, param: I::PlaceholderConst) -> Self;
+
     fn new_unevaluated(interner: I, uv: ty::UnevaluatedConst<I>) -> Self;
 
     fn new_expr(interner: I, expr: I::ExprConst) -> Self;
@@ -524,13 +528,14 @@ pub trait Clauses<I: Interner<Clauses = Self>>:
 }
 
 /// Common capabilities of placeholder kinds
-pub trait PlaceholderLike: Copy + Debug + Hash + Eq {
+pub trait PlaceholderLike<I: Interner>: Copy + Debug + Hash + Eq {
     fn universe(self) -> ty::UniverseIndex;
     fn var(self) -> ty::BoundVar;
 
+    type Bound: BoundVarLike<I>;
+    fn new(ui: ty::UniverseIndex, bound: Self::Bound) -> Self;
+    fn new_anon(ui: ty::UniverseIndex, var: ty::BoundVar) -> Self;
     fn with_updated_universe(self, ui: ty::UniverseIndex) -> Self;
-
-    fn new(ui: ty::UniverseIndex, var: ty::BoundVar) -> Self;
 }
 
 pub trait IntoKind {
@@ -539,13 +544,13 @@ pub trait IntoKind {
     fn kind(self) -> Self::Kind;
 }
 
-pub trait BoundVarLike<I: Interner> {
+pub trait BoundVarLike<I: Interner>: Copy + Debug + Hash + Eq {
     fn var(self) -> ty::BoundVar;
 
     fn assert_eq(self, var: I::BoundVarKind);
 }
 
-pub trait ParamLike {
+pub trait ParamLike: Copy + Debug + Hash + Eq {
     fn index(self) -> u32;
 }
 
diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs
index cc0925b2c32..033d2579678 100644
--- a/compiler/rustc_type_ir/src/interner.rs
+++ b/compiler/rustc_type_ir/src/interner.rs
@@ -103,9 +103,9 @@ pub trait Interner:
     type Ty: Ty<Self>;
     type Tys: Tys<Self>;
     type FnInputTys: Copy + Debug + Hash + Eq + SliceLike<Item = Self::Ty> + TypeVisitable<Self>;
-    type ParamTy: Copy + Debug + Hash + Eq + ParamLike;
-    type BoundTy: Copy + Debug + Hash + Eq + BoundVarLike<Self>;
-    type PlaceholderTy: PlaceholderLike;
+    type ParamTy: ParamLike;
+    type BoundTy: BoundVarLike<Self>;
+    type PlaceholderTy: PlaceholderLike<Self, Bound = Self::BoundTy>;
 
     // Things stored inside of tys
     type ErrorGuaranteed: Copy + Debug + Hash + Eq;
@@ -131,19 +131,19 @@ pub trait Interner:
 
     // Kinds of consts
     type Const: Const<Self>;
-    type PlaceholderConst: PlaceholderLike;
     type ParamConst: Copy + Debug + Hash + Eq + ParamLike;
-    type BoundConst: Copy + Debug + Hash + Eq + BoundVarLike<Self>;
+    type BoundConst: BoundVarLike<Self>;
+    type PlaceholderConst: PlaceholderLike<Self, Bound = Self::BoundConst>;
     type ValueConst: ValueConst<Self>;
     type ExprConst: ExprConst<Self>;
     type ValTree: Copy + Debug + Hash + Eq;
 
     // Kinds of regions
     type Region: Region<Self>;
-    type EarlyParamRegion: Copy + Debug + Hash + Eq + ParamLike;
+    type EarlyParamRegion: ParamLike;
     type LateParamRegion: Copy + Debug + Hash + Eq;
-    type BoundRegion: Copy + Debug + Hash + Eq + BoundVarLike<Self>;
-    type PlaceholderRegion: PlaceholderLike;
+    type BoundRegion: BoundVarLike<Self>;
+    type PlaceholderRegion: PlaceholderLike<Self, Bound = Self::BoundRegion>;
 
     // Predicates
     type ParamEnv: ParamEnv<Self>;
diff --git a/compiler/rustc_type_ir/src/search_graph/global_cache.rs b/compiler/rustc_type_ir/src/search_graph/global_cache.rs
index 0ce927b58bb..a2442660259 100644
--- a/compiler/rustc_type_ir/src/search_graph/global_cache.rs
+++ b/compiler/rustc_type_ir/src/search_graph/global_cache.rs
@@ -4,7 +4,7 @@ use super::{AvailableDepth, Cx, NestedGoals};
 use crate::data_structures::HashMap;
 
 struct Success<X: Cx> {
-    additional_depth: usize,
+    required_depth: usize,
     nested_goals: NestedGoals<X>,
     result: X::Tracked<X::Result>,
 }
@@ -28,7 +28,7 @@ struct CacheEntry<X: Cx> {
 #[derive_where(Debug; X: Cx)]
 pub(super) struct CacheData<'a, X: Cx> {
     pub(super) result: X::Result,
-    pub(super) additional_depth: usize,
+    pub(super) required_depth: usize,
     pub(super) encountered_overflow: bool,
     pub(super) nested_goals: &'a NestedGoals<X>,
 }
@@ -47,7 +47,7 @@ impl<X: Cx> GlobalCache<X> {
         origin_result: X::Result,
         dep_node: X::DepNodeIndex,
 
-        additional_depth: usize,
+        required_depth: usize,
         encountered_overflow: bool,
         nested_goals: NestedGoals<X>,
     ) {
@@ -55,13 +55,13 @@ impl<X: Cx> GlobalCache<X> {
         let entry = self.map.entry(input).or_default();
         if encountered_overflow {
             let with_overflow = WithOverflow { nested_goals, result };
-            let prev = entry.with_overflow.insert(additional_depth, with_overflow);
+            let prev = entry.with_overflow.insert(required_depth, with_overflow);
             if let Some(prev) = &prev {
                 assert!(cx.evaluation_is_concurrent());
                 assert_eq!(cx.get_tracked(&prev.result), origin_result);
             }
         } else {
-            let prev = entry.success.replace(Success { additional_depth, nested_goals, result });
+            let prev = entry.success.replace(Success { required_depth, nested_goals, result });
             if let Some(prev) = &prev {
                 assert!(cx.evaluation_is_concurrent());
                 assert_eq!(cx.get_tracked(&prev.result), origin_result);
@@ -81,13 +81,13 @@ impl<X: Cx> GlobalCache<X> {
         mut candidate_is_applicable: impl FnMut(&NestedGoals<X>) -> bool,
     ) -> Option<CacheData<'a, X>> {
         let entry = self.map.get(&input)?;
-        if let Some(Success { additional_depth, ref nested_goals, ref result }) = entry.success {
-            if available_depth.cache_entry_is_applicable(additional_depth)
+        if let Some(Success { required_depth, ref nested_goals, ref result }) = entry.success {
+            if available_depth.cache_entry_is_applicable(required_depth)
                 && candidate_is_applicable(nested_goals)
             {
                 return Some(CacheData {
                     result: cx.get_tracked(&result),
-                    additional_depth,
+                    required_depth,
                     encountered_overflow: false,
                     nested_goals,
                 });
@@ -101,7 +101,7 @@ impl<X: Cx> GlobalCache<X> {
             if candidate_is_applicable(nested_goals) {
                 return Some(CacheData {
                     result: cx.get_tracked(result),
-                    additional_depth,
+                    required_depth: additional_depth,
                     encountered_overflow: true,
                     nested_goals,
                 });
diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs
index 1acd5d5c2af..f0eb96b47b1 100644
--- a/compiler/rustc_type_ir/src/search_graph/mod.rs
+++ b/compiler/rustc_type_ir/src/search_graph/mod.rs
@@ -19,13 +19,14 @@ use std::hash::Hash;
 use std::marker::PhantomData;
 
 use derive_where::derive_where;
-use rustc_index::{Idx, IndexVec};
 #[cfg(feature = "nightly")]
 use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext};
 use tracing::debug;
 
 use crate::data_structures::HashMap;
 
+mod stack;
+use stack::{Stack, StackDepth, StackEntry};
 mod global_cache;
 use global_cache::CacheData;
 pub use global_cache::GlobalCache;
@@ -225,9 +226,9 @@ impl AvailableDepth {
     /// in case there is exponential blowup.
     fn allowed_depth_for_nested<D: Delegate>(
         root_depth: AvailableDepth,
-        stack: &IndexVec<StackDepth, StackEntry<D::Cx>>,
+        stack: &Stack<D::Cx>,
     ) -> Option<AvailableDepth> {
-        if let Some(last) = stack.raw.last() {
+        if let Some(last) = stack.last() {
             if last.available_depth.0 == 0 {
                 return None;
             }
@@ -433,50 +434,6 @@ impl<X: Cx> NestedGoals<X> {
     }
 }
 
-rustc_index::newtype_index! {
-    #[orderable]
-    #[gate_rustc_only]
-    pub struct StackDepth {}
-}
-
-/// Stack entries of the evaluation stack. Its fields tend to be lazily
-/// when popping a child goal or completely immutable.
-#[derive_where(Debug; X: Cx)]
-struct StackEntry<X: Cx> {
-    input: X::Input,
-
-    /// Whether proving this goal is a coinductive step.
-    ///
-    /// This is used when encountering a trait solver cycle to
-    /// decide whether the initial provisional result of the cycle.
-    step_kind_from_parent: PathKind,
-
-    /// The available depth of a given goal, immutable.
-    available_depth: AvailableDepth,
-
-    /// The maximum depth reached by this stack entry, only up-to date
-    /// for the top of the stack and lazily updated for the rest.
-    reached_depth: StackDepth,
-
-    /// All cycle heads this goal depends on. Lazily updated and only
-    /// up-to date for the top of the stack.
-    heads: CycleHeads,
-
-    /// Whether evaluating this goal encountered overflow. Lazily updated.
-    encountered_overflow: bool,
-
-    /// Whether this goal has been used as the root of a cycle. This gets
-    /// eagerly updated when encountering a cycle.
-    has_been_used: Option<UsageKind>,
-
-    /// The nested goals of this goal, see the doc comment of the type.
-    nested_goals: NestedGoals<X>,
-
-    /// Starts out as `None` and gets set when rerunning this
-    /// goal in case we encounter a cycle.
-    provisional_result: Option<X::Result>,
-}
-
 /// A provisional result of an already computed goals which depends on other
 /// goals still on the stack.
 #[derive_where(Debug; X: Cx)]
@@ -498,7 +455,7 @@ pub struct SearchGraph<D: Delegate<Cx = X>, X: Cx = <D as Delegate>::Cx> {
     /// The stack of goals currently being computed.
     ///
     /// An element is *deeper* in the stack if its index is *lower*.
-    stack: IndexVec<StackDepth, StackEntry<X>>,
+    stack: Stack<X>,
     /// The provisional cache contains entries for already computed goals which
     /// still depend on goals higher-up in the stack. We don't move them to the
     /// global cache and track them locally instead. A provisional cache entry
@@ -537,16 +494,16 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
     /// and using existing global cache entries to make sure they
     /// have the same impact on the remaining evaluation.
     fn update_parent_goal(
-        stack: &mut IndexVec<StackDepth, StackEntry<X>>,
+        stack: &mut Stack<X>,
         step_kind_from_parent: PathKind,
-        reached_depth: StackDepth,
+        required_depth_for_nested: usize,
         heads: &CycleHeads,
         encountered_overflow: bool,
         context: UpdateParentGoalCtxt<'_, X>,
     ) {
         if let Some(parent_index) = stack.last_index() {
             let parent = &mut stack[parent_index];
-            parent.reached_depth = parent.reached_depth.max(reached_depth);
+            parent.required_depth = parent.required_depth.max(required_depth_for_nested + 1);
             parent.encountered_overflow |= encountered_overflow;
 
             parent.heads.extend_from_child(parent_index, step_kind_from_parent, heads);
@@ -588,13 +545,11 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
     /// the stack which completes the cycle. This given an inductive step AB which then cycles
     /// coinductively with A, we need to treat this cycle as coinductive.
     fn cycle_path_kind(
-        stack: &IndexVec<StackDepth, StackEntry<X>>,
+        stack: &Stack<X>,
         step_kind_to_head: PathKind,
         head: StackDepth,
     ) -> PathKind {
-        stack.raw[head.index() + 1..]
-            .iter()
-            .fold(step_kind_to_head, |curr, entry| curr.extend(entry.step_kind_from_parent))
+        stack.cycle_step_kinds(head).fold(step_kind_to_head, |curr, step| curr.extend(step))
     }
 
     /// Probably the most involved method of the whole solver.
@@ -656,20 +611,18 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
             return result;
         }
 
-        // Unfortunate, it looks like we actually have to compute this goalrar.
-        let depth = self.stack.next_index();
-        let entry = StackEntry {
+        // Unfortunate, it looks like we actually have to compute this goal.
+        self.stack.push(StackEntry {
             input,
             step_kind_from_parent,
             available_depth,
-            reached_depth: depth,
+            required_depth: 0,
             heads: Default::default(),
             encountered_overflow: false,
             has_been_used: None,
             nested_goals: Default::default(),
             provisional_result: None,
-        };
-        assert_eq!(self.stack.push(entry), depth);
+        });
 
         // This is for global caching, so we properly track query dependencies.
         // Everything that affects the `result` should be performed within this
@@ -686,7 +639,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
         Self::update_parent_goal(
             &mut self.stack,
             final_entry.step_kind_from_parent,
-            final_entry.reached_depth,
+            final_entry.required_depth,
             &final_entry.heads,
             final_entry.encountered_overflow,
             UpdateParentGoalCtxt::Ordinary(&final_entry.nested_goals),
@@ -700,7 +653,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
                 // the global cache.
                 assert_eq!(result, expected, "input={input:?}");
             } else if D::inspect_is_noop(inspect) {
-                self.insert_global_cache(cx, input, final_entry, result, dep_node)
+                self.insert_global_cache(cx, final_entry, result, dep_node)
             }
         } else if D::ENABLE_PROVISIONAL_CACHE {
             debug_assert!(validate_cache.is_none(), "unexpected non-root: {input:?}");
@@ -728,7 +681,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
         input: X::Input,
         inspect: &mut D::ProofTreeBuilder,
     ) -> X::Result {
-        if let Some(last) = self.stack.raw.last_mut() {
+        if let Some(last) = self.stack.last_mut() {
             last.encountered_overflow = true;
             // If computing a goal `B` depends on another goal `A` and
             // `A` has a nested goal which overflows, then computing `B`
@@ -859,7 +812,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
                 // apply provisional cache entries which encountered overflow once the
                 // current goal is already part of the same cycle. This check could be
                 // improved but seems to be good enough for now.
-                let last = self.stack.raw.last().unwrap();
+                let last = self.stack.last().unwrap();
                 if last.heads.opt_lowest_cycle_head().is_none_or(|lowest| lowest > head) {
                     continue;
                 }
@@ -868,14 +821,10 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
             // A provisional cache entry is only valid if the current path from its
             // highest cycle head to the goal is the same.
             if path_from_head == Self::cycle_path_kind(&self.stack, step_kind_from_parent, head) {
-                // While we don't have to track the full depth of the provisional cache entry,
-                // we do have to increment the required depth by one as we'd have already failed
-                // with overflow otherwise
-                let next_index = self.stack.next_index();
                 Self::update_parent_goal(
                     &mut self.stack,
                     step_kind_from_parent,
-                    next_index,
+                    0,
                     heads,
                     encountered_overflow,
                     UpdateParentGoalCtxt::ProvisionalCacheHit,
@@ -893,7 +842,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
     /// evaluating this entry would not have ended up depending on either a goal
     /// already on the stack or a provisional cache entry.
     fn candidate_is_applicable(
-        stack: &IndexVec<StackDepth, StackEntry<X>>,
+        stack: &Stack<X>,
         step_kind_from_parent: PathKind,
         provisional_cache: &HashMap<X::Input, Vec<ProvisionalCacheEntry<X>>>,
         nested_goals: &NestedGoals<X>,
@@ -991,7 +940,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
         available_depth: AvailableDepth,
     ) -> Option<X::Result> {
         cx.with_global_cache(|cache| {
-            let CacheData { result, additional_depth, encountered_overflow, nested_goals } = cache
+            let CacheData { result, required_depth, encountered_overflow, nested_goals } = cache
                 .get(cx, input, available_depth, |nested_goals| {
                     Self::candidate_is_applicable(
                         &self.stack,
@@ -1001,23 +950,19 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
                     )
                 })?;
 
-            // Update the reached depth of the current goal to make sure
-            // its state is the same regardless of whether we've used the
-            // global cache or not.
-            let reached_depth = self.stack.next_index().plus(additional_depth);
             // We don't move cycle participants to the global cache, so the
             // cycle heads are always empty.
             let heads = Default::default();
             Self::update_parent_goal(
                 &mut self.stack,
                 step_kind_from_parent,
-                reached_depth,
+                required_depth,
                 &heads,
                 encountered_overflow,
                 UpdateParentGoalCtxt::Ordinary(nested_goals),
             );
 
-            debug!(?additional_depth, "global cache hit");
+            debug!(?required_depth, "global cache hit");
             Some(result)
         })
     }
@@ -1028,7 +973,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
         input: X::Input,
         step_kind_from_parent: PathKind,
     ) -> Option<X::Result> {
-        let (head, _stack_entry) = self.stack.iter_enumerated().find(|(_, e)| e.input == input)?;
+        let head = self.stack.find(input)?;
         // We have a nested goal which directly relies on a goal deeper in the stack.
         //
         // We start by tagging all cycle participants, as that's necessary for caching.
@@ -1043,10 +988,9 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
 
         // Subtle: when encountering a cyclic goal, we still first checked for overflow,
         // so we have to update the reached depth.
-        let next_index = self.stack.next_index();
         let last_index = self.stack.last_index().unwrap();
         let last = &mut self.stack[last_index];
-        last.reached_depth = last.reached_depth.max(next_index);
+        last.required_depth = last.required_depth.max(1);
 
         last.nested_goals.insert(input, step_kind_from_parent.into());
         last.nested_goals.insert(last.input, PathsToNested::EMPTY);
@@ -1095,7 +1039,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
         let mut i = 0;
         loop {
             let result = evaluate_goal(self, inspect);
-            let stack_entry = self.stack.pop().unwrap();
+            let stack_entry = self.stack.pop();
             debug_assert_eq!(stack_entry.input, input);
 
             // If the current goal is not the root of a cycle, we are done.
@@ -1176,20 +1120,18 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
     fn insert_global_cache(
         &mut self,
         cx: X,
-        input: X::Input,
         final_entry: StackEntry<X>,
         result: X::Result,
         dep_node: X::DepNodeIndex,
     ) {
-        let additional_depth = final_entry.reached_depth.as_usize() - self.stack.len();
         debug!(?final_entry, ?result, "insert global cache");
         cx.with_global_cache(|cache| {
             cache.insert(
                 cx,
-                input,
+                final_entry.input,
                 result,
                 dep_node,
-                additional_depth,
+                final_entry.required_depth,
                 final_entry.encountered_overflow,
                 final_entry.nested_goals,
             )
diff --git a/compiler/rustc_type_ir/src/search_graph/stack.rs b/compiler/rustc_type_ir/src/search_graph/stack.rs
new file mode 100644
index 00000000000..8bb247bf055
--- /dev/null
+++ b/compiler/rustc_type_ir/src/search_graph/stack.rs
@@ -0,0 +1,113 @@
+use std::ops::{Index, IndexMut};
+
+use derive_where::derive_where;
+use rustc_index::IndexVec;
+
+use super::{AvailableDepth, Cx, CycleHeads, NestedGoals, PathKind, UsageKind};
+
+rustc_index::newtype_index! {
+    #[orderable]
+    #[gate_rustc_only]
+    pub(super) struct StackDepth {}
+}
+
+/// Stack entries of the evaluation stack. Its fields tend to be lazily
+/// when popping a child goal or completely immutable.
+#[derive_where(Debug; X: Cx)]
+pub(super) struct StackEntry<X: Cx> {
+    pub input: X::Input,
+
+    /// Whether proving this goal is a coinductive step.
+    ///
+    /// This is used when encountering a trait solver cycle to
+    /// decide whether the initial provisional result of the cycle.
+    pub step_kind_from_parent: PathKind,
+
+    /// The available depth of a given goal, immutable.
+    pub available_depth: AvailableDepth,
+
+    /// The maximum depth required while evaluating this goal.
+    pub required_depth: usize,
+
+    /// All cycle heads this goal depends on. Lazily updated and only
+    /// up-to date for the top of the stack.
+    pub heads: CycleHeads,
+
+    /// Whether evaluating this goal encountered overflow. Lazily updated.
+    pub encountered_overflow: bool,
+
+    /// Whether this goal has been used as the root of a cycle. This gets
+    /// eagerly updated when encountering a cycle.
+    pub has_been_used: Option<UsageKind>,
+
+    /// The nested goals of this goal, see the doc comment of the type.
+    pub nested_goals: NestedGoals<X>,
+
+    /// Starts out as `None` and gets set when rerunning this
+    /// goal in case we encounter a cycle.
+    pub provisional_result: Option<X::Result>,
+}
+
+#[derive_where(Default; X: Cx)]
+pub(super) struct Stack<X: Cx> {
+    entries: IndexVec<StackDepth, StackEntry<X>>,
+}
+
+impl<X: Cx> Stack<X> {
+    pub(super) fn is_empty(&self) -> bool {
+        self.entries.is_empty()
+    }
+
+    pub(super) fn len(&self) -> usize {
+        self.entries.len()
+    }
+
+    pub(super) fn last_index(&self) -> Option<StackDepth> {
+        self.entries.last_index()
+    }
+
+    pub(super) fn last(&self) -> Option<&StackEntry<X>> {
+        self.entries.raw.last()
+    }
+
+    pub(super) fn last_mut(&mut self) -> Option<&mut StackEntry<X>> {
+        self.entries.raw.last_mut()
+    }
+
+    pub(super) fn next_index(&self) -> StackDepth {
+        self.entries.next_index()
+    }
+
+    pub(super) fn push(&mut self, entry: StackEntry<X>) -> StackDepth {
+        self.entries.push(entry)
+    }
+
+    pub(super) fn pop(&mut self) -> StackEntry<X> {
+        self.entries.pop().unwrap()
+    }
+
+    pub(super) fn cycle_step_kinds(&self, head: StackDepth) -> impl Iterator<Item = PathKind> {
+        self.entries.raw[head.index() + 1..].iter().map(|entry| entry.step_kind_from_parent)
+    }
+
+    pub(super) fn iter(&self) -> impl Iterator<Item = &StackEntry<X>> {
+        self.entries.iter()
+    }
+
+    pub(super) fn find(&self, input: X::Input) -> Option<StackDepth> {
+        self.entries.iter_enumerated().find(|(_, e)| e.input == input).map(|(idx, _)| idx)
+    }
+}
+
+impl<X: Cx> Index<StackDepth> for Stack<X> {
+    type Output = StackEntry<X>;
+    fn index(&self, index: StackDepth) -> &StackEntry<X> {
+        &self.entries[index]
+    }
+}
+
+impl<X: Cx> IndexMut<StackDepth> for Stack<X> {
+    fn index_mut(&mut self, index: StackDepth) -> &mut Self::Output {
+        &mut self.entries[index]
+    }
+}
diff --git a/compiler/rustc_type_ir/src/ty_kind/closure.rs b/compiler/rustc_type_ir/src/ty_kind/closure.rs
index 8ba985d2d19..d1ca9bdb7fb 100644
--- a/compiler/rustc_type_ir/src/ty_kind/closure.rs
+++ b/compiler/rustc_type_ir/src/ty_kind/closure.rs
@@ -342,7 +342,7 @@ struct HasRegionsBoundAt {
 // FIXME: Could be optimized to not walk into components with no escaping bound vars.
 impl<I: Interner> TypeVisitor<I> for HasRegionsBoundAt {
     type Result = ControlFlow<()>;
-    fn visit_binder<T: TypeFoldable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result {
+    fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result {
         self.binder.shift_in(1);
         t.super_visit_with(self)?;
         self.binder.shift_out(1);
diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs
index fc3864dd5ae..a96ac97f785 100644
--- a/compiler/rustc_type_ir/src/visit.rs
+++ b/compiler/rustc_type_ir/src/visit.rs
@@ -52,7 +52,7 @@ use smallvec::SmallVec;
 use thin_vec::ThinVec;
 
 use crate::inherent::*;
-use crate::{self as ty, Interner, TypeFlags, TypeFoldable};
+use crate::{self as ty, Interner, TypeFlags};
 
 /// This trait is implemented for every type that can be visited,
 /// providing the skeleton of the traversal.
@@ -94,7 +94,7 @@ pub trait TypeVisitor<I: Interner>: Sized {
     #[cfg(not(feature = "nightly"))]
     type Result: VisitorResult;
 
-    fn visit_binder<T: TypeFoldable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result {
+    fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result {
         t.super_visit_with(self)
     }
 
@@ -401,7 +401,7 @@ impl std::fmt::Debug for HasTypeFlagsVisitor {
 impl<I: Interner> TypeVisitor<I> for HasTypeFlagsVisitor {
     type Result = ControlFlow<FoundFlags>;
 
-    fn visit_binder<T: TypeFoldable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result {
+    fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result {
         // If we're looking for the HAS_BINDER_VARS flag, check if the
         // binder has vars. This won't be present in the binder's bound
         // value, so we need to check here too.
@@ -510,7 +510,7 @@ struct HasEscapingVarsVisitor {
 impl<I: Interner> TypeVisitor<I> for HasEscapingVarsVisitor {
     type Result = ControlFlow<FoundEscapingVars>;
 
-    fn visit_binder<T: TypeFoldable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result {
+    fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result {
         self.outer_index.shift_in(1);
         let result = t.super_visit_with(self);
         self.outer_index.shift_out(1);
diff --git a/library/Cargo.lock b/library/Cargo.lock
index 862d0938d37..1bd97e7b527 100644
--- a/library/Cargo.lock
+++ b/library/Cargo.lock
@@ -4,11 +4,10 @@ version = 4
 
 [[package]]
 name = "addr2line"
-version = "0.24.2"
+version = "0.25.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
+checksum = "9acbfca36652500c911ddb767ed433e3ed99b032b5d935be73c6923662db1d43"
 dependencies = [
- "compiler_builtins",
  "gimli",
  "rustc-std-workspace-alloc",
  "rustc-std-workspace-core",
@@ -16,11 +15,10 @@ dependencies = [
 
 [[package]]
 name = "adler2"
-version = "2.0.0"
+version = "2.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
+checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
 dependencies = [
- "compiler_builtins",
  "rustc-std-workspace-core",
 ]
 
@@ -51,11 +49,10 @@ dependencies = [
 
 [[package]]
 name = "cfg-if"
-version = "1.0.0"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
 dependencies = [
- "compiler_builtins",
  "rustc-std-workspace-core",
 ]
 
@@ -81,12 +78,11 @@ dependencies = [
 
 [[package]]
 name = "dlmalloc"
-version = "0.2.8"
+version = "0.2.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8cff88b751e7a276c4ab0e222c3f355190adc6dde9ce39c851db39da34990df7"
+checksum = "d01597dde41c0b9da50d5f8c219023d63d8f27f39a27095070fd191fddc83891"
 dependencies = [
  "cfg-if",
- "compiler_builtins",
  "libc",
  "rustc-std-workspace-core",
  "windows-sys",
@@ -104,9 +100,9 @@ dependencies = [
 
 [[package]]
 name = "getopts"
-version = "0.2.21"
+version = "0.2.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
+checksum = "cba6ae63eb948698e300f645f87c70f76630d505f23b8907cf1e193ee85048c1"
 dependencies = [
  "rustc-std-workspace-core",
  "rustc-std-workspace-std",
@@ -115,11 +111,10 @@ dependencies = [
 
 [[package]]
 name = "gimli"
-version = "0.31.1"
+version = "0.32.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
+checksum = "93563d740bc9ef04104f9ed6f86f1e3275c2cdafb95664e26584b9ca807a8ffe"
 dependencies = [
- "compiler_builtins",
  "rustc-std-workspace-alloc",
  "rustc-std-workspace-core",
 ]
@@ -136,11 +131,10 @@ dependencies = [
 
 [[package]]
 name = "hermit-abi"
-version = "0.5.1"
+version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08"
+checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
 dependencies = [
- "compiler_builtins",
  "rustc-std-workspace-alloc",
  "rustc-std-workspace-core",
 ]
@@ -156,33 +150,30 @@ dependencies = [
 
 [[package]]
 name = "memchr"
-version = "2.7.4"
+version = "2.7.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
 dependencies = [
- "compiler_builtins",
  "rustc-std-workspace-core",
 ]
 
 [[package]]
 name = "miniz_oxide"
-version = "0.8.8"
+version = "0.8.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
+checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
 dependencies = [
  "adler2",
- "compiler_builtins",
  "rustc-std-workspace-alloc",
  "rustc-std-workspace-core",
 ]
 
 [[package]]
 name = "object"
-version = "0.36.7"
+version = "0.37.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
+checksum = "03fd943161069e1768b4b3d050890ba48730e590f57e56d4aa04e7e090e61b4a"
 dependencies = [
- "compiler_builtins",
  "memchr",
  "rustc-std-workspace-alloc",
  "rustc-std-workspace-core",
@@ -273,11 +264,10 @@ dependencies = [
 
 [[package]]
 name = "rustc-demangle"
-version = "0.1.24"
+version = "0.1.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
+checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f"
 dependencies = [
- "compiler_builtins",
  "rustc-std-workspace-core",
 ]
 
@@ -352,7 +342,6 @@ name = "std_detect"
 version = "0.1.5"
 dependencies = [
  "cfg-if",
- "compiler_builtins",
  "libc",
  "rustc-std-workspace-alloc",
  "rustc-std-workspace-core",
@@ -380,11 +369,10 @@ dependencies = [
 
 [[package]]
 name = "unicode-width"
-version = "0.1.14"
+version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
+checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c"
 dependencies = [
- "compiler_builtins",
  "rustc-std-workspace-core",
  "rustc-std-workspace-std",
 ]
@@ -402,9 +390,9 @@ dependencies = [
 
 [[package]]
 name = "unwinding"
-version = "0.2.6"
+version = "0.2.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8393f2782b6060a807337ff353780c1ca15206f9ba2424df18cb6e733bd7b345"
+checksum = "7d80f6c2bfede213d9a90b4a14f3eb99b84e33c52df6c1a15de0a100f5a88751"
 dependencies = [
  "compiler_builtins",
  "gimli",
@@ -413,11 +401,10 @@ dependencies = [
 
 [[package]]
 name = "wasi"
-version = "0.11.0+wasi-snapshot-preview1"
+version = "0.11.1+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
 dependencies = [
- "compiler_builtins",
  "rustc-std-workspace-alloc",
  "rustc-std-workspace-core",
 ]
diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs
index 51418036f42..aa9e5fce1d4 100644
--- a/library/alloc/src/collections/btree/set.rs
+++ b/library/alloc/src/collections/btree/set.rs
@@ -1220,11 +1220,11 @@ impl<T, A: Allocator + Clone> BTreeSet<T, A> {
     /// assert_eq!(high.into_iter().collect::<Vec<_>>(), [4, 5, 6, 7]);
     /// ```
     #[unstable(feature = "btree_extract_if", issue = "70530")]
-    pub fn extract_if<'a, F, R>(&'a mut self, range: R, pred: F) -> ExtractIf<'a, T, R, F, A>
+    pub fn extract_if<F, R>(&mut self, range: R, pred: F) -> ExtractIf<'_, T, R, F, A>
     where
         T: Ord,
         R: RangeBounds<T>,
-        F: 'a + FnMut(&T) -> bool,
+        F: FnMut(&T) -> bool,
     {
         let (inner, alloc) = self.map.extract_if_inner(range);
         ExtractIf { pred, inner, alloc }
@@ -1585,11 +1585,11 @@ where
 }
 
 #[unstable(feature = "btree_extract_if", issue = "70530")]
-impl<'a, T, R, F, A: Allocator + Clone> Iterator for ExtractIf<'_, T, R, F, A>
+impl<T, R, F, A: Allocator + Clone> Iterator for ExtractIf<'_, T, R, F, A>
 where
     T: PartialOrd,
     R: RangeBounds<T>,
-    F: 'a + FnMut(&T) -> bool,
+    F: FnMut(&T) -> bool,
 {
     type Item = T;
 
diff --git a/library/alloc/src/ffi/c_str.rs b/library/alloc/src/ffi/c_str.rs
index 8b448a18402..48849bf7536 100644
--- a/library/alloc/src/ffi/c_str.rs
+++ b/library/alloc/src/ffi/c_str.rs
@@ -714,6 +714,8 @@ impl ops::Deref for CString {
     }
 }
 
+/// Delegates to the [`CStr`] implementation of [`fmt::Debug`],
+/// showing invalid UTF-8 as hex escapes.
 #[stable(feature = "rust1", since = "1.0.0")]
 impl fmt::Debug for CString {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs
index ce7321544b6..5bd82560da7 100644
--- a/library/alloc/src/vec/mod.rs
+++ b/library/alloc/src/vec/mod.rs
@@ -109,6 +109,11 @@ mod in_place_collect;
 
 mod partial_eq;
 
+#[unstable(feature = "vec_peek_mut", issue = "122742")]
+pub use self::peek_mut::PeekMut;
+
+mod peek_mut;
+
 #[cfg(not(no_global_oom_handling))]
 use self::spec_from_elem::SpecFromElem;
 
@@ -729,6 +734,33 @@ impl<T> Vec<T> {
     pub unsafe fn from_parts(ptr: NonNull<T>, length: usize, capacity: usize) -> Self {
         unsafe { Self::from_parts_in(ptr, length, capacity, Global) }
     }
+
+    /// Returns a mutable reference to the last item in the vector, or
+    /// `None` if it is empty.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// #![feature(vec_peek_mut)]
+    /// let mut vec = Vec::new();
+    /// assert!(vec.peek_mut().is_none());
+    ///
+    /// vec.push(1);
+    /// vec.push(5);
+    /// vec.push(2);
+    /// assert_eq!(vec.last(), Some(&2));
+    /// if let Some(mut val) = vec.peek_mut() {
+    ///     *val = 0;
+    /// }
+    /// assert_eq!(vec.last(), Some(&0));
+    /// ```
+    #[inline]
+    #[unstable(feature = "vec_peek_mut", issue = "122742")]
+    pub fn peek_mut(&mut self) -> Option<PeekMut<'_, T>> {
+        PeekMut::new(self)
+    }
 }
 
 impl<T, A: Allocator> Vec<T, A> {
diff --git a/library/alloc/src/vec/peek_mut.rs b/library/alloc/src/vec/peek_mut.rs
new file mode 100644
index 00000000000..c0dd941ed39
--- /dev/null
+++ b/library/alloc/src/vec/peek_mut.rs
@@ -0,0 +1,55 @@
+use core::ops::{Deref, DerefMut};
+
+use super::Vec;
+use crate::fmt;
+
+/// Structure wrapping a mutable reference to the last item in a
+/// `Vec`.
+///
+/// This `struct` is created by the [`peek_mut`] method on [`Vec`]. See
+/// its documentation for more.
+///
+/// [`peek_mut`]: Vec::peek_mut
+#[unstable(feature = "vec_peek_mut", issue = "122742")]
+pub struct PeekMut<'a, T> {
+    vec: &'a mut Vec<T>,
+}
+
+#[unstable(feature = "vec_peek_mut", issue = "122742")]
+impl<T: fmt::Debug> fmt::Debug for PeekMut<'_, T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_tuple("PeekMut").field(self.deref()).finish()
+    }
+}
+
+impl<'a, T> PeekMut<'a, T> {
+    pub(crate) fn new(vec: &'a mut Vec<T>) -> Option<Self> {
+        if vec.is_empty() { None } else { Some(Self { vec }) }
+    }
+
+    /// Removes the peeked value from the vector and returns it.
+    #[unstable(feature = "vec_peek_mut", issue = "122742")]
+    pub fn pop(self) -> T {
+        // SAFETY: PeekMut is only constructed if the vec is non-empty
+        unsafe { self.vec.pop().unwrap_unchecked() }
+    }
+}
+
+#[unstable(feature = "vec_peek_mut", issue = "122742")]
+impl<'a, T> Deref for PeekMut<'a, T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        // SAFETY: PeekMut is only constructed if the vec is non-empty
+        unsafe { self.vec.get_unchecked(self.vec.len() - 1) }
+    }
+}
+
+#[unstable(feature = "vec_peek_mut", issue = "122742")]
+impl<'a, T> DerefMut for PeekMut<'a, T> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        let idx = self.vec.len() - 1;
+        // SAFETY: PeekMut is only constructed if the vec is non-empty
+        unsafe { self.vec.get_unchecked_mut(idx) }
+    }
+}
diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs
index 38309585fad..a41162ecd51 100644
--- a/library/alloctests/tests/lib.rs
+++ b/library/alloctests/tests/lib.rs
@@ -40,6 +40,7 @@
 #![feature(vec_deque_truncate_front)]
 #![feature(unique_rc_arc)]
 #![feature(macro_metavar_expr_concat)]
+#![feature(vec_peek_mut)]
 #![allow(internal_features)]
 #![deny(fuzzy_provenance_casts)]
 #![deny(unsafe_op_in_unsafe_fn)]
diff --git a/library/alloctests/tests/slice.rs b/library/alloctests/tests/slice.rs
index 2516563187f..1e15d54d979 100644
--- a/library/alloctests/tests/slice.rs
+++ b/library/alloctests/tests/slice.rs
@@ -1636,6 +1636,19 @@ fn test_chunk_by() {
     assert_eq!(iter.next_back(), Some(&[1][..]));
     assert_eq!(iter.next(), Some(&[2, 2, 2][..]));
     assert_eq!(iter.next_back(), None);
+
+    let mut iter = slice.chunk_by(|a, b| a == b);
+    assert_eq!(iter.next(), Some(&[1, 1, 1][..]));
+    assert_eq!(iter.next(), Some(&[3, 3][..]));
+    let mut iter_clone = iter.clone();
+    assert_eq!(iter.next(), Some(&[2, 2, 2][..]));
+    assert_eq!(iter.next(), Some(&[1][..]));
+    assert_eq!(iter.next(), Some(&[0][..]));
+    assert_eq!(iter.next(), None);
+    assert_eq!(iter_clone.next(), Some(&[2, 2, 2][..]));
+    assert_eq!(iter_clone.next(), Some(&[1][..]));
+    assert_eq!(iter_clone.next(), Some(&[0][..]));
+    assert_eq!(iter_clone.next(), None);
 }
 
 #[test]
diff --git a/library/alloctests/tests/vec.rs b/library/alloctests/tests/vec.rs
index f430d979fa8..51b49b8edb3 100644
--- a/library/alloctests/tests/vec.rs
+++ b/library/alloctests/tests/vec.rs
@@ -2698,6 +2698,23 @@ fn test_pop_if_mutates() {
     assert_eq!(v, [2]);
 }
 
+#[test]
+fn test_peek_mut() {
+    let mut vec = Vec::new();
+    assert!(vec.peek_mut().is_none());
+    vec.push(1);
+    vec.push(2);
+    if let Some(mut p) = vec.peek_mut() {
+        assert_eq!(*p, 2);
+        *p = 0;
+        assert_eq!(*p, 0);
+        p.pop();
+        assert_eq!(vec.len(), 1);
+    } else {
+        unreachable!()
+    }
+}
+
 /// This assortment of tests, in combination with miri, verifies we handle UB on fishy arguments
 /// in the stdlib. Draining and extending the allocation are fairly well-tested earlier, but
 /// `vec.insert(usize::MAX, val)` once slipped by!
diff --git a/library/compiler-builtins/.release-plz.toml b/library/compiler-builtins/.release-plz.toml
deleted file mode 100644
index 8023ade9bfd..00000000000
--- a/library/compiler-builtins/.release-plz.toml
+++ /dev/null
@@ -1,13 +0,0 @@
-[workspace]
-# As part of the release process, we delete `libm/Cargo.toml`. Since
-# this is only run in CI, we shouldn't need to worry about it.
-allow_dirty = true
-publish_allow_dirty = true
-
-[[package]]
-name = "compiler_builtins"
-semver_check = false
-changelog_include = ["libm"] # libm is included as part of builtins
-
-[[package]]
-name = "libm"
diff --git a/library/compiler-builtins/Cargo.toml b/library/compiler-builtins/Cargo.toml
index fb638f2fb37..41350c6cb99 100644
--- a/library/compiler-builtins/Cargo.toml
+++ b/library/compiler-builtins/Cargo.toml
@@ -1,8 +1,8 @@
 [workspace]
 resolver = "2"
 members = [
+    "builtins-shim",
     "builtins-test",
-    "compiler-builtins",
     "crates/josh-sync",
     "crates/libm-macros",
     "crates/musl-math-sys",
@@ -14,8 +14,8 @@ members = [
 ]
 
 default-members = [
+    "builtins-shim",
     "builtins-test",
-    "compiler-builtins",
     "crates/libm-macros",
     "libm",
     "libm-test",
@@ -26,6 +26,10 @@ exclude = [
     # and `mangled-names` disabled, which is the opposite of what is needed for
     # other tests, so it makes sense to keep it out of the workspace.
     "builtins-test-intrinsics",
+    # We test via the `builtins-shim` crate, so exclude the `compiler-builtins`
+    # that has a dependency on `core`. See `builtins-shim/Cargo.toml` for more
+    # details.
+    "compiler-builtins",
 ]
 
 [profile.release]
diff --git a/library/compiler-builtins/builtins-shim/Cargo.toml b/library/compiler-builtins/builtins-shim/Cargo.toml
new file mode 100644
index 00000000000..8eb880c6fd1
--- /dev/null
+++ b/library/compiler-builtins/builtins-shim/Cargo.toml
@@ -0,0 +1,63 @@
+# NOTE: Must be kept in sync with `../compiler-builtins/Cargo.toml`.
+#
+# The manifest at `../compiler-builtins` is what actually gets used in the
+# rust-lang/rust tree; however, we can't build it out of tree because it
+# depends on `core` by path, and even optional Cargo dependencies need to be
+# available at build time. So, we work around this by having this "shim"
+# manifest that is identical except for the `core` dependency and forwards
+# to the same sources, which acts as the `compiler-builtins` Cargo entrypoint
+# for out of tree testing
+
+[package]
+name = "compiler_builtins"
+version = "0.1.160"
+authors = ["Jorge Aparicio <japaricious@gmail.com>"]
+description = "Compiler intrinsics used by the Rust compiler."
+repository = "https://github.com/rust-lang/compiler-builtins"
+license = "MIT AND Apache-2.0 WITH LLVM-exception AND (MIT OR Apache-2.0)"
+edition = "2024"
+publish = false
+links = "compiler-rt"
+
+build = "../compiler-builtins/build.rs"
+
+[lib]
+path = "../compiler-builtins/src/lib.rs"
+bench = false
+doctest = false
+test = false
+
+[build-dependencies]
+cc = { optional = true, version = "1.2" }
+
+[features]
+default = ["compiler-builtins"]
+
+# Enable compilation of C code in compiler-rt, filling in some more optimized
+# implementations and also filling in unimplemented intrinsics
+c = ["dep:cc"]
+
+# Workaround for the Cranelift codegen backend. Disables any implementations
+# which use inline assembly and fall back to pure Rust versions (if available).
+no-asm = []
+
+# Workaround for codegen backends which haven't yet implemented `f16` and
+# `f128` support. Disabled any intrinsics which use those types.
+no-f16-f128 = []
+
+# Flag this library as the unstable compiler-builtins lib
+compiler-builtins = []
+
+# Generate memory-related intrinsics like memcpy
+mem = []
+
+# Mangle all names so this can be linked in with other versions or other
+# compiler-rt implementations. Also used for testing
+mangled-names = []
+
+# Only used in the compiler's build system
+rustc-dep-of-std = ["compiler-builtins"]
+
+# This makes certain traits and function specializations public that
+# are not normally public but are required by the `builtins-test`
+unstable-public-internals = []
diff --git a/library/compiler-builtins/builtins-test-intrinsics/Cargo.toml b/library/compiler-builtins/builtins-test-intrinsics/Cargo.toml
index 064b7cad2f6..e73a1f7b17e 100644
--- a/library/compiler-builtins/builtins-test-intrinsics/Cargo.toml
+++ b/library/compiler-builtins/builtins-test-intrinsics/Cargo.toml
@@ -6,7 +6,7 @@ publish = false
 license = "MIT OR Apache-2.0"
 
 [dependencies]
-compiler_builtins = { path = "../compiler-builtins", features = ["compiler-builtins"] }
+compiler_builtins = { path = "../builtins-shim", features = ["compiler-builtins"] }
 panic-handler = { path = "../crates/panic-handler" }
 
 [features]
diff --git a/library/compiler-builtins/builtins-test/Cargo.toml b/library/compiler-builtins/builtins-test/Cargo.toml
index c7742aa2427..093d4633f87 100644
--- a/library/compiler-builtins/builtins-test/Cargo.toml
+++ b/library/compiler-builtins/builtins-test/Cargo.toml
@@ -17,7 +17,7 @@ rustc_apfloat = "0.2.2"
 iai-callgrind = { version = "0.14.1", optional = true }
 
 [dependencies.compiler_builtins]
-path = "../compiler-builtins"
+path = "../builtins-shim"
 default-features = false
 features = ["unstable-public-internals"]
 
diff --git a/library/compiler-builtins/builtins-test/tests/lse.rs b/library/compiler-builtins/builtins-test/tests/lse.rs
index 53167d98fc0..0d85228d7a2 100644
--- a/library/compiler-builtins/builtins-test/tests/lse.rs
+++ b/library/compiler-builtins/builtins-test/tests/lse.rs
@@ -1,4 +1,5 @@
 #![feature(decl_macro)] // so we can use pub(super)
+#![feature(macro_metavar_expr_concat)]
 #![cfg(all(target_arch = "aarch64", target_os = "linux", not(feature = "no-asm")))]
 
 /// Translate a byte size to a Rust type.
@@ -87,7 +88,7 @@ test_op!(add, |left, right| left.wrapping_add(right));
 test_op!(clr, |left, right| left & !right);
 test_op!(xor, std::ops::BitXor::bitxor);
 test_op!(or, std::ops::BitOr::bitor);
-
+use compiler_builtins::{foreach_bytes, foreach_ordering};
 compiler_builtins::foreach_cas!(cas::test);
 compiler_builtins::foreach_cas16!(test_cas16);
 compiler_builtins::foreach_swp!(swap::test);
diff --git a/library/compiler-builtins/ci/bench-icount.sh b/library/compiler-builtins/ci/bench-icount.sh
index 5724955fe36..d2baebb52d8 100755
--- a/library/compiler-builtins/ci/bench-icount.sh
+++ b/library/compiler-builtins/ci/bench-icount.sh
@@ -57,7 +57,7 @@ function run_icount_benchmarks() {
         # Disregard regressions after merge
         echo "Benchmarks completed with regressions; ignoring (not in a PR)"
     else
-        ./ci/ci-util.py handle-banch-regressions "$PR_NUMBER"
+        ./ci/ci-util.py handle-bench-regressions "$PR_NUMBER"
     fi
 }
 
diff --git a/library/compiler-builtins/compiler-builtins/Cargo.toml b/library/compiler-builtins/compiler-builtins/Cargo.toml
index df8e964825b..c5446cd76e3 100644
--- a/library/compiler-builtins/compiler-builtins/Cargo.toml
+++ b/library/compiler-builtins/compiler-builtins/Cargo.toml
@@ -1,14 +1,18 @@
+# NOTE: Must be kept in sync with `../builtins-shim/Cargo.toml`.
+#
+# This manifest is actually used in-tree by rust-lang/rust,
+# `../builtins-shim/Cargo.toml` is used by out-of-tree testing. See the other
+# manifest for further details.
+
 [package]
-authors = ["Jorge Aparicio <japaricious@gmail.com>"]
 name = "compiler_builtins"
 version = "0.1.160"
-license = "MIT AND Apache-2.0 WITH LLVM-exception AND (MIT OR Apache-2.0)"
-readme = "README.md"
+authors = ["Jorge Aparicio <japaricious@gmail.com>"]
+description = "Compiler intrinsics used by the Rust compiler."
 repository = "https://github.com/rust-lang/compiler-builtins"
-homepage = "https://github.com/rust-lang/compiler-builtins"
-documentation = "https://docs.rs/compiler_builtins"
+license = "MIT AND Apache-2.0 WITH LLVM-exception AND (MIT OR Apache-2.0)"
 edition = "2024"
-description = "Compiler intrinsics used by the Rust compiler."
+publish = false
 links = "compiler-rt"
 
 [lib]
@@ -53,7 +57,3 @@ rustc-dep-of-std = ["compiler-builtins", "dep:core"]
 # This makes certain traits and function specializations public that
 # are not normally public but are required by the `builtins-test`
 unstable-public-internals = []
-
-[lints.rust]
-# The cygwin config can be dropped after our benchmark toolchain is bumped
-unexpected_cfgs = { level = "warn", check-cfg = ['cfg(bootstrap)', 'cfg(target_os, values("cygwin"))'] }
diff --git a/library/compiler-builtins/compiler-builtins/build.rs b/library/compiler-builtins/compiler-builtins/build.rs
index 7c8da02fd28..018899faf1d 100644
--- a/library/compiler-builtins/compiler-builtins/build.rs
+++ b/library/compiler-builtins/compiler-builtins/build.rs
@@ -1,9 +1,6 @@
 mod configure;
 
-use std::collections::BTreeMap;
 use std::env;
-use std::path::PathBuf;
-use std::sync::atomic::Ordering;
 
 use configure::{Target, configure_aliases, configure_f16_f128};
 
@@ -86,10 +83,6 @@ fn main() {
     {
         println!("cargo:rustc-cfg=kernel_user_helpers")
     }
-
-    if llvm_target[0].starts_with("aarch64") {
-        generate_aarch64_outlined_atomics();
-    }
 }
 
 /// Run configuration for `libm` since it is included directly.
@@ -132,61 +125,6 @@ fn configure_libm(target: &Target) {
     println!("cargo:rustc-cfg=feature=\"unstable-intrinsics\"");
 }
 
-fn aarch64_symbol(ordering: Ordering) -> &'static str {
-    match ordering {
-        Ordering::Relaxed => "relax",
-        Ordering::Acquire => "acq",
-        Ordering::Release => "rel",
-        Ordering::AcqRel => "acq_rel",
-        _ => panic!("unknown symbol for {ordering:?}"),
-    }
-}
-
-/// The `concat_idents` macro is extremely annoying and doesn't allow us to define new items.
-/// Define them from the build script instead.
-/// Note that the majority of the code is still defined in `aarch64.rs` through inline macros.
-fn generate_aarch64_outlined_atomics() {
-    use std::fmt::Write;
-    // #[macro_export] so that we can use this in tests
-    let gen_macro =
-        |name| format!("#[macro_export] macro_rules! foreach_{name} {{ ($macro:path) => {{\n");
-
-    // Generate different macros for add/clr/eor/set so that we can test them separately.
-    let sym_names = ["cas", "ldadd", "ldclr", "ldeor", "ldset", "swp"];
-    let mut macros = BTreeMap::new();
-    for sym in sym_names {
-        macros.insert(sym, gen_macro(sym));
-    }
-
-    // Only CAS supports 16 bytes, and it has a different implementation that uses a different macro.
-    let mut cas16 = gen_macro("cas16");
-
-    for ordering in [
-        Ordering::Relaxed,
-        Ordering::Acquire,
-        Ordering::Release,
-        Ordering::AcqRel,
-    ] {
-        let sym_ordering = aarch64_symbol(ordering);
-        for size in [1, 2, 4, 8] {
-            for (sym, macro_) in &mut macros {
-                let name = format!("__aarch64_{sym}{size}_{sym_ordering}");
-                writeln!(macro_, "$macro!( {ordering:?}, {size}, {name} );").unwrap();
-            }
-        }
-        let name = format!("__aarch64_cas16_{sym_ordering}");
-        writeln!(cas16, "$macro!( {ordering:?}, {name} );").unwrap();
-    }
-
-    let mut buf = String::new();
-    for macro_def in macros.values().chain(std::iter::once(&cas16)) {
-        buf += macro_def;
-        buf += "}; }\n";
-    }
-    let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap());
-    std::fs::write(out_dir.join("outlined_atomics.rs"), buf).unwrap();
-}
-
 /// Emit directives for features we expect to support that aren't in `Cargo.toml`.
 ///
 /// These are mostly cfg elements emitted by this `build.rs`.
diff --git a/library/compiler-builtins/compiler-builtins/src/aarch64_linux.rs b/library/compiler-builtins/compiler-builtins/src/aarch64_linux.rs
index 226121237e8..38fcab152ae 100644
--- a/library/compiler-builtins/compiler-builtins/src/aarch64_linux.rs
+++ b/library/compiler-builtins/compiler-builtins/src/aarch64_linux.rs
@@ -262,8 +262,78 @@ macro_rules! or {
     };
 }
 
-// See `generate_aarch64_outlined_atomics` in build.rs.
-include!(concat!(env!("OUT_DIR"), "/outlined_atomics.rs"));
+#[macro_export]
+macro_rules! foreach_ordering {
+    ($macro:path, $bytes:tt, $name:ident) => {
+        $macro!( Relaxed, $bytes, ${concat($name, _relax)} );
+        $macro!( Acquire, $bytes, ${concat($name, _acq)} );
+        $macro!( Release, $bytes, ${concat($name, _rel)} );
+        $macro!( AcqRel, $bytes, ${concat($name, _acq_rel)} );
+    };
+    ($macro:path, $name:ident) => {
+        $macro!( Relaxed, ${concat($name, _relax)} );
+        $macro!( Acquire, ${concat($name, _acq)} );
+        $macro!( Release, ${concat($name, _rel)} );
+        $macro!( AcqRel, ${concat($name, _acq_rel)} );
+    };
+}
+
+#[macro_export]
+macro_rules! foreach_bytes {
+    ($macro:path, $name:ident) => {
+        foreach_ordering!( $macro, 1, ${concat(__aarch64_, $name, "1")} );
+        foreach_ordering!( $macro, 2, ${concat(__aarch64_, $name, "2")} );
+        foreach_ordering!( $macro, 4, ${concat(__aarch64_, $name, "4")} );
+        foreach_ordering!( $macro, 8, ${concat(__aarch64_, $name, "8")} );
+    };
+}
+
+/// Generate different macros for cas/swp/add/clr/eor/set so that we can test them separately.
+#[macro_export]
+macro_rules! foreach_cas {
+    ($macro:path) => {
+        foreach_bytes!($macro, cas);
+    };
+}
+
+/// Only CAS supports 16 bytes, and it has a different implementation that uses a different macro.
+#[macro_export]
+macro_rules! foreach_cas16 {
+    ($macro:path) => {
+        foreach_ordering!($macro, __aarch64_cas16);
+    };
+}
+#[macro_export]
+macro_rules! foreach_swp {
+    ($macro:path) => {
+        foreach_bytes!($macro, swp);
+    };
+}
+#[macro_export]
+macro_rules! foreach_ldadd {
+    ($macro:path) => {
+        foreach_bytes!($macro, ldadd);
+    };
+}
+#[macro_export]
+macro_rules! foreach_ldclr {
+    ($macro:path) => {
+        foreach_bytes!($macro, ldclr);
+    };
+}
+#[macro_export]
+macro_rules! foreach_ldeor {
+    ($macro:path) => {
+        foreach_bytes!($macro, ldeor);
+    };
+}
+#[macro_export]
+macro_rules! foreach_ldset {
+    ($macro:path) => {
+        foreach_bytes!($macro, ldset);
+    };
+}
+
 foreach_cas!(compare_and_swap);
 foreach_cas16!(compare_and_swap_i128);
 foreach_swp!(swap);
diff --git a/library/compiler-builtins/compiler-builtins/src/lib.rs b/library/compiler-builtins/compiler-builtins/src/lib.rs
index 6549d4cef9e..1cec39d8b41 100644
--- a/library/compiler-builtins/compiler-builtins/src/lib.rs
+++ b/library/compiler-builtins/compiler-builtins/src/lib.rs
@@ -8,6 +8,7 @@
 #![feature(linkage)]
 #![feature(naked_functions)]
 #![feature(repr_simd)]
+#![feature(macro_metavar_expr_concat)]
 #![feature(rustc_attrs)]
 #![cfg_attr(f16_enabled, feature(f16))]
 #![cfg_attr(f128_enabled, feature(f128))]
diff --git a/library/compiler-builtins/libm/Cargo.toml b/library/compiler-builtins/libm/Cargo.toml
index b6fb5efcf76..63b4d3c2779 100644
--- a/library/compiler-builtins/libm/Cargo.toml
+++ b/library/compiler-builtins/libm/Cargo.toml
@@ -1,14 +1,12 @@
 [package]
+name = "libm"
+version = "0.2.15"
 authors = ["Jorge Aparicio <jorge@japaric.io>"]
-categories = ["no-std"]
 description = "libm in pure Rust"
-documentation = "https://docs.rs/libm"
+categories = ["no-std"]
 keywords = ["libm", "math"]
-license = "MIT"
-name = "libm"
-readme = "README.md"
 repository = "https://github.com/rust-lang/compiler-builtins"
-version = "0.2.15"
+license = "MIT"
 edition = "2021"
 rust-version = "1.63"
 
diff --git a/library/compiler-builtins/libm/src/math/fmin_fmax.rs b/library/compiler-builtins/libm/src/math/fmin_fmax.rs
index 2947b783e2f..481301994e9 100644
--- a/library/compiler-builtins/libm/src/math/fmin_fmax.rs
+++ b/library/compiler-builtins/libm/src/math/fmin_fmax.rs
@@ -82,22 +82,77 @@ mod tests {
     fn fmin_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
         let cases = [
             (F::ZERO, F::ZERO, F::ZERO),
-            (F::ONE, F::ONE, F::ONE),
             (F::ZERO, F::ONE, F::ZERO),
-            (F::ONE, F::ZERO, F::ZERO),
             (F::ZERO, F::NEG_ONE, F::NEG_ONE),
+            (F::ZERO, F::INFINITY, F::ZERO),
+            (F::ZERO, F::NEG_INFINITY, F::NEG_INFINITY),
+            (F::ZERO, F::NAN, F::ZERO),
+            (F::ZERO, F::NEG_NAN, F::ZERO),
+            (F::NEG_ZERO, F::NEG_ZERO, F::NEG_ZERO),
+            (F::NEG_ZERO, F::ONE, F::NEG_ZERO),
+            (F::NEG_ZERO, F::NEG_ONE, F::NEG_ONE),
+            (F::NEG_ZERO, F::INFINITY, F::NEG_ZERO),
+            (F::NEG_ZERO, F::NEG_INFINITY, F::NEG_INFINITY),
+            (F::NEG_ZERO, F::NAN, F::NEG_ZERO),
+            (F::NEG_ZERO, F::NEG_NAN, F::NEG_ZERO),
+            (F::ONE, F::ZERO, F::ZERO),
+            (F::ONE, F::NEG_ZERO, F::NEG_ZERO),
+            (F::ONE, F::ONE, F::ONE),
+            (F::ONE, F::NEG_ONE, F::NEG_ONE),
+            (F::ONE, F::INFINITY, F::ONE),
+            (F::ONE, F::NEG_INFINITY, F::NEG_INFINITY),
+            (F::ONE, F::NAN, F::ONE),
+            (F::ONE, F::NEG_NAN, F::ONE),
             (F::NEG_ONE, F::ZERO, F::NEG_ONE),
+            (F::NEG_ONE, F::NEG_ZERO, F::NEG_ONE),
+            (F::NEG_ONE, F::ONE, F::NEG_ONE),
+            (F::NEG_ONE, F::NEG_ONE, F::NEG_ONE),
+            (F::NEG_ONE, F::INFINITY, F::NEG_ONE),
+            (F::NEG_ONE, F::NEG_INFINITY, F::NEG_INFINITY),
+            (F::NEG_ONE, F::NAN, F::NEG_ONE),
+            (F::NEG_ONE, F::NEG_NAN, F::NEG_ONE),
             (F::INFINITY, F::ZERO, F::ZERO),
+            (F::INFINITY, F::NEG_ZERO, F::NEG_ZERO),
+            (F::INFINITY, F::ONE, F::ONE),
+            (F::INFINITY, F::NEG_ONE, F::NEG_ONE),
+            (F::INFINITY, F::INFINITY, F::INFINITY),
+            (F::INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
+            (F::INFINITY, F::NAN, F::INFINITY),
+            (F::INFINITY, F::NEG_NAN, F::INFINITY),
             (F::NEG_INFINITY, F::ZERO, F::NEG_INFINITY),
+            (F::NEG_INFINITY, F::NEG_ZERO, F::NEG_INFINITY),
+            (F::NEG_INFINITY, F::ONE, F::NEG_INFINITY),
+            (F::NEG_INFINITY, F::NEG_ONE, F::NEG_INFINITY),
+            (F::NEG_INFINITY, F::INFINITY, F::NEG_INFINITY),
+            (F::NEG_INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
+            (F::NEG_INFINITY, F::NAN, F::NEG_INFINITY),
+            (F::NEG_INFINITY, F::NEG_NAN, F::NEG_INFINITY),
             (F::NAN, F::ZERO, F::ZERO),
-            (F::ZERO, F::NAN, F::ZERO),
+            (F::NAN, F::NEG_ZERO, F::NEG_ZERO),
+            (F::NAN, F::ONE, F::ONE),
+            (F::NAN, F::NEG_ONE, F::NEG_ONE),
+            (F::NAN, F::INFINITY, F::INFINITY),
+            (F::NAN, F::NEG_INFINITY, F::NEG_INFINITY),
             (F::NAN, F::NAN, F::NAN),
+            (F::NEG_NAN, F::ZERO, F::ZERO),
+            (F::NEG_NAN, F::NEG_ZERO, F::NEG_ZERO),
+            (F::NEG_NAN, F::ONE, F::ONE),
+            (F::NEG_NAN, F::NEG_ONE, F::NEG_ONE),
+            (F::NEG_NAN, F::INFINITY, F::INFINITY),
+            (F::NEG_NAN, F::NEG_INFINITY, F::NEG_INFINITY),
         ];
 
         for (x, y, res) in cases {
             let val = f(x, y);
             assert_biteq!(val, res, "fmin({}, {})", Hexf(x), Hexf(y));
         }
+
+        // Ordering between zeros and NaNs does not matter
+        assert_eq!(f(F::ZERO, F::NEG_ZERO), F::ZERO);
+        assert_eq!(f(F::NEG_ZERO, F::ZERO), F::ZERO);
+        assert!(f(F::NAN, F::NEG_NAN).is_nan());
+        assert!(f(F::NEG_NAN, F::NAN).is_nan());
+        assert!(f(F::NEG_NAN, F::NEG_NAN).is_nan());
     }
 
     #[test]
@@ -125,22 +180,77 @@ mod tests {
     fn fmax_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
         let cases = [
             (F::ZERO, F::ZERO, F::ZERO),
-            (F::ONE, F::ONE, F::ONE),
             (F::ZERO, F::ONE, F::ONE),
-            (F::ONE, F::ZERO, F::ONE),
             (F::ZERO, F::NEG_ONE, F::ZERO),
+            (F::ZERO, F::INFINITY, F::INFINITY),
+            (F::ZERO, F::NEG_INFINITY, F::ZERO),
+            (F::ZERO, F::NAN, F::ZERO),
+            (F::ZERO, F::NEG_NAN, F::ZERO),
+            (F::NEG_ZERO, F::NEG_ZERO, F::NEG_ZERO),
+            (F::NEG_ZERO, F::ONE, F::ONE),
+            (F::NEG_ZERO, F::NEG_ONE, F::NEG_ZERO),
+            (F::NEG_ZERO, F::INFINITY, F::INFINITY),
+            (F::NEG_ZERO, F::NEG_INFINITY, F::NEG_ZERO),
+            (F::NEG_ZERO, F::NAN, F::NEG_ZERO),
+            (F::NEG_ZERO, F::NEG_NAN, F::NEG_ZERO),
+            (F::ONE, F::ZERO, F::ONE),
+            (F::ONE, F::NEG_ZERO, F::ONE),
+            (F::ONE, F::ONE, F::ONE),
+            (F::ONE, F::NEG_ONE, F::ONE),
+            (F::ONE, F::INFINITY, F::INFINITY),
+            (F::ONE, F::NEG_INFINITY, F::ONE),
+            (F::ONE, F::NAN, F::ONE),
+            (F::ONE, F::NEG_NAN, F::ONE),
             (F::NEG_ONE, F::ZERO, F::ZERO),
+            (F::NEG_ONE, F::NEG_ZERO, F::NEG_ZERO),
+            (F::NEG_ONE, F::ONE, F::ONE),
+            (F::NEG_ONE, F::NEG_ONE, F::NEG_ONE),
+            (F::NEG_ONE, F::INFINITY, F::INFINITY),
+            (F::NEG_ONE, F::NEG_INFINITY, F::NEG_ONE),
+            (F::NEG_ONE, F::NAN, F::NEG_ONE),
+            (F::NEG_ONE, F::NEG_NAN, F::NEG_ONE),
             (F::INFINITY, F::ZERO, F::INFINITY),
+            (F::INFINITY, F::NEG_ZERO, F::INFINITY),
+            (F::INFINITY, F::ONE, F::INFINITY),
+            (F::INFINITY, F::NEG_ONE, F::INFINITY),
+            (F::INFINITY, F::INFINITY, F::INFINITY),
+            (F::INFINITY, F::NEG_INFINITY, F::INFINITY),
+            (F::INFINITY, F::NAN, F::INFINITY),
+            (F::INFINITY, F::NEG_NAN, F::INFINITY),
             (F::NEG_INFINITY, F::ZERO, F::ZERO),
+            (F::NEG_INFINITY, F::NEG_ZERO, F::NEG_ZERO),
+            (F::NEG_INFINITY, F::ONE, F::ONE),
+            (F::NEG_INFINITY, F::NEG_ONE, F::NEG_ONE),
+            (F::NEG_INFINITY, F::INFINITY, F::INFINITY),
+            (F::NEG_INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
+            (F::NEG_INFINITY, F::NAN, F::NEG_INFINITY),
+            (F::NEG_INFINITY, F::NEG_NAN, F::NEG_INFINITY),
             (F::NAN, F::ZERO, F::ZERO),
-            (F::ZERO, F::NAN, F::ZERO),
+            (F::NAN, F::NEG_ZERO, F::NEG_ZERO),
+            (F::NAN, F::ONE, F::ONE),
+            (F::NAN, F::NEG_ONE, F::NEG_ONE),
+            (F::NAN, F::INFINITY, F::INFINITY),
+            (F::NAN, F::NEG_INFINITY, F::NEG_INFINITY),
             (F::NAN, F::NAN, F::NAN),
+            (F::NEG_NAN, F::ZERO, F::ZERO),
+            (F::NEG_NAN, F::NEG_ZERO, F::NEG_ZERO),
+            (F::NEG_NAN, F::ONE, F::ONE),
+            (F::NEG_NAN, F::NEG_ONE, F::NEG_ONE),
+            (F::NEG_NAN, F::INFINITY, F::INFINITY),
+            (F::NEG_NAN, F::NEG_INFINITY, F::NEG_INFINITY),
         ];
 
         for (x, y, res) in cases {
             let val = f(x, y);
             assert_biteq!(val, res, "fmax({}, {})", Hexf(x), Hexf(y));
         }
+
+        // Ordering between zeros and NaNs does not matter
+        assert_eq!(f(F::ZERO, F::NEG_ZERO), F::ZERO);
+        assert_eq!(f(F::NEG_ZERO, F::ZERO), F::ZERO);
+        assert!(f(F::NAN, F::NEG_NAN).is_nan());
+        assert!(f(F::NEG_NAN, F::NAN).is_nan());
+        assert!(f(F::NEG_NAN, F::NEG_NAN).is_nan());
     }
 
     #[test]
diff --git a/library/compiler-builtins/libm/src/math/fminimum_fmaximum.rs b/library/compiler-builtins/libm/src/math/fminimum_fmaximum.rs
index b7999e27392..8f130867051 100644
--- a/library/compiler-builtins/libm/src/math/fminimum_fmaximum.rs
+++ b/library/compiler-builtins/libm/src/math/fminimum_fmaximum.rs
@@ -74,24 +74,77 @@ mod tests {
     fn fminimum_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
         let cases = [
             (F::ZERO, F::ZERO, F::ZERO),
-            (F::ONE, F::ONE, F::ONE),
+            (F::ZERO, F::NEG_ZERO, F::NEG_ZERO),
             (F::ZERO, F::ONE, F::ZERO),
-            (F::ONE, F::ZERO, F::ZERO),
             (F::ZERO, F::NEG_ONE, F::NEG_ONE),
+            (F::ZERO, F::INFINITY, F::ZERO),
+            (F::ZERO, F::NEG_INFINITY, F::NEG_INFINITY),
+            (F::ZERO, F::NAN, F::NAN),
+            (F::NEG_ZERO, F::ZERO, F::NEG_ZERO),
+            (F::NEG_ZERO, F::NEG_ZERO, F::NEG_ZERO),
+            (F::NEG_ZERO, F::ONE, F::NEG_ZERO),
+            (F::NEG_ZERO, F::NEG_ONE, F::NEG_ONE),
+            (F::NEG_ZERO, F::INFINITY, F::NEG_ZERO),
+            (F::NEG_ZERO, F::NEG_INFINITY, F::NEG_INFINITY),
+            (F::NEG_ZERO, F::NAN, F::NAN),
+            (F::ONE, F::ZERO, F::ZERO),
+            (F::ONE, F::NEG_ZERO, F::NEG_ZERO),
+            (F::ONE, F::ONE, F::ONE),
+            (F::ONE, F::NEG_ONE, F::NEG_ONE),
+            (F::ONE, F::INFINITY, F::ONE),
+            (F::ONE, F::NEG_INFINITY, F::NEG_INFINITY),
+            (F::ONE, F::NAN, F::NAN),
             (F::NEG_ONE, F::ZERO, F::NEG_ONE),
+            (F::NEG_ONE, F::NEG_ZERO, F::NEG_ONE),
+            (F::NEG_ONE, F::ONE, F::NEG_ONE),
+            (F::NEG_ONE, F::NEG_ONE, F::NEG_ONE),
+            (F::NEG_ONE, F::INFINITY, F::NEG_ONE),
+            (F::NEG_ONE, F::NEG_INFINITY, F::NEG_INFINITY),
+            (F::NEG_ONE, F::NAN, F::NAN),
             (F::INFINITY, F::ZERO, F::ZERO),
+            (F::INFINITY, F::NEG_ZERO, F::NEG_ZERO),
+            (F::INFINITY, F::ONE, F::ONE),
+            (F::INFINITY, F::NEG_ONE, F::NEG_ONE),
+            (F::INFINITY, F::INFINITY, F::INFINITY),
+            (F::INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
+            (F::INFINITY, F::NAN, F::NAN),
             (F::NEG_INFINITY, F::ZERO, F::NEG_INFINITY),
+            (F::NEG_INFINITY, F::NEG_ZERO, F::NEG_INFINITY),
+            (F::NEG_INFINITY, F::ONE, F::NEG_INFINITY),
+            (F::NEG_INFINITY, F::NEG_ONE, F::NEG_INFINITY),
+            (F::NEG_INFINITY, F::INFINITY, F::NEG_INFINITY),
+            (F::NEG_INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
+            (F::NEG_INFINITY, F::NAN, F::NAN),
             (F::NAN, F::ZERO, F::NAN),
-            (F::ZERO, F::NAN, F::NAN),
+            (F::NAN, F::NEG_ZERO, F::NAN),
+            (F::NAN, F::ONE, F::NAN),
+            (F::NAN, F::NEG_ONE, F::NAN),
+            (F::NAN, F::INFINITY, F::NAN),
+            (F::NAN, F::NEG_INFINITY, F::NAN),
             (F::NAN, F::NAN, F::NAN),
-            (F::ZERO, F::NEG_ZERO, F::NEG_ZERO),
-            (F::NEG_ZERO, F::ZERO, F::NEG_ZERO),
         ];
 
         for (x, y, res) in cases {
             let val = f(x, y);
             assert_biteq!(val, res, "fminimum({}, {})", Hexf(x), Hexf(y));
         }
+
+        // Ordering between NaNs does not matter
+        assert!(f(F::NAN, F::NEG_NAN).is_nan());
+        assert!(f(F::NEG_NAN, F::NAN).is_nan());
+        assert!(f(F::ZERO, F::NEG_NAN).is_nan());
+        assert!(f(F::NEG_ZERO, F::NEG_NAN).is_nan());
+        assert!(f(F::ONE, F::NEG_NAN).is_nan());
+        assert!(f(F::NEG_ONE, F::NEG_NAN).is_nan());
+        assert!(f(F::INFINITY, F::NEG_NAN).is_nan());
+        assert!(f(F::NEG_INFINITY, F::NEG_NAN).is_nan());
+        assert!(f(F::NEG_NAN, F::ZERO).is_nan());
+        assert!(f(F::NEG_NAN, F::NEG_ZERO).is_nan());
+        assert!(f(F::NEG_NAN, F::ONE).is_nan());
+        assert!(f(F::NEG_NAN, F::NEG_ONE).is_nan());
+        assert!(f(F::NEG_NAN, F::INFINITY).is_nan());
+        assert!(f(F::NEG_NAN, F::NEG_INFINITY).is_nan());
+        assert!(f(F::NEG_NAN, F::NEG_NAN).is_nan());
     }
 
     #[test]
@@ -119,24 +172,77 @@ mod tests {
     fn fmaximum_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
         let cases = [
             (F::ZERO, F::ZERO, F::ZERO),
-            (F::ONE, F::ONE, F::ONE),
+            (F::ZERO, F::NEG_ZERO, F::ZERO),
             (F::ZERO, F::ONE, F::ONE),
-            (F::ONE, F::ZERO, F::ONE),
             (F::ZERO, F::NEG_ONE, F::ZERO),
+            (F::ZERO, F::INFINITY, F::INFINITY),
+            (F::ZERO, F::NEG_INFINITY, F::ZERO),
+            (F::ZERO, F::NAN, F::NAN),
+            (F::NEG_ZERO, F::ZERO, F::ZERO),
+            (F::NEG_ZERO, F::NEG_ZERO, F::NEG_ZERO),
+            (F::NEG_ZERO, F::ONE, F::ONE),
+            (F::NEG_ZERO, F::NEG_ONE, F::NEG_ZERO),
+            (F::NEG_ZERO, F::INFINITY, F::INFINITY),
+            (F::NEG_ZERO, F::NEG_INFINITY, F::NEG_ZERO),
+            (F::NEG_ZERO, F::NAN, F::NAN),
+            (F::ONE, F::ZERO, F::ONE),
+            (F::ONE, F::NEG_ZERO, F::ONE),
+            (F::ONE, F::ONE, F::ONE),
+            (F::ONE, F::NEG_ONE, F::ONE),
+            (F::ONE, F::INFINITY, F::INFINITY),
+            (F::ONE, F::NEG_INFINITY, F::ONE),
+            (F::ONE, F::NAN, F::NAN),
             (F::NEG_ONE, F::ZERO, F::ZERO),
+            (F::NEG_ONE, F::NEG_ZERO, F::NEG_ZERO),
+            (F::NEG_ONE, F::ONE, F::ONE),
+            (F::NEG_ONE, F::NEG_ONE, F::NEG_ONE),
+            (F::NEG_ONE, F::INFINITY, F::INFINITY),
+            (F::NEG_ONE, F::NEG_INFINITY, F::NEG_ONE),
+            (F::NEG_ONE, F::NAN, F::NAN),
             (F::INFINITY, F::ZERO, F::INFINITY),
+            (F::INFINITY, F::NEG_ZERO, F::INFINITY),
+            (F::INFINITY, F::ONE, F::INFINITY),
+            (F::INFINITY, F::NEG_ONE, F::INFINITY),
+            (F::INFINITY, F::INFINITY, F::INFINITY),
+            (F::INFINITY, F::NEG_INFINITY, F::INFINITY),
+            (F::INFINITY, F::NAN, F::NAN),
             (F::NEG_INFINITY, F::ZERO, F::ZERO),
+            (F::NEG_INFINITY, F::NEG_ZERO, F::NEG_ZERO),
+            (F::NEG_INFINITY, F::ONE, F::ONE),
+            (F::NEG_INFINITY, F::NEG_ONE, F::NEG_ONE),
+            (F::NEG_INFINITY, F::INFINITY, F::INFINITY),
+            (F::NEG_INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
+            (F::NEG_INFINITY, F::NAN, F::NAN),
             (F::NAN, F::ZERO, F::NAN),
-            (F::ZERO, F::NAN, F::NAN),
+            (F::NAN, F::NEG_ZERO, F::NAN),
+            (F::NAN, F::ONE, F::NAN),
+            (F::NAN, F::NEG_ONE, F::NAN),
+            (F::NAN, F::INFINITY, F::NAN),
+            (F::NAN, F::NEG_INFINITY, F::NAN),
             (F::NAN, F::NAN, F::NAN),
-            (F::ZERO, F::NEG_ZERO, F::ZERO),
-            (F::NEG_ZERO, F::ZERO, F::ZERO),
         ];
 
         for (x, y, res) in cases {
             let val = f(x, y);
             assert_biteq!(val, res, "fmaximum({}, {})", Hexf(x), Hexf(y));
         }
+
+        // Ordering between NaNs does not matter
+        assert!(f(F::NAN, F::NEG_NAN).is_nan());
+        assert!(f(F::NEG_NAN, F::NAN).is_nan());
+        assert!(f(F::ZERO, F::NEG_NAN).is_nan());
+        assert!(f(F::NEG_ZERO, F::NEG_NAN).is_nan());
+        assert!(f(F::ONE, F::NEG_NAN).is_nan());
+        assert!(f(F::NEG_ONE, F::NEG_NAN).is_nan());
+        assert!(f(F::INFINITY, F::NEG_NAN).is_nan());
+        assert!(f(F::NEG_INFINITY, F::NEG_NAN).is_nan());
+        assert!(f(F::NEG_NAN, F::ZERO).is_nan());
+        assert!(f(F::NEG_NAN, F::NEG_ZERO).is_nan());
+        assert!(f(F::NEG_NAN, F::ONE).is_nan());
+        assert!(f(F::NEG_NAN, F::NEG_ONE).is_nan());
+        assert!(f(F::NEG_NAN, F::INFINITY).is_nan());
+        assert!(f(F::NEG_NAN, F::NEG_INFINITY).is_nan());
+        assert!(f(F::NEG_NAN, F::NEG_NAN).is_nan());
     }
 
     #[test]
diff --git a/library/compiler-builtins/libm/src/math/fminimum_fmaximum_num.rs b/library/compiler-builtins/libm/src/math/fminimum_fmaximum_num.rs
index 180d21f72b7..fadf934180a 100644
--- a/library/compiler-builtins/libm/src/math/fminimum_fmaximum_num.rs
+++ b/library/compiler-builtins/libm/src/math/fminimum_fmaximum_num.rs
@@ -74,24 +74,77 @@ mod tests {
     fn fminimum_num_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
         let cases = [
             (F::ZERO, F::ZERO, F::ZERO),
-            (F::ONE, F::ONE, F::ONE),
+            (F::ZERO, F::NEG_ZERO, F::NEG_ZERO),
             (F::ZERO, F::ONE, F::ZERO),
-            (F::ONE, F::ZERO, F::ZERO),
             (F::ZERO, F::NEG_ONE, F::NEG_ONE),
+            (F::ZERO, F::INFINITY, F::ZERO),
+            (F::ZERO, F::NEG_INFINITY, F::NEG_INFINITY),
+            (F::ZERO, F::NAN, F::ZERO),
+            (F::ZERO, F::NEG_NAN, F::ZERO),
+            (F::NEG_ZERO, F::ZERO, F::NEG_ZERO),
+            (F::NEG_ZERO, F::NEG_ZERO, F::NEG_ZERO),
+            (F::NEG_ZERO, F::ONE, F::NEG_ZERO),
+            (F::NEG_ZERO, F::NEG_ONE, F::NEG_ONE),
+            (F::NEG_ZERO, F::INFINITY, F::NEG_ZERO),
+            (F::NEG_ZERO, F::NEG_INFINITY, F::NEG_INFINITY),
+            (F::NEG_ZERO, F::NAN, F::NEG_ZERO),
+            (F::NEG_ZERO, F::NEG_NAN, F::NEG_ZERO),
+            (F::ONE, F::ZERO, F::ZERO),
+            (F::ONE, F::NEG_ZERO, F::NEG_ZERO),
+            (F::ONE, F::ONE, F::ONE),
+            (F::ONE, F::NEG_ONE, F::NEG_ONE),
+            (F::ONE, F::INFINITY, F::ONE),
+            (F::ONE, F::NEG_INFINITY, F::NEG_INFINITY),
+            (F::ONE, F::NAN, F::ONE),
+            (F::ONE, F::NEG_NAN, F::ONE),
             (F::NEG_ONE, F::ZERO, F::NEG_ONE),
+            (F::NEG_ONE, F::NEG_ZERO, F::NEG_ONE),
+            (F::NEG_ONE, F::ONE, F::NEG_ONE),
+            (F::NEG_ONE, F::NEG_ONE, F::NEG_ONE),
+            (F::NEG_ONE, F::INFINITY, F::NEG_ONE),
+            (F::NEG_ONE, F::NEG_INFINITY, F::NEG_INFINITY),
+            (F::NEG_ONE, F::NAN, F::NEG_ONE),
+            (F::NEG_ONE, F::NEG_NAN, F::NEG_ONE),
             (F::INFINITY, F::ZERO, F::ZERO),
+            (F::INFINITY, F::NEG_ZERO, F::NEG_ZERO),
+            (F::INFINITY, F::ONE, F::ONE),
+            (F::INFINITY, F::NEG_ONE, F::NEG_ONE),
+            (F::INFINITY, F::INFINITY, F::INFINITY),
+            (F::INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
+            (F::INFINITY, F::NAN, F::INFINITY),
+            (F::INFINITY, F::NEG_NAN, F::INFINITY),
             (F::NEG_INFINITY, F::ZERO, F::NEG_INFINITY),
+            (F::NEG_INFINITY, F::NEG_ZERO, F::NEG_INFINITY),
+            (F::NEG_INFINITY, F::ONE, F::NEG_INFINITY),
+            (F::NEG_INFINITY, F::NEG_ONE, F::NEG_INFINITY),
+            (F::NEG_INFINITY, F::INFINITY, F::NEG_INFINITY),
+            (F::NEG_INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
+            (F::NEG_INFINITY, F::NAN, F::NEG_INFINITY),
+            (F::NEG_INFINITY, F::NEG_NAN, F::NEG_INFINITY),
             (F::NAN, F::ZERO, F::ZERO),
-            (F::ZERO, F::NAN, F::ZERO),
+            (F::NAN, F::NEG_ZERO, F::NEG_ZERO),
+            (F::NAN, F::ONE, F::ONE),
+            (F::NAN, F::NEG_ONE, F::NEG_ONE),
+            (F::NAN, F::INFINITY, F::INFINITY),
+            (F::NAN, F::NEG_INFINITY, F::NEG_INFINITY),
             (F::NAN, F::NAN, F::NAN),
-            (F::ZERO, F::NEG_ZERO, F::NEG_ZERO),
-            (F::NEG_ZERO, F::ZERO, F::NEG_ZERO),
+            (F::NEG_NAN, F::ZERO, F::ZERO),
+            (F::NEG_NAN, F::NEG_ZERO, F::NEG_ZERO),
+            (F::NEG_NAN, F::ONE, F::ONE),
+            (F::NEG_NAN, F::NEG_ONE, F::NEG_ONE),
+            (F::NEG_NAN, F::INFINITY, F::INFINITY),
+            (F::NEG_NAN, F::NEG_INFINITY, F::NEG_INFINITY),
         ];
 
-        for (x, y, res) in cases {
-            let val = f(x, y);
-            assert_biteq!(val, res, "fminimum_num({}, {})", Hexf(x), Hexf(y));
+        for (x, y, expected) in cases {
+            let actual = f(x, y);
+            assert_biteq!(actual, expected, "fminimum_num({}, {})", Hexf(x), Hexf(y));
         }
+
+        // Ordering between NaNs does not matter
+        assert!(f(F::NAN, F::NEG_NAN).is_nan());
+        assert!(f(F::NEG_NAN, F::NAN).is_nan());
+        assert!(f(F::NEG_NAN, F::NEG_NAN).is_nan());
     }
 
     #[test]
@@ -119,24 +172,77 @@ mod tests {
     fn fmaximum_num_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
         let cases = [
             (F::ZERO, F::ZERO, F::ZERO),
-            (F::ONE, F::ONE, F::ONE),
+            (F::ZERO, F::NEG_ZERO, F::ZERO),
             (F::ZERO, F::ONE, F::ONE),
-            (F::ONE, F::ZERO, F::ONE),
             (F::ZERO, F::NEG_ONE, F::ZERO),
+            (F::ZERO, F::INFINITY, F::INFINITY),
+            (F::ZERO, F::NEG_INFINITY, F::ZERO),
+            (F::ZERO, F::NAN, F::ZERO),
+            (F::ZERO, F::NEG_NAN, F::ZERO),
+            (F::NEG_ZERO, F::ZERO, F::ZERO),
+            (F::NEG_ZERO, F::NEG_ZERO, F::NEG_ZERO),
+            (F::NEG_ZERO, F::ONE, F::ONE),
+            (F::NEG_ZERO, F::NEG_ONE, F::NEG_ZERO),
+            (F::NEG_ZERO, F::INFINITY, F::INFINITY),
+            (F::NEG_ZERO, F::NEG_INFINITY, F::NEG_ZERO),
+            (F::NEG_ZERO, F::NAN, F::NEG_ZERO),
+            (F::NEG_ZERO, F::NEG_NAN, F::NEG_ZERO),
+            (F::ONE, F::ZERO, F::ONE),
+            (F::ONE, F::NEG_ZERO, F::ONE),
+            (F::ONE, F::ONE, F::ONE),
+            (F::ONE, F::NEG_ONE, F::ONE),
+            (F::ONE, F::INFINITY, F::INFINITY),
+            (F::ONE, F::NEG_INFINITY, F::ONE),
+            (F::ONE, F::NAN, F::ONE),
+            (F::ONE, F::NEG_NAN, F::ONE),
             (F::NEG_ONE, F::ZERO, F::ZERO),
+            (F::NEG_ONE, F::NEG_ZERO, F::NEG_ZERO),
+            (F::NEG_ONE, F::ONE, F::ONE),
+            (F::NEG_ONE, F::NEG_ONE, F::NEG_ONE),
+            (F::NEG_ONE, F::INFINITY, F::INFINITY),
+            (F::NEG_ONE, F::NEG_INFINITY, F::NEG_ONE),
+            (F::NEG_ONE, F::NAN, F::NEG_ONE),
+            (F::NEG_ONE, F::NEG_NAN, F::NEG_ONE),
             (F::INFINITY, F::ZERO, F::INFINITY),
+            (F::INFINITY, F::NEG_ZERO, F::INFINITY),
+            (F::INFINITY, F::ONE, F::INFINITY),
+            (F::INFINITY, F::NEG_ONE, F::INFINITY),
+            (F::INFINITY, F::INFINITY, F::INFINITY),
+            (F::INFINITY, F::NEG_INFINITY, F::INFINITY),
+            (F::INFINITY, F::NAN, F::INFINITY),
+            (F::INFINITY, F::NEG_NAN, F::INFINITY),
             (F::NEG_INFINITY, F::ZERO, F::ZERO),
+            (F::NEG_INFINITY, F::NEG_ZERO, F::NEG_ZERO),
+            (F::NEG_INFINITY, F::ONE, F::ONE),
+            (F::NEG_INFINITY, F::NEG_ONE, F::NEG_ONE),
+            (F::NEG_INFINITY, F::INFINITY, F::INFINITY),
+            (F::NEG_INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
+            (F::NEG_INFINITY, F::NAN, F::NEG_INFINITY),
+            (F::NEG_INFINITY, F::NEG_NAN, F::NEG_INFINITY),
             (F::NAN, F::ZERO, F::ZERO),
-            (F::ZERO, F::NAN, F::ZERO),
+            (F::NAN, F::NEG_ZERO, F::NEG_ZERO),
+            (F::NAN, F::ONE, F::ONE),
+            (F::NAN, F::NEG_ONE, F::NEG_ONE),
+            (F::NAN, F::INFINITY, F::INFINITY),
+            (F::NAN, F::NEG_INFINITY, F::NEG_INFINITY),
             (F::NAN, F::NAN, F::NAN),
-            (F::ZERO, F::NEG_ZERO, F::ZERO),
-            (F::NEG_ZERO, F::ZERO, F::ZERO),
+            (F::NEG_NAN, F::ZERO, F::ZERO),
+            (F::NEG_NAN, F::NEG_ZERO, F::NEG_ZERO),
+            (F::NEG_NAN, F::ONE, F::ONE),
+            (F::NEG_NAN, F::NEG_ONE, F::NEG_ONE),
+            (F::NEG_NAN, F::INFINITY, F::INFINITY),
+            (F::NEG_NAN, F::NEG_INFINITY, F::NEG_INFINITY),
         ];
 
-        for (x, y, res) in cases {
-            let val = f(x, y);
-            assert_biteq!(val, res, "fmaximum_num({}, {})", Hexf(x), Hexf(y));
+        for (x, y, expected) in cases {
+            let actual = f(x, y);
+            assert_biteq!(actual, expected, "fmaximum_num({}, {})", Hexf(x), Hexf(y));
         }
+
+        // Ordering between NaNs does not matter
+        assert!(f(F::NAN, F::NEG_NAN).is_nan());
+        assert!(f(F::NEG_NAN, F::NAN).is_nan());
+        assert!(f(F::NEG_NAN, F::NEG_NAN).is_nan());
     }
 
     #[test]
diff --git a/library/compiler-builtins/libm/src/math/generic/fmax.rs b/library/compiler-builtins/libm/src/math/generic/fmax.rs
index 54207e4b328..b05804704d0 100644
--- a/library/compiler-builtins/libm/src/math/generic/fmax.rs
+++ b/library/compiler-builtins/libm/src/math/generic/fmax.rs
@@ -19,6 +19,5 @@ use crate::support::Float;
 #[inline]
 pub fn fmax<F: Float>(x: F, y: F) -> F {
     let res = if x.is_nan() || x < y { y } else { x };
-    // Canonicalize
-    res * F::ONE
+    res.canonicalize()
 }
diff --git a/library/compiler-builtins/libm/src/math/generic/fmaximum.rs b/library/compiler-builtins/libm/src/math/generic/fmaximum.rs
index 898828b80c7..55a031e18ee 100644
--- a/library/compiler-builtins/libm/src/math/generic/fmaximum.rs
+++ b/library/compiler-builtins/libm/src/math/generic/fmaximum.rs
@@ -4,8 +4,8 @@
 //! Per the spec, returns the canonicalized result of:
 //! - `x` if `x > y`
 //! - `y` if `y > x`
+//! - +0.0 if x and y are zero with opposite signs
 //! - qNaN if either operation is NaN
-//! - Logic following +0.0 > -0.0
 //!
 //! Excluded from our implementation is sNaN handling.
 
@@ -23,6 +23,5 @@ pub fn fmaximum<F: Float>(x: F, y: F) -> F {
         y
     };
 
-    // Canonicalize
-    res * F::ONE
+    res.canonicalize()
 }
diff --git a/library/compiler-builtins/libm/src/math/generic/fmaximum_num.rs b/library/compiler-builtins/libm/src/math/generic/fmaximum_num.rs
index 05df6cbd464..2dc60b2d237 100644
--- a/library/compiler-builtins/libm/src/math/generic/fmaximum_num.rs
+++ b/library/compiler-builtins/libm/src/math/generic/fmaximum_num.rs
@@ -4,10 +4,10 @@
 //! Per the spec, returns:
 //! - `x` if `x > y`
 //! - `y` if `y > x`
-//! - Non-NaN if one operand is NaN
-//! - Logic following +0.0 > -0.0
+//! - +0.0 if x and y are zero with opposite signs
 //! - Either `x` or `y` if `x == y` and the signs are the same
-//! - qNaN if either operand is a NaN
+//! - Non-NaN if one operand is NaN
+//! - qNaN if both operands are NaNx
 //!
 //! Excluded from our implementation is sNaN handling.
 
@@ -15,12 +15,15 @@ use crate::support::Float;
 
 #[inline]
 pub fn fmaximum_num<F: Float>(x: F, y: F) -> F {
-    let res = if x.is_nan() || x < y || (x.biteq(F::NEG_ZERO) && y.is_sign_positive()) {
+    let res = if x > y || y.is_nan() {
+        x
+    } else if y > x || x.is_nan() {
         y
-    } else {
+    } else if x.is_sign_positive() {
         x
+    } else {
+        y
     };
 
-    // Canonicalize
-    res * F::ONE
+    res.canonicalize()
 }
diff --git a/library/compiler-builtins/libm/src/math/generic/fmin.rs b/library/compiler-builtins/libm/src/math/generic/fmin.rs
index 0f86364d230..e2245bf9e13 100644
--- a/library/compiler-builtins/libm/src/math/generic/fmin.rs
+++ b/library/compiler-builtins/libm/src/math/generic/fmin.rs
@@ -19,6 +19,5 @@ use crate::support::Float;
 #[inline]
 pub fn fmin<F: Float>(x: F, y: F) -> F {
     let res = if y.is_nan() || x < y { x } else { y };
-    // Canonicalize
-    res * F::ONE
+    res.canonicalize()
 }
diff --git a/library/compiler-builtins/libm/src/math/generic/fminimum.rs b/library/compiler-builtins/libm/src/math/generic/fminimum.rs
index 8592ac5460e..aa68b1291d4 100644
--- a/library/compiler-builtins/libm/src/math/generic/fminimum.rs
+++ b/library/compiler-builtins/libm/src/math/generic/fminimum.rs
@@ -4,8 +4,8 @@
 //! Per the spec, returns the canonicalized result of:
 //! - `x` if `x < y`
 //! - `y` if `y < x`
+//! - -0.0 if x and y are zero with opposite signs
 //! - qNaN if either operation is NaN
-//! - Logic following +0.0 > -0.0
 //!
 //! Excluded from our implementation is sNaN handling.
 
@@ -23,6 +23,5 @@ pub fn fminimum<F: Float>(x: F, y: F) -> F {
         y
     };
 
-    // Canonicalize
-    res * F::ONE
+    res.canonicalize()
 }
diff --git a/library/compiler-builtins/libm/src/math/generic/fminimum_num.rs b/library/compiler-builtins/libm/src/math/generic/fminimum_num.rs
index 6777bbf8772..265bd4605ce 100644
--- a/library/compiler-builtins/libm/src/math/generic/fminimum_num.rs
+++ b/library/compiler-builtins/libm/src/math/generic/fminimum_num.rs
@@ -4,10 +4,10 @@
 //! Per the spec, returns:
 //! - `x` if `x < y`
 //! - `y` if `y < x`
-//! - Non-NaN if one operand is NaN
-//! - Logic following +0.0 > -0.0
+//! - -0.0 if x and y are zero with opposite signs
 //! - Either `x` or `y` if `x == y` and the signs are the same
-//! - qNaN if either operand is a NaN
+//! - Non-NaN if one operand is NaN
+//! - qNaN if both operands are NaNx
 //!
 //! Excluded from our implementation is sNaN handling.
 
@@ -15,12 +15,15 @@ use crate::support::Float;
 
 #[inline]
 pub fn fminimum_num<F: Float>(x: F, y: F) -> F {
-    let res = if y.is_nan() || x < y || (x.biteq(F::NEG_ZERO) && y.is_sign_positive()) {
+    let res = if x > y || x.is_nan() {
+        y
+    } else if y > x || y.is_nan() {
         x
-    } else {
+    } else if x.is_sign_positive() {
         y
+    } else {
+        x
     };
 
-    // Canonicalize
-    res * F::ONE
+    res.canonicalize()
 }
diff --git a/library/compiler-builtins/libm/src/math/support/float_traits.rs b/library/compiler-builtins/libm/src/math/support/float_traits.rs
index dd9f46209c1..c3e7eeec245 100644
--- a/library/compiler-builtins/libm/src/math/support/float_traits.rs
+++ b/library/compiler-builtins/libm/src/math/support/float_traits.rs
@@ -190,6 +190,15 @@ pub trait Float:
             Self::ONE.copysign(self)
         }
     }
+
+    /// Make a best-effort attempt to canonicalize the number. Note that this is allowed
+    /// to be a nop and does not always quiet sNaNs.
+    fn canonicalize(self) -> Self {
+        // FIXME: LLVM often removes this. We should determine whether we can remove the operation,
+        // or switch to something based on `llvm.canonicalize` (which has crashes,
+        // <https://github.com/llvm/llvm-project/issues/32650>).
+        self * Self::ONE
+    }
 }
 
 /// Access the associated `Int` type from a float (helper to avoid ambiguous associated types).
diff --git a/library/compiler-builtins/libm/src/math/support/macros.rs b/library/compiler-builtins/libm/src/math/support/macros.rs
index 2b8fd580a50..550d2e92eb7 100644
--- a/library/compiler-builtins/libm/src/math/support/macros.rs
+++ b/library/compiler-builtins/libm/src/math/support/macros.rs
@@ -143,10 +143,12 @@ macro_rules! assert_biteq {
         let bits = $crate::support::Int::leading_zeros(l.to_bits() - l.to_bits());
         assert!(
             $crate::support::Float::biteq(l, r),
-            "{}\nl: {l:?} ({lb:#0width$x})\nr: {r:?} ({rb:#0width$x})",
+            "{}\nl: {l:?} ({lb:#0width$x} {lh})\nr: {r:?} ({rb:#0width$x} {rh})",
             format_args!($($tt)*),
             lb = l.to_bits(),
+            lh = $crate::support::Hexf(l),
             rb = r.to_bits(),
+            rh = $crate::support::Hexf(r),
             width = ((bits / 4) + 2) as usize,
 
         );
diff --git a/library/compiler-builtins/rust-version b/library/compiler-builtins/rust-version
index e05aaa0573c..73183983599 100644
--- a/library/compiler-builtins/rust-version
+++ b/library/compiler-builtins/rust-version
@@ -1 +1 @@
-df8102fe5f24f28a918660b0cd918d7331c3896e
+d087f112b7d1323446c7b39a8b616aee7fa56b3d
diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs
index 595cc1fe025..f7a21072f53 100644
--- a/library/core/src/ffi/c_str.rs
+++ b/library/core/src/ffi/c_str.rs
@@ -162,10 +162,12 @@ impl fmt::Display for FromBytesUntilNulError {
     }
 }
 
+/// Shows the underlying bytes as a normal string, with invalid UTF-8
+/// presented as hex escape sequences.
 #[stable(feature = "cstr_debug", since = "1.3.0")]
 impl fmt::Debug for CStr {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "\"{}\"", self.to_bytes().escape_ascii())
+        fmt::Debug::fmt(crate::bstr::ByteStr::from_bytes(self.to_bytes()), f)
     }
 }
 
diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs
index 4434ceb49bc..e0e80fc9b41 100644
--- a/library/core/src/intrinsics/mod.rs
+++ b/library/core/src/intrinsics/mod.rs
@@ -1,5 +1,9 @@
 //! Compiler intrinsics.
 //!
+//! The functions in this module are implementation details of `core` and should
+//! not be used outside of the standard library. We generally provide access to
+//! intrinsics via stable wrapper functions. Use these instead.
+//!
 //! These are the imports making intrinsics available to Rust code. The actual implementations live in the compiler.
 //! Some of these intrinsics are lowered to MIR in <https://github.com/rust-lang/rust/blob/master/compiler/rustc_mir_transform/src/lower_intrinsics.rs>.
 //! The remaining intrinsics are implemented for the LLVM backend in <https://github.com/rust-lang/rust/blob/master/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs>
@@ -926,8 +930,7 @@ pub const unsafe fn slice_get_unchecked<
 pub fn ptr_mask<T>(ptr: *const T, mask: usize) -> *const T;
 
 /// Equivalent to the appropriate `llvm.memcpy.p0i8.0i8.*` intrinsic, with
-/// a size of `count` * `size_of::<T>()` and an alignment of
-/// `min_align_of::<T>()`
+/// a size of `count` * `size_of::<T>()` and an alignment of `align_of::<T>()`.
 ///
 /// This intrinsic does not have a stable counterpart.
 /// # Safety
@@ -941,8 +944,7 @@ pub fn ptr_mask<T>(ptr: *const T, mask: usize) -> *const T;
 #[rustc_nounwind]
 pub unsafe fn volatile_copy_nonoverlapping_memory<T>(dst: *mut T, src: *const T, count: usize);
 /// Equivalent to the appropriate `llvm.memmove.p0i8.0i8.*` intrinsic, with
-/// a size of `count * size_of::<T>()` and an alignment of
-/// `min_align_of::<T>()`
+/// a size of `count * size_of::<T>()` and an alignment of `align_of::<T>()`.
 ///
 /// The volatile parameter is set to `true`, so it will not be optimized out
 /// unless size is equal to zero.
@@ -952,8 +954,7 @@ pub unsafe fn volatile_copy_nonoverlapping_memory<T>(dst: *mut T, src: *const T,
 #[rustc_nounwind]
 pub unsafe fn volatile_copy_memory<T>(dst: *mut T, src: *const T, count: usize);
 /// Equivalent to the appropriate `llvm.memset.p0i8.*` intrinsic, with a
-/// size of `count * size_of::<T>()` and an alignment of
-/// `min_align_of::<T>()`.
+/// size of `count * size_of::<T>()` and an alignment of `align_of::<T>()`.
 ///
 /// This intrinsic does not have a stable counterpart.
 /// # Safety
@@ -2649,7 +2650,7 @@ pub const fn size_of<T>() -> usize;
 #[unstable(feature = "core_intrinsics", issue = "none")]
 #[rustc_intrinsic_const_stable_indirect]
 #[rustc_intrinsic]
-pub const fn min_align_of<T>() -> usize;
+pub const fn align_of<T>() -> usize;
 
 /// Returns the number of variants of the type `T` cast to a `usize`;
 /// if `T` has no variants, returns `0`. Uninhabited variants will be counted.
@@ -2689,7 +2690,7 @@ pub const unsafe fn size_of_val<T: ?Sized>(ptr: *const T) -> usize;
 #[unstable(feature = "core_intrinsics", issue = "none")]
 #[rustc_intrinsic]
 #[rustc_intrinsic_const_stable_indirect]
-pub const unsafe fn min_align_of_val<T: ?Sized>(ptr: *const T) -> usize;
+pub const unsafe fn align_of_val<T: ?Sized>(ptr: *const T) -> usize;
 
 /// Gets a static string slice containing the name of a type.
 ///
diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs
index 0a5f3ee35b1..73d9a8ee26d 100644
--- a/library/core/src/mem/mod.rs
+++ b/library/core/src/mem/mod.rs
@@ -149,8 +149,32 @@ pub const fn forget<T>(t: T) {
 
 /// Like [`forget`], but also accepts unsized values.
 ///
-/// This function is just a shim intended to be removed when the `unsized_locals` feature gets
-/// stabilized.
+/// While Rust does not permit unsized locals since its removal in [#111942] it is
+/// still possible to call functions with unsized values from a function argument
+/// or in-place construction.
+///
+/// ```rust
+/// #![feature(unsized_fn_params, forget_unsized)]
+/// #![allow(internal_features)]
+///
+/// use std::mem::forget_unsized;
+///
+/// pub fn in_place() {
+///     forget_unsized(*Box::<str>::from("str"));
+/// }
+///
+/// pub fn param(x: str) {
+///     forget_unsized(x);
+/// }
+/// ```
+///
+/// This works because the compiler will alter these functions to pass the parameter
+/// by reference instead. This trick is necessary to support `Box<dyn FnOnce()>: FnOnce()`.
+/// See [#68304] and [#71170] for more information.
+///
+/// [#111942]: https://github.com/rust-lang/rust/issues/111942
+/// [#68304]: https://github.com/rust-lang/rust/issues/68304
+/// [#71170]: https://github.com/rust-lang/rust/pull/71170
 #[inline]
 #[unstable(feature = "forget_unsized", issue = "none")]
 pub fn forget_unsized<T: ?Sized>(t: T) {
@@ -412,7 +436,7 @@ pub const unsafe fn size_of_val_raw<T: ?Sized>(val: *const T) -> usize {
 #[stable(feature = "rust1", since = "1.0.0")]
 #[deprecated(note = "use `align_of` instead", since = "1.2.0", suggestion = "align_of")]
 pub fn min_align_of<T>() -> usize {
-    intrinsics::min_align_of::<T>()
+    intrinsics::align_of::<T>()
 }
 
 /// Returns the [ABI]-required minimum alignment of the type of the value that `val` points to in
@@ -436,7 +460,7 @@ pub fn min_align_of<T>() -> usize {
 #[deprecated(note = "use `align_of_val` instead", since = "1.2.0", suggestion = "align_of_val")]
 pub fn min_align_of_val<T: ?Sized>(val: &T) -> usize {
     // SAFETY: val is a reference, so it's a valid raw pointer
-    unsafe { intrinsics::min_align_of_val(val) }
+    unsafe { intrinsics::align_of_val(val) }
 }
 
 /// Returns the [ABI]-required minimum alignment of a type in bytes.
@@ -458,7 +482,7 @@ pub fn min_align_of_val<T: ?Sized>(val: &T) -> usize {
 #[rustc_promotable]
 #[rustc_const_stable(feature = "const_align_of", since = "1.24.0")]
 pub const fn align_of<T>() -> usize {
-    intrinsics::min_align_of::<T>()
+    intrinsics::align_of::<T>()
 }
 
 /// Returns the [ABI]-required minimum alignment of the type of the value that `val` points to in
@@ -477,10 +501,9 @@ pub const fn align_of<T>() -> usize {
 #[must_use]
 #[stable(feature = "rust1", since = "1.0.0")]
 #[rustc_const_stable(feature = "const_align_of_val", since = "1.85.0")]
-#[allow(deprecated)]
 pub const fn align_of_val<T: ?Sized>(val: &T) -> usize {
     // SAFETY: val is a reference, so it's a valid raw pointer
-    unsafe { intrinsics::min_align_of_val(val) }
+    unsafe { intrinsics::align_of_val(val) }
 }
 
 /// Returns the [ABI]-required minimum alignment of the type of the value that `val` points to in
@@ -527,7 +550,7 @@ pub const fn align_of_val<T: ?Sized>(val: &T) -> usize {
 #[unstable(feature = "layout_for_ptr", issue = "69835")]
 pub const unsafe fn align_of_val_raw<T: ?Sized>(val: *const T) -> usize {
     // SAFETY: the caller must provide a valid raw pointer
-    unsafe { intrinsics::min_align_of_val(val) }
+    unsafe { intrinsics::align_of_val(val) }
 }
 
 /// Returns `true` if dropping values of type `T` matters.
@@ -637,8 +660,6 @@ pub const fn needs_drop<T: ?Sized>() -> bool {
 #[inline(always)]
 #[must_use]
 #[stable(feature = "rust1", since = "1.0.0")]
-#[allow(deprecated_in_future)]
-#[allow(deprecated)]
 #[rustc_diagnostic_item = "mem_zeroed"]
 #[track_caller]
 #[rustc_const_stable(feature = "const_mem_zeroed", since = "1.75.0")]
@@ -677,8 +698,6 @@ pub const unsafe fn zeroed<T>() -> T {
 #[must_use]
 #[deprecated(since = "1.39.0", note = "use `mem::MaybeUninit` instead")]
 #[stable(feature = "rust1", since = "1.0.0")]
-#[allow(deprecated_in_future)]
-#[allow(deprecated)]
 #[rustc_diagnostic_item = "mem_uninitialized"]
 #[track_caller]
 pub unsafe fn uninitialized<T>() -> T {
diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs
index 65560f63c18..3a7bc902f93 100644
--- a/library/core/src/num/int_macros.rs
+++ b/library/core/src/num/int_macros.rs
@@ -239,7 +239,6 @@ macro_rules! int_impl {
         /// Basic usage:
         ///
         /// ```
-        ///
         #[doc = concat!("let n = -1", stringify!($SelfT), ";")]
         ///
         #[doc = concat!("assert_eq!(n.cast_unsigned(), ", stringify!($UnsignedT), "::MAX);")]
diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs
index 5c73bddbef2..ab2fcff61cd 100644
--- a/library/core/src/num/mod.rs
+++ b/library/core/src/num/mod.rs
@@ -1053,7 +1053,6 @@ impl u8 {
     /// # Examples
     ///
     /// ```
-    ///
     /// assert_eq!("0", b'0'.escape_ascii().to_string());
     /// assert_eq!("\\t", b'\t'.escape_ascii().to_string());
     /// assert_eq!("\\r", b'\r'.escape_ascii().to_string());
diff --git a/library/core/src/ops/control_flow.rs b/library/core/src/ops/control_flow.rs
index ef7e6f9c2f4..26661b20c12 100644
--- a/library/core/src/ops/control_flow.rs
+++ b/library/core/src/ops/control_flow.rs
@@ -98,7 +98,7 @@ pub enum ControlFlow<B, C = ()> {
     // is a no-op conversion in the `Try` implementation.
 }
 
-#[unstable(feature = "try_trait_v2", issue = "84277")]
+#[unstable(feature = "try_trait_v2", issue = "84277", old_name = "try_trait")]
 impl<B, C> ops::Try for ControlFlow<B, C> {
     type Output = C;
     type Residual = ControlFlow<B, convert::Infallible>;
@@ -117,7 +117,7 @@ impl<B, C> ops::Try for ControlFlow<B, C> {
     }
 }
 
-#[unstable(feature = "try_trait_v2", issue = "84277")]
+#[unstable(feature = "try_trait_v2", issue = "84277", old_name = "try_trait")]
 // Note: manually specifying the residual type instead of using the default to work around
 // https://github.com/rust-lang/rust/issues/99940
 impl<B, C> ops::FromResidual<ControlFlow<B, convert::Infallible>> for ControlFlow<B, C> {
diff --git a/library/core/src/ops/mod.rs b/library/core/src/ops/mod.rs
index 1658f0e5a36..87dd873fdb5 100644
--- a/library/core/src/ops/mod.rs
+++ b/library/core/src/ops/mod.rs
@@ -194,7 +194,7 @@ pub use self::try_trait::Residual;
 #[unstable(feature = "try_trait_v2_yeet", issue = "96374")]
 pub use self::try_trait::Yeet;
 pub(crate) use self::try_trait::{ChangeOutputType, NeverShortCircuit};
-#[unstable(feature = "try_trait_v2", issue = "84277")]
+#[unstable(feature = "try_trait_v2", issue = "84277", old_name = "try_trait")]
 pub use self::try_trait::{FromResidual, Try};
 #[unstable(feature = "coerce_unsized", issue = "18598")]
 pub use self::unsize::CoerceUnsized;
diff --git a/library/core/src/ops/try_trait.rs b/library/core/src/ops/try_trait.rs
index bac8ffb074b..aebbddb4f1c 100644
--- a/library/core/src/ops/try_trait.rs
+++ b/library/core/src/ops/try_trait.rs
@@ -112,7 +112,7 @@ use crate::ops::ControlFlow;
 ///     R::from_output(accum)
 /// }
 /// ```
-#[unstable(feature = "try_trait_v2", issue = "84277")]
+#[unstable(feature = "try_trait_v2", issue = "84277", old_name = "try_trait")]
 #[rustc_on_unimplemented(
     on(
         all(from_desugaring = "TryBlock"),
@@ -130,7 +130,7 @@ use crate::ops::ControlFlow;
 #[lang = "Try"]
 pub trait Try: FromResidual {
     /// The type of the value produced by `?` when *not* short-circuiting.
-    #[unstable(feature = "try_trait_v2", issue = "84277")]
+    #[unstable(feature = "try_trait_v2", issue = "84277", old_name = "try_trait")]
     type Output;
 
     /// The type of the value passed to [`FromResidual::from_residual`]
@@ -154,7 +154,7 @@ pub trait Try: FromResidual {
     /// then typically you can use `Foo<std::convert::Infallible>` as its `Residual`
     /// type: that type will have a "hole" in the correct place, and will maintain the
     /// "foo-ness" of the residual so other types need to opt-in to interconversion.
-    #[unstable(feature = "try_trait_v2", issue = "84277")]
+    #[unstable(feature = "try_trait_v2", issue = "84277", old_name = "try_trait")]
     type Residual;
 
     /// Constructs the type from its `Output` type.
@@ -186,7 +186,7 @@ pub trait Try: FromResidual {
     /// assert_eq!(r, Some(4));
     /// ```
     #[lang = "from_output"]
-    #[unstable(feature = "try_trait_v2", issue = "84277")]
+    #[unstable(feature = "try_trait_v2", issue = "84277", old_name = "try_trait")]
     fn from_output(output: Self::Output) -> Self;
 
     /// Used in `?` to decide whether the operator should produce a value
@@ -213,7 +213,7 @@ pub trait Try: FromResidual {
     /// );
     /// ```
     #[lang = "branch"]
-    #[unstable(feature = "try_trait_v2", issue = "84277")]
+    #[unstable(feature = "try_trait_v2", issue = "84277", old_name = "try_trait")]
     fn branch(self) -> ControlFlow<Self::Residual, Self::Output>;
 }
 
@@ -303,7 +303,7 @@ pub trait Try: FromResidual {
     ),
 )]
 #[rustc_diagnostic_item = "FromResidual"]
-#[unstable(feature = "try_trait_v2", issue = "84277")]
+#[unstable(feature = "try_trait_v2", issue = "84277", old_name = "try_trait")]
 pub trait FromResidual<R = <Self as Try>::Residual> {
     /// Constructs the type from a compatible `Residual` type.
     ///
@@ -326,7 +326,7 @@ pub trait FromResidual<R = <Self as Try>::Residual> {
     /// );
     /// ```
     #[lang = "from_residual"]
-    #[unstable(feature = "try_trait_v2", issue = "84277")]
+    #[unstable(feature = "try_trait_v2", issue = "84277", old_name = "try_trait")]
     fn from_residual(residual: R) -> Self;
 }
 
diff --git a/library/core/src/option.rs b/library/core/src/option.rs
index c04754848b4..f2a1e901188 100644
--- a/library/core/src/option.rs
+++ b/library/core/src/option.rs
@@ -2532,7 +2532,7 @@ impl<A, V: FromIterator<A>> FromIterator<Option<A>> for Option<V> {
     }
 }
 
-#[unstable(feature = "try_trait_v2", issue = "84277")]
+#[unstable(feature = "try_trait_v2", issue = "84277", old_name = "try_trait")]
 impl<T> ops::Try for Option<T> {
     type Output = T;
     type Residual = Option<convert::Infallible>;
@@ -2551,7 +2551,7 @@ impl<T> ops::Try for Option<T> {
     }
 }
 
-#[unstable(feature = "try_trait_v2", issue = "84277")]
+#[unstable(feature = "try_trait_v2", issue = "84277", old_name = "try_trait")]
 // Note: manually specifying the residual type instead of using the default to work around
 // https://github.com/rust-lang/rust/issues/99940
 impl<T> ops::FromResidual<Option<convert::Infallible>> for Option<T> {
diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs
index cb1cf818bf0..0ac887f99dc 100644
--- a/library/core/src/primitive_docs.rs
+++ b/library/core/src/primitive_docs.rs
@@ -1847,6 +1847,8 @@ mod prim_ref {}
 /// - If `T` is guaranteed to be subject to the [null pointer
 ///   optimization](option/index.html#representation), and `E` is an enum satisfying the following
 ///   requirements, then `T` and `E` are ABI-compatible. Such an enum `E` is called "option-like".
+///   - The enum `E` uses the [`Rust` representation], and is not modified by the `align` or
+///     `packed` representation modifiers.
 ///   - The enum `E` has exactly two variants.
 ///   - One variant has exactly one field, of type `T`.
 ///   - All fields of the other variant are zero-sized with 1-byte alignment.
@@ -1920,6 +1922,7 @@ mod prim_ref {}
 /// [`Pointer`]: fmt::Pointer
 /// [`UnwindSafe`]: panic::UnwindSafe
 /// [`RefUnwindSafe`]: panic::RefUnwindSafe
+/// [`Rust` representation]: <https://doc.rust-lang.org/reference/type-layout.html#the-rust-representation>
 ///
 /// In addition, all *safe* function pointers implement [`Fn`], [`FnMut`], and [`FnOnce`], because
 /// these traits are specially known to the compiler.
diff --git a/library/core/src/result.rs b/library/core/src/result.rs
index 23e32c2e0f0..3a84ea66ad4 100644
--- a/library/core/src/result.rs
+++ b/library/core/src/result.rs
@@ -2051,7 +2051,7 @@ impl<A, E, V: FromIterator<A>> FromIterator<Result<A, E>> for Result<V, E> {
     }
 }
 
-#[unstable(feature = "try_trait_v2", issue = "84277")]
+#[unstable(feature = "try_trait_v2", issue = "84277", old_name = "try_trait")]
 impl<T, E> ops::Try for Result<T, E> {
     type Output = T;
     type Residual = Result<convert::Infallible, E>;
@@ -2070,7 +2070,7 @@ impl<T, E> ops::Try for Result<T, E> {
     }
 }
 
-#[unstable(feature = "try_trait_v2", issue = "84277")]
+#[unstable(feature = "try_trait_v2", issue = "84277", old_name = "try_trait")]
 impl<T, E, F: From<E>> ops::FromResidual<Result<convert::Infallible, E>> for Result<T, F> {
     #[inline]
     #[track_caller]
diff --git a/library/core/src/slice/ascii.rs b/library/core/src/slice/ascii.rs
index d91f8bba548..b4d9a1b1ca4 100644
--- a/library/core/src/slice/ascii.rs
+++ b/library/core/src/slice/ascii.rs
@@ -128,7 +128,6 @@ impl [u8] {
     /// # Examples
     ///
     /// ```
-    ///
     /// let s = b"0\t\r\n'\"\\\x9d";
     /// let escaped = s.escape_ascii().to_string();
     /// assert_eq!(escaped, "0\\t\\r\\n\\'\\\"\\\\\\x9d");
diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs
index 85a5e89a49e..6def6ae8530 100644
--- a/library/core/src/slice/iter.rs
+++ b/library/core/src/slice/iter.rs
@@ -3376,6 +3376,13 @@ where
 #[stable(feature = "slice_group_by", since = "1.77.0")]
 impl<'a, T: 'a, P> FusedIterator for ChunkBy<'a, T, P> where P: FnMut(&T, &T) -> bool {}
 
+#[stable(feature = "slice_group_by_clone", since = "CURRENT_RUSTC_VERSION")]
+impl<'a, T: 'a, P: Clone> Clone for ChunkBy<'a, T, P> {
+    fn clone(&self) -> Self {
+        Self { slice: self.slice, predicate: self.predicate.clone() }
+    }
+}
+
 #[stable(feature = "slice_group_by", since = "1.77.0")]
 impl<'a, T: 'a + fmt::Debug, P> fmt::Debug for ChunkBy<'a, T, P> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs
index 4f9f2936564..04c8d1473b0 100644
--- a/library/core/src/sync/atomic.rs
+++ b/library/core/src/sync/atomic.rs
@@ -891,6 +891,19 @@ impl AtomicBool {
     ///            Err(false));
     /// assert_eq!(some_bool.load(Ordering::Relaxed), false);
     /// ```
+    ///
+    /// # Considerations
+    ///
+    /// `compare_exchange` is a [compare-and-swap operation] and thus exhibits the usual downsides
+    /// of CAS operations. In particular, a load of the value followed by a successful
+    /// `compare_exchange` with the previous load *does not ensure* that other threads have not
+    /// changed the value in the interim. This is usually important when the *equality* check in
+    /// the `compare_exchange` is being used to check the *identity* of a value, but equality
+    /// does not necessarily imply identity. In this case, `compare_exchange` can lead to the
+    /// [ABA problem].
+    ///
+    /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem
+    /// [compare-and-swap operation]: https://en.wikipedia.org/wiki/Compare-and-swap
     #[inline]
     #[stable(feature = "extended_compare_and_swap", since = "1.10.0")]
     #[doc(alias = "compare_and_swap")]
@@ -973,6 +986,19 @@ impl AtomicBool {
     ///     }
     /// }
     /// ```
+    ///
+    /// # Considerations
+    ///
+    /// `compare_exchange` is a [compare-and-swap operation] and thus exhibits the usual downsides
+    /// of CAS operations. In particular, a load of the value followed by a successful
+    /// `compare_exchange` with the previous load *does not ensure* that other threads have not
+    /// changed the value in the interim. This is usually important when the *equality* check in
+    /// the `compare_exchange` is being used to check the *identity* of a value, but equality
+    /// does not necessarily imply identity. In this case, `compare_exchange` can lead to the
+    /// [ABA problem].
+    ///
+    /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem
+    /// [compare-and-swap operation]: https://en.wikipedia.org/wiki/Compare-and-swap
     #[inline]
     #[stable(feature = "extended_compare_and_swap", since = "1.10.0")]
     #[doc(alias = "compare_and_swap")]
@@ -1271,11 +1297,14 @@ impl AtomicBool {
     ///
     /// # Considerations
     ///
-    /// This method is not magic; it is not provided by the hardware.
-    /// It is implemented in terms of [`AtomicBool::compare_exchange_weak`], and suffers from the same drawbacks.
-    /// In particular, this method will not circumvent the [ABA Problem].
+    /// This method is not magic; it is not provided by the hardware, and does not act like a
+    /// critical section or mutex.
+    ///
+    /// It is implemented on top of an atomic [compare-and-swap operation], and thus is subject to
+    /// the usual drawbacks of CAS operations. In particular, be careful of the [ABA problem].
     ///
     /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem
+    /// [compare-and-swap operation]: https://en.wikipedia.org/wiki/Compare-and-swap
     ///
     /// # Examples
     ///
@@ -1338,11 +1367,14 @@ impl AtomicBool {
     ///
     /// # Considerations
     ///
-    /// This method is not magic; it is not provided by the hardware.
-    /// It is implemented in terms of [`AtomicBool::compare_exchange_weak`], and suffers from the same drawbacks.
-    /// In particular, this method will not circumvent the [ABA Problem].
+    /// This method is not magic; it is not provided by the hardware, and does not act like a
+    /// critical section or mutex.
+    ///
+    /// It is implemented on top of an atomic [compare-and-swap operation], and thus is subject to
+    /// the usual drawbacks of CAS operations. In particular, be careful of the [ABA problem].
     ///
     /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem
+    /// [compare-and-swap operation]: https://en.wikipedia.org/wiki/Compare-and-swap
     ///
     /// # Examples
     ///
@@ -1393,11 +1425,14 @@ impl AtomicBool {
     ///
     /// # Considerations
     ///
-    /// This method is not magic; it is not provided by the hardware.
-    /// It is implemented in terms of [`AtomicBool::compare_exchange_weak`], and suffers from the same drawbacks.
-    /// In particular, this method will not circumvent the [ABA Problem].
+    /// This method is not magic; it is not provided by the hardware, and does not act like a
+    /// critical section or mutex.
+    ///
+    /// It is implemented on top of an atomic [compare-and-swap operation], and thus is subject to
+    /// the usual drawbacks of CAS operations. In particular, be careful of the [ABA problem].
     ///
     /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem
+    /// [compare-and-swap operation]: https://en.wikipedia.org/wiki/Compare-and-swap
     ///
     /// # Examples
     ///
@@ -1825,6 +1860,20 @@ impl<T> AtomicPtr<T> {
     /// let value = some_ptr.compare_exchange(ptr, other_ptr,
     ///                                       Ordering::SeqCst, Ordering::Relaxed);
     /// ```
+    ///
+    /// # Considerations
+    ///
+    /// `compare_exchange` is a [compare-and-swap operation] and thus exhibits the usual downsides
+    /// of CAS operations. In particular, a load of the value followed by a successful
+    /// `compare_exchange` with the previous load *does not ensure* that other threads have not
+    /// changed the value in the interim. This is usually important when the *equality* check in
+    /// the `compare_exchange` is being used to check the *identity* of a value, but equality
+    /// does not necessarily imply identity. This is a particularly common case for pointers, as
+    /// a pointer holding the same address does not imply that the same object exists at that
+    /// address! In this case, `compare_exchange` can lead to the [ABA problem].
+    ///
+    /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem
+    /// [compare-and-swap operation]: https://en.wikipedia.org/wiki/Compare-and-swap
     #[inline]
     #[stable(feature = "extended_compare_and_swap", since = "1.10.0")]
     #[cfg(target_has_atomic = "ptr")]
@@ -1874,6 +1923,20 @@ impl<T> AtomicPtr<T> {
     ///     }
     /// }
     /// ```
+    ///
+    /// # Considerations
+    ///
+    /// `compare_exchange` is a [compare-and-swap operation] and thus exhibits the usual downsides
+    /// of CAS operations. In particular, a load of the value followed by a successful
+    /// `compare_exchange` with the previous load *does not ensure* that other threads have not
+    /// changed the value in the interim. This is usually important when the *equality* check in
+    /// the `compare_exchange` is being used to check the *identity* of a value, but equality
+    /// does not necessarily imply identity. This is a particularly common case for pointers, as
+    /// a pointer holding the same address does not imply that the same object exists at that
+    /// address! In this case, `compare_exchange` can lead to the [ABA problem].
+    ///
+    /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem
+    /// [compare-and-swap operation]: https://en.wikipedia.org/wiki/Compare-and-swap
     #[inline]
     #[stable(feature = "extended_compare_and_swap", since = "1.10.0")]
     #[cfg(target_has_atomic = "ptr")]
@@ -1917,11 +1980,15 @@ impl<T> AtomicPtr<T> {
     ///
     /// # Considerations
     ///
-    /// This method is not magic; it is not provided by the hardware.
-    /// It is implemented in terms of [`AtomicPtr::compare_exchange_weak`], and suffers from the same drawbacks.
-    /// In particular, this method will not circumvent the [ABA Problem].
+    /// This method is not magic; it is not provided by the hardware, and does not act like a
+    /// critical section or mutex.
+    ///
+    /// It is implemented on top of an atomic [compare-and-swap operation], and thus is subject to
+    /// the usual drawbacks of CAS operations. In particular, be careful of the [ABA problem],
+    /// which is a particularly common pitfall for pointers!
     ///
     /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem
+    /// [compare-and-swap operation]: https://en.wikipedia.org/wiki/Compare-and-swap
     ///
     /// # Examples
     ///
@@ -1992,11 +2059,15 @@ impl<T> AtomicPtr<T> {
     ///
     /// # Considerations
     ///
-    /// This method is not magic; it is not provided by the hardware.
-    /// It is implemented in terms of [`AtomicPtr::compare_exchange_weak`], and suffers from the same drawbacks.
-    /// In particular, this method will not circumvent the [ABA Problem].
+    /// This method is not magic; it is not provided by the hardware, and does not act like a
+    /// critical section or mutex.
+    ///
+    /// It is implemented on top of an atomic [compare-and-swap operation], and thus is subject to
+    /// the usual drawbacks of CAS operations. In particular, be careful of the [ABA problem],
+    /// which is a particularly common pitfall for pointers!
     ///
     /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem
+    /// [compare-and-swap operation]: https://en.wikipedia.org/wiki/Compare-and-swap
     ///
     /// # Examples
     ///
@@ -2057,11 +2128,15 @@ impl<T> AtomicPtr<T> {
     ///
     /// # Considerations
     ///
-    /// This method is not magic; it is not provided by the hardware.
-    /// It is implemented in terms of [`AtomicPtr::compare_exchange_weak`], and suffers from the same drawbacks.
-    /// In particular, this method will not circumvent the [ABA Problem].
+    /// This method is not magic; it is not provided by the hardware, and does not act like a
+    /// critical section or mutex.
+    ///
+    /// It is implemented on top of an atomic [compare-and-swap operation], and thus is subject to
+    /// the usual drawbacks of CAS operations. In particular, be careful of the [ABA problem],
+    /// which is a particularly common pitfall for pointers!
     ///
     /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem
+    /// [compare-and-swap operation]: https://en.wikipedia.org/wiki/Compare-and-swap
     ///
     /// # Examples
     ///
@@ -2967,6 +3042,20 @@ macro_rules! atomic_int {
             ///            Err(10));
             /// assert_eq!(some_var.load(Ordering::Relaxed), 10);
             /// ```
+            ///
+            /// # Considerations
+            ///
+            /// `compare_exchange` is a [compare-and-swap operation] and thus exhibits the usual downsides
+            /// of CAS operations. In particular, a load of the value followed by a successful
+            /// `compare_exchange` with the previous load *does not ensure* that other threads have not
+            /// changed the value in the interim! This is usually important when the *equality* check in
+            /// the `compare_exchange` is being used to check the *identity* of a value, but equality
+            /// does not necessarily imply identity. This is a particularly common case for pointers, as
+            /// a pointer holding the same address does not imply that the same object exists at that
+            /// address! In this case, `compare_exchange` can lead to the [ABA problem].
+            ///
+            /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem
+            /// [compare-and-swap operation]: https://en.wikipedia.org/wiki/Compare-and-swap
             #[inline]
             #[$stable_cxchg]
             #[$cfg_cas]
@@ -3016,6 +3105,20 @@ macro_rules! atomic_int {
             ///     }
             /// }
             /// ```
+            ///
+            /// # Considerations
+            ///
+            /// `compare_exchange` is a [compare-and-swap operation] and thus exhibits the usual downsides
+            /// of CAS operations. In particular, a load of the value followed by a successful
+            /// `compare_exchange` with the previous load *does not ensure* that other threads have not
+            /// changed the value in the interim. This is usually important when the *equality* check in
+            /// the `compare_exchange` is being used to check the *identity* of a value, but equality
+            /// does not necessarily imply identity. This is a particularly common case for pointers, as
+            /// a pointer holding the same address does not imply that the same object exists at that
+            /// address! In this case, `compare_exchange` can lead to the [ABA problem].
+            ///
+            /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem
+            /// [compare-and-swap operation]: https://en.wikipedia.org/wiki/Compare-and-swap
             #[inline]
             #[$stable_cxchg]
             #[$cfg_cas]
@@ -3246,13 +3349,16 @@ macro_rules! atomic_int {
             ///
             /// # Considerations
             ///
-            /// This method is not magic; it is not provided by the hardware.
-            /// It is implemented in terms of
-            #[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange_weak`],")]
-            /// and suffers from the same drawbacks.
-            /// In particular, this method will not circumvent the [ABA Problem].
+            /// This method is not magic; it is not provided by the hardware, and does not act like a
+            /// critical section or mutex.
+            ///
+            /// It is implemented on top of an atomic [compare-and-swap operation], and thus is subject to
+            /// the usual drawbacks of CAS operations. In particular, be careful of the [ABA problem]
+            /// if this atomic integer is an index or more generally if knowledge of only the *bitwise value*
+            /// of the atomic is not in and of itself sufficient to ensure any required preconditions.
             ///
             /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem
+            /// [compare-and-swap operation]: https://en.wikipedia.org/wiki/Compare-and-swap
             ///
             /// # Examples
             ///
@@ -3309,13 +3415,16 @@ macro_rules! atomic_int {
             ///
             /// # Considerations
             ///
-            /// This method is not magic; it is not provided by the hardware.
-            /// It is implemented in terms of
-            #[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange_weak`],")]
-            /// and suffers from the same drawbacks.
-            /// In particular, this method will not circumvent the [ABA Problem].
+            /// This method is not magic; it is not provided by the hardware, and does not act like a
+            /// critical section or mutex.
+            ///
+            /// It is implemented on top of an atomic [compare-and-swap operation], and thus is subject to
+            /// the usual drawbacks of CAS operations. In particular, be careful of the [ABA problem]
+            /// if this atomic integer is an index or more generally if knowledge of only the *bitwise value*
+            /// of the atomic is not in and of itself sufficient to ensure any required preconditions.
             ///
             /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem
+            /// [compare-and-swap operation]: https://en.wikipedia.org/wiki/Compare-and-swap
             ///
             /// # Examples
             ///
@@ -3367,13 +3476,17 @@ macro_rules! atomic_int {
             ///
             /// # Considerations
             ///
-            /// This method is not magic; it is not provided by the hardware.
-            /// It is implemented in terms of
-            #[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange_weak`],")]
-            /// and suffers from the same drawbacks.
-            /// In particular, this method will not circumvent the [ABA Problem].
+            /// [CAS operation]: https://en.wikipedia.org/wiki/Compare-and-swap
+            /// This method is not magic; it is not provided by the hardware, and does not act like a
+            /// critical section or mutex.
+            ///
+            /// It is implemented on top of an atomic [compare-and-swap operation], and thus is subject to
+            /// the usual drawbacks of CAS operations. In particular, be careful of the [ABA problem]
+            /// if this atomic integer is an index or more generally if knowledge of only the *bitwise value*
+            /// of the atomic is not in and of itself sufficient to ensure any required preconditions.
             ///
             /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem
+            /// [compare-and-swap operation]: https://en.wikipedia.org/wiki/Compare-and-swap
             ///
             /// # Examples
             ///
diff --git a/library/core/src/task/poll.rs b/library/core/src/task/poll.rs
index 6aab22177ab..ca668361ef6 100644
--- a/library/core/src/task/poll.rs
+++ b/library/core/src/task/poll.rs
@@ -229,7 +229,7 @@ impl<T> From<T> for Poll<T> {
     }
 }
 
-#[unstable(feature = "try_trait_v2", issue = "84277")]
+#[unstable(feature = "try_trait_v2", issue = "84277", old_name = "try_trait")]
 impl<T, E> ops::Try for Poll<Result<T, E>> {
     type Output = Poll<T>;
     type Residual = Result<convert::Infallible, E>;
@@ -249,7 +249,7 @@ impl<T, E> ops::Try for Poll<Result<T, E>> {
     }
 }
 
-#[unstable(feature = "try_trait_v2", issue = "84277")]
+#[unstable(feature = "try_trait_v2", issue = "84277", old_name = "try_trait")]
 impl<T, E, F: From<E>> ops::FromResidual<Result<convert::Infallible, E>> for Poll<Result<T, F>> {
     #[inline]
     fn from_residual(x: Result<convert::Infallible, E>) -> Self {
@@ -259,7 +259,7 @@ impl<T, E, F: From<E>> ops::FromResidual<Result<convert::Infallible, E>> for Pol
     }
 }
 
-#[unstable(feature = "try_trait_v2", issue = "84277")]
+#[unstable(feature = "try_trait_v2", issue = "84277", old_name = "try_trait")]
 impl<T, E> ops::Try for Poll<Option<Result<T, E>>> {
     type Output = Poll<Option<T>>;
     type Residual = Result<convert::Infallible, E>;
@@ -280,7 +280,7 @@ impl<T, E> ops::Try for Poll<Option<Result<T, E>>> {
     }
 }
 
-#[unstable(feature = "try_trait_v2", issue = "84277")]
+#[unstable(feature = "try_trait_v2", issue = "84277", old_name = "try_trait")]
 impl<T, E, F: From<E>> ops::FromResidual<Result<convert::Infallible, E>>
     for Poll<Option<Result<T, F>>>
 {
diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs
index 9b8fefe42af..bb7efe582f7 100644
--- a/library/core/src/task/wake.rs
+++ b/library/core/src/task/wake.rs
@@ -104,6 +104,7 @@ impl RawWaker {
 /// synchronization. This is because [`LocalWaker`] is not thread safe itself, so it cannot
 /// be sent across threads.
 #[stable(feature = "futures_api", since = "1.36.0")]
+#[allow(unpredictable_function_pointer_comparisons)]
 #[derive(PartialEq, Copy, Clone, Debug)]
 pub struct RawWakerVTable {
     /// This function will be called when the [`RawWaker`] gets cloned, e.g. when
diff --git a/library/coretests/tests/ffi/cstr.rs b/library/coretests/tests/ffi/cstr.rs
index dc34240cd99..7d669cc1c3f 100644
--- a/library/coretests/tests/ffi/cstr.rs
+++ b/library/coretests/tests/ffi/cstr.rs
@@ -17,7 +17,7 @@ fn compares_as_u8s() {
 #[test]
 fn debug() {
     let s = c"abc\x01\x02\n\xE2\x80\xA6\xFF";
-    assert_eq!(format!("{s:?}"), r#""abc\x01\x02\n\xe2\x80\xa6\xff""#);
+    assert_eq!(format!("{s:?}"), r#""abc\x01\x02\n…\xff""#);
 }
 
 #[test]
diff --git a/library/coretests/tests/floats/f128.rs b/library/coretests/tests/floats/f128.rs
index 01770f119df..cf78e8796a0 100644
--- a/library/coretests/tests/floats/f128.rs
+++ b/library/coretests/tests/floats/f128.rs
@@ -5,6 +5,8 @@ use core::ops::{Add, Div, Mul, Sub};
 use std::f128::consts;
 use std::num::FpCategory as Fp;
 
+use super::{assert_approx_eq, assert_biteq};
+
 // Note these tolerances make sense around zero, but not for more extreme exponents.
 
 /// Default tolerances. Works for values that should be near precise but not exact. Roughly
@@ -54,34 +56,6 @@ fn test_num_f128() {
 // the intrinsics.
 
 #[test]
-#[cfg(any(miri, target_has_reliable_f128_math))]
-fn test_min_nan() {
-    assert_biteq!(f128::NAN.min(2.0), 2.0);
-    assert_biteq!(2.0f128.min(f128::NAN), 2.0);
-}
-
-#[test]
-#[cfg(any(miri, target_has_reliable_f128_math))]
-fn test_max_nan() {
-    assert_biteq!(f128::NAN.max(2.0), 2.0);
-    assert_biteq!(2.0f128.max(f128::NAN), 2.0);
-}
-
-#[test]
-#[cfg(any(miri, target_has_reliable_f128_math))]
-fn test_minimum() {
-    assert!(f128::NAN.minimum(2.0).is_nan());
-    assert!(2.0f128.minimum(f128::NAN).is_nan());
-}
-
-#[test]
-#[cfg(any(miri, target_has_reliable_f128_math))]
-fn test_maximum() {
-    assert!(f128::NAN.maximum(2.0).is_nan());
-    assert!(2.0f128.maximum(f128::NAN).is_nan());
-}
-
-#[test]
 fn test_nan() {
     let nan: f128 = f128::NAN;
     assert!(nan.is_nan());
@@ -233,98 +207,6 @@ fn test_classify() {
 }
 
 #[test]
-#[cfg(target_has_reliable_f128_math)]
-fn test_floor() {
-    assert_biteq!(1.0f128.floor(), 1.0f128);
-    assert_biteq!(1.3f128.floor(), 1.0f128);
-    assert_biteq!(1.5f128.floor(), 1.0f128);
-    assert_biteq!(1.7f128.floor(), 1.0f128);
-    assert_biteq!(0.0f128.floor(), 0.0f128);
-    assert_biteq!((-0.0f128).floor(), -0.0f128);
-    assert_biteq!((-1.0f128).floor(), -1.0f128);
-    assert_biteq!((-1.3f128).floor(), -2.0f128);
-    assert_biteq!((-1.5f128).floor(), -2.0f128);
-    assert_biteq!((-1.7f128).floor(), -2.0f128);
-}
-
-#[test]
-#[cfg(any(miri, target_has_reliable_f128_math))]
-fn test_ceil() {
-    assert_biteq!(1.0f128.ceil(), 1.0f128);
-    assert_biteq!(1.3f128.ceil(), 2.0f128);
-    assert_biteq!(1.5f128.ceil(), 2.0f128);
-    assert_biteq!(1.7f128.ceil(), 2.0f128);
-    assert_biteq!(0.0f128.ceil(), 0.0f128);
-    assert_biteq!((-0.0f128).ceil(), -0.0f128);
-    assert_biteq!((-1.0f128).ceil(), -1.0f128);
-    assert_biteq!((-1.3f128).ceil(), -1.0f128);
-    assert_biteq!((-1.5f128).ceil(), -1.0f128);
-    assert_biteq!((-1.7f128).ceil(), -1.0f128);
-}
-
-#[test]
-#[cfg(any(miri, target_has_reliable_f128_math))]
-fn test_round() {
-    assert_biteq!(2.5f128.round(), 3.0f128);
-    assert_biteq!(1.0f128.round(), 1.0f128);
-    assert_biteq!(1.3f128.round(), 1.0f128);
-    assert_biteq!(1.5f128.round(), 2.0f128);
-    assert_biteq!(1.7f128.round(), 2.0f128);
-    assert_biteq!(0.0f128.round(), 0.0f128);
-    assert_biteq!((-0.0f128).round(), -0.0f128);
-    assert_biteq!((-1.0f128).round(), -1.0f128);
-    assert_biteq!((-1.3f128).round(), -1.0f128);
-    assert_biteq!((-1.5f128).round(), -2.0f128);
-    assert_biteq!((-1.7f128).round(), -2.0f128);
-}
-
-#[test]
-#[cfg(any(miri, target_has_reliable_f128_math))]
-fn test_round_ties_even() {
-    assert_biteq!(2.5f128.round_ties_even(), 2.0f128);
-    assert_biteq!(1.0f128.round_ties_even(), 1.0f128);
-    assert_biteq!(1.3f128.round_ties_even(), 1.0f128);
-    assert_biteq!(1.5f128.round_ties_even(), 2.0f128);
-    assert_biteq!(1.7f128.round_ties_even(), 2.0f128);
-    assert_biteq!(0.0f128.round_ties_even(), 0.0f128);
-    assert_biteq!((-0.0f128).round_ties_even(), -0.0f128);
-    assert_biteq!((-1.0f128).round_ties_even(), -1.0f128);
-    assert_biteq!((-1.3f128).round_ties_even(), -1.0f128);
-    assert_biteq!((-1.5f128).round_ties_even(), -2.0f128);
-    assert_biteq!((-1.7f128).round_ties_even(), -2.0f128);
-}
-
-#[test]
-#[cfg(any(miri, target_has_reliable_f128_math))]
-fn test_trunc() {
-    assert_biteq!(1.0f128.trunc(), 1.0f128);
-    assert_biteq!(1.3f128.trunc(), 1.0f128);
-    assert_biteq!(1.5f128.trunc(), 1.0f128);
-    assert_biteq!(1.7f128.trunc(), 1.0f128);
-    assert_biteq!(0.0f128.trunc(), 0.0f128);
-    assert_biteq!((-0.0f128).trunc(), -0.0f128);
-    assert_biteq!((-1.0f128).trunc(), -1.0f128);
-    assert_biteq!((-1.3f128).trunc(), -1.0f128);
-    assert_biteq!((-1.5f128).trunc(), -1.0f128);
-    assert_biteq!((-1.7f128).trunc(), -1.0f128);
-}
-
-#[test]
-#[cfg(any(miri, target_has_reliable_f128_math))]
-fn test_fract() {
-    assert_biteq!(1.0f128.fract(), 0.0f128);
-    assert_biteq!(1.3f128.fract(), 0.300000000000000000000000000000000039f128);
-    assert_biteq!(1.5f128.fract(), 0.5f128);
-    assert_biteq!(1.7f128.fract(), 0.7f128);
-    assert_biteq!(0.0f128.fract(), 0.0f128);
-    assert_biteq!((-0.0f128).fract(), 0.0f128);
-    assert_biteq!((-1.0f128).fract(), 0.0f128);
-    assert_biteq!((-1.3f128).fract(), -0.300000000000000000000000000000000039f128);
-    assert_biteq!((-1.5f128).fract(), -0.5f128);
-    assert_biteq!((-1.7f128).fract(), -0.699999999999999999999999999999999961f128);
-}
-
-#[test]
 #[cfg(any(miri, target_has_reliable_f128_math))]
 fn test_abs() {
     assert_biteq!(f128::INFINITY.abs(), f128::INFINITY);
diff --git a/library/coretests/tests/floats/f16.rs b/library/coretests/tests/floats/f16.rs
index 4797573f7d0..9e91b654304 100644
--- a/library/coretests/tests/floats/f16.rs
+++ b/library/coretests/tests/floats/f16.rs
@@ -4,6 +4,8 @@
 use std::f16::consts;
 use std::num::FpCategory as Fp;
 
+use super::{assert_approx_eq, assert_biteq};
+
 /// Tolerance for results on the order of 10.0e-2
 #[allow(unused)]
 const TOL_N2: f16 = 0.0001;
@@ -50,34 +52,6 @@ fn test_num_f16() {
 // the intrinsics.
 
 #[test]
-#[cfg(any(miri, target_has_reliable_f16_math))]
-fn test_min_nan() {
-    assert_biteq!(f16::NAN.min(2.0), 2.0);
-    assert_biteq!(2.0f16.min(f16::NAN), 2.0);
-}
-
-#[test]
-#[cfg(any(miri, target_has_reliable_f16_math))]
-fn test_max_nan() {
-    assert_biteq!(f16::NAN.max(2.0), 2.0);
-    assert_biteq!(2.0f16.max(f16::NAN), 2.0);
-}
-
-#[test]
-#[cfg(any(miri, target_has_reliable_f16_math))]
-fn test_minimum() {
-    assert!(f16::NAN.minimum(2.0).is_nan());
-    assert!(2.0f16.minimum(f16::NAN).is_nan());
-}
-
-#[test]
-#[cfg(any(miri, target_has_reliable_f16_math))]
-fn test_maximum() {
-    assert!(f16::NAN.maximum(2.0).is_nan());
-    assert!(2.0f16.maximum(f16::NAN).is_nan());
-}
-
-#[test]
 fn test_nan() {
     let nan: f16 = f16::NAN;
     assert!(nan.is_nan());
@@ -230,98 +204,6 @@ fn test_classify() {
 
 #[test]
 #[cfg(any(miri, target_has_reliable_f16_math))]
-fn test_floor() {
-    assert_biteq!(1.0f16.floor(), 1.0f16);
-    assert_biteq!(1.3f16.floor(), 1.0f16);
-    assert_biteq!(1.5f16.floor(), 1.0f16);
-    assert_biteq!(1.7f16.floor(), 1.0f16);
-    assert_biteq!(0.0f16.floor(), 0.0f16);
-    assert_biteq!((-0.0f16).floor(), -0.0f16);
-    assert_biteq!((-1.0f16).floor(), -1.0f16);
-    assert_biteq!((-1.3f16).floor(), -2.0f16);
-    assert_biteq!((-1.5f16).floor(), -2.0f16);
-    assert_biteq!((-1.7f16).floor(), -2.0f16);
-}
-
-#[test]
-#[cfg(any(miri, target_has_reliable_f16_math))]
-fn test_ceil() {
-    assert_biteq!(1.0f16.ceil(), 1.0f16);
-    assert_biteq!(1.3f16.ceil(), 2.0f16);
-    assert_biteq!(1.5f16.ceil(), 2.0f16);
-    assert_biteq!(1.7f16.ceil(), 2.0f16);
-    assert_biteq!(0.0f16.ceil(), 0.0f16);
-    assert_biteq!((-0.0f16).ceil(), -0.0f16);
-    assert_biteq!((-1.0f16).ceil(), -1.0f16);
-    assert_biteq!((-1.3f16).ceil(), -1.0f16);
-    assert_biteq!((-1.5f16).ceil(), -1.0f16);
-    assert_biteq!((-1.7f16).ceil(), -1.0f16);
-}
-
-#[test]
-#[cfg(any(miri, target_has_reliable_f16_math))]
-fn test_round() {
-    assert_biteq!(2.5f16.round(), 3.0f16);
-    assert_biteq!(1.0f16.round(), 1.0f16);
-    assert_biteq!(1.3f16.round(), 1.0f16);
-    assert_biteq!(1.5f16.round(), 2.0f16);
-    assert_biteq!(1.7f16.round(), 2.0f16);
-    assert_biteq!(0.0f16.round(), 0.0f16);
-    assert_biteq!((-0.0f16).round(), -0.0f16);
-    assert_biteq!((-1.0f16).round(), -1.0f16);
-    assert_biteq!((-1.3f16).round(), -1.0f16);
-    assert_biteq!((-1.5f16).round(), -2.0f16);
-    assert_biteq!((-1.7f16).round(), -2.0f16);
-}
-
-#[test]
-#[cfg(any(miri, target_has_reliable_f16_math))]
-fn test_round_ties_even() {
-    assert_biteq!(2.5f16.round_ties_even(), 2.0f16);
-    assert_biteq!(1.0f16.round_ties_even(), 1.0f16);
-    assert_biteq!(1.3f16.round_ties_even(), 1.0f16);
-    assert_biteq!(1.5f16.round_ties_even(), 2.0f16);
-    assert_biteq!(1.7f16.round_ties_even(), 2.0f16);
-    assert_biteq!(0.0f16.round_ties_even(), 0.0f16);
-    assert_biteq!((-0.0f16).round_ties_even(), -0.0f16);
-    assert_biteq!((-1.0f16).round_ties_even(), -1.0f16);
-    assert_biteq!((-1.3f16).round_ties_even(), -1.0f16);
-    assert_biteq!((-1.5f16).round_ties_even(), -2.0f16);
-    assert_biteq!((-1.7f16).round_ties_even(), -2.0f16);
-}
-
-#[test]
-#[cfg(any(miri, target_has_reliable_f16_math))]
-fn test_trunc() {
-    assert_biteq!(1.0f16.trunc(), 1.0f16);
-    assert_biteq!(1.3f16.trunc(), 1.0f16);
-    assert_biteq!(1.5f16.trunc(), 1.0f16);
-    assert_biteq!(1.7f16.trunc(), 1.0f16);
-    assert_biteq!(0.0f16.trunc(), 0.0f16);
-    assert_biteq!((-0.0f16).trunc(), -0.0f16);
-    assert_biteq!((-1.0f16).trunc(), -1.0f16);
-    assert_biteq!((-1.3f16).trunc(), -1.0f16);
-    assert_biteq!((-1.5f16).trunc(), -1.0f16);
-    assert_biteq!((-1.7f16).trunc(), -1.0f16);
-}
-
-#[test]
-#[cfg(any(miri, target_has_reliable_f16_math))]
-fn test_fract() {
-    assert_biteq!(1.0f16.fract(), 0.0f16);
-    assert_biteq!(1.3f16.fract(), 0.2998f16);
-    assert_biteq!(1.5f16.fract(), 0.5f16);
-    assert_biteq!(1.7f16.fract(), 0.7f16);
-    assert_biteq!(0.0f16.fract(), 0.0f16);
-    assert_biteq!((-0.0f16).fract(), 0.0f16);
-    assert_biteq!((-1.0f16).fract(), 0.0f16);
-    assert_biteq!((-1.3f16).fract(), -0.2998f16);
-    assert_biteq!((-1.5f16).fract(), -0.5f16);
-    assert_biteq!((-1.7f16).fract(), -0.7f16);
-}
-
-#[test]
-#[cfg(any(miri, target_has_reliable_f16_math))]
 fn test_abs() {
     assert_biteq!(f16::INFINITY.abs(), f16::INFINITY);
     assert_biteq!(1f16.abs(), 1f16);
diff --git a/library/coretests/tests/floats/f32.rs b/library/coretests/tests/floats/f32.rs
index 98e9695d090..d2724d12e39 100644
--- a/library/coretests/tests/floats/f32.rs
+++ b/library/coretests/tests/floats/f32.rs
@@ -2,6 +2,8 @@ use core::f32;
 use core::f32::consts;
 use core::num::FpCategory as Fp;
 
+use super::{assert_approx_eq, assert_biteq};
+
 /// Smallest number
 const TINY_BITS: u32 = 0x1;
 
@@ -34,30 +36,6 @@ fn test_num_f32() {
 }
 
 #[test]
-fn test_min_nan() {
-    assert_biteq!(f32::NAN.min(2.0), 2.0);
-    assert_biteq!(2.0f32.min(f32::NAN), 2.0);
-}
-
-#[test]
-fn test_max_nan() {
-    assert_biteq!(f32::NAN.max(2.0), 2.0);
-    assert_biteq!(2.0f32.max(f32::NAN), 2.0);
-}
-
-#[test]
-fn test_minimum() {
-    assert!(f32::NAN.minimum(2.0).is_nan());
-    assert!(2.0f32.minimum(f32::NAN).is_nan());
-}
-
-#[test]
-fn test_maximum() {
-    assert!(f32::NAN.maximum(2.0).is_nan());
-    assert!(2.0f32.maximum(f32::NAN).is_nan());
-}
-
-#[test]
 fn test_nan() {
     let nan: f32 = f32::NAN;
     assert!(nan.is_nan());
@@ -209,92 +187,6 @@ fn test_classify() {
 }
 
 #[test]
-fn test_floor() {
-    assert_biteq!(f32::math::floor(1.0f32), 1.0f32);
-    assert_biteq!(f32::math::floor(1.3f32), 1.0f32);
-    assert_biteq!(f32::math::floor(1.5f32), 1.0f32);
-    assert_biteq!(f32::math::floor(1.7f32), 1.0f32);
-    assert_biteq!(f32::math::floor(0.0f32), 0.0f32);
-    assert_biteq!(f32::math::floor(-0.0f32), -0.0f32);
-    assert_biteq!(f32::math::floor(-1.0f32), -1.0f32);
-    assert_biteq!(f32::math::floor(-1.3f32), -2.0f32);
-    assert_biteq!(f32::math::floor(-1.5f32), -2.0f32);
-    assert_biteq!(f32::math::floor(-1.7f32), -2.0f32);
-}
-
-#[test]
-fn test_ceil() {
-    assert_biteq!(f32::math::ceil(1.0f32), 1.0f32);
-    assert_biteq!(f32::math::ceil(1.3f32), 2.0f32);
-    assert_biteq!(f32::math::ceil(1.5f32), 2.0f32);
-    assert_biteq!(f32::math::ceil(1.7f32), 2.0f32);
-    assert_biteq!(f32::math::ceil(0.0f32), 0.0f32);
-    assert_biteq!(f32::math::ceil(-0.0f32), -0.0f32);
-    assert_biteq!(f32::math::ceil(-1.0f32), -1.0f32);
-    assert_biteq!(f32::math::ceil(-1.3f32), -1.0f32);
-    assert_biteq!(f32::math::ceil(-1.5f32), -1.0f32);
-    assert_biteq!(f32::math::ceil(-1.7f32), -1.0f32);
-}
-
-#[test]
-fn test_round() {
-    assert_biteq!(f32::math::round(2.5f32), 3.0f32);
-    assert_biteq!(f32::math::round(1.0f32), 1.0f32);
-    assert_biteq!(f32::math::round(1.3f32), 1.0f32);
-    assert_biteq!(f32::math::round(1.5f32), 2.0f32);
-    assert_biteq!(f32::math::round(1.7f32), 2.0f32);
-    assert_biteq!(f32::math::round(0.0f32), 0.0f32);
-    assert_biteq!(f32::math::round(-0.0f32), -0.0f32);
-    assert_biteq!(f32::math::round(-1.0f32), -1.0f32);
-    assert_biteq!(f32::math::round(-1.3f32), -1.0f32);
-    assert_biteq!(f32::math::round(-1.5f32), -2.0f32);
-    assert_biteq!(f32::math::round(-1.7f32), -2.0f32);
-}
-
-#[test]
-fn test_round_ties_even() {
-    assert_biteq!(f32::math::round_ties_even(2.5f32), 2.0f32);
-    assert_biteq!(f32::math::round_ties_even(1.0f32), 1.0f32);
-    assert_biteq!(f32::math::round_ties_even(1.3f32), 1.0f32);
-    assert_biteq!(f32::math::round_ties_even(1.5f32), 2.0f32);
-    assert_biteq!(f32::math::round_ties_even(1.7f32), 2.0f32);
-    assert_biteq!(f32::math::round_ties_even(0.0f32), 0.0f32);
-    assert_biteq!(f32::math::round_ties_even(-0.0f32), -0.0f32);
-    assert_biteq!(f32::math::round_ties_even(-1.0f32), -1.0f32);
-    assert_biteq!(f32::math::round_ties_even(-1.3f32), -1.0f32);
-    assert_biteq!(f32::math::round_ties_even(-1.5f32), -2.0f32);
-    assert_biteq!(f32::math::round_ties_even(-1.7f32), -2.0f32);
-}
-
-#[test]
-fn test_trunc() {
-    assert_biteq!(f32::math::trunc(1.0f32), 1.0f32);
-    assert_biteq!(f32::math::trunc(1.3f32), 1.0f32);
-    assert_biteq!(f32::math::trunc(1.5f32), 1.0f32);
-    assert_biteq!(f32::math::trunc(1.7f32), 1.0f32);
-    assert_biteq!(f32::math::trunc(0.0f32), 0.0f32);
-    assert_biteq!(f32::math::trunc(-0.0f32), -0.0f32);
-    assert_biteq!(f32::math::trunc(-1.0f32), -1.0f32);
-    assert_biteq!(f32::math::trunc(-1.3f32), -1.0f32);
-    assert_biteq!(f32::math::trunc(-1.5f32), -1.0f32);
-    assert_biteq!(f32::math::trunc(-1.7f32), -1.0f32);
-}
-
-#[test]
-fn test_fract() {
-    assert_biteq!(f32::math::fract(1.0f32), 0.0f32);
-    assert_biteq!(f32::math::fract(1.3f32), 0.29999995f32);
-    assert_biteq!(f32::math::fract(1.5f32), 0.5f32);
-    assert_biteq!(f32::math::fract(1.7f32), 0.70000005f32);
-    assert_biteq!(f32::math::fract(0.0f32), 0.0f32);
-    assert_biteq!(f32::math::fract(-0.0f32), 0.0f32);
-    assert_biteq!(f32::math::fract(-1.0f32), 0.0f32);
-    assert_biteq!(f32::math::fract(-1.3f32), -0.29999995f32);
-    assert_biteq!(f32::math::fract(-1.5f32), -0.5f32);
-    assert_biteq!(f32::math::fract(-1.7f32), -0.70000005f32);
-}
-
-#[test]
 fn test_abs() {
     assert_biteq!(f32::INFINITY.abs(), f32::INFINITY);
     assert_biteq!(1f32.abs(), 1f32);
diff --git a/library/coretests/tests/floats/f64.rs b/library/coretests/tests/floats/f64.rs
index dd5b67c251d..b2b2393a527 100644
--- a/library/coretests/tests/floats/f64.rs
+++ b/library/coretests/tests/floats/f64.rs
@@ -2,6 +2,8 @@ use core::f64;
 use core::f64::consts;
 use core::num::FpCategory as Fp;
 
+use super::{assert_approx_eq, assert_biteq};
+
 /// Smallest number
 const TINY_BITS: u64 = 0x1;
 
@@ -29,18 +31,6 @@ fn test_num_f64() {
 }
 
 #[test]
-fn test_min_nan() {
-    assert_biteq!(f64::NAN.min(2.0), 2.0);
-    assert_biteq!(2.0f64.min(f64::NAN), 2.0);
-}
-
-#[test]
-fn test_max_nan() {
-    assert_biteq!(f64::NAN.max(2.0), 2.0);
-    assert_biteq!(2.0f64.max(f64::NAN), 2.0);
-}
-
-#[test]
 fn test_nan() {
     let nan: f64 = f64::NAN;
     assert!(nan.is_nan());
@@ -191,92 +181,6 @@ fn test_classify() {
 }
 
 #[test]
-fn test_floor() {
-    assert_biteq!(f64::math::floor(1.0f64), 1.0f64);
-    assert_biteq!(f64::math::floor(1.3f64), 1.0f64);
-    assert_biteq!(f64::math::floor(1.5f64), 1.0f64);
-    assert_biteq!(f64::math::floor(1.7f64), 1.0f64);
-    assert_biteq!(f64::math::floor(0.0f64), 0.0f64);
-    assert_biteq!(f64::math::floor(-0.0f64), -0.0f64);
-    assert_biteq!(f64::math::floor(-1.0f64), -1.0f64);
-    assert_biteq!(f64::math::floor(-1.3f64), -2.0f64);
-    assert_biteq!(f64::math::floor(-1.5f64), -2.0f64);
-    assert_biteq!(f64::math::floor(-1.7f64), -2.0f64);
-}
-
-#[test]
-fn test_ceil() {
-    assert_biteq!(f64::math::ceil(1.0f64), 1.0f64);
-    assert_biteq!(f64::math::ceil(1.3f64), 2.0f64);
-    assert_biteq!(f64::math::ceil(1.5f64), 2.0f64);
-    assert_biteq!(f64::math::ceil(1.7f64), 2.0f64);
-    assert_biteq!(f64::math::ceil(0.0f64), 0.0f64);
-    assert_biteq!(f64::math::ceil(-0.0f64), -0.0f64);
-    assert_biteq!(f64::math::ceil(-1.0f64), -1.0f64);
-    assert_biteq!(f64::math::ceil(-1.3f64), -1.0f64);
-    assert_biteq!(f64::math::ceil(-1.5f64), -1.0f64);
-    assert_biteq!(f64::math::ceil(-1.7f64), -1.0f64);
-}
-
-#[test]
-fn test_round() {
-    assert_biteq!(f64::math::round(2.5f64), 3.0f64);
-    assert_biteq!(f64::math::round(1.0f64), 1.0f64);
-    assert_biteq!(f64::math::round(1.3f64), 1.0f64);
-    assert_biteq!(f64::math::round(1.5f64), 2.0f64);
-    assert_biteq!(f64::math::round(1.7f64), 2.0f64);
-    assert_biteq!(f64::math::round(0.0f64), 0.0f64);
-    assert_biteq!(f64::math::round(-0.0f64), -0.0f64);
-    assert_biteq!(f64::math::round(-1.0f64), -1.0f64);
-    assert_biteq!(f64::math::round(-1.3f64), -1.0f64);
-    assert_biteq!(f64::math::round(-1.5f64), -2.0f64);
-    assert_biteq!(f64::math::round(-1.7f64), -2.0f64);
-}
-
-#[test]
-fn test_round_ties_even() {
-    assert_biteq!(f64::math::round_ties_even(2.5f64), 2.0f64);
-    assert_biteq!(f64::math::round_ties_even(1.0f64), 1.0f64);
-    assert_biteq!(f64::math::round_ties_even(1.3f64), 1.0f64);
-    assert_biteq!(f64::math::round_ties_even(1.5f64), 2.0f64);
-    assert_biteq!(f64::math::round_ties_even(1.7f64), 2.0f64);
-    assert_biteq!(f64::math::round_ties_even(0.0f64), 0.0f64);
-    assert_biteq!(f64::math::round_ties_even(-0.0f64), -0.0f64);
-    assert_biteq!(f64::math::round_ties_even(-1.0f64), -1.0f64);
-    assert_biteq!(f64::math::round_ties_even(-1.3f64), -1.0f64);
-    assert_biteq!(f64::math::round_ties_even(-1.5f64), -2.0f64);
-    assert_biteq!(f64::math::round_ties_even(-1.7f64), -2.0f64);
-}
-
-#[test]
-fn test_trunc() {
-    assert_biteq!(f64::math::trunc(1.0f64), 1.0f64);
-    assert_biteq!(f64::math::trunc(1.3f64), 1.0f64);
-    assert_biteq!(f64::math::trunc(1.5f64), 1.0f64);
-    assert_biteq!(f64::math::trunc(1.7f64), 1.0f64);
-    assert_biteq!(f64::math::trunc(0.0f64), 0.0f64);
-    assert_biteq!(f64::math::trunc(-0.0f64), -0.0f64);
-    assert_biteq!(f64::math::trunc(-1.0f64), -1.0f64);
-    assert_biteq!(f64::math::trunc(-1.3f64), -1.0f64);
-    assert_biteq!(f64::math::trunc(-1.5f64), -1.0f64);
-    assert_biteq!(f64::math::trunc(-1.7f64), -1.0f64);
-}
-
-#[test]
-fn test_fract() {
-    assert_biteq!(f64::math::fract(1.0f64), 0.0f64);
-    assert_biteq!(f64::math::fract(1.3f64), 0.30000000000000004f64);
-    assert_biteq!(f64::math::fract(1.5f64), 0.5f64);
-    assert_biteq!(f64::math::fract(1.7f64), 0.7f64);
-    assert_biteq!(f64::math::fract(0.0f64), 0.0f64);
-    assert_biteq!(f64::math::fract(-0.0f64), 0.0f64);
-    assert_biteq!(f64::math::fract(-1.0f64), 0.0f64);
-    assert_biteq!(f64::math::fract(-1.3f64), -0.30000000000000004f64);
-    assert_biteq!(f64::math::fract(-1.5f64), -0.5f64);
-    assert_biteq!(f64::math::fract(-1.7f64), -0.69999999999999996f64);
-}
-
-#[test]
 fn test_abs() {
     assert_biteq!(f64::INFINITY.abs(), f64::INFINITY);
     assert_biteq!(1f64.abs(), 1f64);
diff --git a/library/coretests/tests/floats/mod.rs b/library/coretests/tests/floats/mod.rs
index f9b6c85f871..6b4f586fa9b 100644
--- a/library/coretests/tests/floats/mod.rs
+++ b/library/coretests/tests/floats/mod.rs
@@ -1,9 +1,34 @@
 use std::fmt;
 use std::ops::{Add, Div, Mul, Rem, Sub};
 
-/// Verify that floats are within a tolerance of each other, 1.0e-6 by default.
-macro_rules! assert_approx_eq {
-    ($a:expr, $b:expr) => {{ assert_approx_eq!($a, $b, 1.0e-6) }};
+/// Set the default tolerance for float comparison based on the type.
+trait Approx {
+    const LIM: Self;
+}
+
+impl Approx for f16 {
+    const LIM: Self = 1e-3;
+}
+impl Approx for f32 {
+    const LIM: Self = 1e-6;
+}
+impl Approx for f64 {
+    const LIM: Self = 1e-6;
+}
+impl Approx for f128 {
+    const LIM: Self = 1e-9;
+}
+
+/// Determine the tolerance for values of the argument type.
+const fn lim_for_ty<T: Approx + Copy>(_x: T) -> T {
+    T::LIM
+}
+
+// We have runtime ("rt") and const versions of these macros.
+
+/// Verify that floats are within a tolerance of each other.
+macro_rules! assert_approx_eq_rt {
+    ($a:expr, $b:expr) => {{ assert_approx_eq_rt!($a, $b, $crate::floats::lim_for_ty($a)) }};
     ($a:expr, $b:expr, $lim:expr) => {{
         let (a, b) = (&$a, &$b);
         let diff = (*a - *b).abs();
@@ -14,10 +39,18 @@ macro_rules! assert_approx_eq {
         );
     }};
 }
+macro_rules! assert_approx_eq_const {
+    ($a:expr, $b:expr) => {{ assert_approx_eq_const!($a, $b, $crate::floats::lim_for_ty($a)) }};
+    ($a:expr, $b:expr, $lim:expr) => {{
+        let (a, b) = (&$a, &$b);
+        let diff = (*a - *b).abs();
+        assert!(diff <= $lim);
+    }};
+}
 
 /// Verify that floats have the same bitwise representation. Used to avoid the default `0.0 == -0.0`
 /// behavior, as well as to ensure exact NaN bitpatterns.
-macro_rules! assert_biteq {
+macro_rules! assert_biteq_rt {
     (@inner $left:expr, $right:expr, $msg_sep:literal, $($tt:tt)*) => {{
         let l = $left;
         let r = $right;
@@ -41,32 +74,50 @@ macro_rules! assert_biteq {
         if !l.is_nan() && !r.is_nan() {
             // Also check that standard equality holds, since most tests use `assert_biteq` rather
             // than `assert_eq`.
-            assert_eq!(l, r)
+            assert_eq!(l, r);
         }
     }};
     ($left:expr, $right:expr , $($tt:tt)*) => {
-        assert_biteq!(@inner $left, $right, "\n", $($tt)*)
+        assert_biteq_rt!(@inner $left, $right, "\n", $($tt)*)
     };
     ($left:expr, $right:expr $(,)?) => {
-        assert_biteq!(@inner $left, $right, "", "")
+        assert_biteq_rt!(@inner $left, $right, "", "")
     };
 }
+macro_rules! assert_biteq_const {
+    (@inner $left:expr, $right:expr, $msg_sep:literal, $($tt:tt)*) => {{
+        let l = $left;
+        let r = $right;
 
-mod const_asserts {
-    // Shadow some assert implementations that would otherwise not compile in a const-context.
-    // Every macro added here also needs to be added in the `float_test!` macro below.
-    macro_rules! assert_eq {
-        ($left:expr, $right:expr $(,)?) => {
-            std::assert!($left == $right)
-        };
-        ($left:expr, $right:expr, $($arg:tt)+) => {
-            std::assert!($left == $right, $($arg)+)
-        };
-    }
+        // Hack to coerce left and right to the same type
+        let mut _eq_ty = l;
+        _eq_ty = r;
+
+        assert!(l.to_bits() == r.to_bits());
 
-    pub(crate) use assert_eq;
+        if !l.is_nan() && !r.is_nan() {
+            // Also check that standard equality holds, since most tests use `assert_biteq` rather
+            // than `assert_eq`.
+            assert!(l == r);
+        }
+    }};
+    ($left:expr, $right:expr , $($tt:tt)*) => {
+        assert_biteq_const!(@inner $left, $right, "\n", $($tt)*)
+    };
+    ($left:expr, $right:expr $(,)?) => {
+        assert_biteq_const!(@inner $left, $right, "", "")
+    };
 }
 
+// Use the runtime version by default.
+// This way, they can be shadowed by the const versions.
+pub(crate) use {assert_approx_eq_rt as assert_approx_eq, assert_biteq_rt as assert_biteq};
+
+// Also make the const version available for re-exports.
+#[rustfmt::skip]
+pub(crate) use assert_biteq_const;
+pub(crate) use assert_approx_eq_const;
+
 /// Generate float tests for all our float types, for compile-time and run-time behavior.
 ///
 /// By default all tests run for all float types. Configuration can be applied via `attrs`.
@@ -84,6 +135,7 @@ mod const_asserts {
 ///         /* write tests here, using `Float` as the type */
 ///     }
 /// }
+/// ```
 macro_rules! float_test {
     (
         name: $name:ident,
@@ -101,6 +153,8 @@ macro_rules! float_test {
         test<$fty:ident> $test:block
     ) => {
         mod $name {
+            use super::*;
+
             #[test]
             $( $( #[$f16_meta] )+ )?
             fn test_f16() {
@@ -131,7 +185,14 @@ macro_rules! float_test {
 
             $( $( #[$const_meta] )+ )?
             mod const_ {
-                use $crate::floats::const_asserts::assert_eq;
+                #[allow(unused)]
+                use super::Approx;
+                // Shadow the runtime versions of the macro with const-compatible versions.
+                #[allow(unused)]
+                use $crate::floats::{
+                    assert_approx_eq_const as assert_approx_eq,
+                    assert_biteq_const as assert_biteq,
+                };
 
                 #[test]
                 $( $( #[$f16_const_meta] )+ )?
@@ -196,29 +257,25 @@ float_test! {
         f128: #[cfg(any(miri, target_has_reliable_f128_math))],
     },
     test<Float> {
-        assert_eq!((0.0 as Float).min(0.0), 0.0);
-        assert!((0.0 as Float).min(0.0).is_sign_positive());
-        assert_eq!((-0.0 as Float).min(-0.0), -0.0);
-        assert!((-0.0 as Float).min(-0.0).is_sign_negative());
-        assert_eq!((9.0 as Float).min(9.0), 9.0);
-        assert_eq!((-9.0 as Float).min(0.0), -9.0);
-        assert_eq!((0.0 as Float).min(9.0), 0.0);
-        assert!((0.0 as Float).min(9.0).is_sign_positive());
-        assert_eq!((-0.0 as Float).min(9.0), -0.0);
-        assert!((-0.0 as Float).min(9.0).is_sign_negative());
-        assert_eq!((-0.0 as Float).min(-9.0), -9.0);
-        assert_eq!(Float::INFINITY.min(9.0), 9.0);
-        assert_eq!((9.0 as Float).min(Float::INFINITY), 9.0);
-        assert_eq!(Float::INFINITY.min(-9.0), -9.0);
-        assert_eq!((-9.0 as Float).min(Float::INFINITY), -9.0);
-        assert_eq!(Float::NEG_INFINITY.min(9.0), Float::NEG_INFINITY);
-        assert_eq!((9.0 as Float).min(Float::NEG_INFINITY), Float::NEG_INFINITY);
-        assert_eq!(Float::NEG_INFINITY.min(-9.0), Float::NEG_INFINITY);
-        assert_eq!((-9.0 as Float).min(Float::NEG_INFINITY), Float::NEG_INFINITY);
-        assert_eq!(Float::NAN.min(9.0), 9.0);
-        assert_eq!(Float::NAN.min(-9.0), -9.0);
-        assert_eq!((9.0 as Float).min(Float::NAN), 9.0);
-        assert_eq!((-9.0 as Float).min(Float::NAN), -9.0);
+        assert_biteq!((0.0 as Float).min(0.0), 0.0);
+        assert_biteq!((-0.0 as Float).min(-0.0), -0.0);
+        assert_biteq!((9.0 as Float).min(9.0), 9.0);
+        assert_biteq!((-9.0 as Float).min(0.0), -9.0);
+        assert_biteq!((0.0 as Float).min(9.0), 0.0);
+        assert_biteq!((-0.0 as Float).min(9.0), -0.0);
+        assert_biteq!((-0.0 as Float).min(-9.0), -9.0);
+        assert_biteq!(Float::INFINITY.min(9.0), 9.0);
+        assert_biteq!((9.0 as Float).min(Float::INFINITY), 9.0);
+        assert_biteq!(Float::INFINITY.min(-9.0), -9.0);
+        assert_biteq!((-9.0 as Float).min(Float::INFINITY), -9.0);
+        assert_biteq!(Float::NEG_INFINITY.min(9.0), Float::NEG_INFINITY);
+        assert_biteq!((9.0 as Float).min(Float::NEG_INFINITY), Float::NEG_INFINITY);
+        assert_biteq!(Float::NEG_INFINITY.min(-9.0), Float::NEG_INFINITY);
+        assert_biteq!((-9.0 as Float).min(Float::NEG_INFINITY), Float::NEG_INFINITY);
+        assert_biteq!(Float::NAN.min(9.0), 9.0);
+        assert_biteq!(Float::NAN.min(-9.0), -9.0);
+        assert_biteq!((9.0 as Float).min(Float::NAN), 9.0);
+        assert_biteq!((-9.0 as Float).min(Float::NAN), -9.0);
         assert!(Float::NAN.min(Float::NAN).is_nan());
     }
 }
@@ -230,32 +287,26 @@ float_test! {
         f128: #[cfg(any(miri, target_has_reliable_f128_math))],
     },
     test<Float> {
-        assert_eq!((0.0 as Float).max(0.0), 0.0);
-        assert!((0.0 as Float).max(0.0).is_sign_positive());
-        assert_eq!((-0.0 as Float).max(-0.0), -0.0);
-        assert!((-0.0 as Float).max(-0.0).is_sign_negative());
-        assert_eq!((9.0 as Float).max(9.0), 9.0);
-        assert_eq!((-9.0 as Float).max(0.0), 0.0);
-        assert!((-9.0 as Float).max(0.0).is_sign_positive());
-        assert_eq!((-9.0 as Float).max(-0.0), -0.0);
-        assert!((-9.0 as Float).max(-0.0).is_sign_negative());
-        assert_eq!((0.0 as Float).max(9.0), 9.0);
-        assert_eq!((0.0 as Float).max(-9.0), 0.0);
-        assert!((0.0 as Float).max(-9.0).is_sign_positive());
-        assert_eq!((-0.0 as Float).max(-9.0), -0.0);
-        assert!((-0.0 as Float).max(-9.0).is_sign_negative());
-        assert_eq!(Float::INFINITY.max(9.0), Float::INFINITY);
-        assert_eq!((9.0 as Float).max(Float::INFINITY), Float::INFINITY);
-        assert_eq!(Float::INFINITY.max(-9.0), Float::INFINITY);
-        assert_eq!((-9.0 as Float).max(Float::INFINITY), Float::INFINITY);
-        assert_eq!(Float::NEG_INFINITY.max(9.0), 9.0);
-        assert_eq!((9.0 as Float).max(Float::NEG_INFINITY), 9.0);
-        assert_eq!(Float::NEG_INFINITY.max(-9.0), -9.0);
-        assert_eq!((-9.0 as Float).max(Float::NEG_INFINITY), -9.0);
-        assert_eq!(Float::NAN.max(9.0), 9.0);
-        assert_eq!(Float::NAN.max(-9.0), -9.0);
-        assert_eq!((9.0 as Float).max(Float::NAN), 9.0);
-        assert_eq!((-9.0 as Float).max(Float::NAN), -9.0);
+        assert_biteq!((0.0 as Float).max(0.0), 0.0);
+        assert_biteq!((-0.0 as Float).max(-0.0), -0.0);
+        assert_biteq!((9.0 as Float).max(9.0), 9.0);
+        assert_biteq!((-9.0 as Float).max(0.0), 0.0);
+        assert_biteq!((-9.0 as Float).max(-0.0), -0.0);
+        assert_biteq!((0.0 as Float).max(9.0), 9.0);
+        assert_biteq!((0.0 as Float).max(-9.0), 0.0);
+        assert_biteq!((-0.0 as Float).max(-9.0), -0.0);
+        assert_biteq!(Float::INFINITY.max(9.0), Float::INFINITY);
+        assert_biteq!((9.0 as Float).max(Float::INFINITY), Float::INFINITY);
+        assert_biteq!(Float::INFINITY.max(-9.0), Float::INFINITY);
+        assert_biteq!((-9.0 as Float).max(Float::INFINITY), Float::INFINITY);
+        assert_biteq!(Float::NEG_INFINITY.max(9.0), 9.0);
+        assert_biteq!((9.0 as Float).max(Float::NEG_INFINITY), 9.0);
+        assert_biteq!(Float::NEG_INFINITY.max(-9.0), -9.0);
+        assert_biteq!((-9.0 as Float).max(Float::NEG_INFINITY), -9.0);
+        assert_biteq!(Float::NAN.max(9.0), 9.0);
+        assert_biteq!(Float::NAN.max(-9.0), -9.0);
+        assert_biteq!((9.0 as Float).max(Float::NAN), 9.0);
+        assert_biteq!((-9.0 as Float).max(Float::NAN), -9.0);
         assert!(Float::NAN.max(Float::NAN).is_nan());
     }
 }
@@ -267,27 +318,22 @@ float_test! {
         f128: #[cfg(any(miri, target_has_reliable_f128_math))],
     },
     test<Float> {
-        assert_eq!((0.0 as Float).minimum(0.0), 0.0);
-        assert!((0.0 as Float).minimum(0.0).is_sign_positive());
-        assert_eq!((-0.0 as Float).minimum(0.0), -0.0);
-        assert!((-0.0 as Float).minimum(0.0).is_sign_negative());
-        assert_eq!((-0.0 as Float).minimum(-0.0), -0.0);
-        assert!((-0.0 as Float).minimum(-0.0).is_sign_negative());
-        assert_eq!((9.0 as Float).minimum(9.0), 9.0);
-        assert_eq!((-9.0 as Float).minimum(0.0), -9.0);
-        assert_eq!((0.0 as Float).minimum(9.0), 0.0);
-        assert!((0.0 as Float).minimum(9.0).is_sign_positive());
-        assert_eq!((-0.0 as Float).minimum(9.0), -0.0);
-        assert!((-0.0 as Float).minimum(9.0).is_sign_negative());
-        assert_eq!((-0.0 as Float).minimum(-9.0), -9.0);
-        assert_eq!(Float::INFINITY.minimum(9.0), 9.0);
-        assert_eq!((9.0 as Float).minimum(Float::INFINITY), 9.0);
-        assert_eq!(Float::INFINITY.minimum(-9.0), -9.0);
-        assert_eq!((-9.0 as Float).minimum(Float::INFINITY), -9.0);
-        assert_eq!(Float::NEG_INFINITY.minimum(9.0), Float::NEG_INFINITY);
-        assert_eq!((9.0 as Float).minimum(Float::NEG_INFINITY), Float::NEG_INFINITY);
-        assert_eq!(Float::NEG_INFINITY.minimum(-9.0), Float::NEG_INFINITY);
-        assert_eq!((-9.0 as Float).minimum(Float::NEG_INFINITY), Float::NEG_INFINITY);
+        assert_biteq!((0.0 as Float).minimum(0.0), 0.0);
+        assert_biteq!((-0.0 as Float).minimum(0.0), -0.0);
+        assert_biteq!((-0.0 as Float).minimum(-0.0), -0.0);
+        assert_biteq!((9.0 as Float).minimum(9.0), 9.0);
+        assert_biteq!((-9.0 as Float).minimum(0.0), -9.0);
+        assert_biteq!((0.0 as Float).minimum(9.0), 0.0);
+        assert_biteq!((-0.0 as Float).minimum(9.0), -0.0);
+        assert_biteq!((-0.0 as Float).minimum(-9.0), -9.0);
+        assert_biteq!(Float::INFINITY.minimum(9.0), 9.0);
+        assert_biteq!((9.0 as Float).minimum(Float::INFINITY), 9.0);
+        assert_biteq!(Float::INFINITY.minimum(-9.0), -9.0);
+        assert_biteq!((-9.0 as Float).minimum(Float::INFINITY), -9.0);
+        assert_biteq!(Float::NEG_INFINITY.minimum(9.0), Float::NEG_INFINITY);
+        assert_biteq!((9.0 as Float).minimum(Float::NEG_INFINITY), Float::NEG_INFINITY);
+        assert_biteq!(Float::NEG_INFINITY.minimum(-9.0), Float::NEG_INFINITY);
+        assert_biteq!((-9.0 as Float).minimum(Float::NEG_INFINITY), Float::NEG_INFINITY);
         assert!(Float::NAN.minimum(9.0).is_nan());
         assert!(Float::NAN.minimum(-9.0).is_nan());
         assert!((9.0 as Float).minimum(Float::NAN).is_nan());
@@ -303,30 +349,23 @@ float_test! {
         f128: #[cfg(any(miri, target_has_reliable_f128_math))],
     },
     test<Float> {
-        assert_eq!((0.0 as Float).maximum(0.0), 0.0);
-        assert!((0.0 as Float).maximum(0.0).is_sign_positive());
-        assert_eq!((-0.0 as Float).maximum(0.0), 0.0);
-        assert!((-0.0 as Float).maximum(0.0).is_sign_positive());
-        assert_eq!((-0.0 as Float).maximum(-0.0), -0.0);
-        assert!((-0.0 as Float).maximum(-0.0).is_sign_negative());
-        assert_eq!((9.0 as Float).maximum(9.0), 9.0);
-        assert_eq!((-9.0 as Float).maximum(0.0), 0.0);
-        assert!((-9.0 as Float).maximum(0.0).is_sign_positive());
-        assert_eq!((-9.0 as Float).maximum(-0.0), -0.0);
-        assert!((-9.0 as Float).maximum(-0.0).is_sign_negative());
-        assert_eq!((0.0 as Float).maximum(9.0), 9.0);
-        assert_eq!((0.0 as Float).maximum(-9.0), 0.0);
-        assert!((0.0 as Float).maximum(-9.0).is_sign_positive());
-        assert_eq!((-0.0 as Float).maximum(-9.0), -0.0);
-        assert!((-0.0 as Float).maximum(-9.0).is_sign_negative());
-        assert_eq!(Float::INFINITY.maximum(9.0), Float::INFINITY);
-        assert_eq!((9.0 as Float).maximum(Float::INFINITY), Float::INFINITY);
-        assert_eq!(Float::INFINITY.maximum(-9.0), Float::INFINITY);
-        assert_eq!((-9.0 as Float).maximum(Float::INFINITY), Float::INFINITY);
-        assert_eq!(Float::NEG_INFINITY.maximum(9.0), 9.0);
-        assert_eq!((9.0 as Float).maximum(Float::NEG_INFINITY), 9.0);
-        assert_eq!(Float::NEG_INFINITY.maximum(-9.0), -9.0);
-        assert_eq!((-9.0 as Float).maximum(Float::NEG_INFINITY), -9.0);
+        assert_biteq!((0.0 as Float).maximum(0.0), 0.0);
+        assert_biteq!((-0.0 as Float).maximum(0.0), 0.0);
+        assert_biteq!((-0.0 as Float).maximum(-0.0), -0.0);
+        assert_biteq!((9.0 as Float).maximum(9.0), 9.0);
+        assert_biteq!((-9.0 as Float).maximum(0.0), 0.0);
+        assert_biteq!((-9.0 as Float).maximum(-0.0), -0.0);
+        assert_biteq!((0.0 as Float).maximum(9.0), 9.0);
+        assert_biteq!((0.0 as Float).maximum(-9.0), 0.0);
+        assert_biteq!((-0.0 as Float).maximum(-9.0), -0.0);
+        assert_biteq!(Float::INFINITY.maximum(9.0), Float::INFINITY);
+        assert_biteq!((9.0 as Float).maximum(Float::INFINITY), Float::INFINITY);
+        assert_biteq!(Float::INFINITY.maximum(-9.0), Float::INFINITY);
+        assert_biteq!((-9.0 as Float).maximum(Float::INFINITY), Float::INFINITY);
+        assert_biteq!(Float::NEG_INFINITY.maximum(9.0), 9.0);
+        assert_biteq!((9.0 as Float).maximum(Float::NEG_INFINITY), 9.0);
+        assert_biteq!(Float::NEG_INFINITY.maximum(-9.0), -9.0);
+        assert_biteq!((-9.0 as Float).maximum(Float::NEG_INFINITY), -9.0);
         assert!(Float::NAN.maximum(9.0).is_nan());
         assert!(Float::NAN.maximum(-9.0).is_nan());
         assert!((9.0 as Float).maximum(Float::NAN).is_nan());
@@ -342,41 +381,43 @@ float_test! {
         f128: #[cfg(any(miri, target_has_reliable_f128_math))],
     },
     test<Float> {
-        assert_eq!((0.5 as Float).midpoint(0.5), 0.5);
-        assert_eq!((0.5 as Float).midpoint(2.5), 1.5);
-        assert_eq!((3.0 as Float).midpoint(4.0), 3.5);
-        assert_eq!((-3.0 as Float).midpoint(4.0), 0.5);
-        assert_eq!((3.0 as Float).midpoint(-4.0), -0.5);
-        assert_eq!((-3.0 as Float).midpoint(-4.0), -3.5);
-        assert_eq!((0.0 as Float).midpoint(0.0), 0.0);
-        assert_eq!((-0.0 as Float).midpoint(-0.0), -0.0);
-        assert_eq!((-5.0 as Float).midpoint(5.0), 0.0);
-        assert_eq!(Float::MAX.midpoint(Float::MIN), 0.0);
-        assert_eq!(Float::MIN.midpoint(Float::MAX), -0.0);
-        assert_eq!(Float::MAX.midpoint(Float::MIN_POSITIVE), Float::MAX / 2.);
-        assert_eq!((-Float::MAX).midpoint(Float::MIN_POSITIVE), -Float::MAX / 2.);
-        assert_eq!(Float::MAX.midpoint(-Float::MIN_POSITIVE), Float::MAX / 2.);
-        assert_eq!((-Float::MAX).midpoint(-Float::MIN_POSITIVE), -Float::MAX / 2.);
-        assert_eq!((Float::MIN_POSITIVE).midpoint(Float::MAX), Float::MAX / 2.);
-        assert_eq!((Float::MIN_POSITIVE).midpoint(-Float::MAX), -Float::MAX / 2.);
-        assert_eq!((-Float::MIN_POSITIVE).midpoint(Float::MAX), Float::MAX / 2.);
-        assert_eq!((-Float::MIN_POSITIVE).midpoint(-Float::MAX), -Float::MAX / 2.);
-        assert_eq!(Float::MAX.midpoint(Float::MAX), Float::MAX);
-        assert_eq!(
+        assert_biteq!((0.5 as Float).midpoint(0.5), 0.5);
+        assert_biteq!((0.5 as Float).midpoint(2.5), 1.5);
+        assert_biteq!((3.0 as Float).midpoint(4.0), 3.5);
+        assert_biteq!((-3.0 as Float).midpoint(4.0), 0.5);
+        assert_biteq!((3.0 as Float).midpoint(-4.0), -0.5);
+        assert_biteq!((-3.0 as Float).midpoint(-4.0), -3.5);
+        assert_biteq!((0.0 as Float).midpoint(0.0), 0.0);
+        assert_biteq!((-0.0 as Float).midpoint(-0.0), -0.0);
+        assert_biteq!((-5.0 as Float).midpoint(5.0), 0.0);
+        assert_biteq!(Float::MAX.midpoint(Float::MIN), 0.0);
+        assert_biteq!(Float::MIN.midpoint(Float::MAX), 0.0);
+        assert_biteq!(Float::MAX.midpoint(Float::MIN_POSITIVE), Float::MAX / 2.);
+        assert_biteq!((-Float::MAX).midpoint(Float::MIN_POSITIVE), -Float::MAX / 2.);
+        assert_biteq!(Float::MAX.midpoint(-Float::MIN_POSITIVE), Float::MAX / 2.);
+        assert_biteq!((-Float::MAX).midpoint(-Float::MIN_POSITIVE), -Float::MAX / 2.);
+        assert_biteq!((Float::MIN_POSITIVE).midpoint(Float::MAX), Float::MAX / 2.);
+        assert_biteq!((Float::MIN_POSITIVE).midpoint(-Float::MAX), -Float::MAX / 2.);
+        assert_biteq!((-Float::MIN_POSITIVE).midpoint(Float::MAX), Float::MAX / 2.);
+        assert_biteq!((-Float::MIN_POSITIVE).midpoint(-Float::MAX), -Float::MAX / 2.);
+        assert_biteq!(Float::MAX.midpoint(Float::MAX), Float::MAX);
+        assert_biteq!(
             (Float::MIN_POSITIVE).midpoint(Float::MIN_POSITIVE),
             Float::MIN_POSITIVE
         );
-        assert_eq!(
+        assert_biteq!(
             (-Float::MIN_POSITIVE).midpoint(-Float::MIN_POSITIVE),
             -Float::MIN_POSITIVE
         );
-        assert_eq!(Float::MAX.midpoint(5.0), Float::MAX / 2.0 + 2.5);
-        assert_eq!(Float::MAX.midpoint(-5.0), Float::MAX / 2.0 - 2.5);
-        assert_eq!(Float::INFINITY.midpoint(Float::INFINITY), Float::INFINITY);
-        assert_eq!(
+        assert_biteq!(Float::MAX.midpoint(5.0), Float::MAX / 2.0 + 2.5);
+        assert_biteq!(Float::MAX.midpoint(-5.0), Float::MAX / 2.0 - 2.5);
+        assert_biteq!(Float::INFINITY.midpoint(Float::INFINITY), Float::INFINITY);
+        assert_biteq!(
             Float::NEG_INFINITY.midpoint(Float::NEG_INFINITY),
             Float::NEG_INFINITY
         );
+        assert!(Float::NEG_INFINITY.midpoint(Float::INFINITY).is_nan());
+        assert!(Float::INFINITY.midpoint(Float::NEG_INFINITY).is_nan());
         assert!(Float::NAN.midpoint(1.0).is_nan());
         assert!((1.0 as Float).midpoint(Float::NAN).is_nan());
         assert!(Float::NAN.midpoint(Float::NAN).is_nan());
@@ -410,7 +451,7 @@ float_test! {
                 let naive = (large + small) / 2.0;
                 let midpoint = large.midpoint(small);
 
-                assert_eq!(naive, midpoint);
+                assert_biteq!(naive, midpoint);
             }
         }
     }
@@ -423,10 +464,10 @@ float_test! {
         f128: #[cfg(any(miri, target_has_reliable_f128_math))],
     },
     test<Float> {
-        assert_eq!((-1.0 as Float).abs(), 1.0);
-        assert_eq!((1.0 as Float).abs(), 1.0);
-        assert_eq!(Float::NEG_INFINITY.abs(), Float::INFINITY);
-        assert_eq!(Float::INFINITY.abs(), Float::INFINITY);
+        assert_biteq!((-1.0 as Float).abs(), 1.0);
+        assert_biteq!((1.0 as Float).abs(), 1.0);
+        assert_biteq!(Float::NEG_INFINITY.abs(), Float::INFINITY);
+        assert_biteq!(Float::INFINITY.abs(), Float::INFINITY);
     }
 }
 
@@ -437,10 +478,10 @@ float_test! {
         f128: #[cfg(any(miri, target_has_reliable_f128_math))],
     },
     test<Float> {
-        assert_eq!((1.0 as Float).copysign(-2.0), -1.0);
-        assert_eq!((-1.0 as Float).copysign(2.0), 1.0);
-        assert_eq!(Float::INFINITY.copysign(-0.0), Float::NEG_INFINITY);
-        assert_eq!(Float::NEG_INFINITY.copysign(0.0), Float::INFINITY);
+        assert_biteq!((1.0 as Float).copysign(-2.0), -1.0);
+        assert_biteq!((-1.0 as Float).copysign(2.0), 1.0);
+        assert_biteq!(Float::INFINITY.copysign(-0.0), Float::NEG_INFINITY);
+        assert_biteq!(Float::NEG_INFINITY.copysign(0.0), Float::INFINITY);
     }
 }
 
@@ -453,7 +494,7 @@ float_test! {
     },
     test<Float> {
         assert!(Float::INFINITY.rem_euclid(42.0 as Float).is_nan());
-        assert_eq!((42.0 as Float).rem_euclid(Float::INFINITY), (42.0 as Float));
+        assert_biteq!((42.0 as Float).rem_euclid(Float::INFINITY), 42.0 as Float);
         assert!((42.0 as Float).rem_euclid(Float::NAN).is_nan());
         assert!(Float::INFINITY.rem_euclid(Float::INFINITY).is_nan());
         assert!(Float::INFINITY.rem_euclid(Float::NAN).is_nan());
@@ -469,7 +510,7 @@ float_test! {
         f128: #[cfg(any(miri, target_has_reliable_f128_math))],
     },
     test<Float> {
-        assert_eq!((42.0 as Float).div_euclid(Float::INFINITY), 0.0);
+        assert_biteq!((42.0 as Float).div_euclid(Float::INFINITY), 0.0);
         assert!((42.0 as Float).div_euclid(Float::NAN).is_nan());
         assert!(Float::INFINITY.div_euclid(Float::INFINITY).is_nan());
         assert!(Float::INFINITY.div_euclid(Float::NAN).is_nan());
@@ -484,20 +525,25 @@ float_test! {
         f128: #[cfg(any(miri, target_has_reliable_f128_math))],
     },
     test<Float> {
-        assert_eq!((0.0 as Float).floor(), 0.0);
-        assert!((0.0 as Float).floor().is_sign_positive());
-        assert_eq!((-0.0 as Float).floor(), -0.0);
-        assert!((-0.0 as Float).floor().is_sign_negative());
-        assert_eq!((0.5 as Float).floor(), 0.0);
-        assert_eq!((-0.5 as Float).floor(), -1.0);
-        assert_eq!((1.5 as Float).floor(), 1.0);
-        assert_eq!(Float::MAX.floor(), Float::MAX);
-        assert_eq!(Float::MIN.floor(), Float::MIN);
-        assert_eq!(Float::MIN_POSITIVE.floor(), 0.0);
-        assert_eq!((-Float::MIN_POSITIVE).floor(), -1.0);
+        assert_biteq!((1.0 as Float).floor(), 1.0);
+        assert_biteq!((1.3 as Float).floor(), 1.0);
+        assert_biteq!((1.5 as Float).floor(), 1.0);
+        assert_biteq!((1.7 as Float).floor(), 1.0);
+        assert_biteq!((0.5 as Float).floor(), 0.0);
+        assert_biteq!((0.0 as Float).floor(), 0.0);
+        assert_biteq!((-0.0 as Float).floor(), -0.0);
+        assert_biteq!((-0.5 as Float).floor(), -1.0);
+        assert_biteq!((-1.0 as Float).floor(), -1.0);
+        assert_biteq!((-1.3 as Float).floor(), -2.0);
+        assert_biteq!((-1.5 as Float).floor(), -2.0);
+        assert_biteq!((-1.7 as Float).floor(), -2.0);
+        assert_biteq!(Float::MAX.floor(), Float::MAX);
+        assert_biteq!(Float::MIN.floor(), Float::MIN);
+        assert_biteq!(Float::MIN_POSITIVE.floor(), 0.0);
+        assert_biteq!((-Float::MIN_POSITIVE).floor(), -1.0);
         assert!(Float::NAN.floor().is_nan());
-        assert_eq!(Float::INFINITY.floor(), Float::INFINITY);
-        assert_eq!(Float::NEG_INFINITY.floor(), Float::NEG_INFINITY);
+        assert_biteq!(Float::INFINITY.floor(), Float::INFINITY);
+        assert_biteq!(Float::NEG_INFINITY.floor(), Float::NEG_INFINITY);
     }
 }
 
@@ -508,19 +554,25 @@ float_test! {
         f128: #[cfg(any(miri, target_has_reliable_f128_math))],
     },
     test<Float> {
-        assert_eq!((0.0 as Float).ceil(), 0.0);
-        assert!((0.0 as Float).ceil().is_sign_positive());
-        assert_eq!((-0.0 as Float).ceil(), 0.0);
-        assert!((-0.0 as Float).ceil().is_sign_negative());
-        assert_eq!((0.5 as Float).ceil(), 1.0);
-        assert_eq!((-0.5 as Float).ceil(), 0.0);
-        assert_eq!(Float::MAX.ceil(), Float::MAX);
-        assert_eq!(Float::MIN.ceil(), Float::MIN);
-        assert_eq!(Float::MIN_POSITIVE.ceil(), 1.0);
-        assert_eq!((-Float::MIN_POSITIVE).ceil(), 0.0);
+        assert_biteq!((1.0 as Float).ceil(), 1.0);
+        assert_biteq!((1.3 as Float).ceil(), 2.0);
+        assert_biteq!((1.5 as Float).ceil(), 2.0);
+        assert_biteq!((1.7 as Float).ceil(), 2.0);
+        assert_biteq!((0.5 as Float).ceil(), 1.0);
+        assert_biteq!((0.0 as Float).ceil(), 0.0);
+        assert_biteq!((-0.0 as Float).ceil(), -0.0);
+        assert_biteq!((-0.5 as Float).ceil(), -0.0);
+        assert_biteq!((-1.0 as Float).ceil(), -1.0);
+        assert_biteq!((-1.3 as Float).ceil(), -1.0);
+        assert_biteq!((-1.5 as Float).ceil(), -1.0);
+        assert_biteq!((-1.7 as Float).ceil(), -1.0);
+        assert_biteq!(Float::MAX.ceil(), Float::MAX);
+        assert_biteq!(Float::MIN.ceil(), Float::MIN);
+        assert_biteq!(Float::MIN_POSITIVE.ceil(), 1.0);
+        assert_biteq!((-Float::MIN_POSITIVE).ceil(), -0.0);
         assert!(Float::NAN.ceil().is_nan());
-        assert_eq!(Float::INFINITY.ceil(), Float::INFINITY);
-        assert_eq!(Float::NEG_INFINITY.ceil(), Float::NEG_INFINITY);
+        assert_biteq!(Float::INFINITY.ceil(), Float::INFINITY);
+        assert_biteq!(Float::NEG_INFINITY.ceil(), Float::NEG_INFINITY);
     }
 }
 
@@ -531,19 +583,26 @@ float_test! {
         f128: #[cfg(any(miri, target_has_reliable_f128_math))],
     },
     test<Float> {
-        assert_eq!((0.0 as Float).round(), 0.0);
-        assert!((0.0 as Float).round().is_sign_positive());
-        assert_eq!((-0.0 as Float).round(), -0.0);
-        assert!((-0.0 as Float).round().is_sign_negative());
-        assert_eq!((0.5 as Float).round(), 1.0);
-        assert_eq!((-0.5 as Float).round(), -1.0);
-        assert_eq!(Float::MAX.round(), Float::MAX);
-        assert_eq!(Float::MIN.round(), Float::MIN);
-        assert_eq!(Float::MIN_POSITIVE.round(), 0.0);
-        assert_eq!((-Float::MIN_POSITIVE).round(), 0.0);
+        assert_biteq!((2.5 as Float).round(), 3.0);
+        assert_biteq!((1.0 as Float).round(), 1.0);
+        assert_biteq!((1.3 as Float).round(), 1.0);
+        assert_biteq!((1.5 as Float).round(), 2.0);
+        assert_biteq!((1.7 as Float).round(), 2.0);
+        assert_biteq!((0.5 as Float).round(), 1.0);
+        assert_biteq!((0.0 as Float).round(), 0.0);
+        assert_biteq!((-0.0 as Float).round(), -0.0);
+        assert_biteq!((-0.5 as Float).round(), -1.0);
+        assert_biteq!((-1.0 as Float).round(), -1.0);
+        assert_biteq!((-1.3 as Float).round(), -1.0);
+        assert_biteq!((-1.5 as Float).round(), -2.0);
+        assert_biteq!((-1.7 as Float).round(), -2.0);
+        assert_biteq!(Float::MAX.round(), Float::MAX);
+        assert_biteq!(Float::MIN.round(), Float::MIN);
+        assert_biteq!(Float::MIN_POSITIVE.round(), 0.0);
+        assert_biteq!((-Float::MIN_POSITIVE).round(), -0.0);
         assert!(Float::NAN.round().is_nan());
-        assert_eq!(Float::INFINITY.round(), Float::INFINITY);
-        assert_eq!(Float::NEG_INFINITY.round(), Float::NEG_INFINITY);
+        assert_biteq!(Float::INFINITY.round(), Float::INFINITY);
+        assert_biteq!(Float::NEG_INFINITY.round(), Float::NEG_INFINITY);
     }
 }
 
@@ -554,21 +613,26 @@ float_test! {
         f128: #[cfg(any(miri, target_has_reliable_f128_math))],
     },
     test<Float> {
-        assert_eq!((0.0 as Float).round_ties_even(), 0.0);
-        assert!((0.0 as Float).round_ties_even().is_sign_positive());
-        assert_eq!((-0.0 as Float).round_ties_even(), -0.0);
-        assert!((-0.0 as Float).round_ties_even().is_sign_negative());
-        assert_eq!((0.5 as Float).round_ties_even(), 0.0);
-        assert!((0.5 as Float).round_ties_even().is_sign_positive());
-        assert_eq!((-0.5 as Float).round_ties_even(), -0.0);
-        assert!((-0.5 as Float).round_ties_even().is_sign_negative());
-        assert_eq!(Float::MAX.round_ties_even(), Float::MAX);
-        assert_eq!(Float::MIN.round_ties_even(), Float::MIN);
-        assert_eq!(Float::MIN_POSITIVE.round_ties_even(), 0.0);
-        assert_eq!((-Float::MIN_POSITIVE).round_ties_even(), 0.0);
+        assert_biteq!((2.5 as Float).round_ties_even(), 2.0);
+        assert_biteq!((1.0 as Float).round_ties_even(), 1.0);
+        assert_biteq!((1.3 as Float).round_ties_even(), 1.0);
+        assert_biteq!((1.5 as Float).round_ties_even(), 2.0);
+        assert_biteq!((1.7 as Float).round_ties_even(), 2.0);
+        assert_biteq!((0.5 as Float).round_ties_even(), 0.0);
+        assert_biteq!((0.0 as Float).round_ties_even(), 0.0);
+        assert_biteq!((-0.0 as Float).round_ties_even(), -0.0);
+        assert_biteq!((-0.5 as Float).round_ties_even(), -0.0);
+        assert_biteq!((-1.0 as Float).round_ties_even(), -1.0);
+        assert_biteq!((-1.3 as Float).round_ties_even(), -1.0);
+        assert_biteq!((-1.5 as Float).round_ties_even(), -2.0);
+        assert_biteq!((-1.7 as Float).round_ties_even(), -2.0);
+        assert_biteq!(Float::MAX.round_ties_even(), Float::MAX);
+        assert_biteq!(Float::MIN.round_ties_even(), Float::MIN);
+        assert_biteq!(Float::MIN_POSITIVE.round_ties_even(), 0.0);
+        assert_biteq!((-Float::MIN_POSITIVE).round_ties_even(), -0.0);
         assert!(Float::NAN.round_ties_even().is_nan());
-        assert_eq!(Float::INFINITY.round_ties_even(), Float::INFINITY);
-        assert_eq!(Float::NEG_INFINITY.round_ties_even(), Float::NEG_INFINITY);
+        assert_biteq!(Float::INFINITY.round_ties_even(), Float::INFINITY);
+        assert_biteq!(Float::NEG_INFINITY.round_ties_even(), Float::NEG_INFINITY);
     }
 }
 
@@ -579,21 +643,25 @@ float_test! {
         f128: #[cfg(any(miri, target_has_reliable_f128_math))],
     },
     test<Float> {
-        assert_eq!((0.0 as Float).trunc(), 0.0);
-        assert!((0.0 as Float).trunc().is_sign_positive());
-        assert_eq!((-0.0 as Float).trunc(), -0.0);
-        assert!((-0.0 as Float).trunc().is_sign_negative());
-        assert_eq!((0.5 as Float).trunc(), 0.0);
-        assert!((0.5 as Float).trunc().is_sign_positive());
-        assert_eq!((-0.5 as Float).trunc(), -0.0);
-        assert!((-0.5 as Float).trunc().is_sign_negative());
-        assert_eq!(Float::MAX.trunc(), Float::MAX);
-        assert_eq!(Float::MIN.trunc(), Float::MIN);
-        assert_eq!(Float::MIN_POSITIVE.trunc(), 0.0);
-        assert_eq!((-Float::MIN_POSITIVE).trunc(), 0.0);
+        assert_biteq!((1.0 as Float).trunc(), 1.0);
+        assert_biteq!((1.3 as Float).trunc(), 1.0);
+        assert_biteq!((1.5 as Float).trunc(), 1.0);
+        assert_biteq!((1.7 as Float).trunc(), 1.0);
+        assert_biteq!((0.5 as Float).trunc(), 0.0);
+        assert_biteq!((0.0 as Float).trunc(), 0.0);
+        assert_biteq!((-0.0 as Float).trunc(), -0.0);
+        assert_biteq!((-0.5 as Float).trunc(), -0.0);
+        assert_biteq!((-1.0 as Float).trunc(), -1.0);
+        assert_biteq!((-1.3 as Float).trunc(), -1.0);
+        assert_biteq!((-1.5 as Float).trunc(), -1.0);
+        assert_biteq!((-1.7 as Float).trunc(), -1.0);
+        assert_biteq!(Float::MAX.trunc(), Float::MAX);
+        assert_biteq!(Float::MIN.trunc(), Float::MIN);
+        assert_biteq!(Float::MIN_POSITIVE.trunc(), 0.0);
+        assert_biteq!((-Float::MIN_POSITIVE).trunc(), -0.0);
         assert!(Float::NAN.trunc().is_nan());
-        assert_eq!(Float::INFINITY.trunc(), Float::INFINITY);
-        assert_eq!(Float::NEG_INFINITY.trunc(), Float::NEG_INFINITY);
+        assert_biteq!(Float::INFINITY.trunc(), Float::INFINITY);
+        assert_biteq!(Float::NEG_INFINITY.trunc(), Float::NEG_INFINITY);
     }
 }
 
@@ -604,19 +672,23 @@ float_test! {
         f128: #[cfg(any(miri, target_has_reliable_f128_math))],
     },
     test<Float> {
-        assert_eq!((0.0 as Float).fract(), 0.0);
-        assert!((0.0 as Float).fract().is_sign_positive());
-        assert_eq!((-0.0 as Float).fract(), 0.0);
-        assert!((-0.0 as Float).fract().is_sign_positive());
-        assert_eq!((0.5 as Float).fract(), 0.5);
-        assert!((0.5 as Float).fract().is_sign_positive());
-        assert_eq!((-0.5 as Float).fract(), -0.5);
-        assert!((-0.5 as Float).fract().is_sign_negative());
-        assert_eq!(Float::MAX.fract(), 0.0);
-        assert_eq!(Float::MIN.fract(), 0.0);
-        assert_eq!(Float::MIN_POSITIVE.fract(), Float::MIN_POSITIVE);
+        assert_biteq!((1.0 as Float).fract(), 0.0);
+        assert_approx_eq!((1.3 as Float).fract(), 0.3); // rounding differs between float types
+        assert_biteq!((1.5 as Float).fract(), 0.5);
+        assert_approx_eq!((1.7 as Float).fract(), 0.7);
+        assert_biteq!((0.5 as Float).fract(), 0.5);
+        assert_biteq!((0.0 as Float).fract(), 0.0);
+        assert_biteq!((-0.0 as Float).fract(), 0.0);
+        assert_biteq!((-0.5 as Float).fract(), -0.5);
+        assert_biteq!((-1.0 as Float).fract(), 0.0);
+        assert_approx_eq!((-1.3 as Float).fract(), -0.3); // rounding differs between float types
+        assert_biteq!((-1.5 as Float).fract(), -0.5);
+        assert_approx_eq!((-1.7 as Float).fract(), -0.7);
+        assert_biteq!(Float::MAX.fract(), 0.0);
+        assert_biteq!(Float::MIN.fract(), 0.0);
+        assert_biteq!(Float::MIN_POSITIVE.fract(), Float::MIN_POSITIVE);
         assert!(Float::MIN_POSITIVE.fract().is_sign_positive());
-        assert_eq!((-Float::MIN_POSITIVE).fract(), -Float::MIN_POSITIVE);
+        assert_biteq!((-Float::MIN_POSITIVE).fract(), -Float::MIN_POSITIVE);
         assert!((-Float::MIN_POSITIVE).fract().is_sign_negative());
         assert!(Float::NAN.fract().is_nan());
         assert!(Float::INFINITY.fract().is_nan());
diff --git a/library/coretests/tests/ptr.rs b/library/coretests/tests/ptr.rs
index bb60fb07468..197a14423b5 100644
--- a/library/coretests/tests/ptr.rs
+++ b/library/coretests/tests/ptr.rs
@@ -653,7 +653,6 @@ fn thin_box() {
     //   if `{size,align}_of_for_meta<T: ?Sized>(T::Metadata)` are added.
     // * Constructing a `ThinBox` without consuming and deallocating a `Box`
     //   requires either the unstable `Unsize` marker trait,
-    //   or the unstable `unsized_locals` language feature,
     //   or taking `&dyn T` and restricting to `T: Copy`.
 
     use std::alloc::*;
diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml
index 53d78dcc488..ae7107938f3 100644
--- a/library/std/Cargo.toml
+++ b/library/std/Cargo.toml
@@ -32,7 +32,7 @@ rustc-demangle = { version = "0.1.24", features = ['rustc-dep-of-std'] }
 
 [target.'cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))'.dependencies]
 miniz_oxide = { version = "0.8.0", optional = true, default-features = false }
-addr2line = { version = "0.24.0", optional = true, default-features = false }
+addr2line = { version = "0.25.0", optional = true, default-features = false }
 
 [target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies]
 libc = { version = "0.2.172", default-features = false, features = [
@@ -40,7 +40,7 @@ libc = { version = "0.2.172", default-features = false, features = [
 ], public = true }
 
 [target.'cfg(all(not(target_os = "aix"), not(all(windows, target_env = "msvc", not(target_vendor = "uwp")))))'.dependencies]
-object = { version = "0.36.0", default-features = false, optional = true, features = [
+object = { version = "0.37.1", default-features = false, optional = true, features = [
     'read_core',
     'elf',
     'macho',
@@ -50,7 +50,7 @@ object = { version = "0.36.0", default-features = false, optional = true, featur
 ] }
 
 [target.'cfg(target_os = "aix")'.dependencies]
-object = { version = "0.36.0", default-features = false, optional = true, features = [
+object = { version = "0.37.1", default-features = false, optional = true, features = [
     'read_core',
     'xcoff',
     'unaligned',
diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs
index 6cbf8301e01..0cd794fd3ef 100644
--- a/library/std/src/fs.rs
+++ b/library/std/src/fs.rs
@@ -153,9 +153,8 @@ pub struct Metadata(fs_imp::FileAttr);
 /// dependent.
 ///
 /// # Errors
-///
-/// This [`io::Result`] will be an [`Err`] if there's some sort of intermittent
-/// IO error during iteration.
+/// This [`io::Result`] will be an [`Err`] if an error occurred while fetching
+/// the next entry from the OS.
 #[stable(feature = "rust1", since = "1.0.0")]
 #[derive(Debug)]
 pub struct ReadDir(fs_imp::ReadDir);
diff --git a/library/std/src/os/unix/net/addr.rs b/library/std/src/os/unix/net/addr.rs
index cb1246db310..fd6fe72dd24 100644
--- a/library/std/src/os/unix/net/addr.rs
+++ b/library/std/src/os/unix/net/addr.rs
@@ -1,3 +1,4 @@
+use crate::bstr::ByteStr;
 use crate::ffi::OsStr;
 #[cfg(any(doc, target_os = "android", target_os = "linux"))]
 use crate::os::net::linux_ext;
@@ -61,7 +62,7 @@ pub(super) fn sockaddr_un(path: &Path) -> io::Result<(libc::sockaddr_un, libc::s
 enum AddressKind<'a> {
     Unnamed,
     Pathname(&'a Path),
-    Abstract(&'a [u8]),
+    Abstract(&'a ByteStr),
 }
 
 /// An address associated with a Unix socket.
@@ -245,7 +246,7 @@ impl SocketAddr {
         {
             AddressKind::Unnamed
         } else if self.addr.sun_path[0] == 0 {
-            AddressKind::Abstract(&path[1..len])
+            AddressKind::Abstract(ByteStr::from_bytes(&path[1..len]))
         } else {
             AddressKind::Pathname(OsStr::from_bytes(&path[..len - 1]).as_ref())
         }
@@ -260,7 +261,7 @@ impl Sealed for SocketAddr {}
 #[stable(feature = "unix_socket_abstract", since = "1.70.0")]
 impl linux_ext::addr::SocketAddrExt for SocketAddr {
     fn as_abstract_name(&self) -> Option<&[u8]> {
-        if let AddressKind::Abstract(name) = self.address() { Some(name) } else { None }
+        if let AddressKind::Abstract(name) = self.address() { Some(name.as_bytes()) } else { None }
     }
 
     fn from_abstract_name<N>(name: N) -> crate::io::Result<Self>
@@ -295,7 +296,7 @@ impl fmt::Debug for SocketAddr {
     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self.address() {
             AddressKind::Unnamed => write!(fmt, "(unnamed)"),
-            AddressKind::Abstract(name) => write!(fmt, "\"{}\" (abstract)", name.escape_ascii()),
+            AddressKind::Abstract(name) => write!(fmt, "{name:?} (abstract)"),
             AddressKind::Pathname(path) => write!(fmt, "{path:?} (pathname)"),
         }
     }
diff --git a/library/std/src/os/unix/net/tests.rs b/library/std/src/os/unix/net/tests.rs
index 0398a535eb5..9a88687b1df 100644
--- a/library/std/src/os/unix/net/tests.rs
+++ b/library/std/src/os/unix/net/tests.rs
@@ -411,6 +411,15 @@ fn test_unix_datagram_timeout_zero_duration() {
     assert_eq!(err.kind(), ErrorKind::InvalidInput);
 }
 
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[test]
+fn abstract_socket_addr_debug() {
+    assert_eq!(
+        r#""\0hello world\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x11\x12\r\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f \x7f\x80\x81\xfe\xff" (abstract)"#,
+        format!("{:?}", SocketAddr::from_abstract_name(b"\0hello world\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x11\x12\r\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f \x7f\x80\x81\xfe\xff").unwrap()),
+    );
+}
+
 #[test]
 fn abstract_namespace_not_allowed_connect() {
     assert!(UnixStream::connect("\0asdf").is_err());
diff --git a/library/stdarch b/library/stdarch
-Subproject 5c1c436524c0bbc8db83577f42f8bea9006a7b7
+Subproject 1b4d15df12079504942d0a3f1030b2039b8a776
diff --git a/library/unwind/Cargo.toml b/library/unwind/Cargo.toml
index ad373420a96..f8da09f7193 100644
--- a/library/unwind/Cargo.toml
+++ b/library/unwind/Cargo.toml
@@ -22,7 +22,7 @@ cfg-if = "1.0"
 libc = { version = "0.2.140", features = ['rustc-dep-of-std'], default-features = false }
 
 [target.'cfg(target_os = "xous")'.dependencies]
-unwinding = { version = "0.2.6", features = ['rustc-dep-of-std', 'unwinder', 'fde-custom'], default-features = false }
+unwinding = { version = "0.2.7", features = ['rustc-dep-of-std', 'unwinder', 'fde-custom'], default-features = false }
 
 [features]
 
diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs
index 680718f0552..f64d67341cf 100644
--- a/src/bootstrap/src/core/build_steps/tool.rs
+++ b/src/bootstrap/src/core/build_steps/tool.rs
@@ -136,6 +136,19 @@ impl Step for ToolBuild {
             _ => panic!("unexpected Mode for tool build"),
         }
 
+        // build.tool.TOOL_NAME.features in bootstrap.toml allows specifying which features to
+        // enable for a specific tool. `extra_features` instead is not controlled by the toml and
+        // provides features that are always enabled for a specific tool (e.g. "in-rust-tree" for
+        // rust-analyzer). Finally, `prepare_tool_cargo` might add more features to adapt the build
+        // to the chosen flags (e.g. "all-static" for cargo if `cargo_native_static` is true).
+        let mut features = builder
+            .config
+            .tool
+            .get(self.tool)
+            .and_then(|tool| tool.features.clone())
+            .unwrap_or_default();
+        features.extend(self.extra_features.clone());
+
         let mut cargo = prepare_tool_cargo(
             builder,
             self.compiler,
@@ -144,7 +157,7 @@ impl Step for ToolBuild {
             Kind::Build,
             path,
             self.source_type,
-            &self.extra_features,
+            &features,
         );
 
         // The stage0 compiler changes infrequently and does not directly depend on code
diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs
index 6aff376bde3..970a982dae4 100644
--- a/src/bootstrap/src/core/config/config.rs
+++ b/src/bootstrap/src/core/config/config.rs
@@ -35,7 +35,7 @@ pub use crate::core::config::flags::Subcommand;
 use crate::core::config::flags::{Color, Flags};
 use crate::core::config::target_selection::TargetSelectionList;
 use crate::core::config::toml::TomlConfig;
-use crate::core::config::toml::build::Build;
+use crate::core::config::toml::build::{Build, Tool};
 use crate::core::config::toml::change_id::ChangeId;
 use crate::core::config::toml::rust::{
     LldMode, RustOptimize, check_incompatible_options_for_ci_rustc,
@@ -101,6 +101,9 @@ pub struct Config {
     pub bootstrap_cache_path: Option<PathBuf>,
     pub extended: bool,
     pub tools: Option<HashSet<String>>,
+    /// Specify build configuration specific for some tool, such as enabled features, see [Tool].
+    /// The key in the map is the name of the tool, and the value is tool-specific configuration.
+    pub tool: HashMap<String, Tool>,
     pub sanitizers: bool,
     pub profiler: bool,
     pub omit_git_hash: bool,
@@ -676,6 +679,7 @@ impl Config {
             bootstrap_cache_path,
             extended,
             tools,
+            tool,
             verbose,
             sanitizers,
             profiler,
@@ -822,6 +826,7 @@ impl Config {
         set(&mut config.full_bootstrap, full_bootstrap);
         set(&mut config.extended, extended);
         config.tools = tools;
+        set(&mut config.tool, tool);
         set(&mut config.verbose, verbose);
         set(&mut config.sanitizers, sanitizers);
         set(&mut config.profiler, profiler);
diff --git a/src/bootstrap/src/core/config/toml/build.rs b/src/bootstrap/src/core/config/toml/build.rs
index 85ded3c87d9..98e1194de72 100644
--- a/src/bootstrap/src/core/config/toml/build.rs
+++ b/src/bootstrap/src/core/config/toml/build.rs
@@ -6,6 +6,8 @@
 //! various feature flags. These options apply across different stages and components
 //! unless specifically overridden by other configuration sections or command-line flags.
 
+use std::collections::HashMap;
+
 use serde::{Deserialize, Deserializer};
 
 use crate::core::config::toml::ReplaceOpt;
@@ -42,6 +44,7 @@ define_config! {
         bootstrap_cache_path: Option<PathBuf> = "bootstrap-cache-path",
         extended: Option<bool> = "extended",
         tools: Option<HashSet<String>> = "tools",
+        tool: Option<HashMap<String, Tool>> = "tool",
         verbose: Option<usize> = "verbose",
         sanitizers: Option<bool> = "sanitizers",
         profiler: Option<bool> = "profiler",
@@ -70,3 +73,11 @@ define_config! {
         exclude: Option<Vec<PathBuf>> = "exclude",
     }
 }
+
+define_config! {
+    /// Configuration specific for some tool, e.g. which features to enable during build.
+    #[derive(Default, Clone)]
+    struct Tool {
+        features: Option<Vec<String>> = "features",
+    }
+}
diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs
index ef776e21943..493f73b21fe 100644
--- a/src/bootstrap/src/core/sanity.rs
+++ b/src/bootstrap/src/core/sanity.rs
@@ -200,12 +200,14 @@ than building it.
         .map(|p| cmd_finder.must_have(p))
         .or_else(|| cmd_finder.maybe_have("reuse"));
 
-    let stage0_supported_target_list: HashSet<String> = crate::utils::helpers::output(
-        command(&build.config.initial_rustc).args(["--print", "target-list"]).as_command_mut(),
-    )
-    .lines()
-    .map(|s| s.to_string())
-    .collect();
+    let stage0_supported_target_list: HashSet<String> = command(&build.config.initial_rustc)
+        .args(["--print", "target-list"])
+        .run_always()
+        .run_capture_stdout(&build)
+        .stdout()
+        .lines()
+        .map(|s| s.to_string())
+        .collect();
 
     // Compiler tools like `cc` and `ar` are not configured for cross-targets on certain subcommands
     // because they are not needed.
diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs
index 25e59bfe3a8..7254c653a2d 100644
--- a/src/bootstrap/src/lib.rs
+++ b/src/bootstrap/src/lib.rs
@@ -21,7 +21,6 @@ use std::cell::{Cell, RefCell};
 use std::collections::{BTreeSet, HashMap, HashSet};
 use std::fmt::Display;
 use std::path::{Path, PathBuf};
-use std::process::Command;
 use std::sync::OnceLock;
 use std::time::SystemTime;
 use std::{env, fs, io, str};
@@ -39,7 +38,7 @@ use crate::core::builder::Kind;
 use crate::core::config::{DryRun, LldMode, LlvmLibunwind, TargetSelection, flags};
 use crate::utils::exec::{BehaviorOnFailure, BootstrapCommand, CommandOutput, OutputMode, command};
 use crate::utils::helpers::{
-    self, dir_is_empty, exe, libdir, output, set_file_times, split_debuginfo, symlink_dir,
+    self, dir_is_empty, exe, libdir, set_file_times, split_debuginfo, symlink_dir,
 };
 
 mod core;
@@ -376,10 +375,13 @@ impl Build {
         let in_tree_llvm_info = config.in_tree_llvm_info.clone();
         let in_tree_gcc_info = config.in_tree_gcc_info.clone();
 
-        let initial_target_libdir =
-            output(Command::new(&config.initial_rustc).args(["--print", "target-libdir"]))
-                .trim()
-                .to_owned();
+        let initial_target_libdir = command(&config.initial_rustc)
+            .run_always()
+            .args(["--print", "target-libdir"])
+            .run_capture_stdout(&config)
+            .stdout()
+            .trim()
+            .to_owned();
 
         let initial_target_dir = Path::new(&initial_target_libdir)
             .parent()
@@ -479,8 +481,11 @@ impl Build {
 
         // If local-rust is the same major.minor as the current version, then force a
         // local-rebuild
-        let local_version_verbose =
-            output(Command::new(&build.initial_rustc).arg("--version").arg("--verbose"));
+        let local_version_verbose = command(&build.initial_rustc)
+            .run_always()
+            .args(["--version", "--verbose"])
+            .run_capture_stdout(&build)
+            .stdout();
         let local_release = local_version_verbose
             .lines()
             .filter_map(|x| x.strip_prefix("release:"))
@@ -941,9 +946,14 @@ impl Build {
     fn rustc_snapshot_sysroot(&self) -> &Path {
         static SYSROOT_CACHE: OnceLock<PathBuf> = OnceLock::new();
         SYSROOT_CACHE.get_or_init(|| {
-            let mut rustc = Command::new(&self.initial_rustc);
-            rustc.args(["--print", "sysroot"]);
-            output(&mut rustc).trim().into()
+            command(&self.initial_rustc)
+                .run_always()
+                .args(["--print", "sysroot"])
+                .run_capture_stdout(self)
+                .stdout()
+                .trim()
+                .to_owned()
+                .into()
         })
     }
 
diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs
index e939a8362ad..93e01a58077 100644
--- a/src/bootstrap/src/utils/change_tracker.rs
+++ b/src/bootstrap/src/utils/change_tracker.rs
@@ -421,4 +421,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[
         severity: ChangeSeverity::Info,
         summary: "Added new bootstrap flag `--skip-std-check-if-no-download-rustc` that skips std checks when download-rustc is unavailable. Mainly intended for developers to reduce RA overhead.",
     },
+    ChangeInfo {
+        change_id: 142379,
+        severity: ChangeSeverity::Info,
+        summary: "Added new option `tool.TOOL_NAME.features` to specify the features to compile a tool with",
+    },
 ];
diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs
index f2c3e8c0df4..3e04e046844 100644
--- a/src/bootstrap/src/utils/helpers.rs
+++ b/src/bootstrap/src/utils/helpers.rs
@@ -300,25 +300,6 @@ pub fn make(host: &str) -> PathBuf {
     }
 }
 
-#[track_caller]
-pub fn output(cmd: &mut Command) -> String {
-    #[cfg(feature = "tracing")]
-    let _run_span = crate::trace_cmd!(cmd);
-
-    let output = match cmd.stderr(Stdio::inherit()).output() {
-        Ok(status) => status,
-        Err(e) => fail(&format!("failed to execute command: {cmd:?}\nERROR: {e}")),
-    };
-    if !output.status.success() {
-        panic!(
-            "command did not execute successfully: {:?}\n\
-             expected success, got: {}",
-            cmd, output.status
-        );
-    }
-    String::from_utf8(output.stdout).unwrap()
-}
-
 /// Spawn a process and return a closure that will wait for the process
 /// to finish and then return its output. This allows the spawned process
 /// to do work without immediately blocking bootstrap.
diff --git a/src/ci/docker/host-x86_64/mingw-check-tidy/Dockerfile b/src/ci/docker/host-x86_64/mingw-check-tidy/Dockerfile
index 006a697af21..8d2c5e004e4 100644
--- a/src/ci/docker/host-x86_64/mingw-check-tidy/Dockerfile
+++ b/src/ci/docker/host-x86_64/mingw-check-tidy/Dockerfile
@@ -41,7 +41,9 @@ RUN pip3 install --no-deps --no-cache-dir --require-hashes -r /tmp/reuse-require
 COPY host-x86_64/mingw-check-1/validate-toolstate.sh /scripts/
 COPY host-x86_64/mingw-check-1/validate-error-codes.sh /scripts/
 
+RUN bash -c 'npm install -g eslint@$(cat /tmp/eslint.version)'
+
 # NOTE: intentionally uses python2 for x.py so we can test it still works.
 # validate-toolstate only runs in our CI, so it's ok for it to only support python3.
-ENV SCRIPT TIDY_PRINT_DIFF=1 npm install eslint@$(head -n 1 /tmp/eslint.version) && \
- python2.7 ../x.py test --stage 0 src/tools/tidy tidyselftest --extra-checks=py,cpp
+ENV SCRIPT TIDY_PRINT_DIFF=1 python2.7 ../x.py test --stage 0 \
+    src/tools/tidy tidyselftest --extra-checks=py,cpp
diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml
index 43c77d1ddf7..d6d9e0fb773 100644
--- a/src/ci/github-actions/jobs.yml
+++ b/src/ci/github-actions/jobs.yml
@@ -256,7 +256,7 @@ auto:
       IMAGE: dist-x86_64-linux
       CODEGEN_BACKENDS: llvm,cranelift
       DOCKER_SCRIPT: dist-alt.sh
-    <<: *job-linux-4c-largedisk
+    <<: *job-linux-8c
 
   - name: dist-x86_64-musl
     env:
diff --git a/src/ci/run.sh b/src/ci/run.sh
index e0c00dc1925..a6721a818b3 100755
--- a/src/ci/run.sh
+++ b/src/ci/run.sh
@@ -134,6 +134,11 @@ if [ "$DEPLOY$DEPLOY_ALT" = "1" ]; then
 
   CODEGEN_BACKENDS="${CODEGEN_BACKENDS:-llvm}"
   RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.codegen-backends=$CODEGEN_BACKENDS"
+
+  # Unless explicitly disabled, we want rustc debug assertions on the -alt builds
+  if [ "$DEPLOY_ALT" != "" ] && [ "$NO_DEBUG_ASSERTIONS" = "" ]; then
+    RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-debug-assertions"
+  fi
 else
   # We almost always want debug assertions enabled, but sometimes this takes too
   # long for too little benefit, so we just turn them off.
diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version
index c8721bb3600..86d35b31498 100644
--- a/src/doc/rustc-dev-guide/rust-version
+++ b/src/doc/rustc-dev-guide/rust-version
@@ -1 +1 @@
-c31cccb7b5cc098b1a8c1794ed38d7fdbec0ccb0
+14346303d760027e53214e705109a62c0f00b214
diff --git a/src/doc/rustc-dev-guide/src/git.md b/src/doc/rustc-dev-guide/src/git.md
index 8118ddff10c..8726ddfce20 100644
--- a/src/doc/rustc-dev-guide/src/git.md
+++ b/src/doc/rustc-dev-guide/src/git.md
@@ -142,7 +142,8 @@ The most common cause is that you rebased after a change and ran `git add .` wit
 `x` to update the submodules.  Alternatively, you might have run `cargo fmt` instead of `x fmt`
 and modified files in a submodule, then committed the changes.
 
-To fix it, do the following things:
+To fix it, do the following things (if you changed a submodule other than cargo,
+replace `src/tools/cargo` with the path to that submodule):
 
 1. See which commit has the accidental changes: `git log --stat -n1 src/tools/cargo`
 2. Revert the changes to that commit: `git checkout <my-commit>~ src/tools/cargo`. Type `~`
diff --git a/src/doc/rustc-dev-guide/src/implementing_new_features.md b/src/doc/rustc-dev-guide/src/implementing_new_features.md
index d7561bbbad2..5d0e875cbc1 100644
--- a/src/doc/rustc-dev-guide/src/implementing_new_features.md
+++ b/src/doc/rustc-dev-guide/src/implementing_new_features.md
@@ -156,8 +156,8 @@ a new unstable feature:
    [`incomplete_features` lint]: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#incomplete-features
 
    ```rust ignore
-   /// Allows unsized rvalues at arguments and parameters.
-   (incomplete, unsized_locals, "CURRENT_RUSTC_VERSION", Some(48055), None),
+   /// Allows deref patterns.
+   (incomplete, deref_patterns, "CURRENT_RUSTC_VERSION", Some(87121), None),
    ```
 
    To avoid [semantic merge conflicts], please use `CURRENT_RUSTC_VERSION` instead of `1.70` or
diff --git a/src/doc/rustc-dev-guide/src/tests/compiletest.md b/src/doc/rustc-dev-guide/src/tests/compiletest.md
index 20dd16c81df..ded30234e70 100644
--- a/src/doc/rustc-dev-guide/src/tests/compiletest.md
+++ b/src/doc/rustc-dev-guide/src/tests/compiletest.md
@@ -115,7 +115,7 @@ default behavior without any commands is to:
 2. Run `rustc -Zunpretty=normal` on the output of the previous step.
 3. The output of the previous two steps should be the same.
 4. Run `rustc -Zno-codegen` on the output to make sure that it can type check
-   (this is similar to running `cargo check`).
+   (similar to `cargo check`).
 
 If any of the commands above fail, then the test fails.
 
diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md
index a3939e5a5c4..201a5503079 100644
--- a/src/doc/rustc/src/SUMMARY.md
+++ b/src/doc/rustc/src/SUMMARY.md
@@ -113,6 +113,7 @@
     - [\*-unknown-openbsd](platform-support/openbsd.md)
     - [\*-unknown-redox](platform-support/redox.md)
     - [\*-unknown-uefi](platform-support/unknown-uefi.md)
+    - [\*-unknown-windows-msvc](platform-support/windows-msvc.md)
     - [\*-uwp-windows-msvc](platform-support/uwp-windows-msvc.md)
     - [\*-wrs-vxworks](platform-support/vxworks.md)
     - [wasm32-wasip1](platform-support/wasm32-wasip1.md)
diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md
index 559e4867bbb..3cab57df75a 100644
--- a/src/doc/rustc/src/platform-support.md
+++ b/src/doc/rustc/src/platform-support.md
@@ -34,11 +34,11 @@ target | notes
 -------|-------
 [`aarch64-apple-darwin`](platform-support/apple-darwin.md) | ARM64 macOS (11.0+, Big Sur+)
 `aarch64-unknown-linux-gnu` | ARM64 Linux (kernel 4.1+, glibc 2.17+)
-`i686-pc-windows-msvc` | 32-bit MSVC (Windows 10+, Windows Server 2016+, Pentium 4) [^x86_32-floats-return-ABI] [^win32-msvc-alignment]
+[`i686-pc-windows-msvc`](platform-support/windows-msvc.md) | 32-bit MSVC (Windows 10+, Windows Server 2016+, Pentium 4) [^x86_32-floats-return-ABI] [^win32-msvc-alignment]
 `i686-unknown-linux-gnu` | 32-bit Linux (kernel 3.2+, glibc 2.17+, Pentium 4) [^x86_32-floats-return-ABI]
 [`x86_64-apple-darwin`](platform-support/apple-darwin.md) | 64-bit macOS (10.12+, Sierra+)
 [`x86_64-pc-windows-gnu`](platform-support/windows-gnu.md) | 64-bit MinGW (Windows 10+, Windows Server 2016+)
-`x86_64-pc-windows-msvc` | 64-bit MSVC (Windows 10+, Windows Server 2016+)
+[`x86_64-pc-windows-msvc`](platform-support/windows-msvc.md) | 64-bit MSVC (Windows 10+, Windows Server 2016+)
 `x86_64-unknown-linux-gnu` | 64-bit Linux (kernel 3.2+, glibc 2.17+)
 
 [^x86_32-floats-return-ABI]: Due to limitations of the C ABI, floating-point support on `i686` targets is non-compliant: floating-point return values are passed via an x87 register, so NaN payload bits can be lost. Functions with the default Rust ABI are not affected. See [issue #115567][x86-32-float-return-issue].
@@ -88,7 +88,7 @@ so Rustup may install the documentation for a similar tier 1 target instead.
 
 target | notes
 -------|-------
-`aarch64-pc-windows-msvc` | ARM64 Windows MSVC
+[`aarch64-pc-windows-msvc`](platform-support/windows-msvc.md) | ARM64 Windows MSVC
 `aarch64-unknown-linux-musl` | ARM64 Linux with musl 1.2.3
 [`aarch64-unknown-linux-ohos`](platform-support/openharmony.md) | ARM64 OpenHarmony
 `arm-unknown-linux-gnueabi` | Armv6 Linux (kernel 3.2+, glibc 2.17)
diff --git a/src/doc/rustc/src/platform-support/windows-msvc.md b/src/doc/rustc/src/platform-support/windows-msvc.md
new file mode 100644
index 00000000000..71dc4ddc2e6
--- /dev/null
+++ b/src/doc/rustc/src/platform-support/windows-msvc.md
@@ -0,0 +1,69 @@
+# `*-pc-windows-msvc`
+
+Windows MSVC targets.
+
+**Tier 1 with host tools:**
+
+- `i686-pc-windows-msvc`: Windows on 32-bit x86.
+- `x86_64-pc-windows-msvc`: Windows on 64-bit x86.
+
+**Tier 2 with host tools:**
+
+- `aarch64-pc-windows-msvc`: Windows on ARM64.
+
+## Target maintainers
+
+[@ChrisDenton](https://github.com/ChrisDenton)
+[@dpaoliello](https://github.com/dpaoliello)
+[@lambdageek](https://github.com/lambdageek)
+[@sivadeilra](https://github.com/sivadeilra)
+[@wesleywiser](https://github.com/wesleywiser)
+
+## Requirements
+
+### OS version
+
+Windows 10 or higher is required for client installs, Windows Server 2016 or higher is required for server installs.
+
+### Host tooling
+
+The minimum supported Visual Studio version is 2017 but this support is not actively tested in CI.
+It is **highly** recommended to use the latest version of VS (currently VS 2022).
+
+### Platform details
+
+These targets fully implement the Rust standard library.
+
+The `extern "C"` calling convention conforms to Microsoft's default calling convention for the given architecture: [`__cdecl`] on `i686`, [`x64`] on `x86_64` and [`ARM64`] on `aarch64`.
+
+The `*-windows-msvc` targets produce PE/COFF binaries with CodeView debuginfo, the native formats used on Windows.
+
+[`__cdecl`]: https://learn.microsoft.com/en-us/cpp/cpp/cdecl?view=msvc-170
+[`x64`]: https://learn.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-170
+[`ARM64`]: https://learn.microsoft.com/en-us/cpp/build/arm64-windows-abi-conventions?view=msvc-170
+
+## Building Rust programs
+
+These targets are distributed via `rustup` and can be installed via `rustup component add [--toolchain {name}] {target}`.
+
+For example, adding the 32-bit x86 target to the `nightly` toolchain:
+
+```text
+rustup component add --toolchain nightly i686-pc-windows-msvc
+```
+
+or adding the ARM64 target to the active toolchain:
+
+```text
+rustup component add aarch64-pc-windows-msvc
+```
+
+## Testing
+
+There are no special requirements for testing and running this target.
+
+## Cross-compilation toolchains and C code
+
+Architectural cross-compilation from one Windows host to a different Windows platform is natively supported by the MSVC toolchain provided the appropriate components are selected when using the VS Installer.
+
+Cross-compilation from a non-Windows host to a `*-windows-msvc` target _may_ be possible but is not supported.
diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md
index 69e5a5adbec..27910ad0ab7 100644
--- a/src/doc/rustdoc/src/unstable-features.md
+++ b/src/doc/rustdoc/src/unstable-features.md
@@ -581,7 +581,9 @@ For this rust code:
 
 ```rust
 /// ```
+/// #![allow(dead_code)]
 /// let x = 12;
+/// Ok(())
 /// ```
 pub trait Trait {}
 ```
@@ -590,10 +592,10 @@ The generated output (formatted) will look like this:
 
 ```json
 {
-  "format_version": 1,
+  "format_version": 2,
   "doctests": [
     {
-      "file": "foo.rs",
+      "file": "src/lib.rs",
       "line": 1,
       "doctest_attributes": {
         "original": "",
@@ -609,9 +611,17 @@ The generated output (formatted) will look like this:
         "added_css_classes": [],
         "unknown": []
       },
-      "original_code": "let x = 12;",
-      "doctest_code": "#![allow(unused)]\nfn main() {\nlet x = 12;\n}",
-      "name": "foo.rs - Trait (line 1)"
+      "original_code": "#![allow(dead_code)]\nlet x = 12;\nOk(())",
+      "doctest_code": {
+        "crate_level": "#![allow(unused)]\n#![allow(dead_code)]\n\n",
+        "code": "let x = 12;\nOk(())",
+        "wrapper": {
+          "before": "fn main() { fn _inner() -> core::result::Result<(), impl core::fmt::Debug> {\n",
+          "after": "\n} _inner().unwrap() }",
+          "returns_result": true
+        }
+      },
+      "name": "src/lib.rs - (line 1)"
     }
   ]
 }
@@ -624,6 +634,10 @@ The generated output (formatted) will look like this:
    * `doctest_attributes` contains computed information about the attributes used on the doctests. For more information about doctest attributes, take a look [here](write-documentation/documentation-tests.html#attributes).
    * `original_code` is the code as written in the source code before rustdoc modifies it.
    * `doctest_code` is the code modified by rustdoc that will be run. If there is a fatal syntax error, this field will not be present.
+     * `crate_level` is the crate level code (like attributes or `extern crate`) that will be added at the top-level of the generated doctest.
+     * `code` is "naked" doctest without anything from `crate_level` and `wrapper` content.
+     * `wrapper` contains extra code that will be added before and after `code`.
+       * `returns_result` is a boolean. If `true`, it means that the doctest returns a `Result` type.
    * `name` is the name generated by rustdoc which represents this doctest.
 
 ### html
diff --git a/src/doc/unstable-book/src/compiler-flags/macro-stats.md b/src/doc/unstable-book/src/compiler-flags/macro-stats.md
new file mode 100644
index 00000000000..b2622cff057
--- /dev/null
+++ b/src/doc/unstable-book/src/compiler-flags/macro-stats.md
@@ -0,0 +1,24 @@
+# `macro-stats`
+
+This feature is perma-unstable and has no tracking issue.
+
+----
+
+Some macros, especially procedural macros, can generate a surprising amount of
+code, which can slow down compile times. This is hard to detect because the
+generated code is normally invisible to the programmer.
+
+This flag helps identify such cases. When enabled, the compiler measures the
+effect on code size of all used macros and prints a table summarizing that
+effect. For each distinct macro, it counts how many times it is used, and the
+net effect on code size (in terms of lines of code, and bytes of code). The
+code size evaluation uses the compiler's internal pretty-printing, and so will
+be independent of the formatting in the original code.
+
+Note that the net effect of a macro may be negative. E.g. the `cfg!` and
+`#[test]` macros often strip out code.
+
+If a macro is identified as causing a large increase in code size, it is worth
+using `cargo expand` to inspect the post-expansion code, which includes the
+code produced by all macros. It may be possible to optimize the macro to
+produce smaller code, or it may be possible to avoid using it altogether.
diff --git a/src/doc/unstable-book/src/language-features/asm-experimental-arch.md b/src/doc/unstable-book/src/language-features/asm-experimental-arch.md
index d9566c9f55c..121f9493435 100644
--- a/src/doc/unstable-book/src/language-features/asm-experimental-arch.md
+++ b/src/doc/unstable-book/src/language-features/asm-experimental-arch.md
@@ -19,6 +19,7 @@ This feature tracks `asm!` and `global_asm!` support for the following architect
 - M68k
 - CSKY
 - SPARC
+- LoongArch32
 
 ## Register classes
 
@@ -53,6 +54,8 @@ This feature tracks `asm!` and `global_asm!` support for the following architect
 | CSKY         | `freg`         | `f[0-31]`                          | `f`                  |
 | SPARC        | `reg`          | `r[2-29]`                          | `r`                  |
 | SPARC        | `yreg`         | `y`                                | Only clobbers        |
+| LoongArch32  | `reg`          | `$r1`, `$r[4-20]`, `$r[23,30]`     | `r`                  |
+| LoongArch32  | `freg`         | `$f[0-31]`                         | `f`                  |
 
 > **Notes**:
 > - NVPTX doesn't have a fixed register set, so named registers are not supported.
@@ -91,6 +94,8 @@ This feature tracks `asm!` and `global_asm!` support for the following architect
 | CSKY         | `freg`                          | None           | `f32`,                                  |
 | SPARC        | `reg`                           | None           | `i8`, `i16`, `i32`, `i64` (SPARC64 only) |
 | SPARC        | `yreg`                          | N/A            | Only clobbers                           |
+| LoongArch32  | `reg`                           | None           | `i8`, `i16`, `i32`, `f32`               |
+| LoongArch32  | `freg`                          | None           | `f32`, `f64`                            |
 
 ## Register aliases
 
diff --git a/src/doc/unstable-book/src/language-features/unsized-locals.md b/src/doc/unstable-book/src/language-features/unsized-locals.md
deleted file mode 100644
index d5b01a3d616..00000000000
--- a/src/doc/unstable-book/src/language-features/unsized-locals.md
+++ /dev/null
@@ -1,175 +0,0 @@
-# `unsized_locals`
-
-The tracking issue for this feature is: [#48055]
-
-[#48055]: https://github.com/rust-lang/rust/issues/48055
-
-------------------------
-
-This implements [RFC1909]. When turned on, you can have unsized arguments and locals:
-
-[RFC1909]: https://github.com/rust-lang/rfcs/blob/master/text/1909-unsized-rvalues.md
-
-```rust
-#![allow(incomplete_features)]
-#![feature(unsized_locals, unsized_fn_params)]
-
-use std::any::Any;
-
-fn main() {
-    let x: Box<dyn Any> = Box::new(42);
-    let x: dyn Any = *x;
-    //  ^ unsized local variable
-    //               ^^ unsized temporary
-    foo(x);
-}
-
-fn foo(_: dyn Any) {}
-//     ^^^^^^ unsized argument
-```
-
-The RFC still forbids the following unsized expressions:
-
-```rust,compile_fail
-#![feature(unsized_locals)]
-
-use std::any::Any;
-
-struct MyStruct<T: ?Sized> {
-    content: T,
-}
-
-struct MyTupleStruct<T: ?Sized>(T);
-
-fn answer() -> Box<dyn Any> {
-    Box::new(42)
-}
-
-fn main() {
-    // You CANNOT have unsized statics.
-    static X: dyn Any = *answer();  // ERROR
-    const Y: dyn Any = *answer();  // ERROR
-
-    // You CANNOT have struct initialized unsized.
-    MyStruct { content: *answer() };  // ERROR
-    MyTupleStruct(*answer());  // ERROR
-    (42, *answer());  // ERROR
-
-    // You CANNOT have unsized return types.
-    fn my_function() -> dyn Any { *answer() }  // ERROR
-
-    // You CAN have unsized local variables...
-    let mut x: dyn Any = *answer();  // OK
-    // ...but you CANNOT reassign to them.
-    x = *answer();  // ERROR
-
-    // You CANNOT even initialize them separately.
-    let y: dyn Any;  // OK
-    y = *answer();  // ERROR
-
-    // Not mentioned in the RFC, but by-move captured variables are also Sized.
-    let x: dyn Any = *answer();
-    (move || {  // ERROR
-        let y = x;
-    })();
-
-    // You CAN create a closure with unsized arguments,
-    // but you CANNOT call it.
-    // This is an implementation detail and may be changed in the future.
-    let f = |x: dyn Any| {};
-    f(*answer());  // ERROR
-}
-```
-
-## By-value trait objects
-
-With this feature, you can have by-value `self` arguments without `Self: Sized` bounds.
-
-```rust
-#![feature(unsized_fn_params)]
-
-trait Foo {
-    fn foo(self) {}
-}
-
-impl<T: ?Sized> Foo for T {}
-
-fn main() {
-    let slice: Box<[i32]> = Box::new([1, 2, 3]);
-    <[i32] as Foo>::foo(*slice);
-}
-```
-
-And `Foo` will also be object-safe.
-
-```rust
-#![feature(unsized_fn_params)]
-
-trait Foo {
-    fn foo(self) {}
-}
-
-impl<T: ?Sized> Foo for T {}
-
-fn main () {
-    let slice: Box<dyn Foo> = Box::new([1, 2, 3]);
-    // doesn't compile yet
-    <dyn Foo as Foo>::foo(*slice);
-}
-```
-
-One of the objectives of this feature is to allow `Box<dyn FnOnce>`.
-
-## Variable length arrays
-
-The RFC also describes an extension to the array literal syntax: `[e; dyn n]`. In the syntax, `n` isn't necessarily a constant expression. The array is dynamically allocated on the stack and has the type of `[T]`, instead of `[T; n]`.
-
-```rust,ignore (not-yet-implemented)
-#![feature(unsized_locals)]
-
-fn mergesort<T: Ord>(a: &mut [T]) {
-    let mut tmp = [T; dyn a.len()];
-    // ...
-}
-
-fn main() {
-    let mut a = [3, 1, 5, 6];
-    mergesort(&mut a);
-    assert_eq!(a, [1, 3, 5, 6]);
-}
-```
-
-VLAs are not implemented yet. The syntax isn't final, either. We may need an alternative syntax for Rust 2015 because, in Rust 2015, expressions like `[e; dyn(1)]` would be ambiguous. One possible alternative proposed in the RFC is `[e; n]`: if `n` captures one or more local variables, then it is considered as `[e; dyn n]`.
-
-## Advisory on stack usage
-
-It's advised not to casually use the `#![feature(unsized_locals)]` feature. Typical use-cases are:
-
-- When you need a by-value trait objects.
-- When you really need a fast allocation of small temporary arrays.
-
-Another pitfall is repetitive allocation and temporaries. Currently the compiler simply extends the stack frame every time it encounters an unsized assignment. So for example, the code
-
-```rust
-#![feature(unsized_locals)]
-
-fn main() {
-    let x: Box<[i32]> = Box::new([1, 2, 3, 4, 5]);
-    let _x = {{{{{{{{{{*x}}}}}}}}}};
-}
-```
-
-and the code
-
-```rust
-#![feature(unsized_locals)]
-
-fn main() {
-    for _ in 0..10 {
-        let x: Box<[i32]> = Box::new([1, 2, 3, 4, 5]);
-        let _x = *x;
-    }
-}
-```
-
-will unnecessarily extend the stack frame.
diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs
index a3762e4117d..b6ce8551060 100644
--- a/src/librustdoc/clean/cfg.rs
+++ b/src/librustdoc/clean/cfg.rs
@@ -169,33 +169,36 @@ impl Cfg {
         msg
     }
 
-    /// Renders the configuration for long display, as a long HTML description.
-    pub(crate) fn render_long_html(&self) -> String {
+    fn render_long_inner(&self, format: Format) -> String {
         let on = if self.omit_preposition() {
-            ""
+            " "
         } else if self.should_use_with_in_description() {
-            "with "
+            " with "
         } else {
-            "on "
+            " on "
         };
 
-        let mut msg = format!("Available {on}<strong>{}</strong>", Display(self, Format::LongHtml));
+        let mut msg = if matches!(format, Format::LongHtml) {
+            format!("Available{on}<strong>{}</strong>", Display(self, format))
+        } else {
+            format!("Available{on}{}", Display(self, format))
+        };
         if self.should_append_only_to_description() {
             msg.push_str(" only");
         }
+        msg
+    }
+
+    /// Renders the configuration for long display, as a long HTML description.
+    pub(crate) fn render_long_html(&self) -> String {
+        let mut msg = self.render_long_inner(Format::LongHtml);
         msg.push('.');
         msg
     }
 
     /// Renders the configuration for long display, as a long plain text description.
     pub(crate) fn render_long_plain(&self) -> String {
-        let on = if self.should_use_with_in_description() { "with" } else { "on" };
-
-        let mut msg = format!("Available {on} {}", Display(self, Format::LongPlain));
-        if self.should_append_only_to_description() {
-            msg.push_str(" only");
-        }
-        msg
+        self.render_long_inner(Format::LongPlain)
     }
 
     fn should_capitalize_first_letter(&self) -> bool {
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index bde1a2e5e66..58e05bd1e85 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -2638,7 +2638,7 @@ mod size_asserts {
     static_assert_size!(GenericParamDef, 40);
     static_assert_size!(Generics, 16);
     static_assert_size!(Item, 8);
-    static_assert_size!(ItemInner, 136);
+    static_assert_size!(ItemInner, 144);
     static_assert_size!(ItemKind, 48);
     static_assert_size!(PathSegment, 32);
     static_assert_size!(Type, 32);
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index a81d6020f71..130fdff1afe 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -1053,14 +1053,14 @@ fn doctest_run_fn(
     let report_unused_externs = |uext| {
         unused_externs.lock().unwrap().push(uext);
     };
-    let (full_test_code, full_test_line_offset) = doctest.generate_unique_doctest(
+    let (wrapped, full_test_line_offset) = doctest.generate_unique_doctest(
         &scraped_test.text,
         scraped_test.langstr.test_harness,
         &global_opts,
         Some(&global_opts.crate_name),
     );
     let runnable_test = RunnableDocTest {
-        full_test_code,
+        full_test_code: wrapped.to_string(),
         full_test_line_offset,
         test_opts,
         global_opts,
diff --git a/src/librustdoc/doctest/extracted.rs b/src/librustdoc/doctest/extracted.rs
index ebe6bfd22ba..925fb6fee2c 100644
--- a/src/librustdoc/doctest/extracted.rs
+++ b/src/librustdoc/doctest/extracted.rs
@@ -3,8 +3,10 @@
 //! This module contains the logic to extract doctests and output a JSON containing this
 //! information.
 
+use rustc_span::edition::Edition;
 use serde::Serialize;
 
+use super::make::DocTestWrapResult;
 use super::{BuildDocTestBuilder, ScrapedDocTest};
 use crate::config::Options as RustdocOptions;
 use crate::html::markdown;
@@ -14,7 +16,7 @@ use crate::html::markdown;
 /// This integer is incremented with every breaking change to the API,
 /// and is returned along with the JSON blob into the `format_version` root field.
 /// Consuming code should assert that this value matches the format version(s) that it supports.
-const FORMAT_VERSION: u32 = 1;
+const FORMAT_VERSION: u32 = 2;
 
 #[derive(Serialize)]
 pub(crate) struct ExtractedDocTests {
@@ -34,7 +36,16 @@ impl ExtractedDocTests {
         options: &RustdocOptions,
     ) {
         let edition = scraped_test.edition(options);
+        self.add_test_with_edition(scraped_test, opts, edition)
+    }
 
+    /// This method is used by unit tests to not have to provide a `RustdocOptions`.
+    pub(crate) fn add_test_with_edition(
+        &mut self,
+        scraped_test: ScrapedDocTest,
+        opts: &super::GlobalTestOptions,
+        edition: Edition,
+    ) {
         let ScrapedDocTest { filename, line, langstr, text, name, global_crate_attrs, .. } =
             scraped_test;
 
@@ -44,8 +55,7 @@ impl ExtractedDocTests {
             .edition(edition)
             .lang_str(&langstr)
             .build(None);
-
-        let (full_test_code, size) = doctest.generate_unique_doctest(
+        let (wrapped, _size) = doctest.generate_unique_doctest(
             &text,
             langstr.test_harness,
             opts,
@@ -55,11 +65,46 @@ impl ExtractedDocTests {
             file: filename.prefer_remapped_unconditionaly().to_string(),
             line,
             doctest_attributes: langstr.into(),
-            doctest_code: if size != 0 { Some(full_test_code) } else { None },
+            doctest_code: match wrapped {
+                DocTestWrapResult::Valid { crate_level_code, wrapper, code } => Some(DocTest {
+                    crate_level: crate_level_code,
+                    code,
+                    wrapper: wrapper.map(
+                        |super::make::WrapperInfo { before, after, returns_result, .. }| {
+                            WrapperInfo { before, after, returns_result }
+                        },
+                    ),
+                }),
+                DocTestWrapResult::SyntaxError { .. } => None,
+            },
             original_code: text,
             name,
         });
     }
+
+    #[cfg(test)]
+    pub(crate) fn doctests(&self) -> &[ExtractedDocTest] {
+        &self.doctests
+    }
+}
+
+#[derive(Serialize)]
+pub(crate) struct WrapperInfo {
+    before: String,
+    after: String,
+    returns_result: bool,
+}
+
+#[derive(Serialize)]
+pub(crate) struct DocTest {
+    crate_level: String,
+    code: String,
+    /// This field can be `None` if one of the following conditions is true:
+    ///
+    /// * The doctest's codeblock has the `test_harness` attribute.
+    /// * The doctest has a `main` function.
+    /// * The doctest has the `![no_std]` attribute.
+    pub(crate) wrapper: Option<WrapperInfo>,
 }
 
 #[derive(Serialize)]
@@ -69,7 +114,7 @@ pub(crate) struct ExtractedDocTest {
     doctest_attributes: LangString,
     original_code: String,
     /// `None` if the code syntax is invalid.
-    doctest_code: Option<String>,
+    pub(crate) doctest_code: Option<DocTest>,
     name: String,
 }
 
diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs
index 5e571613d6f..3ff6828e52f 100644
--- a/src/librustdoc/doctest/make.rs
+++ b/src/librustdoc/doctest/make.rs
@@ -196,6 +196,80 @@ pub(crate) struct DocTestBuilder {
     pub(crate) can_be_merged: bool,
 }
 
+/// Contains needed information for doctest to be correctly generated with expected "wrapping".
+pub(crate) struct WrapperInfo {
+    pub(crate) before: String,
+    pub(crate) after: String,
+    pub(crate) returns_result: bool,
+    insert_indent_space: bool,
+}
+
+impl WrapperInfo {
+    fn len(&self) -> usize {
+        self.before.len() + self.after.len()
+    }
+}
+
+/// Contains a doctest information. Can be converted into code with the `to_string()` method.
+pub(crate) enum DocTestWrapResult {
+    Valid {
+        crate_level_code: String,
+        /// This field can be `None` if one of the following conditions is true:
+        ///
+        /// * The doctest's codeblock has the `test_harness` attribute.
+        /// * The doctest has a `main` function.
+        /// * The doctest has the `![no_std]` attribute.
+        wrapper: Option<WrapperInfo>,
+        /// Contains the doctest processed code without the wrappers (which are stored in the
+        /// `wrapper` field).
+        code: String,
+    },
+    /// Contains the original source code.
+    SyntaxError(String),
+}
+
+impl std::string::ToString for DocTestWrapResult {
+    fn to_string(&self) -> String {
+        match self {
+            Self::SyntaxError(s) => s.clone(),
+            Self::Valid { crate_level_code, wrapper, code } => {
+                let mut prog_len = code.len() + crate_level_code.len();
+                if let Some(wrapper) = wrapper {
+                    prog_len += wrapper.len();
+                    if wrapper.insert_indent_space {
+                        prog_len += code.lines().count() * 4;
+                    }
+                }
+                let mut prog = String::with_capacity(prog_len);
+
+                prog.push_str(crate_level_code);
+                if let Some(wrapper) = wrapper {
+                    prog.push_str(&wrapper.before);
+
+                    // add extra 4 spaces for each line to offset the code block
+                    if wrapper.insert_indent_space {
+                        write!(
+                            prog,
+                            "{}",
+                            fmt::from_fn(|f| code
+                                .lines()
+                                .map(|line| fmt::from_fn(move |f| write!(f, "    {line}")))
+                                .joined("\n", f))
+                        )
+                        .unwrap();
+                    } else {
+                        prog.push_str(code);
+                    }
+                    prog.push_str(&wrapper.after);
+                } else {
+                    prog.push_str(code);
+                }
+                prog
+            }
+        }
+    }
+}
+
 impl DocTestBuilder {
     fn invalid(
         global_crate_attrs: Vec<String>,
@@ -228,50 +302,49 @@ impl DocTestBuilder {
         dont_insert_main: bool,
         opts: &GlobalTestOptions,
         crate_name: Option<&str>,
-    ) -> (String, usize) {
+    ) -> (DocTestWrapResult, usize) {
         if self.invalid_ast {
             // If the AST failed to compile, no need to go generate a complete doctest, the error
             // will be better this way.
             debug!("invalid AST:\n{test_code}");
-            return (test_code.to_string(), 0);
+            return (DocTestWrapResult::SyntaxError(test_code.to_string()), 0);
         }
         let mut line_offset = 0;
-        let mut prog = String::new();
-        let everything_else = self.everything_else.trim();
-
+        let mut crate_level_code = String::new();
+        let processed_code = self.everything_else.trim();
         if self.global_crate_attrs.is_empty() {
             // If there aren't any attributes supplied by #![doc(test(attr(...)))], then allow some
             // lints that are commonly triggered in doctests. The crate-level test attributes are
             // commonly used to make tests fail in case they trigger warnings, so having this there in
             // that case may cause some tests to pass when they shouldn't have.
-            prog.push_str("#![allow(unused)]\n");
+            crate_level_code.push_str("#![allow(unused)]\n");
             line_offset += 1;
         }
 
         // Next, any attributes that came from #![doc(test(attr(...)))].
         for attr in &self.global_crate_attrs {
-            prog.push_str(&format!("#![{attr}]\n"));
+            crate_level_code.push_str(&format!("#![{attr}]\n"));
             line_offset += 1;
         }
 
         // Now push any outer attributes from the example, assuming they
         // are intended to be crate attributes.
         if !self.crate_attrs.is_empty() {
-            prog.push_str(&self.crate_attrs);
+            crate_level_code.push_str(&self.crate_attrs);
             if !self.crate_attrs.ends_with('\n') {
-                prog.push('\n');
+                crate_level_code.push('\n');
             }
         }
         if !self.maybe_crate_attrs.is_empty() {
-            prog.push_str(&self.maybe_crate_attrs);
+            crate_level_code.push_str(&self.maybe_crate_attrs);
             if !self.maybe_crate_attrs.ends_with('\n') {
-                prog.push('\n');
+                crate_level_code.push('\n');
             }
         }
         if !self.crates.is_empty() {
-            prog.push_str(&self.crates);
+            crate_level_code.push_str(&self.crates);
             if !self.crates.ends_with('\n') {
-                prog.push('\n');
+                crate_level_code.push('\n');
             }
         }
 
@@ -289,17 +362,20 @@ impl DocTestBuilder {
         {
             // rustdoc implicitly inserts an `extern crate` item for the own crate
             // which may be unused, so we need to allow the lint.
-            prog.push_str("#[allow(unused_extern_crates)]\n");
+            crate_level_code.push_str("#[allow(unused_extern_crates)]\n");
 
-            prog.push_str(&format!("extern crate r#{crate_name};\n"));
+            crate_level_code.push_str(&format!("extern crate r#{crate_name};\n"));
             line_offset += 1;
         }
 
         // FIXME: This code cannot yet handle no_std test cases yet
-        if dont_insert_main || self.has_main_fn || prog.contains("![no_std]") {
-            prog.push_str(everything_else);
+        let wrapper = if dont_insert_main
+            || self.has_main_fn
+            || crate_level_code.contains("![no_std]")
+        {
+            None
         } else {
-            let returns_result = everything_else.ends_with("(())");
+            let returns_result = processed_code.ends_with("(())");
             // Give each doctest main function a unique name.
             // This is for example needed for the tooling around `-C instrument-coverage`.
             let inner_fn_name = if let Some(ref test_id) = self.test_id {
@@ -333,28 +409,22 @@ impl DocTestBuilder {
             // /// ``` <- end of the inner main
             line_offset += 1;
 
-            prog.push_str(&main_pre);
-
-            // add extra 4 spaces for each line to offset the code block
-            if opts.insert_indent_space {
-                write!(
-                    prog,
-                    "{}",
-                    fmt::from_fn(|f| everything_else
-                        .lines()
-                        .map(|line| fmt::from_fn(move |f| write!(f, "    {line}")))
-                        .joined("\n", f))
-                )
-                .unwrap();
-            } else {
-                prog.push_str(everything_else);
-            };
-            prog.push_str(&main_post);
-        }
-
-        debug!("final doctest:\n{prog}");
+            Some(WrapperInfo {
+                before: main_pre,
+                after: main_post,
+                returns_result,
+                insert_indent_space: opts.insert_indent_space,
+            })
+        };
 
-        (prog, line_offset)
+        (
+            DocTestWrapResult::Valid {
+                code: processed_code.to_string(),
+                wrapper,
+                crate_level_code,
+            },
+            line_offset,
+        )
     }
 }
 
diff --git a/src/librustdoc/doctest/tests.rs b/src/librustdoc/doctest/tests.rs
index ce2984ced79..ccc3e55a331 100644
--- a/src/librustdoc/doctest/tests.rs
+++ b/src/librustdoc/doctest/tests.rs
@@ -1,6 +1,11 @@
 use std::path::PathBuf;
 
-use super::{BuildDocTestBuilder, GlobalTestOptions};
+use rustc_span::edition::Edition;
+use rustc_span::{DUMMY_SP, FileName};
+
+use super::extracted::ExtractedDocTests;
+use super::{BuildDocTestBuilder, GlobalTestOptions, ScrapedDocTest};
+use crate::html::markdown::LangString;
 
 fn make_test(
     test_code: &str,
@@ -19,9 +24,9 @@ fn make_test(
         builder = builder.test_id(test_id.to_string());
     }
     let doctest = builder.build(None);
-    let (code, line_offset) =
+    let (wrapped, line_offset) =
         doctest.generate_unique_doctest(test_code, dont_insert_main, opts, crate_name);
-    (code, line_offset)
+    (wrapped.to_string(), line_offset)
 }
 
 /// Default [`GlobalTestOptions`] for these unit tests.
@@ -461,3 +466,51 @@ pub mod outer_module {
     let (output, len) = make_test(input, None, false, &opts, Vec::new(), None);
     assert_eq!((output, len), (expected, 2));
 }
+
+fn get_extracted_doctests(code: &str) -> ExtractedDocTests {
+    let opts = default_global_opts("");
+    let mut extractor = ExtractedDocTests::new();
+    extractor.add_test_with_edition(
+        ScrapedDocTest::new(
+            FileName::Custom(String::new()),
+            0,
+            Vec::new(),
+            LangString::default(),
+            code.to_string(),
+            DUMMY_SP,
+            Vec::new(),
+        ),
+        &opts,
+        Edition::Edition2018,
+    );
+    extractor
+}
+
+// Test that `extracted::DocTest::wrapper` is `None` if the doctest has a `main` function.
+#[test]
+fn test_extracted_doctest_wrapper_field() {
+    let extractor = get_extracted_doctests("fn main() {}");
+
+    assert_eq!(extractor.doctests().len(), 1);
+    let doctest_code = extractor.doctests()[0].doctest_code.as_ref().unwrap();
+    assert!(doctest_code.wrapper.is_none());
+}
+
+// Test that `ExtractedDocTest::doctest_code` is `None` if the doctest has syntax error.
+#[test]
+fn test_extracted_doctest_doctest_code_field() {
+    let extractor = get_extracted_doctests("let x +=");
+
+    assert_eq!(extractor.doctests().len(), 1);
+    assert!(extractor.doctests()[0].doctest_code.is_none());
+}
+
+// Test that `extracted::DocTest::wrapper` is `Some` if the doctest needs wrapping.
+#[test]
+fn test_extracted_doctest_wrapper_field_with_info() {
+    let extractor = get_extracted_doctests("let x = 12;");
+
+    assert_eq!(extractor.doctests().len(), 1);
+    let doctest_code = extractor.doctests()[0].doctest_code.as_ref().unwrap();
+    assert!(doctest_code.wrapper.is_some());
+}
diff --git a/src/librustdoc/formats/renderer.rs b/src/librustdoc/formats/renderer.rs
index 5e4e6f27a15..48626171404 100644
--- a/src/librustdoc/formats/renderer.rs
+++ b/src/librustdoc/formats/renderer.rs
@@ -56,7 +56,7 @@ pub(crate) trait FormatRenderer<'tcx>: Sized {
     fn restore_module_data(&mut self, info: Self::ModuleData);
 
     /// Renders a single non-module item. This means no recursive sub-item rendering is required.
-    fn item(&mut self, item: clean::Item) -> Result<(), Error>;
+    fn item(&mut self, item: &clean::Item) -> Result<(), Error>;
 
     /// Renders a module (should not handle recursing into children).
     fn mod_item_in(&mut self, item: &clean::Item) -> Result<(), Error>;
@@ -67,14 +67,14 @@ pub(crate) trait FormatRenderer<'tcx>: Sized {
     }
 
     /// Post processing hook for cleanup and dumping output to files.
-    fn after_krate(&mut self) -> Result<(), Error>;
+    fn after_krate(self) -> Result<(), Error>;
 
     fn cache(&self) -> &Cache;
 }
 
 fn run_format_inner<'tcx, T: FormatRenderer<'tcx>>(
     cx: &mut T,
-    item: clean::Item,
+    item: &clean::Item,
     prof: &SelfProfilerRef,
 ) -> Result<(), Error> {
     if item.is_mod() && T::RUN_ON_MODULE {
@@ -84,12 +84,12 @@ fn run_format_inner<'tcx, T: FormatRenderer<'tcx>>(
             prof.generic_activity_with_arg("render_mod_item", item.name.unwrap().to_string());
 
         cx.mod_item_in(&item)?;
-        let (clean::StrippedItem(box clean::ModuleItem(module)) | clean::ModuleItem(module)) =
-            item.inner.kind
+        let (clean::StrippedItem(box clean::ModuleItem(ref module))
+        | clean::ModuleItem(ref module)) = item.inner.kind
         else {
             unreachable!()
         };
-        for it in module.items {
+        for it in module.items.iter() {
             let info = cx.save_module_data();
             run_format_inner(cx, it, prof)?;
             cx.restore_module_data(info);
@@ -101,7 +101,7 @@ fn run_format_inner<'tcx, T: FormatRenderer<'tcx>>(
     } else if let Some(item_name) = item.name
         && !item.is_extern_crate()
     {
-        prof.generic_activity_with_arg("render_item", item_name.as_str()).run(|| cx.item(item))?;
+        prof.generic_activity_with_arg("render_item", item_name.as_str()).run(|| cx.item(&item))?;
     }
     Ok(())
 }
@@ -125,7 +125,7 @@ pub(crate) fn run_format<'tcx, T: FormatRenderer<'tcx>>(
     }
 
     // Render the crate documentation
-    run_format_inner(&mut format_renderer, krate.module, prof)?;
+    run_format_inner(&mut format_renderer, &krate.module, prof)?;
 
     prof.verbose_generic_activity_with_arg("renderer_after_krate", T::descr())
         .run(|| format_renderer.after_krate())
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index d3701784f9d..f626e07b000 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -307,7 +307,8 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
                 builder = builder.crate_name(krate);
             }
             let doctest = builder.build(None);
-            let (test, _) = doctest.generate_unique_doctest(&test, false, &opts, krate);
+            let (wrapped, _) = doctest.generate_unique_doctest(&test, false, &opts, krate);
+            let test = wrapped.to_string();
             let channel = if test.contains("#![feature(") { "&amp;version=nightly" } else { "" };
 
             let test_escaped = small_url_encode(test);
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index 5984dcd74ca..38214451657 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -609,7 +609,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
         self.info = info;
     }
 
-    fn after_krate(&mut self) -> Result<(), Error> {
+    fn after_krate(mut self) -> Result<(), Error> {
         let crate_name = self.tcx().crate_name(LOCAL_CRATE);
         let final_file = self.dst.join(crate_name.as_str()).join("all.html");
         let settings_file = self.dst.join("settings.html");
@@ -830,7 +830,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
         Ok(())
     }
 
-    fn item(&mut self, item: clean::Item) -> Result<(), Error> {
+    fn item(&mut self, item: &clean::Item) -> Result<(), Error> {
         // Stripped modules survive the rustdoc passes (i.e., `strip-private`)
         // if they contain impls for public types. These modules can also
         // contain items such as publicly re-exported structures.
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index 3ade40940bc..6bdf3b5fe38 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -13,6 +13,7 @@ use rustc_metadata::rendered_const;
 use rustc_middle::{bug, ty};
 use rustc_span::{Pos, Symbol, kw};
 use rustdoc_json_types::*;
+use thin_vec::ThinVec;
 
 use crate::clean::{self, ItemId};
 use crate::formats::FormatRenderer;
@@ -21,7 +22,7 @@ use crate::json::JsonRenderer;
 use crate::passes::collect_intra_doc_links::UrlFragment;
 
 impl JsonRenderer<'_> {
-    pub(super) fn convert_item(&self, item: clean::Item) -> Option<Item> {
+    pub(super) fn convert_item(&self, item: &clean::Item) -> Option<Item> {
         let deprecation = item.deprecation(self.tcx);
         let links = self
             .cache
@@ -107,49 +108,54 @@ impl JsonRenderer<'_> {
         }
     }
 
-    fn ids(&self, items: impl IntoIterator<Item = clean::Item>) -> Vec<Id> {
+    fn ids(&self, items: &[clean::Item]) -> Vec<Id> {
         items
-            .into_iter()
-            .filter(|x| !x.is_stripped() && !x.is_keyword())
+            .iter()
+            .filter(|i| !i.is_stripped() && !i.is_keyword())
             .map(|i| self.id_from_item(&i))
             .collect()
     }
 
-    fn ids_keeping_stripped(
-        &self,
-        items: impl IntoIterator<Item = clean::Item>,
-    ) -> Vec<Option<Id>> {
+    fn ids_keeping_stripped(&self, items: &[clean::Item]) -> Vec<Option<Id>> {
         items
-            .into_iter()
+            .iter()
             .map(|i| (!i.is_stripped() && !i.is_keyword()).then(|| self.id_from_item(&i)))
             .collect()
     }
 }
 
 pub(crate) trait FromClean<T> {
-    fn from_clean(f: T, renderer: &JsonRenderer<'_>) -> Self;
+    fn from_clean(f: &T, renderer: &JsonRenderer<'_>) -> Self;
 }
 
 pub(crate) trait IntoJson<T> {
-    fn into_json(self, renderer: &JsonRenderer<'_>) -> T;
+    fn into_json(&self, renderer: &JsonRenderer<'_>) -> T;
 }
 
 impl<T, U> IntoJson<U> for T
 where
     U: FromClean<T>,
 {
-    fn into_json(self, renderer: &JsonRenderer<'_>) -> U {
+    fn into_json(&self, renderer: &JsonRenderer<'_>) -> U {
         U::from_clean(self, renderer)
     }
 }
 
-impl<I, T, U> FromClean<I> for Vec<U>
+impl<T, U> FromClean<Vec<T>> for Vec<U>
 where
-    I: IntoIterator<Item = T>,
     U: FromClean<T>,
 {
-    fn from_clean(f: I, renderer: &JsonRenderer<'_>) -> Vec<U> {
-        f.into_iter().map(|x| x.into_json(renderer)).collect()
+    fn from_clean(items: &Vec<T>, renderer: &JsonRenderer<'_>) -> Vec<U> {
+        items.iter().map(|i| i.into_json(renderer)).collect()
+    }
+}
+
+impl<T, U> FromClean<ThinVec<T>> for Vec<U>
+where
+    U: FromClean<T>,
+{
+    fn from_clean(items: &ThinVec<T>, renderer: &JsonRenderer<'_>) -> Vec<U> {
+        items.iter().map(|i| i.into_json(renderer)).collect()
     }
 }
 
@@ -165,7 +171,7 @@ pub(crate) fn from_deprecation(deprecation: attrs::Deprecation) -> Deprecation {
 }
 
 impl FromClean<clean::GenericArgs> for GenericArgs {
-    fn from_clean(args: clean::GenericArgs, renderer: &JsonRenderer<'_>) -> Self {
+    fn from_clean(args: &clean::GenericArgs, renderer: &JsonRenderer<'_>) -> Self {
         use clean::GenericArgs::*;
         match args {
             AngleBracketed { args, constraints } => GenericArgs::AngleBracketed {
@@ -174,7 +180,7 @@ impl FromClean<clean::GenericArgs> for GenericArgs {
             },
             Parenthesized { inputs, output } => GenericArgs::Parenthesized {
                 inputs: inputs.into_json(renderer),
-                output: output.map(|a| (*a).into_json(renderer)),
+                output: output.as_ref().map(|a| a.as_ref().into_json(renderer)),
             },
             ReturnTypeNotation => GenericArgs::ReturnTypeNotation,
         }
@@ -182,7 +188,7 @@ impl FromClean<clean::GenericArgs> for GenericArgs {
 }
 
 impl FromClean<clean::GenericArg> for GenericArg {
-    fn from_clean(arg: clean::GenericArg, renderer: &JsonRenderer<'_>) -> Self {
+    fn from_clean(arg: &clean::GenericArg, renderer: &JsonRenderer<'_>) -> Self {
         use clean::GenericArg::*;
         match arg {
             Lifetime(l) => GenericArg::Lifetime(convert_lifetime(l)),
@@ -195,7 +201,7 @@ impl FromClean<clean::GenericArg> for GenericArg {
 
 impl FromClean<clean::Constant> for Constant {
     // FIXME(generic_const_items): Add support for generic const items.
-    fn from_clean(constant: clean::Constant, renderer: &JsonRenderer<'_>) -> Self {
+    fn from_clean(constant: &clean::Constant, renderer: &JsonRenderer<'_>) -> Self {
         let tcx = renderer.tcx;
         let expr = constant.expr(tcx);
         let value = constant.value(tcx);
@@ -206,7 +212,7 @@ impl FromClean<clean::Constant> for Constant {
 
 impl FromClean<clean::ConstantKind> for Constant {
     // FIXME(generic_const_items): Add support for generic const items.
-    fn from_clean(constant: clean::ConstantKind, renderer: &JsonRenderer<'_>) -> Self {
+    fn from_clean(constant: &clean::ConstantKind, renderer: &JsonRenderer<'_>) -> Self {
         let tcx = renderer.tcx;
         let expr = constant.expr(tcx);
         let value = constant.value(tcx);
@@ -216,7 +222,7 @@ impl FromClean<clean::ConstantKind> for Constant {
 }
 
 impl FromClean<clean::AssocItemConstraint> for AssocItemConstraint {
-    fn from_clean(constraint: clean::AssocItemConstraint, renderer: &JsonRenderer<'_>) -> Self {
+    fn from_clean(constraint: &clean::AssocItemConstraint, renderer: &JsonRenderer<'_>) -> Self {
         AssocItemConstraint {
             name: constraint.assoc.name.to_string(),
             args: constraint.assoc.args.into_json(renderer),
@@ -226,7 +232,7 @@ impl FromClean<clean::AssocItemConstraint> for AssocItemConstraint {
 }
 
 impl FromClean<clean::AssocItemConstraintKind> for AssocItemConstraintKind {
-    fn from_clean(kind: clean::AssocItemConstraintKind, renderer: &JsonRenderer<'_>) -> Self {
+    fn from_clean(kind: &clean::AssocItemConstraintKind, renderer: &JsonRenderer<'_>) -> Self {
         use clean::AssocItemConstraintKind::*;
         match kind {
             Equality { term } => AssocItemConstraintKind::Equality(term.into_json(renderer)),
@@ -235,15 +241,15 @@ impl FromClean<clean::AssocItemConstraintKind> for AssocItemConstraintKind {
     }
 }
 
-fn from_clean_item(item: clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum {
+fn from_clean_item(item: &clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum {
     use clean::ItemKind::*;
     let name = item.name;
     let is_crate = item.is_crate();
     let header = item.fn_header(renderer.tcx);
 
-    match item.inner.kind {
+    match &item.inner.kind {
         ModuleItem(m) => {
-            ItemEnum::Module(Module { is_crate, items: renderer.ids(m.items), is_stripped: false })
+            ItemEnum::Module(Module { is_crate, items: renderer.ids(&m.items), is_stripped: false })
         }
         ImportItem(i) => ItemEnum::Use(i.into_json(renderer)),
         StructItem(s) => ItemEnum::Struct(s.into_json(renderer)),
@@ -251,27 +257,27 @@ fn from_clean_item(item: clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum {
         StructFieldItem(f) => ItemEnum::StructField(f.into_json(renderer)),
         EnumItem(e) => ItemEnum::Enum(e.into_json(renderer)),
         VariantItem(v) => ItemEnum::Variant(v.into_json(renderer)),
-        FunctionItem(f) => ItemEnum::Function(from_function(*f, true, header.unwrap(), renderer)),
+        FunctionItem(f) => ItemEnum::Function(from_function(f, true, header.unwrap(), renderer)),
         ForeignFunctionItem(f, _) => {
-            ItemEnum::Function(from_function(*f, false, header.unwrap(), renderer))
+            ItemEnum::Function(from_function(f, false, header.unwrap(), renderer))
         }
-        TraitItem(t) => ItemEnum::Trait((*t).into_json(renderer)),
+        TraitItem(t) => ItemEnum::Trait(t.as_ref().into_json(renderer)),
         TraitAliasItem(t) => ItemEnum::TraitAlias(t.into_json(renderer)),
-        MethodItem(m, _) => ItemEnum::Function(from_function(*m, true, header.unwrap(), renderer)),
+        MethodItem(m, _) => ItemEnum::Function(from_function(m, true, header.unwrap(), renderer)),
         RequiredMethodItem(m) => {
-            ItemEnum::Function(from_function(*m, false, header.unwrap(), renderer))
+            ItemEnum::Function(from_function(m, false, header.unwrap(), renderer))
         }
-        ImplItem(i) => ItemEnum::Impl((*i).into_json(renderer)),
-        StaticItem(s) => ItemEnum::Static(convert_static(s, rustc_hir::Safety::Safe, renderer)),
+        ImplItem(i) => ItemEnum::Impl(i.as_ref().into_json(renderer)),
+        StaticItem(s) => ItemEnum::Static(convert_static(s, &rustc_hir::Safety::Safe, renderer)),
         ForeignStaticItem(s, safety) => ItemEnum::Static(convert_static(s, safety, renderer)),
         ForeignTypeItem => ItemEnum::ExternType,
-        TypeAliasItem(t) => ItemEnum::TypeAlias(t.into_json(renderer)),
+        TypeAliasItem(t) => ItemEnum::TypeAlias(t.as_ref().into_json(renderer)),
         // FIXME(generic_const_items): Add support for generic free consts
         ConstantItem(ci) => ItemEnum::Constant {
             type_: ci.type_.into_json(renderer),
             const_: ci.kind.into_json(renderer),
         },
-        MacroItem(m) => ItemEnum::Macro(m.source),
+        MacroItem(m) => ItemEnum::Macro(m.source.clone()),
         ProcMacroItem(m) => ItemEnum::ProcMacro(m.into_json(renderer)),
         PrimitiveItem(p) => {
             ItemEnum::Primitive(Primitive {
@@ -281,7 +287,7 @@ fn from_clean_item(item: clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum {
         }
         // FIXME(generic_const_items): Add support for generic associated consts.
         RequiredAssocConstItem(_generics, ty) => {
-            ItemEnum::AssocConst { type_: (*ty).into_json(renderer), value: None }
+            ItemEnum::AssocConst { type_: ty.as_ref().into_json(renderer), value: None }
         }
         // FIXME(generic_const_items): Add support for generic associated consts.
         ProvidedAssocConstItem(ci) | ImplAssocConstItem(ci) => ItemEnum::AssocConst {
@@ -296,22 +302,22 @@ fn from_clean_item(item: clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum {
         AssocTypeItem(t, b) => ItemEnum::AssocType {
             generics: t.generics.into_json(renderer),
             bounds: b.into_json(renderer),
-            type_: Some(t.item_type.unwrap_or(t.type_).into_json(renderer)),
+            type_: Some(t.item_type.as_ref().unwrap_or(&t.type_).into_json(renderer)),
         },
         // `convert_item` early returns `None` for stripped items and keywords.
         KeywordItem => unreachable!(),
         StrippedItem(inner) => {
-            match *inner {
+            match inner.as_ref() {
                 ModuleItem(m) => ItemEnum::Module(Module {
                     is_crate,
-                    items: renderer.ids(m.items),
+                    items: renderer.ids(&m.items),
                     is_stripped: true,
                 }),
                 // `convert_item` early returns `None` for stripped items we're not including
                 _ => unreachable!(),
             }
         }
-        ExternCrateItem { ref src } => ItemEnum::ExternCrate {
+        ExternCrateItem { src } => ItemEnum::ExternCrate {
             name: name.as_ref().unwrap().to_string(),
             rename: src.map(|x| x.to_string()),
         },
@@ -319,17 +325,17 @@ fn from_clean_item(item: clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum {
 }
 
 impl FromClean<clean::Struct> for Struct {
-    fn from_clean(struct_: clean::Struct, renderer: &JsonRenderer<'_>) -> Self {
+    fn from_clean(struct_: &clean::Struct, renderer: &JsonRenderer<'_>) -> Self {
         let has_stripped_fields = struct_.has_stripped_entries();
         let clean::Struct { ctor_kind, generics, fields } = struct_;
 
         let kind = match ctor_kind {
-            Some(CtorKind::Fn) => StructKind::Tuple(renderer.ids_keeping_stripped(fields)),
+            Some(CtorKind::Fn) => StructKind::Tuple(renderer.ids_keeping_stripped(&fields)),
             Some(CtorKind::Const) => {
                 assert!(fields.is_empty());
                 StructKind::Unit
             }
-            None => StructKind::Plain { fields: renderer.ids(fields), has_stripped_fields },
+            None => StructKind::Plain { fields: renderer.ids(&fields), has_stripped_fields },
         };
 
         Struct {
@@ -341,13 +347,13 @@ impl FromClean<clean::Struct> for Struct {
 }
 
 impl FromClean<clean::Union> for Union {
-    fn from_clean(union_: clean::Union, renderer: &JsonRenderer<'_>) -> Self {
+    fn from_clean(union_: &clean::Union, renderer: &JsonRenderer<'_>) -> Self {
         let has_stripped_fields = union_.has_stripped_entries();
         let clean::Union { generics, fields } = union_;
         Union {
             generics: generics.into_json(renderer),
             has_stripped_fields,
-            fields: renderer.ids(fields),
+            fields: renderer.ids(&fields),
             impls: Vec::new(), // Added in JsonRenderer::item
         }
     }
@@ -377,12 +383,12 @@ fn convert_abi(a: ExternAbi) -> Abi {
     }
 }
 
-fn convert_lifetime(l: clean::Lifetime) -> String {
+fn convert_lifetime(l: &clean::Lifetime) -> String {
     l.0.to_string()
 }
 
 impl FromClean<clean::Generics> for Generics {
-    fn from_clean(generics: clean::Generics, renderer: &JsonRenderer<'_>) -> Self {
+    fn from_clean(generics: &clean::Generics, renderer: &JsonRenderer<'_>) -> Self {
         Generics {
             params: generics.params.into_json(renderer),
             where_predicates: generics.where_predicates.into_json(renderer),
@@ -391,7 +397,7 @@ impl FromClean<clean::Generics> for Generics {
 }
 
 impl FromClean<clean::GenericParamDef> for GenericParamDef {
-    fn from_clean(generic_param: clean::GenericParamDef, renderer: &JsonRenderer<'_>) -> Self {
+    fn from_clean(generic_param: &clean::GenericParamDef, renderer: &JsonRenderer<'_>) -> Self {
         GenericParamDef {
             name: generic_param.name.to_string(),
             kind: generic_param.kind.into_json(renderer),
@@ -400,7 +406,7 @@ impl FromClean<clean::GenericParamDef> for GenericParamDef {
 }
 
 impl FromClean<clean::GenericParamDefKind> for GenericParamDefKind {
-    fn from_clean(kind: clean::GenericParamDefKind, renderer: &JsonRenderer<'_>) -> Self {
+    fn from_clean(kind: &clean::GenericParamDefKind, renderer: &JsonRenderer<'_>) -> Self {
         use clean::GenericParamDefKind::*;
         match kind {
             Lifetime { outlives } => GenericParamDefKind::Lifetime {
@@ -408,29 +414,29 @@ impl FromClean<clean::GenericParamDefKind> for GenericParamDefKind {
             },
             Type { bounds, default, synthetic } => GenericParamDefKind::Type {
                 bounds: bounds.into_json(renderer),
-                default: default.map(|x| (*x).into_json(renderer)),
-                is_synthetic: synthetic,
+                default: default.as_ref().map(|x| x.as_ref().into_json(renderer)),
+                is_synthetic: *synthetic,
             },
             Const { ty, default, synthetic: _ } => GenericParamDefKind::Const {
-                type_: (*ty).into_json(renderer),
-                default: default.map(|x| *x),
+                type_: ty.as_ref().into_json(renderer),
+                default: default.as_ref().map(|x| x.as_ref().clone()),
             },
         }
     }
 }
 
 impl FromClean<clean::WherePredicate> for WherePredicate {
-    fn from_clean(predicate: clean::WherePredicate, renderer: &JsonRenderer<'_>) -> Self {
+    fn from_clean(predicate: &clean::WherePredicate, renderer: &JsonRenderer<'_>) -> Self {
         use clean::WherePredicate::*;
         match predicate {
             BoundPredicate { ty, bounds, bound_params } => WherePredicate::BoundPredicate {
                 type_: ty.into_json(renderer),
                 bounds: bounds.into_json(renderer),
                 generic_params: bound_params
-                    .into_iter()
+                    .iter()
                     .map(|x| {
                         let name = x.name.to_string();
-                        let kind = match x.kind {
+                        let kind = match &x.kind {
                             clean::GenericParamDefKind::Lifetime { outlives } => {
                                 GenericParamDefKind::Lifetime {
                                     outlives: outlives.iter().map(|lt| lt.0.to_string()).collect(),
@@ -442,14 +448,16 @@ impl FromClean<clean::WherePredicate> for WherePredicate {
                                         .into_iter()
                                         .map(|bound| bound.into_json(renderer))
                                         .collect(),
-                                    default: default.map(|ty| (*ty).into_json(renderer)),
-                                    is_synthetic: synthetic,
+                                    default: default
+                                        .as_ref()
+                                        .map(|ty| ty.as_ref().into_json(renderer)),
+                                    is_synthetic: *synthetic,
                                 }
                             }
                             clean::GenericParamDefKind::Const { ty, default, synthetic: _ } => {
                                 GenericParamDefKind::Const {
-                                    type_: (*ty).into_json(renderer),
-                                    default: default.map(|d| *d),
+                                    type_: ty.as_ref().into_json(renderer),
+                                    default: default.as_ref().map(|d| d.as_ref().clone()),
                                 }
                             }
                         };
@@ -462,7 +470,7 @@ impl FromClean<clean::WherePredicate> for WherePredicate {
                 outlives: bounds
                     .iter()
                     .map(|bound| match bound {
-                        clean::GenericBound::Outlives(lt) => convert_lifetime(*lt),
+                        clean::GenericBound::Outlives(lt) => convert_lifetime(lt),
                         _ => bug!("found non-outlives-bound on lifetime predicate"),
                     })
                     .collect(),
@@ -479,7 +487,7 @@ impl FromClean<clean::WherePredicate> for WherePredicate {
 }
 
 impl FromClean<clean::GenericBound> for GenericBound {
-    fn from_clean(bound: clean::GenericBound, renderer: &JsonRenderer<'_>) -> Self {
+    fn from_clean(bound: &clean::GenericBound, renderer: &JsonRenderer<'_>) -> Self {
         use clean::GenericBound::*;
         match bound {
             TraitBound(clean::PolyTrait { trait_, generic_params }, modifier) => {
@@ -494,7 +502,7 @@ impl FromClean<clean::GenericBound> for GenericBound {
                 args.iter()
                     .map(|arg| match arg {
                         clean::PreciseCapturingArg::Lifetime(lt) => {
-                            PreciseCapturingArg::Lifetime(convert_lifetime(*lt))
+                            PreciseCapturingArg::Lifetime(convert_lifetime(lt))
                         }
                         clean::PreciseCapturingArg::Param(param) => {
                             PreciseCapturingArg::Param(param.to_string())
@@ -507,7 +515,7 @@ impl FromClean<clean::GenericBound> for GenericBound {
 }
 
 pub(crate) fn from_trait_bound_modifier(
-    modifiers: rustc_hir::TraitBoundModifiers,
+    modifiers: &rustc_hir::TraitBoundModifiers,
 ) -> TraitBoundModifier {
     use rustc_hir as hir;
     let hir::TraitBoundModifiers { constness, polarity } = modifiers;
@@ -523,7 +531,7 @@ pub(crate) fn from_trait_bound_modifier(
 }
 
 impl FromClean<clean::Type> for Type {
-    fn from_clean(ty: clean::Type, renderer: &JsonRenderer<'_>) -> Self {
+    fn from_clean(ty: &clean::Type, renderer: &JsonRenderer<'_>) -> Self {
         use clean::Type::{
             Array, BareFunction, BorrowedRef, Generic, ImplTrait, Infer, Primitive, QPath,
             RawPointer, SelfTy, Slice, Tuple, UnsafeBinder,
@@ -532,35 +540,35 @@ impl FromClean<clean::Type> for Type {
         match ty {
             clean::Type::Path { path } => Type::ResolvedPath(path.into_json(renderer)),
             clean::Type::DynTrait(bounds, lt) => Type::DynTrait(DynTrait {
-                lifetime: lt.map(convert_lifetime),
+                lifetime: lt.as_ref().map(convert_lifetime),
                 traits: bounds.into_json(renderer),
             }),
             Generic(s) => Type::Generic(s.to_string()),
             // FIXME: add dedicated variant to json Type?
             SelfTy => Type::Generic("Self".to_owned()),
             Primitive(p) => Type::Primitive(p.as_sym().to_string()),
-            BareFunction(f) => Type::FunctionPointer(Box::new((*f).into_json(renderer))),
+            BareFunction(f) => Type::FunctionPointer(Box::new(f.as_ref().into_json(renderer))),
             Tuple(t) => Type::Tuple(t.into_json(renderer)),
-            Slice(t) => Type::Slice(Box::new((*t).into_json(renderer))),
+            Slice(t) => Type::Slice(Box::new(t.as_ref().into_json(renderer))),
             Array(t, s) => {
-                Type::Array { type_: Box::new((*t).into_json(renderer)), len: s.to_string() }
+                Type::Array { type_: Box::new(t.as_ref().into_json(renderer)), len: s.to_string() }
             }
             clean::Type::Pat(t, p) => Type::Pat {
-                type_: Box::new((*t).into_json(renderer)),
+                type_: Box::new(t.as_ref().into_json(renderer)),
                 __pat_unstable_do_not_use: p.to_string(),
             },
             ImplTrait(g) => Type::ImplTrait(g.into_json(renderer)),
             Infer => Type::Infer,
             RawPointer(mutability, type_) => Type::RawPointer {
-                is_mutable: mutability == ast::Mutability::Mut,
-                type_: Box::new((*type_).into_json(renderer)),
+                is_mutable: *mutability == ast::Mutability::Mut,
+                type_: Box::new(type_.as_ref().into_json(renderer)),
             },
             BorrowedRef { lifetime, mutability, type_ } => Type::BorrowedRef {
-                lifetime: lifetime.map(convert_lifetime),
-                is_mutable: mutability == ast::Mutability::Mut,
-                type_: Box::new((*type_).into_json(renderer)),
+                lifetime: lifetime.as_ref().map(convert_lifetime),
+                is_mutable: *mutability == ast::Mutability::Mut,
+                type_: Box::new(type_.as_ref().into_json(renderer)),
             },
-            QPath(qpath) => (*qpath).into_json(renderer),
+            QPath(qpath) => qpath.as_ref().into_json(renderer),
             // FIXME(unsafe_binder): Implement rustdoc-json.
             UnsafeBinder(_) => todo!(),
         }
@@ -568,30 +576,30 @@ impl FromClean<clean::Type> for Type {
 }
 
 impl FromClean<clean::Path> for Path {
-    fn from_clean(path: clean::Path, renderer: &JsonRenderer<'_>) -> Path {
+    fn from_clean(path: &clean::Path, renderer: &JsonRenderer<'_>) -> Path {
         Path {
             path: path.whole_name(),
             id: renderer.id_from_item_default(path.def_id().into()),
-            args: path.segments.last().map(|args| Box::new(args.clone().args.into_json(renderer))),
+            args: path.segments.last().map(|args| Box::new(args.args.into_json(renderer))),
         }
     }
 }
 
 impl FromClean<clean::QPathData> for Type {
-    fn from_clean(qpath: clean::QPathData, renderer: &JsonRenderer<'_>) -> Self {
+    fn from_clean(qpath: &clean::QPathData, renderer: &JsonRenderer<'_>) -> Self {
         let clean::QPathData { assoc, self_type, should_fully_qualify: _, trait_ } = qpath;
 
         Self::QualifiedPath {
             name: assoc.name.to_string(),
             args: Box::new(assoc.args.into_json(renderer)),
             self_type: Box::new(self_type.into_json(renderer)),
-            trait_: trait_.map(|trait_| trait_.into_json(renderer)),
+            trait_: trait_.as_ref().map(|trait_| trait_.into_json(renderer)),
         }
     }
 }
 
 impl FromClean<clean::Term> for Term {
-    fn from_clean(term: clean::Term, renderer: &JsonRenderer<'_>) -> Term {
+    fn from_clean(term: &clean::Term, renderer: &JsonRenderer<'_>) -> Term {
         match term {
             clean::Term::Type(ty) => Term::Type(ty.into_json(renderer)),
             clean::Term::Constant(c) => Term::Constant(c.into_json(renderer)),
@@ -600,14 +608,14 @@ impl FromClean<clean::Term> for Term {
 }
 
 impl FromClean<clean::BareFunctionDecl> for FunctionPointer {
-    fn from_clean(bare_decl: clean::BareFunctionDecl, renderer: &JsonRenderer<'_>) -> Self {
+    fn from_clean(bare_decl: &clean::BareFunctionDecl, renderer: &JsonRenderer<'_>) -> Self {
         let clean::BareFunctionDecl { safety, generic_params, decl, abi } = bare_decl;
         FunctionPointer {
             header: FunctionHeader {
                 is_unsafe: safety.is_unsafe(),
                 is_const: false,
                 is_async: false,
-                abi: convert_abi(abi),
+                abi: convert_abi(*abi),
             },
             generic_params: generic_params.into_json(renderer),
             sig: decl.into_json(renderer),
@@ -616,7 +624,7 @@ impl FromClean<clean::BareFunctionDecl> for FunctionPointer {
 }
 
 impl FromClean<clean::FnDecl> for FunctionSignature {
-    fn from_clean(decl: clean::FnDecl, renderer: &JsonRenderer<'_>) -> Self {
+    fn from_clean(decl: &clean::FnDecl, renderer: &JsonRenderer<'_>) -> Self {
         let clean::FnDecl { inputs, output, c_variadic } = decl;
         FunctionSignature {
             inputs: inputs
@@ -629,13 +637,13 @@ impl FromClean<clean::FnDecl> for FunctionSignature {
                 })
                 .collect(),
             output: if output.is_unit() { None } else { Some(output.into_json(renderer)) },
-            is_c_variadic: c_variadic,
+            is_c_variadic: *c_variadic,
         }
     }
 }
 
 impl FromClean<clean::Trait> for Trait {
-    fn from_clean(trait_: clean::Trait, renderer: &JsonRenderer<'_>) -> Self {
+    fn from_clean(trait_: &clean::Trait, renderer: &JsonRenderer<'_>) -> Self {
         let tcx = renderer.tcx;
         let is_auto = trait_.is_auto(tcx);
         let is_unsafe = trait_.safety(tcx).is_unsafe();
@@ -645,7 +653,7 @@ impl FromClean<clean::Trait> for Trait {
             is_auto,
             is_unsafe,
             is_dyn_compatible,
-            items: renderer.ids(items),
+            items: renderer.ids(&items),
             generics: generics.into_json(renderer),
             bounds: bounds.into_json(renderer),
             implementations: Vec::new(), // Added in JsonRenderer::item
@@ -655,7 +663,7 @@ impl FromClean<clean::Trait> for Trait {
 
 impl FromClean<clean::PolyTrait> for PolyTrait {
     fn from_clean(
-        clean::PolyTrait { trait_, generic_params }: clean::PolyTrait,
+        clean::PolyTrait { trait_, generic_params }: &clean::PolyTrait,
         renderer: &JsonRenderer<'_>,
     ) -> Self {
         PolyTrait {
@@ -666,14 +674,14 @@ impl FromClean<clean::PolyTrait> for PolyTrait {
 }
 
 impl FromClean<clean::Impl> for Impl {
-    fn from_clean(impl_: clean::Impl, renderer: &JsonRenderer<'_>) -> Self {
+    fn from_clean(impl_: &clean::Impl, renderer: &JsonRenderer<'_>) -> Self {
         let provided_trait_methods = impl_.provided_trait_methods(renderer.tcx);
         let clean::Impl { safety, generics, trait_, for_, items, polarity, kind } = impl_;
         // FIXME: use something like ImplKind in JSON?
         let (is_synthetic, blanket_impl) = match kind {
             clean::ImplKind::Normal | clean::ImplKind::FakeVariadic => (false, None),
             clean::ImplKind::Auto => (true, None),
-            clean::ImplKind::Blanket(ty) => (false, Some(*ty)),
+            clean::ImplKind::Blanket(ty) => (false, Some(ty)),
         };
         let is_negative = match polarity {
             ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => false,
@@ -686,18 +694,18 @@ impl FromClean<clean::Impl> for Impl {
                 .into_iter()
                 .map(|x| x.to_string())
                 .collect(),
-            trait_: trait_.map(|path| path.into_json(renderer)),
+            trait_: trait_.as_ref().map(|path| path.into_json(renderer)),
             for_: for_.into_json(renderer),
-            items: renderer.ids(items),
+            items: renderer.ids(&items),
             is_negative,
             is_synthetic,
-            blanket_impl: blanket_impl.map(|x| x.into_json(renderer)),
+            blanket_impl: blanket_impl.map(|x| x.as_ref().into_json(renderer)),
         }
     }
 }
 
 pub(crate) fn from_function(
-    clean::Function { decl, generics }: clean::Function,
+    clean::Function { decl, generics }: &clean::Function,
     has_body: bool,
     header: rustc_hir::FnHeader,
     renderer: &JsonRenderer<'_>,
@@ -711,30 +719,30 @@ pub(crate) fn from_function(
 }
 
 impl FromClean<clean::Enum> for Enum {
-    fn from_clean(enum_: clean::Enum, renderer: &JsonRenderer<'_>) -> Self {
+    fn from_clean(enum_: &clean::Enum, renderer: &JsonRenderer<'_>) -> Self {
         let has_stripped_variants = enum_.has_stripped_entries();
         let clean::Enum { variants, generics } = enum_;
         Enum {
             generics: generics.into_json(renderer),
             has_stripped_variants,
-            variants: renderer.ids(variants),
+            variants: renderer.ids(&variants.as_slice().raw),
             impls: Vec::new(), // Added in JsonRenderer::item
         }
     }
 }
 
 impl FromClean<clean::Variant> for Variant {
-    fn from_clean(variant: clean::Variant, renderer: &JsonRenderer<'_>) -> Self {
+    fn from_clean(variant: &clean::Variant, renderer: &JsonRenderer<'_>) -> Self {
         use clean::VariantKind::*;
 
-        let discriminant = variant.discriminant.map(|d| d.into_json(renderer));
+        let discriminant = variant.discriminant.as_ref().map(|d| d.into_json(renderer));
 
-        let kind = match variant.kind {
+        let kind = match &variant.kind {
             CLike => VariantKind::Plain,
-            Tuple(fields) => VariantKind::Tuple(renderer.ids_keeping_stripped(fields)),
+            Tuple(fields) => VariantKind::Tuple(renderer.ids_keeping_stripped(&fields)),
             Struct(s) => VariantKind::Struct {
                 has_stripped_fields: s.has_stripped_entries(),
-                fields: renderer.ids(s.fields),
+                fields: renderer.ids(&s.fields),
             },
         };
 
@@ -743,7 +751,7 @@ impl FromClean<clean::Variant> for Variant {
 }
 
 impl FromClean<clean::Discriminant> for Discriminant {
-    fn from_clean(disr: clean::Discriminant, renderer: &JsonRenderer<'_>) -> Self {
+    fn from_clean(disr: &clean::Discriminant, renderer: &JsonRenderer<'_>) -> Self {
         let tcx = renderer.tcx;
         Discriminant {
             // expr is only none if going through the inlining path, which gets
@@ -756,7 +764,7 @@ impl FromClean<clean::Discriminant> for Discriminant {
 }
 
 impl FromClean<clean::Import> for Use {
-    fn from_clean(import: clean::Import, renderer: &JsonRenderer<'_>) -> Self {
+    fn from_clean(import: &clean::Import, renderer: &JsonRenderer<'_>) -> Self {
         use clean::ImportKind::*;
         let (name, is_glob) = match import.kind {
             Simple(s) => (s.to_string(), false),
@@ -775,7 +783,7 @@ impl FromClean<clean::Import> for Use {
 }
 
 impl FromClean<clean::ProcMacro> for ProcMacro {
-    fn from_clean(mac: clean::ProcMacro, _renderer: &JsonRenderer<'_>) -> Self {
+    fn from_clean(mac: &clean::ProcMacro, _renderer: &JsonRenderer<'_>) -> Self {
         ProcMacro {
             kind: from_macro_kind(mac.kind),
             helpers: mac.helpers.iter().map(|x| x.to_string()).collect(),
@@ -792,21 +800,21 @@ pub(crate) fn from_macro_kind(kind: rustc_span::hygiene::MacroKind) -> MacroKind
     }
 }
 
-impl FromClean<Box<clean::TypeAlias>> for TypeAlias {
-    fn from_clean(type_alias: Box<clean::TypeAlias>, renderer: &JsonRenderer<'_>) -> Self {
-        let clean::TypeAlias { type_, generics, item_type: _, inner_type: _ } = *type_alias;
+impl FromClean<clean::TypeAlias> for TypeAlias {
+    fn from_clean(type_alias: &clean::TypeAlias, renderer: &JsonRenderer<'_>) -> Self {
+        let clean::TypeAlias { type_, generics, item_type: _, inner_type: _ } = type_alias;
         TypeAlias { type_: type_.into_json(renderer), generics: generics.into_json(renderer) }
     }
 }
 
 fn convert_static(
-    stat: clean::Static,
-    safety: rustc_hir::Safety,
+    stat: &clean::Static,
+    safety: &rustc_hir::Safety,
     renderer: &JsonRenderer<'_>,
 ) -> Static {
     let tcx = renderer.tcx;
     Static {
-        type_: (*stat.type_).into_json(renderer),
+        type_: stat.type_.as_ref().into_json(renderer),
         is_mutable: stat.mutability == ast::Mutability::Mut,
         is_unsafe: safety.is_unsafe(),
         expr: stat
@@ -817,7 +825,7 @@ fn convert_static(
 }
 
 impl FromClean<clean::TraitAlias> for TraitAlias {
-    fn from_clean(alias: clean::TraitAlias, renderer: &JsonRenderer<'_>) -> Self {
+    fn from_clean(alias: &clean::TraitAlias, renderer: &JsonRenderer<'_>) -> Self {
         TraitAlias {
             generics: alias.generics.into_json(renderer),
             params: alias.bounds.into_json(renderer),
@@ -826,7 +834,7 @@ impl FromClean<clean::TraitAlias> for TraitAlias {
 }
 
 impl FromClean<ItemType> for ItemKind {
-    fn from_clean(kind: ItemType, _renderer: &JsonRenderer<'_>) -> Self {
+    fn from_clean(kind: &ItemType, _renderer: &JsonRenderer<'_>) -> Self {
         use ItemType::*;
         match kind {
             Module => ItemKind::Module,
diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs
index 131a12ce228..2feadce26d0 100644
--- a/src/librustdoc/json/mod.rs
+++ b/src/librustdoc/json/mod.rs
@@ -18,6 +18,7 @@ use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::def_id::{DefId, DefIdSet};
 use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
+use rustc_session::features::StabilityExt;
 use rustc_span::def_id::LOCAL_CRATE;
 use rustdoc_json_types as types;
 // It's important to use the FxHashMap from rustdoc_json_types here, instead of
@@ -36,19 +37,18 @@ use crate::formats::cache::Cache;
 use crate::json::conversions::IntoJson;
 use crate::{clean, try_err};
 
-#[derive(Clone)]
 pub(crate) struct JsonRenderer<'tcx> {
     tcx: TyCtxt<'tcx>,
     /// A mapping of IDs that contains all local items for this crate which gets output as a top
     /// level field of the JSON blob.
-    index: Rc<RefCell<FxHashMap<types::Id, types::Item>>>,
+    index: FxHashMap<types::Id, types::Item>,
     /// The directory where the JSON blob should be written to.
     ///
     /// If this is `None`, the blob will be printed to `stdout` instead.
     out_dir: Option<PathBuf>,
     cache: Rc<Cache>,
     imported_items: DefIdSet,
-    id_interner: Rc<RefCell<ids::IdInterner>>,
+    id_interner: RefCell<ids::IdInterner>,
 }
 
 impl<'tcx> JsonRenderer<'tcx> {
@@ -65,7 +65,7 @@ impl<'tcx> JsonRenderer<'tcx> {
                     .iter()
                     .map(|i| {
                         let item = &i.impl_item;
-                        self.item(item.clone()).unwrap();
+                        self.item(item).unwrap();
                         self.id_from_item(item)
                     })
                     .collect()
@@ -96,7 +96,7 @@ impl<'tcx> JsonRenderer<'tcx> {
                         }
 
                         if item.item_id.is_local() || is_primitive_impl {
-                            self.item(item.clone()).unwrap();
+                            self.item(item).unwrap();
                             Some(self.id_from_item(item))
                         } else {
                             None
@@ -148,7 +148,7 @@ fn target(sess: &rustc_session::Session) -> types::Target {
             .copied()
             .filter(|(_, stability, _)| {
                 // Describe only target features which the user can toggle
-                stability.toggle_allowed().is_ok()
+                stability.is_toggle_permitted(sess).is_ok()
             })
             .map(|(name, stability, implied_features)| {
                 types::TargetFeature {
@@ -164,7 +164,7 @@ fn target(sess: &rustc_session::Session) -> types::Target {
                             // Imply only target features which the user can toggle
                             feature_stability
                                 .get(name)
-                                .map(|stability| stability.toggle_allowed().is_ok())
+                                .map(|stability| stability.is_toggle_permitted(sess).is_ok())
                                 .unwrap_or(false)
                         })
                         .map(String::from)
@@ -197,7 +197,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
         Ok((
             JsonRenderer {
                 tcx,
-                index: Rc::new(RefCell::new(FxHashMap::default())),
+                index: FxHashMap::default(),
                 out_dir: if options.output_to_stdout { None } else { Some(options.output) },
                 cache: Rc::new(cache),
                 imported_items,
@@ -217,7 +217,9 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
     /// Inserts an item into the index. This should be used rather than directly calling insert on
     /// the hashmap because certain items (traits and types) need to have their mappings for trait
     /// implementations filled out before they're inserted.
-    fn item(&mut self, item: clean::Item) -> Result<(), Error> {
+    fn item(&mut self, item: &clean::Item) -> Result<(), Error> {
+        use std::collections::hash_map::Entry;
+
         let item_type = item.type_();
         let item_name = item.name;
         trace!("rendering {item_type} {item_name:?}");
@@ -225,11 +227,11 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
         // Flatten items that recursively store other items. We include orphaned items from
         // stripped modules and etc that are otherwise reachable.
         if let ItemKind::StrippedItem(inner) = &item.kind {
-            inner.inner_items().for_each(|i| self.item(i.clone()).unwrap());
+            inner.inner_items().for_each(|i| self.item(i).unwrap());
         }
 
         // Flatten items that recursively store other items
-        item.kind.inner_items().for_each(|i| self.item(i.clone()).unwrap());
+        item.kind.inner_items().for_each(|i| self.item(i).unwrap());
 
         let item_id = item.item_id;
         if let Some(mut new_item) = self.convert_item(item) {
@@ -272,18 +274,25 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
                 | types::ItemEnum::Macro(_)
                 | types::ItemEnum::ProcMacro(_) => false,
             };
-            let removed = self.index.borrow_mut().insert(new_item.id, new_item.clone());
 
             // FIXME(adotinthevoid): Currently, the index is duplicated. This is a sanity check
             // to make sure the items are unique. The main place this happens is when an item, is
             // reexported in more than one place. See `rustdoc-json/reexport/in_root_and_mod`
-            if let Some(old_item) = removed {
-                // In case of generic implementations (like `impl<T> Trait for T {}`), all the
-                // inner items will be duplicated so we can ignore if they are slightly different.
-                if !can_be_ignored {
-                    assert_eq!(old_item, new_item);
+            match self.index.entry(new_item.id) {
+                Entry::Vacant(entry) => {
+                    entry.insert(new_item);
+                }
+                Entry::Occupied(mut entry) => {
+                    // In case of generic implementations (like `impl<T> Trait for T {}`), all the
+                    // inner items will be duplicated so we can ignore if they are slightly
+                    // different.
+                    let old_item = entry.get_mut();
+                    if !can_be_ignored {
+                        assert_eq!(*old_item, new_item);
+                    }
+                    trace!("replaced {old_item:?}\nwith {new_item:?}");
+                    *old_item = new_item;
                 }
-                trace!("replaced {old_item:?}\nwith {new_item:?}");
             }
         }
 
@@ -295,11 +304,13 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
         unreachable!("RUN_ON_MODULE = false, should never call mod_item_in")
     }
 
-    fn after_krate(&mut self) -> Result<(), Error> {
+    fn after_krate(mut self) -> Result<(), Error> {
         debug!("Done with crate");
 
         let e = ExternalCrate { crate_num: LOCAL_CRATE };
-        let index = (*self.index).clone().into_inner();
+
+        // We've finished using the index, and don't want to clone it, because it is big.
+        let index = std::mem::take(&mut self.index);
 
         // Note that tcx.rust_target_features is inappropriate here because rustdoc tries to run for
         // multiple targets: https://github.com/rust-lang/rust/pull/137632
@@ -324,7 +335,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
                         types::ItemSummary {
                             crate_id: k.krate.as_u32(),
                             path: path.iter().map(|s| s.to_string()).collect(),
-                            kind: kind.into_json(self),
+                            kind: kind.into_json(&self),
                         },
                     )
                 })
diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md
index 3a98217f625..0cfe89ad378 100644
--- a/src/tools/clippy/CHANGELOG.md
+++ b/src/tools/clippy/CHANGELOG.md
@@ -5685,6 +5685,7 @@ Released 2018-09-13
 [`cmp_nan`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_nan
 [`cmp_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_null
 [`cmp_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_owned
+[`coerce_container_to_any`]: https://rust-lang.github.io/rust-clippy/master/index.html#coerce_container_to_any
 [`cognitive_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity
 [`collapsible_else_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_else_if
 [`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
@@ -5736,6 +5737,7 @@ Released 2018-09-13
 [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
 [`doc_nested_refdefs`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_nested_refdefs
 [`doc_overindented_list_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_overindented_list_items
+[`doc_suspicious_footnotes`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_suspicious_footnotes
 [`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons
 [`double_ended_iterator_last`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_ended_iterator_last
 [`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use
@@ -5862,6 +5864,7 @@ Released 2018-09-13
 [`ineffective_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#ineffective_open_options
 [`inefficient_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inefficient_to_string
 [`infallible_destructuring_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#infallible_destructuring_match
+[`infallible_try_from`]: https://rust-lang.github.io/rust-clippy/master/index.html#infallible_try_from
 [`infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#infinite_iter
 [`infinite_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#infinite_loop
 [`inherent_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string
@@ -5888,6 +5891,7 @@ Released 2018-09-13
 [`inverted_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#inverted_saturating_sub
 [`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters
 [`io_other_error`]: https://rust-lang.github.io/rust-clippy/master/index.html#io_other_error
+[`ip_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#ip_constant
 [`is_digit_ascii_radix`]: https://rust-lang.github.io/rust-clippy/master/index.html#is_digit_ascii_radix
 [`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements
 [`items_after_test_module`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_test_module
@@ -6163,6 +6167,7 @@ Released 2018-09-13
 [`pathbuf_init_then_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#pathbuf_init_then_push
 [`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch
 [`permissions_set_readonly_false`]: https://rust-lang.github.io/rust-clippy/master/index.html#permissions_set_readonly_false
+[`pointer_format`]: https://rust-lang.github.io/rust-clippy/master/index.html#pointer_format
 [`pointers_in_nomem_asm_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#pointers_in_nomem_asm_block
 [`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters
 [`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs
index 320462a2c96..08592f2521f 100644
--- a/src/tools/clippy/clippy_dev/src/update_lints.rs
+++ b/src/tools/clippy/clippy_dev/src/update_lints.rs
@@ -73,8 +73,8 @@ pub fn generate_lint_files(
             (
                 "clippy_lints/src/lib.rs",
                 &mut update_text_region_fn(
-                    "// begin lints modules, do not remove this comment, it’s used in `update_lints`\n",
-                    "// end lints modules, do not remove this comment, it’s used in `update_lints`",
+                    "// begin lints modules, do not remove this comment, it's used in `update_lints`\n",
+                    "// end lints modules, do not remove this comment, it's used in `update_lints`",
                     |dst| {
                         for lint_mod in lints.iter().map(|l| &l.module).sorted().dedup() {
                             writeln!(dst, "mod {lint_mod};").unwrap();
diff --git a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs
index 59a0c7c8868..b9ae9afe851 100644
--- a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs
+++ b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs
@@ -204,7 +204,7 @@ impl ArbitrarySourceItemOrdering {
                 self.assoc_types_order
             ),
             Some(before_item.span),
-            format!("should be placed before `{}`", before_item.ident.as_str(),),
+            format!("should be placed before `{}`", before_item.ident.name),
         );
     }
 
@@ -216,7 +216,7 @@ impl ArbitrarySourceItemOrdering {
             ident.span,
             "incorrect ordering of items (must be alphabetically ordered)",
             Some(before_ident.span),
-            format!("should be placed before `{}`", before_ident.as_str(),),
+            format!("should be placed before `{}`", before_ident.name),
         );
     }
 
@@ -228,7 +228,7 @@ impl ArbitrarySourceItemOrdering {
         };
 
         let (before_span, note) = if let Some(ident) = before_item.kind.ident() {
-            (ident.span, format!("should be placed before `{}`", ident.as_str(),))
+            (ident.span, format!("should be placed before `{}`", ident.name))
         } else {
             (
                 before_item.span,
@@ -255,7 +255,7 @@ impl ArbitrarySourceItemOrdering {
                 self.assoc_types_order
             ),
             Some(before_item.span),
-            format!("should be placed before `{}`", before_item.ident.as_str(),),
+            format!("should be placed before `{}`", before_item.ident.name),
         );
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs b/src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs
index 0edb50be8c7..d67a194b020 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs
@@ -61,7 +61,7 @@ pub(super) fn check_clippy(cx: &EarlyContext<'_>, attr: &Attribute) {
 
 fn check_deprecated_cfg_recursively(cx: &EarlyContext<'_>, attr: &rustc_ast::MetaItem) {
     if let Some(ident) = attr.ident() {
-        if ["any", "all", "not"].contains(&ident.name.as_str()) {
+        if matches!(ident.name, sym::any | sym::all | sym::not) {
             let Some(list) = attr.meta_item_list() else { return };
             for item in list.iter().filter_map(|item| item.meta_item()) {
                 check_deprecated_cfg_recursively(cx, item);
diff --git a/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs b/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs
index a851daaede7..c2406bcfb64 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs
@@ -1,9 +1,10 @@
 use super::DUPLICATED_ATTRIBUTES;
 use clippy_utils::diagnostics::span_lint_and_then;
+use itertools::Itertools;
 use rustc_ast::{Attribute, MetaItem};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_lint::EarlyContext;
-use rustc_span::{Span, sym};
+use rustc_span::{Span, Symbol, sym};
 use std::collections::hash_map::Entry;
 
 fn emit_if_duplicated(
@@ -29,7 +30,7 @@ fn check_duplicated_attr(
     cx: &EarlyContext<'_>,
     attr: &MetaItem,
     attr_paths: &mut FxHashMap<String, Span>,
-    parent: &mut Vec<String>,
+    parent: &mut Vec<Symbol>,
 ) {
     if attr.span.from_expansion() {
         return;
@@ -43,7 +44,7 @@ fn check_duplicated_attr(
         return;
     }
     if let Some(direct_parent) = parent.last()
-        && direct_parent == sym::cfg_trace.as_str()
+        && *direct_parent == sym::cfg_trace
         && [sym::all, sym::not, sym::any].contains(&name)
     {
         // FIXME: We don't correctly check `cfg`s for now, so if it's more complex than just a one
@@ -51,9 +52,14 @@ fn check_duplicated_attr(
         return;
     }
     if let Some(value) = attr.value_str() {
-        emit_if_duplicated(cx, attr, attr_paths, format!("{}:{name}={value}", parent.join(":")));
+        emit_if_duplicated(
+            cx,
+            attr,
+            attr_paths,
+            format!("{}:{name}={value}", parent.iter().join(":")),
+        );
     } else if let Some(sub_attrs) = attr.meta_item_list() {
-        parent.push(name.as_str().to_string());
+        parent.push(name);
         for sub_attr in sub_attrs {
             if let Some(meta) = sub_attr.meta_item() {
                 check_duplicated_attr(cx, meta, attr_paths, parent);
@@ -61,7 +67,7 @@ fn check_duplicated_attr(
         }
         parent.pop();
     } else {
-        emit_if_duplicated(cx, attr, attr_paths, format!("{}:{name}", parent.join(":")));
+        emit_if_duplicated(cx, attr, attr_paths, format!("{}:{name}", parent.iter().join(":")));
     }
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs
index 7c6fd91ca67..bf43234ff50 100644
--- a/src/tools/clippy/clippy_lints/src/booleans.rs
+++ b/src/tools/clippy/clippy_lints/src/booleans.rs
@@ -1,10 +1,10 @@
 use clippy_config::Conf;
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
-use clippy_utils::eq_expr_value;
 use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::SpanRangeExt;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
+use clippy_utils::{eq_expr_value, sym};
 use rustc_ast::ast::LitKind;
 use rustc_attr_data_structures::RustcVersion;
 use rustc_errors::Applicability;
@@ -13,7 +13,7 @@ use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, UnOp};
 use rustc_lint::{LateContext, LateLintPass, Level};
 use rustc_session::impl_lint_pass;
 use rustc_span::def_id::LocalDefId;
-use rustc_span::{Span, SyntaxContext, sym};
+use rustc_span::{Span, Symbol, SyntaxContext};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -73,10 +73,10 @@ declare_clippy_lint! {
 }
 
 // For each pairs, both orders are considered.
-const METHODS_WITH_NEGATION: [(Option<RustcVersion>, &str, &str); 3] = [
-    (None, "is_some", "is_none"),
-    (None, "is_err", "is_ok"),
-    (Some(msrvs::IS_NONE_OR), "is_some_and", "is_none_or"),
+const METHODS_WITH_NEGATION: [(Option<RustcVersion>, Symbol, Symbol); 3] = [
+    (None, sym::is_some, sym::is_none),
+    (None, sym::is_err, sym::is_ok),
+    (Some(msrvs::IS_NONE_OR), sym::is_some_and, sym::is_none_or),
 ];
 
 pub struct NonminimalBool {
@@ -440,9 +440,7 @@ fn simplify_not(cx: &LateContext<'_>, curr_msrv: Msrv, expr: &Expr<'_>) -> Optio
                 .iter()
                 .copied()
                 .flat_map(|(msrv, a, b)| vec![(msrv, a, b), (msrv, b, a)])
-                .find(|&(msrv, a, _)| {
-                    a == path.ident.name.as_str() && msrv.is_none_or(|msrv| curr_msrv.meets(cx, msrv))
-                })
+                .find(|&(msrv, a, _)| a == path.ident.name && msrv.is_none_or(|msrv| curr_msrv.meets(cx, msrv)))
                 .and_then(|(_, _, neg_method)| {
                     let negated_args = args
                         .iter()
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs
index 0f066fae118..c1d6cec1b62 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs
@@ -76,19 +76,20 @@ fn should_lint(cx: &LateContext<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, msrv: M
         return false;
     }
 
-    match (cast_from.is_integral(), cast_to.is_integral()) {
-        (true, true) => {
+    match (
+        utils::int_ty_to_nbits(cx.tcx, cast_from),
+        utils::int_ty_to_nbits(cx.tcx, cast_to),
+    ) {
+        (Some(from_nbits), Some(to_nbits)) => {
             let cast_signed_to_unsigned = cast_from.is_signed() && !cast_to.is_signed();
-            let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx);
-            let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
             !is_isize_or_usize(cast_from)
                 && !is_isize_or_usize(cast_to)
                 && from_nbits < to_nbits
                 && !cast_signed_to_unsigned
         },
 
-        (true, false) => {
-            let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx);
+        (Some(from_nbits), None) => {
+            // FIXME: handle `f16` and `f128`
             let to_nbits = if let ty::Float(FloatTy::F32) = cast_to.kind() {
                 32
             } else {
@@ -96,9 +97,7 @@ fn should_lint(cx: &LateContext<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, msrv: M
             };
             !is_isize_or_usize(cast_from) && from_nbits < to_nbits
         },
-        (false, true) if matches!(cast_from.kind(), ty::Bool) && msrv.meets(cx, msrvs::FROM_BOOL) => true,
-        (_, _) => {
-            matches!(cast_from.kind(), ty::Float(FloatTy::F32)) && matches!(cast_to.kind(), ty::Float(FloatTy::F64))
-        },
+        (None, Some(_)) if cast_from.is_bool() && msrv.meets(cx, msrvs::FROM_BOOL) => true,
+        _ => matches!(cast_from.kind(), ty::Float(FloatTy::F32)) && matches!(cast_to.kind(), ty::Float(FloatTy::F64)),
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs
index 4120e5c8cb7..a2ecb5fb44a 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs
@@ -91,15 +91,14 @@ pub(super) fn check(
     cast_to: Ty<'_>,
     cast_to_span: Span,
 ) {
-    let msg = match (cast_from.kind(), cast_to.is_integral()) {
-        (ty::Int(_) | ty::Uint(_), true) => {
+    let msg = match (cast_from.kind(), utils::int_ty_to_nbits(cx.tcx, cast_to)) {
+        (ty::Int(_) | ty::Uint(_), Some(to_nbits)) => {
             let from_nbits = apply_reductions(
                 cx,
-                utils::int_ty_to_nbits(cast_from, cx.tcx),
+                utils::int_ty_to_nbits(cx.tcx, cast_from).unwrap(),
                 cast_expr,
                 cast_from.is_signed(),
             );
-            let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
 
             let (should_lint, suffix) = match (is_isize_or_usize(cast_from), is_isize_or_usize(cast_to)) {
                 (true, true) | (false, false) => (to_nbits < from_nbits, ""),
@@ -121,7 +120,7 @@ pub(super) fn check(
             format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}",)
         },
 
-        (ty::Adt(def, _), true) if def.is_enum() => {
+        (ty::Adt(def, _), Some(to_nbits)) if def.is_enum() => {
             let (from_nbits, variant) = if let ExprKind::Path(p) = &cast_expr.kind
                 && let Res::Def(DefKind::Ctor(..), id) = cx.qpath_res(p, cast_expr.hir_id)
             {
@@ -132,7 +131,6 @@ pub(super) fn check(
             } else {
                 (utils::enum_ty_to_nbits(*def, cx.tcx), None)
             };
-            let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
 
             let cast_from_ptr_size = def.repr().int.is_none_or(|ty| matches!(ty, IntegerType::Pointer(_),));
             let suffix = match (cast_from_ptr_size, is_isize_or_usize(cast_to)) {
@@ -157,11 +155,11 @@ pub(super) fn check(
             format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}")
         },
 
-        (ty::Float(_), true) => {
+        (ty::Float(_), Some(_)) => {
             format!("casting `{cast_from}` to `{cast_to}` may truncate the value")
         },
 
-        (ty::Float(FloatTy::F64), false) if matches!(cast_to.kind(), &ty::Float(FloatTy::F32)) => {
+        (ty::Float(FloatTy::F64), None) if matches!(cast_to.kind(), &ty::Float(FloatTy::F32)) => {
             "casting `f64` to `f32` may truncate the value".to_string()
         },
 
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs b/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs
index 504d0a267e4..e26c03ccda9 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs
@@ -17,9 +17,12 @@ enum EmitState {
 }
 
 pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
-    if !(cast_from.is_integral() && cast_to.is_integral()) {
+    let (Some(from_nbits), Some(to_nbits)) = (
+        utils::int_ty_to_nbits(cx.tcx, cast_from),
+        utils::int_ty_to_nbits(cx.tcx, cast_to),
+    ) else {
         return;
-    }
+    };
 
     // emit a lint if a cast is:
     // 1. unsigned to signed
@@ -35,9 +38,6 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, ca
         return;
     }
 
-    let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx);
-    let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
-
     let should_lint = match (cast_from.is_ptr_sized_integral(), cast_to.is_ptr_sized_integral()) {
         (true, true) => {
             // casts between two ptr sized integers are trivially always the same size
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_precision_loss.rs b/src/tools/clippy/clippy_lints/src/casts/cast_precision_loss.rs
index 1eb115ce6bd..712e38db499 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_precision_loss.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_precision_loss.rs
@@ -7,15 +7,14 @@ use rustc_middle::ty::{self, FloatTy, Ty};
 use super::{CAST_PRECISION_LOSS, utils};
 
 pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
-    if !cast_from.is_integral() || cast_to.is_integral() {
+    let Some(from_nbits) = utils::int_ty_to_nbits(cx.tcx, cast_from) else {
         return;
-    }
+    };
 
-    let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx);
-    let to_nbits = if cast_to.kind() == &ty::Float(FloatTy::F32) {
-        32
-    } else {
-        64
+    // FIXME: handle `f16` and `f128`
+    let to_nbits = match cast_to.kind() {
+        ty::Float(f @ (FloatTy::F32 | FloatTy::F64)) => f.bit_width(),
+        _ => return,
     };
 
     if !(is_isize_or_usize(cast_from) || from_nbits >= to_nbits) {
@@ -29,9 +28,10 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, ca
     let from_nbits_str = if arch_dependent {
         "64".to_owned()
     } else if is_isize_or_usize(cast_from) {
+        // FIXME: handle 16 bits `usize` type
         "32 or 64".to_owned()
     } else {
-        utils::int_ty_to_nbits(cast_from, cx.tcx).to_string()
+        from_nbits.to_string()
     };
 
     span_lint(
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs
index 01020f3eee2..e4dafde0f9d 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs
@@ -61,7 +61,7 @@ fn is_used_as_unaligned(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
     };
     match parent.kind {
         ExprKind::MethodCall(name, self_arg, ..) if self_arg.hir_id == e.hir_id => {
-            if matches!(name.ident.as_str(), "read_unaligned" | "write_unaligned")
+            if matches!(name.ident.name, sym::read_unaligned | sym::write_unaligned)
                 && let Some(def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id)
                 && let Some(def_id) = cx.tcx.impl_of_method(def_id)
                 && cx.tcx.type_of(def_id).instantiate_identity().is_raw_ptr()
diff --git a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast.rs b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast.rs
index ac1a355c8d9..105477093b5 100644
--- a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast.rs
@@ -3,24 +3,22 @@ use clippy_utils::source::snippet_with_applicability;
 use rustc_errors::Applicability;
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
-use rustc_middle::ty::{self, Ty, UintTy};
+use rustc_middle::ty::{self, Ty};
 
 use super::{FN_TO_NUMERIC_CAST, utils};
 
 pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
     // We only want to check casts to `ty::Uint` or `ty::Int`
-    match cast_to.kind() {
-        ty::Uint(_) | ty::Int(..) => { /* continue on */ },
-        _ => return,
-    }
+    let Some(to_nbits) = utils::int_ty_to_nbits(cx.tcx, cast_to) else {
+        return;
+    };
 
     match cast_from.kind() {
         ty::FnDef(..) | ty::FnPtr(..) => {
             let mut applicability = Applicability::MaybeIncorrect;
-            let from_snippet = snippet_with_applicability(cx, cast_expr.span, "x", &mut applicability);
-            let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
 
-            if (to_nbits >= cx.tcx.data_layout.pointer_size.bits()) && (*cast_to.kind() != ty::Uint(UintTy::Usize)) {
+            if to_nbits >= cx.tcx.data_layout.pointer_size.bits() && !cast_to.is_usize() {
+                let from_snippet = snippet_with_applicability(cx, cast_expr.span, "x", &mut applicability);
                 span_lint_and_sugg(
                     cx,
                     FN_TO_NUMERIC_CAST,
diff --git a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs
index 18e7798452e..700b7d0d426 100644
--- a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs
@@ -9,16 +9,14 @@ use super::{FN_TO_NUMERIC_CAST_WITH_TRUNCATION, utils};
 
 pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
     // We only want to check casts to `ty::Uint` or `ty::Int`
-    match cast_to.kind() {
-        ty::Uint(_) | ty::Int(..) => { /* continue on */ },
-        _ => return,
-    }
+    let Some(to_nbits) = utils::int_ty_to_nbits(cx.tcx, cast_to) else {
+        return;
+    };
     match cast_from.kind() {
         ty::FnDef(..) | ty::FnPtr(..) => {
             let mut applicability = Applicability::MaybeIncorrect;
             let from_snippet = snippet_with_applicability(cx, cast_expr.span, "x", &mut applicability);
 
-            let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
             if to_nbits < cx.tcx.data_layout.pointer_size.bits() {
                 span_lint_and_sugg(
                     cx,
diff --git a/src/tools/clippy/clippy_lints/src/casts/utils.rs b/src/tools/clippy/clippy_lints/src/casts/utils.rs
index 5ccba92a0af..318a1646477 100644
--- a/src/tools/clippy/clippy_lints/src/casts/utils.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/utils.rs
@@ -1,27 +1,14 @@
 use clippy_utils::ty::{EnumValue, read_explicit_enum_value};
 use rustc_middle::ty::{self, AdtDef, IntTy, Ty, TyCtxt, UintTy, VariantDiscr};
 
-/// Returns the size in bits of an integral type.
-/// Will return 0 if the type is not an int or uint variant
-pub(super) fn int_ty_to_nbits(typ: Ty<'_>, tcx: TyCtxt<'_>) -> u64 {
-    match typ.kind() {
-        ty::Int(i) => match i {
-            IntTy::Isize => tcx.data_layout.pointer_size.bits(),
-            IntTy::I8 => 8,
-            IntTy::I16 => 16,
-            IntTy::I32 => 32,
-            IntTy::I64 => 64,
-            IntTy::I128 => 128,
-        },
-        ty::Uint(i) => match i {
-            UintTy::Usize => tcx.data_layout.pointer_size.bits(),
-            UintTy::U8 => 8,
-            UintTy::U16 => 16,
-            UintTy::U32 => 32,
-            UintTy::U64 => 64,
-            UintTy::U128 => 128,
-        },
-        _ => 0,
+/// Returns the size in bits of an integral type, or `None` if `ty` is not an
+/// integral type.
+pub(super) fn int_ty_to_nbits(tcx: TyCtxt<'_>, ty: Ty<'_>) -> Option<u64> {
+    match ty.kind() {
+        ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => Some(tcx.data_layout.pointer_size.bits()),
+        ty::Int(i) => i.bit_width(),
+        ty::Uint(i) => i.bit_width(),
+        _ => None,
     }
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/checked_conversions.rs b/src/tools/clippy/clippy_lints/src/checked_conversions.rs
index 8ada608049c..9b3822f9d8f 100644
--- a/src/tools/clippy/clippy_lints/src/checked_conversions.rs
+++ b/src/tools/clippy/clippy_lints/src/checked_conversions.rs
@@ -2,11 +2,12 @@ use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{SpanlessEq, is_in_const_context, is_integer_literal};
+use clippy_utils::{SpanlessEq, is_in_const_context, is_integer_literal, sym};
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind, QPath, TyKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_session::impl_lint_pass;
+use rustc_span::Symbol;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -98,7 +99,7 @@ impl LateLintPass<'_> for CheckedConversions {
 struct Conversion<'a> {
     cvt: ConversionType,
     expr_to_cast: &'a Expr<'a>,
-    to_type: Option<&'a str>,
+    to_type: Option<Symbol>,
 }
 
 /// The kind of conversion that is checked
@@ -150,7 +151,7 @@ impl<'a> Conversion<'a> {
     }
 
     /// Try to construct a new conversion if the conversion type is valid
-    fn try_new(expr_to_cast: &'a Expr<'_>, from_type: &str, to_type: &'a str) -> Option<Conversion<'a>> {
+    fn try_new(expr_to_cast: &'a Expr<'_>, from_type: Symbol, to_type: Symbol) -> Option<Conversion<'a>> {
         ConversionType::try_new(from_type, to_type).map(|cvt| Conversion {
             cvt,
             expr_to_cast,
@@ -171,7 +172,7 @@ impl<'a> Conversion<'a> {
 impl ConversionType {
     /// Creates a conversion type if the type is allowed & conversion is valid
     #[must_use]
-    fn try_new(from: &str, to: &str) -> Option<Self> {
+    fn try_new(from: Symbol, to: Symbol) -> Option<Self> {
         if UINTS.contains(&from) {
             Some(Self::FromUnsigned)
         } else if SINTS.contains(&from) {
@@ -190,7 +191,7 @@ impl ConversionType {
 
 /// Check for `expr <= (to_type::MAX as from_type)`
 fn check_upper_bound<'tcx>(lt: &'tcx Expr<'tcx>, gt: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> {
-    if let Some((from, to)) = get_types_from_cast(gt, INTS, "max_value", "MAX") {
+    if let Some((from, to)) = get_types_from_cast(gt, INTS, sym::max_value, sym::MAX) {
         Conversion::try_new(lt, from, to)
     } else {
         None
@@ -209,7 +210,7 @@ fn check_lower_bound_zero<'a>(candidate: &'a Expr<'_>, check: &'a Expr<'_>) -> O
 
 /// Check for `expr >= (to_type::MIN as from_type)`
 fn check_lower_bound_min<'a>(candidate: &'a Expr<'_>, check: &'a Expr<'_>) -> Option<Conversion<'a>> {
-    if let Some((from, to)) = get_types_from_cast(check, SINTS, "min_value", "MIN") {
+    if let Some((from, to)) = get_types_from_cast(check, SINTS, sym::min_value, sym::MIN) {
         Conversion::try_new(candidate, from, to)
     } else {
         None
@@ -217,15 +218,15 @@ fn check_lower_bound_min<'a>(candidate: &'a Expr<'_>, check: &'a Expr<'_>) -> Op
 }
 
 /// Tries to extract the from- and to-type from a cast expression
-fn get_types_from_cast<'a>(
-    expr: &'a Expr<'_>,
-    types: &'a [&str],
-    func: &'a str,
-    assoc_const: &'a str,
-) -> Option<(&'a str, &'a str)> {
+fn get_types_from_cast(
+    expr: &Expr<'_>,
+    types: &[Symbol],
+    func: Symbol,
+    assoc_const: Symbol,
+) -> Option<(Symbol, Symbol)> {
     // `to_type::max_value() as from_type`
     // or `to_type::MAX as from_type`
-    let call_from_cast: Option<(&Expr<'_>, &str)> = if let ExprKind::Cast(limit, from_type) = &expr.kind
+    let call_from_cast: Option<(&Expr<'_>, Symbol)> = if let ExprKind::Cast(limit, from_type) = &expr.kind
         // to_type::max_value(), from_type
         && let TyKind::Path(from_type_path) = &from_type.kind
         && let Some(from_sym) = int_ty_to_sym(from_type_path)
@@ -236,12 +237,12 @@ fn get_types_from_cast<'a>(
     };
 
     // `from_type::from(to_type::max_value())`
-    let limit_from: Option<(&Expr<'_>, &str)> = call_from_cast.or_else(|| {
+    let limit_from: Option<(&Expr<'_>, Symbol)> = call_from_cast.or_else(|| {
         if let ExprKind::Call(from_func, [limit]) = &expr.kind
             // `from_type::from, to_type::max_value()`
             // `from_type::from`
             && let ExprKind::Path(path) = &from_func.kind
-            && let Some(from_sym) = get_implementing_type(path, INTS, "from")
+            && let Some(from_sym) = get_implementing_type(path, INTS, sym::from)
         {
             Some((limit, from_sym))
         } else {
@@ -273,32 +274,41 @@ fn get_types_from_cast<'a>(
 }
 
 /// Gets the type which implements the called function
-fn get_implementing_type<'a>(path: &QPath<'_>, candidates: &'a [&str], function: &str) -> Option<&'a str> {
+fn get_implementing_type(path: &QPath<'_>, candidates: &[Symbol], function: Symbol) -> Option<Symbol> {
     if let QPath::TypeRelative(ty, path) = &path
-        && path.ident.name.as_str() == function
+        && path.ident.name == function
         && let TyKind::Path(QPath::Resolved(None, tp)) = &ty.kind
         && let [int] = tp.segments
     {
-        let name = int.ident.name.as_str();
-        candidates.iter().find(|c| &name == *c).copied()
+        candidates.iter().find(|c| int.ident.name == **c).copied()
     } else {
         None
     }
 }
 
 /// Gets the type as a string, if it is a supported integer
-fn int_ty_to_sym<'tcx>(path: &QPath<'_>) -> Option<&'tcx str> {
+fn int_ty_to_sym(path: &QPath<'_>) -> Option<Symbol> {
     if let QPath::Resolved(_, path) = *path
         && let [ty] = path.segments
     {
-        let name = ty.ident.name.as_str();
-        INTS.iter().find(|c| &name == *c).copied()
+        INTS.iter().find(|c| ty.ident.name == **c).copied()
     } else {
         None
     }
 }
 
 // Constants
-const UINTS: &[&str] = &["u8", "u16", "u32", "u64", "usize"];
-const SINTS: &[&str] = &["i8", "i16", "i32", "i64", "isize"];
-const INTS: &[&str] = &["u8", "u16", "u32", "u64", "usize", "i8", "i16", "i32", "i64", "isize"];
+const UINTS: &[Symbol] = &[sym::u8, sym::u16, sym::u32, sym::u64, sym::usize];
+const SINTS: &[Symbol] = &[sym::i8, sym::i16, sym::i32, sym::i64, sym::isize];
+const INTS: &[Symbol] = &[
+    sym::u8,
+    sym::u16,
+    sym::u32,
+    sym::u64,
+    sym::usize,
+    sym::i8,
+    sym::i16,
+    sym::i32,
+    sym::i64,
+    sym::isize,
+];
diff --git a/src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs b/src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs
index 6b239a1541b..e33a8e0fb74 100644
--- a/src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs
+++ b/src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs
@@ -17,7 +17,7 @@ declare_clippy_lint! {
     ///
     /// ### Why is this bad
     ///
-    /// A reference does not need to be owned in order to used as a slice.
+    /// A reference does not need to be owned in order to be used as a slice.
     ///
     /// ### Known problems
     ///
diff --git a/src/tools/clippy/clippy_lints/src/coerce_container_to_any.rs b/src/tools/clippy/clippy_lints/src/coerce_container_to_any.rs
new file mode 100644
index 00000000000..2b659253763
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/coerce_container_to_any.rs
@@ -0,0 +1,108 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use clippy_utils::sym;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::{self, ExistentialPredicate, Ty, TyCtxt};
+use rustc_session::declare_lint_pass;
+
+declare_clippy_lint! {
+    /// ### What it does
+    ///
+    /// Protects against unintended coercion of references to container types to `&dyn Any` when the
+    /// container type dereferences to a `dyn Any` which could be directly referenced instead.
+    ///
+    /// ### Why is this bad?
+    ///
+    /// The intention is usually to get a reference to the `dyn Any` the value dereferences to,
+    /// rather than coercing a reference to the container itself to `&dyn Any`.
+    ///
+    /// ### Example
+    ///
+    /// Because `Box<dyn Any>` itself implements `Any`, `&Box<dyn Any>`
+    /// can be coerced to an `&dyn Any` which refers to *the `Box` itself*, rather than the
+    /// inner `dyn Any`.
+    /// ```no_run
+    /// # use std::any::Any;
+    /// let x: Box<dyn Any> = Box::new(0u32);
+    /// let dyn_any_of_box: &dyn Any = &x;
+    ///
+    /// // Fails as we have a &dyn Any to the Box, not the u32
+    /// assert_eq!(dyn_any_of_box.downcast_ref::<u32>(), None);
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// # use std::any::Any;
+    /// let x: Box<dyn Any> = Box::new(0u32);
+    /// let dyn_any_of_u32: &dyn Any = &*x;
+    ///
+    /// // Succeeds since we have a &dyn Any to the inner u32!
+    /// assert_eq!(dyn_any_of_u32.downcast_ref::<u32>(), Some(&0u32));
+    /// ```
+    #[clippy::version = "1.88.0"]
+    pub COERCE_CONTAINER_TO_ANY,
+    nursery,
+    "coercing to `&dyn Any` when dereferencing could produce a `dyn Any` without coercion is usually not intended"
+}
+declare_lint_pass!(CoerceContainerToAny => [COERCE_CONTAINER_TO_ANY]);
+
+impl<'tcx> LateLintPass<'tcx> for CoerceContainerToAny {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
+        // If this expression has an effective type of `&dyn Any` ...
+        {
+            let coerced_ty = cx.typeck_results().expr_ty_adjusted(e);
+
+            let ty::Ref(_, coerced_ref_ty, _) = *coerced_ty.kind() else {
+                return;
+            };
+            if !is_dyn_any(cx.tcx, coerced_ref_ty) {
+                return;
+            }
+        }
+
+        let expr_ty = cx.typeck_results().expr_ty(e);
+        let ty::Ref(_, expr_ref_ty, _) = *expr_ty.kind() else {
+            return;
+        };
+        // ... but only due to coercion ...
+        if is_dyn_any(cx.tcx, expr_ref_ty) {
+            return;
+        }
+        // ... and it also *derefs* to `dyn Any` ...
+        let Some((depth, target)) = clippy_utils::ty::deref_chain(cx, expr_ref_ty).enumerate().last() else {
+            return;
+        };
+        if !is_dyn_any(cx.tcx, target) {
+            return;
+        }
+
+        // ... that's probably not intended.
+        let (span, deref_count) = match e.kind {
+            // If `e` was already an `&` expression, skip `*&` in the suggestion
+            ExprKind::AddrOf(_, _, referent) => (referent.span, depth),
+            _ => (e.span, depth + 1),
+        };
+        span_lint_and_sugg(
+            cx,
+            COERCE_CONTAINER_TO_ANY,
+            e.span,
+            format!("coercing `{expr_ty}` to `&dyn Any`"),
+            "consider dereferencing",
+            format!("&{}{}", str::repeat("*", deref_count), snippet(cx, span, "x")),
+            Applicability::MaybeIncorrect,
+        );
+    }
+}
+
+fn is_dyn_any(tcx: TyCtxt<'_>, ty: Ty<'_>) -> bool {
+    let ty::Dynamic(traits, ..) = ty.kind() else {
+        return false;
+    };
+    traits.iter().any(|binder| {
+        let ExistentialPredicate::Trait(t) = binder.skip_binder() else {
+            return false;
+        };
+        tcx.is_diagnostic_item(sym::Any, t.def_id)
+    })
+}
diff --git a/src/tools/clippy/clippy_lints/src/copies.rs b/src/tools/clippy/clippy_lints/src/copies.rs
index 2467fc95fd0..5ef726638a5 100644
--- a/src/tools/clippy/clippy_lints/src/copies.rs
+++ b/src/tools/clippy/clippy_lints/src/copies.rs
@@ -425,7 +425,9 @@ fn scan_block_for_eq<'tcx>(
             modifies_any_local(cx, stmt, &cond_locals)
                 || !eq_stmts(stmt, blocks, |b| b.stmts.get(i), &mut eq, &mut moved_locals)
         })
-        .map_or(block.stmts.len(), |(i, _)| i);
+        .map_or(block.stmts.len(), |(i, stmt)| {
+            adjust_by_closest_callsite(i, stmt, block.stmts[..i].iter().enumerate().rev())
+        });
 
     if local_needs_ordered_drop {
         return BlockEq {
@@ -467,7 +469,9 @@ fn scan_block_for_eq<'tcx>(
                     .is_none_or(|s| hash != hash_stmt(cx, s))
             })
         })
-        .map_or(block.stmts.len() - start_end_eq, |(i, _)| i);
+        .map_or(block.stmts.len() - start_end_eq, |(i, stmt)| {
+            adjust_by_closest_callsite(i, stmt, (0..i).rev().zip(block.stmts[(block.stmts.len() - i)..].iter()))
+        });
 
     let moved_locals_at_start = moved_locals.len();
     let mut i = end_search_start;
@@ -522,6 +526,49 @@ fn scan_block_for_eq<'tcx>(
     }
 }
 
+/// Adjusts the index for which the statements begin to differ to the closest macro callsite. This
+/// avoids giving suggestions that requires splitting a macro call in half, when only a part of the
+/// macro expansion is equal.
+///
+/// For example, for the following macro:
+/// ```rust,ignore
+/// macro_rules! foo {
+///    ($x:expr) => {
+///        let y = 42;
+///        $x;
+///    };
+/// }
+/// ```
+/// If the macro is called like this:
+/// ```rust,ignore
+/// if false {
+///    let z = 42;
+///    foo!(println!("Hello"));
+/// } else {
+///    let z = 42;
+///    foo!(println!("World"));
+/// }
+/// ```
+/// Although the expanded `let y = 42;` is equal, the macro call should not be included in the
+/// suggestion.
+fn adjust_by_closest_callsite<'tcx>(
+    i: usize,
+    stmt: &'tcx Stmt<'tcx>,
+    mut iter: impl Iterator<Item = (usize, &'tcx Stmt<'tcx>)>,
+) -> usize {
+    let Some((_, first)) = iter.next() else {
+        return 0;
+    };
+
+    // If it is already at the boundary of a macro call, then just return.
+    if first.span.source_callsite() != stmt.span.source_callsite() {
+        return i;
+    }
+
+    iter.find(|(_, stmt)| stmt.span.source_callsite() != first.span.source_callsite())
+        .map_or(0, |(i, _)| i + 1)
+}
+
 fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &[(HirId, Symbol)], if_expr: &Expr<'_>) -> bool {
     get_enclosing_block(cx, if_expr.hir_id).is_some_and(|block| {
         let ignore_span = block.span.shrink_to_lo().to(if_expr.span);
diff --git a/src/tools/clippy/clippy_lints/src/create_dir.rs b/src/tools/clippy/clippy_lints/src/create_dir.rs
index b43906903a0..695b25aeb0b 100644
--- a/src/tools/clippy/clippy_lints/src/create_dir.rs
+++ b/src/tools/clippy/clippy_lints/src/create_dir.rs
@@ -1,7 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::snippet_with_applicability;
 use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind};
+use rustc_hir::{Expr, ExprKind, QPath};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
 use rustc_span::sym;
@@ -34,10 +33,12 @@ declare_lint_pass!(CreateDir => [CREATE_DIR]);
 
 impl LateLintPass<'_> for CreateDir {
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
-        if let ExprKind::Call(func, [arg]) = expr.kind
+        if let ExprKind::Call(func, [_]) = expr.kind
             && let ExprKind::Path(ref path) = func.kind
             && let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id()
             && cx.tcx.is_diagnostic_item(sym::fs_create_dir, def_id)
+            && let QPath::Resolved(_, path) = path
+            && let Some(last) = path.segments.last()
         {
             span_lint_and_then(
                 cx,
@@ -45,15 +46,15 @@ impl LateLintPass<'_> for CreateDir {
                 expr.span,
                 "calling `std::fs::create_dir` where there may be a better way",
                 |diag| {
-                    let mut app = Applicability::MaybeIncorrect;
-                    diag.span_suggestion_verbose(
-                        expr.span,
+                    let mut suggestions = vec![(last.ident.span.shrink_to_hi(), "_all".to_owned())];
+                    if path.segments.len() == 1 {
+                        suggestions.push((path.span.shrink_to_lo(), "std::fs::".to_owned()));
+                    }
+
+                    diag.multipart_suggestion_verbose(
                         "consider calling `std::fs::create_dir_all` instead",
-                        format!(
-                            "create_dir_all({})",
-                            snippet_with_applicability(cx, arg.span, "..", &mut app)
-                        ),
-                        app,
+                        suggestions,
+                        Applicability::MaybeIncorrect,
                     );
                 },
             );
diff --git a/src/tools/clippy/clippy_lints/src/ctfe.rs b/src/tools/clippy/clippy_lints/src/ctfe.rs
deleted file mode 100644
index 7bae04a10f1..00000000000
--- a/src/tools/clippy/clippy_lints/src/ctfe.rs
+++ /dev/null
@@ -1,26 +0,0 @@
-use rustc_hir::def_id::LocalDefId;
-use rustc_hir::intravisit::FnKind;
-use rustc_hir::{Body, FnDecl};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::declare_lint_pass;
-use rustc_span::Span;
-
-declare_lint_pass! {
-    /// Ensures that Constant-time Function Evaluation is being done (specifically, MIR lint passes).
-    /// As Clippy deactivates codegen, this lint ensures that CTFE (used in hard errors) is still ran.
-    ClippyCtfe => []
-}
-
-impl<'tcx> LateLintPass<'tcx> for ClippyCtfe {
-    fn check_fn(
-        &mut self,
-        cx: &LateContext<'_>,
-        _: FnKind<'tcx>,
-        _: &'tcx FnDecl<'tcx>,
-        _: &'tcx Body<'tcx>,
-        _: Span,
-        defid: LocalDefId,
-    ) {
-        cx.tcx.ensure_ok().mir_drops_elaborated_and_const_checked(defid); // Lint
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs
index 5fcb851dfeb..1e3907d9ede 100644
--- a/src/tools/clippy/clippy_lints/src/declared_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs
@@ -77,6 +77,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::cfg_not_test::CFG_NOT_TEST_INFO,
     crate::checked_conversions::CHECKED_CONVERSIONS_INFO,
     crate::cloned_ref_to_slice_refs::CLONED_REF_TO_SLICE_REFS_INFO,
+    crate::coerce_container_to_any::COERCE_CONTAINER_TO_ANY_INFO,
     crate::cognitive_complexity::COGNITIVE_COMPLEXITY_INFO,
     crate::collapsible_if::COLLAPSIBLE_ELSE_IF_INFO,
     crate::collapsible_if::COLLAPSIBLE_IF_INFO,
@@ -119,6 +120,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::doc::DOC_MARKDOWN_INFO,
     crate::doc::DOC_NESTED_REFDEFS_INFO,
     crate::doc::DOC_OVERINDENTED_LIST_ITEMS_INFO,
+    crate::doc::DOC_SUSPICIOUS_FOOTNOTES_INFO,
     crate::doc::EMPTY_DOCS_INFO,
     crate::doc::MISSING_ERRORS_DOC_INFO,
     crate::doc::MISSING_PANICS_DOC_INFO,
@@ -166,6 +168,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::floating_point_arithmetic::SUBOPTIMAL_FLOPS_INFO,
     crate::format::USELESS_FORMAT_INFO,
     crate::format_args::FORMAT_IN_FORMAT_ARGS_INFO,
+    crate::format_args::POINTER_FORMAT_INFO,
     crate::format_args::TO_STRING_IN_FORMAT_ARGS_INFO,
     crate::format_args::UNINLINED_FORMAT_ARGS_INFO,
     crate::format_args::UNNECESSARY_DEBUG_FORMATTING_INFO,
@@ -211,6 +214,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::indexing_slicing::INDEXING_SLICING_INFO,
     crate::indexing_slicing::OUT_OF_BOUNDS_INDEXING_INFO,
     crate::ineffective_open_options::INEFFECTIVE_OPEN_OPTIONS_INFO,
+    crate::infallible_try_from::INFALLIBLE_TRY_FROM_INFO,
     crate::infinite_iter::INFINITE_ITER_INFO,
     crate::infinite_iter::MAYBE_INFINITE_ITER_INFO,
     crate::inherent_impl::MULTIPLE_INHERENT_IMPL_INFO,
@@ -379,6 +383,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::methods::INSPECT_FOR_EACH_INFO,
     crate::methods::INTO_ITER_ON_REF_INFO,
     crate::methods::IO_OTHER_ERROR_INFO,
+    crate::methods::IP_CONSTANT_INFO,
     crate::methods::IS_DIGIT_ASCII_RADIX_INFO,
     crate::methods::ITERATOR_STEP_BY_ZERO_INFO,
     crate::methods::ITER_CLONED_COLLECT_INFO,
diff --git a/src/tools/clippy/clippy_lints/src/default.rs b/src/tools/clippy/clippy_lints/src/default.rs
index 886c325b355..a48e4d2fbd5 100644
--- a/src/tools/clippy/clippy_lints/src/default.rs
+++ b/src/tools/clippy/clippy_lints/src/default.rs
@@ -1,10 +1,9 @@
 use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg};
 use clippy_utils::source::snippet_with_context;
 use clippy_utils::ty::{has_drop, is_copy};
-use clippy_utils::{contains_name, get_parent_expr, in_automatically_derived, is_from_proc_macro};
+use clippy_utils::{contains_name, get_parent_expr, in_automatically_derived, is_expr_default, is_from_proc_macro};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
-use rustc_hir::def::Res;
 use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind, StructTailExpr};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
@@ -129,7 +128,7 @@ impl<'tcx> LateLintPass<'tcx> for Default {
                 // only take bindings to identifiers
                 && let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind
                 // only when assigning `... = Default::default()`
-                && is_expr_default(expr, cx)
+                && is_expr_default(cx, expr)
                 && let binding_type = cx.typeck_results().node_type(binding_id)
                 && let ty::Adt(adt, args) = *binding_type.kind()
                 && adt.is_struct()
@@ -251,19 +250,6 @@ impl<'tcx> LateLintPass<'tcx> for Default {
     }
 }
 
-/// Checks if the given expression is the `default` method belonging to the `Default` trait.
-fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool {
-    if let ExprKind::Call(fn_expr, []) = &expr.kind
-        && let ExprKind::Path(qpath) = &fn_expr.kind
-        && let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id)
-    {
-        // right hand side of assignment is `Default::default`
-        cx.tcx.is_diagnostic_item(sym::default_fn, def_id)
-    } else {
-        false
-    }
-}
-
 /// Returns the reassigned field and the assigning expression (right-hand side of assign).
 fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Option<(Ident, &'tcx Expr<'tcx>)> {
     if let StmtKind::Semi(later_expr) = this.kind
diff --git a/src/tools/clippy/clippy_lints/src/disallowed_names.rs b/src/tools/clippy/clippy_lints/src/disallowed_names.rs
index f55b0cf1c50..566aa12b08f 100644
--- a/src/tools/clippy/clippy_lints/src/disallowed_names.rs
+++ b/src/tools/clippy/clippy_lints/src/disallowed_names.rs
@@ -1,6 +1,6 @@
 use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::is_in_test;
+use clippy_utils::{is_from_proc_macro, is_in_test};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::{Pat, PatKind};
 use rustc_lint::{LateContext, LateLintPass};
@@ -43,8 +43,10 @@ impl_lint_pass!(DisallowedNames => [DISALLOWED_NAMES]);
 impl<'tcx> LateLintPass<'tcx> for DisallowedNames {
     fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
         if let PatKind::Binding(.., ident, _) = pat.kind
+            && !ident.span.from_expansion()
             && self.disallow.contains(&ident.name)
             && !is_in_test(cx.tcx, pat.hir_id)
+            && !is_from_proc_macro(cx, &ident)
         {
             span_lint(
                 cx,
diff --git a/src/tools/clippy/clippy_lints/src/disallowed_types.rs b/src/tools/clippy/clippy_lints/src/disallowed_types.rs
index 821bb25d2ce..7875cdd77e8 100644
--- a/src/tools/clippy/clippy_lints/src/disallowed_types.rs
+++ b/src/tools/clippy/clippy_lints/src/disallowed_types.rs
@@ -105,10 +105,10 @@ impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]);
 
 impl<'tcx> LateLintPass<'tcx> for DisallowedTypes {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
-        if let ItemKind::Use(path, UseKind::Single(_)) = &item.kind {
-            if let Some(res) = path.res.type_ns {
-                self.check_res_emit(cx, &res, item.span);
-            }
+        if let ItemKind::Use(path, UseKind::Single(_)) = &item.kind
+            && let Some(res) = path.res.type_ns
+        {
+            self.check_res_emit(cx, &res, item.span);
         }
     }
 
diff --git a/src/tools/clippy/clippy_lints/src/doc/doc_suspicious_footnotes.rs b/src/tools/clippy/clippy_lints/src/doc/doc_suspicious_footnotes.rs
new file mode 100644
index 00000000000..289b6b915d4
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/doc/doc_suspicious_footnotes.rs
@@ -0,0 +1,113 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use rustc_ast::token::CommentKind;
+use rustc_errors::Applicability;
+use rustc_hir::{AttrStyle, Attribute};
+use rustc_lint::{LateContext, LintContext};
+use rustc_resolve::rustdoc::DocFragmentKind;
+
+use std::ops::Range;
+
+use super::{DOC_SUSPICIOUS_FOOTNOTES, Fragments};
+
+pub fn check(cx: &LateContext<'_>, doc: &str, range: Range<usize>, fragments: &Fragments<'_>, attrs: &[Attribute]) {
+    for i in doc[range.clone()]
+        .bytes()
+        .enumerate()
+        .filter_map(|(i, c)| if c == b'[' { Some(i) } else { None })
+    {
+        let start = i + range.start;
+        if doc.as_bytes().get(start + 1) == Some(&b'^')
+            && let Some(end) = all_numbers_upto_brace(doc, start + 2)
+            && doc.as_bytes().get(end) != Some(&b':')
+            && doc.as_bytes().get(start - 1) != Some(&b'\\')
+            && let Some(this_fragment) = {
+                // the `doc` string contains all fragments concatenated together
+                // figure out which one this suspicious footnote comes from
+                let mut starting_position = 0;
+                let mut found_fragment = fragments.fragments.last();
+                for fragment in fragments.fragments {
+                    if start >= starting_position && start < starting_position + fragment.doc.as_str().len() {
+                        found_fragment = Some(fragment);
+                        break;
+                    }
+                    starting_position += fragment.doc.as_str().len();
+                }
+                found_fragment
+            }
+        {
+            let span = fragments.span(cx, start..end).unwrap_or(this_fragment.span);
+            span_lint_and_then(
+                cx,
+                DOC_SUSPICIOUS_FOOTNOTES,
+                span,
+                "looks like a footnote ref, but has no matching footnote",
+                |diag| {
+                    if this_fragment.kind == DocFragmentKind::SugaredDoc {
+                        let (doc_attr, (_, doc_attr_comment_kind)) = attrs
+                            .iter()
+                            .filter(|attr| attr.span().overlaps(this_fragment.span))
+                            .rev()
+                            .find_map(|attr| Some((attr, attr.doc_str_and_comment_kind()?)))
+                            .unwrap();
+                        let (to_add, terminator) = match (doc_attr_comment_kind, doc_attr.style()) {
+                            (CommentKind::Line, AttrStyle::Outer) => ("\n///\n/// ", ""),
+                            (CommentKind::Line, AttrStyle::Inner) => ("\n//!\n//! ", ""),
+                            (CommentKind::Block, AttrStyle::Outer) => ("\n/** ", " */"),
+                            (CommentKind::Block, AttrStyle::Inner) => ("\n/*! ", " */"),
+                        };
+                        diag.span_suggestion_verbose(
+                            doc_attr.span().shrink_to_hi(),
+                            "add footnote definition",
+                            format!(
+                                "{to_add}{label}: <!-- description -->{terminator}",
+                                label = &doc[start..end]
+                            ),
+                            Applicability::HasPlaceholders,
+                        );
+                    } else {
+                        let is_file_include = cx
+                            .sess()
+                            .source_map()
+                            .span_to_snippet(this_fragment.span)
+                            .as_ref()
+                            .map(|vdoc| vdoc.trim())
+                            == Ok(doc);
+                        if is_file_include {
+                            // if this is a file include, then there's no quote marks
+                            diag.span_suggestion_verbose(
+                                this_fragment.span.shrink_to_hi(),
+                                "add footnote definition",
+                                format!("\n\n{label}: <!-- description -->", label = &doc[start..end],),
+                                Applicability::HasPlaceholders,
+                            );
+                        } else {
+                            // otherwise, we wrap in a string
+                            diag.span_suggestion_verbose(
+                                this_fragment.span,
+                                "add footnote definition",
+                                format!(
+                                    "r#\"{doc}\n\n{label}: <!-- description -->\"#",
+                                    doc = this_fragment.doc,
+                                    label = &doc[start..end],
+                                ),
+                                Applicability::HasPlaceholders,
+                            );
+                        }
+                    }
+                },
+            );
+        }
+    }
+}
+
+fn all_numbers_upto_brace(text: &str, i: usize) -> Option<usize> {
+    for (j, c) in text.as_bytes()[i..].iter().copied().enumerate().take(64) {
+        if c == b']' && j != 0 {
+            return Some(i + j + 1);
+        }
+        if !c.is_ascii_digit() || j >= 64 {
+            break;
+        }
+    }
+    None
+}
diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs
index c46dd09d60c..e0fc2fd9347 100644
--- a/src/tools/clippy/clippy_lints/src/doc/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs
@@ -25,6 +25,7 @@ use std::ops::Range;
 use url::Url;
 
 mod doc_comment_double_space_linebreaks;
+mod doc_suspicious_footnotes;
 mod include_in_doc_without_cfg;
 mod lazy_continuation;
 mod link_with_quotes;
@@ -607,6 +608,37 @@ declare_clippy_lint! {
     "double-space used for doc comment linebreak instead of `\\`"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Detects syntax that looks like a footnote reference.
+    ///
+    /// Rustdoc footnotes are compatible with GitHub-Flavored Markdown (GFM).
+    /// GFM does not parse a footnote reference unless its definition also
+    /// exists. This lint checks for footnote references with missing
+    /// definitions, unless it thinks you're writing a regex.
+    ///
+    /// ### Why is this bad?
+    /// This probably means that a footnote was meant to exist,
+    /// but was not written.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// /// This is not a footnote[^1], because no definition exists.
+    /// fn my_fn() {}
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// /// This is a footnote[^1].
+    /// ///
+    /// /// [^1]: defined here
+    /// fn my_fn() {}
+    /// ```
+    #[clippy::version = "1.88.0"]
+    pub DOC_SUSPICIOUS_FOOTNOTES,
+    suspicious,
+    "looks like a link or footnote ref, but with no definition"
+}
+
 pub struct Documentation {
     valid_idents: FxHashSet<String>,
     check_private_items: bool,
@@ -638,7 +670,8 @@ impl_lint_pass!(Documentation => [
     DOC_OVERINDENTED_LIST_ITEMS,
     TOO_LONG_FIRST_DOC_PARAGRAPH,
     DOC_INCLUDE_WITHOUT_CFG,
-    DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS
+    DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS,
+    DOC_SUSPICIOUS_FOOTNOTES,
 ]);
 
 impl EarlyLintPass for Documentation {
@@ -825,6 +858,7 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[
             doc: &doc,
             fragments: &fragments,
         },
+        attrs,
     ))
 }
 
@@ -905,6 +939,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
     events: Events,
     doc: &str,
     fragments: Fragments<'_>,
+    attrs: &[Attribute],
 ) -> DocHeaders {
     // true if a safety header was found
     let mut headers = DocHeaders::default();
@@ -1148,7 +1183,8 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
                         // Don't check the text associated with external URLs
                         continue;
                     }
-                    text_to_check.push((text, range, code_level));
+                    text_to_check.push((text, range.clone(), code_level));
+                    doc_suspicious_footnotes::check(cx, doc, range, &fragments, attrs);
                 }
             }
             FootnoteReference(_) => {}
diff --git a/src/tools/clippy/clippy_lints/src/endian_bytes.rs b/src/tools/clippy/clippy_lints/src/endian_bytes.rs
index a7670ffce88..75db923b1c3 100644
--- a/src/tools/clippy/clippy_lints/src/endian_bytes.rs
+++ b/src/tools/clippy/clippy_lints/src/endian_bytes.rs
@@ -1,6 +1,6 @@
 use crate::Lint;
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::is_lint_allowed;
+use clippy_utils::{is_lint_allowed, sym};
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::ty::Ty;
@@ -65,9 +65,9 @@ declare_clippy_lint! {
 
 declare_lint_pass!(EndianBytes => [HOST_ENDIAN_BYTES, LITTLE_ENDIAN_BYTES, BIG_ENDIAN_BYTES]);
 
-const HOST_NAMES: [&str; 2] = ["from_ne_bytes", "to_ne_bytes"];
-const LITTLE_NAMES: [&str; 2] = ["from_le_bytes", "to_le_bytes"];
-const BIG_NAMES: [&str; 2] = ["from_be_bytes", "to_be_bytes"];
+const HOST_NAMES: [Symbol; 2] = [sym::from_ne_bytes, sym::to_ne_bytes];
+const LITTLE_NAMES: [Symbol; 2] = [sym::from_le_bytes, sym::to_le_bytes];
+const BIG_NAMES: [Symbol; 2] = [sym::from_be_bytes, sym::to_be_bytes];
 
 #[derive(Clone, Debug)]
 enum LintKind {
@@ -95,7 +95,7 @@ impl LintKind {
         }
     }
 
-    fn as_name(&self, prefix: Prefix) -> &str {
+    fn as_name(&self, prefix: Prefix) -> Symbol {
         let index = usize::from(prefix == Prefix::To);
 
         match self {
@@ -133,7 +133,7 @@ fn maybe_lint_endian_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, prefix: Prefix
     let le = LintKind::Little.as_name(prefix);
     let be = LintKind::Big.as_name(prefix);
 
-    let (lint, other_lints) = match name.as_str() {
+    let (lint, other_lints) = match name {
         name if name == ne => ((&LintKind::Host), [(&LintKind::Little), (&LintKind::Big)]),
         name if name == le => ((&LintKind::Little), [(&LintKind::Host), (&LintKind::Big)]),
         name if name == be => ((&LintKind::Big), [(&LintKind::Host), (&LintKind::Little)]),
diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs
index a26e736c7ae..0c39aae9ca9 100644
--- a/src/tools/clippy/clippy_lints/src/format_args.rs
+++ b/src/tools/clippy/clippy_lints/src/format_args.rs
@@ -1,6 +1,8 @@
+use std::collections::hash_map::Entry;
+
 use arrayvec::ArrayVec;
 use clippy_config::Conf;
-use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::macros::{
     FormatArgsStorage, FormatParamUsage, MacroCall, find_format_arg_expr, format_arg_removal_span,
     format_placeholder_format_span, is_assert_macro, is_format_macro, is_panic, matching_root_macro_call,
@@ -9,7 +11,7 @@ use clippy_utils::macros::{
 use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::{SpanRangeExt, snippet};
 use clippy_utils::ty::{implements_trait, is_type_lang_item};
-use clippy_utils::{is_diag_trait_item, is_from_proc_macro, is_in_test};
+use clippy_utils::{is_diag_trait_item, is_from_proc_macro, is_in_test, trait_ref_of_method};
 use itertools::Itertools;
 use rustc_ast::{
     FormatArgPosition, FormatArgPositionKind, FormatArgsPiece, FormatArgumentKind, FormatCount, FormatOptions,
@@ -22,10 +24,12 @@ use rustc_errors::SuggestionStyle::{CompletelyHidden, ShowCode};
 use rustc_hir::{Expr, ExprKind, LangItem};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::ty::adjustment::{Adjust, Adjustment};
-use rustc_middle::ty::{List, Ty, TyCtxt};
+use rustc_middle::ty::{self, GenericArg, List, TraitRef, Ty, TyCtxt, Upcast};
 use rustc_session::impl_lint_pass;
 use rustc_span::edition::Edition::Edition2021;
 use rustc_span::{Span, Symbol, sym};
+use rustc_trait_selection::infer::TyCtxtInferExt;
+use rustc_trait_selection::traits::{Obligation, ObligationCause, Selection, SelectionContext};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -194,12 +198,41 @@ declare_clippy_lint! {
     "use of a format specifier that has no effect"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Detects [pointer format] as well as `Debug` formatting of raw pointers or function pointers
+    /// or any types that have a derived `Debug` impl that recursively contains them.
+    ///
+    /// ### Why restrict this?
+    /// The addresses are only useful in very specific contexts, and certain projects may want to keep addresses of
+    /// certain data structures or functions from prying hacker eyes as an additional line of security.
+    ///
+    /// ### Known problems
+    /// The lint currently only looks through derived `Debug` implementations. Checking whether a manual
+    /// implementation prints an address is left as an exercise to the next lint implementer.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// let foo = &0_u32;
+    /// fn bar() {}
+    /// println!("{:p}", foo);
+    /// let _ = format!("{:?}", &(bar as fn()));
+    /// ```
+    ///
+    /// [pointer format]: https://doc.rust-lang.org/std/fmt/index.html#formatting-traits
+    #[clippy::version = "1.88.0"]
+    pub POINTER_FORMAT,
+    restriction,
+    "formatting a pointer"
+}
+
 impl_lint_pass!(FormatArgs<'_> => [
     FORMAT_IN_FORMAT_ARGS,
     TO_STRING_IN_FORMAT_ARGS,
     UNINLINED_FORMAT_ARGS,
     UNNECESSARY_DEBUG_FORMATTING,
     UNUSED_FORMAT_SPECS,
+    POINTER_FORMAT,
 ]);
 
 #[allow(clippy::struct_field_names)]
@@ -208,6 +241,8 @@ pub struct FormatArgs<'tcx> {
     msrv: Msrv,
     ignore_mixed: bool,
     ty_msrv_map: FxHashMap<Ty<'tcx>, Option<RustcVersion>>,
+    has_derived_debug: FxHashMap<Ty<'tcx>, bool>,
+    has_pointer_format: FxHashMap<Ty<'tcx>, bool>,
 }
 
 impl<'tcx> FormatArgs<'tcx> {
@@ -218,6 +253,8 @@ impl<'tcx> FormatArgs<'tcx> {
             msrv: conf.msrv,
             ignore_mixed: conf.allow_mixed_uninlined_format_args,
             ty_msrv_map,
+            has_derived_debug: FxHashMap::default(),
+            has_pointer_format: FxHashMap::default(),
         }
     }
 }
@@ -228,7 +265,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs<'tcx> {
             && is_format_macro(cx, macro_call.def_id)
             && let Some(format_args) = self.format_args.get(cx, expr, macro_call.expn)
         {
-            let linter = FormatArgsExpr {
+            let mut linter = FormatArgsExpr {
                 cx,
                 expr,
                 macro_call: &macro_call,
@@ -236,6 +273,8 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs<'tcx> {
                 ignore_mixed: self.ignore_mixed,
                 msrv: &self.msrv,
                 ty_msrv_map: &self.ty_msrv_map,
+                has_derived_debug: &mut self.has_derived_debug,
+                has_pointer_format: &mut self.has_pointer_format,
             };
 
             linter.check_templates();
@@ -255,10 +294,12 @@ struct FormatArgsExpr<'a, 'tcx> {
     ignore_mixed: bool,
     msrv: &'a Msrv,
     ty_msrv_map: &'a FxHashMap<Ty<'tcx>, Option<RustcVersion>>,
+    has_derived_debug: &'a mut FxHashMap<Ty<'tcx>, bool>,
+    has_pointer_format: &'a mut FxHashMap<Ty<'tcx>, bool>,
 }
 
 impl<'tcx> FormatArgsExpr<'_, 'tcx> {
-    fn check_templates(&self) {
+    fn check_templates(&mut self) {
         for piece in &self.format_args.template {
             if let FormatArgsPiece::Placeholder(placeholder) = piece
                 && let Ok(index) = placeholder.argument.index
@@ -279,6 +320,17 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> {
                 if placeholder.format_trait == FormatTrait::Debug {
                     let name = self.cx.tcx.item_name(self.macro_call.def_id);
                     self.check_unnecessary_debug_formatting(name, arg_expr);
+                    if let Some(span) = placeholder.span
+                        && self.has_pointer_debug(self.cx.typeck_results().expr_ty(arg_expr), 0)
+                    {
+                        span_lint(self.cx, POINTER_FORMAT, span, "pointer formatting detected");
+                    }
+                }
+
+                if placeholder.format_trait == FormatTrait::Pointer
+                    && let Some(span) = placeholder.span
+                {
+                    span_lint(self.cx, POINTER_FORMAT, span, "pointer formatting detected");
                 }
             }
         }
@@ -490,6 +542,16 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> {
             && let ty = cx.typeck_results().expr_ty(value)
             && self.can_display_format(ty)
         {
+            // If the parent function is a method of `Debug`, we don't want to lint
+            // because it is likely that the user wants to use `Debug` formatting.
+            let parent_fn = cx.tcx.hir_get_parent_item(value.hir_id);
+            if let Some(trait_ref) = trait_ref_of_method(cx, parent_fn)
+                && let Some(trait_def_id) = trait_ref.trait_def_id()
+                && cx.tcx.is_diagnostic_item(sym::Debug, trait_def_id)
+            {
+                return;
+            }
+
             let snippet = snippet(cx.sess(), value.span, "..");
             span_lint_and_then(
                 cx,
@@ -559,6 +621,58 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> {
 
         false
     }
+
+    fn has_pointer_debug(&mut self, ty: Ty<'tcx>, depth: usize) -> bool {
+        let cx = self.cx;
+        let tcx = cx.tcx;
+        if !tcx.recursion_limit().value_within_limit(depth) {
+            return false;
+        }
+        let depth = depth + 1;
+        let typing_env = cx.typing_env();
+        let ty = tcx.normalize_erasing_regions(typing_env, ty);
+        match ty.kind() {
+            ty::RawPtr(..) | ty::FnPtr(..) | ty::FnDef(..) => true,
+            ty::Ref(_, t, _) | ty::Slice(t) | ty::Array(t, _) => self.has_pointer_debug(*t, depth),
+            ty::Tuple(ts) => ts.iter().any(|t| self.has_pointer_debug(t, depth)),
+            ty::Adt(adt, args) => {
+                match self.has_pointer_format.entry(ty) {
+                    Entry::Occupied(o) => return *o.get(),
+                    Entry::Vacant(v) => v.insert(false),
+                };
+                let derived_debug = if let Some(&known) = self.has_derived_debug.get(&ty) {
+                    known
+                } else {
+                    let Some(trait_id) = tcx.get_diagnostic_item(sym::Debug) else {
+                        return false;
+                    };
+                    let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
+                    let trait_ref = TraitRef::new(tcx, trait_id, [GenericArg::from(ty)]);
+                    let obligation = Obligation {
+                        cause: ObligationCause::dummy(),
+                        param_env,
+                        recursion_depth: 0,
+                        predicate: trait_ref.upcast(tcx),
+                    };
+                    let selection = SelectionContext::new(&infcx).select(&obligation);
+                    let derived = if let Ok(Some(Selection::UserDefined(data))) = selection {
+                        tcx.has_attr(data.impl_def_id, sym::automatically_derived)
+                    } else {
+                        false
+                    };
+                    self.has_derived_debug.insert(ty, derived);
+                    derived
+                };
+                let pointer_debug = derived_debug
+                    && adt.all_fields().any(|f| {
+                        self.has_pointer_debug(tcx.normalize_erasing_regions(typing_env, f.ty(tcx, args)), depth)
+                    });
+                self.has_pointer_format.insert(ty, pointer_debug);
+                pointer_debug
+            },
+            _ => false,
+        }
+    }
 }
 
 fn make_ty_msrv_map(tcx: TyCtxt<'_>) -> FxHashMap<Ty<'_>, Option<RustcVersion>> {
diff --git a/src/tools/clippy/clippy_lints/src/functions/mod.rs b/src/tools/clippy/clippy_lints/src/functions/mod.rs
index d0d02a382d1..6051dc9479b 100644
--- a/src/tools/clippy/clippy_lints/src/functions/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/mod.rs
@@ -303,7 +303,7 @@ declare_clippy_lint! {
     /// to the name of the method, when there is a field's whose name matches that of the method.
     ///
     /// ### Why is this bad?
-    /// It is most likely that such a  method is a bug caused by a typo or by copy-pasting.
+    /// It is most likely that such a method is a bug caused by a typo or by copy-pasting.
     ///
     /// ### Example
 
diff --git a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs
index 6444e99ae9c..a99118f90f8 100644
--- a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs
+++ b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs
@@ -1,14 +1,13 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::visitors::for_each_expr_without_closures;
-use clippy_utils::{eq_expr_value, higher};
+use clippy_utils::{eq_expr_value, higher, sym};
 use core::ops::ControlFlow;
 use rustc_errors::Diag;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
 use rustc_span::edition::Edition::Edition2024;
-use rustc_span::sym;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -94,7 +93,7 @@ fn mutex_lock_call<'tcx>(
     op_mutex: Option<&'tcx Expr<'_>>,
 ) -> ControlFlow<&'tcx Expr<'tcx>> {
     if let ExprKind::MethodCall(path, self_arg, [], _) = &expr.kind
-        && path.ident.as_str() == "lock"
+        && path.ident.name == sym::lock
         && let ty = cx.typeck_results().expr_ty(self_arg).peel_refs()
         && is_type_diagnostic_item(cx, ty, sym::Mutex)
         && op_mutex.is_none_or(|op| eq_expr_value(cx, self_arg, op))
diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs
index 0823ef53ef9..c743501da25 100644
--- a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs
+++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs
@@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::sugg::{Sugg, make_binop};
 use clippy_utils::{
-    SpanlessEq, eq_expr_value, higher, is_in_const_context, is_integer_literal, peel_blocks, peel_blocks_with_stmt,
+    SpanlessEq, eq_expr_value, higher, is_in_const_context, is_integer_literal, peel_blocks, peel_blocks_with_stmt, sym,
 };
 use rustc_ast::ast::LitKind;
 use rustc_data_structures::packed::Pu128;
@@ -11,7 +11,7 @@ use rustc_errors::Applicability;
 use rustc_hir::{AssignOpKind, BinOp, BinOpKind, Expr, ExprKind, QPath};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
-use rustc_span::Span;
+use rustc_span::{Span, Symbol};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -325,7 +325,7 @@ fn check_with_condition<'tcx>(
         }
 
         // Get the variable name
-        let var_name = ares_path.segments[0].ident.name.as_str();
+        let var_name = ares_path.segments[0].ident.name;
         match cond_num_val.kind {
             ExprKind::Lit(cond_lit) => {
                 // Check if the constant is zero
@@ -337,7 +337,7 @@ fn check_with_condition<'tcx>(
                 }
             },
             ExprKind::Path(QPath::TypeRelative(_, name)) => {
-                if name.ident.as_str() == "MIN"
+                if name.ident.name == sym::MIN
                     && let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id)
                     && let Some(impl_id) = cx.tcx.impl_of_method(const_id)
                     && let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl
@@ -348,7 +348,7 @@ fn check_with_condition<'tcx>(
             },
             ExprKind::Call(func, []) => {
                 if let ExprKind::Path(QPath::TypeRelative(_, name)) = func.kind
-                    && name.ident.as_str() == "min_value"
+                    && name.ident.name == sym::min_value
                     && let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id)
                     && let Some(impl_id) = cx.tcx.impl_of_method(func_id)
                     && let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl
@@ -383,7 +383,7 @@ fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a Exp
     }
 }
 
-fn print_lint_and_sugg(cx: &LateContext<'_>, var_name: &str, expr: &Expr<'_>) {
+fn print_lint_and_sugg(cx: &LateContext<'_>, var_name: Symbol, expr: &Expr<'_>) {
     span_lint_and_sugg(
         cx,
         IMPLICIT_SATURATING_SUB,
diff --git a/src/tools/clippy/clippy_lints/src/infallible_try_from.rs b/src/tools/clippy/clippy_lints/src/infallible_try_from.rs
new file mode 100644
index 00000000000..b54c289fa7e
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/infallible_try_from.rs
@@ -0,0 +1,76 @@
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::sym;
+use rustc_errors::MultiSpan;
+use rustc_hir::{AssocItemKind, Item, ItemKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::declare_lint_pass;
+
+declare_clippy_lint! {
+    /// ### What it does
+    ///
+    /// Finds manual impls of `TryFrom` with infallible error types.
+    ///
+    /// ### Why is this bad?
+    ///
+    /// Infalliable conversions should be implemented via `From` with the blanket conversion.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// use std::convert::Infallible;
+    /// struct MyStruct(i16);
+    /// impl TryFrom<i16> for MyStruct {
+    ///     type Error = Infallible;
+    ///     fn try_from(other: i16) -> Result<Self, Infallible> {
+    ///         Ok(Self(other.into()))
+    ///     }
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// struct MyStruct(i16);
+    /// impl From<i16> for MyStruct {
+    ///     fn from(other: i16) -> Self {
+    ///         Self(other)
+    ///     }
+    /// }
+    /// ```
+    #[clippy::version = "1.88.0"]
+    pub INFALLIBLE_TRY_FROM,
+    suspicious,
+    "TryFrom with infallible Error type"
+}
+declare_lint_pass!(InfallibleTryFrom => [INFALLIBLE_TRY_FROM]);
+
+impl<'tcx> LateLintPass<'tcx> for InfallibleTryFrom {
+    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
+        let ItemKind::Impl(imp) = item.kind else { return };
+        let Some(r#trait) = imp.of_trait else { return };
+        let Some(trait_def_id) = r#trait.trait_def_id() else {
+            return;
+        };
+        if !cx.tcx.is_diagnostic_item(sym::TryFrom, trait_def_id) {
+            return;
+        }
+        for ii in imp.items {
+            if ii.kind == AssocItemKind::Type {
+                let ii = cx.tcx.hir_impl_item(ii.id);
+                if ii.ident.name != sym::Error {
+                    continue;
+                }
+                let ii_ty = ii.expect_type();
+                let ii_ty_span = ii_ty.span;
+                let ii_ty = clippy_utils::ty::ty_from_hir_ty(cx, ii_ty);
+                if !ii_ty.is_inhabited_from(cx.tcx, ii.owner_id.to_def_id(), cx.typing_env()) {
+                    let mut span = MultiSpan::from_span(cx.tcx.def_span(item.owner_id.to_def_id()));
+                    span.push_span_label(ii_ty_span, "infallible error type");
+                    span_lint(
+                        cx,
+                        INFALLIBLE_TRY_FROM,
+                        span,
+                        "infallible TryFrom impl; consider implementing From, instead",
+                    );
+                }
+            }
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs
index 92eb3d7a7c5..be9142b17fe 100644
--- a/src/tools/clippy/clippy_lints/src/lib.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.rs
@@ -55,6 +55,7 @@ extern crate rustc_session;
 extern crate rustc_span;
 extern crate rustc_target;
 extern crate rustc_trait_selection;
+extern crate smallvec;
 extern crate thin_vec;
 
 #[macro_use]
@@ -65,11 +66,10 @@ extern crate clippy_utils;
 
 mod utils;
 
-pub mod ctfe; // Very important lint, do not remove (rust#125116)
 pub mod declared_lints;
 pub mod deprecated_lints;
 
-// begin lints modules, do not remove this comment, it’s used in `update_lints`
+// begin lints modules, do not remove this comment, it's used in `update_lints`
 mod absolute_paths;
 mod almost_complete_range;
 mod approx_const;
@@ -95,6 +95,7 @@ mod casts;
 mod cfg_not_test;
 mod checked_conversions;
 mod cloned_ref_to_slice_refs;
+mod coerce_container_to_any;
 mod cognitive_complexity;
 mod collapsible_if;
 mod collection_is_never_read;
@@ -169,6 +170,7 @@ mod inconsistent_struct_constructor;
 mod index_refutable_slice;
 mod indexing_slicing;
 mod ineffective_open_options;
+mod infallible_try_from;
 mod infinite_iter;
 mod inherent_impl;
 mod inherent_to_string;
@@ -404,7 +406,7 @@ mod zero_div_zero;
 mod zero_repeat_side_effects;
 mod zero_sized_map_values;
 mod zombie_processes;
-// end lints modules, do not remove this comment, it’s used in `update_lints`
+// end lints modules, do not remove this comment, it's used in `update_lints`
 
 use clippy_config::{Conf, get_configuration_metadata, sanitize_explanation};
 use clippy_utils::macros::FormatArgsStorage;
@@ -583,8 +585,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
     let attrs = attr_storage.clone();
     store.register_early_pass(move || Box::new(AttrCollector::new(attrs.clone())));
 
-    store.register_late_pass(|_| Box::new(ctfe::ClippyCtfe));
-
     store.register_late_pass(move |_| Box::new(operators::arithmetic_side_effects::ArithmeticSideEffects::new(conf)));
     store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir));
     store.register_late_pass(|_| Box::new(utils::author::Author));
@@ -946,5 +946,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
     store.register_late_pass(|_| Box::new(single_option_map::SingleOptionMap));
     store.register_late_pass(move |_| Box::new(redundant_test_prefix::RedundantTestPrefix));
     store.register_late_pass(|_| Box::new(cloned_ref_to_slice_refs::ClonedRefToSliceRefs::new(conf)));
+    store.register_late_pass(|_| Box::new(infallible_try_from::InfallibleTryFrom));
+    store.register_late_pass(|_| Box::new(coerce_container_to_any::CoerceContainerToAny));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs b/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs
index 81f14b7b2b0..ddb8bb536c0 100644
--- a/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs
@@ -2,8 +2,9 @@ use super::MANUAL_FLATTEN;
 use super::utils::make_iterator_snippet;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::msrvs::{self, Msrv};
+use clippy_utils::source::{HasSession, indent_of, reindent_multiline, snippet_with_applicability};
 use clippy_utils::visitors::is_local_used;
-use clippy_utils::{higher, is_refutable, path_to_local_id, peel_blocks_with_stmt};
+use clippy_utils::{higher, is_refutable, path_to_local_id, peel_blocks_with_stmt, span_contains_comment};
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{Expr, Pat, PatKind};
@@ -39,13 +40,20 @@ pub(super) fn check<'tcx>(
         && msrv.meets(cx, msrvs::ITER_FLATTEN)
         && !is_refutable(cx, inner_pat)
     {
+        if arg.span.from_expansion() || if_then.span.from_expansion() {
+            return;
+        }
         let if_let_type = if some_ctor { "Some" } else { "Ok" };
         // Prepare the error message
         let msg =
             format!("unnecessary `if let` since only the `{if_let_type}` variant of the iterator element is used");
 
         // Prepare the help message
-        let mut applicability = Applicability::MaybeIncorrect;
+        let mut applicability = if span_contains_comment(cx.sess().source_map(), body.span) {
+            Applicability::MaybeIncorrect
+        } else {
+            Applicability::MachineApplicable
+        };
         let arg_snippet = make_iterator_snippet(cx, arg, &mut applicability);
         let copied = match cx.typeck_results().expr_ty(let_expr).kind() {
             ty::Ref(_, inner, _) => match inner.kind() {
@@ -55,20 +63,26 @@ pub(super) fn check<'tcx>(
             _ => "",
         };
 
-        let sugg = format!("{arg_snippet}{copied}.flatten()");
+        let help_msg = "try `.flatten()` and remove the `if let` statement in the for loop";
 
-        // If suggestion is not a one-liner, it won't be shown inline within the error message. In that
-        // case, it will be shown in the extra `help` message at the end, which is why the first
-        // `help_msg` needs to refer to the correct relative position of the suggestion.
-        let help_msg = if sugg.contains('\n') {
-            "remove the `if let` statement in the for loop and then..."
-        } else {
-            "...and remove the `if let` statement in the for loop"
-        };
+        let pat_snippet =
+            snippet_with_applicability(cx, inner_pat.span.source_callsite(), "_", &mut applicability).to_string();
+        let body_snippet =
+            snippet_with_applicability(cx, if_then.span.source_callsite(), "[body]", &mut applicability).to_string();
+        let suggestions = vec![
+            // flatten the iterator
+            (arg.span, format!("{arg_snippet}{copied}.flatten()")),
+            (pat.span, pat_snippet),
+            // remove the `if let` statement
+            (
+                body.span,
+                reindent_multiline(&body_snippet, true, indent_of(cx, body.span)),
+            ),
+        ];
 
         span_lint_and_then(cx, MANUAL_FLATTEN, span, msg, |diag| {
-            diag.span_suggestion(arg.span, "try", sugg, applicability);
             diag.span_help(inner_expr.span, help_msg);
+            diag.multipart_suggestion("try", suggestions, applicability);
         });
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/manual_clamp.rs b/src/tools/clippy/clippy_lints/src/manual_clamp.rs
index 02afe9f0997..42fe386d2c3 100644
--- a/src/tools/clippy/clippy_lints/src/manual_clamp.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_clamp.rs
@@ -8,7 +8,7 @@ use clippy_utils::ty::implements_trait;
 use clippy_utils::visitors::is_const_evaluatable;
 use clippy_utils::{
     MaybePath, eq_expr_value, is_diag_trait_item, is_in_const_context, is_trait_method, path_res, path_to_local_id,
-    peel_blocks, peel_blocks_with_stmt,
+    peel_blocks, peel_blocks_with_stmt, sym,
 };
 use itertools::Itertools;
 use rustc_errors::{Applicability, Diag};
@@ -18,7 +18,6 @@ use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::Ty;
 use rustc_session::impl_lint_pass;
 use rustc_span::Span;
-use rustc_span::symbol::sym;
 use std::cmp::Ordering;
 use std::ops::Deref;
 
@@ -299,9 +298,9 @@ fn is_max_min_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> O
         && (cx.typeck_results().expr_ty_adjusted(input).is_floating_point() || is_trait_method(cx, receiver, sym::Ord))
     {
         let is_float = cx.typeck_results().expr_ty_adjusted(input).is_floating_point();
-        let (min, max) = match (seg_first.ident.as_str(), seg_second.ident.as_str()) {
-            ("min", "max") => (arg_second, arg_first),
-            ("max", "min") => (arg_first, arg_second),
+        let (min, max) = match (seg_first.ident.name, seg_second.ident.name) {
+            (sym::min, sym::max) => (arg_second, arg_first),
+            (sym::max, sym::min) => (arg_first, arg_second),
             _ => return None,
         };
         Some(ClampSuggestion {
diff --git a/src/tools/clippy/clippy_lints/src/manual_string_new.rs b/src/tools/clippy/clippy_lints/src/manual_string_new.rs
index 73ee1c3c78a..9b590e092d0 100644
--- a/src/tools/clippy/clippy_lints/src/manual_string_new.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_string_new.rs
@@ -1,11 +1,12 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::sym;
 use rustc_ast::LitKind;
 use rustc_errors::Applicability::MachineApplicable;
 use rustc_hir::{Expr, ExprKind, PathSegment, QPath, TyKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
 use rustc_session::declare_lint_pass;
-use rustc_span::{Span, sym};
+use rustc_span::Span;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -89,9 +90,10 @@ fn warn_then_suggest(cx: &LateContext<'_>, span: Span) {
 
 /// Tries to parse an expression as a method call, emitting the warning if necessary.
 fn parse_method_call(cx: &LateContext<'_>, span: Span, path_segment: &PathSegment<'_>, receiver: &Expr<'_>) {
-    let ident = path_segment.ident.as_str();
     let method_arg_kind = &receiver.kind;
-    if ["to_string", "to_owned", "into"].contains(&ident) && is_expr_kind_empty_str(method_arg_kind) {
+    if matches!(path_segment.ident.name, sym::to_string | sym::to_owned | sym::into)
+        && is_expr_kind_empty_str(method_arg_kind)
+    {
         warn_then_suggest(cx, span);
     } else if let ExprKind::Call(func, [arg]) = method_arg_kind {
         // If our first argument is a function call itself, it could be an `unwrap`-like function.
diff --git a/src/tools/clippy/clippy_lints/src/match_result_ok.rs b/src/tools/clippy/clippy_lints/src/match_result_ok.rs
index 2a5fc8b6609..e0cb5d14d3c 100644
--- a/src/tools/clippy/clippy_lints/src/match_result_ok.rs
+++ b/src/tools/clippy/clippy_lints/src/match_result_ok.rs
@@ -1,12 +1,11 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_context;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{higher, is_res_lang_ctor};
+use clippy_utils::{higher, is_res_lang_ctor, sym};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, LangItem, PatKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
-use rustc_span::sym;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -57,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchResultOk {
 
         if let ExprKind::MethodCall(ok_path, recv, [], ..) = let_expr.kind //check is expr.ok() has type Result<T,E>.ok(, _)
             && let PatKind::TupleStruct(ref pat_path, [ok_pat], _)  = let_pat.kind //get operation
-            && ok_path.ident.as_str() == "ok"
+            && ok_path.ident.name == sym::ok
             && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result)
             && is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome)
             && let ctxt = expr.span.ctxt()
diff --git a/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs b/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs
index adda3586990..6a76c6cedd0 100644
--- a/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::macros::HirNode;
 use clippy_utils::source::{indent_of, snippet, snippet_block_with_context, snippet_with_context};
-use clippy_utils::{get_parent_expr, is_refutable, peel_blocks};
+use clippy_utils::{is_refutable, peel_blocks};
 use rustc_errors::Applicability;
 use rustc_hir::{Arm, Expr, ExprKind, Node, PatKind, StmtKind};
 use rustc_lint::LateContext;
@@ -9,6 +9,7 @@ use rustc_span::Span;
 
 use super::MATCH_SINGLE_BINDING;
 
+#[derive(Debug)]
 enum AssignmentExpr {
     Assign { span: Span, match_span: Span },
     Local { span: Span, pat_span: Span },
@@ -160,6 +161,17 @@ fn opt_parent_assign_span<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option<Ass
     None
 }
 
+fn expr_parent_requires_curlies<'a>(cx: &LateContext<'a>, match_expr: &Expr<'a>) -> bool {
+    let parent = cx.tcx.parent_hir_node(match_expr.hir_id);
+    matches!(
+        parent,
+        Node::Expr(Expr {
+            kind: ExprKind::Closure { .. },
+            ..
+        }) | Node::AnonConst(..)
+    )
+}
+
 fn sugg_with_curlies<'a>(
     cx: &LateContext<'a>,
     (ex, match_expr): (&Expr<'a>, &Expr<'a>),
@@ -172,9 +184,7 @@ fn sugg_with_curlies<'a>(
     let mut indent = " ".repeat(indent_of(cx, ex.span).unwrap_or(0));
 
     let (mut cbrace_start, mut cbrace_end) = (String::new(), String::new());
-    if let Some(parent_expr) = get_parent_expr(cx, match_expr)
-        && let ExprKind::Closure { .. } = parent_expr.kind
-    {
+    if expr_parent_requires_curlies(cx, match_expr) {
         cbrace_end = format!("\n{indent}}}");
         // Fix body indent due to the closure
         indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0));
diff --git a/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs b/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs
index 65b93a095b9..8b4c1700051 100644
--- a/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs
@@ -1,6 +1,7 @@
 use std::ops::ControlFlow;
 
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::sym;
 use clippy_utils::ty::is_type_lang_item;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
@@ -42,7 +43,7 @@ impl<'tcx> Visitor<'tcx> for MatchExprVisitor<'_, 'tcx> {
     type Result = ControlFlow<CaseMethod>;
     fn visit_expr(&mut self, ex: &'tcx Expr<'_>) -> Self::Result {
         if let ExprKind::MethodCall(segment, receiver, [], _) = ex.kind {
-            let result = self.case_altered(segment.ident.as_str(), receiver);
+            let result = self.case_altered(segment.ident.name, receiver);
             if result.is_break() {
                 return result;
             }
@@ -53,7 +54,7 @@ impl<'tcx> Visitor<'tcx> for MatchExprVisitor<'_, 'tcx> {
 }
 
 impl MatchExprVisitor<'_, '_> {
-    fn case_altered(&mut self, segment_ident: &str, receiver: &Expr<'_>) -> ControlFlow<CaseMethod> {
+    fn case_altered(&mut self, segment_ident: Symbol, receiver: &Expr<'_>) -> ControlFlow<CaseMethod> {
         if let Some(case_method) = get_case_method(segment_ident) {
             let ty = self.cx.typeck_results().expr_ty(receiver).peel_refs();
 
@@ -66,12 +67,12 @@ impl MatchExprVisitor<'_, '_> {
     }
 }
 
-fn get_case_method(segment_ident_str: &str) -> Option<CaseMethod> {
-    match segment_ident_str {
-        "to_lowercase" => Some(CaseMethod::LowerCase),
-        "to_ascii_lowercase" => Some(CaseMethod::AsciiLowerCase),
-        "to_uppercase" => Some(CaseMethod::UpperCase),
-        "to_ascii_uppercase" => Some(CaseMethod::AsciiUppercase),
+fn get_case_method(segment_ident: Symbol) -> Option<CaseMethod> {
+    match segment_ident {
+        sym::to_lowercase => Some(CaseMethod::LowerCase),
+        sym::to_ascii_lowercase => Some(CaseMethod::AsciiLowerCase),
+        sym::to_uppercase => Some(CaseMethod::UpperCase),
+        sym::to_ascii_uppercase => Some(CaseMethod::AsciiUppercase),
         _ => None,
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/methods/ip_constant.rs b/src/tools/clippy/clippy_lints/src/methods/ip_constant.rs
new file mode 100644
index 00000000000..83803fba6a1
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/methods/ip_constant.rs
@@ -0,0 +1,52 @@
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
+use clippy_utils::diagnostics::span_lint_and_then;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind, QPath, Ty, TyKind};
+use rustc_lint::LateContext;
+use rustc_span::sym;
+use smallvec::SmallVec;
+
+use super::IP_CONSTANT;
+
+pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args: &[Expr<'_>]) {
+    if let ExprKind::Path(QPath::TypeRelative(
+        Ty {
+            kind: TyKind::Path(QPath::Resolved(_, func_path)),
+            ..
+        },
+        p,
+    )) = func.kind
+        && p.ident.name == sym::new
+        && let Some(func_def_id) = func_path.res.opt_def_id()
+        && matches!(
+            cx.tcx.get_diagnostic_name(func_def_id),
+            Some(sym::Ipv4Addr | sym::Ipv6Addr)
+        )
+        && let Some(args) = args
+            .iter()
+            .map(|arg| {
+                if let Some(Constant::Int(constant @ (0 | 1 | 127 | 255))) = ConstEvalCtxt::new(cx).eval(arg) {
+                    u8::try_from(constant).ok()
+                } else {
+                    None
+                }
+            })
+            .collect::<Option<SmallVec<[u8; 8]>>>()
+    {
+        let constant_name = match args.as_slice() {
+            [0, 0, 0, 0] | [0, 0, 0, 0, 0, 0, 0, 0] => "UNSPECIFIED",
+            [127, 0, 0, 1] | [0, 0, 0, 0, 0, 0, 0, 1] => "LOCALHOST",
+            [255, 255, 255, 255] => "BROADCAST",
+            _ => return,
+        };
+
+        span_lint_and_then(cx, IP_CONSTANT, expr.span, "hand-coded well-known IP address", |diag| {
+            diag.span_suggestion_verbose(
+                expr.span.with_lo(p.ident.span.lo()),
+                "use",
+                constant_name,
+                Applicability::MachineApplicable,
+            );
+        });
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs
index c88462129af..cbb1b450e60 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs
@@ -46,7 +46,7 @@ pub(super) fn check<'tcx>(
 
         if let ExprKind::Path(rustc_hir::QPath::Resolved(_, path)) = body_expr.kind
             && let [local_ident] = path.segments
-            && local_ident.ident.as_str() == bound_ident.as_str()
+            && local_ident.ident.name == bound_ident.name
         {
             span_lint_and_sugg(
                 cx,
diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs
index e2df8ce1513..c785b23bc9c 100644
--- a/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs
@@ -72,9 +72,9 @@ fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<MinMax> {
     if let hir::ExprKind::Call(func, []) = &expr.kind
         && let hir::ExprKind::Path(hir::QPath::TypeRelative(_, segment)) = &func.kind
     {
-        match segment.ident.as_str() {
-            "max_value" => return Some(MinMax::Max),
-            "min_value" => return Some(MinMax::Min),
+        match segment.ident.name {
+            sym::max_value => return Some(MinMax::Max),
+            sym::min_value => return Some(MinMax::Min),
             _ => {},
         }
     }
diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs
index bc159206985..347960e0003 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs
@@ -37,6 +37,7 @@ mod inefficient_to_string;
 mod inspect_for_each;
 mod into_iter_on_ref;
 mod io_other_error;
+mod ip_constant;
 mod is_digit_ascii_radix;
 mod is_empty;
 mod iter_cloned_collect;
@@ -4528,6 +4529,42 @@ declare_clippy_lint! {
     "detect swap with a temporary value"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for IP addresses that could be replaced with predefined constants such as
+    /// `Ipv4Addr::new(127, 0, 0, 1)` instead of using the appropriate constants.
+    ///
+    /// ### Why is this bad?
+    /// Using specific IP addresses like `127.0.0.1` or `::1` is less clear and less maintainable than using the
+    /// predefined constants `Ipv4Addr::LOCALHOST` or `Ipv6Addr::LOCALHOST`. These constants improve code
+    /// readability, make the intent explicit, and are less error-prone.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// use std::net::{Ipv4Addr, Ipv6Addr};
+    ///
+    /// // IPv4 loopback
+    /// let addr_v4 = Ipv4Addr::new(127, 0, 0, 1);
+    ///
+    /// // IPv6 loopback
+    /// let addr_v6 = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// use std::net::{Ipv4Addr, Ipv6Addr};
+    ///
+    /// // IPv4 loopback
+    /// let addr_v4 = Ipv4Addr::LOCALHOST;
+    ///
+    /// // IPv6 loopback
+    /// let addr_v6 = Ipv6Addr::LOCALHOST;
+    /// ```
+    #[clippy::version = "1.89.0"]
+    pub IP_CONSTANT,
+    pedantic,
+    "hardcoded localhost IP address"
+}
+
 #[expect(clippy::struct_excessive_bools)]
 pub struct Methods {
     avoid_breaking_exported_api: bool,
@@ -4706,6 +4743,7 @@ impl_lint_pass!(Methods => [
     MANUAL_CONTAINS,
     IO_OTHER_ERROR,
     SWAP_WITH_TEMPORARY,
+    IP_CONSTANT,
 ]);
 
 /// Extracts a method call name, args, and `Span` of the method name.
@@ -4738,6 +4776,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
                 useless_nonzero_new_unchecked::check(cx, expr, func, args, self.msrv);
                 io_other_error::check(cx, expr, func, args, self.msrv);
                 swap_with_temporary::check(cx, expr, func, args);
+                ip_constant::check(cx, expr, func, args);
             },
             ExprKind::MethodCall(method_call, receiver, args, _) => {
                 let method_span = method_call.ident.span;
diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs
index 2b75d6a8248..0075bf166cc 100644
--- a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs
@@ -44,11 +44,10 @@ pub(super) fn check<'tcx>(
 
             if let ExprKind::MethodCall(name, _, args @ ([] | [_]), _) = parent.kind {
                 let mut app = Applicability::MachineApplicable;
-                let name = name.ident.as_str();
                 let collect_ty = cx.typeck_results().expr_ty(collect_expr);
 
-                let sugg: String = match name {
-                    "len" => {
+                let sugg: String = match name.ident.name {
+                    sym::len => {
                         if let Some(adt) = collect_ty.ty_adt_def()
                             && matches!(
                                 cx.tcx.get_diagnostic_name(adt.did()),
@@ -60,13 +59,13 @@ pub(super) fn check<'tcx>(
                             return;
                         }
                     },
-                    "is_empty"
+                    sym::is_empty
                         if is_is_empty_sig(cx, parent.hir_id)
                             && iterates_same_ty(cx, cx.typeck_results().expr_ty(iter_expr), collect_ty) =>
                     {
                         "next().is_none()".into()
                     },
-                    "contains" => {
+                    sym::contains => {
                         if is_contains_sig(cx, parent.hir_id, iter_expr)
                             && let Some(arg) = args.first()
                         {
diff --git a/src/tools/clippy/clippy_lints/src/methods/open_options.rs b/src/tools/clippy/clippy_lints/src/methods/open_options.rs
index bce314e64f0..fd368024177 100644
--- a/src/tools/clippy/clippy_lints/src/methods/open_options.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/open_options.rs
@@ -1,14 +1,14 @@
 use rustc_data_structures::fx::FxHashMap;
 
 use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
-use clippy_utils::paths;
 use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{paths, sym};
 use rustc_ast::ast::LitKind;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty::Ty;
+use rustc_span::Span;
 use rustc_span::source_map::Spanned;
-use rustc_span::{Span, sym};
 
 use super::{NONSENSICAL_OPEN_OPTIONS, SUSPICIOUS_OPEN_OPTIONS};
 
@@ -87,23 +87,23 @@ fn get_open_options(
                 _ => Argument::Unknown,
             };
 
-            match path.ident.as_str() {
-                "create" => {
+            match path.ident.name {
+                sym::create => {
                     options.push((OpenOption::Create, argument_option, span));
                 },
-                "create_new" => {
+                sym::create_new => {
                     options.push((OpenOption::CreateNew, argument_option, span));
                 },
-                "append" => {
+                sym::append => {
                     options.push((OpenOption::Append, argument_option, span));
                 },
-                "truncate" => {
+                sym::truncate => {
                     options.push((OpenOption::Truncate, argument_option, span));
                 },
-                "read" => {
+                sym::read => {
                     options.push((OpenOption::Read, argument_option, span));
                 },
-                "write" => {
+                sym::write => {
                     options.push((OpenOption::Write, argument_option, span));
                 },
                 _ => {
diff --git a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
index 6935ae1f391..6f78d6c6128 100644
--- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
@@ -285,9 +285,9 @@ fn parse_iter_usage<'tcx>(
             let did = cx.typeck_results().type_dependent_def_id(e.hir_id)?;
             let iter_id = cx.tcx.get_diagnostic_item(sym::Iterator)?;
 
-            match (name.ident.as_str(), args) {
-                ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => (IterUsageKind::Nth(0), e.span),
-                ("next_tuple", []) => {
+            match (name.ident.name, args) {
+                (sym::next, []) if cx.tcx.trait_of_item(did) == Some(iter_id) => (IterUsageKind::Nth(0), e.span),
+                (sym::next_tuple, []) => {
                     return if paths::ITERTOOLS_NEXT_TUPLE.matches(cx, did)
                         && let ty::Adt(adt_def, subs) = cx.typeck_results().expr_ty(e).kind()
                         && cx.tcx.is_diagnostic_item(sym::Option, adt_def.did())
@@ -303,7 +303,7 @@ fn parse_iter_usage<'tcx>(
                         None
                     };
                 },
-                ("nth" | "skip", [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
+                (sym::nth | sym::skip, [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
                     if let Some(Constant::Int(idx)) = ConstEvalCtxt::new(cx).eval(idx_expr) {
                         let span = if name.ident.as_str() == "nth" {
                             e.span
diff --git a/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs b/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs
index 4b32ba83b32..741f38f9756 100644
--- a/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs
+++ b/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs
@@ -47,7 +47,7 @@ impl EarlyLintPass for MultipleBoundLocations {
 
             for param in &generics.params {
                 if !param.bounds.is_empty() {
-                    generic_params_with_bounds.insert(param.ident.name.as_str(), param.ident.span);
+                    generic_params_with_bounds.insert(param.ident.as_str(), param.ident.span);
                 }
             }
             for clause in &generics.where_clause.predicates {
@@ -64,7 +64,7 @@ impl EarlyLintPass for MultipleBoundLocations {
                     },
                     WherePredicateKind::RegionPredicate(pred) => {
                         if !pred.bounds.is_empty()
-                            && let Some(bound_span) = generic_params_with_bounds.get(&pred.lifetime.ident.name.as_str())
+                            && let Some(bound_span) = generic_params_with_bounds.get(&pred.lifetime.ident.as_str())
                         {
                             emit_lint(cx, *bound_span, pred.lifetime.ident.span);
                         }
diff --git a/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs b/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs
index 93865197ec9..04b09276966 100644
--- a/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs
+++ b/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs
@@ -293,7 +293,14 @@ fn self_cmp_call<'tcx>(
         ExprKind::Call(path, [_, _]) => path_res(cx, path)
             .opt_def_id()
             .is_some_and(|def_id| cx.tcx.is_diagnostic_item(sym::ord_cmp_method, def_id)),
-        ExprKind::MethodCall(_, _, [_other], ..) => {
+        ExprKind::MethodCall(_, recv, [_], ..) => {
+            let ExprKind::Path(path) = recv.kind else {
+                return false;
+            };
+            if last_path_segment(&path).ident.name != kw::SelfLower {
+                return false;
+            }
+
             // We can set this to true here no matter what as if it's a `MethodCall` and goes to the
             // `else` branch, it must be a method named `cmp` that isn't `Ord::cmp`
             *needs_fully_qualified = true;
diff --git a/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs b/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs
index 35caac855cf..4ce6827cac9 100644
--- a/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs
+++ b/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs
@@ -1,14 +1,14 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::path_to_local_id;
 use clippy_utils::source::{SpanRangeExt, snippet};
 use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{path_to_local_id, sym};
 use rustc_ast::{LitKind, StrStyle};
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
 use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, LetStmt, PatKind, QPath, Stmt, StmtKind, TyKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_session::impl_lint_pass;
-use rustc_span::{Span, Symbol, sym};
+use rustc_span::{Span, Symbol};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -177,7 +177,7 @@ impl<'tcx> LateLintPass<'tcx> for PathbufThenPush<'tcx> {
             && let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind
             && let ExprKind::MethodCall(name, self_arg, [arg_expr], _) = expr.kind
             && path_to_local_id(self_arg, searcher.local_id)
-            && name.ident.as_str() == "push"
+            && name.ident.name == sym::push
         {
             searcher.err_span = searcher.err_span.to(stmt.span);
             searcher.arg = Some(*arg_expr);
diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs
index 9149406642d..94cdcf00054 100644
--- a/src/tools/clippy/clippy_lints/src/ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/ptr.rs
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then, span_lin
 use clippy_utils::source::SpanRangeExt;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::visitors::contains_unsafe_block;
-use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local, std_or_core};
+use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local, std_or_core, sym};
 use hir::LifetimeKind;
 use rustc_abi::ExternAbi;
 use rustc_errors::{Applicability, MultiSpan};
@@ -18,8 +18,8 @@ use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::hir::nested_filter;
 use rustc_middle::ty::{self, Binder, ClauseKind, ExistentialPredicate, List, PredicateKind, Ty};
 use rustc_session::declare_lint_pass;
+use rustc_span::Span;
 use rustc_span::symbol::Symbol;
-use rustc_span::{Span, sym};
 use rustc_trait_selection::infer::InferCtxtExt as _;
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
 use std::{fmt, iter};
@@ -268,6 +268,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
                 (false, false, true) if let Some(sugg) = Sugg::hir_opt(cx, l) => sugg.maybe_paren(),
                 _ => return check_ptr_eq(cx, expr, op.node, l, r),
             };
+            let invert = if op.node == BinOpKind::Eq { "" } else { "!" };
 
             span_lint_and_sugg(
                 cx,
@@ -275,7 +276,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
                 expr.span,
                 "comparing with null is better expressed by the `.is_null()` method",
                 "try",
-                format!("{non_null_path_snippet}.is_null()"),
+                format!("{invert}{non_null_path_snippet}.is_null()",),
                 Applicability::MachineApplicable,
             );
         }
@@ -299,7 +300,7 @@ struct PtrArg<'tcx> {
     emission_id: HirId,
     span: Span,
     ty_name: Symbol,
-    method_renames: &'static [(&'static str, &'static str)],
+    method_renames: &'static [(Symbol, &'static str)],
     ref_prefix: RefPrefix,
     deref_ty: DerefTy<'tcx>,
 }
@@ -385,6 +386,7 @@ impl<'tcx> DerefTy<'tcx> {
     }
 }
 
+#[expect(clippy::too_many_lines)]
 fn check_fn_args<'cx, 'tcx: 'cx>(
     cx: &'cx LateContext<'tcx>,
     fn_sig: ty::FnSig<'tcx>,
@@ -409,7 +411,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
                 let emission_id = params.get(i).map_or(hir_ty.hir_id, |param| param.hir_id);
                 let (method_renames, deref_ty) = match cx.tcx.get_diagnostic_name(adt.did()) {
                     Some(sym::Vec) => (
-                        [("clone", ".to_owned()")].as_slice(),
+                        [(sym::clone, ".to_owned()")].as_slice(),
                         DerefTy::Slice(
                             name.args.and_then(|args| args.args.first()).and_then(|arg| {
                                 if let GenericArg::Type(ty) = arg {
@@ -421,10 +423,14 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
                             args.type_at(0),
                         ),
                     ),
-                    _ if Some(adt.did()) == cx.tcx.lang_items().string() => {
-                        ([("clone", ".to_owned()"), ("as_str", "")].as_slice(), DerefTy::Str)
-                    },
-                    Some(sym::PathBuf) => ([("clone", ".to_path_buf()"), ("as_path", "")].as_slice(), DerefTy::Path),
+                    _ if Some(adt.did()) == cx.tcx.lang_items().string() => (
+                        [(sym::clone, ".to_owned()"), (sym::as_str, "")].as_slice(),
+                        DerefTy::Str,
+                    ),
+                    Some(sym::PathBuf) => (
+                        [(sym::clone, ".to_path_buf()"), (sym::as_path, "")].as_slice(),
+                        DerefTy::Path,
+                    ),
                     Some(sym::Cow) if mutability == Mutability::Not => {
                         if let Some((lifetime, ty)) = name.args.and_then(|args| {
                             if let [GenericArg::Lifetime(lifetime), ty] = args.args {
@@ -595,7 +601,7 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &Body<'tcx>, args: &[
                     if let ExprKind::MethodCall(name, receiver, ..) = use_expr.kind
                         && receiver.hir_id == child_id
                     {
-                        let name = name.ident.as_str();
+                        let name = name.ident.name;
 
                         // Check if the method can be renamed.
                         if let Some((_, replacement)) = args.method_renames.iter().find(|&&(x, _)| x == name) {
diff --git a/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs b/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs
index 49b522994fb..6b1dc864fb7 100644
--- a/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs
+++ b/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
-use clippy_utils::get_enclosing_block;
 use clippy_utils::higher::{VecInitKind, get_vec_init_kind};
 use clippy_utils::source::snippet;
+use clippy_utils::{get_enclosing_block, sym};
 
 use hir::{Expr, ExprKind, HirId, LetStmt, PatKind, PathSegment, QPath, StmtKind};
 use rustc_errors::Applicability;
@@ -87,7 +87,7 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec {
                                 diag.span_suggestion(
                                     expr.span,
                                     "try",
-                                    format!("{}.resize({len}, 0); {}", ident.as_str(), snippet(cx, expr.span, "..")),
+                                    format!("{}.resize({len}, 0); {}", ident, snippet(cx, expr.span, "..")),
                                     applicability,
                                 );
                             },
@@ -106,7 +106,7 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec {
                                         "try",
                                         format!(
                                             "{}.resize({}, 0); {}",
-                                            ident.as_str(),
+                                            ident,
                                             snippet(cx, e.span, ".."),
                                             snippet(cx, expr.span, "..")
                                         ),
@@ -142,8 +142,8 @@ impl<'tcx> Visitor<'tcx> for ReadVecVisitor<'tcx> {
         if let ExprKind::MethodCall(path, receiver, args, _) = e.kind {
             let PathSegment { ident, .. } = *path;
 
-            match ident.as_str() {
-                "read" | "read_exact" => {
+            match ident.name {
+                sym::read | sym::read_exact => {
                     let [arg] = args else { return };
                     if let ExprKind::AddrOf(_, hir::Mutability::Mut, inner) = arg.kind
                         && let ExprKind::Path(QPath::Resolved(None, inner_path)) = inner.kind
@@ -155,7 +155,7 @@ impl<'tcx> Visitor<'tcx> for ReadVecVisitor<'tcx> {
                         return;
                     }
                 },
-                "resize" => {
+                sym::resize => {
                     // If the Vec is resized, then it's a valid read
                     if let ExprKind::Path(QPath::Resolved(_, inner_path)) = receiver.kind
                         && let Res::Local(res_id) = inner_path.res
diff --git a/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs b/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs
index 152d7450f5f..51adbbcd58b 100644
--- a/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs
+++ b/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::higher::{VecInitKind, get_vec_init_kind};
 use clippy_utils::source::snippet;
-use clippy_utils::{is_from_proc_macro, path_to_local_id};
+use clippy_utils::{is_from_proc_macro, path_to_local_id, sym};
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
 use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, LetStmt, PatKind, QPath, Stmt, StmtKind};
@@ -126,7 +126,7 @@ impl<'tcx> LateLintPass<'tcx> for ReserveAfterInitialization {
             if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind
                 && let ExprKind::MethodCall(name, self_arg, [space_hint], _) = expr.kind
                 && path_to_local_id(self_arg, searcher.local_id)
-                && name.ident.as_str() == "reserve"
+                && name.ident.name == sym::reserve
                 && !is_from_proc_macro(cx, expr)
             {
                 self.searcher = Some(VecReserveSearcher {
diff --git a/src/tools/clippy/clippy_lints/src/semicolon_block.rs b/src/tools/clippy/clippy_lints/src/semicolon_block.rs
index d85f4a8fa01..f6c128d4c52 100644
--- a/src/tools/clippy/clippy_lints/src/semicolon_block.rs
+++ b/src/tools/clippy/clippy_lints/src/semicolon_block.rs
@@ -143,7 +143,7 @@ impl LateLintPass<'_> for SemicolonBlock {
             StmtKind::Expr(Expr {
                 kind: ExprKind::Block(block, _),
                 ..
-            }) if !block.span.from_expansion() => {
+            }) if !block.span.from_expansion() && stmt.span.contains(block.span) => {
                 let Block {
                     expr: None,
                     stmts: [.., stmt],
diff --git a/src/tools/clippy/clippy_lints/src/serde_api.rs b/src/tools/clippy/clippy_lints/src/serde_api.rs
index a64b9b22378..b36a5d6d502 100644
--- a/src/tools/clippy/clippy_lints/src/serde_api.rs
+++ b/src/tools/clippy/clippy_lints/src/serde_api.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::paths;
+use clippy_utils::{paths, sym};
 use rustc_hir::{Impl, Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
@@ -36,9 +36,9 @@ impl<'tcx> LateLintPass<'tcx> for SerdeApi {
                 let mut seen_str = None;
                 let mut seen_string = None;
                 for item in *items {
-                    match item.ident.as_str() {
-                        "visit_str" => seen_str = Some(item.span),
-                        "visit_string" => seen_string = Some(item.span),
+                    match item.ident.name {
+                        sym::visit_str => seen_str = Some(item.span),
+                        sym::visit_string => seen_string = Some(item.span),
                         _ => {},
                     }
                 }
diff --git a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs
index 3d39386ecf9..442b3250d86 100644
--- a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs
+++ b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs
@@ -1,13 +1,13 @@
 use clippy_config::Conf;
-use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::is_from_proc_macro;
 use clippy_utils::msrvs::Msrv;
 use rustc_attr_data_structures::{StabilityLevel, StableSince};
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
 use rustc_hir::def_id::DefId;
-use rustc_hir::{HirId, Path, PathSegment};
-use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_hir::{Block, Body, HirId, Path, PathSegment};
+use rustc_lint::{LateContext, LateLintPass, Lint, LintContext};
 use rustc_session::impl_lint_pass;
 use rustc_span::symbol::kw;
 use rustc_span::{Span, sym};
@@ -88,24 +88,35 @@ declare_clippy_lint! {
 }
 
 pub struct StdReexports {
-    // Paths which can be either a module or a macro (e.g. `std::env`) will cause this check to happen
-    // twice. First for the mod, second for the macro. This is used to avoid the lint reporting for the macro
-    // when the path could be also be used to access the module.
-    prev_span: Span,
+    lint_point: (Span, Option<LintPoint>),
     msrv: Msrv,
 }
 
 impl StdReexports {
     pub fn new(conf: &'static Conf) -> Self {
         Self {
-            prev_span: Span::default(),
+            lint_point: Default::default(),
             msrv: conf.msrv,
         }
     }
+
+    fn lint_if_finish(&mut self, cx: &LateContext<'_>, (span, item): (Span, Option<LintPoint>)) {
+        if span.source_equal(self.lint_point.0) {
+            return;
+        }
+
+        if !self.lint_point.0.is_dummy() {
+            emit_lints(cx, &self.lint_point);
+        }
+
+        self.lint_point = (span, item);
+    }
 }
 
 impl_lint_pass!(StdReexports => [STD_INSTEAD_OF_CORE, STD_INSTEAD_OF_ALLOC, ALLOC_INSTEAD_OF_CORE]);
 
+type LintPoint = (&'static Lint, &'static str, &'static str);
+
 impl<'tcx> LateLintPass<'tcx> for StdReexports {
     fn check_path(&mut self, cx: &LateContext<'tcx>, path: &Path<'tcx>, _: HirId) {
         if let Res::Def(_, def_id) = path.res
@@ -119,7 +130,7 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports {
                     sym::core => (STD_INSTEAD_OF_CORE, "std", "core"),
                     sym::alloc => (STD_INSTEAD_OF_ALLOC, "std", "alloc"),
                     _ => {
-                        self.prev_span = first_segment.ident.span;
+                        self.lint_if_finish(cx, (first_segment.ident.span, None));
                         return;
                     },
                 },
@@ -127,32 +138,44 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports {
                     if cx.tcx.crate_name(def_id.krate) == sym::core {
                         (ALLOC_INSTEAD_OF_CORE, "alloc", "core")
                     } else {
-                        self.prev_span = first_segment.ident.span;
+                        self.lint_if_finish(cx, (first_segment.ident.span, None));
                         return;
                     }
                 },
                 _ => return,
             };
-            if first_segment.ident.span != self.prev_span {
-                #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
-                span_lint_and_then(
-                    cx,
-                    lint,
-                    first_segment.ident.span,
-                    format!("used import from `{used_mod}` instead of `{replace_with}`"),
-                    |diag| {
-                        diag.span_suggestion(
-                            first_segment.ident.span,
-                            format!("consider importing the item from `{replace_with}`"),
-                            replace_with.to_string(),
-                            Applicability::MachineApplicable,
-                        );
-                    },
-                );
-                self.prev_span = first_segment.ident.span;
-            }
+
+            self.lint_if_finish(cx, (first_segment.ident.span, Some((lint, used_mod, replace_with))));
         }
     }
+
+    fn check_block_post(&mut self, cx: &LateContext<'tcx>, _: &Block<'tcx>) {
+        self.lint_if_finish(cx, Default::default());
+    }
+
+    fn check_body_post(&mut self, cx: &LateContext<'tcx>, _: &Body<'tcx>) {
+        self.lint_if_finish(cx, Default::default());
+    }
+
+    fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
+        self.lint_if_finish(cx, Default::default());
+    }
+}
+
+fn emit_lints(cx: &LateContext<'_>, (span, item): &(Span, Option<LintPoint>)) {
+    let Some((lint, used_mod, replace_with)) = item else {
+        return;
+    };
+
+    span_lint_and_sugg(
+        cx,
+        lint,
+        *span,
+        format!("used import from `{used_mod}` instead of `{replace_with}`"),
+        format!("consider importing the item from `{replace_with}`"),
+        (*replace_with).to_string(),
+        Applicability::MachineApplicable,
+    );
 }
 
 /// Returns the first named segment of a [`Path`].
diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs
index 73a9fe71e00..1cda6f596f4 100644
--- a/src/tools/clippy/clippy_lints/src/strings.rs
+++ b/src/tools/clippy/clippy_lints/src/strings.rs
@@ -560,7 +560,7 @@ impl<'tcx> LateLintPass<'tcx> for TrimSplitWhitespace {
             && let Some(split_ws_def_id) = tyckres.type_dependent_def_id(expr.hir_id)
             && cx.tcx.is_diagnostic_item(sym::str_split_whitespace, split_ws_def_id)
             && let ExprKind::MethodCall(path, _trim_recv, [], trim_span) = split_recv.kind
-            && let trim_fn_name @ ("trim" | "trim_start" | "trim_end") = path.ident.name.as_str()
+            && let trim_fn_name @ (sym::trim | sym::trim_start | sym::trim_end) = path.ident.name
             && let Some(trim_def_id) = tyckres.type_dependent_def_id(split_recv.hir_id)
             && is_one_of_trim_diagnostic_items(cx, trim_def_id)
         {
diff --git a/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs
index e3ecd6508bf..5ecbb56925e 100644
--- a/src/tools/clippy/clippy_lints/src/swap.rs
+++ b/src/tools/clippy/clippy_lints/src/swap.rs
@@ -3,7 +3,7 @@ use clippy_utils::source::{snippet_indent, snippet_with_context};
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::is_type_diagnostic_item;
 
-use clippy_utils::{can_mut_borrow_both, eq_expr_value, is_in_const_context, std_or_core};
+use clippy_utils::{can_mut_borrow_both, eq_expr_value, is_in_const_context, path_to_local, std_or_core};
 use itertools::Itertools;
 
 use rustc_data_structures::fx::FxIndexSet;
@@ -350,12 +350,21 @@ impl<'tcx> IndexBinding<'_, 'tcx> {
                 format!("{lhs_snippet}{rhs_snippet}")
             },
             ExprKind::Path(QPath::Resolved(_, path)) => {
-                let init = self.cx.expr_or_init(expr);
-
                 let Some(first_segment) = path.segments.first() else {
                     return String::new();
                 };
-                if !self.suggest_span.contains(init.span) || !self.is_used_other_than_swapping(first_segment.ident) {
+
+                let init = self.cx.expr_or_init(expr);
+
+                // We skip suggesting a variable binding in any of these cases:
+                // - Variable initialization is outside the suggestion span
+                // - Variable declaration is outside the suggestion span
+                // - Variable is not used as an index or elsewhere later
+                if !self.suggest_span.contains(init.span)
+                    || path_to_local(expr)
+                        .is_some_and(|hir_id| !self.suggest_span.contains(self.cx.tcx.hir_span(hir_id)))
+                    || !self.is_used_other_than_swapping(first_segment.ident)
+                {
                     return String::new();
                 }
 
diff --git a/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs b/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs
index 96286fcf73d..08f36a2ed5d 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs
@@ -74,7 +74,7 @@ pub(super) fn check<'tcx>(
         last.ident.span.with_hi(path.span.hi()),
         "transmute used without annotations",
         "consider adding missing annotations",
-        format!("{}::<{from_ty}, {to_ty}>", last.ident.as_str()),
+        format!("{}::<{from_ty}, {to_ty}>", last.ident),
         Applicability::MaybeIncorrect,
     );
     true
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 004ad03e708..4b23367645e 100644
--- a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs
+++ b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs
@@ -33,7 +33,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m
                 let ltopt = if lt.is_anonymous() {
                     String::new()
                 } else {
-                    format!("{} ", lt.ident.as_str())
+                    format!("{} ", lt.ident)
                 };
 
                 if mut_ty.mutbl == Mutability::Mut {
diff --git a/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs b/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs
index 019ae16ca85..ae6d8a1c1aa 100644
--- a/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs
+++ b/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs
@@ -1,9 +1,14 @@
+use std::iter;
+
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::is_from_proc_macro;
-use clippy_utils::source::{SourceText, SpanRangeExt, indent_of, reindent_multiline};
+use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline};
+use clippy_utils::sugg::Sugg;
+use clippy_utils::ty::expr_type_is_certain;
+use clippy_utils::{is_expr_default, is_from_proc_macro};
 use rustc_errors::Applicability;
 use rustc_hir::{Block, Expr, ExprKind, MatchSource, Node, StmtKind};
 use rustc_lint::LateContext;
+use rustc_span::SyntaxContext;
 
 use super::{UNIT_ARG, utils};
 
@@ -59,7 +64,7 @@ fn is_questionmark_desugar_marked_call(expr: &Expr<'_>) -> bool {
     }
 }
 
-fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Expr<'_>]) {
+fn lint_unit_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, args_to_recover: &[&'tcx Expr<'tcx>]) {
     let mut applicability = Applicability::MachineApplicable;
     let (singular, plural) = if args_to_recover.len() > 1 {
         ("", "s")
@@ -100,34 +105,41 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp
 
             let arg_snippets: Vec<_> = args_to_recover
                 .iter()
-                .filter_map(|arg| arg.span.get_source_text(cx))
+                // If the argument is from an expansion and is a `Default::default()`, we skip it
+                .filter(|arg| !arg.span.from_expansion() || !is_expr_default_nested(cx, arg))
+                .filter_map(|arg| get_expr_snippet(cx, arg))
                 .collect();
-            let arg_snippets_without_empty_blocks: Vec<_> = args_to_recover
+
+            // If the argument is an empty block or `Default::default()`, we can replace it with `()`.
+            let arg_snippets_without_redundant_exprs: Vec<_> = args_to_recover
                 .iter()
-                .filter(|arg| !is_empty_block(arg))
-                .filter_map(|arg| arg.span.get_source_text(cx))
+                .filter(|arg| !is_expr_default_nested(cx, arg) && (arg.span.from_expansion() || !is_empty_block(arg)))
+                .filter_map(|arg| get_expr_snippet_with_type_certainty(cx, arg))
                 .collect();
 
             if let Some(call_snippet) = expr.span.get_source_text(cx) {
-                let sugg = fmt_stmts_and_call(
-                    cx,
-                    expr,
-                    &call_snippet,
-                    &arg_snippets,
-                    &arg_snippets_without_empty_blocks,
-                );
-
-                if arg_snippets_without_empty_blocks.is_empty() {
+                if arg_snippets_without_redundant_exprs.is_empty()
+                    && let suggestions = args_to_recover
+                        .iter()
+                        .filter(|arg| !arg.span.from_expansion() || !is_expr_default_nested(cx, arg))
+                        .map(|arg| (arg.span.parent_callsite().unwrap_or(arg.span), "()".to_string()))
+                        .collect::<Vec<_>>()
+                    && !suggestions.is_empty()
+                {
                     db.multipart_suggestion(
                         format!("use {singular}unit literal{plural} instead"),
-                        args_to_recover
-                            .iter()
-                            .map(|arg| (arg.span, "()".to_string()))
-                            .collect::<Vec<_>>(),
+                        suggestions,
                         applicability,
                     );
                 } else {
-                    let plural = arg_snippets_without_empty_blocks.len() > 1;
+                    let plural = arg_snippets_without_redundant_exprs.len() > 1;
+                    let sugg = fmt_stmts_and_call(
+                        cx,
+                        expr,
+                        &call_snippet,
+                        arg_snippets,
+                        arg_snippets_without_redundant_exprs,
+                    );
                     let empty_or_s = if plural { "s" } else { "" };
                     let it_or_them = if plural { "them" } else { "it" };
                     db.span_suggestion(
@@ -144,6 +156,55 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp
     );
 }
 
+fn is_expr_default_nested<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
+    is_expr_default(cx, expr)
+        || matches!(expr.kind, ExprKind::Block(block, _)
+        if block.expr.is_some() && is_expr_default_nested(cx, block.expr.unwrap()))
+}
+
+enum MaybeTypeUncertain<'tcx> {
+    Certain(Sugg<'tcx>),
+    Uncertain(Sugg<'tcx>),
+}
+
+impl From<MaybeTypeUncertain<'_>> for String {
+    fn from(value: MaybeTypeUncertain<'_>) -> Self {
+        match value {
+            MaybeTypeUncertain::Certain(sugg) => sugg.to_string(),
+            MaybeTypeUncertain::Uncertain(sugg) => format!("let _: () = {sugg}"),
+        }
+    }
+}
+
+fn get_expr_snippet<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<Sugg<'tcx>> {
+    let mut app = Applicability::MachineApplicable;
+    let snip = Sugg::hir_with_context(cx, expr, SyntaxContext::root(), "..", &mut app);
+    if app != Applicability::MachineApplicable {
+        return None;
+    }
+
+    Some(snip)
+}
+
+fn get_expr_snippet_with_type_certainty<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'tcx>,
+) -> Option<MaybeTypeUncertain<'tcx>> {
+    get_expr_snippet(cx, expr).map(|snip| {
+        // If the type of the expression is certain, we can use it directly.
+        // Otherwise, we wrap it in a `let _: () = ...` to ensure the type is correct.
+        if !expr_type_is_certain(cx, expr) && !is_block_with_no_expr(expr) {
+            MaybeTypeUncertain::Uncertain(snip)
+        } else {
+            MaybeTypeUncertain::Certain(snip)
+        }
+    })
+}
+
+fn is_block_with_no_expr(expr: &Expr<'_>) -> bool {
+    matches!(expr.kind, ExprKind::Block(Block { expr: None, .. }, _))
+}
+
 fn is_empty_block(expr: &Expr<'_>) -> bool {
     matches!(
         expr.kind,
@@ -162,23 +223,20 @@ fn fmt_stmts_and_call(
     cx: &LateContext<'_>,
     call_expr: &Expr<'_>,
     call_snippet: &str,
-    args_snippets: &[SourceText],
-    non_empty_block_args_snippets: &[SourceText],
+    args_snippets: Vec<Sugg<'_>>,
+    non_empty_block_args_snippets: Vec<MaybeTypeUncertain<'_>>,
 ) -> String {
     let call_expr_indent = indent_of(cx, call_expr.span).unwrap_or(0);
-    let call_snippet_with_replacements = args_snippets
-        .iter()
-        .fold(call_snippet.to_owned(), |acc, arg| acc.replacen(arg.as_ref(), "()", 1));
+    let call_snippet_with_replacements = args_snippets.into_iter().fold(call_snippet.to_owned(), |acc, arg| {
+        acc.replacen(&arg.to_string(), "()", 1)
+    });
 
-    let mut stmts_and_call = non_empty_block_args_snippets
-        .iter()
-        .map(|it| it.as_ref().to_owned())
-        .collect::<Vec<_>>();
-    stmts_and_call.push(call_snippet_with_replacements);
-    stmts_and_call = stmts_and_call
+    let stmts_and_call = non_empty_block_args_snippets
         .into_iter()
+        .map(Into::into)
+        .chain(iter::once(call_snippet_with_replacements))
         .map(|v| reindent_multiline(&v, true, Some(call_expr_indent)))
-        .collect();
+        .collect::<Vec<_>>();
 
     let mut stmts_and_call_snippet = stmts_and_call.join(&format!("{}{}", ";\n", " ".repeat(call_expr_indent)));
     // expr is not in a block statement or result expression position, wrap in a block
diff --git a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs
index 5e1cb9e54f5..12cc1093899 100644
--- a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs
@@ -1,11 +1,11 @@
 use clippy_utils::diagnostics::span_lint_hir_and_then;
 use clippy_utils::macros::{is_panic, root_macro_call_first_node};
-use clippy_utils::{is_res_lang_ctor, paths, peel_blocks};
+use clippy_utils::{is_res_lang_ctor, paths, peel_blocks, sym};
 use hir::{ExprKind, HirId, PatKind};
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
-use rustc_span::{Span, sym};
+use rustc_span::Span;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -232,8 +232,16 @@ fn is_unreachable_or_panic(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
 fn unpack_call_chain<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> {
     while let ExprKind::MethodCall(path, receiver, ..) = expr.kind {
         if matches!(
-            path.ident.as_str(),
-            "unwrap" | "expect" | "unwrap_or" | "unwrap_or_else" | "ok" | "is_ok" | "is_err" | "or_else" | "or"
+            path.ident.name,
+            sym::unwrap
+                | sym::expect
+                | sym::unwrap_or
+                | sym::unwrap_or_else
+                | sym::ok
+                | sym::is_ok
+                | sym::is_err
+                | sym::or_else
+                | sym::or
         ) {
             expr = receiver;
         } else {
diff --git a/src/tools/clippy/clippy_lints/src/unused_result_ok.rs b/src/tools/clippy/clippy_lints/src/unused_result_ok.rs
index 958f19d1833..f5ed10fb760 100644
--- a/src/tools/clippy/clippy_lints/src/unused_result_ok.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_result_ok.rs
@@ -1,11 +1,11 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_context;
+use clippy_utils::sym;
 use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_errors::Applicability;
 use rustc_hir::{ExprKind, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_session::declare_lint_pass;
-use rustc_span::sym;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -36,7 +36,7 @@ impl LateLintPass<'_> for UnusedResultOk {
     fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) {
         if let StmtKind::Semi(expr) = stmt.kind
             && let ExprKind::MethodCall(ok_path, recv, [], ..) = expr.kind //check is expr.ok() has type Result<T,E>.ok(, _)
-            && ok_path.ident.as_str() == "ok"
+            && ok_path.ident.name == sym::ok
             && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result)
             && !stmt.span.in_external_macro(cx.sess().source_map())
         {
diff --git a/src/tools/clippy/clippy_lints/src/unused_unit.rs b/src/tools/clippy/clippy_lints/src/unused_unit.rs
index 9859ddfdf7b..3811f0fe6b5 100644
--- a/src/tools/clippy/clippy_lints/src/unused_unit.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_unit.rs
@@ -100,7 +100,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedUnit {
         let segments = &poly.trait_ref.path.segments;
 
         if segments.len() == 1
-            && ["Fn", "FnMut", "FnOnce"].contains(&segments[0].ident.name.as_str())
+            && matches!(segments[0].ident.name, sym::Fn | sym::FnMut | sym::FnOnce)
             && let Some(args) = segments[0].args
             && args.parenthesized == GenericArgsParentheses::ParenSugar
             && let constraints = &args.constraints
@@ -109,6 +109,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedUnit {
             && let AssocItemConstraintKind::Equality { term: Term::Ty(hir_ty) } = constraints[0].kind
             && args.span_ext.hi() != poly.span.hi()
             && !hir_ty.span.from_expansion()
+            && args.span_ext.hi() != hir_ty.span.hi()
             && is_unit_ty(hir_ty)
         {
             lint_unneeded_unit_return(cx, hir_ty.span, poly.span);
diff --git a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs
index 3c23662e9d1..8d873536295 100644
--- a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs
+++ b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::higher::{VecInitKind, get_vec_init_kind};
 use clippy_utils::source::snippet;
 use clippy_utils::visitors::for_each_local_use_after_expr;
-use clippy_utils::{get_parent_expr, path_to_local_id};
+use clippy_utils::{get_parent_expr, path_to_local_id, sym};
 use core::ops::ControlFlow;
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
@@ -202,7 +202,7 @@ impl<'tcx> LateLintPass<'tcx> for VecInitThenPush {
             if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind
                 && let ExprKind::MethodCall(name, self_arg, [_], _) = expr.kind
                 && path_to_local_id(self_arg, searcher.local_id)
-                && name.ident.as_str() == "push"
+                && name.ident.name == sym::push
             {
                 self.searcher = Some(VecPushSearcher {
                     err_span: searcher.err_span.to(stmt.span),
diff --git a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
index 467811c586b..d9dda6eadb2 100644
--- a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
+++ b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
@@ -9,8 +9,8 @@ use rustc_hir::{Item, ItemKind, PathSegment, UseKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::ty;
 use rustc_session::impl_lint_pass;
+use rustc_span::BytePos;
 use rustc_span::symbol::kw;
-use rustc_span::{BytePos, sym};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -193,9 +193,7 @@ impl WildcardImports {
 // Allow "...prelude::..::*" imports.
 // Many crates have a prelude, and it is imported as a glob by design.
 fn is_prelude_import(segments: &[PathSegment<'_>]) -> bool {
-    segments
-        .iter()
-        .any(|ps| ps.ident.as_str().contains(sym::prelude.as_str()))
+    segments.iter().any(|ps| ps.ident.as_str().contains("prelude"))
 }
 
 // Allow "super::*" imports in tests.
diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs
index a8758b65c91..d9a007635ca 100644
--- a/src/tools/clippy/clippy_lints/src/write.rs
+++ b/src/tools/clippy/clippy_lints/src/write.rs
@@ -5,8 +5,8 @@ use clippy_utils::source::{SpanRangeExt, expand_past_previous_comma};
 use clippy_utils::{is_in_test, sym};
 use rustc_ast::token::LitKind;
 use rustc_ast::{
-    FormatArgPosition, FormatArgPositionKind, FormatArgs, FormatArgsPiece, FormatOptions, FormatPlaceholder,
-    FormatTrait,
+    FormatArgPosition, FormatArgPositionKind, FormatArgs, FormatArgsPiece, FormatCount, FormatOptions,
+    FormatPlaceholder, FormatTrait,
 };
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, Impl, Item, ItemKind};
@@ -556,12 +556,7 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
     // Decrement the index of the remaining by the number of replaced positional arguments
     if !suggestion.is_empty() {
         for piece in &format_args.template {
-            if let Some((span, index)) = positional_arg_piece_span(piece)
-                && suggestion.iter().all(|(s, _)| *s != span)
-            {
-                let decrement = replaced_position.iter().filter(|i| **i < index).count();
-                suggestion.push((span, format!("{{{}}}", index.saturating_sub(decrement))));
-            }
+            relocalize_format_args_indexes(piece, &mut suggestion, &replaced_position);
         }
     }
 
@@ -574,7 +569,7 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
     }
 }
 
-/// Extract Span and its index from the given `piece`, iff it's positional argument.
+/// Extract Span and its index from the given `piece`, if it's positional argument.
 fn positional_arg_piece_span(piece: &FormatArgsPiece) -> Option<(Span, usize)> {
     match piece {
         FormatArgsPiece::Placeholder(FormatPlaceholder {
@@ -591,6 +586,57 @@ fn positional_arg_piece_span(piece: &FormatArgsPiece) -> Option<(Span, usize)> {
     }
 }
 
+/// Relocalizes the indexes of positional arguments in the format string
+fn relocalize_format_args_indexes(
+    piece: &FormatArgsPiece,
+    suggestion: &mut Vec<(Span, String)>,
+    replaced_position: &[usize],
+) {
+    if let FormatArgsPiece::Placeholder(FormatPlaceholder {
+        argument:
+            FormatArgPosition {
+                index: Ok(index),
+                // Only consider positional arguments
+                kind: FormatArgPositionKind::Number,
+                span: Some(span),
+            },
+        format_options,
+        ..
+    }) = piece
+    {
+        if suggestion.iter().any(|(s, _)| s.overlaps(*span)) {
+            // If the span is already in the suggestion, we don't need to process it again
+            return;
+        }
+
+        // lambda to get the decremented index based on the replaced positions
+        let decremented_index = |index: usize| -> usize {
+            let decrement = replaced_position.iter().filter(|&&i| i < index).count();
+            index - decrement
+        };
+
+        suggestion.push((*span, decremented_index(*index).to_string()));
+
+        // If there are format options, we need to handle them as well
+        if *format_options != FormatOptions::default() {
+            // lambda to process width and precision format counts and add them to the suggestion
+            let mut process_format_count = |count: &Option<FormatCount>, formatter: &dyn Fn(usize) -> String| {
+                if let Some(FormatCount::Argument(FormatArgPosition {
+                    index: Ok(format_arg_index),
+                    kind: FormatArgPositionKind::Number,
+                    span: Some(format_arg_span),
+                })) = count
+                {
+                    suggestion.push((*format_arg_span, formatter(decremented_index(*format_arg_index))));
+                }
+            };
+
+            process_format_count(&format_options.width, &|index: usize| format!("{index}$"));
+            process_format_count(&format_options.precision, &|index: usize| format!(".{index}$"));
+        }
+    }
+}
+
 /// Removes the raw marker, `#`s and quotes from a str, and returns if the literal is raw
 ///
 /// `r#"a"#` -> (`a`, true)
diff --git a/src/tools/clippy/clippy_lints/src/zombie_processes.rs b/src/tools/clippy/clippy_lints/src/zombie_processes.rs
index 09f1084fe70..6ab94a52210 100644
--- a/src/tools/clippy/clippy_lints/src/zombie_processes.rs
+++ b/src/tools/clippy/clippy_lints/src/zombie_processes.rs
@@ -5,7 +5,7 @@ use rustc_ast::Mutability;
 use rustc_ast::visit::visit_opt;
 use rustc_errors::Applicability;
 use rustc_hir::def_id::LocalDefId;
-use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_local};
+use rustc_hir::intravisit::{Visitor, walk_block, walk_expr};
 use rustc_hir::{Expr, ExprKind, HirId, LetStmt, Node, PatKind, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::hir::nested_filter;
@@ -69,8 +69,9 @@ impl<'tcx> LateLintPass<'tcx> for ZombieProcesses {
                     let mut vis = WaitFinder {
                         cx,
                         local_id,
+                        create_id: expr.hir_id,
                         body_id: cx.tcx.hir_enclosing_body_owner(expr.hir_id),
-                        state: VisitorState::WalkUpToLocal,
+                        state: VisitorState::WalkUpToCreate,
                         early_return: None,
                         missing_wait_branch: None,
                     };
@@ -131,6 +132,7 @@ struct MaybeWait(Span);
 struct WaitFinder<'a, 'tcx> {
     cx: &'a LateContext<'tcx>,
     local_id: HirId,
+    create_id: HirId,
     body_id: LocalDefId,
     state: VisitorState,
     early_return: Option<Span>,
@@ -141,8 +143,8 @@ struct WaitFinder<'a, 'tcx> {
 
 #[derive(PartialEq)]
 enum VisitorState {
-    WalkUpToLocal,
-    LocalFound,
+    WalkUpToCreate,
+    CreateFound,
 }
 
 #[derive(Copy, Clone)]
@@ -155,19 +157,13 @@ impl<'tcx> Visitor<'tcx> for WaitFinder<'_, 'tcx> {
     type NestedFilter = nested_filter::OnlyBodies;
     type Result = ControlFlow<MaybeWait>;
 
-    fn visit_local(&mut self, l: &'tcx LetStmt<'tcx>) -> Self::Result {
-        if self.state == VisitorState::WalkUpToLocal
-            && let PatKind::Binding(_, pat_id, ..) = l.pat.kind
-            && self.local_id == pat_id
-        {
-            self.state = VisitorState::LocalFound;
+    fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) -> Self::Result {
+        if ex.hir_id == self.create_id {
+            self.state = VisitorState::CreateFound;
+            return Continue(());
         }
 
-        walk_local(self, l)
-    }
-
-    fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) -> Self::Result {
-        if self.state != VisitorState::LocalFound {
+        if self.state != VisitorState::CreateFound {
             return walk_expr(self, ex);
         }
 
diff --git a/src/tools/clippy/clippy_lints_internal/src/almost_standard_lint_formulation.rs b/src/tools/clippy/clippy_lints_internal/src/almost_standard_lint_formulation.rs
index 311f76fee6b..7eeec84720f 100644
--- a/src/tools/clippy/clippy_lints_internal/src/almost_standard_lint_formulation.rs
+++ b/src/tools/clippy/clippy_lints_internal/src/almost_standard_lint_formulation.rs
@@ -45,7 +45,7 @@ impl AlmostStandardFormulation {
 impl<'tcx> LateLintPass<'tcx> for AlmostStandardFormulation {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
         let mut check_next = false;
-        if let ItemKind::Static(_, ty, Mutability::Not, _) = item.kind {
+        if let ItemKind::Static(Mutability::Not, _, ty, _) = item.kind {
             let lines = cx
                 .tcx
                 .hir_attrs(item.hir_id())
diff --git a/src/tools/clippy/clippy_lints_internal/src/lint_without_lint_pass.rs b/src/tools/clippy/clippy_lints_internal/src/lint_without_lint_pass.rs
index 0edeef3ab85..45a866030b2 100644
--- a/src/tools/clippy/clippy_lints_internal/src/lint_without_lint_pass.rs
+++ b/src/tools/clippy/clippy_lints_internal/src/lint_without_lint_pass.rs
@@ -105,7 +105,7 @@ impl_lint_pass!(LintWithoutLintPass => [
 
 impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
-        if let hir::ItemKind::Static(ident, ty, Mutability::Not, body_id) = item.kind {
+        if let hir::ItemKind::Static(Mutability::Not, ident, ty, body_id) = item.kind {
             if is_lint_ref_type(cx, ty) {
                 check_invalid_clippy_version_attribute(cx, item);
 
diff --git a/src/tools/clippy/clippy_utils/README.md b/src/tools/clippy/clippy_utils/README.md
index efbacbd72db..1aa16e3943c 100644
--- a/src/tools/clippy/clippy_utils/README.md
+++ b/src/tools/clippy/clippy_utils/README.md
@@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain:
 
 <!-- begin autogenerated nightly -->
 ```
-nightly-2025-05-31
+nightly-2025-06-12
 ```
 <!-- end autogenerated nightly -->
 
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index f6ef638e618..c7a2375c8df 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -1793,10 +1793,9 @@ pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool {
 
 /// Checks if the given `DefId` matches the `libc` item.
 pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: Symbol) -> bool {
-    let path = cx.get_def_path(did);
     // libc is meant to be used as a flat list of names, but they're all actually defined in different
     // modules based on the target platform. Ignore everything but crate name and the item name.
-    path.first().is_some_and(|s| *s == sym::libc) && path.last().copied() == Some(name)
+    cx.tcx.crate_name(did.krate) == sym::libc && cx.tcx.def_path_str(did).ends_with(name.as_str())
 }
 
 /// Returns the list of condition expressions and the list of blocks in a
@@ -3473,3 +3472,15 @@ pub fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
         None
     }
 }
+
+/// Checks if the given expression is a call to `Default::default()`.
+pub fn is_expr_default<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
+    if let ExprKind::Call(fn_expr, []) = &expr.kind
+        && let ExprKind::Path(qpath) = &fn_expr.kind
+        && let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id)
+    {
+        cx.tcx.is_diagnostic_item(sym::default_fn, def_id)
+    } else {
+        false
+    }
+}
diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
index 8e16f943e9f..e629012b187 100644
--- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
+++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
@@ -18,7 +18,7 @@ use rustc_middle::mir::{
 };
 use rustc_middle::traits::{BuiltinImplSource, ImplSource, ObligationCause};
 use rustc_middle::ty::adjustment::PointerCoercion;
-use rustc_middle::ty::{self, GenericArgKind, TraitRef, Ty, TyCtxt};
+use rustc_middle::ty::{self, GenericArgKind, Instance, TraitRef, Ty, TyCtxt};
 use rustc_span::Span;
 use rustc_span::symbol::sym;
 use rustc_trait_selection::traits::{ObligationCtxt, SelectionContext};
@@ -349,7 +349,15 @@ fn check_terminator<'tcx>(
         }
         | TerminatorKind::TailCall { func, args, fn_span: _ } => {
             let fn_ty = func.ty(body, cx.tcx);
-            if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() {
+            if let ty::FnDef(fn_def_id, fn_substs) = fn_ty.kind() {
+                // FIXME: when analyzing a function with generic parameters, we may not have enough information to
+                // resolve to an instance. However, we could check if a host effect predicate can guarantee that
+                // this can be made a `const` call.
+                let fn_def_id = match Instance::try_resolve(cx.tcx, cx.typing_env(), *fn_def_id, fn_substs) {
+                    Ok(Some(fn_inst)) => fn_inst.def_id(),
+                    Ok(None) => return Err((span, format!("cannot resolve instance for {func:?}").into())),
+                    Err(_) => return Err((span, format!("error during instance resolution of {func:?}").into())),
+                };
                 if !is_stable_const_fn(cx, fn_def_id, msrv) {
                     return Err((
                         span,
diff --git a/src/tools/clippy/clippy_utils/src/sym.rs b/src/tools/clippy/clippy_utils/src/sym.rs
index f417530be36..3b58dba5628 100644
--- a/src/tools/clippy/clippy_utils/src/sym.rs
+++ b/src/tools/clippy/clippy_utils/src/sym.rs
@@ -76,7 +76,6 @@ generate! {
     Visitor,
     Weak,
     abs,
-    align_of,
     ambiguous_glob_reexports,
     append,
     arg,
@@ -84,6 +83,7 @@ generate! {
     as_deref,
     as_deref_mut,
     as_mut,
+    as_path,
     assert_failed,
     author,
     borrow,
@@ -121,6 +121,8 @@ generate! {
     copy_to,
     copy_to_nonoverlapping,
     count_ones,
+    create,
+    create_new,
     cycle,
     cyclomatic_complexity,
     de,
@@ -132,7 +134,6 @@ generate! {
     enum_glob_use,
     enumerate,
     err,
-    error,
     exp,
     expect_err,
     expn_data,
@@ -150,8 +151,11 @@ generate! {
     floor_char_boundary,
     fold,
     for_each,
+    from_be_bytes,
     from_bytes_with_nul,
     from_bytes_with_nul_unchecked,
+    from_le_bytes,
+    from_ne_bytes,
     from_ptr,
     from_raw,
     from_ref,
@@ -184,8 +188,10 @@ generate! {
     is_err,
     is_file,
     is_none,
+    is_none_or,
     is_ok,
     is_some,
+    is_some_and,
     isqrt,
     itertools,
     join,
@@ -252,9 +258,12 @@ generate! {
     powi,
     product,
     push,
+    read,
+    read_exact,
     read_line,
     read_to_end,
     read_to_string,
+    read_unaligned,
     redundant_pub_crate,
     regex,
     rem_euclid,
@@ -323,16 +332,22 @@ generate! {
     then_some,
     to_ascii_lowercase,
     to_ascii_uppercase,
+    to_be_bytes,
     to_digit,
+    to_le_bytes,
     to_lowercase,
+    to_ne_bytes,
     to_os_string,
     to_owned,
     to_path_buf,
     to_uppercase,
     tokio,
     trim,
+    trim_end,
     trim_end_matches,
+    trim_start,
     trim_start_matches,
+    truncate,
     unreachable_pub,
     unsafe_removed_from_name,
     unused,
@@ -347,12 +362,15 @@ generate! {
     unwrap_unchecked,
     unzip,
     utils,
+    visit_str,
+    visit_string,
     wake,
     warnings,
     wildcard_imports,
     with_capacity,
     wrapping_offset,
     write,
+    write_unaligned,
     writeln,
     zip,
 }
diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs
index 61e70b3fa0b..32a992ccc2d 100644
--- a/src/tools/clippy/clippy_utils/src/ty/mod.rs
+++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs
@@ -20,7 +20,7 @@ use rustc_middle::traits::EvaluationResult;
 use rustc_middle::ty::layout::ValidityRequirement;
 use rustc_middle::ty::{
     self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef,
-    GenericParamDefKind, IntTy, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable,
+    GenericParamDefKind, IntTy, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable,
     TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr,
 };
 use rustc_span::symbol::Ident;
@@ -853,7 +853,7 @@ pub fn for_each_top_level_late_bound_region<B>(
                 ControlFlow::Continue(())
             }
         }
-        fn visit_binder<T: TypeFoldable<TyCtxt<'tcx>>>(&mut self, t: &Binder<'tcx, T>) -> Self::Result {
+        fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &Binder<'tcx, T>) -> Self::Result {
             self.index += 1;
             let res = t.super_visit_with(self);
             self.index -= 1;
@@ -1137,21 +1137,38 @@ impl<'tcx> InteriorMut<'tcx> {
 
     /// Check if given type has interior mutability such as [`std::cell::Cell`] or
     /// [`std::cell::RefCell`] etc. and if it does, returns a chain of types that causes
-    /// this type to be interior mutable
+    /// this type to be interior mutable.  False negatives may be expected for infinitely recursive
+    /// types, and `None` will be returned there.
     pub fn interior_mut_ty_chain(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx ty::List<Ty<'tcx>>> {
+        self.interior_mut_ty_chain_inner(cx, ty, 0)
+    }
+
+    fn interior_mut_ty_chain_inner(
+        &mut self,
+        cx: &LateContext<'tcx>,
+        ty: Ty<'tcx>,
+        depth: usize,
+    ) -> Option<&'tcx ty::List<Ty<'tcx>>> {
+        if !cx.tcx.recursion_limit().value_within_limit(depth) {
+            return None;
+        }
+
         match self.tys.entry(ty) {
             Entry::Occupied(o) => return *o.get(),
             // Temporarily insert a `None` to break cycles
             Entry::Vacant(v) => v.insert(None),
         };
+        let depth = depth + 1;
 
         let chain = match *ty.kind() {
-            ty::RawPtr(inner_ty, _) if !self.ignore_pointers => self.interior_mut_ty_chain(cx, inner_ty),
-            ty::Ref(_, inner_ty, _) | ty::Slice(inner_ty) => self.interior_mut_ty_chain(cx, inner_ty),
+            ty::RawPtr(inner_ty, _) if !self.ignore_pointers => self.interior_mut_ty_chain_inner(cx, inner_ty, depth),
+            ty::Ref(_, inner_ty, _) | ty::Slice(inner_ty) => self.interior_mut_ty_chain_inner(cx, inner_ty, depth),
             ty::Array(inner_ty, size) if size.try_to_target_usize(cx.tcx) != Some(0) => {
-                self.interior_mut_ty_chain(cx, inner_ty)
+                self.interior_mut_ty_chain_inner(cx, inner_ty, depth)
             },
-            ty::Tuple(fields) => fields.iter().find_map(|ty| self.interior_mut_ty_chain(cx, ty)),
+            ty::Tuple(fields) => fields
+                .iter()
+                .find_map(|ty| self.interior_mut_ty_chain_inner(cx, ty, depth)),
             ty::Adt(def, _) if def.is_unsafe_cell() => Some(ty::List::empty()),
             ty::Adt(def, args) => {
                 let is_std_collection = matches!(
@@ -1171,16 +1188,17 @@ impl<'tcx> InteriorMut<'tcx> {
 
                 if is_std_collection || def.is_box() {
                     // Include the types from std collections that are behind pointers internally
-                    args.types().find_map(|ty| self.interior_mut_ty_chain(cx, ty))
+                    args.types()
+                        .find_map(|ty| self.interior_mut_ty_chain_inner(cx, ty, depth))
                 } else if self.ignored_def_ids.contains(&def.did()) || def.is_phantom_data() {
                     None
                 } else {
                     def.all_fields()
-                        .find_map(|f| self.interior_mut_ty_chain(cx, f.ty(cx.tcx, args)))
+                        .find_map(|f| self.interior_mut_ty_chain_inner(cx, f.ty(cx.tcx, args), depth))
                 }
             },
             ty::Alias(ty::Projection, _) => match cx.tcx.try_normalize_erasing_regions(cx.typing_env(), ty) {
-                Ok(normalized_ty) if ty != normalized_ty => self.interior_mut_ty_chain(cx, normalized_ty),
+                Ok(normalized_ty) if ty != normalized_ty => self.interior_mut_ty_chain_inner(cx, normalized_ty, depth),
                 _ => None,
             },
             _ => None,
@@ -1342,7 +1360,8 @@ pub fn has_non_owning_mutable_access<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<'
                 mutability.is_mut() || !pointee_ty.is_freeze(cx.tcx, cx.typing_env())
             },
             ty::Closure(_, closure_args) => {
-                matches!(closure_args.types().next_back(), Some(captures) if has_non_owning_mutable_access_inner(cx, phantoms, captures))
+                matches!(closure_args.types().next_back(),
+                         Some(captures) if has_non_owning_mutable_access_inner(cx, phantoms, captures))
             },
             ty::Tuple(tuple_args) => tuple_args
                 .iter()
diff --git a/src/tools/clippy/rust-toolchain.toml b/src/tools/clippy/rust-toolchain.toml
index b6817d9a146..3fc5a1224a8 100644
--- a/src/tools/clippy/rust-toolchain.toml
+++ b/src/tools/clippy/rust-toolchain.toml
@@ -1,6 +1,6 @@
 [toolchain]
 # begin autogenerated nightly
-channel = "nightly-2025-05-31"
+channel = "nightly-2025-06-12"
 # end autogenerated nightly
 components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
 profile = "minimal"
diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs
index 78b27e2f613..99a01257a7b 100644
--- a/src/tools/clippy/tests/compile-test.rs
+++ b/src/tools/clippy/tests/compile-test.rs
@@ -273,10 +273,10 @@ fn run_ui_cargo(cx: &TestContext) {
     config.program.input_file_flag = CommandBuilder::cargo().input_file_flag;
     config.program.out_dir_flag = CommandBuilder::cargo().out_dir_flag;
     config.program.args = vec!["clippy".into(), "--color".into(), "never".into(), "--quiet".into()];
-    config
-        .program
-        .envs
-        .push(("RUSTFLAGS".into(), Some("-Dwarnings".into())));
+    config.program.envs.extend([
+        ("RUSTFLAGS".into(), Some("-Dwarnings".into())),
+        ("CARGO_INCREMENTAL".into(), Some("0".into())),
+    ]);
     // We need to do this while we still have a rustc in the `program` field.
     config.fill_host_and_target().unwrap();
     config.program.program.set_file_name(if cfg!(windows) {
diff --git a/src/tools/clippy/tests/symbols-used.rs b/src/tools/clippy/tests/symbols-used.rs
new file mode 100644
index 00000000000..bc0456711fb
--- /dev/null
+++ b/src/tools/clippy/tests/symbols-used.rs
@@ -0,0 +1,81 @@
+// This test checks that all symbols defined in Clippy's `sym.rs` file
+// are used in Clippy. Otherwise, it will fail with a list of symbols
+// which are unused.
+//
+// This test is a no-op if run as part of the compiler test suite
+// and will always succeed.
+
+use std::collections::HashSet;
+use std::ffi::OsStr;
+use std::fs;
+
+use regex::Regex;
+use walkdir::{DirEntry, WalkDir};
+
+const SYM_FILE: &str = "clippy_utils/src/sym.rs";
+
+type Result<T, E = AnyError> = std::result::Result<T, E>;
+type AnyError = Box<dyn std::error::Error>;
+
+#[test]
+#[allow(clippy::case_sensitive_file_extension_comparisons)]
+fn all_symbols_are_used() -> Result<()> {
+    if option_env!("RUSTC_TEST_SUITE").is_some() {
+        return Ok(());
+    }
+
+    // Load all symbols defined in `SYM_FILE`.
+    let content = fs::read_to_string(SYM_FILE)?;
+    let content = content
+        .split_once("generate! {")
+        .ok_or("cannot find symbols start")?
+        .1
+        .split_once("\n}\n")
+        .ok_or("cannot find symbols end")?
+        .0;
+    let mut interned: HashSet<String> = Regex::new(r"(?m)^    (\w+)")
+        .unwrap()
+        .captures_iter(content)
+        .map(|m| m[1].to_owned())
+        .collect();
+
+    // Remove symbols used as `sym::*`.
+    let used_re = Regex::new(r"\bsym::(\w+)\b").unwrap();
+    let rs_ext = OsStr::new("rs");
+    for dir in ["clippy_lints", "clippy_lints_internal", "clippy_utils", "src"] {
+        for file in WalkDir::new(dir)
+            .into_iter()
+            .flatten()
+            .map(DirEntry::into_path)
+            .filter(|p| p.extension() == Some(rs_ext))
+        {
+            for cap in used_re.captures_iter(&fs::read_to_string(file)?) {
+                interned.remove(&cap[1]);
+            }
+        }
+    }
+
+    // Remove symbols used as part of paths.
+    let paths_re = Regex::new(r"path!\(([\w:]+)\)").unwrap();
+    for path in [
+        "clippy_utils/src/paths.rs",
+        "clippy_lints_internal/src/internal_paths.rs",
+    ] {
+        for cap in paths_re.captures_iter(&fs::read_to_string(path)?) {
+            for sym in cap[1].split("::") {
+                interned.remove(sym);
+            }
+        }
+    }
+
+    let mut extra = interned.iter().collect::<Vec<_>>();
+    if !extra.is_empty() {
+        extra.sort_unstable();
+        eprintln!("Unused symbols defined in {SYM_FILE}:");
+        for sym in extra {
+            eprintln!("  - {sym}");
+        }
+        Err(format!("extra symbols found — remove them {SYM_FILE}"))?;
+    }
+    Ok(())
+}
diff --git a/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs b/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs
index 6a9a49324db..4a179cd929f 100644
--- a/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs
+++ b/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs
@@ -1,4 +1,5 @@
 #![warn(clippy::await_holding_invalid_type)]
+#![allow(clippy::ip_constant)]
 use std::net::Ipv4Addr;
 
 async fn bad() -> u32 {
diff --git a/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr b/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr
index deb7f49db9e..c3c88698032 100644
--- a/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr
+++ b/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr
@@ -1,5 +1,5 @@
 error: holding a disallowed type across an await point `std::string::String`
-  --> tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs:5:9
+  --> tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs:6:9
    |
 LL |     let _x = String::from("hello");
    |         ^^
@@ -9,13 +9,13 @@ LL |     let _x = String::from("hello");
    = help: to override `-D warnings` add `#[allow(clippy::await_holding_invalid_type)]`
 
 error: holding a disallowed type across an await point `std::net::Ipv4Addr`
-  --> tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs:11:9
+  --> tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs:12:9
    |
 LL |     let x = Ipv4Addr::new(127, 0, 0, 1);
    |         ^
 
 error: holding a disallowed type across an await point `std::string::String`
-  --> tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs:35:13
+  --> tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs:36:13
    |
 LL |         let _x = String::from("hi!");
    |             ^^
diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.rs b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.rs
index 06472a4f5d5..922d30443fc 100644
--- a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.rs
+++ b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.rs
@@ -239,3 +239,40 @@ fn fp_if_let_issue7054() {
 }
 
 fn main() {}
+
+mod issue14873 {
+    fn foo() -> i32 {
+        todo!()
+    }
+
+    macro_rules! qux {
+        ($a:ident, $b:ident, $condition:expr) => {
+            if $condition {
+                "."
+            } else {
+                ""
+            };
+            $a = foo();
+            $b = foo();
+        };
+    }
+
+    fn share_on_bottom() {
+        let mut a = 0;
+        let mut b = 0;
+        if false {
+            qux!(a, b, a == b);
+        } else {
+            qux!(a, b, a != b);
+        };
+
+        if false {
+            qux!(a, b, a == b);
+            let y = 1;
+        } else {
+            qux!(a, b, a != b);
+            let y = 1;
+            //~^ branches_sharing_code
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.stderr b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.stderr
index 648a99c65ed..f437db8b733 100644
--- a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.stderr
+++ b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.stderr
@@ -157,5 +157,20 @@ LL ~     if x == 17 { b = 1; a = 0x99; } else { }
 LL +     a = 0x99;
    |
 
-error: aborting due to 9 previous errors
+error: all if blocks contain the same code at the end
+  --> tests/ui/branches_sharing_code/shared_at_bottom.rs:274:9
+   |
+LL | /             let y = 1;
+LL | |
+LL | |         }
+   | |_________^
+   |
+   = warning: some moved values might need to be renamed to avoid wrong references
+help: consider moving these statements after the if
+   |
+LL ~         }
+LL +         let y = 1;
+   |
+
+error: aborting due to 10 previous errors
 
diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.rs b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.rs
index 694c67d4c85..dcd77679fc6 100644
--- a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.rs
+++ b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.rs
@@ -124,3 +124,34 @@ fn pf_local_with_inferred_type_issue7053() {
 }
 
 fn main() {}
+
+mod issue14873 {
+    fn foo() -> i32 {
+        todo!()
+    }
+
+    macro_rules! qux {
+        ($a:ident, $b:ident, $condition:expr) => {
+            let $a: i32 = foo();
+            let $b: i32 = foo();
+            if $condition { "." } else { "" }
+        };
+    }
+
+    fn share_on_top() {
+        if false {
+            qux!(a, b, a == b);
+        } else {
+            qux!(a, b, a != b);
+        };
+
+        if false {
+            //~^ branches_sharing_code
+            let x = 1;
+            qux!(a, b, a == b);
+        } else {
+            let x = 1;
+            qux!(a, b, a != b);
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.stderr b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.stderr
index d28e9c7af29..30efb98b3f5 100644
--- a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.stderr
+++ b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.stderr
@@ -125,5 +125,20 @@ note: the lint level is defined here
 LL | #![deny(clippy::branches_sharing_code, clippy::if_same_then_else)]
    |                                        ^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 7 previous errors
+error: all if blocks contain the same code at the start
+  --> tests/ui/branches_sharing_code/shared_at_top.rs:148:9
+   |
+LL | /         if false {
+LL | |
+LL | |             let x = 1;
+   | |______________________^
+   |
+   = warning: some moved values might need to be renamed to avoid wrong references
+help: consider moving these statements before the if
+   |
+LL ~         let x = 1;
+LL +         if false {
+   |
+
+error: aborting due to 8 previous errors
 
diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs
index 75334f70f1f..e848f0601e3 100644
--- a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs
+++ b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs
@@ -128,3 +128,42 @@ fn added_note_for_expression_use() -> u32 {
 }
 
 fn main() {}
+
+mod issue14873 {
+    fn foo() -> i32 {
+        todo!()
+    }
+
+    macro_rules! qux {
+        ($a:ident, $b:ident, $condition:expr) => {
+            let mut $a: i32 = foo();
+            let mut $b: i32 = foo();
+            if $condition {
+                "."
+            } else {
+                ""
+            };
+            $a = foo();
+            $b = foo();
+        };
+    }
+
+    fn share_on_top_and_bottom() {
+        if false {
+            qux!(a, b, a == b);
+        } else {
+            qux!(a, b, a != b);
+        };
+
+        if false {
+            //~^ branches_sharing_code
+            let x = 1;
+            qux!(a, b, a == b);
+            let y = 1;
+        } else {
+            let x = 1;
+            qux!(a, b, a != b);
+            let y = 1;
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr
index 2200ab45089..40f3453edb9 100644
--- a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr
+++ b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr
@@ -159,5 +159,31 @@ LL ~     }
 LL +     x * 4
    |
 
-error: aborting due to 5 previous errors
+error: all if blocks contain the same code at both the start and the end
+  --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:158:9
+   |
+LL | /         if false {
+LL | |
+LL | |             let x = 1;
+   | |______________________^
+   |
+note: this code is shared at the end
+  --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:166:9
+   |
+LL | /             let y = 1;
+LL | |         }
+   | |_________^
+   = warning: some moved values might need to be renamed to avoid wrong references
+help: consider moving these statements before the if
+   |
+LL ~         let x = 1;
+LL +         if false {
+   |
+help: consider moving these statements after the if
+   |
+LL ~         }
+LL +         let y = 1;
+   |
+
+error: aborting due to 6 previous errors
 
diff --git a/src/tools/clippy/tests/ui/cmp_null.fixed b/src/tools/clippy/tests/ui/cmp_null.fixed
index 140ddb10aeb..04b8ec50160 100644
--- a/src/tools/clippy/tests/ui/cmp_null.fixed
+++ b/src/tools/clippy/tests/ui/cmp_null.fixed
@@ -33,3 +33,9 @@ fn main() {
     let _ = (x as *const ()).is_null();
     //~^ cmp_null
 }
+
+fn issue15010() {
+    let f: *mut i32 = std::ptr::null_mut();
+    debug_assert!(!f.is_null());
+    //~^ cmp_null
+}
diff --git a/src/tools/clippy/tests/ui/cmp_null.rs b/src/tools/clippy/tests/ui/cmp_null.rs
index 16ed17765da..6f7762e6ae8 100644
--- a/src/tools/clippy/tests/ui/cmp_null.rs
+++ b/src/tools/clippy/tests/ui/cmp_null.rs
@@ -33,3 +33,9 @@ fn main() {
     let _ = x as *const () == ptr::null();
     //~^ cmp_null
 }
+
+fn issue15010() {
+    let f: *mut i32 = std::ptr::null_mut();
+    debug_assert!(f != std::ptr::null_mut());
+    //~^ cmp_null
+}
diff --git a/src/tools/clippy/tests/ui/cmp_null.stderr b/src/tools/clippy/tests/ui/cmp_null.stderr
index 6821846d046..8a75b050111 100644
--- a/src/tools/clippy/tests/ui/cmp_null.stderr
+++ b/src/tools/clippy/tests/ui/cmp_null.stderr
@@ -31,5 +31,11 @@ error: comparing with null is better expressed by the `.is_null()` method
 LL |     let _ = x as *const () == ptr::null();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(x as *const ()).is_null()`
 
-error: aborting due to 5 previous errors
+error: comparing with null is better expressed by the `.is_null()` method
+  --> tests/ui/cmp_null.rs:39:19
+   |
+LL |     debug_assert!(f != std::ptr::null_mut());
+   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `!f.is_null()`
+
+error: aborting due to 6 previous errors
 
diff --git a/src/tools/clippy/tests/ui/coerce_container_to_any.fixed b/src/tools/clippy/tests/ui/coerce_container_to_any.fixed
new file mode 100644
index 00000000000..ae9d3ef9656
--- /dev/null
+++ b/src/tools/clippy/tests/ui/coerce_container_to_any.fixed
@@ -0,0 +1,26 @@
+#![warn(clippy::coerce_container_to_any)]
+
+use std::any::Any;
+
+fn main() {
+    let x: Box<dyn Any> = Box::new(());
+    let ref_x = &x;
+
+    f(&*x);
+    //~^ coerce_container_to_any
+
+    f(&**ref_x);
+    //~^ coerce_container_to_any
+
+    let _: &dyn Any = &*x;
+    //~^ coerce_container_to_any
+
+    f(&42);
+    f(&Box::new(()));
+    f(&Box::new(Box::new(())));
+    f(&**ref_x);
+    f(&*x);
+    let _: &dyn Any = &*x;
+}
+
+fn f(_: &dyn Any) {}
diff --git a/src/tools/clippy/tests/ui/coerce_container_to_any.rs b/src/tools/clippy/tests/ui/coerce_container_to_any.rs
new file mode 100644
index 00000000000..9948bd48e0d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/coerce_container_to_any.rs
@@ -0,0 +1,26 @@
+#![warn(clippy::coerce_container_to_any)]
+
+use std::any::Any;
+
+fn main() {
+    let x: Box<dyn Any> = Box::new(());
+    let ref_x = &x;
+
+    f(&x);
+    //~^ coerce_container_to_any
+
+    f(ref_x);
+    //~^ coerce_container_to_any
+
+    let _: &dyn Any = &x;
+    //~^ coerce_container_to_any
+
+    f(&42);
+    f(&Box::new(()));
+    f(&Box::new(Box::new(())));
+    f(&**ref_x);
+    f(&*x);
+    let _: &dyn Any = &*x;
+}
+
+fn f(_: &dyn Any) {}
diff --git a/src/tools/clippy/tests/ui/coerce_container_to_any.stderr b/src/tools/clippy/tests/ui/coerce_container_to_any.stderr
new file mode 100644
index 00000000000..00ab77e0ce0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/coerce_container_to_any.stderr
@@ -0,0 +1,23 @@
+error: coercing `&std::boxed::Box<dyn std::any::Any>` to `&dyn Any`
+  --> tests/ui/coerce_container_to_any.rs:9:7
+   |
+LL |     f(&x);
+   |       ^^ help: consider dereferencing: `&*x`
+   |
+   = note: `-D clippy::coerce-container-to-any` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::coerce_container_to_any)]`
+
+error: coercing `&std::boxed::Box<dyn std::any::Any>` to `&dyn Any`
+  --> tests/ui/coerce_container_to_any.rs:12:7
+   |
+LL |     f(ref_x);
+   |       ^^^^^ help: consider dereferencing: `&**ref_x`
+
+error: coercing `&std::boxed::Box<dyn std::any::Any>` to `&dyn Any`
+  --> tests/ui/coerce_container_to_any.rs:15:23
+   |
+LL |     let _: &dyn Any = &x;
+   |                       ^^ help: consider dereferencing: `&*x`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/crashes/ice-14935.rs b/src/tools/clippy/tests/ui/crashes/ice-14935.rs
new file mode 100644
index 00000000000..74cda9aae53
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-14935.rs
@@ -0,0 +1,27 @@
+//@check-pass
+#![warn(clippy::mutable_key_type)]
+
+use std::marker::PhantomData;
+
+trait Group {
+    type ExposantSet: Group;
+}
+
+struct Pow<T: Group> {
+    exposant: Box<Pow<T::ExposantSet>>,
+    _p: PhantomData<T>,
+}
+
+impl<T: Group> Pow<T> {
+    fn is_zero(&self) -> bool {
+        false
+    }
+    fn normalize(&self) {
+        #[expect(clippy::if_same_then_else)]
+        if self.is_zero() {
+        } else if false {
+        }
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-9463.rs b/src/tools/clippy/tests/ui/crashes/ice-9463.rs
index 93808e0f892..cfa6cdac6fa 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-9463.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-9463.rs
@@ -1,8 +1,7 @@
-#![deny(arithmetic_overflow)]
+//@check-pass
+
 fn main() {
     let _x = -1_i32 >> -1;
-    //~^ ERROR: this arithmetic operation will overflow
+    #[expect(overflowing_literals)]
     let _y = 1u32 >> 10000000000000u32;
-    //~^ ERROR: this arithmetic operation will overflow
-    //~| ERROR: literal out of range
 }
diff --git a/src/tools/clippy/tests/ui/crashes/ice-9463.stderr b/src/tools/clippy/tests/ui/crashes/ice-9463.stderr
deleted file mode 100644
index 9a3a5e444ad..00000000000
--- a/src/tools/clippy/tests/ui/crashes/ice-9463.stderr
+++ /dev/null
@@ -1,29 +0,0 @@
-error: this arithmetic operation will overflow
-  --> tests/ui/crashes/ice-9463.rs:3:14
-   |
-LL |     let _x = -1_i32 >> -1;
-   |              ^^^^^^^^^^^^ attempt to shift right by `-1_i32`, which would overflow
-   |
-note: the lint level is defined here
-  --> tests/ui/crashes/ice-9463.rs:1:9
-   |
-LL | #![deny(arithmetic_overflow)]
-   |         ^^^^^^^^^^^^^^^^^^^
-
-error: this arithmetic operation will overflow
-  --> tests/ui/crashes/ice-9463.rs:5:14
-   |
-LL |     let _y = 1u32 >> 10000000000000u32;
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^ attempt to shift right by `1316134912_u32`, which would overflow
-
-error: literal out of range for `u32`
-  --> tests/ui/crashes/ice-9463.rs:5:22
-   |
-LL |     let _y = 1u32 >> 10000000000000u32;
-   |                      ^^^^^^^^^^^^^^^^^
-   |
-   = note: the literal `10000000000000u32` does not fit into the type `u32` whose range is `0..=4294967295`
-   = note: `#[deny(overflowing_literals)]` on by default
-
-error: aborting due to 3 previous errors
-
diff --git a/src/tools/clippy/tests/ui/crashes/ice-rust-107877.rs b/src/tools/clippy/tests/ui/crashes/ice-rust-107877.rs
index 55fe418bed1..dccb0cf8ac1 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-rust-107877.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-rust-107877.rs
@@ -4,6 +4,7 @@
 
 struct Foo;
 
+#[allow(clippy::infallible_try_from)]
 impl<'a> std::convert::TryFrom<&'a String> for Foo {
     type Error = std::convert::Infallible;
 
diff --git a/src/tools/clippy/tests/ui/create_dir.fixed b/src/tools/clippy/tests/ui/create_dir.fixed
index 4a5b1b77be6..d4b8f8b4d07 100644
--- a/src/tools/clippy/tests/ui/create_dir.fixed
+++ b/src/tools/clippy/tests/ui/create_dir.fixed
@@ -7,12 +7,31 @@ fn create_dir() {}
 
 fn main() {
     // Should be warned
-    create_dir_all("foo");
+    std::fs::create_dir_all("foo");
     //~^ create_dir
-    create_dir_all("bar").unwrap();
+    std::fs::create_dir_all("bar").unwrap();
     //~^ create_dir
 
     // Shouldn't be warned
     create_dir();
     std::fs::create_dir_all("foobar");
 }
+
+mod issue14994 {
+    fn with_no_prefix() {
+        use std::fs::create_dir;
+        std::fs::create_dir_all("some/dir").unwrap();
+        //~^ create_dir
+    }
+
+    fn with_fs_prefix() {
+        use std::fs;
+        fs::create_dir_all("/some/dir").unwrap();
+        //~^ create_dir
+    }
+
+    fn with_full_prefix() {
+        std::fs::create_dir_all("/some/dir").unwrap();
+        //~^ create_dir
+    }
+}
diff --git a/src/tools/clippy/tests/ui/create_dir.rs b/src/tools/clippy/tests/ui/create_dir.rs
index bf185ba3a7c..21e0bdba03b 100644
--- a/src/tools/clippy/tests/ui/create_dir.rs
+++ b/src/tools/clippy/tests/ui/create_dir.rs
@@ -16,3 +16,22 @@ fn main() {
     create_dir();
     std::fs::create_dir_all("foobar");
 }
+
+mod issue14994 {
+    fn with_no_prefix() {
+        use std::fs::create_dir;
+        create_dir("some/dir").unwrap();
+        //~^ create_dir
+    }
+
+    fn with_fs_prefix() {
+        use std::fs;
+        fs::create_dir("/some/dir").unwrap();
+        //~^ create_dir
+    }
+
+    fn with_full_prefix() {
+        std::fs::create_dir("/some/dir").unwrap();
+        //~^ create_dir
+    }
+}
diff --git a/src/tools/clippy/tests/ui/create_dir.stderr b/src/tools/clippy/tests/ui/create_dir.stderr
index 51d6ac686e0..e3ca0e7255c 100644
--- a/src/tools/clippy/tests/ui/create_dir.stderr
+++ b/src/tools/clippy/tests/ui/create_dir.stderr
@@ -8,9 +8,8 @@ LL |     std::fs::create_dir("foo");
    = help: to override `-D warnings` add `#[allow(clippy::create_dir)]`
 help: consider calling `std::fs::create_dir_all` instead
    |
-LL -     std::fs::create_dir("foo");
-LL +     create_dir_all("foo");
-   |
+LL |     std::fs::create_dir_all("foo");
+   |                        ++++
 
 error: calling `std::fs::create_dir` where there may be a better way
   --> tests/ui/create_dir.rs:12:5
@@ -20,9 +19,41 @@ LL |     std::fs::create_dir("bar").unwrap();
    |
 help: consider calling `std::fs::create_dir_all` instead
    |
-LL -     std::fs::create_dir("bar").unwrap();
-LL +     create_dir_all("bar").unwrap();
+LL |     std::fs::create_dir_all("bar").unwrap();
+   |                        ++++
+
+error: calling `std::fs::create_dir` where there may be a better way
+  --> tests/ui/create_dir.rs:23:9
+   |
+LL |         create_dir("some/dir").unwrap();
+   |         ^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider calling `std::fs::create_dir_all` instead
+   |
+LL |         std::fs::create_dir_all("some/dir").unwrap();
+   |         +++++++++          ++++
+
+error: calling `std::fs::create_dir` where there may be a better way
+  --> tests/ui/create_dir.rs:29:9
+   |
+LL |         fs::create_dir("/some/dir").unwrap();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider calling `std::fs::create_dir_all` instead
+   |
+LL |         fs::create_dir_all("/some/dir").unwrap();
+   |                       ++++
+
+error: calling `std::fs::create_dir` where there may be a better way
+  --> tests/ui/create_dir.rs:34:9
+   |
+LL |         std::fs::create_dir("/some/dir").unwrap();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider calling `std::fs::create_dir_all` instead
    |
+LL |         std::fs::create_dir_all("/some/dir").unwrap();
+   |                            ++++
 
-error: aborting due to 2 previous errors
+error: aborting due to 5 previous errors
 
diff --git a/src/tools/clippy/tests/ui/disallowed_names.rs b/src/tools/clippy/tests/ui/disallowed_names.rs
index 30fbdbc1fdc..15bb6734997 100644
--- a/src/tools/clippy/tests/ui/disallowed_names.rs
+++ b/src/tools/clippy/tests/ui/disallowed_names.rs
@@ -1,3 +1,4 @@
+//@aux-build:proc_macros.rs
 #![allow(
     dead_code,
     clippy::needless_if,
@@ -9,6 +10,9 @@
 )]
 #![warn(clippy::disallowed_names)]
 
+extern crate proc_macros;
+use proc_macros::{external, with_span};
+
 fn test(foo: ()) {}
 //~^ disallowed_names
 
@@ -66,6 +70,17 @@ fn issue_1647_ref_mut() {
     //~^ disallowed_names
 }
 
+pub fn issue_14958_proc_macro() {
+    // does not lint macro-generated code
+    external! {
+        let foo = 0;
+    }
+    with_span! {
+        span
+        let foo = 0;
+    }
+}
+
 #[cfg(test)]
 mod tests {
     fn issue_7305() {
diff --git a/src/tools/clippy/tests/ui/disallowed_names.stderr b/src/tools/clippy/tests/ui/disallowed_names.stderr
index 09398ebbab7..b43d1b3ebfa 100644
--- a/src/tools/clippy/tests/ui/disallowed_names.stderr
+++ b/src/tools/clippy/tests/ui/disallowed_names.stderr
@@ -1,5 +1,5 @@
 error: use of a disallowed/placeholder name `foo`
-  --> tests/ui/disallowed_names.rs:12:9
+  --> tests/ui/disallowed_names.rs:16:9
    |
 LL | fn test(foo: ()) {}
    |         ^^^
@@ -8,79 +8,79 @@ LL | fn test(foo: ()) {}
    = help: to override `-D warnings` add `#[allow(clippy::disallowed_names)]`
 
 error: use of a disallowed/placeholder name `foo`
-  --> tests/ui/disallowed_names.rs:16:9
+  --> tests/ui/disallowed_names.rs:20:9
    |
 LL |     let foo = 42;
    |         ^^^
 
 error: use of a disallowed/placeholder name `baz`
-  --> tests/ui/disallowed_names.rs:19:9
+  --> tests/ui/disallowed_names.rs:23:9
    |
 LL |     let baz = 42;
    |         ^^^
 
 error: use of a disallowed/placeholder name `quux`
-  --> tests/ui/disallowed_names.rs:22:9
+  --> tests/ui/disallowed_names.rs:26:9
    |
 LL |     let quux = 42;
    |         ^^^^
 
 error: use of a disallowed/placeholder name `foo`
-  --> tests/ui/disallowed_names.rs:35:10
+  --> tests/ui/disallowed_names.rs:39:10
    |
 LL |         (foo, Some(baz), quux @ Some(_)) => (),
    |          ^^^
 
 error: use of a disallowed/placeholder name `baz`
-  --> tests/ui/disallowed_names.rs:35:20
+  --> tests/ui/disallowed_names.rs:39:20
    |
 LL |         (foo, Some(baz), quux @ Some(_)) => (),
    |                    ^^^
 
 error: use of a disallowed/placeholder name `quux`
-  --> tests/ui/disallowed_names.rs:35:26
+  --> tests/ui/disallowed_names.rs:39:26
    |
 LL |         (foo, Some(baz), quux @ Some(_)) => (),
    |                          ^^^^
 
 error: use of a disallowed/placeholder name `foo`
-  --> tests/ui/disallowed_names.rs:43:19
+  --> tests/ui/disallowed_names.rs:47:19
    |
 LL | fn issue_1647(mut foo: u8) {
    |                   ^^^
 
 error: use of a disallowed/placeholder name `baz`
-  --> tests/ui/disallowed_names.rs:46:13
+  --> tests/ui/disallowed_names.rs:50:13
    |
 LL |     let mut baz = 0;
    |             ^^^
 
 error: use of a disallowed/placeholder name `quux`
-  --> tests/ui/disallowed_names.rs:49:21
+  --> tests/ui/disallowed_names.rs:53:21
    |
 LL |     if let Some(mut quux) = Some(42) {}
    |                     ^^^^
 
 error: use of a disallowed/placeholder name `baz`
-  --> tests/ui/disallowed_names.rs:54:13
+  --> tests/ui/disallowed_names.rs:58:13
    |
 LL |     let ref baz = 0;
    |             ^^^
 
 error: use of a disallowed/placeholder name `quux`
-  --> tests/ui/disallowed_names.rs:57:21
+  --> tests/ui/disallowed_names.rs:61:21
    |
 LL |     if let Some(ref quux) = Some(42) {}
    |                     ^^^^
 
 error: use of a disallowed/placeholder name `baz`
-  --> tests/ui/disallowed_names.rs:62:17
+  --> tests/ui/disallowed_names.rs:66:17
    |
 LL |     let ref mut baz = 0;
    |                 ^^^
 
 error: use of a disallowed/placeholder name `quux`
-  --> tests/ui/disallowed_names.rs:65:25
+  --> tests/ui/disallowed_names.rs:69:25
    |
 LL |     if let Some(ref mut quux) = Some(42) {}
    |                         ^^^^
diff --git a/src/tools/clippy/tests/ui/doc_suspicious_footnotes.fixed b/src/tools/clippy/tests/ui/doc_suspicious_footnotes.fixed
new file mode 100644
index 00000000000..9ed3fd4ef31
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc_suspicious_footnotes.fixed
@@ -0,0 +1,186 @@
+#![warn(clippy::doc_suspicious_footnotes)]
+#![allow(clippy::needless_raw_string_hashes)]
+//! This is not a footnote[^1].
+//!
+//! [^1]: <!-- description -->
+//~^ doc_suspicious_footnotes
+//!
+//! This is not a footnote[^either], but it doesn't warn.
+//!
+//! This is not a footnote\[^1], but it also doesn't warn.
+//!
+//! This is not a footnote[^1\], but it also doesn't warn.
+//!
+//! This is not a `footnote[^1]`, but it also doesn't warn.
+//!
+//! This is a footnote[^2].
+//!
+//! [^2]: hello world
+
+/// This is not a footnote[^1].
+///
+/// [^1]: <!-- description -->
+//~^ doc_suspicious_footnotes
+///
+/// This is not a footnote[^either], but it doesn't warn.
+///
+/// This is not a footnote\[^1], but it also doesn't warn.
+///
+/// This is not a footnote[^1\], but it also doesn't warn.
+///
+/// This is not a `footnote[^1]`, but it also doesn't warn.
+///
+/// This is a footnote[^2].
+///
+/// [^2]: hello world
+pub fn footnotes() {
+    // test code goes here
+}
+
+pub struct Foo;
+#[rustfmt::skip]
+impl Foo {
+    #[doc = r#"This is not a footnote[^1].
+
+[^1]: <!-- description -->"#]
+    //~^ doc_suspicious_footnotes
+    #[doc = r#""#]
+    #[doc = r#"This is not a footnote[^either], but it doesn't warn."#]
+    #[doc = r#""#]
+    #[doc = r#"This is not a footnote\[^1], but it also doesn't warn."#]
+    #[doc = r#""#]
+    #[doc = r#"This is not a footnote[^1\], but it also doesn't warn."#]
+    #[doc = r#""#]
+    #[doc = r#"This is not a `footnote[^1]`, but it also doesn't warn."#]
+    #[doc = r#""#]
+    #[doc = r#"This is a footnote[^2]."#]
+    #[doc = r#""#]
+    #[doc = r#"[^2]: hello world"#]
+    pub fn footnotes() {
+        // test code goes here
+    }
+    #[doc = r#"This is not a footnote[^1].
+
+    This is not a footnote[^either], but it doesn't warn.
+
+    This is not a footnote\[^1], but it also doesn't warn.
+
+    This is not a footnote[^1\], but it also doesn't warn.
+
+    This is not a `footnote[^1]`, but it also doesn't warn.
+
+    This is a footnote[^2].
+
+    [^2]: hello world
+    
+
+[^1]: <!-- description -->"#]
+    //~^^^^^^^^^^^^^^ doc_suspicious_footnotes
+    pub fn footnotes2() {
+        // test code goes here
+    }
+    #[cfg_attr(
+        not(FALSE),
+        doc = r#"This is not a footnote[^1].
+
+This is not a footnote[^either], but it doesn't warn.
+
+[^1]: <!-- description -->"#
+    //~^ doc_suspicious_footnotes
+    )]
+    pub fn footnotes3() {
+        // test code goes here
+    }
+    #[doc = "My footnote [^foot\note]"]
+    pub fn footnote4() {
+        // test code goes here
+    }
+    #[doc = "Hihi"]pub fn footnote5() {
+        // test code goes here
+    }
+}
+
+#[doc = r#"This is not a footnote[^1].
+
+[^1]: <!-- description -->"#]
+//~^ doc_suspicious_footnotes
+#[doc = r""]
+#[doc = r"This is not a footnote[^either], but it doesn't warn."]
+#[doc = r""]
+#[doc = r"This is not a footnote\[^1], but it also doesn't warn."]
+#[doc = r""]
+#[doc = r"This is not a footnote[^1\], but it also doesn't warn."]
+#[doc = r""]
+#[doc = r"This is not a `footnote[^1]`, but it also doesn't warn."]
+#[doc = r""]
+#[doc = r"This is a footnote[^2]."]
+#[doc = r""]
+#[doc = r"[^2]: hello world"]
+pub fn footnotes_attrs() {
+    // test code goes here
+}
+
+pub mod multiline {
+    /*!
+     * This is not a footnote[^1]. //~ doc_suspicious_footnotes
+     *
+     * This is not a footnote\[^1], but it doesn't warn.
+     *
+     * This is a footnote[^2].
+     *
+     * These give weird results, but correct ones, so it works.
+     *
+     * [^2]: hello world
+     */
+/*! [^1]: <!-- description --> */
+    /**
+     * This is not a footnote[^1]. //~ doc_suspicious_footnotes
+     *
+     * This is not a footnote\[^1], but it doesn't warn.
+     *
+     * This is a footnote[^2].
+     *
+     * These give weird results, but correct ones, so it works.
+     *
+     * [^2]: hello world
+     */
+/** [^1]: <!-- description --> */
+    pub fn foo() {}
+}
+
+/// This is not a footnote [^1]
+///
+/// [^1]: <!-- description -->
+//~^ doc_suspicious_footnotes
+///
+/// This one is [^2]
+///
+/// [^2]: contents
+#[doc = r#"This is not a footnote [^3]
+
+[^3]: <!-- description -->"#]
+//~^ doc_suspicious_footnotes
+#[doc = ""]
+#[doc = "This one is [^4]"]
+#[doc = ""]
+#[doc = "[^4]: contents"]
+pub struct MultiFragmentFootnote;
+
+#[doc(inline)]
+/// This is not a footnote [^5]
+///
+/// [^5]: <!-- description -->
+//~^ doc_suspicious_footnotes
+///
+/// This one is [^6]
+///
+/// [^6]: contents
+#[doc = r#"This is not a footnote [^7]
+
+[^7]: <!-- description -->"#]
+//~^ doc_suspicious_footnotes
+#[doc = ""]
+#[doc = "This one is [^8]"]
+#[doc = ""]
+#[doc = "[^8]: contents"]
+pub use MultiFragmentFootnote as OtherInlinedFootnote;
diff --git a/src/tools/clippy/tests/ui/doc_suspicious_footnotes.rs b/src/tools/clippy/tests/ui/doc_suspicious_footnotes.rs
new file mode 100644
index 00000000000..9a8d0dcf475
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc_suspicious_footnotes.rs
@@ -0,0 +1,162 @@
+#![warn(clippy::doc_suspicious_footnotes)]
+#![allow(clippy::needless_raw_string_hashes)]
+//! This is not a footnote[^1].
+//~^ doc_suspicious_footnotes
+//!
+//! This is not a footnote[^either], but it doesn't warn.
+//!
+//! This is not a footnote\[^1], but it also doesn't warn.
+//!
+//! This is not a footnote[^1\], but it also doesn't warn.
+//!
+//! This is not a `footnote[^1]`, but it also doesn't warn.
+//!
+//! This is a footnote[^2].
+//!
+//! [^2]: hello world
+
+/// This is not a footnote[^1].
+//~^ doc_suspicious_footnotes
+///
+/// This is not a footnote[^either], but it doesn't warn.
+///
+/// This is not a footnote\[^1], but it also doesn't warn.
+///
+/// This is not a footnote[^1\], but it also doesn't warn.
+///
+/// This is not a `footnote[^1]`, but it also doesn't warn.
+///
+/// This is a footnote[^2].
+///
+/// [^2]: hello world
+pub fn footnotes() {
+    // test code goes here
+}
+
+pub struct Foo;
+#[rustfmt::skip]
+impl Foo {
+    #[doc = r#"This is not a footnote[^1]."#]
+    //~^ doc_suspicious_footnotes
+    #[doc = r#""#]
+    #[doc = r#"This is not a footnote[^either], but it doesn't warn."#]
+    #[doc = r#""#]
+    #[doc = r#"This is not a footnote\[^1], but it also doesn't warn."#]
+    #[doc = r#""#]
+    #[doc = r#"This is not a footnote[^1\], but it also doesn't warn."#]
+    #[doc = r#""#]
+    #[doc = r#"This is not a `footnote[^1]`, but it also doesn't warn."#]
+    #[doc = r#""#]
+    #[doc = r#"This is a footnote[^2]."#]
+    #[doc = r#""#]
+    #[doc = r#"[^2]: hello world"#]
+    pub fn footnotes() {
+        // test code goes here
+    }
+    #[doc = "This is not a footnote[^1].
+
+    This is not a footnote[^either], but it doesn't warn.
+
+    This is not a footnote\\[^1], but it also doesn't warn.
+
+    This is not a footnote[^1\\], but it also doesn't warn.
+
+    This is not a `footnote[^1]`, but it also doesn't warn.
+
+    This is a footnote[^2].
+
+    [^2]: hello world
+    "]
+    //~^^^^^^^^^^^^^^ doc_suspicious_footnotes
+    pub fn footnotes2() {
+        // test code goes here
+    }
+    #[cfg_attr(
+        not(FALSE),
+        doc = "This is not a footnote[^1].\n\nThis is not a footnote[^either], but it doesn't warn."
+    //~^ doc_suspicious_footnotes
+    )]
+    pub fn footnotes3() {
+        // test code goes here
+    }
+    #[doc = "My footnote [^foot\note]"]
+    pub fn footnote4() {
+        // test code goes here
+    }
+    #[doc = "Hihi"]pub fn footnote5() {
+        // test code goes here
+    }
+}
+
+#[doc = r"This is not a footnote[^1]."]
+//~^ doc_suspicious_footnotes
+#[doc = r""]
+#[doc = r"This is not a footnote[^either], but it doesn't warn."]
+#[doc = r""]
+#[doc = r"This is not a footnote\[^1], but it also doesn't warn."]
+#[doc = r""]
+#[doc = r"This is not a footnote[^1\], but it also doesn't warn."]
+#[doc = r""]
+#[doc = r"This is not a `footnote[^1]`, but it also doesn't warn."]
+#[doc = r""]
+#[doc = r"This is a footnote[^2]."]
+#[doc = r""]
+#[doc = r"[^2]: hello world"]
+pub fn footnotes_attrs() {
+    // test code goes here
+}
+
+pub mod multiline {
+    /*!
+     * This is not a footnote[^1]. //~ doc_suspicious_footnotes
+     *
+     * This is not a footnote\[^1], but it doesn't warn.
+     *
+     * This is a footnote[^2].
+     *
+     * These give weird results, but correct ones, so it works.
+     *
+     * [^2]: hello world
+     */
+    /**
+     * This is not a footnote[^1]. //~ doc_suspicious_footnotes
+     *
+     * This is not a footnote\[^1], but it doesn't warn.
+     *
+     * This is a footnote[^2].
+     *
+     * These give weird results, but correct ones, so it works.
+     *
+     * [^2]: hello world
+     */
+    pub fn foo() {}
+}
+
+/// This is not a footnote [^1]
+//~^ doc_suspicious_footnotes
+///
+/// This one is [^2]
+///
+/// [^2]: contents
+#[doc = "This is not a footnote [^3]"]
+//~^ doc_suspicious_footnotes
+#[doc = ""]
+#[doc = "This one is [^4]"]
+#[doc = ""]
+#[doc = "[^4]: contents"]
+pub struct MultiFragmentFootnote;
+
+#[doc(inline)]
+/// This is not a footnote [^5]
+//~^ doc_suspicious_footnotes
+///
+/// This one is [^6]
+///
+/// [^6]: contents
+#[doc = "This is not a footnote [^7]"]
+//~^ doc_suspicious_footnotes
+#[doc = ""]
+#[doc = "This one is [^8]"]
+#[doc = ""]
+#[doc = "[^8]: contents"]
+pub use MultiFragmentFootnote as OtherInlinedFootnote;
diff --git a/src/tools/clippy/tests/ui/doc_suspicious_footnotes.stderr b/src/tools/clippy/tests/ui/doc_suspicious_footnotes.stderr
new file mode 100644
index 00000000000..4f920f37a62
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc_suspicious_footnotes.stderr
@@ -0,0 +1,179 @@
+error: looks like a footnote ref, but has no matching footnote
+  --> tests/ui/doc_suspicious_footnotes.rs:3:27
+   |
+LL | //! This is not a footnote[^1].
+   |                           ^^^^
+   |
+   = note: `-D clippy::doc-suspicious-footnotes` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::doc_suspicious_footnotes)]`
+help: add footnote definition
+   |
+LL ~ //! This is not a footnote[^1].
+LL + //!
+LL + //! [^1]: <!-- description -->
+   |
+
+error: looks like a footnote ref, but has no matching footnote
+  --> tests/ui/doc_suspicious_footnotes.rs:18:27
+   |
+LL | /// This is not a footnote[^1].
+   |                           ^^^^
+   |
+help: add footnote definition
+   |
+LL ~ /// This is not a footnote[^1].
+LL + ///
+LL + /// [^1]: <!-- description -->
+   |
+
+error: looks like a footnote ref, but has no matching footnote
+  --> tests/ui/doc_suspicious_footnotes.rs:39:13
+   |
+LL |     #[doc = r#"This is not a footnote[^1]."#]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: add footnote definition
+   |
+LL ~     #[doc = r#"This is not a footnote[^1].
+LL + 
+LL ~ [^1]: <!-- description -->"#]
+   |
+
+error: looks like a footnote ref, but has no matching footnote
+  --> tests/ui/doc_suspicious_footnotes.rs:56:13
+   |
+LL |       #[doc = "This is not a footnote[^1].
+   |  _____________^
+LL | |
+LL | |     This is not a footnote[^either], but it doesn't warn.
+...  |
+LL | |     [^2]: hello world
+LL | |     "]
+   | |_____^
+   |
+help: add footnote definition
+   |
+LL ~     #[doc = r#"This is not a footnote[^1].
+LL + 
+LL +     This is not a footnote[^either], but it doesn't warn.
+LL + 
+LL +     This is not a footnote\[^1], but it also doesn't warn.
+LL + 
+LL +     This is not a footnote[^1\], but it also doesn't warn.
+LL + 
+LL +     This is not a `footnote[^1]`, but it also doesn't warn.
+LL + 
+LL +     This is a footnote[^2].
+LL + 
+LL +     [^2]: hello world
+LL +     
+LL + 
+LL ~ [^1]: <!-- description -->"#]
+   |
+
+error: looks like a footnote ref, but has no matching footnote
+  --> tests/ui/doc_suspicious_footnotes.rs:76:38
+   |
+LL |         doc = "This is not a footnote[^1].\n\nThis is not a footnote[^either], but it doesn't warn."
+   |                                      ^^^^
+   |
+help: add footnote definition
+   |
+LL ~         doc = r#"This is not a footnote[^1].
+LL + 
+LL + This is not a footnote[^either], but it doesn't warn.
+LL + 
+LL + [^1]: <!-- description -->"#
+   |
+
+error: looks like a footnote ref, but has no matching footnote
+  --> tests/ui/doc_suspicious_footnotes.rs:91:9
+   |
+LL | #[doc = r"This is not a footnote[^1]."]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: add footnote definition
+   |
+LL ~ #[doc = r#"This is not a footnote[^1].
+LL + 
+LL ~ [^1]: <!-- description -->"#]
+   |
+
+error: looks like a footnote ref, but has no matching footnote
+  --> tests/ui/doc_suspicious_footnotes.rs:111:30
+   |
+LL |      * This is not a footnote[^1].
+   |                              ^^^^
+   |
+help: add footnote definition
+   |
+LL ~      */
+LL + /*! [^1]: <!-- description --> */
+   |
+
+error: looks like a footnote ref, but has no matching footnote
+  --> tests/ui/doc_suspicious_footnotes.rs:122:30
+   |
+LL |      * This is not a footnote[^1].
+   |                              ^^^^
+   |
+help: add footnote definition
+   |
+LL ~      */
+LL + /** [^1]: <!-- description --> */
+   |
+
+error: looks like a footnote ref, but has no matching footnote
+  --> tests/ui/doc_suspicious_footnotes.rs:135:28
+   |
+LL | /// This is not a footnote [^1]
+   |                            ^^^^
+   |
+help: add footnote definition
+   |
+LL ~ /// This is not a footnote [^1]
+LL + ///
+LL + /// [^1]: <!-- description -->
+   |
+
+error: looks like a footnote ref, but has no matching footnote
+  --> tests/ui/doc_suspicious_footnotes.rs:141:33
+   |
+LL | #[doc = "This is not a footnote [^3]"]
+   |                                 ^^^^
+   |
+help: add footnote definition
+   |
+LL ~ #[doc = r#"This is not a footnote [^3]
+LL + 
+LL ~ [^3]: <!-- description -->"#]
+   |
+
+error: looks like a footnote ref, but has no matching footnote
+  --> tests/ui/doc_suspicious_footnotes.rs:150:28
+   |
+LL | /// This is not a footnote [^5]
+   |                            ^^^^
+   |
+help: add footnote definition
+   |
+LL ~ /// This is not a footnote [^5]
+LL + ///
+LL + /// [^5]: <!-- description -->
+   |
+
+error: looks like a footnote ref, but has no matching footnote
+  --> tests/ui/doc_suspicious_footnotes.rs:156:33
+   |
+LL | #[doc = "This is not a footnote [^7]"]
+   |                                 ^^^^
+   |
+help: add footnote definition
+   |
+LL ~ #[doc = r#"This is not a footnote [^7]
+LL + 
+LL ~ [^7]: <!-- description -->"#]
+   |
+
+error: aborting due to 12 previous errors
+
diff --git a/src/tools/clippy/tests/ui/doc_suspicious_footnotes_include.rs b/src/tools/clippy/tests/ui/doc_suspicious_footnotes_include.rs
new file mode 100644
index 00000000000..4f75ad94eaf
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc_suspicious_footnotes_include.rs
@@ -0,0 +1,4 @@
+//@ error-in-other-file: footnote
+//@ no-rustfix
+#![warn(clippy::doc_suspicious_footnotes)]
+#![doc=include_str!("doc_suspicious_footnotes_include.txt")]
diff --git a/src/tools/clippy/tests/ui/doc_suspicious_footnotes_include.stderr b/src/tools/clippy/tests/ui/doc_suspicious_footnotes_include.stderr
new file mode 100644
index 00000000000..74154e3f4ef
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc_suspicious_footnotes_include.stderr
@@ -0,0 +1,17 @@
+error: looks like a footnote ref, but has no matching footnote
+  --> tests/ui/doc_suspicious_footnotes_include.txt:1:23
+   |
+LL | This is not a footnote[^1].
+   |                       ^^^^
+   |
+   = note: `-D clippy::doc-suspicious-footnotes` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::doc_suspicious_footnotes)]`
+help: add footnote definition
+   |
+LL ~ [^2]: hello world
+LL + 
+LL + [^1]: <!-- description -->
+   |
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/clippy/tests/ui/doc_suspicious_footnotes_include.txt b/src/tools/clippy/tests/ui/doc_suspicious_footnotes_include.txt
new file mode 100644
index 00000000000..2a533e32c4a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc_suspicious_footnotes_include.txt
@@ -0,0 +1,13 @@
+This is not a footnote[^1]. //~ doc_suspicious_footnotes
+
+This is not a footnote[^either], but it doesn't warn.
+
+This is not a footnote\[^1], but it also doesn't warn.
+
+This is not a footnote[^1\], but it also doesn't warn.
+
+This is not a `footnote[^1]`, but it also doesn't warn.
+
+This is a footnote[^2].
+
+[^2]: hello world
diff --git a/src/tools/clippy/tests/ui/format_args.fixed b/src/tools/clippy/tests/ui/format_args.fixed
index edfdaa23ca1..3cb6c0b4d97 100644
--- a/src/tools/clippy/tests/ui/format_args.fixed
+++ b/src/tools/clippy/tests/ui/format_args.fixed
@@ -192,3 +192,13 @@ mod issue_9256 {
         print_substring("Hello, world!");
     }
 }
+
+mod issue14952 {
+    use std::path::Path;
+    struct Foo(Path);
+    impl std::fmt::Debug for Foo {
+        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+            write!(f, "{:?}", &self.0)
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui/format_args.rs b/src/tools/clippy/tests/ui/format_args.rs
index 367560d577d..8a9c369fff3 100644
--- a/src/tools/clippy/tests/ui/format_args.rs
+++ b/src/tools/clippy/tests/ui/format_args.rs
@@ -192,3 +192,13 @@ mod issue_9256 {
         print_substring("Hello, world!");
     }
 }
+
+mod issue14952 {
+    use std::path::Path;
+    struct Foo(Path);
+    impl std::fmt::Debug for Foo {
+        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+            write!(f, "{:?}", &self.0)
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui/indexing_slicing_index.rs b/src/tools/clippy/tests/ui/indexing_slicing_index.rs
index ab6a8235008..2510a023acd 100644
--- a/src/tools/clippy/tests/ui/indexing_slicing_index.rs
+++ b/src/tools/clippy/tests/ui/indexing_slicing_index.rs
@@ -68,7 +68,6 @@ fn main() {
     // This should be linted, since `suppress-restriction-lint-in-const` default is false.
     const { &ARR[idx4()] };
     //~^ ERROR: indexing may panic
-    //~| ERROR: index out of bounds
 
     let y = &x;
     // Ok, referencing shouldn't affect this lint. See the issue 6021
diff --git a/src/tools/clippy/tests/ui/indexing_slicing_index.stderr b/src/tools/clippy/tests/ui/indexing_slicing_index.stderr
index 8e24b898ed5..c68e1d53a93 100644
--- a/src/tools/clippy/tests/ui/indexing_slicing_index.stderr
+++ b/src/tools/clippy/tests/ui/indexing_slicing_index.stderr
@@ -9,18 +9,6 @@ LL | const REF: &i32 = &ARR[idx()]; // This should be linted, since `suppress-re
    = note: `-D clippy::indexing-slicing` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::indexing_slicing)]`
 
-error[E0080]: index out of bounds: the length is 2 but the index is 4
-  --> tests/ui/indexing_slicing_index.rs:69:14
-   |
-LL |     const { &ARR[idx4()] };
-   |              ^^^^^^^^^^^ evaluation of `main::{constant#3}` failed here
-
-note: erroneous constant encountered
-  --> tests/ui/indexing_slicing_index.rs:69:5
-   |
-LL |     const { &ARR[idx4()] };
-   |     ^^^^^^^^^^^^^^^^^^^^^^
-
 error: indexing may panic
   --> tests/ui/indexing_slicing_index.rs:48:5
    |
@@ -63,13 +51,13 @@ LL |     const { &ARR[idx4()] };
    = note: the suggestion might not be applicable in constant blocks
 
 error: index is out of bounds
-  --> tests/ui/indexing_slicing_index.rs:77:5
+  --> tests/ui/indexing_slicing_index.rs:76:5
    |
 LL |     y[4];
    |     ^^^^
 
 error: indexing may panic
-  --> tests/ui/indexing_slicing_index.rs:81:5
+  --> tests/ui/indexing_slicing_index.rs:80:5
    |
 LL |     v[0];
    |     ^^^^
@@ -77,7 +65,7 @@ LL |     v[0];
    = help: consider using `.get(n)` or `.get_mut(n)` instead
 
 error: indexing may panic
-  --> tests/ui/indexing_slicing_index.rs:83:5
+  --> tests/ui/indexing_slicing_index.rs:82:5
    |
 LL |     v[10];
    |     ^^^^^
@@ -85,7 +73,7 @@ LL |     v[10];
    = help: consider using `.get(n)` or `.get_mut(n)` instead
 
 error: indexing may panic
-  --> tests/ui/indexing_slicing_index.rs:85:5
+  --> tests/ui/indexing_slicing_index.rs:84:5
    |
 LL |     v[1 << 3];
    |     ^^^^^^^^^
@@ -93,13 +81,13 @@ LL |     v[1 << 3];
    = help: consider using `.get(n)` or `.get_mut(n)` instead
 
 error: index is out of bounds
-  --> tests/ui/indexing_slicing_index.rs:93:5
+  --> tests/ui/indexing_slicing_index.rs:92:5
    |
 LL |     x[N];
    |     ^^^^
 
 error: indexing may panic
-  --> tests/ui/indexing_slicing_index.rs:97:5
+  --> tests/ui/indexing_slicing_index.rs:96:5
    |
 LL |     v[N];
    |     ^^^^
@@ -107,7 +95,7 @@ LL |     v[N];
    = help: consider using `.get(n)` or `.get_mut(n)` instead
 
 error: indexing may panic
-  --> tests/ui/indexing_slicing_index.rs:99:5
+  --> tests/ui/indexing_slicing_index.rs:98:5
    |
 LL |     v[M];
    |     ^^^^
@@ -115,11 +103,10 @@ LL |     v[M];
    = help: consider using `.get(n)` or `.get_mut(n)` instead
 
 error: index is out of bounds
-  --> tests/ui/indexing_slicing_index.rs:103:13
+  --> tests/ui/indexing_slicing_index.rs:102:13
    |
 LL |     let _ = x[4];
    |             ^^^^
 
-error: aborting due to 15 previous errors
+error: aborting due to 14 previous errors
 
-For more information about this error, try `rustc --explain E0080`.
diff --git a/src/tools/clippy/tests/ui/infallible_try_from.rs b/src/tools/clippy/tests/ui/infallible_try_from.rs
new file mode 100644
index 00000000000..6a1f12f824f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/infallible_try_from.rs
@@ -0,0 +1,33 @@
+#![feature(never_type)]
+#![warn(clippy::infallible_try_from)]
+
+use std::convert::Infallible;
+
+struct MyStruct(i32);
+
+impl TryFrom<i8> for MyStruct {
+    //~^ infallible_try_from
+    type Error = !;
+    fn try_from(other: i8) -> Result<Self, !> {
+        Ok(Self(other.into()))
+    }
+}
+
+impl TryFrom<i16> for MyStruct {
+    //~^ infallible_try_from
+    type Error = Infallible;
+    fn try_from(other: i16) -> Result<Self, Infallible> {
+        Ok(Self(other.into()))
+    }
+}
+
+impl TryFrom<i64> for MyStruct {
+    type Error = i64;
+    fn try_from(other: i64) -> Result<Self, i64> {
+        Ok(Self(i32::try_from(other).map_err(|_| other)?))
+    }
+}
+
+fn main() {
+    // test code goes here
+}
diff --git a/src/tools/clippy/tests/ui/infallible_try_from.stderr b/src/tools/clippy/tests/ui/infallible_try_from.stderr
new file mode 100644
index 00000000000..705b1188489
--- /dev/null
+++ b/src/tools/clippy/tests/ui/infallible_try_from.stderr
@@ -0,0 +1,23 @@
+error: infallible TryFrom impl; consider implementing From, instead
+  --> tests/ui/infallible_try_from.rs:8:1
+   |
+LL | impl TryFrom<i8> for MyStruct {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |
+LL |     type Error = !;
+   |                  - infallible error type
+   |
+   = note: `-D clippy::infallible-try-from` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::infallible_try_from)]`
+
+error: infallible TryFrom impl; consider implementing From, instead
+  --> tests/ui/infallible_try_from.rs:16:1
+   |
+LL | impl TryFrom<i16> for MyStruct {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |
+LL |     type Error = Infallible;
+   |                  ---------- infallible error type
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/ip_constant.fixed b/src/tools/clippy/tests/ui/ip_constant.fixed
new file mode 100644
index 00000000000..2e3389c1193
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ip_constant.fixed
@@ -0,0 +1,107 @@
+#![warn(clippy::ip_constant)]
+#![allow(dead_code)]
+#![allow(clippy::identity_op)]
+#![allow(clippy::eq_op)]
+
+fn literal_test1() {
+    use std::net::Ipv4Addr;
+    let _ = Ipv4Addr::LOCALHOST;
+    //~^ ip_constant
+    let _ = Ipv4Addr::BROADCAST;
+    //~^ ip_constant
+    let _ = Ipv4Addr::UNSPECIFIED;
+    //~^ ip_constant
+
+    use std::net::Ipv6Addr;
+    let _ = Ipv6Addr::LOCALHOST;
+    //~^ ip_constant
+    let _ = Ipv6Addr::UNSPECIFIED;
+    //~^ ip_constant
+}
+
+fn literal_test2() {
+    use std::net;
+    let _ = net::Ipv4Addr::LOCALHOST;
+    //~^ ip_constant
+    let _ = net::Ipv4Addr::BROADCAST;
+    //~^ ip_constant
+    let _ = net::Ipv4Addr::UNSPECIFIED;
+    //~^ ip_constant
+
+    let _ = net::Ipv6Addr::LOCALHOST;
+    //~^ ip_constant
+    let _ = net::Ipv6Addr::UNSPECIFIED;
+    //~^ ip_constant
+}
+
+fn literal_test3() {
+    let _ = std::net::Ipv4Addr::LOCALHOST;
+    //~^ ip_constant
+    let _ = std::net::Ipv4Addr::BROADCAST;
+    //~^ ip_constant
+    let _ = std::net::Ipv4Addr::UNSPECIFIED;
+    //~^ ip_constant
+
+    let _ = std::net::Ipv6Addr::LOCALHOST;
+    //~^ ip_constant
+    let _ = std::net::Ipv6Addr::UNSPECIFIED;
+    //~^ ip_constant
+}
+
+const CONST_U8_0: u8 = 0;
+const CONST_U8_1: u8 = 1;
+const CONST_U8_127: u8 = 127;
+const CONST_U8_255: u8 = 255;
+
+const CONST_U16_0: u16 = 0;
+const CONST_U16_1: u16 = 1;
+
+fn const_test1() {
+    use std::net::Ipv4Addr;
+    let _ = Ipv4Addr::LOCALHOST;
+    //~^ ip_constant
+    let _ = Ipv4Addr::BROADCAST;
+    //~^ ip_constant
+    let _ = Ipv4Addr::UNSPECIFIED;
+    //~^ ip_constant
+
+    use std::net::Ipv6Addr;
+    let _ = Ipv6Addr::LOCALHOST;
+
+    let _ = Ipv6Addr::UNSPECIFIED;
+}
+
+fn const_test2() {
+    use std::net::Ipv4Addr;
+    let _ = Ipv4Addr::LOCALHOST;
+    //~^ ip_constant
+    let _ = Ipv4Addr::BROADCAST;
+    //~^ ip_constant
+    let _ = Ipv4Addr::UNSPECIFIED;
+    //~^ ip_constant
+
+    use std::net::Ipv6Addr;
+    let _ = Ipv6Addr::LOCALHOST;
+    //~^ ip_constant
+    let _ = Ipv6Addr::LOCALHOST;
+    //~^ ip_constant
+}
+
+macro_rules! ipv4_new {
+    ($a:expr, $b:expr, $c:expr, $d:expr) => {
+        std::net::Ipv4Addr::new($a, $b, $c, $d)
+    };
+}
+
+fn macro_test() {
+    let _ = ipv4_new!(127, 0, 0, 1);
+    // no lint
+    let _ = ipv4_new!(255, 255, 255, 255);
+    // no lint
+    let _ = ipv4_new!(0, 0, 0, 0);
+    // no lint
+}
+
+fn main() {
+    // UI Test
+}
diff --git a/src/tools/clippy/tests/ui/ip_constant.rs b/src/tools/clippy/tests/ui/ip_constant.rs
new file mode 100644
index 00000000000..15e0b0551ba
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ip_constant.rs
@@ -0,0 +1,127 @@
+#![warn(clippy::ip_constant)]
+#![allow(dead_code)]
+#![allow(clippy::identity_op)]
+#![allow(clippy::eq_op)]
+
+fn literal_test1() {
+    use std::net::Ipv4Addr;
+    let _ = Ipv4Addr::new(127, 0, 0, 1);
+    //~^ ip_constant
+    let _ = Ipv4Addr::new(255, 255, 255, 255);
+    //~^ ip_constant
+    let _ = Ipv4Addr::new(0, 0, 0, 0);
+    //~^ ip_constant
+
+    use std::net::Ipv6Addr;
+    let _ = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
+    //~^ ip_constant
+    let _ = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
+    //~^ ip_constant
+}
+
+fn literal_test2() {
+    use std::net;
+    let _ = net::Ipv4Addr::new(127, 0, 0, 1);
+    //~^ ip_constant
+    let _ = net::Ipv4Addr::new(255, 255, 255, 255);
+    //~^ ip_constant
+    let _ = net::Ipv4Addr::new(0, 0, 0, 0);
+    //~^ ip_constant
+
+    let _ = net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
+    //~^ ip_constant
+    let _ = net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
+    //~^ ip_constant
+}
+
+fn literal_test3() {
+    let _ = std::net::Ipv4Addr::new(127, 0, 0, 1);
+    //~^ ip_constant
+    let _ = std::net::Ipv4Addr::new(255, 255, 255, 255);
+    //~^ ip_constant
+    let _ = std::net::Ipv4Addr::new(0, 0, 0, 0);
+    //~^ ip_constant
+
+    let _ = std::net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
+    //~^ ip_constant
+    let _ = std::net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
+    //~^ ip_constant
+}
+
+const CONST_U8_0: u8 = 0;
+const CONST_U8_1: u8 = 1;
+const CONST_U8_127: u8 = 127;
+const CONST_U8_255: u8 = 255;
+
+const CONST_U16_0: u16 = 0;
+const CONST_U16_1: u16 = 1;
+
+fn const_test1() {
+    use std::net::Ipv4Addr;
+    let _ = Ipv4Addr::new(CONST_U8_127, CONST_U8_0, CONST_U8_0, CONST_U8_1);
+    //~^ ip_constant
+    let _ = Ipv4Addr::new(CONST_U8_255, CONST_U8_255, CONST_U8_255, CONST_U8_255);
+    //~^ ip_constant
+    let _ = Ipv4Addr::new(CONST_U8_0, CONST_U8_0, CONST_U8_0, CONST_U8_0);
+    //~^ ip_constant
+
+    use std::net::Ipv6Addr;
+    let _ = Ipv6Addr::new(
+        //~^ ip_constant
+        CONST_U16_0,
+        CONST_U16_0,
+        CONST_U16_0,
+        CONST_U16_0,
+        CONST_U16_0,
+        CONST_U16_0,
+        CONST_U16_0,
+        CONST_U16_1,
+    );
+
+    let _ = Ipv6Addr::new(
+        //~^ ip_constant
+        CONST_U16_0,
+        CONST_U16_0,
+        CONST_U16_0,
+        CONST_U16_0,
+        CONST_U16_0,
+        CONST_U16_0,
+        CONST_U16_0,
+        CONST_U16_0,
+    );
+}
+
+fn const_test2() {
+    use std::net::Ipv4Addr;
+    let _ = Ipv4Addr::new(126 + 1, 0, 0, 1);
+    //~^ ip_constant
+    let _ = Ipv4Addr::new(254 + CONST_U8_1, 255, { 255 - CONST_U8_0 }, CONST_U8_255);
+    //~^ ip_constant
+    let _ = Ipv4Addr::new(0, CONST_U8_255 - 255, 0, { 1 + 0 - 1 });
+    //~^ ip_constant
+
+    use std::net::Ipv6Addr;
+    let _ = Ipv6Addr::new(0 + CONST_U16_0, 0, 0, 0, 0, 0, 0, 1);
+    //~^ ip_constant
+    let _ = Ipv6Addr::new(0 + 0, 0, 0, 0, 0, { 2 - 1 - CONST_U16_1 }, 0, 1);
+    //~^ ip_constant
+}
+
+macro_rules! ipv4_new {
+    ($a:expr, $b:expr, $c:expr, $d:expr) => {
+        std::net::Ipv4Addr::new($a, $b, $c, $d)
+    };
+}
+
+fn macro_test() {
+    let _ = ipv4_new!(127, 0, 0, 1);
+    // no lint
+    let _ = ipv4_new!(255, 255, 255, 255);
+    // no lint
+    let _ = ipv4_new!(0, 0, 0, 0);
+    // no lint
+}
+
+fn main() {
+    // UI Test
+}
diff --git a/src/tools/clippy/tests/ui/ip_constant.stderr b/src/tools/clippy/tests/ui/ip_constant.stderr
new file mode 100644
index 00000000000..3e984c6cb3b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ip_constant.stderr
@@ -0,0 +1,338 @@
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:8:13
+   |
+LL |     let _ = Ipv4Addr::new(127, 0, 0, 1);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::ip-constant` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::ip_constant)]`
+help: use
+   |
+LL -     let _ = Ipv4Addr::new(127, 0, 0, 1);
+LL +     let _ = Ipv4Addr::LOCALHOST;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:10:13
+   |
+LL |     let _ = Ipv4Addr::new(255, 255, 255, 255);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = Ipv4Addr::new(255, 255, 255, 255);
+LL +     let _ = Ipv4Addr::BROADCAST;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:12:13
+   |
+LL |     let _ = Ipv4Addr::new(0, 0, 0, 0);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = Ipv4Addr::new(0, 0, 0, 0);
+LL +     let _ = Ipv4Addr::UNSPECIFIED;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:16:13
+   |
+LL |     let _ = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
+LL +     let _ = Ipv6Addr::LOCALHOST;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:18:13
+   |
+LL |     let _ = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
+LL +     let _ = Ipv6Addr::UNSPECIFIED;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:24:13
+   |
+LL |     let _ = net::Ipv4Addr::new(127, 0, 0, 1);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = net::Ipv4Addr::new(127, 0, 0, 1);
+LL +     let _ = net::Ipv4Addr::LOCALHOST;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:26:13
+   |
+LL |     let _ = net::Ipv4Addr::new(255, 255, 255, 255);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = net::Ipv4Addr::new(255, 255, 255, 255);
+LL +     let _ = net::Ipv4Addr::BROADCAST;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:28:13
+   |
+LL |     let _ = net::Ipv4Addr::new(0, 0, 0, 0);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = net::Ipv4Addr::new(0, 0, 0, 0);
+LL +     let _ = net::Ipv4Addr::UNSPECIFIED;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:31:13
+   |
+LL |     let _ = net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
+LL +     let _ = net::Ipv6Addr::LOCALHOST;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:33:13
+   |
+LL |     let _ = net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
+LL +     let _ = net::Ipv6Addr::UNSPECIFIED;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:38:13
+   |
+LL |     let _ = std::net::Ipv4Addr::new(127, 0, 0, 1);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = std::net::Ipv4Addr::new(127, 0, 0, 1);
+LL +     let _ = std::net::Ipv4Addr::LOCALHOST;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:40:13
+   |
+LL |     let _ = std::net::Ipv4Addr::new(255, 255, 255, 255);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = std::net::Ipv4Addr::new(255, 255, 255, 255);
+LL +     let _ = std::net::Ipv4Addr::BROADCAST;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:42:13
+   |
+LL |     let _ = std::net::Ipv4Addr::new(0, 0, 0, 0);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = std::net::Ipv4Addr::new(0, 0, 0, 0);
+LL +     let _ = std::net::Ipv4Addr::UNSPECIFIED;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:45:13
+   |
+LL |     let _ = std::net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = std::net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
+LL +     let _ = std::net::Ipv6Addr::LOCALHOST;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:47:13
+   |
+LL |     let _ = std::net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = std::net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
+LL +     let _ = std::net::Ipv6Addr::UNSPECIFIED;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:61:13
+   |
+LL |     let _ = Ipv4Addr::new(CONST_U8_127, CONST_U8_0, CONST_U8_0, CONST_U8_1);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = Ipv4Addr::new(CONST_U8_127, CONST_U8_0, CONST_U8_0, CONST_U8_1);
+LL +     let _ = Ipv4Addr::LOCALHOST;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:63:13
+   |
+LL |     let _ = Ipv4Addr::new(CONST_U8_255, CONST_U8_255, CONST_U8_255, CONST_U8_255);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = Ipv4Addr::new(CONST_U8_255, CONST_U8_255, CONST_U8_255, CONST_U8_255);
+LL +     let _ = Ipv4Addr::BROADCAST;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:65:13
+   |
+LL |     let _ = Ipv4Addr::new(CONST_U8_0, CONST_U8_0, CONST_U8_0, CONST_U8_0);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = Ipv4Addr::new(CONST_U8_0, CONST_U8_0, CONST_U8_0, CONST_U8_0);
+LL +     let _ = Ipv4Addr::UNSPECIFIED;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:69:13
+   |
+LL |       let _ = Ipv6Addr::new(
+   |  _____________^
+LL | |
+LL | |         CONST_U16_0,
+LL | |         CONST_U16_0,
+...  |
+LL | |         CONST_U16_1,
+LL | |     );
+   | |_____^
+   |
+help: use
+   |
+LL -     let _ = Ipv6Addr::new(
+LL -
+LL -         CONST_U16_0,
+LL -         CONST_U16_0,
+LL -         CONST_U16_0,
+LL -         CONST_U16_0,
+LL -         CONST_U16_0,
+LL -         CONST_U16_0,
+LL -         CONST_U16_0,
+LL -         CONST_U16_1,
+LL -     );
+LL +     let _ = Ipv6Addr::LOCALHOST;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:81:13
+   |
+LL |       let _ = Ipv6Addr::new(
+   |  _____________^
+LL | |
+LL | |         CONST_U16_0,
+LL | |         CONST_U16_0,
+...  |
+LL | |         CONST_U16_0,
+LL | |     );
+   | |_____^
+   |
+help: use
+   |
+LL -     let _ = Ipv6Addr::new(
+LL -
+LL -         CONST_U16_0,
+LL -         CONST_U16_0,
+LL -         CONST_U16_0,
+LL -         CONST_U16_0,
+LL -         CONST_U16_0,
+LL -         CONST_U16_0,
+LL -         CONST_U16_0,
+LL -         CONST_U16_0,
+LL -     );
+LL +     let _ = Ipv6Addr::UNSPECIFIED;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:96:13
+   |
+LL |     let _ = Ipv4Addr::new(126 + 1, 0, 0, 1);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = Ipv4Addr::new(126 + 1, 0, 0, 1);
+LL +     let _ = Ipv4Addr::LOCALHOST;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:98:13
+   |
+LL |     let _ = Ipv4Addr::new(254 + CONST_U8_1, 255, { 255 - CONST_U8_0 }, CONST_U8_255);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = Ipv4Addr::new(254 + CONST_U8_1, 255, { 255 - CONST_U8_0 }, CONST_U8_255);
+LL +     let _ = Ipv4Addr::BROADCAST;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:100:13
+   |
+LL |     let _ = Ipv4Addr::new(0, CONST_U8_255 - 255, 0, { 1 + 0 - 1 });
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = Ipv4Addr::new(0, CONST_U8_255 - 255, 0, { 1 + 0 - 1 });
+LL +     let _ = Ipv4Addr::UNSPECIFIED;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:104:13
+   |
+LL |     let _ = Ipv6Addr::new(0 + CONST_U16_0, 0, 0, 0, 0, 0, 0, 1);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = Ipv6Addr::new(0 + CONST_U16_0, 0, 0, 0, 0, 0, 0, 1);
+LL +     let _ = Ipv6Addr::LOCALHOST;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:106:13
+   |
+LL |     let _ = Ipv6Addr::new(0 + 0, 0, 0, 0, 0, { 2 - 1 - CONST_U16_1 }, 0, 1);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = Ipv6Addr::new(0 + 0, 0, 0, 0, 0, { 2 - 1 - CONST_U16_1 }, 0, 1);
+LL +     let _ = Ipv6Addr::LOCALHOST;
+   |
+
+error: aborting due to 25 previous errors
+
diff --git a/src/tools/clippy/tests/ui/ip_constant_from_external.rs b/src/tools/clippy/tests/ui/ip_constant_from_external.rs
new file mode 100644
index 00000000000..7fd27022f12
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ip_constant_from_external.rs
@@ -0,0 +1,12 @@
+//@error-in-other-file: hand-coded well-known IP address
+//@no-rustfix
+#![warn(clippy::ip_constant)]
+
+fn external_constant_test() {
+    let _ = include!("localhost.txt");
+    // lint in external file `localhost.txt`
+}
+
+fn main() {
+    external_constant_test();
+}
diff --git a/src/tools/clippy/tests/ui/ip_constant_from_external.stderr b/src/tools/clippy/tests/ui/ip_constant_from_external.stderr
new file mode 100644
index 00000000000..99dd827d41f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ip_constant_from_external.stderr
@@ -0,0 +1,16 @@
+error: hand-coded well-known IP address
+  --> tests/ui/localhost.txt:1:1
+   |
+LL | std::net::Ipv4Addr::new(127, 0, 0, 1)
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::ip-constant` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::ip_constant)]`
+help: use
+   |
+LL - std::net::Ipv4Addr::new(127, 0, 0, 1)
+LL + std::net::Ipv4Addr::LOCALHOST
+   |
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/clippy/tests/ui/large_stack_frames.rs b/src/tools/clippy/tests/ui/large_stack_frames.rs
index 3ed124f69ef..132f1450b6d 100644
--- a/src/tools/clippy/tests/ui/large_stack_frames.rs
+++ b/src/tools/clippy/tests/ui/large_stack_frames.rs
@@ -1,8 +1,7 @@
 //@ normalize-stderr-test: "\b10000(08|16|32)\b" -> "100$$PTR"
 //@ normalize-stderr-test: "\b2500(060|120)\b" -> "250$$PTR"
-#![allow(unused, incomplete_features)]
+#![allow(unused)]
 #![warn(clippy::large_stack_frames)]
-#![feature(unsized_locals)]
 
 use std::hint::black_box;
 
@@ -11,11 +10,6 @@ fn generic<T: Default>() {
     black_box(&x);
 }
 
-fn unsized_local() {
-    let x: dyn std::fmt::Display = *(Box::new(1) as Box<dyn std::fmt::Display>);
-    black_box(&x);
-}
-
 struct ArrayDefault<const N: usize>([u8; N]);
 
 impl<const N: usize> Default for ArrayDefault<N> {
diff --git a/src/tools/clippy/tests/ui/large_stack_frames.stderr b/src/tools/clippy/tests/ui/large_stack_frames.stderr
index 0ff49e9f5b3..79482e65c3e 100644
--- a/src/tools/clippy/tests/ui/large_stack_frames.stderr
+++ b/src/tools/clippy/tests/ui/large_stack_frames.stderr
@@ -1,5 +1,5 @@
 error: this function may allocate 250$PTR bytes on the stack
-  --> tests/ui/large_stack_frames.rs:27:4
+  --> tests/ui/large_stack_frames.rs:21:4
    |
 LL | fn many_small_arrays() {
    |    ^^^^^^^^^^^^^^^^^
@@ -13,7 +13,7 @@ LL |     let x5 = [0u8; 500_000];
    = help: to override `-D warnings` add `#[allow(clippy::large_stack_frames)]`
 
 error: this function may allocate 1000000 bytes on the stack
-  --> tests/ui/large_stack_frames.rs:38:4
+  --> tests/ui/large_stack_frames.rs:32:4
    |
 LL | fn large_return_value() -> ArrayDefault<1_000_000> {
    |    ^^^^^^^^^^^^^^^^^^      ----------------------- this is the largest part, at 1000000 bytes for type `ArrayDefault<1000000>`
@@ -21,7 +21,7 @@ LL | fn large_return_value() -> ArrayDefault<1_000_000> {
    = note: 1000000 bytes is larger than Clippy's configured `stack-size-threshold` of 512000
 
 error: this function may allocate 100$PTR bytes on the stack
-  --> tests/ui/large_stack_frames.rs:44:4
+  --> tests/ui/large_stack_frames.rs:38:4
    |
 LL | fn large_fn_arg(x: ArrayDefault<1_000_000>) {
    |    ^^^^^^^^^^^^ - `x` is the largest part, at 1000000 bytes for type `ArrayDefault<1000000>`
@@ -29,7 +29,7 @@ LL | fn large_fn_arg(x: ArrayDefault<1_000_000>) {
    = note: 100$PTR bytes is larger than Clippy's configured `stack-size-threshold` of 512000
 
 error: this function may allocate 100$PTR bytes on the stack
-  --> tests/ui/large_stack_frames.rs:51:13
+  --> tests/ui/large_stack_frames.rs:45:13
    |
 LL |     let f = || black_box(&[0u8; 1_000_000]);
    |             ^^^^^^^^^^^^^^----------------^
diff --git a/src/tools/clippy/tests/ui/localhost.txt b/src/tools/clippy/tests/ui/localhost.txt
new file mode 100644
index 00000000000..4502ec2b234
--- /dev/null
+++ b/src/tools/clippy/tests/ui/localhost.txt
@@ -0,0 +1 @@
+std::net::Ipv4Addr::new(127, 0, 0, 1)
\ No newline at end of file
diff --git a/src/tools/clippy/tests/ui/manual_flatten.fixed b/src/tools/clippy/tests/ui/manual_flatten.fixed
new file mode 100644
index 00000000000..cc1fbd25765
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_flatten.fixed
@@ -0,0 +1,148 @@
+#![warn(clippy::manual_flatten)]
+#![allow(clippy::useless_vec, clippy::uninlined_format_args)]
+
+fn main() {
+    // Test for loop over implicitly adjusted `Iterator` with `if let` expression
+    let x = vec![Some(1), Some(2), Some(3)];
+    for y in x.into_iter().flatten() {
+        println!("{}", y);
+    }
+
+    // Test for loop over implicitly adjusted `Iterator` with `if let` statement
+    let y: Vec<Result<i32, i32>> = vec![];
+    for n in y.clone().into_iter().flatten() {
+        println!("{}", n);
+    }
+
+    // Test for loop over by reference
+    for n in y.iter().flatten() {
+        println!("{}", n);
+    }
+
+    // Test for loop over an implicit reference
+    let z = &y;
+    for n in z.iter().flatten() {
+        println!("{}", n);
+    }
+
+    // Test for loop over `Iterator` with `if let` expression
+    let z = vec![Some(1), Some(2), Some(3)];
+    let z = z.iter();
+    for m in z.flatten() {
+        println!("{}", m);
+    }
+
+    // Using the `None` variant should not trigger the lint
+    // Note: for an autofixable suggestion, the binding in the for loop has to take the
+    // name of the binding in the `if let`
+    let z = vec![Some(1), Some(2), Some(3)];
+    for n in z {
+        if n.is_none() {
+            println!("Nada.");
+        }
+    }
+
+    // Using the `Err` variant should not trigger the lint
+    for n in y.clone() {
+        if let Err(e) = n {
+            println!("Oops: {}!", e);
+        }
+    }
+
+    // Having an else clause should not trigger the lint
+    for n in y.clone() {
+        if let Ok(n) = n {
+            println!("{}", n);
+        } else {
+            println!("Oops!");
+        }
+    }
+
+    let vec_of_ref = vec![&Some(1)];
+    for n in vec_of_ref.iter().copied().flatten() {
+        println!("{:?}", n);
+    }
+
+    let vec_of_ref = &vec_of_ref;
+    for n in vec_of_ref.iter().copied().flatten() {
+        println!("{:?}", n);
+    }
+
+    let slice_of_ref = &[&Some(1)];
+    for n in slice_of_ref.iter().copied().flatten() {
+        println!("{:?}", n);
+    }
+
+    struct Test {
+        a: usize,
+    }
+
+    let mut vec_of_struct = [Some(Test { a: 1 }), None];
+
+    // Usage of `if let` expression should not trigger lint
+    for n in vec_of_struct.iter_mut() {
+        if let Some(z) = n {
+            *n = None;
+        }
+    }
+
+    // Using manual flatten should not trigger the lint
+    for n in vec![Some(1), Some(2), Some(3)].iter().flatten() {
+        println!("{}", n);
+    }
+
+    // Using nested `Some` pattern should not trigger the lint
+    for n in vec![Some((1, Some(2)))] {
+        if let Some((_, Some(n))) = n {
+            println!("{}", n);
+        }
+    }
+
+    macro_rules! inner {
+        ($id:ident / $new:pat => $action:expr) => {
+            if let Some($new) = $id {
+                $action;
+            }
+        };
+    }
+
+    // Usage of `if let` expression with macro should not trigger lint
+    for ab in [Some((1, 2)), Some((3, 4))] {
+        inner!(ab / (c, d) => println!("{c}-{d}"));
+    }
+
+    macro_rules! args {
+        ($($arg:expr),*) => {
+            vec![$(Some($arg)),*]
+        };
+    }
+
+    // Usage of `if let` expression with macro should not trigger lint
+    for n in args!(1, 2, 3) {
+        if let Some(n) = n {
+            println!("{:?}", n);
+        }
+    }
+
+    // This should trigger the lint, but the applicability is `MaybeIncorrect`
+    let z = vec![Some(1), Some(2), Some(3)];
+    for n in z.into_iter().flatten() {
+        println!("{:?}", n);
+    }
+
+    run_unformatted_tests();
+}
+
+#[rustfmt::skip]
+fn run_unformatted_tests() {
+    // Skip rustfmt here on purpose so the suggestion does not fit in one line
+    for n in vec![
+    //~^ manual_flatten
+
+        Some(1),
+        Some(2),
+        Some(3)
+    ].iter().flatten() {
+        println!("{:?}", n);
+    }
+}
diff --git a/src/tools/clippy/tests/ui/manual_flatten.rs b/src/tools/clippy/tests/ui/manual_flatten.rs
index f1a0053ef38..53b4ac7d3b6 100644
--- a/src/tools/clippy/tests/ui/manual_flatten.rs
+++ b/src/tools/clippy/tests/ui/manual_flatten.rs
@@ -1,6 +1,6 @@
 #![warn(clippy::manual_flatten)]
 #![allow(clippy::useless_vec, clippy::uninlined_format_args)]
-//@no-rustfix
+
 fn main() {
     // Test for loop over implicitly adjusted `Iterator` with `if let` expression
     let x = vec![Some(1), Some(2), Some(3)];
@@ -130,6 +130,43 @@ fn main() {
         }
     }
 
+    macro_rules! inner {
+        ($id:ident / $new:pat => $action:expr) => {
+            if let Some($new) = $id {
+                $action;
+            }
+        };
+    }
+
+    // Usage of `if let` expression with macro should not trigger lint
+    for ab in [Some((1, 2)), Some((3, 4))] {
+        inner!(ab / (c, d) => println!("{c}-{d}"));
+    }
+
+    macro_rules! args {
+        ($($arg:expr),*) => {
+            vec![$(Some($arg)),*]
+        };
+    }
+
+    // Usage of `if let` expression with macro should not trigger lint
+    for n in args!(1, 2, 3) {
+        if let Some(n) = n {
+            println!("{:?}", n);
+        }
+    }
+
+    // This should trigger the lint, but the applicability is `MaybeIncorrect`
+    let z = vec![Some(1), Some(2), Some(3)];
+    for n in z {
+        //~^ manual_flatten
+
+        if let Some(n) = n {
+            println!("{:?}", n);
+        }
+        // foo
+    }
+
     run_unformatted_tests();
 }
 
diff --git a/src/tools/clippy/tests/ui/manual_flatten.stderr b/src/tools/clippy/tests/ui/manual_flatten.stderr
index 9a846fe17f3..eb39ee42071 100644
--- a/src/tools/clippy/tests/ui/manual_flatten.stderr
+++ b/src/tools/clippy/tests/ui/manual_flatten.stderr
@@ -1,10 +1,7 @@
 error: unnecessary `if let` since only the `Some` variant of the iterator element is used
   --> tests/ui/manual_flatten.rs:7:5
    |
-LL |       for n in x {
-   |       ^        - help: try: `x.into_iter().flatten()`
-   |  _____|
-   | |
+LL | /     for n in x {
 LL | |
 LL | |
 LL | |         if let Some(y) = n {
@@ -12,7 +9,7 @@ LL | |         if let Some(y) = n {
 LL | |     }
    | |_____^
    |
-help: ...and remove the `if let` statement in the for loop
+help: try `.flatten()` and remove the `if let` statement in the for loop
   --> tests/ui/manual_flatten.rs:10:9
    |
 LL | /         if let Some(y) = n {
@@ -21,14 +18,17 @@ LL | |         }
    | |_________^
    = note: `-D clippy::manual-flatten` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::manual_flatten)]`
+help: try
+   |
+LL ~     for y in x.into_iter().flatten() {
+LL +         println!("{}", y);
+LL +     }
+   |
 
 error: unnecessary `if let` since only the `Ok` variant of the iterator element is used
   --> tests/ui/manual_flatten.rs:17:5
    |
-LL |       for n in y.clone() {
-   |       ^        --------- help: try: `y.clone().into_iter().flatten()`
-   |  _____|
-   | |
+LL | /     for n in y.clone() {
 LL | |
 LL | |
 LL | |         if let Ok(n) = n {
@@ -37,21 +37,24 @@ LL | |         };
 LL | |     }
    | |_____^
    |
-help: ...and remove the `if let` statement in the for loop
+help: try `.flatten()` and remove the `if let` statement in the for loop
   --> tests/ui/manual_flatten.rs:20:9
    |
 LL | /         if let Ok(n) = n {
 LL | |             println!("{}", n);
 LL | |         };
    | |_________^
+help: try
+   |
+LL ~     for n in y.clone().into_iter().flatten() {
+LL +         println!("{}", n);
+LL +     }
+   |
 
 error: unnecessary `if let` since only the `Ok` variant of the iterator element is used
   --> tests/ui/manual_flatten.rs:26:5
    |
-LL |       for n in &y {
-   |       ^        -- help: try: `y.iter().flatten()`
-   |  _____|
-   | |
+LL | /     for n in &y {
 LL | |
 LL | |
 LL | |         if let Ok(n) = n {
@@ -59,21 +62,24 @@ LL | |         if let Ok(n) = n {
 LL | |     }
    | |_____^
    |
-help: ...and remove the `if let` statement in the for loop
+help: try `.flatten()` and remove the `if let` statement in the for loop
   --> tests/ui/manual_flatten.rs:29:9
    |
 LL | /         if let Ok(n) = n {
 LL | |             println!("{}", n);
 LL | |         }
    | |_________^
+help: try
+   |
+LL ~     for n in y.iter().flatten() {
+LL +         println!("{}", n);
+LL +     }
+   |
 
 error: unnecessary `if let` since only the `Ok` variant of the iterator element is used
   --> tests/ui/manual_flatten.rs:36:5
    |
-LL |       for n in z {
-   |       ^        - help: try: `z.iter().flatten()`
-   |  _____|
-   | |
+LL | /     for n in z {
 LL | |
 LL | |
 LL | |         if let Ok(n) = n {
@@ -81,21 +87,24 @@ LL | |         if let Ok(n) = n {
 LL | |     }
    | |_____^
    |
-help: ...and remove the `if let` statement in the for loop
+help: try `.flatten()` and remove the `if let` statement in the for loop
   --> tests/ui/manual_flatten.rs:39:9
    |
 LL | /         if let Ok(n) = n {
 LL | |             println!("{}", n);
 LL | |         }
    | |_________^
+help: try
+   |
+LL ~     for n in z.iter().flatten() {
+LL +         println!("{}", n);
+LL +     }
+   |
 
 error: unnecessary `if let` since only the `Some` variant of the iterator element is used
   --> tests/ui/manual_flatten.rs:47:5
    |
-LL |       for n in z {
-   |       ^        - help: try: `z.flatten()`
-   |  _____|
-   | |
+LL | /     for n in z {
 LL | |
 LL | |
 LL | |         if let Some(m) = n {
@@ -103,21 +112,24 @@ LL | |         if let Some(m) = n {
 LL | |     }
    | |_____^
    |
-help: ...and remove the `if let` statement in the for loop
+help: try `.flatten()` and remove the `if let` statement in the for loop
   --> tests/ui/manual_flatten.rs:50:9
    |
 LL | /         if let Some(m) = n {
 LL | |             println!("{}", m);
 LL | |         }
    | |_________^
+help: try
+   |
+LL ~     for m in z.flatten() {
+LL +         println!("{}", m);
+LL +     }
+   |
 
 error: unnecessary `if let` since only the `Some` variant of the iterator element is used
   --> tests/ui/manual_flatten.rs:82:5
    |
-LL |       for n in &vec_of_ref {
-   |       ^        ----------- help: try: `vec_of_ref.iter().copied().flatten()`
-   |  _____|
-   | |
+LL | /     for n in &vec_of_ref {
 LL | |
 LL | |
 LL | |         if let Some(n) = n {
@@ -125,21 +137,24 @@ LL | |         if let Some(n) = n {
 LL | |     }
    | |_____^
    |
-help: ...and remove the `if let` statement in the for loop
+help: try `.flatten()` and remove the `if let` statement in the for loop
   --> tests/ui/manual_flatten.rs:85:9
    |
 LL | /         if let Some(n) = n {
 LL | |             println!("{:?}", n);
 LL | |         }
    | |_________^
+help: try
+   |
+LL ~     for n in vec_of_ref.iter().copied().flatten() {
+LL +         println!("{:?}", n);
+LL +     }
+   |
 
 error: unnecessary `if let` since only the `Some` variant of the iterator element is used
   --> tests/ui/manual_flatten.rs:91:5
    |
-LL |       for n in vec_of_ref {
-   |       ^        ---------- help: try: `vec_of_ref.iter().copied().flatten()`
-   |  _____|
-   | |
+LL | /     for n in vec_of_ref {
 LL | |
 LL | |
 LL | |         if let Some(n) = n {
@@ -147,21 +162,24 @@ LL | |         if let Some(n) = n {
 LL | |     }
    | |_____^
    |
-help: ...and remove the `if let` statement in the for loop
+help: try `.flatten()` and remove the `if let` statement in the for loop
   --> tests/ui/manual_flatten.rs:94:9
    |
 LL | /         if let Some(n) = n {
 LL | |             println!("{:?}", n);
 LL | |         }
    | |_________^
+help: try
+   |
+LL ~     for n in vec_of_ref.iter().copied().flatten() {
+LL +         println!("{:?}", n);
+LL +     }
+   |
 
 error: unnecessary `if let` since only the `Some` variant of the iterator element is used
   --> tests/ui/manual_flatten.rs:100:5
    |
-LL |       for n in slice_of_ref {
-   |       ^        ------------ help: try: `slice_of_ref.iter().copied().flatten()`
-   |  _____|
-   | |
+LL | /     for n in slice_of_ref {
 LL | |
 LL | |
 LL | |         if let Some(n) = n {
@@ -169,16 +187,47 @@ LL | |         if let Some(n) = n {
 LL | |     }
    | |_____^
    |
-help: ...and remove the `if let` statement in the for loop
+help: try `.flatten()` and remove the `if let` statement in the for loop
   --> tests/ui/manual_flatten.rs:103:9
    |
 LL | /         if let Some(n) = n {
 LL | |             println!("{:?}", n);
 LL | |         }
    | |_________^
+help: try
+   |
+LL ~     for n in slice_of_ref.iter().copied().flatten() {
+LL +         println!("{:?}", n);
+LL +     }
+   |
+
+error: unnecessary `if let` since only the `Some` variant of the iterator element is used
+  --> tests/ui/manual_flatten.rs:161:5
+   |
+LL | /     for n in z {
+LL | |
+LL | |
+LL | |         if let Some(n) = n {
+...  |
+LL | |     }
+   | |_____^
+   |
+help: try `.flatten()` and remove the `if let` statement in the for loop
+  --> tests/ui/manual_flatten.rs:164:9
+   |
+LL | /         if let Some(n) = n {
+LL | |             println!("{:?}", n);
+LL | |         }
+   | |_________^
+help: try
+   |
+LL ~     for n in z.into_iter().flatten() {
+LL +         println!("{:?}", n);
+LL +     }
+   |
 
 error: unnecessary `if let` since only the `Some` variant of the iterator element is used
-  --> tests/ui/manual_flatten.rs:139:5
+  --> tests/ui/manual_flatten.rs:176:5
    |
 LL | /     for n in vec![
 LL | |
@@ -188,8 +237,8 @@ LL | |         Some(1),
 LL | |     }
    | |_____^
    |
-help: remove the `if let` statement in the for loop and then...
-  --> tests/ui/manual_flatten.rs:146:9
+help: try `.flatten()` and remove the `if let` statement in the for loop
+  --> tests/ui/manual_flatten.rs:183:9
    |
 LL | /         if let Some(n) = n {
 LL | |             println!("{:?}", n);
@@ -201,7 +250,9 @@ LL |     for n in vec![
 ...
 LL |         Some(3)
 LL ~     ].iter().flatten() {
+LL +         println!("{:?}", n);
+LL +     }
    |
 
-error: aborting due to 9 previous errors
+error: aborting due to 10 previous errors
 
diff --git a/src/tools/clippy/tests/ui/manual_swap_auto_fix.fixed b/src/tools/clippy/tests/ui/manual_swap_auto_fix.fixed
index 28466ff3f9b..6cd81bafce8 100644
--- a/src/tools/clippy/tests/ui/manual_swap_auto_fix.fixed
+++ b/src/tools/clippy/tests/ui/manual_swap_auto_fix.fixed
@@ -55,3 +55,14 @@ fn swap8() {
     let i2 = 1;
     v.swap(i1 + i2, i2);
 }
+
+fn issue_14931() {
+    let mut v = [1, 2, 3, 4];
+
+    let mut i1 = 0;
+    for i2 in 0..4 {
+        v.swap(i1, i2);
+
+        i1 += 2;
+    }
+}
diff --git a/src/tools/clippy/tests/ui/manual_swap_auto_fix.rs b/src/tools/clippy/tests/ui/manual_swap_auto_fix.rs
index c9880e651cd..19dabfd833f 100644
--- a/src/tools/clippy/tests/ui/manual_swap_auto_fix.rs
+++ b/src/tools/clippy/tests/ui/manual_swap_auto_fix.rs
@@ -78,3 +78,17 @@ fn swap8() {
     v[i1 + i2] = v[i2];
     v[i2] = tmp;
 }
+
+fn issue_14931() {
+    let mut v = [1, 2, 3, 4];
+
+    let mut i1 = 0;
+    for i2 in 0..4 {
+        let tmp = v[i1];
+        //~^ manual_swap
+        v[i1] = v[i2];
+        v[i2] = tmp;
+
+        i1 += 2;
+    }
+}
diff --git a/src/tools/clippy/tests/ui/manual_swap_auto_fix.stderr b/src/tools/clippy/tests/ui/manual_swap_auto_fix.stderr
index 7ab898fcc72..a0bb32233e2 100644
--- a/src/tools/clippy/tests/ui/manual_swap_auto_fix.stderr
+++ b/src/tools/clippy/tests/ui/manual_swap_auto_fix.stderr
@@ -92,5 +92,14 @@ LL | |     v[i1 + i2] = v[i2];
 LL | |     v[i2] = tmp;
    | |________________^ help: try: `v.swap(i1 + i2, i2);`
 
-error: aborting due to 8 previous errors
+error: this looks like you are swapping elements of `v` manually
+  --> tests/ui/manual_swap_auto_fix.rs:87:9
+   |
+LL | /         let tmp = v[i1];
+LL | |
+LL | |         v[i1] = v[i2];
+LL | |         v[i2] = tmp;
+   | |____________________^ help: try: `v.swap(i1, i2);`
+
+error: aborting due to 9 previous errors
 
diff --git a/src/tools/clippy/tests/ui/match_single_binding.fixed b/src/tools/clippy/tests/ui/match_single_binding.fixed
index bdf39796ebf..e11dea35204 100644
--- a/src/tools/clippy/tests/ui/match_single_binding.fixed
+++ b/src/tools/clippy/tests/ui/match_single_binding.fixed
@@ -188,3 +188,19 @@ fn issue14634() {
     let id!(_a) = dbg!(b + 1);
     //~^^^ match_single_binding
 }
+
+mod issue14991 {
+    struct AnnoConstWOBlock {
+        inner: [(); {
+            let _n = 1;
+            42
+        }],
+    }
+
+    struct AnnoConstWBlock {
+        inner: [(); {
+            let _n = 1;
+            42
+        }],
+    }
+}
diff --git a/src/tools/clippy/tests/ui/match_single_binding.rs b/src/tools/clippy/tests/ui/match_single_binding.rs
index 419ff95d873..d498da30fc8 100644
--- a/src/tools/clippy/tests/ui/match_single_binding.rs
+++ b/src/tools/clippy/tests/ui/match_single_binding.rs
@@ -249,3 +249,21 @@ fn issue14634() {
     };
     //~^^^ match_single_binding
 }
+
+mod issue14991 {
+    struct AnnoConstWOBlock {
+        inner: [(); match 1 {
+            //~^ match_single_binding
+            _n => 42,
+        }],
+    }
+
+    struct AnnoConstWBlock {
+        inner: [(); {
+            match 1 {
+                //~^ match_single_binding
+                _n => 42,
+            }
+        }],
+    }
+}
diff --git a/src/tools/clippy/tests/ui/match_single_binding.stderr b/src/tools/clippy/tests/ui/match_single_binding.stderr
index bdd0134a5f1..f274f80c81d 100644
--- a/src/tools/clippy/tests/ui/match_single_binding.stderr
+++ b/src/tools/clippy/tests/ui/match_single_binding.stderr
@@ -378,5 +378,38 @@ LL ~     let id!(b) = dbg!(3);
 LL +     let id!(_a) = dbg!(b + 1);
    |
 
-error: aborting due to 27 previous errors
+error: this match could be written as a `let` statement
+  --> tests/ui/match_single_binding.rs:255:21
+   |
+LL |           inner: [(); match 1 {
+   |  _____________________^
+LL | |
+LL | |             _n => 42,
+LL | |         }],
+   | |_________^
+   |
+help: consider using a `let` statement
+   |
+LL ~         inner: [(); {
+LL +             let _n = 1;
+LL +             42
+LL ~         }],
+   |
+
+error: this match could be written as a `let` statement
+  --> tests/ui/match_single_binding.rs:263:13
+   |
+LL | /             match 1 {
+LL | |
+LL | |                 _n => 42,
+LL | |             }
+   | |_____________^
+   |
+help: consider using a `let` statement
+   |
+LL ~             let _n = 1;
+LL +             42
+   |
+
+error: aborting due to 29 previous errors
 
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.fixed b/src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.fixed
new file mode 100644
index 00000000000..7e0d4fccaae
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.fixed
@@ -0,0 +1,36 @@
+#![feature(const_trait_impl)]
+#![warn(clippy::missing_const_for_fn)]
+
+// Reduced test case from https://github.com/rust-lang/rust-clippy/issues/14658
+
+#[const_trait]
+trait ConstTrait {
+    fn method(self);
+}
+
+impl ConstTrait for u32 {
+    fn method(self) {}
+}
+
+impl const ConstTrait for u64 {
+    fn method(self) {}
+}
+
+fn cannot_be_const() {
+    0u32.method();
+}
+
+//~v missing_const_for_fn
+const fn can_be_const() {
+    0u64.method();
+}
+
+// False negative, see FIXME comment in `clipy_utils::qualify_min_const`
+fn could_be_const_but_does_not_trigger<T>(t: T)
+where
+    T: const ConstTrait,
+{
+    t.method();
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.rs
new file mode 100644
index 00000000000..439da4622d7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.rs
@@ -0,0 +1,36 @@
+#![feature(const_trait_impl)]
+#![warn(clippy::missing_const_for_fn)]
+
+// Reduced test case from https://github.com/rust-lang/rust-clippy/issues/14658
+
+#[const_trait]
+trait ConstTrait {
+    fn method(self);
+}
+
+impl ConstTrait for u32 {
+    fn method(self) {}
+}
+
+impl const ConstTrait for u64 {
+    fn method(self) {}
+}
+
+fn cannot_be_const() {
+    0u32.method();
+}
+
+//~v missing_const_for_fn
+fn can_be_const() {
+    0u64.method();
+}
+
+// False negative, see FIXME comment in `clipy_utils::qualify_min_const`
+fn could_be_const_but_does_not_trigger<T>(t: T)
+where
+    T: const ConstTrait,
+{
+    t.method();
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.stderr b/src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.stderr
new file mode 100644
index 00000000000..b994b88fac6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.stderr
@@ -0,0 +1,17 @@
+error: this could be a `const fn`
+  --> tests/ui/missing_const_for_fn/const_trait.rs:24:1
+   |
+LL | / fn can_be_const() {
+LL | |     0u64.method();
+LL | | }
+   | |_^
+   |
+   = note: `-D clippy::missing-const-for-fn` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::missing_const_for_fn)]`
+help: make the function `const`
+   |
+LL | const fn can_be_const() {
+   | +++++
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.fixed b/src/tools/clippy/tests/ui/needless_lifetimes.fixed
index ceea4480d0d..15ca409c95b 100644
--- a/src/tools/clippy/tests/ui/needless_lifetimes.fixed
+++ b/src/tools/clippy/tests/ui/needless_lifetimes.fixed
@@ -10,7 +10,7 @@
     clippy::unnecessary_wraps,
     dyn_drop,
     clippy::get_first,
-    mismatched_lifetime_syntaxes,
+    mismatched_lifetime_syntaxes
 )]
 
 extern crate proc_macros;
diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.rs b/src/tools/clippy/tests/ui/needless_lifetimes.rs
index 8432f9e6d2f..af9649d7298 100644
--- a/src/tools/clippy/tests/ui/needless_lifetimes.rs
+++ b/src/tools/clippy/tests/ui/needless_lifetimes.rs
@@ -10,7 +10,7 @@
     clippy::unnecessary_wraps,
     dyn_drop,
     clippy::get_first,
-    mismatched_lifetime_syntaxes,
+    mismatched_lifetime_syntaxes
 )]
 
 extern crate proc_macros;
diff --git a/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.fixed b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.fixed
index 23dbee5a084..6915e1984fb 100644
--- a/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.fixed
+++ b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.fixed
@@ -195,3 +195,19 @@ impl PartialOrd for K {
     //~^ non_canonical_partial_ord_impl
     fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
 }
+
+// #14574, check that partial_cmp invokes other.cmp
+
+#[derive(Eq, PartialEq)]
+struct L(u32);
+
+impl Ord for L {
+    fn cmp(&self, other: &Self) -> Ordering {
+        todo!();
+    }
+}
+
+impl PartialOrd for L {
+    //~^ non_canonical_partial_ord_impl
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
+}
diff --git a/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.rs b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.rs
index 12f055a542b..7ce4cdc9aec 100644
--- a/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.rs
+++ b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.rs
@@ -201,3 +201,21 @@ impl PartialOrd for K {
         Ordering::Greater.into()
     }
 }
+
+// #14574, check that partial_cmp invokes other.cmp
+
+#[derive(Eq, PartialEq)]
+struct L(u32);
+
+impl Ord for L {
+    fn cmp(&self, other: &Self) -> Ordering {
+        todo!();
+    }
+}
+
+impl PartialOrd for L {
+    //~^ non_canonical_partial_ord_impl
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        Some(other.cmp(self))
+    }
+}
diff --git a/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.stderr b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.stderr
index c7de968588f..9bd6b1f726d 100644
--- a/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.stderr
+++ b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.stderr
@@ -44,5 +44,18 @@ LL | ||     }
 LL | |  }
    | |__^
 
-error: aborting due to 3 previous errors
+error: non-canonical implementation of `partial_cmp` on an `Ord` type
+  --> tests/ui/non_canonical_partial_ord_impl.rs:216:1
+   |
+LL | /  impl PartialOrd for L {
+LL | |
+LL | |      fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+   | | _____________________________________________________________-
+LL | ||         Some(other.cmp(self))
+LL | ||     }
+   | ||_____- help: change this to: `{ Some(self.cmp(other)) }`
+LL | |  }
+   | |__^
+
+error: aborting due to 4 previous errors
 
diff --git a/src/tools/clippy/tests/ui/pointer_format.rs b/src/tools/clippy/tests/ui/pointer_format.rs
new file mode 100644
index 00000000000..0621f966ad1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/pointer_format.rs
@@ -0,0 +1,66 @@
+#![warn(clippy::pointer_format)]
+
+use core::fmt::Debug;
+use core::marker::PhantomData;
+
+#[derive(Debug)]
+struct ContainsPointerDeep {
+    w: WithPointer,
+}
+
+struct ManualDebug {
+    ptr: *const u8,
+}
+
+#[derive(Debug)]
+struct WithPointer {
+    len: usize,
+    ptr: *const u8,
+}
+
+impl std::fmt::Debug for ManualDebug {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+        f.write_str("ManualDebug")
+    }
+}
+
+trait Foo {
+    type Assoc: Foo + Debug;
+}
+
+#[derive(Debug)]
+struct S<T: Foo + 'static>(&'static S<T::Assoc>, PhantomData<T>);
+
+#[allow(unused)]
+fn unbounded<T: Foo + Debug + 'static>(s: &S<T>) {
+    format!("{s:?}");
+}
+
+fn main() {
+    let m = &(main as fn());
+    let g = &0;
+    let o = &format!("{m:p}");
+    //~^ pointer_format
+    let _ = format!("{m:?}");
+    //~^ pointer_format
+    println!("{g:p}");
+    //~^ pointer_format
+    panic!("{o:p}");
+    //~^ pointer_format
+    let answer = 42;
+    let x = &raw const answer;
+    let arr = [0u8; 8];
+    let with_ptr = WithPointer { len: 8, ptr: &arr as _ };
+    let _ = format!("{x:?}");
+    //~^ pointer_format
+    print!("{with_ptr:?}");
+    //~^ pointer_format
+    let container = ContainsPointerDeep { w: with_ptr };
+    print!("{container:?}");
+    //~^ pointer_format
+
+    let no_pointer = "foo";
+    println!("{no_pointer:?}");
+    let manual_debug = ManualDebug { ptr: &arr as _ };
+    println!("{manual_debug:?}");
+}
diff --git a/src/tools/clippy/tests/ui/pointer_format.stderr b/src/tools/clippy/tests/ui/pointer_format.stderr
new file mode 100644
index 00000000000..21ba39b8f8c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/pointer_format.stderr
@@ -0,0 +1,47 @@
+error: pointer formatting detected
+  --> tests/ui/pointer_format.rs:42:23
+   |
+LL |     let o = &format!("{m:p}");
+   |                       ^^^^^
+   |
+   = note: `-D clippy::pointer-format` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::pointer_format)]`
+
+error: pointer formatting detected
+  --> tests/ui/pointer_format.rs:44:22
+   |
+LL |     let _ = format!("{m:?}");
+   |                      ^^^^^
+
+error: pointer formatting detected
+  --> tests/ui/pointer_format.rs:46:15
+   |
+LL |     println!("{g:p}");
+   |               ^^^^^
+
+error: pointer formatting detected
+  --> tests/ui/pointer_format.rs:48:13
+   |
+LL |     panic!("{o:p}");
+   |             ^^^^^
+
+error: pointer formatting detected
+  --> tests/ui/pointer_format.rs:54:22
+   |
+LL |     let _ = format!("{x:?}");
+   |                      ^^^^^
+
+error: pointer formatting detected
+  --> tests/ui/pointer_format.rs:56:13
+   |
+LL |     print!("{with_ptr:?}");
+   |             ^^^^^^^^^^^^
+
+error: pointer formatting detected
+  --> tests/ui/pointer_format.rs:59:13
+   |
+LL |     print!("{container:?}");
+   |             ^^^^^^^^^^^^^
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/print_literal.fixed b/src/tools/clippy/tests/ui/print_literal.fixed
index 24c45a4a61b..ebfe19c700e 100644
--- a/src/tools/clippy/tests/ui/print_literal.fixed
+++ b/src/tools/clippy/tests/ui/print_literal.fixed
@@ -94,3 +94,14 @@ fn issue_13959() {
 "
     );
 }
+
+fn issue_14930() {
+    println!("Hello x is {0:2$.1$}", 0.01, 2, 3);
+    //~^ print_literal
+    println!("Hello x is {0:2$.1$}", 0.01, 2, 3);
+    //~^ print_literal
+    println!("Hello x is {0:2$.1$}", 0.01, 2, 3);
+    //~^ print_literal
+    println!("Hello x is {0:2$.1$}", 0.01, 2, 3);
+    //~^ print_literal
+}
diff --git a/src/tools/clippy/tests/ui/print_literal.rs b/src/tools/clippy/tests/ui/print_literal.rs
index 42ae589ca63..8f3d9be0698 100644
--- a/src/tools/clippy/tests/ui/print_literal.rs
+++ b/src/tools/clippy/tests/ui/print_literal.rs
@@ -95,3 +95,14 @@ fn issue_13959() {
 "#
     );
 }
+
+fn issue_14930() {
+    println!("Hello {3} is {0:2$.1$}", 0.01, 2, 3, "x");
+    //~^ print_literal
+    println!("Hello {2} is {0:3$.1$}", 0.01, 2, "x", 3);
+    //~^ print_literal
+    println!("Hello {1} is {0:3$.2$}", 0.01, "x", 2, 3);
+    //~^ print_literal
+    println!("Hello {0} is {1:3$.2$}", "x", 0.01, 2, 3);
+    //~^ print_literal
+}
diff --git a/src/tools/clippy/tests/ui/print_literal.stderr b/src/tools/clippy/tests/ui/print_literal.stderr
index da663000686..1c378017d15 100644
--- a/src/tools/clippy/tests/ui/print_literal.stderr
+++ b/src/tools/clippy/tests/ui/print_literal.stderr
@@ -229,5 +229,53 @@ LL +         bar
 LL ~ "
    |
 
-error: aborting due to 18 previous errors
+error: literal with an empty format string
+  --> tests/ui/print_literal.rs:100:52
+   |
+LL |     println!("Hello {3} is {0:2$.1$}", 0.01, 2, 3, "x");
+   |                                                    ^^^
+   |
+help: try
+   |
+LL -     println!("Hello {3} is {0:2$.1$}", 0.01, 2, 3, "x");
+LL +     println!("Hello x is {0:2$.1$}", 0.01, 2, 3);
+   |
+
+error: literal with an empty format string
+  --> tests/ui/print_literal.rs:102:49
+   |
+LL |     println!("Hello {2} is {0:3$.1$}", 0.01, 2, "x", 3);
+   |                                                 ^^^
+   |
+help: try
+   |
+LL -     println!("Hello {2} is {0:3$.1$}", 0.01, 2, "x", 3);
+LL +     println!("Hello x is {0:2$.1$}", 0.01, 2, 3);
+   |
+
+error: literal with an empty format string
+  --> tests/ui/print_literal.rs:104:46
+   |
+LL |     println!("Hello {1} is {0:3$.2$}", 0.01, "x", 2, 3);
+   |                                              ^^^
+   |
+help: try
+   |
+LL -     println!("Hello {1} is {0:3$.2$}", 0.01, "x", 2, 3);
+LL +     println!("Hello x is {0:2$.1$}", 0.01, 2, 3);
+   |
+
+error: literal with an empty format string
+  --> tests/ui/print_literal.rs:106:40
+   |
+LL |     println!("Hello {0} is {1:3$.2$}", "x", 0.01, 2, 3);
+   |                                        ^^^
+   |
+help: try
+   |
+LL -     println!("Hello {0} is {1:3$.2$}", "x", 0.01, 2, 3);
+LL +     println!("Hello x is {0:2$.1$}", 0.01, 2, 3);
+   |
+
+error: aborting due to 22 previous errors
 
diff --git a/src/tools/clippy/tests/ui/semicolon_outside_block.fixed b/src/tools/clippy/tests/ui/semicolon_outside_block.fixed
index 52fae9a3aff..a3be80b4928 100644
--- a/src/tools/clippy/tests/ui/semicolon_outside_block.fixed
+++ b/src/tools/clippy/tests/ui/semicolon_outside_block.fixed
@@ -96,3 +96,28 @@ fn main() {
 
     unit_fn_block()
 }
+
+fn issue14926() {
+    macro_rules! gen_code {
+        [$l:lifetime: $b:block, $b2: block $(,)?] => {
+            $l: loop {
+                $b
+                break $l;
+            }
+            $l: loop {
+                $b2
+                break $l;
+            }
+        };
+    }
+
+    gen_code! {
+        'root:
+        {
+            println!("Block1");
+        },
+        {
+            println!("Block2");
+        },
+    }
+}
diff --git a/src/tools/clippy/tests/ui/semicolon_outside_block.rs b/src/tools/clippy/tests/ui/semicolon_outside_block.rs
index 5975e66fbb8..3b7bf68029f 100644
--- a/src/tools/clippy/tests/ui/semicolon_outside_block.rs
+++ b/src/tools/clippy/tests/ui/semicolon_outside_block.rs
@@ -96,3 +96,28 @@ fn main() {
 
     unit_fn_block()
 }
+
+fn issue14926() {
+    macro_rules! gen_code {
+        [$l:lifetime: $b:block, $b2: block $(,)?] => {
+            $l: loop {
+                $b
+                break $l;
+            }
+            $l: loop {
+                $b2
+                break $l;
+            }
+        };
+    }
+
+    gen_code! {
+        'root:
+        {
+            println!("Block1");
+        },
+        {
+            println!("Block2");
+        },
+    }
+}
diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.fixed b/src/tools/clippy/tests/ui/std_instead_of_core.fixed
index ab2e801eee2..1820ade422f 100644
--- a/src/tools/clippy/tests/ui/std_instead_of_core.fixed
+++ b/src/tools/clippy/tests/ui/std_instead_of_core.fixed
@@ -90,3 +90,9 @@ fn msrv_1_76(_: std::net::IpAddr) {}
 #[clippy::msrv = "1.77"]
 fn msrv_1_77(_: core::net::IpAddr) {}
 //~^ std_instead_of_core
+
+#[warn(clippy::std_instead_of_core)]
+#[rustfmt::skip]
+fn issue14982() {
+    use std::{collections::HashMap, hash::Hash};
+}
diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.rs b/src/tools/clippy/tests/ui/std_instead_of_core.rs
index f760b3561ae..32c49330981 100644
--- a/src/tools/clippy/tests/ui/std_instead_of_core.rs
+++ b/src/tools/clippy/tests/ui/std_instead_of_core.rs
@@ -90,3 +90,9 @@ fn msrv_1_76(_: std::net::IpAddr) {}
 #[clippy::msrv = "1.77"]
 fn msrv_1_77(_: std::net::IpAddr) {}
 //~^ std_instead_of_core
+
+#[warn(clippy::std_instead_of_core)]
+#[rustfmt::skip]
+fn issue14982() {
+    use std::{collections::HashMap, hash::Hash};
+}
diff --git a/src/tools/clippy/tests/ui/unit_arg.rs b/src/tools/clippy/tests/ui/unit_arg.rs
index 22a6a26dab6..4208efad677 100644
--- a/src/tools/clippy/tests/ui/unit_arg.rs
+++ b/src/tools/clippy/tests/ui/unit_arg.rs
@@ -151,3 +151,27 @@ fn main() {
     bad();
     ok();
 }
+
+fn issue14857() {
+    let fn_take_unit = |_: ()| {};
+    fn some_other_fn(_: &i32) {}
+
+    macro_rules! mac {
+        (def) => {
+            Default::default()
+        };
+        (func $f:expr) => {
+            $f()
+        };
+        (nonempty_block $e:expr) => {{
+            some_other_fn(&$e);
+            $e
+        }};
+    }
+    fn_take_unit(mac!(def));
+    //~^ unit_arg
+    fn_take_unit(mac!(func Default::default));
+    //~^ unit_arg
+    fn_take_unit(mac!(nonempty_block Default::default()));
+    //~^ unit_arg
+}
diff --git a/src/tools/clippy/tests/ui/unit_arg.stderr b/src/tools/clippy/tests/ui/unit_arg.stderr
index 6c333d9792d..0dcfb02b9fa 100644
--- a/src/tools/clippy/tests/ui/unit_arg.stderr
+++ b/src/tools/clippy/tests/ui/unit_arg.stderr
@@ -199,5 +199,26 @@ LL ~     foo(1);
 LL +     Some(())
    |
 
-error: aborting due to 10 previous errors
+error: passing a unit value to a function
+  --> tests/ui/unit_arg.rs:171:5
+   |
+LL |     fn_take_unit(mac!(def));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+
+error: passing a unit value to a function
+  --> tests/ui/unit_arg.rs:173:5
+   |
+LL |     fn_take_unit(mac!(func Default::default));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+
+error: passing a unit value to a function
+  --> tests/ui/unit_arg.rs:175:5
+   |
+LL |     fn_take_unit(mac!(nonempty_block Default::default()));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+
+error: aborting due to 13 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unit_arg_empty_blocks.fixed b/src/tools/clippy/tests/ui/unit_arg_empty_blocks.fixed
deleted file mode 100644
index b045a33608d..00000000000
--- a/src/tools/clippy/tests/ui/unit_arg_empty_blocks.fixed
+++ /dev/null
@@ -1,34 +0,0 @@
-#![warn(clippy::unit_arg)]
-#![allow(unused_must_use, unused_variables)]
-#![allow(clippy::no_effect, clippy::uninlined_format_args)]
-
-use std::fmt::Debug;
-
-fn foo<T: Debug>(t: T) {
-    println!("{:?}", t);
-}
-
-fn foo3<T1: Debug, T2: Debug, T3: Debug>(t1: T1, t2: T2, t3: T3) {
-    println!("{:?}, {:?}, {:?}", t1, t2, t3);
-}
-
-fn bad() {
-    foo(());
-    //~^ unit_arg
-    foo3((), 2, 2);
-    //~^ unit_arg
-    foo(0);
-    taking_two_units((), ());
-    //~^ unit_arg
-    foo(0);
-    foo(1);
-    taking_three_units((), (), ());
-    //~^ unit_arg
-}
-
-fn taking_two_units(a: (), b: ()) {}
-fn taking_three_units(a: (), b: (), c: ()) {}
-
-fn main() {
-    bad();
-}
diff --git a/src/tools/clippy/tests/ui/unit_arg_empty_blocks.rs b/src/tools/clippy/tests/ui/unit_arg_empty_blocks.rs
deleted file mode 100644
index ab305913f3f..00000000000
--- a/src/tools/clippy/tests/ui/unit_arg_empty_blocks.rs
+++ /dev/null
@@ -1,31 +0,0 @@
-#![warn(clippy::unit_arg)]
-#![allow(unused_must_use, unused_variables)]
-#![allow(clippy::no_effect, clippy::uninlined_format_args)]
-
-use std::fmt::Debug;
-
-fn foo<T: Debug>(t: T) {
-    println!("{:?}", t);
-}
-
-fn foo3<T1: Debug, T2: Debug, T3: Debug>(t1: T1, t2: T2, t3: T3) {
-    println!("{:?}, {:?}, {:?}", t1, t2, t3);
-}
-
-fn bad() {
-    foo({});
-    //~^ unit_arg
-    foo3({}, 2, 2);
-    //~^ unit_arg
-    taking_two_units({}, foo(0));
-    //~^ unit_arg
-    taking_three_units({}, foo(0), foo(1));
-    //~^ unit_arg
-}
-
-fn taking_two_units(a: (), b: ()) {}
-fn taking_three_units(a: (), b: (), c: ()) {}
-
-fn main() {
-    bad();
-}
diff --git a/src/tools/clippy/tests/ui/unit_arg_empty_blocks.stderr b/src/tools/clippy/tests/ui/unit_arg_empty_blocks.stderr
deleted file mode 100644
index 2c686d58ecc..00000000000
--- a/src/tools/clippy/tests/ui/unit_arg_empty_blocks.stderr
+++ /dev/null
@@ -1,46 +0,0 @@
-error: passing a unit value to a function
-  --> tests/ui/unit_arg_empty_blocks.rs:16:5
-   |
-LL |     foo({});
-   |     ^^^^--^
-   |         |
-   |         help: use a unit literal instead: `()`
-   |
-   = note: `-D clippy::unit-arg` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::unit_arg)]`
-
-error: passing a unit value to a function
-  --> tests/ui/unit_arg_empty_blocks.rs:18:5
-   |
-LL |     foo3({}, 2, 2);
-   |     ^^^^^--^^^^^^^
-   |          |
-   |          help: use a unit literal instead: `()`
-
-error: passing unit values to a function
-  --> tests/ui/unit_arg_empty_blocks.rs:20:5
-   |
-LL |     taking_two_units({}, foo(0));
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: move the expression in front of the call and replace it with the unit literal `()`
-   |
-LL ~     foo(0);
-LL ~     taking_two_units((), ());
-   |
-
-error: passing unit values to a function
-  --> tests/ui/unit_arg_empty_blocks.rs:22:5
-   |
-LL |     taking_three_units({}, foo(0), foo(1));
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: move the expressions in front of the call and replace them with the unit literal `()`
-   |
-LL ~     foo(0);
-LL +     foo(1);
-LL ~     taking_three_units((), (), ());
-   |
-
-error: aborting due to 4 previous errors
-
diff --git a/src/tools/clippy/tests/ui/unit_arg_fixable.fixed b/src/tools/clippy/tests/ui/unit_arg_fixable.fixed
new file mode 100644
index 00000000000..03353a14081
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unit_arg_fixable.fixed
@@ -0,0 +1,78 @@
+#![warn(clippy::unit_arg)]
+#![allow(unused_must_use, unused_variables)]
+#![allow(clippy::no_effect, clippy::uninlined_format_args)]
+
+use std::fmt::Debug;
+
+fn foo<T: Debug>(t: T) {
+    println!("{:?}", t);
+}
+
+fn foo3<T1: Debug, T2: Debug, T3: Debug>(t1: T1, t2: T2, t3: T3) {
+    println!("{:?}, {:?}, {:?}", t1, t2, t3);
+}
+
+fn bad() {
+    foo(());
+    //~^ unit_arg
+    foo3((), 2, 2);
+    //~^ unit_arg
+    foo(0);
+    taking_two_units((), ());
+    //~^ unit_arg
+    foo(0);
+    foo(1);
+    taking_three_units((), (), ());
+    //~^ unit_arg
+}
+
+fn taking_two_units(a: (), b: ()) {}
+fn taking_three_units(a: (), b: (), c: ()) {}
+
+fn main() {
+    bad();
+}
+
+fn issue14857() {
+    let fn_take_unit = |_: ()| {};
+    fn_take_unit(());
+    //~^ unit_arg
+
+    fn some_other_fn(_: &i32) {}
+
+    macro_rules! another_mac {
+        () => {
+            some_other_fn(&Default::default())
+        };
+        ($e:expr) => {
+            some_other_fn(&$e)
+        };
+    }
+
+    another_mac!();
+    fn_take_unit(());
+    //~^ unit_arg
+    another_mac!(1);
+    fn_take_unit(());
+    //~^ unit_arg
+
+    macro_rules! mac {
+        (nondef $e:expr) => {
+            $e
+        };
+        (empty_block) => {{}};
+    }
+    fn_take_unit(mac!(nondef ()));
+    //~^ unit_arg
+    mac!(empty_block);
+    fn_take_unit(());
+    //~^ unit_arg
+
+    fn def<T: Default>() -> T {
+        Default::default()
+    }
+
+    let _: () = def();
+    fn_take_unit(());
+    //~^ unit_arg
+}
diff --git a/src/tools/clippy/tests/ui/unit_arg_fixable.rs b/src/tools/clippy/tests/ui/unit_arg_fixable.rs
new file mode 100644
index 00000000000..03fd96efdf9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unit_arg_fixable.rs
@@ -0,0 +1,71 @@
+#![warn(clippy::unit_arg)]
+#![allow(unused_must_use, unused_variables)]
+#![allow(clippy::no_effect, clippy::uninlined_format_args)]
+
+use std::fmt::Debug;
+
+fn foo<T: Debug>(t: T) {
+    println!("{:?}", t);
+}
+
+fn foo3<T1: Debug, T2: Debug, T3: Debug>(t1: T1, t2: T2, t3: T3) {
+    println!("{:?}, {:?}, {:?}", t1, t2, t3);
+}
+
+fn bad() {
+    foo({});
+    //~^ unit_arg
+    foo3({}, 2, 2);
+    //~^ unit_arg
+    taking_two_units({}, foo(0));
+    //~^ unit_arg
+    taking_three_units({}, foo(0), foo(1));
+    //~^ unit_arg
+}
+
+fn taking_two_units(a: (), b: ()) {}
+fn taking_three_units(a: (), b: (), c: ()) {}
+
+fn main() {
+    bad();
+}
+
+fn issue14857() {
+    let fn_take_unit = |_: ()| {};
+    fn_take_unit(Default::default());
+    //~^ unit_arg
+
+    fn some_other_fn(_: &i32) {}
+
+    macro_rules! another_mac {
+        () => {
+            some_other_fn(&Default::default())
+        };
+        ($e:expr) => {
+            some_other_fn(&$e)
+        };
+    }
+
+    fn_take_unit(another_mac!());
+    //~^ unit_arg
+    fn_take_unit(another_mac!(1));
+    //~^ unit_arg
+
+    macro_rules! mac {
+        (nondef $e:expr) => {
+            $e
+        };
+        (empty_block) => {{}};
+    }
+    fn_take_unit(mac!(nondef Default::default()));
+    //~^ unit_arg
+    fn_take_unit(mac!(empty_block));
+    //~^ unit_arg
+
+    fn def<T: Default>() -> T {
+        Default::default()
+    }
+
+    fn_take_unit(def());
+    //~^ unit_arg
+}
diff --git a/src/tools/clippy/tests/ui/unit_arg_fixable.stderr b/src/tools/clippy/tests/ui/unit_arg_fixable.stderr
new file mode 100644
index 00000000000..ccd5aa8322f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unit_arg_fixable.stderr
@@ -0,0 +1,110 @@
+error: passing a unit value to a function
+  --> tests/ui/unit_arg_fixable.rs:16:5
+   |
+LL |     foo({});
+   |     ^^^^--^
+   |         |
+   |         help: use a unit literal instead: `()`
+   |
+   = note: `-D clippy::unit-arg` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::unit_arg)]`
+
+error: passing a unit value to a function
+  --> tests/ui/unit_arg_fixable.rs:18:5
+   |
+LL |     foo3({}, 2, 2);
+   |     ^^^^^--^^^^^^^
+   |          |
+   |          help: use a unit literal instead: `()`
+
+error: passing unit values to a function
+  --> tests/ui/unit_arg_fixable.rs:20:5
+   |
+LL |     taking_two_units({}, foo(0));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: move the expression in front of the call and replace it with the unit literal `()`
+   |
+LL ~     foo(0);
+LL ~     taking_two_units((), ());
+   |
+
+error: passing unit values to a function
+  --> tests/ui/unit_arg_fixable.rs:22:5
+   |
+LL |     taking_three_units({}, foo(0), foo(1));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: move the expressions in front of the call and replace them with the unit literal `()`
+   |
+LL ~     foo(0);
+LL +     foo(1);
+LL ~     taking_three_units((), (), ());
+   |
+
+error: passing a unit value to a function
+  --> tests/ui/unit_arg_fixable.rs:35:5
+   |
+LL |     fn_take_unit(Default::default());
+   |     ^^^^^^^^^^^^^------------------^
+   |                  |
+   |                  help: use a unit literal instead: `()`
+
+error: passing a unit value to a function
+  --> tests/ui/unit_arg_fixable.rs:49:5
+   |
+LL |     fn_take_unit(another_mac!());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: move the expression in front of the call and replace it with the unit literal `()`
+   |
+LL ~     another_mac!();
+LL ~     fn_take_unit(());
+   |
+
+error: passing a unit value to a function
+  --> tests/ui/unit_arg_fixable.rs:51:5
+   |
+LL |     fn_take_unit(another_mac!(1));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: move the expression in front of the call and replace it with the unit literal `()`
+   |
+LL ~     another_mac!(1);
+LL ~     fn_take_unit(());
+   |
+
+error: passing a unit value to a function
+  --> tests/ui/unit_arg_fixable.rs:60:5
+   |
+LL |     fn_take_unit(mac!(nondef Default::default()));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^------------------^^
+   |                              |
+   |                              help: use a unit literal instead: `()`
+
+error: passing a unit value to a function
+  --> tests/ui/unit_arg_fixable.rs:62:5
+   |
+LL |     fn_take_unit(mac!(empty_block));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: move the expression in front of the call and replace it with the unit literal `()`
+   |
+LL ~     mac!(empty_block);
+LL ~     fn_take_unit(());
+   |
+
+error: passing a unit value to a function
+  --> tests/ui/unit_arg_fixable.rs:69:5
+   |
+LL |     fn_take_unit(def());
+   |     ^^^^^^^^^^^^^^^^^^^
+   |
+help: move the expression in front of the call and replace it with the unit literal `()`
+   |
+LL ~     let _: () = def();
+LL ~     fn_take_unit(());
+   |
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unused_unit.edition2021.fixed b/src/tools/clippy/tests/ui/unused_unit.edition2021.fixed
index 93dd58b8e9d..def8ef86e3c 100644
--- a/src/tools/clippy/tests/ui/unused_unit.edition2021.fixed
+++ b/src/tools/clippy/tests/ui/unused_unit.edition2021.fixed
@@ -143,4 +143,10 @@ mod issue14577 {
             todo!()
         }
     }
-}
\ No newline at end of file
+}
+
+mod pr14962 {
+    #[allow(unused_parens)]
+    type UnusedParensButNoUnit = Box<dyn (Fn())>;
+}
+
diff --git a/src/tools/clippy/tests/ui/unused_unit.edition2024.fixed b/src/tools/clippy/tests/ui/unused_unit.edition2024.fixed
index 987d901b97d..f908b958b19 100644
--- a/src/tools/clippy/tests/ui/unused_unit.edition2024.fixed
+++ b/src/tools/clippy/tests/ui/unused_unit.edition2024.fixed
@@ -143,4 +143,10 @@ mod issue14577 {
             todo!()
         }
     }
-}
\ No newline at end of file
+}
+
+mod pr14962 {
+    #[allow(unused_parens)]
+    type UnusedParensButNoUnit = Box<dyn (Fn())>;
+}
+
diff --git a/src/tools/clippy/tests/ui/unused_unit.rs b/src/tools/clippy/tests/ui/unused_unit.rs
index b7645f7b6a2..7298ec40cc2 100644
--- a/src/tools/clippy/tests/ui/unused_unit.rs
+++ b/src/tools/clippy/tests/ui/unused_unit.rs
@@ -143,4 +143,10 @@ mod issue14577 {
             todo!()
         }
     }
-}
\ No newline at end of file
+}
+
+mod pr14962 {
+    #[allow(unused_parens)]
+    type UnusedParensButNoUnit = Box<dyn (Fn())>;
+}
+
diff --git a/src/tools/clippy/tests/ui/write_literal.fixed b/src/tools/clippy/tests/ui/write_literal.fixed
index e84f768e33d..29352fd468e 100644
--- a/src/tools/clippy/tests/ui/write_literal.fixed
+++ b/src/tools/clippy/tests/ui/write_literal.fixed
@@ -87,3 +87,15 @@ fn issue_13959() {
 "
     );
 }
+
+fn issue_14930() {
+    let mut v = Vec::new();
+    writeln!(v, "Hello x is {0:2$.1$}", 0.01, 2, 3);
+    //~^ write_literal
+    writeln!(v, "Hello x is {0:2$.1$}", 0.01, 2, 3);
+    //~^ write_literal
+    writeln!(v, "Hello x is {0:2$.1$}", 0.01, 2, 3);
+    //~^ write_literal
+    writeln!(v, "Hello x is {0:2$.1$}", 0.01, 2, 3);
+    //~^ write_literal
+}
diff --git a/src/tools/clippy/tests/ui/write_literal.rs b/src/tools/clippy/tests/ui/write_literal.rs
index fc29fcbede7..92872752759 100644
--- a/src/tools/clippy/tests/ui/write_literal.rs
+++ b/src/tools/clippy/tests/ui/write_literal.rs
@@ -88,3 +88,15 @@ fn issue_13959() {
 "#
     );
 }
+
+fn issue_14930() {
+    let mut v = Vec::new();
+    writeln!(v, "Hello {3} is {0:2$.1$}", 0.01, 2, 3, "x");
+    //~^ write_literal
+    writeln!(v, "Hello {2} is {0:3$.1$}", 0.01, 2, "x", 3);
+    //~^ write_literal
+    writeln!(v, "Hello {1} is {0:3$.2$}", 0.01, "x", 2, 3);
+    //~^ write_literal
+    writeln!(v, "Hello {0} is {1:3$.2$}", "x", 0.01, 2, 3);
+    //~^ write_literal
+}
diff --git a/src/tools/clippy/tests/ui/write_literal.stderr b/src/tools/clippy/tests/ui/write_literal.stderr
index d53c2a7de2e..ca37406c811 100644
--- a/src/tools/clippy/tests/ui/write_literal.stderr
+++ b/src/tools/clippy/tests/ui/write_literal.stderr
@@ -181,5 +181,53 @@ LL +         bar
 LL ~ "
    |
 
-error: aborting due to 14 previous errors
+error: literal with an empty format string
+  --> tests/ui/write_literal.rs:94:55
+   |
+LL |     writeln!(v, "Hello {3} is {0:2$.1$}", 0.01, 2, 3, "x");
+   |                                                       ^^^
+   |
+help: try
+   |
+LL -     writeln!(v, "Hello {3} is {0:2$.1$}", 0.01, 2, 3, "x");
+LL +     writeln!(v, "Hello x is {0:2$.1$}", 0.01, 2, 3);
+   |
+
+error: literal with an empty format string
+  --> tests/ui/write_literal.rs:96:52
+   |
+LL |     writeln!(v, "Hello {2} is {0:3$.1$}", 0.01, 2, "x", 3);
+   |                                                    ^^^
+   |
+help: try
+   |
+LL -     writeln!(v, "Hello {2} is {0:3$.1$}", 0.01, 2, "x", 3);
+LL +     writeln!(v, "Hello x is {0:2$.1$}", 0.01, 2, 3);
+   |
+
+error: literal with an empty format string
+  --> tests/ui/write_literal.rs:98:49
+   |
+LL |     writeln!(v, "Hello {1} is {0:3$.2$}", 0.01, "x", 2, 3);
+   |                                                 ^^^
+   |
+help: try
+   |
+LL -     writeln!(v, "Hello {1} is {0:3$.2$}", 0.01, "x", 2, 3);
+LL +     writeln!(v, "Hello x is {0:2$.1$}", 0.01, 2, 3);
+   |
+
+error: literal with an empty format string
+  --> tests/ui/write_literal.rs:100:43
+   |
+LL |     writeln!(v, "Hello {0} is {1:3$.2$}", "x", 0.01, 2, 3);
+   |                                           ^^^
+   |
+help: try
+   |
+LL -     writeln!(v, "Hello {0} is {1:3$.2$}", "x", 0.01, 2, 3);
+LL +     writeln!(v, "Hello x is {0:2$.1$}", 0.01, 2, 3);
+   |
+
+error: aborting due to 18 previous errors
 
diff --git a/src/tools/clippy/tests/ui/zombie_processes.rs b/src/tools/clippy/tests/ui/zombie_processes.rs
index 395f9dd2def..e81b5fd4f3e 100644
--- a/src/tools/clippy/tests/ui/zombie_processes.rs
+++ b/src/tools/clippy/tests/ui/zombie_processes.rs
@@ -198,3 +198,13 @@ mod issue14677 {
         child.wait().unwrap();
     }
 }
+
+fn issue14911() -> std::io::Result<String> {
+    let (mut recv, send) = std::io::pipe()?;
+    let mut command = Command::new("ls")
+        .stdout(send.try_clone()?)
+        .spawn()
+        .expect("Could not spawn new process...");
+    command.wait()?;
+    Ok("".into())
+}
diff --git a/src/tools/clippy/util/gh-pages/script.js b/src/tools/clippy/util/gh-pages/script.js
index fec883938d6..285aa34e701 100644
--- a/src/tools/clippy/util/gh-pages/script.js
+++ b/src/tools/clippy/util/gh-pages/script.js
@@ -602,7 +602,7 @@ filters.filterLints();
 updateLintCount();
 
 function updateLintCount() {
-    const allLints = filters.getAllLints();
+    const allLints = filters.getAllLints().filter(lint => lint.group != "deprecated");
     const totalLints = allLints.length;
     
     const countElement = document.getElementById("lint-count");
diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs
index 81f5679aead..202582bea8c 100644
--- a/src/tools/compiletest/src/util.rs
+++ b/src/tools/compiletest/src/util.rs
@@ -29,6 +29,7 @@ fn path_div() -> &'static str {
 pub fn logv(config: &Config, s: String) {
     debug!("{}", s);
     if config.verbose {
+        // Note: `./x test ... --verbose --no-capture` is needed to see this print.
         println!("{}", s);
     }
 }
diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md
index fc7942a49c0..5554c7975ff 100644
--- a/src/tools/miri/README.md
+++ b/src/tools/miri/README.md
@@ -286,6 +286,11 @@ environment variable. We first document the most relevant and most commonly used
   specific circumstances, but Miri's behavior will also be more stable across versions and targets.
   This is equivalent to `-Zmiri-fixed-schedule -Zmiri-compare-exchange-weak-failure-rate=0.0
   -Zmiri-address-reuse-cross-thread-rate=0.0 -Zmiri-disable-weak-memory-emulation`.
+* `-Zmiri-deterministic-floats` makes Miri's floating-point behavior fully deterministic. This means
+  that operations will always return the preferred NaN, imprecise operations will not have any
+  random error applied to them, and `min`/`max` as "maybe fused" multiply-add all behave
+  deterministically. Note that Miri still uses host floats for some operations, so behavior can
+  still differ depending on the host target and setup.
 * `-Zmiri-disable-isolation` disables host isolation. As a consequence,
   the program has access to host resources such as environment variables, file
   systems, and randomness.
diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs
index 2faaec5a174..d4ba7fbd6a4 100644
--- a/src/tools/miri/src/bin/miri.rs
+++ b/src/tools/miri/src/bin/miri.rs
@@ -601,6 +601,8 @@ fn main() {
             miri_config.collect_leak_backtraces = false;
         } else if arg == "-Zmiri-force-intrinsic-fallback" {
             miri_config.force_intrinsic_fallback = true;
+        } else if arg == "-Zmiri-deterministic-floats" {
+            miri_config.float_nondet = false;
         } else if arg == "-Zmiri-strict-provenance" {
             miri_config.provenance_mode = ProvenanceMode::Strict;
         } else if arg == "-Zmiri-permissive-provenance" {
diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs
index 6f5f756e144..7a5f96ec177 100644
--- a/src/tools/miri/src/eval.rs
+++ b/src/tools/miri/src/eval.rs
@@ -166,6 +166,8 @@ pub struct MiriConfig {
     pub fixed_scheduling: bool,
     /// Always prefer the intrinsic fallback body over the native Miri implementation.
     pub force_intrinsic_fallback: bool,
+    /// Whether floating-point operations can behave non-deterministically.
+    pub float_nondet: bool,
 }
 
 impl Default for MiriConfig {
@@ -205,6 +207,7 @@ impl Default for MiriConfig {
             address_reuse_cross_thread_rate: 0.1,
             fixed_scheduling: false,
             force_intrinsic_fallback: false,
+            float_nondet: true,
         }
     }
 }
diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs
index 9957e351ff1..458b7723299 100644
--- a/src/tools/miri/src/intrinsics/mod.rs
+++ b/src/tools/miri/src/intrinsics/mod.rs
@@ -272,8 +272,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 let a = this.read_scalar(a)?.to_f32()?;
                 let b = this.read_scalar(b)?.to_f32()?;
                 let c = this.read_scalar(c)?.to_f32()?;
-                // FIXME: Using host floats, to work around https://github.com/rust-lang/rustc_apfloat/issues/11
-                let res = a.to_host().mul_add(b.to_host(), c.to_host()).to_soft();
+                let res = a.mul_add(b, c).value;
                 let res = this.adjust_nan(res, &[a, b, c]);
                 this.write_scalar(res, dest)?;
             }
@@ -282,8 +281,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 let a = this.read_scalar(a)?.to_f64()?;
                 let b = this.read_scalar(b)?.to_f64()?;
                 let c = this.read_scalar(c)?.to_f64()?;
-                // FIXME: Using host floats, to work around https://github.com/rust-lang/rustc_apfloat/issues/11
-                let res = a.to_host().mul_add(b.to_host(), c.to_host()).to_soft();
+                let res = a.mul_add(b, c).value;
                 let res = this.adjust_nan(res, &[a, b, c]);
                 this.write_scalar(res, dest)?;
             }
@@ -293,10 +291,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 let a = this.read_scalar(a)?.to_f32()?;
                 let b = this.read_scalar(b)?.to_f32()?;
                 let c = this.read_scalar(c)?.to_f32()?;
-                let fuse: bool = this.machine.rng.get_mut().random();
+                let fuse: bool = this.machine.float_nondet && this.machine.rng.get_mut().random();
                 let res = if fuse {
-                    // FIXME: Using host floats, to work around https://github.com/rust-lang/rustc_apfloat/issues/11
-                    a.to_host().mul_add(b.to_host(), c.to_host()).to_soft()
+                    a.mul_add(b, c).value
                 } else {
                     ((a * b).value + c).value
                 };
@@ -308,10 +305,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 let a = this.read_scalar(a)?.to_f64()?;
                 let b = this.read_scalar(b)?.to_f64()?;
                 let c = this.read_scalar(c)?.to_f64()?;
-                let fuse: bool = this.machine.rng.get_mut().random();
+                let fuse: bool = this.machine.float_nondet && this.machine.rng.get_mut().random();
                 let res = if fuse {
-                    // FIXME: Using host floats, to work around https://github.com/rust-lang/rustc_apfloat/issues/11
-                    a.to_host().mul_add(b.to_host(), c.to_host()).to_soft()
+                    a.mul_add(b, c).value
                 } else {
                     ((a * b).value + c).value
                 };
diff --git a/src/tools/miri/src/intrinsics/simd.rs b/src/tools/miri/src/intrinsics/simd.rs
index b17fd4fb7f9..dbe193bdbda 100644
--- a/src/tools/miri/src/intrinsics/simd.rs
+++ b/src/tools/miri/src/intrinsics/simd.rs
@@ -306,7 +306,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     let c = this.read_scalar(&this.project_index(&c, i)?)?;
                     let dest = this.project_index(&dest, i)?;
 
-                    let fuse: bool = intrinsic_name == "fma" || this.machine.rng.get_mut().random();
+                    let fuse: bool = intrinsic_name == "fma"
+                        || (this.machine.float_nondet && this.machine.rng.get_mut().random());
 
                     // Works for f32 and f64.
                     // FIXME: using host floats to work around https://github.com/rust-lang/miri/issues/2468.
@@ -320,7 +321,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                             let b = b.to_f32()?;
                             let c = c.to_f32()?;
                             let res = if fuse {
-                                a.to_host().mul_add(b.to_host(), c.to_host()).to_soft()
+                                a.mul_add(b, c).value
                             } else {
                                 ((a * b).value + c).value
                             };
@@ -332,7 +333,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                             let b = b.to_f64()?;
                             let c = c.to_f64()?;
                             let res = if fuse {
-                                a.to_host().mul_add(b.to_host(), c.to_host()).to_soft()
+                                a.mul_add(b, c).value
                             } else {
                                 ((a * b).value + c).value
                             };
diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs
index b221dd85092..b4d7db34efa 100644
--- a/src/tools/miri/src/machine.rs
+++ b/src/tools/miri/src/machine.rs
@@ -618,6 +618,9 @@ pub struct MiriMachine<'tcx> {
 
     /// Always prefer the intrinsic fallback body over the native Miri implementation.
     pub force_intrinsic_fallback: bool,
+
+    /// Whether floating-point operations can behave non-deterministically.
+    pub float_nondet: bool,
 }
 
 impl<'tcx> MiriMachine<'tcx> {
@@ -778,6 +781,7 @@ impl<'tcx> MiriMachine<'tcx> {
             int2ptr_warned: Default::default(),
             mangle_internal_symbol_cache: Default::default(),
             force_intrinsic_fallback: config.force_intrinsic_fallback,
+            float_nondet: config.float_nondet,
         }
     }
 
@@ -956,6 +960,7 @@ impl VisitProvenance for MiriMachine<'_> {
             int2ptr_warned: _,
             mangle_internal_symbol_cache: _,
             force_intrinsic_fallback: _,
+            float_nondet: _,
         } = self;
 
         threads.visit_provenance(visit);
diff --git a/src/tools/miri/src/math.rs b/src/tools/miri/src/math.rs
index d1355a21684..cf16a5676d6 100644
--- a/src/tools/miri/src/math.rs
+++ b/src/tools/miri/src/math.rs
@@ -15,6 +15,10 @@ pub(crate) fn apply_random_float_error<F: rustc_apfloat::Float>(
     val: F,
     err_scale: i32,
 ) -> F {
+    if !ecx.machine.float_nondet {
+        return val;
+    }
+
     let rng = ecx.machine.rng.get_mut();
     // Generate a random integer in the range [0, 2^PREC).
     // (When read as binary, the position of the first `1` determines the exponent,
diff --git a/src/tools/miri/src/operator.rs b/src/tools/miri/src/operator.rs
index 81f22b2d0b2..73d671121f6 100644
--- a/src/tools/miri/src/operator.rs
+++ b/src/tools/miri/src/operator.rs
@@ -76,6 +76,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     }
 
     fn generate_nan<F1: Float + FloatConvert<F2>, F2: Float>(&self, inputs: &[F1]) -> F2 {
+        let this = self.eval_context_ref();
+        if !this.machine.float_nondet {
+            return F2::NAN;
+        }
+
         /// Make the given NaN a signaling NaN.
         /// Returns `None` if this would not result in a NaN.
         fn make_signaling<F: Float>(f: F) -> Option<F> {
@@ -89,7 +94,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             if f.is_nan() { Some(f) } else { None }
         }
 
-        let this = self.eval_context_ref();
         let mut rand = this.machine.rng.borrow_mut();
         // Assemble an iterator of possible NaNs: preferred, quieting propagation, unchanged propagation.
         // On some targets there are more possibilities; for now we just generate those options that
@@ -118,6 +122,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
     fn equal_float_min_max<F: Float>(&self, a: F, b: F) -> F {
         let this = self.eval_context_ref();
+        if !this.machine.float_nondet {
+            return a;
+        }
         // Return one side non-deterministically.
         let mut rand = this.machine.rng.borrow_mut();
         if rand.random() { a } else { b }
diff --git a/src/tools/miri/tests/fail/unsized-local.rs b/src/tools/miri/tests/fail/unsized-local.rs
deleted file mode 100644
index ceccae4e3e7..00000000000
--- a/src/tools/miri/tests/fail/unsized-local.rs
+++ /dev/null
@@ -1,23 +0,0 @@
-#![feature(unsized_locals)]
-#![allow(incomplete_features)]
-
-fn main() {
-    pub trait Foo {
-        fn foo(self) -> String;
-    }
-
-    struct A;
-
-    impl Foo for A {
-        fn foo(self) -> String {
-            format!("hello")
-        }
-    }
-
-    let x = *(Box::new(A) as Box<dyn Foo>); //~ERROR: unsized locals are not supported
-    assert_eq!(x.foo(), format!("hello"));
-
-    // I'm not sure whether we want this to work
-    let x = Box::new(A) as Box<dyn Foo>;
-    assert_eq!(x.foo(), format!("hello"));
-}
diff --git a/src/tools/miri/tests/fail/unsized-local.stderr b/src/tools/miri/tests/fail/unsized-local.stderr
deleted file mode 100644
index 548f3d66c73..00000000000
--- a/src/tools/miri/tests/fail/unsized-local.stderr
+++ /dev/null
@@ -1,14 +0,0 @@
-error: unsupported operation: unsized locals are not supported
-  --> tests/fail/unsized-local.rs:LL:CC
-   |
-LL |     let x = *(Box::new(A) as Box<dyn Foo>);
-   |         ^ unsized locals are not supported
-   |
-   = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support
-   = note: BACKTRACE:
-   = note: inside `main` at tests/fail/unsized-local.rs:LL:CC
-
-note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
-
-error: aborting due to 1 previous error
-
diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt
index 3e9d79224fd..045f2f0692a 100644
--- a/src/tools/tidy/src/issues.txt
+++ b/src/tools/tidy/src/issues.txt
@@ -754,7 +754,6 @@ ui/consts/issue-46553.rs
 ui/consts/issue-47789.rs
 ui/consts/issue-50439.rs
 ui/consts/issue-52023-array-size-pointer-cast.rs
-ui/consts/issue-54224.rs
 ui/consts/issue-54348.rs
 ui/consts/issue-54387.rs
 ui/consts/issue-54582.rs
diff --git a/tests/assembly/stack-protector/stack-protector-heuristics-effect-windows-32bit.rs b/tests/assembly/stack-protector/stack-protector-heuristics-effect-windows-32bit.rs
index 2a8251785e7..3287e018b40 100644
--- a/tests/assembly/stack-protector/stack-protector-heuristics-effect-windows-32bit.rs
+++ b/tests/assembly/stack-protector/stack-protector-heuristics-effect-windows-32bit.rs
@@ -10,8 +10,8 @@
 //@ compile-flags: -C opt-level=2 -Z merge-functions=disabled
 
 #![crate_type = "lib"]
-#![allow(incomplete_features)]
-#![feature(unsized_locals, unsized_fn_params)]
+#![allow(internal_features)]
+#![feature(unsized_fn_params)]
 
 // CHECK-LABEL: emptyfn:
 #[no_mangle]
@@ -357,27 +357,3 @@ pub fn unsized_fn_param(s: [u8], l: bool, f: fn([u8])) {
     // none-NOT: __security_check_cookie
     // missing-NOT: __security_check_cookie
 }
-
-// CHECK-LABEL: unsized_local
-#[no_mangle]
-pub fn unsized_local(s: &[u8], l: bool, f: fn(&mut [u8])) {
-    let n = if l { 1 } else { 2 };
-    let mut a: [u8] = *Box::<[u8]>::from(&s[0..n]); // slice-copy with Box::from
-    f(&mut a);
-
-    // This function allocates a slice as a local variable in its stack
-    // frame. Since the size is not a compile-time constant, an array
-    // alloca is required, and the function is protected by both the
-    // `strong` and `basic` heuristic.
-
-    // We should have a __security_check_cookie call in `all`, `strong` and `basic` modes but
-    // LLVM does not support generating stack protectors in functions with funclet
-    // based EH personalities.
-    // https://github.com/llvm/llvm-project/blob/37fd3c96b917096d8a550038f6e61cdf0fc4174f/llvm/lib/CodeGen/StackProtector.cpp#L103C1-L109C4
-    // all-NOT: __security_check_cookie
-    // strong-NOT: __security_check_cookie
-    // basic-NOT: __security_check_cookie
-
-    // none-NOT: __security_check_cookie
-    // missing-NOT: __security_check_cookie
-}
diff --git a/tests/assembly/stack-protector/stack-protector-heuristics-effect-windows-64bit.rs b/tests/assembly/stack-protector/stack-protector-heuristics-effect-windows-64bit.rs
index 9729da4e5d2..9a3dabc74dd 100644
--- a/tests/assembly/stack-protector/stack-protector-heuristics-effect-windows-64bit.rs
+++ b/tests/assembly/stack-protector/stack-protector-heuristics-effect-windows-64bit.rs
@@ -10,8 +10,7 @@
 //@ compile-flags: -C opt-level=2 -Z merge-functions=disabled
 
 #![crate_type = "lib"]
-#![allow(incomplete_features)]
-#![feature(unsized_locals, unsized_fn_params)]
+#![feature(unsized_fn_params)]
 
 // CHECK-LABEL: emptyfn:
 #[no_mangle]
@@ -365,27 +364,3 @@ pub fn unsized_fn_param(s: [u8], l: bool, f: fn([u8])) {
     // none-NOT: __security_check_cookie
     // missing-NOT: __security_check_cookie
 }
-
-// CHECK-LABEL: unsized_local
-#[no_mangle]
-pub fn unsized_local(s: &[u8], l: bool, f: fn(&mut [u8])) {
-    let n = if l { 1 } else { 2 };
-    let mut a: [u8] = *Box::<[u8]>::from(&s[0..n]); // slice-copy with Box::from
-    f(&mut a);
-
-    // This function allocates a slice as a local variable in its stack
-    // frame. Since the size is not a compile-time constant, an array
-    // alloca is required, and the function is protected by both the
-    // `strong` and `basic` heuristic.
-
-    // We should have a __security_check_cookie call in `all`, `strong` and `basic` modes but
-    // LLVM does not support generating stack protectors in functions with funclet
-    // based EH personalities.
-    // https://github.com/llvm/llvm-project/blob/37fd3c96b917096d8a550038f6e61cdf0fc4174f/llvm/lib/CodeGen/StackProtector.cpp#L103C1-L109C4
-    // all-NOT: __security_check_cookie
-    // strong-NOT: __security_check_cookie
-    // basic-NOT: __security_check_cookie
-
-    // none-NOT: __security_check_cookie
-    // missing-NOT: __security_check_cookie
-}
diff --git a/tests/assembly/stack-protector/stack-protector-heuristics-effect.rs b/tests/assembly/stack-protector/stack-protector-heuristics-effect.rs
index 91c83fa2f5b..ae281cb95da 100644
--- a/tests/assembly/stack-protector/stack-protector-heuristics-effect.rs
+++ b/tests/assembly/stack-protector/stack-protector-heuristics-effect.rs
@@ -16,8 +16,8 @@
 // See comments on https://github.com/rust-lang/rust/issues/114903.
 
 #![crate_type = "lib"]
-#![allow(incomplete_features)]
-#![feature(unsized_locals, unsized_fn_params)]
+#![allow(internal_features)]
+#![feature(unsized_fn_params)]
 
 // CHECK-LABEL: emptyfn{{:|\[}}
 #[no_mangle]
@@ -343,22 +343,3 @@ pub fn unsized_fn_param(s: [u8], l: bool, f: fn([u8])) {
     // none-NOT: __stack_chk_fail
     // missing-NOT: __stack_chk_fail
 }
-
-// CHECK-LABEL: unsized_local{{:|\[}}
-#[no_mangle]
-pub fn unsized_local(s: &[u8], l: bool, f: fn(&mut [u8])) {
-    let n = if l { 1 } else { 2 };
-    let mut a: [u8] = *Box::<[u8]>::from(&s[0..n]); // slice-copy with Box::from
-    f(&mut a);
-
-    // This function allocates a slice as a local variable in its stack
-    // frame. Since the size is not a compile-time constant, an array
-    // alloca is required, and the function is protected by both the
-    // `strong` and `basic` heuristic.
-
-    // all: __stack_chk_fail
-    // strong: __stack_chk_fail
-    // basic: __stack_chk_fail
-    // none-NOT: __stack_chk_fail
-    // missing-NOT: __stack_chk_fail
-}
diff --git a/tests/codegen/autovec/dont-shuffle-bswaps-opt2.rs b/tests/codegen/autovec/dont-shuffle-bswaps-opt2.rs
new file mode 100644
index 00000000000..c354228acc5
--- /dev/null
+++ b/tests/codegen/autovec/dont-shuffle-bswaps-opt2.rs
@@ -0,0 +1,31 @@
+//@ compile-flags: -Copt-level=2
+
+#![crate_type = "lib"]
+#![no_std]
+
+// This test is paired with the arch-specific -opt3.rs test.
+
+// The code is from https://github.com/rust-lang/rust/issues/122805.
+// Ensure we do not generate the shufflevector instruction
+// to avoid complicating the code.
+
+// CHECK-LABEL: define{{.*}}void @convert(
+// CHECK-NOT: shufflevector
+#[no_mangle]
+pub fn convert(value: [u16; 8]) -> [u8; 16] {
+    #[cfg(target_endian = "little")]
+    let bswap = u16::to_be;
+    #[cfg(target_endian = "big")]
+    let bswap = u16::to_le;
+    let addr16 = [
+        bswap(value[0]),
+        bswap(value[1]),
+        bswap(value[2]),
+        bswap(value[3]),
+        bswap(value[4]),
+        bswap(value[5]),
+        bswap(value[6]),
+        bswap(value[7]),
+    ];
+    unsafe { core::mem::transmute::<_, [u8; 16]>(addr16) }
+}
diff --git a/tests/codegen/dont-shuffle-bswaps.rs b/tests/codegen/autovec/dont-shuffle-bswaps-opt3.rs
index c1dab2bc295..203d12005de 100644
--- a/tests/codegen/dont-shuffle-bswaps.rs
+++ b/tests/codegen/autovec/dont-shuffle-bswaps-opt3.rs
@@ -1,29 +1,27 @@
-//@ revisions: OPT2 OPT3 OPT3_S390X
-//@[OPT2] compile-flags: -Copt-level=2
-//@[OPT3] compile-flags: -C opt-level=3
-// some targets don't do the opt we are looking for
-//@[OPT3] only-64bit
-//@[OPT3] ignore-s390x
-//@[OPT3_S390X] compile-flags: -C opt-level=3 -C target-cpu=z13
-//@[OPT3_S390X] only-s390x
+//@ revisions: AARCH64 X86_64 Z13
+//@ compile-flags: -Copt-level=3
+//@[AARCH64] only-aarch64
+//@[X86_64] only-x86_64
+//@[Z13] only-s390x
+//@[Z13] compile-flags: -Ctarget-cpu=z13
 
 #![crate_type = "lib"]
 #![no_std]
 
+// This test is paired with the arch-neutral -opt2.rs test
+
 // The code is from https://github.com/rust-lang/rust/issues/122805.
 // Ensure we do not generate the shufflevector instruction
 // to avoid complicating the code.
+
 // CHECK-LABEL: define{{.*}}void @convert(
 // CHECK-NOT: shufflevector
+
 // On higher opt levels, this should just be a bswap:
-// OPT3: load <8 x i16>
-// OPT3-NEXT: call <8 x i16> @llvm.bswap
-// OPT3-NEXT: store <8 x i16>
-// OPT3-NEXT: ret void
-// OPT3_S390X: load <8 x i16>
-// OPT3_S390X-NEXT: call <8 x i16> @llvm.bswap
-// OPT3_S390X-NEXT: store <8 x i16>
-// OPT3_S390X-NEXT: ret void
+// CHECK: load <8 x i16>
+// CHECK-NEXT: call <8 x i16> @llvm.bswap
+// CHECK-NEXT: store <8 x i16>
+// CHECK-NEXT: ret void
 #[no_mangle]
 pub fn convert(value: [u16; 8]) -> [u8; 16] {
     #[cfg(target_endian = "little")]
diff --git a/tests/codegen/deduced-param-attrs.rs b/tests/codegen/deduced-param-attrs.rs
index 22db090d4d8..34504c80fad 100644
--- a/tests/codegen/deduced-param-attrs.rs
+++ b/tests/codegen/deduced-param-attrs.rs
@@ -1,8 +1,8 @@
 //@ compile-flags: -Copt-level=3
 
 #![crate_type = "lib"]
-#![allow(incomplete_features)]
-#![feature(unsized_locals, unsized_fn_params)]
+#![allow(internal_features)]
+#![feature(unsized_fn_params)]
 
 use std::cell::Cell;
 use std::hint;
diff --git a/tests/codegen/retpoline.rs b/tests/codegen/retpoline.rs
new file mode 100644
index 00000000000..915c2c3d797
--- /dev/null
+++ b/tests/codegen/retpoline.rs
@@ -0,0 +1,27 @@
+// ignore-tidy-linelength
+// Test that the
+// `retpoline-external-thunk`, `retpoline-indirect-branches`, `retpoline-indirect-calls`
+// target features are (not) emitted when the `retpoline/retpoline-external-thunk` flag is (not) set.
+
+//@ add-core-stubs
+//@ revisions: disabled enabled_retpoline enabled_retpoline_external_thunk
+//@ needs-llvm-components: x86
+//@ compile-flags: --target x86_64-unknown-linux-gnu
+//@ [enabled_retpoline] compile-flags: -Zretpoline
+//@ [enabled_retpoline_external_thunk] compile-flags: -Zretpoline-external-thunk
+#![crate_type = "lib"]
+#![feature(no_core)]
+#![no_core]
+extern crate minicore;
+
+#[no_mangle]
+pub fn foo() {
+    // CHECK: @foo() unnamed_addr #0
+
+    // disabled-NOT: attributes #0 = { {{.*}}"target-features"="{{[^"]*}}+retpoline-external-thunk{{.*}} }
+    // disabled-NOT: attributes #0 = { {{.*}}"target-features"="{{[^"]*}}+retpoline-indirect-branches{{.*}} }
+    // disabled-NOT: attributes #0 = { {{.*}}"target-features"="{{[^"]*}}+retpoline-indirect-calls{{.*}} }
+
+    // enabled_retpoline: attributes #0 = { {{.*}}"target-features"="{{[^"]*}}+retpoline-indirect-branches,+retpoline-indirect-calls{{.*}} }
+    // enabled_retpoline_external_thunk: attributes #0 = { {{.*}}"target-features"="{{[^"]*}}+retpoline-external-thunk,+retpoline-indirect-branches,+retpoline-indirect-calls{{.*}} }
+}
diff --git a/tests/codegen/virtual-function-elimination.rs b/tests/codegen/virtual-function-elimination.rs
index d2d0c4b78ab..26604478c11 100644
--- a/tests/codegen/virtual-function-elimination.rs
+++ b/tests/codegen/virtual-function-elimination.rs
@@ -6,8 +6,6 @@
 // CHECK: @vtable.2 = {{.*}}, !type ![[TYPE2:[0-9]+]], !vcall_visibility ![[VCALL_VIS2:[0-9]+]]
 
 #![crate_type = "lib"]
-#![allow(incomplete_features)]
-#![feature(unsized_locals)]
 
 use std::rc::Rc;
 
diff --git a/tests/crashes/137188.rs b/tests/crashes/137188.rs
deleted file mode 100644
index fdd098d300f..00000000000
--- a/tests/crashes/137188.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-//@ known-bug: #137188
-#![feature(min_generic_const_args)]
-trait Trait {}
-impl Trait for [(); N] {}
-fn N<T>() {}
-pub fn main() {}
diff --git a/tests/crashes/138166.rs b/tests/crashes/138166.rs
deleted file mode 100644
index 98003bd6dae..00000000000
--- a/tests/crashes/138166.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-//@ known-bug: #138166
-#![feature(min_generic_const_args)]
-#![feature(inherent_associated_types)]
-struct a(Box<[u8; Box::b]>);
-impl a {
-  fn c(self) { self.0.d() }
-}
-fn main() {}
diff --git a/tests/crashes/138240.rs b/tests/crashes/138240.rs
deleted file mode 100644
index 6ffb7868bd5..00000000000
--- a/tests/crashes/138240.rs
+++ /dev/null
@@ -1,9 +0,0 @@
-//@ known-bug: #138240
-//@edition:2024
-#![feature(min_generic_const_args)]
-#![feature(inherent_associated_types)]
-async fn _CF() -> Box<[u8; Box::b]> {
-    Box::new(true)
-}
-
-fn main() {}
diff --git a/tests/crashes/138266.rs b/tests/crashes/138266.rs
deleted file mode 100644
index 9a4de9abcff..00000000000
--- a/tests/crashes/138266.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-//@ known-bug: #138266
-//@compile-flags: --crate-type=lib
-#![feature(min_generic_const_args)]
-#![feature(inherent_associated_types)]
-pub fn f(mut x: [u8; Box::b]) {
-    x[72] = 1;
-}
diff --git a/tests/crashes/138359.rs b/tests/crashes/138359.rs
deleted file mode 100644
index d4376d536ee..00000000000
--- a/tests/crashes/138359.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-//@ known-bug: #138359
-#![feature(min_generic_const_args)]
-#![feature(inherent_associated_types)]
-struct a(Box<[u8; Box::b]>);
-impl a {
-  fn c(self) { self.0.da }
-}
-fn main() {}
diff --git a/tests/crashes/79409.rs b/tests/crashes/79409.rs
deleted file mode 100644
index 98b5f606336..00000000000
--- a/tests/crashes/79409.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-//@ known-bug: #79409
-
-#![feature(extern_types)]
-#![feature(unsized_locals)]
-
-extern {
-    type Device;
-}
-
-unsafe fn make_device() -> Box<Device> {
-    Box::from_raw(0 as *mut _)
-}
-
-fn main() {
-    let d: Device = unsafe { *make_device() };
-}
diff --git a/tests/mir-opt/lower_intrinsics.align_of.LowerIntrinsics.panic-abort.diff b/tests/mir-opt/lower_intrinsics.align_of.LowerIntrinsics.panic-abort.diff
index 8ebd07b9c9c..e61a3e8d738 100644
--- a/tests/mir-opt/lower_intrinsics.align_of.LowerIntrinsics.panic-abort.diff
+++ b/tests/mir-opt/lower_intrinsics.align_of.LowerIntrinsics.panic-abort.diff
@@ -5,7 +5,7 @@
       let mut _0: usize;
   
       bb0: {
--         _0 = std::intrinsics::min_align_of::<T>() -> [return: bb1, unwind unreachable];
+-         _0 = std::intrinsics::align_of::<T>() -> [return: bb1, unwind unreachable];
 +         _0 = AlignOf(T);
 +         goto -> bb1;
       }
diff --git a/tests/mir-opt/lower_intrinsics.align_of.LowerIntrinsics.panic-unwind.diff b/tests/mir-opt/lower_intrinsics.align_of.LowerIntrinsics.panic-unwind.diff
index 8ebd07b9c9c..e61a3e8d738 100644
--- a/tests/mir-opt/lower_intrinsics.align_of.LowerIntrinsics.panic-unwind.diff
+++ b/tests/mir-opt/lower_intrinsics.align_of.LowerIntrinsics.panic-unwind.diff
@@ -5,7 +5,7 @@
       let mut _0: usize;
   
       bb0: {
--         _0 = std::intrinsics::min_align_of::<T>() -> [return: bb1, unwind unreachable];
+-         _0 = std::intrinsics::align_of::<T>() -> [return: bb1, unwind unreachable];
 +         _0 = AlignOf(T);
 +         goto -> bb1;
       }
diff --git a/tests/mir-opt/lower_intrinsics.rs b/tests/mir-opt/lower_intrinsics.rs
index 1e4f2024194..7729e502ad6 100644
--- a/tests/mir-opt/lower_intrinsics.rs
+++ b/tests/mir-opt/lower_intrinsics.rs
@@ -51,7 +51,7 @@ pub fn size_of<T>() -> usize {
 pub fn align_of<T>() -> usize {
     // CHECK-LABEL: fn align_of(
     // CHECK: {{_.*}} = AlignOf(T);
-    core::intrinsics::min_align_of::<T>()
+    core::intrinsics::align_of::<T>()
 }
 
 // EMIT_MIR lower_intrinsics.forget.LowerIntrinsics.diff
diff --git a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir
index 30eafe8594b..2eee8a97db0 100644
--- a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir
@@ -39,7 +39,7 @@ fn vec_deref_to_slice(_1: &Vec<u8>) -> &[u8] {
                 }
                 scope 15 (inlined std::mem::size_of::<u8>) {
                 }
-                scope 16 (inlined align_of::<u8>) {
+                scope 16 (inlined std::mem::align_of::<u8>) {
                 }
                 scope 17 (inlined slice_from_raw_parts::<u8>) {
                     debug data => _3;
diff --git a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir
index 30eafe8594b..2eee8a97db0 100644
--- a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir
@@ -39,7 +39,7 @@ fn vec_deref_to_slice(_1: &Vec<u8>) -> &[u8] {
                 }
                 scope 15 (inlined std::mem::size_of::<u8>) {
                 }
-                scope 16 (inlined align_of::<u8>) {
+                scope 16 (inlined std::mem::align_of::<u8>) {
                 }
                 scope 17 (inlined slice_from_raw_parts::<u8>) {
                     debug data => _3;
diff --git a/tests/pretty/postfix-match/precedence.pp b/tests/pretty/postfix-match/precedence.pp
index 967aa7bc39e..2052b445a2b 100644
--- a/tests/pretty/postfix-match/precedence.pp
+++ b/tests/pretty/postfix-match/precedence.pp
@@ -26,7 +26,7 @@ pub fn main() {
         _ => {}
     };
     (4 as usize).match { _ => {} };
-    (return).match { _ => {} };
+    return.match { _ => {} };
     (a = 42).match { _ => {} };
     (|| {}).match { _ => {} };
     (42..101).match { _ => {} };
diff --git a/tests/rustdoc-ui/extract-doctests-result.rs b/tests/rustdoc-ui/extract-doctests-result.rs
new file mode 100644
index 00000000000..88affb6d333
--- /dev/null
+++ b/tests/rustdoc-ui/extract-doctests-result.rs
@@ -0,0 +1,11 @@
+// Test to ensure that it generates expected output for `--output-format=doctest` command-line
+// flag.
+
+//@ compile-flags:-Z unstable-options --output-format=doctest
+//@ normalize-stdout: "tests/rustdoc-ui" -> "$$DIR"
+//@ check-pass
+
+//! ```
+//! let x = 12;
+//! Ok(())
+//! ```
diff --git a/tests/rustdoc-ui/extract-doctests-result.stdout b/tests/rustdoc-ui/extract-doctests-result.stdout
new file mode 100644
index 00000000000..44e6d33c662
--- /dev/null
+++ b/tests/rustdoc-ui/extract-doctests-result.stdout
@@ -0,0 +1 @@
+{"format_version":2,"doctests":[{"file":"$DIR/extract-doctests-result.rs","line":8,"doctest_attributes":{"original":"","should_panic":false,"no_run":false,"ignore":"None","rust":true,"test_harness":false,"compile_fail":false,"standalone_crate":false,"error_codes":[],"edition":null,"added_css_classes":[],"unknown":[]},"original_code":"let x = 12;\nOk(())","doctest_code":{"crate_level":"#![allow(unused)]\n","code":"let x = 12;\nOk(())","wrapper":{"before":"fn main() { fn _inner() -> core::result::Result<(), impl core::fmt::Debug> {\n","after":"\n} _inner().unwrap() }","returns_result":true}},"name":"$DIR/extract-doctests-result.rs - (line 8)"}]}
\ No newline at end of file
diff --git a/tests/rustdoc-ui/extract-doctests.stdout b/tests/rustdoc-ui/extract-doctests.stdout
index b11531b844e..796ecd82f1c 100644
--- a/tests/rustdoc-ui/extract-doctests.stdout
+++ b/tests/rustdoc-ui/extract-doctests.stdout
@@ -1 +1 @@
-{"format_version":1,"doctests":[{"file":"$DIR/extract-doctests.rs","line":8,"doctest_attributes":{"original":"ignore (checking attributes)","should_panic":false,"no_run":false,"ignore":"All","rust":true,"test_harness":false,"compile_fail":false,"standalone_crate":false,"error_codes":[],"edition":null,"added_css_classes":[],"unknown":[]},"original_code":"let x = 12;\nlet y = 14;","doctest_code":"#![allow(unused)]\nfn main() {\nlet x = 12;\nlet y = 14;\n}","name":"$DIR/extract-doctests.rs - (line 8)"},{"file":"$DIR/extract-doctests.rs","line":13,"doctest_attributes":{"original":"edition2018,compile_fail","should_panic":false,"no_run":true,"ignore":"None","rust":true,"test_harness":false,"compile_fail":true,"standalone_crate":false,"error_codes":[],"edition":"2018","added_css_classes":[],"unknown":[]},"original_code":"let","doctest_code":null,"name":"$DIR/extract-doctests.rs - (line 13)"}]}
\ No newline at end of file
+{"format_version":2,"doctests":[{"file":"$DIR/extract-doctests.rs","line":8,"doctest_attributes":{"original":"ignore (checking attributes)","should_panic":false,"no_run":false,"ignore":"All","rust":true,"test_harness":false,"compile_fail":false,"standalone_crate":false,"error_codes":[],"edition":null,"added_css_classes":[],"unknown":[]},"original_code":"let x = 12;\nlet y = 14;","doctest_code":{"crate_level":"#![allow(unused)]\n","code":"let x = 12;\nlet y = 14;","wrapper":{"before":"fn main() {\n","after":"\n}","returns_result":false}},"name":"$DIR/extract-doctests.rs - (line 8)"},{"file":"$DIR/extract-doctests.rs","line":13,"doctest_attributes":{"original":"edition2018,compile_fail","should_panic":false,"no_run":true,"ignore":"None","rust":true,"test_harness":false,"compile_fail":true,"standalone_crate":false,"error_codes":[],"edition":"2018","added_css_classes":[],"unknown":[]},"original_code":"let","doctest_code":null,"name":"$DIR/extract-doctests.rs - (line 13)"}]}
\ No newline at end of file
diff --git a/tests/rustdoc/cfg-bool.rs b/tests/rustdoc/cfg-bool.rs
index 34fdfbe930e..0aaa132e0b5 100644
--- a/tests/rustdoc/cfg-bool.rs
+++ b/tests/rustdoc/cfg-bool.rs
@@ -3,11 +3,15 @@
 
 // regression test for https://github.com/rust-lang/rust/issues/138112
 
-//@ has 'foo/fn.foo.html' '//div[@class="stab portability"]' 'Available nowhere'
+//@ has 'foo/index.html'
+//@ has - '//*[@class="stab portability"]/@title' 'Available nowhere'
+
+//@ count 'foo/fn.foo.html' '//*[@class="stab portability"]' 1
+//@ has 'foo/fn.foo.html' '//*[@class="stab portability"]' 'Available nowhere'
 #[doc(cfg(false))]
 pub fn foo() {}
 
-// a cfg(true) will simply be ommited, as it is the same as no cfg.
-//@ !has 'foo/fn.bar.html' '//div[@class="stab portability"]' ''
+// a cfg(true) will simply be omitted, as it is the same as no cfg.
+//@ count 'foo/fn.bar.html' '//*[@class="stab portability"]' 0
 #[doc(cfg(true))]
 pub fn bar() {}
diff --git a/tests/rustdoc/macro/macro-generated-macro.macro_morestuff_pre.html b/tests/rustdoc/macro/macro-generated-macro.macro_morestuff_pre.html
index 28f15522a82..4dcc8111c6f 100644
--- a/tests/rustdoc/macro/macro-generated-macro.macro_morestuff_pre.html
+++ b/tests/rustdoc/macro/macro-generated-macro.macro_morestuff_pre.html
@@ -1,7 +1,7 @@
 macro_rules! morestuff {
     (
-        <= "space between most kinds of tokens" : 1 $x + @ :: >>= 'static
-        "no space inside paren or bracket" : (2 a) [2 a] $(2 $a:tt)*
+        <= "space between most kinds of tokens" : 1 $x:ident + @ :: >>=
+        'static "no space inside paren or bracket" : (2 a) [2 a] $(2 $a:tt)*
         "space inside curly brace" : { 2 a }
         "no space inside empty delimiters" : () [] {}
         "no space before comma or semicolon" : a, (a), { a }, a; [T; 0];
diff --git a/tests/rustdoc/macro/macro-generated-macro.rs b/tests/rustdoc/macro/macro-generated-macro.rs
index e77d0cf89e7..dfb152bafb6 100644
--- a/tests/rustdoc/macro/macro-generated-macro.rs
+++ b/tests/rustdoc/macro/macro-generated-macro.rs
@@ -25,7 +25,7 @@ make_macro!(linebreak 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 
 //@ snapshot macro_morestuff_pre macro_generated_macro/macro.morestuff.html //pre/text()
 make_macro!(morestuff
-    "space between most kinds of tokens": 1 $x + @ :: >>= 'static
+    "space between most kinds of tokens": 1 $x:ident + @ :: >>= 'static
     "no space inside paren or bracket": (2 a) [2 a] $(2 $a:tt)*
     "space inside curly brace": { 2 a }
     "no space inside empty delimiters": () [] {}
diff --git a/tests/ui-fulldeps/pprust-parenthesis-insertion.rs b/tests/ui-fulldeps/pprust-parenthesis-insertion.rs
index c566ac459e0..36916e97856 100644
--- a/tests/ui-fulldeps/pprust-parenthesis-insertion.rs
+++ b/tests/ui-fulldeps/pprust-parenthesis-insertion.rs
@@ -63,8 +63,8 @@ static EXPRS: &[&str] = &[
     "(2 += 2) += 2",
     // Return has lower precedence than a binary operator.
     "(return 2) + 2",
-    "2 + (return 2)", // FIXME: no parenthesis needed.
-    "(return) + 2",   // FIXME: no parenthesis needed.
+    "2 + return 2",
+    "return + 2",
     // These mean different things.
     "return - 2",
     "(return) - 2",
diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr
index 03fca17aa55..001699b2bc7 100644
--- a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr
+++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr
@@ -583,18 +583,32 @@ error: cannot find attribute `multipart_suggestion` in this scope
    |
 LL | #[multipart_suggestion(no_crate_suggestion)]
    |   ^^^^^^^^^^^^^^^^^^^^
+   |
+help: `multipart_suggestion` is an attribute that can be used by the derive macro `Subdiagnostic`, you might be missing a `derive` attribute
+   |
+LL + #[derive(Subdiagnostic)]
+LL | struct MultipartSuggestion {
+   |
 
 error: cannot find attribute `multipart_suggestion` in this scope
   --> $DIR/diagnostic-derive.rs:647:3
    |
 LL | #[multipart_suggestion()]
    |   ^^^^^^^^^^^^^^^^^^^^
+   |
+help: `multipart_suggestion` is an attribute that can be used by the derive macro `Subdiagnostic`, you might be missing a `derive` attribute
+   |
+LL + #[derive(Subdiagnostic)]
+LL | struct MultipartSuggestion {
+   |
 
 error: cannot find attribute `multipart_suggestion` in this scope
   --> $DIR/diagnostic-derive.rs:651:7
    |
 LL |     #[multipart_suggestion(no_crate_suggestion)]
    |       ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `multipart_suggestion` is an attribute that can be used by the derive macro `Subdiagnostic`, you might be missing a `derive` attribute
 
 error[E0425]: cannot find value `nonsense` in module `crate::fluent_generated`
   --> $DIR/diagnostic-derive.rs:75:8
diff --git a/tests/ui/abi/bad-custom.rs b/tests/ui/abi/bad-custom.rs
new file mode 100644
index 00000000000..e792f0955b9
--- /dev/null
+++ b/tests/ui/abi/bad-custom.rs
@@ -0,0 +1,121 @@
+//@ edition: 2021
+//@ check-fail
+//@ needs-asm-support
+#![feature(abi_custom)]
+
+#[unsafe(naked)]
+extern "custom" fn must_be_unsafe(a: i64) -> i64 {
+    //~^ ERROR functions with the `"custom"` ABI must be unsafe
+    //~| ERROR invalid signature for `extern "custom"` function
+    std::arch::naked_asm!("")
+}
+
+#[unsafe(naked)]
+unsafe extern "custom" fn no_parameters(a: i64) {
+    //~^ ERROR invalid signature for `extern "custom"` function
+    std::arch::naked_asm!("")
+}
+
+#[unsafe(naked)]
+unsafe extern "custom" fn no_return_type() -> i64 {
+    //~^ ERROR invalid signature for `extern "custom"` function
+    std::arch::naked_asm!("")
+}
+
+unsafe extern "custom" fn double(a: i64) -> i64 {
+    //~^ ERROR items with the `"custom"` ABI can only be declared externally or defined via naked functions
+    //~| ERROR invalid signature for `extern "custom"` function
+    unimplemented!()
+}
+
+struct Thing(i64);
+
+impl Thing {
+    unsafe extern "custom" fn is_even(self) -> bool {
+        //~^ ERROR items with the `"custom"` ABI can only be declared externally or defined via naked functions
+        //~| ERROR invalid signature for `extern "custom"` function
+        unimplemented!()
+    }
+}
+
+trait BitwiseNot {
+    unsafe extern "custom" fn bitwise_not(a: i64) -> i64 {
+        //~^ ERROR items with the `"custom"` ABI can only be declared externally or defined via naked functions
+        //~| ERROR invalid signature for `extern "custom"` function
+        unimplemented!()
+    }
+}
+
+impl BitwiseNot for Thing {}
+
+trait Negate {
+    extern "custom" fn negate(a: i64) -> i64;
+    //~^ ERROR functions with the `"custom"` ABI must be unsafe
+    //~| ERROR invalid signature for `extern "custom"` function
+}
+
+impl Negate for Thing {
+    extern "custom" fn negate(a: i64) -> i64 {
+        //~^ ERROR items with the `"custom"` ABI can only be declared externally or defined via naked functions
+        //~| ERROR functions with the `"custom"` ABI must be unsafe
+        //~| ERROR invalid signature for `extern "custom"` function
+        -a
+    }
+}
+
+unsafe extern "custom" {
+    fn increment(a: i64) -> i64;
+    //~^ ERROR invalid signature for `extern "custom"` function
+
+    safe fn extern_cannot_be_safe();
+    //~^ ERROR foreign functions with the `"custom"` ABI cannot be safe
+}
+
+fn caller(f: unsafe extern "custom" fn(i64) -> i64, mut x: i64) -> i64 {
+    unsafe { f(x) }
+    //~^ ERROR functions with the `"custom"` ABI cannot be called
+}
+
+fn caller_by_ref(f: &unsafe extern "custom" fn(i64) -> i64, mut x: i64) -> i64 {
+    unsafe { f(x) }
+    //~^ ERROR functions with the `"custom"` ABI cannot be called
+}
+
+type Custom = unsafe extern "custom" fn(i64) -> i64;
+
+fn caller_alias(f: Custom, mut x: i64) -> i64 {
+    unsafe { f(x) }
+    //~^ ERROR functions with the `"custom"` ABI cannot be called
+}
+
+#[unsafe(naked)]
+const unsafe extern "custom" fn no_const_fn() {
+    std::arch::naked_asm!("")
+    //~^ ERROR inline assembly is not allowed in constant functions
+}
+
+async unsafe extern "custom" fn no_async_fn() {
+    //~^ ERROR items with the `"custom"` ABI can only be declared externally or defined via naked functions
+    //~| ERROR functions with the `"custom"` ABI cannot be `async`
+}
+
+fn no_promotion_to_fn_trait(f: unsafe extern "custom" fn()) -> impl Fn()  {
+    //~^ ERROR expected a `Fn()` closure, found `unsafe extern "custom" fn()`
+    f
+}
+
+pub fn main() {
+    unsafe {
+        assert_eq!(double(21), 42);
+        //~^ ERROR functions with the `"custom"` ABI cannot be called
+
+        assert_eq!(unsafe { increment(41) }, 42);
+        //~^ ERROR functions with the `"custom"` ABI cannot be called
+
+        assert!(Thing(41).is_even());
+        //~^ ERROR functions with the `"custom"` ABI cannot be called
+
+        assert_eq!(Thing::bitwise_not(42), !42);
+        //~^ ERROR functions with the `"custom"` ABI cannot be called
+    }
+}
diff --git a/tests/ui/abi/bad-custom.stderr b/tests/ui/abi/bad-custom.stderr
new file mode 100644
index 00000000000..ec0f11af898
--- /dev/null
+++ b/tests/ui/abi/bad-custom.stderr
@@ -0,0 +1,299 @@
+error: functions with the `"custom"` ABI must be unsafe
+  --> $DIR/bad-custom.rs:7:1
+   |
+LL | extern "custom" fn must_be_unsafe(a: i64) -> i64 {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: add the `unsafe` keyword to this definition
+   |
+LL | unsafe extern "custom" fn must_be_unsafe(a: i64) -> i64 {
+   | ++++++
+
+error: invalid signature for `extern "custom"` function
+  --> $DIR/bad-custom.rs:7:35
+   |
+LL | extern "custom" fn must_be_unsafe(a: i64) -> i64 {
+   |                                   ^^^^^^     ^^^
+   |
+   = note: functions with the `"custom"` ABI cannot have any parameters or return type
+help: remove the parameters and return type
+   |
+LL - extern "custom" fn must_be_unsafe(a: i64) -> i64 {
+LL + extern "custom" fn must_be_unsafe() {
+   |
+
+error: invalid signature for `extern "custom"` function
+  --> $DIR/bad-custom.rs:14:41
+   |
+LL | unsafe extern "custom" fn no_parameters(a: i64) {
+   |                                         ^^^^^^
+   |
+   = note: functions with the `"custom"` ABI cannot have any parameters or return type
+help: remove the parameters and return type
+   |
+LL - unsafe extern "custom" fn no_parameters(a: i64) {
+LL + unsafe extern "custom" fn no_parameters() {
+   |
+
+error: invalid signature for `extern "custom"` function
+  --> $DIR/bad-custom.rs:20:47
+   |
+LL | unsafe extern "custom" fn no_return_type() -> i64 {
+   |                                               ^^^
+   |
+   = note: functions with the `"custom"` ABI cannot have any parameters or return type
+help: remove the parameters and return type
+   |
+LL - unsafe extern "custom" fn no_return_type() -> i64 {
+LL + unsafe extern "custom" fn no_return_type() {
+   |
+
+error: invalid signature for `extern "custom"` function
+  --> $DIR/bad-custom.rs:25:34
+   |
+LL | unsafe extern "custom" fn double(a: i64) -> i64 {
+   |                                  ^^^^^^     ^^^
+   |
+   = note: functions with the `"custom"` ABI cannot have any parameters or return type
+help: remove the parameters and return type
+   |
+LL - unsafe extern "custom" fn double(a: i64) -> i64 {
+LL + unsafe extern "custom" fn double() {
+   |
+
+error: invalid signature for `extern "custom"` function
+  --> $DIR/bad-custom.rs:34:39
+   |
+LL |     unsafe extern "custom" fn is_even(self) -> bool {
+   |                                       ^^^^     ^^^^
+   |
+   = note: functions with the `"custom"` ABI cannot have any parameters or return type
+help: remove the parameters and return type
+   |
+LL -     unsafe extern "custom" fn is_even(self) -> bool {
+LL +     unsafe extern "custom" fn is_even() {
+   |
+
+error: invalid signature for `extern "custom"` function
+  --> $DIR/bad-custom.rs:42:43
+   |
+LL |     unsafe extern "custom" fn bitwise_not(a: i64) -> i64 {
+   |                                           ^^^^^^     ^^^
+   |
+   = note: functions with the `"custom"` ABI cannot have any parameters or return type
+help: remove the parameters and return type
+   |
+LL -     unsafe extern "custom" fn bitwise_not(a: i64) -> i64 {
+LL +     unsafe extern "custom" fn bitwise_not() {
+   |
+
+error: functions with the `"custom"` ABI must be unsafe
+  --> $DIR/bad-custom.rs:52:5
+   |
+LL |     extern "custom" fn negate(a: i64) -> i64;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: add the `unsafe` keyword to this definition
+   |
+LL |     unsafe extern "custom" fn negate(a: i64) -> i64;
+   |     ++++++
+
+error: invalid signature for `extern "custom"` function
+  --> $DIR/bad-custom.rs:52:31
+   |
+LL |     extern "custom" fn negate(a: i64) -> i64;
+   |                               ^^^^^^     ^^^
+   |
+   = note: functions with the `"custom"` ABI cannot have any parameters or return type
+help: remove the parameters and return type
+   |
+LL -     extern "custom" fn negate(a: i64) -> i64;
+LL +     extern "custom" fn negate();
+   |
+
+error: functions with the `"custom"` ABI must be unsafe
+  --> $DIR/bad-custom.rs:58:5
+   |
+LL |     extern "custom" fn negate(a: i64) -> i64 {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: add the `unsafe` keyword to this definition
+   |
+LL |     unsafe extern "custom" fn negate(a: i64) -> i64 {
+   |     ++++++
+
+error: invalid signature for `extern "custom"` function
+  --> $DIR/bad-custom.rs:58:31
+   |
+LL |     extern "custom" fn negate(a: i64) -> i64 {
+   |                               ^^^^^^     ^^^
+   |
+   = note: functions with the `"custom"` ABI cannot have any parameters or return type
+help: remove the parameters and return type
+   |
+LL -     extern "custom" fn negate(a: i64) -> i64 {
+LL +     extern "custom" fn negate() {
+   |
+
+error: invalid signature for `extern "custom"` function
+  --> $DIR/bad-custom.rs:67:18
+   |
+LL |     fn increment(a: i64) -> i64;
+   |                  ^^^^^^     ^^^
+   |
+   = note: functions with the `"custom"` ABI cannot have any parameters or return type
+help: remove the parameters and return type
+   |
+LL -     fn increment(a: i64) -> i64;
+LL +     fn increment();
+   |
+
+error: foreign functions with the `"custom"` ABI cannot be safe
+  --> $DIR/bad-custom.rs:70:5
+   |
+LL |     safe fn extern_cannot_be_safe();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove the `safe` keyword from this definition
+   |
+LL -     safe fn extern_cannot_be_safe();
+LL +     fn extern_cannot_be_safe();
+   |
+
+error: functions with the `"custom"` ABI cannot be `async`
+  --> $DIR/bad-custom.rs:97:1
+   |
+LL | async unsafe extern "custom" fn no_async_fn() {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove the `async` keyword from this definiton
+   |
+LL - async unsafe extern "custom" fn no_async_fn() {
+LL + unsafe extern "custom" fn no_async_fn() {
+   |
+
+error: items with the `"custom"` ABI can only be declared externally or defined via naked functions
+  --> $DIR/bad-custom.rs:97:1
+   |
+LL | async unsafe extern "custom" fn no_async_fn() {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: convert this to an `#[unsafe(naked)]` function
+   |
+LL + #[unsafe(naked)]
+LL | async unsafe extern "custom" fn no_async_fn() {
+   |
+
+error[E0277]: expected a `Fn()` closure, found `unsafe extern "custom" fn()`
+  --> $DIR/bad-custom.rs:102:64
+   |
+LL | fn no_promotion_to_fn_trait(f: unsafe extern "custom" fn()) -> impl Fn()  {
+   |                                                                ^^^^^^^^^ call the function in a closure: `|| unsafe { /* code */ }`
+LL |
+LL |     f
+   |     - return type was inferred to be `unsafe extern "custom" fn()` here
+   |
+   = help: the trait `Fn()` is not implemented for `unsafe extern "custom" fn()`
+   = note: unsafe function cannot be called generically without an unsafe block
+   = note: wrap the `unsafe extern "custom" fn()` in a closure with no arguments: `|| { /* code */ }`
+
+error: items with the `"custom"` ABI can only be declared externally or defined via naked functions
+  --> $DIR/bad-custom.rs:25:1
+   |
+LL | unsafe extern "custom" fn double(a: i64) -> i64 {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: convert this to an `#[unsafe(naked)]` function
+   |
+LL + #[unsafe(naked)]
+LL | unsafe extern "custom" fn double(a: i64) -> i64 {
+   |
+
+error: items with the `"custom"` ABI can only be declared externally or defined via naked functions
+  --> $DIR/bad-custom.rs:34:5
+   |
+LL |     unsafe extern "custom" fn is_even(self) -> bool {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: convert this to an `#[unsafe(naked)]` function
+   |
+LL +     #[unsafe(naked)]
+LL |     unsafe extern "custom" fn is_even(self) -> bool {
+   |
+
+error: items with the `"custom"` ABI can only be declared externally or defined via naked functions
+  --> $DIR/bad-custom.rs:42:5
+   |
+LL |     unsafe extern "custom" fn bitwise_not(a: i64) -> i64 {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: convert this to an `#[unsafe(naked)]` function
+   |
+LL +     #[unsafe(naked)]
+LL |     unsafe extern "custom" fn bitwise_not(a: i64) -> i64 {
+   |
+
+error: items with the `"custom"` ABI can only be declared externally or defined via naked functions
+  --> $DIR/bad-custom.rs:58:5
+   |
+LL |     extern "custom" fn negate(a: i64) -> i64 {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: convert this to an `#[unsafe(naked)]` function
+   |
+LL +     #[unsafe(naked)]
+LL |     extern "custom" fn negate(a: i64) -> i64 {
+   |
+
+error: functions with the `"custom"` ABI cannot be called
+  --> $DIR/bad-custom.rs:75:14
+   |
+LL |     unsafe { f(x) }
+   |              ^^^^
+
+error: functions with the `"custom"` ABI cannot be called
+  --> $DIR/bad-custom.rs:80:14
+   |
+LL |     unsafe { f(x) }
+   |              ^^^^
+
+error: functions with the `"custom"` ABI cannot be called
+  --> $DIR/bad-custom.rs:87:14
+   |
+LL |     unsafe { f(x) }
+   |              ^^^^
+
+error: functions with the `"custom"` ABI cannot be called
+  --> $DIR/bad-custom.rs:109:20
+   |
+LL |         assert_eq!(double(21), 42);
+   |                    ^^^^^^^^^^
+
+error: functions with the `"custom"` ABI cannot be called
+  --> $DIR/bad-custom.rs:112:29
+   |
+LL |         assert_eq!(unsafe { increment(41) }, 42);
+   |                             ^^^^^^^^^^^^^
+
+error: functions with the `"custom"` ABI cannot be called
+  --> $DIR/bad-custom.rs:115:17
+   |
+LL |         assert!(Thing(41).is_even());
+   |                 ^^^^^^^^^^^^^^^^^^^
+
+error: functions with the `"custom"` ABI cannot be called
+  --> $DIR/bad-custom.rs:118:20
+   |
+LL |         assert_eq!(Thing::bitwise_not(42), !42);
+   |                    ^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0015]: inline assembly is not allowed in constant functions
+  --> $DIR/bad-custom.rs:93:5
+   |
+LL |     std::arch::naked_asm!("")
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 28 previous errors
+
+Some errors have detailed explanations: E0015, E0277.
+For more information about an error, try `rustc --explain E0015`.
diff --git a/tests/ui/abi/custom.rs b/tests/ui/abi/custom.rs
new file mode 100644
index 00000000000..0f6ff77f580
--- /dev/null
+++ b/tests/ui/abi/custom.rs
@@ -0,0 +1,88 @@
+// Test that `extern "custom"` functions can be called from assembly, and defined using a naked
+// function, and `global_asm!` with an `extern "custom"` block.
+//
+//@ run-pass
+//@ only-x86_64
+#![feature(abi_custom)]
+
+use std::arch::{asm, global_asm, naked_asm};
+
+#[unsafe(naked)]
+unsafe extern "custom" fn double() {
+    naked_asm!("add rax, rax", "ret");
+}
+
+global_asm!(
+    // work around macOS prefixing symbols with _
+    "    .globl  {0}",
+    "{0}:",
+    "    add     rax, 1",
+    "    ret",
+    sym increment,
+);
+
+unsafe extern "custom" {
+    fn increment();
+}
+
+#[repr(transparent)]
+struct Thing(u64);
+
+impl Thing {
+    #[unsafe(naked)]
+    unsafe extern "custom" fn is_even() {
+        naked_asm!("test al, 1", "sete al", "ret");
+    }
+}
+
+trait BitwiseNot {
+    #[unsafe(naked)]
+    unsafe extern "custom" fn bitwise_not() {
+        naked_asm!("not rax", "ret");
+    }
+}
+
+impl BitwiseNot for Thing {}
+
+#[unsafe(naked)]
+unsafe extern "C" fn const_generic<const N: u64>() {
+    naked_asm!(
+        "mov rax, {}",
+        "ret",
+        const N,
+    );
+}
+
+pub fn main() {
+    let mut x: u64 = 21;
+    unsafe { asm!("call {}", sym double, inout("rax") x) };
+    assert_eq!(x, 42);
+
+    let mut x: u64 = 41;
+    unsafe { asm!("call {}", sym increment, inout("rax") x) };
+    assert_eq!(x, 42);
+
+    let mut x: u8;
+    unsafe { asm!("call {}", sym Thing::is_even, inout("al") 42u8 => x) };
+    assert!(x != 0);
+
+    let mut x: u64 = 42;
+    unsafe { asm!("call {}", sym Thing::bitwise_not, inout("rax") x) };
+    assert_eq!(x, !42);
+
+    // Create and call in `asm!` an `extern "custom"` function pointer.
+    fn caller(f: unsafe extern "custom" fn(), mut x: u64) -> u64 {
+        unsafe { asm!("call {}", in(reg) f, inout("rax") x) };
+        x
+    }
+
+    assert_eq!(caller(double, 2), 4);
+
+    let x: u64;
+    unsafe { asm!("call {}", sym const_generic::<42>, out("rax") x) };
+    assert_eq!(x, 42);
+
+    let x: u64;
+    unsafe { asm!("call {}", sym const_generic::<84>, out("rax") x) };
+    assert_eq!(x, 84);
+}
diff --git a/tests/ui/abi/unsupported.aarch64.stderr b/tests/ui/abi/unsupported.aarch64.stderr
index 22dca00e3b2..4721c26026d 100644
--- a/tests/ui/abi/unsupported.aarch64.stderr
+++ b/tests/ui/abi/unsupported.aarch64.stderr
@@ -369,42 +369,6 @@ LL | fn stdcall_ptr(f: extern "stdcall" fn()) {
    = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
 
 Future breakage diagnostic:
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:131:17
-   |
-LL | fn cdecl_ptr(f: extern "cdecl" fn()) {
-   |                 ^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: use `extern "C"` instead
-   = note: `#[warn(unsupported_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:136:1
-   |
-LL | extern "cdecl" {}
-   | ^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: use `extern "C"` instead
-   = note: `#[warn(unsupported_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:139:1
-   |
-LL | extern "cdecl-unwind" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: use `extern "C-unwind"` instead
-   = note: `#[warn(unsupported_calling_conventions)]` on by default
-
-Future breakage diagnostic:
 warning: the calling convention "vectorcall" is not supported on this target
   --> $DIR/unsupported.rs:145:22
    |
@@ -437,15 +401,3 @@ LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) {
    = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
    = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
 
-Future breakage diagnostic:
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:128:1
-   |
-LL | extern "cdecl" fn cdecl() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: use `extern "C"` instead
-   = note: `#[warn(unsupported_calling_conventions)]` on by default
-
diff --git a/tests/ui/abi/unsupported.arm.stderr b/tests/ui/abi/unsupported.arm.stderr
index 0ac6d888f8d..ed9cd2ab2c5 100644
--- a/tests/ui/abi/unsupported.arm.stderr
+++ b/tests/ui/abi/unsupported.arm.stderr
@@ -337,42 +337,6 @@ LL | fn stdcall_ptr(f: extern "stdcall" fn()) {
    = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
 
 Future breakage diagnostic:
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:131:17
-   |
-LL | fn cdecl_ptr(f: extern "cdecl" fn()) {
-   |                 ^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: use `extern "C"` instead
-   = note: `#[warn(unsupported_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:136:1
-   |
-LL | extern "cdecl" {}
-   | ^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: use `extern "C"` instead
-   = note: `#[warn(unsupported_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:139:1
-   |
-LL | extern "cdecl-unwind" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: use `extern "C-unwind"` instead
-   = note: `#[warn(unsupported_calling_conventions)]` on by default
-
-Future breakage diagnostic:
 warning: the calling convention "vectorcall" is not supported on this target
   --> $DIR/unsupported.rs:145:22
    |
@@ -405,15 +369,3 @@ LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) {
    = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
    = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
 
-Future breakage diagnostic:
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:128:1
-   |
-LL | extern "cdecl" fn cdecl() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: use `extern "C"` instead
-   = note: `#[warn(unsupported_calling_conventions)]` on by default
-
diff --git a/tests/ui/abi/unsupported.riscv32.stderr b/tests/ui/abi/unsupported.riscv32.stderr
index ad57a89e455..9e75dfafca0 100644
--- a/tests/ui/abi/unsupported.riscv32.stderr
+++ b/tests/ui/abi/unsupported.riscv32.stderr
@@ -337,42 +337,6 @@ LL | fn stdcall_ptr(f: extern "stdcall" fn()) {
    = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
 
 Future breakage diagnostic:
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:131:17
-   |
-LL | fn cdecl_ptr(f: extern "cdecl" fn()) {
-   |                 ^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: use `extern "C"` instead
-   = note: `#[warn(unsupported_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:136:1
-   |
-LL | extern "cdecl" {}
-   | ^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: use `extern "C"` instead
-   = note: `#[warn(unsupported_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:139:1
-   |
-LL | extern "cdecl-unwind" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: use `extern "C-unwind"` instead
-   = note: `#[warn(unsupported_calling_conventions)]` on by default
-
-Future breakage diagnostic:
 warning: the calling convention "vectorcall" is not supported on this target
   --> $DIR/unsupported.rs:145:22
    |
@@ -405,15 +369,3 @@ LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) {
    = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
    = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
 
-Future breakage diagnostic:
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:128:1
-   |
-LL | extern "cdecl" fn cdecl() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: use `extern "C"` instead
-   = note: `#[warn(unsupported_calling_conventions)]` on by default
-
diff --git a/tests/ui/abi/unsupported.riscv64.stderr b/tests/ui/abi/unsupported.riscv64.stderr
index ad57a89e455..9e75dfafca0 100644
--- a/tests/ui/abi/unsupported.riscv64.stderr
+++ b/tests/ui/abi/unsupported.riscv64.stderr
@@ -337,42 +337,6 @@ LL | fn stdcall_ptr(f: extern "stdcall" fn()) {
    = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
 
 Future breakage diagnostic:
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:131:17
-   |
-LL | fn cdecl_ptr(f: extern "cdecl" fn()) {
-   |                 ^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: use `extern "C"` instead
-   = note: `#[warn(unsupported_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:136:1
-   |
-LL | extern "cdecl" {}
-   | ^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: use `extern "C"` instead
-   = note: `#[warn(unsupported_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:139:1
-   |
-LL | extern "cdecl-unwind" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: use `extern "C-unwind"` instead
-   = note: `#[warn(unsupported_calling_conventions)]` on by default
-
-Future breakage diagnostic:
 warning: the calling convention "vectorcall" is not supported on this target
   --> $DIR/unsupported.rs:145:22
    |
@@ -405,15 +369,3 @@ LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) {
    = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
    = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
 
-Future breakage diagnostic:
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:128:1
-   |
-LL | extern "cdecl" fn cdecl() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: use `extern "C"` instead
-   = note: `#[warn(unsupported_calling_conventions)]` on by default
-
diff --git a/tests/ui/abi/unsupported.x64.stderr b/tests/ui/abi/unsupported.x64.stderr
index f777fe86e24..5b55e5707fa 100644
--- a/tests/ui/abi/unsupported.x64.stderr
+++ b/tests/ui/abi/unsupported.x64.stderr
@@ -316,42 +316,6 @@ LL | fn stdcall_ptr(f: extern "stdcall" fn()) {
    = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
 
 Future breakage diagnostic:
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:131:17
-   |
-LL | fn cdecl_ptr(f: extern "cdecl" fn()) {
-   |                 ^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: use `extern "C"` instead
-   = note: `#[warn(unsupported_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:136:1
-   |
-LL | extern "cdecl" {}
-   | ^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: use `extern "C"` instead
-   = note: `#[warn(unsupported_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:139:1
-   |
-LL | extern "cdecl-unwind" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: use `extern "C-unwind"` instead
-   = note: `#[warn(unsupported_calling_conventions)]` on by default
-
-Future breakage diagnostic:
 warning: the calling convention "C-cmse-nonsecure-call" is not supported on this target
   --> $DIR/unsupported.rs:153:21
    |
@@ -373,15 +337,3 @@ LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) {
    = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
    = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
 
-Future breakage diagnostic:
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:128:1
-   |
-LL | extern "cdecl" fn cdecl() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: use `extern "C"` instead
-   = note: `#[warn(unsupported_calling_conventions)]` on by default
-
diff --git a/tests/ui/abi/unsupported.x64_win.stderr b/tests/ui/abi/unsupported.x64_win.stderr
index 328f4c3b043..93b5a272e92 100644
--- a/tests/ui/abi/unsupported.x64_win.stderr
+++ b/tests/ui/abi/unsupported.x64_win.stderr
@@ -322,78 +322,6 @@ LL | fn thiscall_ptr(f: extern "thiscall" fn()) {
    = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
 
 Future breakage diagnostic:
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:112:19
-   |
-LL | fn stdcall_ptr(f: extern "stdcall" fn()) {
-   |                   ^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"`
-   = note: `#[warn(unsupported_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:119:1
-   |
-LL | extern "stdcall" {}
-   | ^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"`
-   = note: `#[warn(unsupported_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:123:1
-   |
-LL | extern "stdcall-unwind" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: if you need `extern "stdcall-unwind"` on win32 and `extern "C-unwind"` everywhere else, use `extern "system-unwind"`
-   = note: `#[warn(unsupported_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:131:17
-   |
-LL | fn cdecl_ptr(f: extern "cdecl" fn()) {
-   |                 ^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: use `extern "C"` instead
-   = note: `#[warn(unsupported_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:136:1
-   |
-LL | extern "cdecl" {}
-   | ^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: use `extern "C"` instead
-   = note: `#[warn(unsupported_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:139:1
-   |
-LL | extern "cdecl-unwind" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: use `extern "C-unwind"` instead
-   = note: `#[warn(unsupported_calling_conventions)]` on by default
-
-Future breakage diagnostic:
 warning: the calling convention "C-cmse-nonsecure-call" is not supported on this target
   --> $DIR/unsupported.rs:153:21
    |
@@ -415,39 +343,3 @@ LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) {
    = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
    = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
 
-Future breakage diagnostic:
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:171:1
-   |
-LL | extern "cdecl" {}
-   | ^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: use `extern "C"` instead
-   = note: `#[warn(unsupported_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:108:1
-   |
-LL | extern "stdcall" fn stdcall() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"`
-   = note: `#[warn(unsupported_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:128:1
-   |
-LL | extern "cdecl" fn cdecl() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: use `extern "C"` instead
-   = note: `#[warn(unsupported_calling_conventions)]` on by default
-
diff --git a/tests/ui/associated-types/associated-type-call.fixed b/tests/ui/associated-types/associated-type-call.fixed
new file mode 100644
index 00000000000..d450b3b82c9
--- /dev/null
+++ b/tests/ui/associated-types/associated-type-call.fixed
@@ -0,0 +1,22 @@
+// issue: <https://github.com/rust-lang/rust/issues/142473>
+//
+//@ run-rustfix
+#![allow(unused)]
+struct T();
+
+trait Trait {
+    type Assoc;
+
+    fn f();
+}
+
+impl Trait for () {
+    type Assoc = T;
+
+    fn f() {
+        T();
+        //~^ ERROR no associated item named `Assoc` found for unit type `()` in the current scope
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/associated-types/associated-type-call.rs b/tests/ui/associated-types/associated-type-call.rs
new file mode 100644
index 00000000000..ffe540c329e
--- /dev/null
+++ b/tests/ui/associated-types/associated-type-call.rs
@@ -0,0 +1,22 @@
+// issue: <https://github.com/rust-lang/rust/issues/142473>
+//
+//@ run-rustfix
+#![allow(unused)]
+struct T();
+
+trait Trait {
+    type Assoc;
+
+    fn f();
+}
+
+impl Trait for () {
+    type Assoc = T;
+
+    fn f() {
+        <Self>::Assoc();
+        //~^ ERROR no associated item named `Assoc` found for unit type `()` in the current scope
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/associated-types/associated-type-call.stderr b/tests/ui/associated-types/associated-type-call.stderr
new file mode 100644
index 00000000000..eaef775e304
--- /dev/null
+++ b/tests/ui/associated-types/associated-type-call.stderr
@@ -0,0 +1,15 @@
+error[E0599]: no associated item named `Assoc` found for unit type `()` in the current scope
+  --> $DIR/associated-type-call.rs:17:17
+   |
+LL |         <Self>::Assoc();
+   |                 ^^^^^ associated item not found in `()`
+   |
+help: to construct a value of type `T`, use the explicit path
+   |
+LL -         <Self>::Assoc();
+LL +         T();
+   |
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0599`.
diff --git a/tests/ui/associated-types/associated-types-unsized.stderr b/tests/ui/associated-types/associated-types-unsized.stderr
index e46b2a39464..5a55e0341c1 100644
--- a/tests/ui/associated-types/associated-types-unsized.stderr
+++ b/tests/ui/associated-types/associated-types-unsized.stderr
@@ -6,7 +6,6 @@ LL |     let x = t.get();
    |
    = help: the trait `Sized` is not implemented for `<T as Get>::Value`
    = note: all local variables must have a statically known size
-   = help: unsized locals are gated as an unstable feature
 help: consider further restricting the associated type
    |
 LL | fn foo<T:Get>(t: T) where <T as Get>::Value: Sized {
diff --git a/tests/ui/async-await/awaiting-unsized-param.rs b/tests/ui/async-await/awaiting-unsized-param.rs
index 45611eae41f..d957e5bd9b7 100644
--- a/tests/ui/async-await/awaiting-unsized-param.rs
+++ b/tests/ui/async-await/awaiting-unsized-param.rs
@@ -1,12 +1,11 @@
 //@ edition: 2021
 
-#![feature(unsized_fn_params, unsized_locals)]
-//~^ WARN the feature `unsized_locals` is incomplete
+#![feature(unsized_fn_params)]
 
 use std::future::Future;
 
 async fn bug<T>(mut f: dyn Future<Output = T> + Unpin) -> T {
-    //~^ ERROR the size for values of type `(dyn Future<Output = T> + Unpin + 'static)` cannot be known at compilation time
+    //~^ ERROR the size for values of type `dyn Future<Output = T> + Unpin` cannot be known at compilation time
     (&mut f).await
 }
 
diff --git a/tests/ui/async-await/awaiting-unsized-param.stderr b/tests/ui/async-await/awaiting-unsized-param.stderr
index 0104736976d..bcb0bcdf71a 100644
--- a/tests/ui/async-await/awaiting-unsized-param.stderr
+++ b/tests/ui/async-await/awaiting-unsized-param.stderr
@@ -1,21 +1,12 @@
-warning: the feature `unsized_locals` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/awaiting-unsized-param.rs:3:31
-   |
-LL | #![feature(unsized_fn_params, unsized_locals)]
-   |                               ^^^^^^^^^^^^^^
-   |
-   = note: see issue #48055 <https://github.com/rust-lang/rust/issues/48055> for more information
-   = note: `#[warn(incomplete_features)]` on by default
-
-error[E0277]: the size for values of type `(dyn Future<Output = T> + Unpin + 'static)` cannot be known at compilation time
-  --> $DIR/awaiting-unsized-param.rs:8:17
+error[E0277]: the size for values of type `dyn Future<Output = T> + Unpin` cannot be known at compilation time
+  --> $DIR/awaiting-unsized-param.rs:7:17
    |
 LL | async fn bug<T>(mut f: dyn Future<Output = T> + Unpin) -> T {
    |                 ^^^^^ doesn't have a size known at compile-time
    |
-   = help: the trait `Sized` is not implemented for `(dyn Future<Output = T> + Unpin + 'static)`
-   = note: all values captured by value by a closure must have a statically known size
+   = help: the trait `Sized` is not implemented for `dyn Future<Output = T> + Unpin`
+   = note: all local variables must have a statically known size
 
-error: aborting due to 1 previous error; 1 warning emitted
+error: aborting due to 1 previous error
 
 For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/async-await/unsized-across-await.rs b/tests/ui/async-await/unsized-across-await.rs
index b6bd5567fb2..3e8d58d4243 100644
--- a/tests/ui/async-await/unsized-across-await.rs
+++ b/tests/ui/async-await/unsized-across-await.rs
@@ -1,8 +1,5 @@
 //@ edition: 2021
 
-#![feature(unsized_locals)]
-//~^ WARN the feature `unsized_locals` is incomplete
-
 async fn f() {}
 
 async fn g(x: Box<dyn std::fmt::Display>) {
diff --git a/tests/ui/async-await/unsized-across-await.stderr b/tests/ui/async-await/unsized-across-await.stderr
index 5bb2b7f4791..f06c390ae16 100644
--- a/tests/ui/async-await/unsized-across-await.stderr
+++ b/tests/ui/async-await/unsized-across-await.stderr
@@ -1,21 +1,17 @@
-warning: the feature `unsized_locals` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/unsized-across-await.rs:3:12
-   |
-LL | #![feature(unsized_locals)]
-   |            ^^^^^^^^^^^^^^
-   |
-   = note: see issue #48055 <https://github.com/rust-lang/rust/issues/48055> for more information
-   = note: `#[warn(incomplete_features)]` on by default
-
 error[E0277]: the size for values of type `dyn std::fmt::Display` cannot be known at compilation time
-  --> $DIR/unsized-across-await.rs:9:9
+  --> $DIR/unsized-across-await.rs:6:9
    |
 LL |     let _x = *x;
    |         ^^ doesn't have a size known at compile-time
    |
    = help: the trait `Sized` is not implemented for `dyn std::fmt::Display`
-   = note: all values live across `await` must have a statically known size
+   = note: all local variables must have a statically known size
+help: references are always `Sized`, even if they point to unsized data; consider not dereferencing the expression
+   |
+LL -     let _x = *x;
+LL +     let _x = x;
+   |
 
-error: aborting due to 1 previous error; 1 warning emitted
+error: aborting due to 1 previous error
 
 For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/borrowck/issue-47646.stderr b/tests/ui/borrowck/issue-47646.stderr
index 85adfc03d10..cfe6f3f3993 100644
--- a/tests/ui/borrowck/issue-47646.stderr
+++ b/tests/ui/borrowck/issue-47646.stderr
@@ -11,7 +11,7 @@ LL |             println!("{:?}", heap);
    |                              ^^^^ immutable borrow occurs here
 ...
 LL |     };
-   |      - ... and the mutable borrow might be used here, when that temporary is dropped and runs the destructor for type `(Option<PeekMut<'_, i32>>, ())`
+   |      - ... and the mutable borrow might be used here, when that temporary is dropped and runs the destructor for type `(Option<std::collections::binary_heap::PeekMut<'_, i32>>, ())`
    |
    = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
diff --git a/tests/ui/borrowck/issue-85581.stderr b/tests/ui/borrowck/issue-85581.stderr
index 80f1f4cb509..5fd457eb8dd 100644
--- a/tests/ui/borrowck/issue-85581.stderr
+++ b/tests/ui/borrowck/issue-85581.stderr
@@ -10,7 +10,7 @@ LL |         Some(_) => { heap.pop(); },
    |                      ^^^^ second mutable borrow occurs here
 ...
 LL | }
-   | - ... and the first borrow might be used here, when that temporary is dropped and runs the destructor for type `Option<PeekMut<'_, i32>>`
+   | - ... and the first borrow might be used here, when that temporary is dropped and runs the destructor for type `Option<std::collections::binary_heap::PeekMut<'_, i32>>`
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.rs b/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.rs
index 58370bff220..1e0576b2186 100644
--- a/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.rs
+++ b/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.rs
@@ -1,15 +1,15 @@
 //@ only-x86_64
 
 fn efiapi(f: extern "efiapi" fn(usize, ...)) {
-    //~^ ERROR: using calling conventions other than `C` or `cdecl` for varargs functions is unstable
+    //~^ ERROR: unstable
     f(22, 44);
 }
 fn sysv(f: extern "sysv64" fn(usize, ...)) {
-    //~^ ERROR: using calling conventions other than `C` or `cdecl` for varargs functions is unstable
+    //~^ ERROR: unstable
     f(22, 44);
 }
 fn win(f: extern "win64" fn(usize, ...)) {
-    //~^ ERROR: using calling conventions other than `C` or `cdecl` for varargs functions is unstable
+    //~^ ERROR: unstable
     f(22, 44);
 }
 
diff --git a/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.stderr b/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.stderr
index 9565575dc42..7ef54b639ad 100644
--- a/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.stderr
+++ b/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.stderr
@@ -1,4 +1,4 @@
-error[E0658]: using calling conventions other than `C` or `cdecl` for varargs functions is unstable
+error[E0658]: C-variadic functions with the "efiapi" calling convention are unstable
   --> $DIR/feature-gate-extended_varargs_abi_support.rs:3:14
    |
 LL | fn efiapi(f: extern "efiapi" fn(usize, ...)) {
@@ -8,7 +8,7 @@ LL | fn efiapi(f: extern "efiapi" fn(usize, ...)) {
    = help: add `#![feature(extended_varargs_abi_support)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
-error[E0658]: using calling conventions other than `C` or `cdecl` for varargs functions is unstable
+error[E0658]: C-variadic functions with the "sysv64" calling convention are unstable
   --> $DIR/feature-gate-extended_varargs_abi_support.rs:7:12
    |
 LL | fn sysv(f: extern "sysv64" fn(usize, ...)) {
@@ -18,7 +18,7 @@ LL | fn sysv(f: extern "sysv64" fn(usize, ...)) {
    = help: add `#![feature(extended_varargs_abi_support)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
-error[E0658]: using calling conventions other than `C` or `cdecl` for varargs functions is unstable
+error[E0658]: C-variadic functions with the "win64" calling convention are unstable
   --> $DIR/feature-gate-extended_varargs_abi_support.rs:11:11
    |
 LL | fn win(f: extern "win64" fn(usize, ...)) {
diff --git a/tests/ui/c-variadic/variadic-ffi-1.rs b/tests/ui/c-variadic/variadic-ffi-1.rs
index 9dcd55d13e3..cd8f2a951ef 100644
--- a/tests/ui/c-variadic/variadic-ffi-1.rs
+++ b/tests/ui/c-variadic/variadic-ffi-1.rs
@@ -9,8 +9,7 @@ use minicore::*;
 
 extern "stdcall" {
     fn printf(_: *const u8, ...);
-    //~^ ERROR: C-variadic function must have a compatible calling convention,
-    // like C, cdecl, win64, sysv64 or efiapi
+    //~^ ERROR: C-variadic functions with the "stdcall" calling convention are not supported
 }
 
 extern "C" {
diff --git a/tests/ui/c-variadic/variadic-ffi-1.stderr b/tests/ui/c-variadic/variadic-ffi-1.stderr
index f99abed0a62..a49fc0ce126 100644
--- a/tests/ui/c-variadic/variadic-ffi-1.stderr
+++ b/tests/ui/c-variadic/variadic-ffi-1.stderr
@@ -1,17 +1,17 @@
-error[E0045]: C-variadic function must have a compatible calling convention, like `C`, `cdecl`, `system`, `aapcs`, `win64`, `sysv64` or `efiapi`
+error[E0045]: C-variadic functions with the "stdcall" calling convention are not supported
   --> $DIR/variadic-ffi-1.rs:11:5
    |
 LL |     fn printf(_: *const u8, ...);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention
 
 error[E0060]: this function takes at least 2 arguments but 0 arguments were supplied
-  --> $DIR/variadic-ffi-1.rs:24:9
+  --> $DIR/variadic-ffi-1.rs:23:9
    |
 LL |         foo();
    |         ^^^-- two arguments of type `isize` and `u8` are missing
    |
 note: function defined here
-  --> $DIR/variadic-ffi-1.rs:17:8
+  --> $DIR/variadic-ffi-1.rs:16:8
    |
 LL |     fn foo(f: isize, x: u8, ...);
    |        ^^^ -         -
@@ -21,13 +21,13 @@ LL |         foo(/* isize */, /* u8 */);
    |             +++++++++++++++++++++
 
 error[E0060]: this function takes at least 2 arguments but 1 argument was supplied
-  --> $DIR/variadic-ffi-1.rs:25:9
+  --> $DIR/variadic-ffi-1.rs:24:9
    |
 LL |         foo(1);
    |         ^^^--- argument #2 of type `u8` is missing
    |
 note: function defined here
-  --> $DIR/variadic-ffi-1.rs:17:8
+  --> $DIR/variadic-ffi-1.rs:16:8
    |
 LL |     fn foo(f: isize, x: u8, ...);
    |        ^^^           -
@@ -37,7 +37,7 @@ LL |         foo(1, /* u8 */);
    |              ++++++++++
 
 error[E0308]: mismatched types
-  --> $DIR/variadic-ffi-1.rs:27:56
+  --> $DIR/variadic-ffi-1.rs:26:56
    |
 LL |         let x: unsafe extern "C" fn(f: isize, x: u8) = foo;
    |                -------------------------------------   ^^^ expected non-variadic fn, found variadic function
@@ -48,7 +48,7 @@ LL |         let x: unsafe extern "C" fn(f: isize, x: u8) = foo;
                  found fn item `unsafe extern "C" fn(_, _, ...) {foo}`
 
 error[E0308]: mismatched types
-  --> $DIR/variadic-ffi-1.rs:28:54
+  --> $DIR/variadic-ffi-1.rs:27:54
    |
 LL |         let y: extern "C" fn(f: isize, x: u8, ...) = bar;
    |                -----------------------------------   ^^^ expected variadic fn, found non-variadic function
@@ -59,7 +59,7 @@ LL |         let y: extern "C" fn(f: isize, x: u8, ...) = bar;
                  found fn item `extern "C" fn(_, _) {bar}`
 
 error[E0617]: can't pass `f32` to variadic function
-  --> $DIR/variadic-ffi-1.rs:30:19
+  --> $DIR/variadic-ffi-1.rs:29:19
    |
 LL |         foo(1, 2, 3f32);
    |                   ^^^^
@@ -70,7 +70,7 @@ LL |         foo(1, 2, 3f32 as c_double);
    |                        +++++++++++
 
 error[E0617]: can't pass `bool` to variadic function
-  --> $DIR/variadic-ffi-1.rs:31:19
+  --> $DIR/variadic-ffi-1.rs:30:19
    |
 LL |         foo(1, 2, true);
    |                   ^^^^
@@ -81,7 +81,7 @@ LL |         foo(1, 2, true as c_int);
    |                        ++++++++
 
 error[E0617]: can't pass `i8` to variadic function
-  --> $DIR/variadic-ffi-1.rs:32:19
+  --> $DIR/variadic-ffi-1.rs:31:19
    |
 LL |         foo(1, 2, 1i8);
    |                   ^^^
@@ -92,7 +92,7 @@ LL |         foo(1, 2, 1i8 as c_int);
    |                       ++++++++
 
 error[E0617]: can't pass `u8` to variadic function
-  --> $DIR/variadic-ffi-1.rs:33:19
+  --> $DIR/variadic-ffi-1.rs:32:19
    |
 LL |         foo(1, 2, 1u8);
    |                   ^^^
@@ -103,7 +103,7 @@ LL |         foo(1, 2, 1u8 as c_uint);
    |                       +++++++++
 
 error[E0617]: can't pass `i16` to variadic function
-  --> $DIR/variadic-ffi-1.rs:34:19
+  --> $DIR/variadic-ffi-1.rs:33:19
    |
 LL |         foo(1, 2, 1i16);
    |                   ^^^^
@@ -114,7 +114,7 @@ LL |         foo(1, 2, 1i16 as c_int);
    |                        ++++++++
 
 error[E0617]: can't pass `u16` to variadic function
-  --> $DIR/variadic-ffi-1.rs:35:19
+  --> $DIR/variadic-ffi-1.rs:34:19
    |
 LL |         foo(1, 2, 1u16);
    |                   ^^^^
diff --git a/tests/ui/c-variadic/variadic-ffi-2.rs b/tests/ui/c-variadic/variadic-ffi-2.rs
index da7bb76fc14..adfd9bfa279 100644
--- a/tests/ui/c-variadic/variadic-ffi-2.rs
+++ b/tests/ui/c-variadic/variadic-ffi-2.rs
@@ -1,8 +1,7 @@
 #![feature(extended_varargs_abi_support)]
 
 fn baz(f: extern "Rust" fn(usize, ...)) {
-    //~^ ERROR: C-variadic function must have a compatible calling convention,
-    // like C, cdecl, system, aapcs, win64, sysv64 or efiapi
+    //~^ ERROR: C-variadic functions with the "Rust" calling convention are not supported
     f(22, 44);
 }
 
diff --git a/tests/ui/c-variadic/variadic-ffi-2.stderr b/tests/ui/c-variadic/variadic-ffi-2.stderr
index 9f8dcefdb03..2ac0a9f5ea2 100644
--- a/tests/ui/c-variadic/variadic-ffi-2.stderr
+++ b/tests/ui/c-variadic/variadic-ffi-2.stderr
@@ -1,4 +1,4 @@
-error[E0045]: C-variadic function must have a compatible calling convention, like `C`, `cdecl`, `system`, `aapcs`, `win64`, `sysv64` or `efiapi`
+error[E0045]: C-variadic functions with the "Rust" calling convention are not supported
   --> $DIR/variadic-ffi-2.rs:3:11
    |
 LL | fn baz(f: extern "Rust" fn(usize, ...)) {
diff --git a/tests/ui/check-cfg/target_feature.stderr b/tests/ui/check-cfg/target_feature.stderr
index ec81ba2e3d8..f29a41d6a8e 100644
--- a/tests/ui/check-cfg/target_feature.stderr
+++ b/tests/ui/check-cfg/target_feature.stderr
@@ -212,6 +212,9 @@ LL |     cfg!(target_feature = "_UNEXPECTED_VALUE");
 `relax`
 `relaxed-simd`
 `reserve-x18`
+`retpoline-external-thunk`
+`retpoline-indirect-branches`
+`retpoline-indirect-calls`
 `rtm`
 `sb`
 `scq`
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs
index 18041b08061..84080890e08 100644
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs
@@ -38,4 +38,4 @@ type WithTransparentTraitObject =
 //~^ ERROR return value of `"C-cmse-nonsecure-call"` function too large to pass via registers [E0798]
 
 type WithVarArgs = extern "C-cmse-nonsecure-call" fn(u32, ...);
-//~^ ERROR C-variadic function must have a compatible calling convention, like `C`
+//~^ ERROR C-variadic functions with the "C-cmse-nonsecure-call" calling convention are not supported
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr
index ab7c9cee4f0..2b51f48915b 100644
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr
@@ -69,7 +69,7 @@ LL |     extern "C-cmse-nonsecure-call" fn(WrapperTransparent) -> WrapperTranspa
    = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
    = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
 
-error[E0045]: C-variadic function must have a compatible calling convention, like `C`, `cdecl`, `system`, `aapcs`, `win64`, `sysv64` or `efiapi`
+error[E0045]: C-variadic functions with the "C-cmse-nonsecure-call" calling convention are not supported
   --> $DIR/generics.rs:40:20
    |
 LL | type WithVarArgs = extern "C-cmse-nonsecure-call" fn(u32, ...);
diff --git a/tests/ui/const-generics/mgca/missing_generic_params.rs b/tests/ui/const-generics/mgca/missing_generic_params.rs
new file mode 100644
index 00000000000..ab1db3364ec
--- /dev/null
+++ b/tests/ui/const-generics/mgca/missing_generic_params.rs
@@ -0,0 +1,16 @@
+// This used to ICE: #137188
+// The missing parameter list on `N` was set to
+// "infer from use site" in ast lowering, which
+// caused later code to not emit a missing generic
+// param error. The missing param was then attempted
+// to be inferred, but inference of generic params
+// is only possible within bodies. So a delayed
+// bug was generated with no error ever reported.
+
+#![feature(min_generic_const_args)]
+#![allow(incomplete_features)]
+trait Trait {}
+impl Trait for [(); N] {}
+//~^ ERROR: missing generics for function `N`
+fn N<T>() {}
+pub fn main() {}
diff --git a/tests/ui/const-generics/mgca/missing_generic_params.stderr b/tests/ui/const-generics/mgca/missing_generic_params.stderr
new file mode 100644
index 00000000000..78010c75621
--- /dev/null
+++ b/tests/ui/const-generics/mgca/missing_generic_params.stderr
@@ -0,0 +1,19 @@
+error[E0107]: missing generics for function `N`
+  --> $DIR/missing_generic_params.rs:13:21
+   |
+LL | impl Trait for [(); N] {}
+   |                     ^ expected 1 generic argument
+   |
+note: function defined here, with 1 generic parameter: `T`
+  --> $DIR/missing_generic_params.rs:15:4
+   |
+LL | fn N<T>() {}
+   |    ^ -
+help: add missing generic argument
+   |
+LL | impl Trait for [(); N<T>] {}
+   |                      +++
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0107`.
diff --git a/tests/ui/consts/auxiliary/unstable_intrinsic.rs b/tests/ui/consts/auxiliary/unstable_intrinsic.rs
index 45631df7859..c1c7571e0c9 100644
--- a/tests/ui/consts/auxiliary/unstable_intrinsic.rs
+++ b/tests/ui/consts/auxiliary/unstable_intrinsic.rs
@@ -8,4 +8,4 @@ pub const unsafe fn size_of_val<T>(x: *const T) -> usize;
 #[unstable(feature = "unstable", issue = "42")]
 #[rustc_const_unstable(feature = "unstable", issue = "42")]
 #[rustc_intrinsic]
-pub const unsafe fn min_align_of_val<T>(x: *const T) -> usize;
+pub const unsafe fn align_of_val<T>(x: *const T) -> usize;
diff --git a/tests/ui/consts/const-adt-align-mismatch.rs b/tests/ui/consts/const-adt-align-mismatch.rs
index 8faddbff30d..6ff74ad3a9c 100644
--- a/tests/ui/consts/const-adt-align-mismatch.rs
+++ b/tests/ui/consts/const-adt-align-mismatch.rs
@@ -18,5 +18,5 @@ static FOO: Foo = Foo::C;
 fn main() {
     assert_eq!(FOO, Foo::C);
     assert_eq!(mem::size_of::<Foo>(), 12);
-    assert_eq!(mem::min_align_of::<Foo>(), 4);
+    assert_eq!(mem::align_of::<Foo>(), 4);
 }
diff --git a/tests/ui/consts/const-mut-refs/mut_ref_in_final.rs b/tests/ui/consts/const-mut-refs/mut_ref_in_final.rs
index 29474c76320..28facc18886 100644
--- a/tests/ui/consts/const-mut-refs/mut_ref_in_final.rs
+++ b/tests/ui/consts/const-mut-refs/mut_ref_in_final.rs
@@ -18,7 +18,7 @@ const B: *mut i32 = &mut 4; //~ ERROR mutable references are not allowed
 const B2: Option<&mut i32> = None;
 
 // Not ok, can't prove that no mutable allocation ends up in final value
-const B3: Option<&mut i32> = Some(&mut 42); //~ ERROR temporary value dropped while borrowed
+const B3: Option<&mut i32> = Some(&mut 42); //~ ERROR mutable references are not allowed
 
 const fn helper(x: &mut i32) -> Option<&mut i32> { Some(x) }
 const B4: Option<&mut i32> = helper(&mut 42); //~ ERROR temporary value dropped while borrowed
diff --git a/tests/ui/consts/const-mut-refs/mut_ref_in_final.stderr b/tests/ui/consts/const-mut-refs/mut_ref_in_final.stderr
index 834ea3410f6..122e5c1bdf0 100644
--- a/tests/ui/consts/const-mut-refs/mut_ref_in_final.stderr
+++ b/tests/ui/consts/const-mut-refs/mut_ref_in_final.stderr
@@ -4,15 +4,11 @@ error[E0764]: mutable references are not allowed in the final value of constants
 LL | const B: *mut i32 = &mut 4;
    |                     ^^^^^^
 
-error[E0716]: temporary value dropped while borrowed
-  --> $DIR/mut_ref_in_final.rs:21:40
+error[E0764]: mutable references are not allowed in the final value of constants
+  --> $DIR/mut_ref_in_final.rs:21:35
    |
 LL | const B3: Option<&mut i32> = Some(&mut 42);
-   |                              ----------^^-
-   |                              |         | |
-   |                              |         | temporary value is freed at the end of this statement
-   |                              |         creates a temporary value which is freed while still in use
-   |                              using this value as a constant requires that borrow lasts for `'static`
+   |                                   ^^^^^^^
 
 error[E0716]: temporary value dropped while borrowed
   --> $DIR/mut_ref_in_final.rs:24:42
diff --git a/tests/ui/consts/const-size_of_val-align_of_val-extern-type.rs b/tests/ui/consts/const-size_of_val-align_of_val-extern-type.rs
index 14cdf6bb7c6..423ff37baef 100644
--- a/tests/ui/consts/const-size_of_val-align_of_val-extern-type.rs
+++ b/tests/ui/consts/const-size_of_val-align_of_val-extern-type.rs
@@ -1,13 +1,13 @@
 #![feature(extern_types)]
 #![feature(core_intrinsics)]
 
-use std::intrinsics::{min_align_of_val, size_of_val};
+use std::intrinsics::{align_of_val, size_of_val};
 
 extern "C" {
     type Opaque;
 }
 
 const _SIZE: usize = unsafe { size_of_val(&4 as *const i32 as *const Opaque) }; //~ ERROR layout
-const _ALIGN: usize = unsafe { min_align_of_val(&4 as *const i32 as *const Opaque) }; //~ ERROR layout
+const _ALIGN: usize = unsafe { align_of_val(&4 as *const i32 as *const Opaque) }; //~ ERROR layout
 
 fn main() {}
diff --git a/tests/ui/consts/const-size_of_val-align_of_val-extern-type.stderr b/tests/ui/consts/const-size_of_val-align_of_val-extern-type.stderr
index 64b7a4129e0..c78626bdefc 100644
--- a/tests/ui/consts/const-size_of_val-align_of_val-extern-type.stderr
+++ b/tests/ui/consts/const-size_of_val-align_of_val-extern-type.stderr
@@ -7,8 +7,8 @@ LL | const _SIZE: usize = unsafe { size_of_val(&4 as *const i32 as *const Opaque
 error[E0080]: `extern type` does not have known layout
   --> $DIR/const-size_of_val-align_of_val-extern-type.rs:11:32
    |
-LL | const _ALIGN: usize = unsafe { min_align_of_val(&4 as *const i32 as *const Opaque) };
-   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `_ALIGN` failed here
+LL | const _ALIGN: usize = unsafe { align_of_val(&4 as *const i32 as *const Opaque) };
+   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `_ALIGN` failed here
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/consts/const-unstable-intrinsic.rs b/tests/ui/consts/const-unstable-intrinsic.rs
index 890a30c73c8..d130eb6c707 100644
--- a/tests/ui/consts/const-unstable-intrinsic.rs
+++ b/tests/ui/consts/const-unstable-intrinsic.rs
@@ -17,13 +17,13 @@ const fn const_main() {
         unstable_intrinsic::size_of_val(&x);
         //~^ERROR: unstable library feature `unstable`
         //~|ERROR: not yet stable as a const intrinsic
-        unstable_intrinsic::min_align_of_val(&x);
+        unstable_intrinsic::align_of_val(&x);
         //~^ERROR: unstable library feature `unstable`
         //~|ERROR: not yet stable as a const intrinsic
 
         size_of_val(&x);
         //~^ERROR: cannot use `#[feature(local)]`
-        min_align_of_val(&x);
+        align_of_val(&x);
         //~^ERROR: cannot use `#[feature(local)]`
     }
 }
@@ -35,7 +35,7 @@ pub const unsafe fn size_of_val<T>(x: *const T) -> usize;
 #[unstable(feature = "local", issue = "42")]
 #[rustc_const_unstable(feature = "local", issue = "42")]
 #[rustc_intrinsic]
-pub const unsafe fn min_align_of_val<T>(x: *const T) -> usize;
+pub const unsafe fn align_of_val<T>(x: *const T) -> usize;
 
 #[stable(feature = "rust1", since = "1.0.0")]
 #[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.63.0")]
diff --git a/tests/ui/consts/const-unstable-intrinsic.stderr b/tests/ui/consts/const-unstable-intrinsic.stderr
index 7e7ba966cee..973c7bae586 100644
--- a/tests/ui/consts/const-unstable-intrinsic.stderr
+++ b/tests/ui/consts/const-unstable-intrinsic.stderr
@@ -11,8 +11,8 @@ LL |         unstable_intrinsic::size_of_val(&x);
 error[E0658]: use of unstable library feature `unstable`
   --> $DIR/const-unstable-intrinsic.rs:20:9
    |
-LL |         unstable_intrinsic::min_align_of_val(&x);
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |         unstable_intrinsic::align_of_val(&x);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: see issue #42 <https://github.com/rust-lang/rust/issues/42> for more information
    = help: add `#![feature(unstable)]` to the crate attributes to enable
@@ -29,11 +29,11 @@ help: add `#![feature(unstable)]` to the crate attributes to enable
 LL + #![feature(unstable)]
    |
 
-error: `min_align_of_val` is not yet stable as a const intrinsic
+error: `align_of_val` is not yet stable as a const intrinsic
   --> $DIR/const-unstable-intrinsic.rs:20:9
    |
-LL |         unstable_intrinsic::min_align_of_val(&x);
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |         unstable_intrinsic::align_of_val(&x);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 help: add `#![feature(unstable)]` to the crate attributes to enable
    |
@@ -55,8 +55,8 @@ LL | const fn const_main() {
 error: const function that might be (indirectly) exposed to stable cannot use `#[feature(local)]`
   --> $DIR/const-unstable-intrinsic.rs:26:9
    |
-LL |         min_align_of_val(&x);
-   |         ^^^^^^^^^^^^^^^^^^^^
+LL |         align_of_val(&x);
+   |         ^^^^^^^^^^^^^^^^
    |
 help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]`
    |
diff --git a/tests/ui/consts/gate-do-not-const-check.rs b/tests/ui/consts/gate-do-not-const-check.rs
index be7e70dfabb..0ebb1e7c82e 100644
--- a/tests/ui/consts/gate-do-not-const-check.rs
+++ b/tests/ui/consts/gate-do-not-const-check.rs
@@ -1,5 +1,7 @@
 #[rustc_do_not_const_check]
-//~^ ERROR this is an internal attribute that will never be stable
+//~^ ERROR use of an internal attribute [E0658]
+//~| NOTE the `#[rustc_do_not_const_check]` attribute is an internal implementation detail that will never be stable
+//~| NOTE `#[rustc_do_not_const_check]` skips const-check for this function's body
 const fn foo() {}
 
 fn main() {}
diff --git a/tests/ui/consts/gate-do-not-const-check.stderr b/tests/ui/consts/gate-do-not-const-check.stderr
index 74ea71c4ed8..778ee50e71b 100644
--- a/tests/ui/consts/gate-do-not-const-check.stderr
+++ b/tests/ui/consts/gate-do-not-const-check.stderr
@@ -1,11 +1,12 @@
-error[E0658]: this is an internal attribute that will never be stable
+error[E0658]: use of an internal attribute
   --> $DIR/gate-do-not-const-check.rs:1:1
    |
 LL | #[rustc_do_not_const_check]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+   = note: the `#[rustc_do_not_const_check]` attribute is an internal implementation detail that will never be stable
+   = note: `#[rustc_do_not_const_check]` skips const-check for this function's body
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/consts/issue-54224.rs b/tests/ui/consts/issue-54224.rs
deleted file mode 100644
index f1947933d67..00000000000
--- a/tests/ui/consts/issue-54224.rs
+++ /dev/null
@@ -1,12 +0,0 @@
-const FOO: Option<&[[u8; 3]]> = Some(&[*b"foo"]); //~ ERROR temporary value dropped while borrowed
-
-use std::borrow::Cow;
-
-pub const X: [u8; 3] = *b"ABC";
-pub const Y: Cow<'static, [ [u8; 3] ]> = Cow::Borrowed(&[X]);
-
-
-pub const Z: Cow<'static, [ [u8; 3] ]> = Cow::Borrowed(&[*b"ABC"]);
-//~^ ERROR temporary value dropped while borrowed
-
-fn main() {}
diff --git a/tests/ui/consts/issue-54224.stderr b/tests/ui/consts/issue-54224.stderr
deleted file mode 100644
index 55fe55759df..00000000000
--- a/tests/ui/consts/issue-54224.stderr
+++ /dev/null
@@ -1,23 +0,0 @@
-error[E0716]: temporary value dropped while borrowed
-  --> $DIR/issue-54224.rs:1:39
-   |
-LL | const FOO: Option<&[[u8; 3]]> = Some(&[*b"foo"]);
-   |                                 ------^^^^^^^^^-
-   |                                 |     |        |
-   |                                 |     |        temporary value is freed at the end of this statement
-   |                                 |     creates a temporary value which is freed while still in use
-   |                                 using this value as a constant requires that borrow lasts for `'static`
-
-error[E0716]: temporary value dropped while borrowed
-  --> $DIR/issue-54224.rs:9:57
-   |
-LL | pub const Z: Cow<'static, [ [u8; 3] ]> = Cow::Borrowed(&[*b"ABC"]);
-   |                                          ---------------^^^^^^^^^-
-   |                                          |              |        |
-   |                                          |              |        temporary value is freed at the end of this statement
-   |                                          |              creates a temporary value which is freed while still in use
-   |                                          using this value as a constant requires that borrow lasts for `'static`
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0716`.
diff --git a/tests/ui/consts/promote-not.rs b/tests/ui/consts/promote-not.rs
index 207baccd6ab..74e0efb858a 100644
--- a/tests/ui/consts/promote-not.rs
+++ b/tests/ui/consts/promote-not.rs
@@ -3,10 +3,11 @@
 #![allow(unconditional_panic)]
 
 use std::cell::Cell;
+use std::convert::identity;
 use std::mem::ManuallyDrop;
 
 // We do not promote mutable references.
-static mut TEST1: Option<&mut [i32]> = Some(&mut [1, 2, 3]); //~ ERROR temporary value dropped while borrowed
+static mut TEST1: &mut [i32] = identity(&mut [1, 2, 3]); //~ ERROR temporary value dropped while borrowed
 
 static mut TEST2: &'static mut [i32] = {
     let x = &mut [1,2,3]; //~ ERROR temporary value dropped while borrowed
diff --git a/tests/ui/consts/promote-not.stderr b/tests/ui/consts/promote-not.stderr
index d8b6091dc9a..ec552d9dd7d 100644
--- a/tests/ui/consts/promote-not.stderr
+++ b/tests/ui/consts/promote-not.stderr
@@ -1,15 +1,15 @@
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/promote-not.rs:9:50
+  --> $DIR/promote-not.rs:10:46
    |
-LL | static mut TEST1: Option<&mut [i32]> = Some(&mut [1, 2, 3]);
-   |                                        ----------^^^^^^^^^-
-   |                                        |         |        |
-   |                                        |         |        temporary value is freed at the end of this statement
-   |                                        |         creates a temporary value which is freed while still in use
-   |                                        using this value as a static requires that borrow lasts for `'static`
+LL | static mut TEST1: &mut [i32] = identity(&mut [1, 2, 3]);
+   |                                --------------^^^^^^^^^-
+   |                                |             |        |
+   |                                |             |        temporary value is freed at the end of this statement
+   |                                |             creates a temporary value which is freed while still in use
+   |                                using this value as a static requires that borrow lasts for `'static`
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/promote-not.rs:12:18
+  --> $DIR/promote-not.rs:13:18
    |
 LL |     let x = &mut [1,2,3];
    |                  ^^^^^^^ creates a temporary value which is freed while still in use
@@ -19,7 +19,7 @@ LL | };
    | - temporary value is freed at the end of this statement
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/promote-not.rs:34:29
+  --> $DIR/promote-not.rs:35:29
    |
 LL |     let _x: &'static i32 = &unsafe { U { x: 0 }.x };
    |             ------------    ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use
@@ -29,7 +29,7 @@ LL | };
    | - temporary value is freed at the end of this statement
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/promote-not.rs:40:29
+  --> $DIR/promote-not.rs:41:29
    |
 LL |     let _val: &'static _ = &(Cell::new(1), 2).1;
    |               ----------    ^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use
@@ -39,7 +39,7 @@ LL | };
    | - temporary value is freed at the end of this statement
 
 error[E0493]: destructor of `String` cannot be evaluated at compile-time
-  --> $DIR/promote-not.rs:47:14
+  --> $DIR/promote-not.rs:48:14
    |
 LL |     let x = &String::new();
    |              ^^^^^^^^^^^^^ the destructor for this type cannot be evaluated in constants
@@ -48,7 +48,7 @@ LL | };
    | - value is dropped here
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/promote-not.rs:59:33
+  --> $DIR/promote-not.rs:60:33
    |
 LL |         let _x: &'static u32 = &mk_panic();
    |                 ------------    ^^^^^^^^^^ creates a temporary value which is freed while still in use
@@ -58,7 +58,7 @@ LL |     }
    |     - temporary value is freed at the end of this statement
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/promote-not.rs:21:32
+  --> $DIR/promote-not.rs:22:32
    |
 LL |         let _x: &'static () = &foo();
    |                 -----------    ^^^^^ creates a temporary value which is freed while still in use
@@ -68,7 +68,7 @@ LL |     }
    |     - temporary value is freed at the end of this statement
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/promote-not.rs:29:29
+  --> $DIR/promote-not.rs:30:29
    |
 LL |     let _x: &'static i32 = &unsafe { U { x: 0 }.x };
    |             ------------    ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use
@@ -78,7 +78,7 @@ LL | }
    | - temporary value is freed at the end of this statement
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/promote-not.rs:65:29
+  --> $DIR/promote-not.rs:66:29
    |
 LL |     let _val: &'static _ = &(Cell::new(1), 2).0;
    |               ----------    ^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use
@@ -89,7 +89,7 @@ LL | }
    | - temporary value is freed at the end of this statement
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/promote-not.rs:66:29
+  --> $DIR/promote-not.rs:67:29
    |
 LL |     let _val: &'static _ = &(Cell::new(1), 2).1;
    |               ----------    ^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use
@@ -100,7 +100,7 @@ LL | }
    | - temporary value is freed at the end of this statement
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/promote-not.rs:69:29
+  --> $DIR/promote-not.rs:70:29
    |
 LL |     let _val: &'static _ = &(1/0);
    |               ----------    ^^^^^ creates a temporary value which is freed while still in use
@@ -111,7 +111,7 @@ LL | }
    | - temporary value is freed at the end of this statement
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/promote-not.rs:70:29
+  --> $DIR/promote-not.rs:71:29
    |
 LL |     let _val: &'static _ = &(1/(1-1));
    |               ----------    ^^^^^^^^^ creates a temporary value which is freed while still in use
@@ -122,7 +122,7 @@ LL | }
    | - temporary value is freed at the end of this statement
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/promote-not.rs:71:29
+  --> $DIR/promote-not.rs:72:29
    |
 LL |     let _val: &'static _ = &((1+1)/(1-1));
    |               ----------    ^^^^^^^^^^^^^ creates a temporary value which is freed while still in use
@@ -133,7 +133,7 @@ LL | }
    | - temporary value is freed at the end of this statement
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/promote-not.rs:72:29
+  --> $DIR/promote-not.rs:73:29
    |
 LL |     let _val: &'static _ = &(i32::MIN/-1);
    |               ----------    ^^^^^^^^^^^^^ creates a temporary value which is freed while still in use
@@ -144,7 +144,7 @@ LL | }
    | - temporary value is freed at the end of this statement
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/promote-not.rs:73:29
+  --> $DIR/promote-not.rs:74:29
    |
 LL |     let _val: &'static _ = &(i32::MIN/(0-1));
    |               ----------    ^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use
@@ -155,7 +155,7 @@ LL | }
    | - temporary value is freed at the end of this statement
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/promote-not.rs:74:29
+  --> $DIR/promote-not.rs:75:29
    |
 LL |     let _val: &'static _ = &(-128i8/-1);
    |               ----------    ^^^^^^^^^^^ creates a temporary value which is freed while still in use
@@ -166,7 +166,7 @@ LL | }
    | - temporary value is freed at the end of this statement
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/promote-not.rs:75:29
+  --> $DIR/promote-not.rs:76:29
    |
 LL |     let _val: &'static _ = &(1%0);
    |               ----------    ^^^^^ creates a temporary value which is freed while still in use
@@ -177,7 +177,7 @@ LL | }
    | - temporary value is freed at the end of this statement
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/promote-not.rs:76:29
+  --> $DIR/promote-not.rs:77:29
    |
 LL |     let _val: &'static _ = &(1%(1-1));
    |               ----------    ^^^^^^^^^ creates a temporary value which is freed while still in use
@@ -188,7 +188,7 @@ LL | }
    | - temporary value is freed at the end of this statement
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/promote-not.rs:77:29
+  --> $DIR/promote-not.rs:78:29
    |
 LL |     let _val: &'static _ = &([1,2,3][4]+1);
    |               ----------    ^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use
@@ -199,7 +199,7 @@ LL | }
    | - temporary value is freed at the end of this statement
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/promote-not.rs:81:29
+  --> $DIR/promote-not.rs:82:29
    |
 LL |     let _val: &'static _ = &TEST_DROP;
    |               ----------    ^^^^^^^^^ creates a temporary value which is freed while still in use
@@ -210,7 +210,7 @@ LL | }
    | - temporary value is freed at the end of this statement
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/promote-not.rs:83:29
+  --> $DIR/promote-not.rs:84:29
    |
 LL |     let _val: &'static _ = &&TEST_DROP;
    |               ----------    ^^^^^^^^^^ creates a temporary value which is freed while still in use
@@ -221,7 +221,7 @@ LL | }
    | - temporary value is freed at the end of this statement
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/promote-not.rs:83:30
+  --> $DIR/promote-not.rs:84:30
    |
 LL |     let _val: &'static _ = &&TEST_DROP;
    |               ----------     ^^^^^^^^^ creates a temporary value which is freed while still in use
@@ -232,7 +232,7 @@ LL | }
    | - temporary value is freed at the end of this statement
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/promote-not.rs:86:29
+  --> $DIR/promote-not.rs:87:29
    |
 LL |     let _val: &'static _ = &(&TEST_DROP,);
    |               ----------    ^^^^^^^^^^^^^ creates a temporary value which is freed while still in use
@@ -243,7 +243,7 @@ LL | }
    | - temporary value is freed at the end of this statement
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/promote-not.rs:86:31
+  --> $DIR/promote-not.rs:87:31
    |
 LL |     let _val: &'static _ = &(&TEST_DROP,);
    |               ----------      ^^^^^^^^^ creates a temporary value which is freed while still in use
@@ -254,7 +254,7 @@ LL | }
    | - temporary value is freed at the end of this statement
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/promote-not.rs:89:29
+  --> $DIR/promote-not.rs:90:29
    |
 LL |     let _val: &'static _ = &[&TEST_DROP; 1];
    |               ----------    ^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use
@@ -265,7 +265,7 @@ LL | }
    | - temporary value is freed at the end of this statement
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/promote-not.rs:89:31
+  --> $DIR/promote-not.rs:90:31
    |
 LL |     let _val: &'static _ = &[&TEST_DROP; 1];
    |               ----------      ^^^^^^^^^    - temporary value is freed at the end of this statement
@@ -274,7 +274,7 @@ LL |     let _val: &'static _ = &[&TEST_DROP; 1];
    |               type annotation requires that borrow lasts for `'static`
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/promote-not.rs:98:26
+  --> $DIR/promote-not.rs:99:26
    |
 LL |     let x: &'static _ = &UnionWithCell { f1: 0 };
    |            ----------    ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use
diff --git a/tests/ui/coroutine/auto-trait-regions.rs b/tests/ui/coroutine/auto-trait-regions.rs
index 1c7f0304ddb..f115896a473 100644
--- a/tests/ui/coroutine/auto-trait-regions.rs
+++ b/tests/ui/coroutine/auto-trait-regions.rs
@@ -43,8 +43,8 @@ fn main() {
     // Disallow impls which relates lifetimes in the coroutine interior
     let gen = #[coroutine] move || {
         let a = A(&mut true, &mut true, No);
-        //~^ ERROR temporary value dropped while borrowed
-        //~| ERROR temporary value dropped while borrowed
+        //~^ ERROR borrow may still be in use when coroutine yields
+        //~| ERROR borrow may still be in use when coroutine yields
         yield;
         assert_foo(a);
     };
diff --git a/tests/ui/coroutine/auto-trait-regions.stderr b/tests/ui/coroutine/auto-trait-regions.stderr
index a9a0bde2ba0..77b5f3ce57c 100644
--- a/tests/ui/coroutine/auto-trait-regions.stderr
+++ b/tests/ui/coroutine/auto-trait-regions.stderr
@@ -1,36 +1,34 @@
-error[E0716]: temporary value dropped while borrowed
-  --> $DIR/auto-trait-regions.rs:45:24
+error[E0626]: borrow may still be in use when coroutine yields
+  --> $DIR/auto-trait-regions.rs:45:19
    |
+LL |     let gen = #[coroutine] move || {
+   |                            ------- within this coroutine
 LL |         let a = A(&mut true, &mut true, No);
-   |                        ^^^^                - temporary value is freed at the end of this statement
-   |                        |
-   |                        creates a temporary value which is freed while still in use
+   |                   ^^^^^^^^^
 ...
-LL |         assert_foo(a);
-   |                    - borrow later used here
+LL |         yield;
+   |         ----- possible yield occurs here
    |
-help: consider using a `let` binding to create a longer lived value
-   |
-LL ~         let binding = true;
-LL ~         let a = A(&mut binding, &mut true, No);
+help: add `static` to mark this coroutine as unmovable
    |
+LL |     let gen = #[coroutine] static move || {
+   |                            ++++++
 
-error[E0716]: temporary value dropped while borrowed
-  --> $DIR/auto-trait-regions.rs:45:35
+error[E0626]: borrow may still be in use when coroutine yields
+  --> $DIR/auto-trait-regions.rs:45:30
    |
+LL |     let gen = #[coroutine] move || {
+   |                            ------- within this coroutine
 LL |         let a = A(&mut true, &mut true, No);
-   |                                   ^^^^     - temporary value is freed at the end of this statement
-   |                                   |
-   |                                   creates a temporary value which is freed while still in use
+   |                              ^^^^^^^^^
 ...
-LL |         assert_foo(a);
-   |                    - borrow later used here
-   |
-help: consider using a `let` binding to create a longer lived value
+LL |         yield;
+   |         ----- possible yield occurs here
    |
-LL ~         let binding = true;
-LL ~         let a = A(&mut true, &mut binding, No);
+help: add `static` to mark this coroutine as unmovable
    |
+LL |     let gen = #[coroutine] static move || {
+   |                            ++++++
 
 error: implementation of `Foo` is not general enough
   --> $DIR/auto-trait-regions.rs:31:5
@@ -52,4 +50,4 @@ LL |     assert_foo(gen);
 
 error: aborting due to 4 previous errors
 
-For more information about this error, try `rustc --explain E0716`.
+For more information about this error, try `rustc --explain E0626`.
diff --git a/tests/ui/coroutine/unsized-capture-across-yield.rs b/tests/ui/coroutine/unsized-capture-across-yield.rs
index c86b1823aaf..ee27ea064ec 100644
--- a/tests/ui/coroutine/unsized-capture-across-yield.rs
+++ b/tests/ui/coroutine/unsized-capture-across-yield.rs
@@ -1,16 +1,13 @@
 #![feature(coroutine_trait)]
 #![feature(coroutines)]
-#![feature(unsized_locals)]
-//~^ WARN the feature `unsized_locals` is incomplete and may not be safe to use and/or cause compiler crashes
 
 use std::ops::Coroutine;
 
 fn capture() -> impl Coroutine {
-    let b: [u8] = *(Box::new([]) as Box<[u8]>);
+    let b: [u8] = *(Box::new([]) as Box<[u8]>); //~ERROR he size for values of type `[u8]` cannot be known at compilation time
     #[coroutine]
     move || {
         println!("{:?}", &b);
-        //~^ ERROR the size for values of type `[u8]` cannot be known at compilation time
 
         yield;
 
diff --git a/tests/ui/coroutine/unsized-capture-across-yield.stderr b/tests/ui/coroutine/unsized-capture-across-yield.stderr
index 03551f1bbff..c46c08ff53e 100644
--- a/tests/ui/coroutine/unsized-capture-across-yield.stderr
+++ b/tests/ui/coroutine/unsized-capture-across-yield.stderr
@@ -1,23 +1,16 @@
-warning: the feature `unsized_locals` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/unsized-capture-across-yield.rs:3:12
-   |
-LL | #![feature(unsized_locals)]
-   |            ^^^^^^^^^^^^^^
-   |
-   = note: see issue #48055 <https://github.com/rust-lang/rust/issues/48055> for more information
-   = note: `#[warn(incomplete_features)]` on by default
-
 error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
-  --> $DIR/unsized-capture-across-yield.rs:12:27
+  --> $DIR/unsized-capture-across-yield.rs:7:9
    |
-LL |     move || {
-   |          -- this closure captures all values by move
-LL |         println!("{:?}", &b);
-   |                           ^ doesn't have a size known at compile-time
+LL |     let b: [u8] = *(Box::new([]) as Box<[u8]>);
+   |         ^ doesn't have a size known at compile-time
    |
    = help: the trait `Sized` is not implemented for `[u8]`
-   = note: all values captured by value by a closure must have a statically known size
+   = note: all local variables must have a statically known size
+help: consider borrowing here
+   |
+LL |     let b: &[u8] = *(Box::new([]) as Box<[u8]>);
+   |            +
 
-error: aborting due to 1 previous error; 1 warning emitted
+error: aborting due to 1 previous error
 
 For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/coroutine/unsized-local-across-yield.rs b/tests/ui/coroutine/unsized-local-across-yield.rs
index cb8ced13a11..4c688e5b997 100644
--- a/tests/ui/coroutine/unsized-local-across-yield.rs
+++ b/tests/ui/coroutine/unsized-local-across-yield.rs
@@ -1,7 +1,5 @@
 #![feature(coroutine_trait)]
 #![feature(coroutines)]
-#![feature(unsized_locals)]
-//~^ WARN the feature `unsized_locals` is incomplete and may not be safe to use and/or cause compiler crashes
 
 use std::ops::Coroutine;
 
diff --git a/tests/ui/coroutine/unsized-local-across-yield.stderr b/tests/ui/coroutine/unsized-local-across-yield.stderr
index 4fe0f135a9d..fd6cd4676c2 100644
--- a/tests/ui/coroutine/unsized-local-across-yield.stderr
+++ b/tests/ui/coroutine/unsized-local-across-yield.stderr
@@ -1,21 +1,16 @@
-warning: the feature `unsized_locals` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/unsized-local-across-yield.rs:3:12
-   |
-LL | #![feature(unsized_locals)]
-   |            ^^^^^^^^^^^^^^
-   |
-   = note: see issue #48055 <https://github.com/rust-lang/rust/issues/48055> for more information
-   = note: `#[warn(incomplete_features)]` on by default
-
 error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
-  --> $DIR/unsized-local-across-yield.rs:11:13
+  --> $DIR/unsized-local-across-yield.rs:9:13
    |
 LL |         let b: [u8] = *(Box::new([]) as Box<[u8]>);
    |             ^ doesn't have a size known at compile-time
    |
    = help: the trait `Sized` is not implemented for `[u8]`
-   = note: all values live across `yield` must have a statically known size
+   = note: all local variables must have a statically known size
+help: consider borrowing here
+   |
+LL |         let b: &[u8] = *(Box::new([]) as Box<[u8]>);
+   |                +
 
-error: aborting due to 1 previous error; 1 warning emitted
+error: aborting due to 1 previous error
 
 For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/diagnostic_namespace/multiline_spans.rs b/tests/ui/diagnostic_namespace/multiline_spans.rs
new file mode 100644
index 00000000000..994dd9fd011
--- /dev/null
+++ b/tests/ui/diagnostic_namespace/multiline_spans.rs
@@ -0,0 +1,55 @@
+#![crate_type = "lib"]
+#![deny(unknown_or_malformed_diagnostic_attributes)]
+
+
+#[diagnostic::on_unimplemented(message = "here is a big \
+                                         multiline string \
+                                         {unknown}")]
+//~^ ERROR there is no parameter `unknown` on trait `MultiLine` [unknown_or_malformed_diagnostic_attributes]
+pub trait MultiLine {}
+
+#[diagnostic::on_unimplemented(message = "here is a big \
+                                         multiline string {unknown}")]
+//~^ ERROR there is no parameter `unknown` on trait `MultiLine2` [unknown_or_malformed_diagnostic_attributes]
+pub trait MultiLine2 {}
+
+#[diagnostic::on_unimplemented(message = "here is a big \
+    multiline string {unknown}")]
+//~^ ERROR there is no parameter `unknown` on trait `MultiLine3` [unknown_or_malformed_diagnostic_attributes]
+pub trait MultiLine3 {}
+
+
+#[diagnostic::on_unimplemented(message = "here is a big \
+\
+                \
+                                \
+                                                \
+    multiline string {unknown}")]
+//~^ ERROR there is no parameter `unknown` on trait `MultiLine4` [unknown_or_malformed_diagnostic_attributes]
+pub trait MultiLine4 {}
+
+#[diagnostic::on_unimplemented(message = "here is a big \
+                                         multiline string \
+                                         {Self:+}")]
+//~^ ERROR invalid format specifier [unknown_or_malformed_diagnostic_attributes]
+pub trait MultiLineFmt {}
+
+#[diagnostic::on_unimplemented(message = "here is a big \
+                                         multiline string {Self:X}")]
+//~^ ERROR invalid format specifier [unknown_or_malformed_diagnostic_attributes]
+pub trait MultiLineFmt2 {}
+
+#[diagnostic::on_unimplemented(message = "here is a big \
+    multiline string {Self:#}")]
+//~^ ERROR invalid format specifier [unknown_or_malformed_diagnostic_attributes]
+pub trait MultiLineFmt3 {}
+
+
+#[diagnostic::on_unimplemented(message = "here is a big \
+\
+                \
+                                \
+                                                \
+    multiline string {Self:?}")]
+//~^ ERROR invalid format specifier [unknown_or_malformed_diagnostic_attributes]
+pub trait MultiLineFmt4 {}
diff --git a/tests/ui/diagnostic_namespace/multiline_spans.stderr b/tests/ui/diagnostic_namespace/multiline_spans.stderr
new file mode 100644
index 00000000000..894bfe3d90a
--- /dev/null
+++ b/tests/ui/diagnostic_namespace/multiline_spans.stderr
@@ -0,0 +1,71 @@
+error: there is no parameter `unknown` on trait `MultiLine`
+  --> $DIR/multiline_spans.rs:7:43
+   |
+LL | ...                   {unknown}")]
+   |                        ^^^^^^^
+   |
+   = help: expect either a generic argument name or `{Self}` as format argument
+note: the lint level is defined here
+  --> $DIR/multiline_spans.rs:2:9
+   |
+LL | #![deny(unknown_or_malformed_diagnostic_attributes)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: there is no parameter `unknown` on trait `MultiLine2`
+  --> $DIR/multiline_spans.rs:12:60
+   |
+LL | ...                   multiline string {unknown}")]
+   |                                         ^^^^^^^
+   |
+   = help: expect either a generic argument name or `{Self}` as format argument
+
+error: there is no parameter `unknown` on trait `MultiLine3`
+  --> $DIR/multiline_spans.rs:17:23
+   |
+LL |     multiline string {unknown}")]
+   |                       ^^^^^^^
+   |
+   = help: expect either a generic argument name or `{Self}` as format argument
+
+error: there is no parameter `unknown` on trait `MultiLine4`
+  --> $DIR/multiline_spans.rs:27:23
+   |
+LL |     multiline string {unknown}")]
+   |                       ^^^^^^^
+   |
+   = help: expect either a generic argument name or `{Self}` as format argument
+
+error: invalid format specifier
+  --> $DIR/multiline_spans.rs:33:47
+   |
+LL | ...                   {Self:+}")]
+   |                            ^^
+   |
+   = help: no format specifier are supported in this position
+
+error: invalid format specifier
+  --> $DIR/multiline_spans.rs:38:64
+   |
+LL | ...                   multiline string {Self:X}")]
+   |                                             ^^
+   |
+   = help: no format specifier are supported in this position
+
+error: invalid format specifier
+  --> $DIR/multiline_spans.rs:43:27
+   |
+LL |     multiline string {Self:#}")]
+   |                           ^^
+   |
+   = help: no format specifier are supported in this position
+
+error: invalid format specifier
+  --> $DIR/multiline_spans.rs:53:27
+   |
+LL |     multiline string {Self:?}")]
+   |                           ^^
+   |
+   = help: no format specifier are supported in this position
+
+error: aborting due to 8 previous errors
+
diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.rs b/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.rs
index 44f269eb967..4762d9e793f 100644
--- a/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.rs
+++ b/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.rs
@@ -12,6 +12,8 @@ trait ImportantTrait2 {}
 #[diagnostic::on_unimplemented(message = "Test {1:}")]
 //~^WARN positional format arguments are not allowed here
 //~|WARN positional format arguments are not allowed here
+//~|WARN invalid format specifier [unknown_or_malformed_diagnostic_attributes]
+//~|WARN invalid format specifier [unknown_or_malformed_diagnostic_attributes]
 trait ImportantTrait3 {}
 
 #[diagnostic::on_unimplemented(message = "Test {Self:123}")]
@@ -20,17 +22,22 @@ trait ImportantTrait3 {}
 trait ImportantTrait4 {}
 
 #[diagnostic::on_unimplemented(message = "Test {Self:!}")]
-//~^WARN expected `}`, found `!`
-//~|WARN expected `}`, found `!`
-//~|WARN unmatched `}` found
-//~|WARN unmatched `}` found
+//~^WARN invalid format specifier [unknown_or_malformed_diagnostic_attributes]
+//~|WARN invalid format specifier [unknown_or_malformed_diagnostic_attributes]
 trait ImportantTrait5 {}
 
+#[diagnostic::on_unimplemented(message = "Test {Self:}")]
+//~^WARN invalid format specifier [unknown_or_malformed_diagnostic_attributes]
+//~|WARN invalid format specifier [unknown_or_malformed_diagnostic_attributes]
+trait ImportantTrait6 {}
+
+
 fn check_1(_: impl ImportantTrait1) {}
 fn check_2(_: impl ImportantTrait2) {}
 fn check_3(_: impl ImportantTrait3) {}
 fn check_4(_: impl ImportantTrait4) {}
 fn check_5(_: impl ImportantTrait5) {}
+fn check_6(_: impl ImportantTrait6) {}
 
 fn main() {
     check_1(());
@@ -42,5 +49,7 @@ fn main() {
     check_4(());
     //~^ERROR Test ()
     check_5(());
-    //~^ERROR Test {Self:!}
+    //~^ERROR Test ()
+    check_6(());
+    //~^ERROR Test ()
 }
diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr
index a82a1e78da0..2670d0630f7 100644
--- a/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr
+++ b/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr
@@ -14,6 +14,14 @@ LL | #[diagnostic::on_unimplemented(message = "Test {}")]
    |
    = help: only named format arguments with the name of one of the generic types are allowed in this context
 
+warning: invalid format specifier
+  --> $DIR/broken_format.rs:12:50
+   |
+LL | #[diagnostic::on_unimplemented(message = "Test {1:}")]
+   |                                                  ^
+   |
+   = help: no format specifier are supported in this position
+
 warning: positional format arguments are not allowed here
   --> $DIR/broken_format.rs:12:49
    |
@@ -23,24 +31,28 @@ LL | #[diagnostic::on_unimplemented(message = "Test {1:}")]
    = help: only named format arguments with the name of one of the generic types are allowed in this context
 
 warning: invalid format specifier
-  --> $DIR/broken_format.rs:17:42
+  --> $DIR/broken_format.rs:19:53
    |
 LL | #[diagnostic::on_unimplemented(message = "Test {Self:123}")]
-   |                                          ^^^^^^^^^^^^^^^^^
+   |                                                     ^^^^
    |
    = help: no format specifier are supported in this position
 
-warning: expected `}`, found `!`
-  --> $DIR/broken_format.rs:22:42
+warning: invalid format specifier
+  --> $DIR/broken_format.rs:24:53
    |
 LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")]
-   |                                          ^^^^^^^^^^^^^^^
+   |                                                     ^^
+   |
+   = help: no format specifier are supported in this position
 
-warning: unmatched `}` found
-  --> $DIR/broken_format.rs:22:42
+warning: invalid format specifier
+  --> $DIR/broken_format.rs:29:53
    |
-LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")]
-   |                                          ^^^^^^^^^^^^^^^
+LL | #[diagnostic::on_unimplemented(message = "Test {Self:}")]
+   |                                                     ^
+   |
+   = help: no format specifier are supported in this position
 
 warning: unmatched `}` found
   --> $DIR/broken_format.rs:2:42
@@ -51,7 +63,7 @@ LL | #[diagnostic::on_unimplemented(message = "{{Test } thing")]
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error[E0277]: {{Test } thing
-  --> $DIR/broken_format.rs:36:13
+  --> $DIR/broken_format.rs:43:13
    |
 LL |     check_1(());
    |     ------- ^^ the trait `ImportantTrait1` is not implemented for `()`
@@ -64,7 +76,7 @@ help: this trait has no implementations, consider adding one
 LL | trait ImportantTrait1 {}
    | ^^^^^^^^^^^^^^^^^^^^^
 note: required by a bound in `check_1`
-  --> $DIR/broken_format.rs:29:20
+  --> $DIR/broken_format.rs:35:20
    |
 LL | fn check_1(_: impl ImportantTrait1) {}
    |                    ^^^^^^^^^^^^^^^ required by this bound in `check_1`
@@ -79,7 +91,7 @@ LL | #[diagnostic::on_unimplemented(message = "Test {}")]
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error[E0277]: Test {}
-  --> $DIR/broken_format.rs:38:13
+  --> $DIR/broken_format.rs:45:13
    |
 LL |     check_2(());
    |     ------- ^^ the trait `ImportantTrait2` is not implemented for `()`
@@ -92,11 +104,20 @@ help: this trait has no implementations, consider adding one
 LL | trait ImportantTrait2 {}
    | ^^^^^^^^^^^^^^^^^^^^^
 note: required by a bound in `check_2`
-  --> $DIR/broken_format.rs:30:20
+  --> $DIR/broken_format.rs:36:20
    |
 LL | fn check_2(_: impl ImportantTrait2) {}
    |                    ^^^^^^^^^^^^^^^ required by this bound in `check_2`
 
+warning: invalid format specifier
+  --> $DIR/broken_format.rs:12:50
+   |
+LL | #[diagnostic::on_unimplemented(message = "Test {1:}")]
+   |                                                  ^
+   |
+   = help: no format specifier are supported in this position
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
 warning: positional format arguments are not allowed here
   --> $DIR/broken_format.rs:12:49
    |
@@ -107,7 +128,7 @@ LL | #[diagnostic::on_unimplemented(message = "Test {1:}")]
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error[E0277]: Test {1}
-  --> $DIR/broken_format.rs:40:13
+  --> $DIR/broken_format.rs:47:13
    |
 LL |     check_3(());
    |     ------- ^^ the trait `ImportantTrait3` is not implemented for `()`
@@ -115,27 +136,27 @@ LL |     check_3(());
    |     required by a bound introduced by this call
    |
 help: this trait has no implementations, consider adding one
-  --> $DIR/broken_format.rs:15:1
+  --> $DIR/broken_format.rs:17:1
    |
 LL | trait ImportantTrait3 {}
    | ^^^^^^^^^^^^^^^^^^^^^
 note: required by a bound in `check_3`
-  --> $DIR/broken_format.rs:31:20
+  --> $DIR/broken_format.rs:37:20
    |
 LL | fn check_3(_: impl ImportantTrait3) {}
    |                    ^^^^^^^^^^^^^^^ required by this bound in `check_3`
 
 warning: invalid format specifier
-  --> $DIR/broken_format.rs:17:42
+  --> $DIR/broken_format.rs:19:53
    |
 LL | #[diagnostic::on_unimplemented(message = "Test {Self:123}")]
-   |                                          ^^^^^^^^^^^^^^^^^
+   |                                                     ^^^^
    |
    = help: no format specifier are supported in this position
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error[E0277]: Test ()
-  --> $DIR/broken_format.rs:42:13
+  --> $DIR/broken_format.rs:49:13
    |
 LL |     check_4(());
    |     ------- ^^ the trait `ImportantTrait4` is not implemented for `()`
@@ -143,34 +164,27 @@ LL |     check_4(());
    |     required by a bound introduced by this call
    |
 help: this trait has no implementations, consider adding one
-  --> $DIR/broken_format.rs:20:1
+  --> $DIR/broken_format.rs:22:1
    |
 LL | trait ImportantTrait4 {}
    | ^^^^^^^^^^^^^^^^^^^^^
 note: required by a bound in `check_4`
-  --> $DIR/broken_format.rs:32:20
+  --> $DIR/broken_format.rs:38:20
    |
 LL | fn check_4(_: impl ImportantTrait4) {}
    |                    ^^^^^^^^^^^^^^^ required by this bound in `check_4`
 
-warning: expected `}`, found `!`
-  --> $DIR/broken_format.rs:22:42
-   |
-LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")]
-   |                                          ^^^^^^^^^^^^^^^
-   |
-   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
-
-warning: unmatched `}` found
-  --> $DIR/broken_format.rs:22:42
+warning: invalid format specifier
+  --> $DIR/broken_format.rs:24:53
    |
 LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")]
-   |                                          ^^^^^^^^^^^^^^^
+   |                                                     ^^
    |
+   = help: no format specifier are supported in this position
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
-error[E0277]: Test {Self:!}
-  --> $DIR/broken_format.rs:44:13
+error[E0277]: Test ()
+  --> $DIR/broken_format.rs:51:13
    |
 LL |     check_5(());
    |     ------- ^^ the trait `ImportantTrait5` is not implemented for `()`
@@ -183,11 +197,39 @@ help: this trait has no implementations, consider adding one
 LL | trait ImportantTrait5 {}
    | ^^^^^^^^^^^^^^^^^^^^^
 note: required by a bound in `check_5`
-  --> $DIR/broken_format.rs:33:20
+  --> $DIR/broken_format.rs:39:20
    |
 LL | fn check_5(_: impl ImportantTrait5) {}
    |                    ^^^^^^^^^^^^^^^ required by this bound in `check_5`
 
-error: aborting due to 5 previous errors; 12 warnings emitted
+warning: invalid format specifier
+  --> $DIR/broken_format.rs:29:53
+   |
+LL | #[diagnostic::on_unimplemented(message = "Test {Self:}")]
+   |                                                     ^
+   |
+   = help: no format specifier are supported in this position
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error[E0277]: Test ()
+  --> $DIR/broken_format.rs:53:13
+   |
+LL |     check_6(());
+   |     ------- ^^ the trait `ImportantTrait6` is not implemented for `()`
+   |     |
+   |     required by a bound introduced by this call
+   |
+help: this trait has no implementations, consider adding one
+  --> $DIR/broken_format.rs:32:1
+   |
+LL | trait ImportantTrait6 {}
+   | ^^^^^^^^^^^^^^^^^^^^^
+note: required by a bound in `check_6`
+  --> $DIR/broken_format.rs:40:20
+   |
+LL | fn check_6(_: impl ImportantTrait6) {}
+   |                    ^^^^^^^^^^^^^^^ required by this bound in `check_6`
+
+error: aborting due to 6 previous errors; 14 warnings emitted
 
 For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/enum/dead-code-associated-function.rs b/tests/ui/enum/dead-code-associated-function.rs
new file mode 100644
index 00000000000..d172ceb41dd
--- /dev/null
+++ b/tests/ui/enum/dead-code-associated-function.rs
@@ -0,0 +1,20 @@
+//@ check-pass
+#![warn(dead_code)]
+
+enum E {
+    F(),
+    C(),
+}
+
+impl E {
+    #[expect(non_snake_case)]
+    fn F() {}
+    //~^ WARN: associated items `F` and `C` are never used
+
+    const C: () = ();
+}
+
+fn main() {
+    let _: E = E::F();
+    let _: E = E::C();
+}
diff --git a/tests/ui/enum/dead-code-associated-function.stderr b/tests/ui/enum/dead-code-associated-function.stderr
new file mode 100644
index 00000000000..e3c1a4c81a4
--- /dev/null
+++ b/tests/ui/enum/dead-code-associated-function.stderr
@@ -0,0 +1,30 @@
+warning: associated items `F` and `C` are never used
+  --> $DIR/dead-code-associated-function.rs:11:8
+   |
+LL | impl E {
+   | ------ associated items in this implementation
+LL |     #[expect(non_snake_case)]
+LL |     fn F() {}
+   |        ^
+...
+LL |     const C: () = ();
+   |           ^
+   |
+note: it is impossible to refer to the associated function `F` because it is shadowed by this enum variant with the same name
+  --> $DIR/dead-code-associated-function.rs:5:5
+   |
+LL |     F(),
+   |     ^
+note: it is impossible to refer to the associated constant `C` because it is shadowed by this enum variant with the same name
+  --> $DIR/dead-code-associated-function.rs:6:5
+   |
+LL |     C(),
+   |     ^
+note: the lint level is defined here
+  --> $DIR/dead-code-associated-function.rs:2:9
+   |
+LL | #![warn(dead_code)]
+   |         ^^^^^^^^^
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/error-codes/E0045.stderr b/tests/ui/error-codes/E0045.stderr
index b8ee31a4049..ecf5f4e0182 100644
--- a/tests/ui/error-codes/E0045.stderr
+++ b/tests/ui/error-codes/E0045.stderr
@@ -1,4 +1,4 @@
-error[E0045]: C-variadic function must have a compatible calling convention, like `C`, `cdecl`, `system`, `aapcs`, `win64`, `sysv64` or `efiapi`
+error[E0045]: C-variadic functions with the "Rust" calling convention are not supported
   --> $DIR/E0045.rs:1:17
    |
 LL | extern "Rust" { fn foo(x: u8, ...); }
diff --git a/tests/ui/error-codes/E0161.rs b/tests/ui/error-codes/E0161.rs
index 3a9b93d2430..1ae2a67d60e 100644
--- a/tests/ui/error-codes/E0161.rs
+++ b/tests/ui/error-codes/E0161.rs
@@ -1,12 +1,7 @@
 // Check that E0161 is a hard error in all possible configurations that might
 // affect it.
 
-//@ revisions: base ul
-//@[base] check-fail
-//@[ul] check-pass
-
-#![allow(incomplete_features)]
-#![cfg_attr(ul, feature(unsized_locals))]
+#![crate_type = "lib"]
 
 trait Bar {
     fn f(self);
@@ -14,7 +9,5 @@ trait Bar {
 
 fn foo(x: Box<dyn Bar>) {
     x.f();
-    //[base]~^ ERROR E0161
+    //~^ ERROR E0161
 }
-
-fn main() {}
diff --git a/tests/ui/error-codes/E0161.base.stderr b/tests/ui/error-codes/E0161.stderr
index d80de66b247..f84f348459b 100644
--- a/tests/ui/error-codes/E0161.base.stderr
+++ b/tests/ui/error-codes/E0161.stderr
@@ -1,5 +1,5 @@
 error[E0161]: cannot move a value of type `dyn Bar`
-  --> $DIR/E0161.rs:16:5
+  --> $DIR/E0161.rs:11:5
    |
 LL |     x.f();
    |     ^ the size of `dyn Bar` cannot be statically determined
diff --git a/tests/ui/extern/unsized-extern-derefmove.rs b/tests/ui/extern/unsized-extern-derefmove.rs
new file mode 100644
index 00000000000..4ec9e53f49d
--- /dev/null
+++ b/tests/ui/extern/unsized-extern-derefmove.rs
@@ -0,0 +1,15 @@
+//! Regression test for #79409
+
+#![feature(extern_types)]
+
+unsafe extern "C" {
+    type Device;
+}
+
+unsafe fn make_device() -> Box<Device> {
+    Box::from_raw(0 as *mut _)
+}
+
+fn main() {
+    let d: Device = unsafe { *make_device() }; //~ERROR the size for values of type `Device` cannot be known at compilation time
+}
diff --git a/tests/ui/extern/unsized-extern-derefmove.stderr b/tests/ui/extern/unsized-extern-derefmove.stderr
new file mode 100644
index 00000000000..c43184d94e1
--- /dev/null
+++ b/tests/ui/extern/unsized-extern-derefmove.stderr
@@ -0,0 +1,16 @@
+error[E0277]: the size for values of type `Device` cannot be known at compilation time
+  --> $DIR/unsized-extern-derefmove.rs:14:9
+   |
+LL |     let d: Device = unsafe { *make_device() };
+   |         ^ doesn't have a size known at compile-time
+   |
+   = help: the trait `Sized` is not implemented for `Device`
+   = note: all local variables must have a statically known size
+help: consider borrowing here
+   |
+LL |     let d: &Device = unsafe { *make_device() };
+   |            +
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/feature-gates/feature-gate-abi-custom.rs b/tests/ui/feature-gates/feature-gate-abi-custom.rs
new file mode 100644
index 00000000000..3ddce974dd7
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-abi-custom.rs
@@ -0,0 +1,51 @@
+//@ add-core-stubs
+//@ needs-asm-support
+#![no_core]
+#![feature(no_core, lang_items)]
+#![crate_type = "rlib"]
+
+extern crate minicore;
+use minicore::*;
+
+#[unsafe(naked)]
+unsafe extern "custom" fn f7() {
+    //~^ ERROR "custom" ABI is experimental
+    naked_asm!("")
+}
+trait Tr {
+    extern "custom" fn m7();
+    //~^ ERROR "custom" ABI is experimental
+    //~| ERROR functions with the `"custom"` ABI must be unsafe
+    #[unsafe(naked)]
+    extern "custom" fn dm7() {
+        //~^ ERROR "custom" ABI is experimental
+        //~| ERROR functions with the `"custom"` ABI must be unsafe
+        naked_asm!("")
+    }
+}
+
+struct S;
+
+// Methods in trait impl
+impl Tr for S {
+    #[unsafe(naked)]
+    extern "custom" fn m7() {
+        //~^ ERROR "custom" ABI is experimental
+        //~| ERROR functions with the `"custom"` ABI must be unsafe
+        naked_asm!("")
+    }
+}
+
+// Methods in inherent impl
+impl S {
+    #[unsafe(naked)]
+    extern "custom" fn im7() {
+        //~^ ERROR "custom" ABI is experimental
+        //~| ERROR functions with the `"custom"` ABI must be unsafe
+        naked_asm!("")
+    }
+}
+
+type A7 = extern "custom" fn(); //~ ERROR "custom" ABI is experimental
+
+extern "custom" {} //~ ERROR "custom" ABI is experimental
diff --git a/tests/ui/feature-gates/feature-gate-abi-custom.stderr b/tests/ui/feature-gates/feature-gate-abi-custom.stderr
new file mode 100644
index 00000000000..e6dce0126d6
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-abi-custom.stderr
@@ -0,0 +1,117 @@
+error: functions with the `"custom"` ABI must be unsafe
+  --> $DIR/feature-gate-abi-custom.rs:16:5
+   |
+LL |     extern "custom" fn m7();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: add the `unsafe` keyword to this definition
+   |
+LL |     unsafe extern "custom" fn m7();
+   |     ++++++
+
+error: functions with the `"custom"` ABI must be unsafe
+  --> $DIR/feature-gate-abi-custom.rs:20:5
+   |
+LL |     extern "custom" fn dm7() {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: add the `unsafe` keyword to this definition
+   |
+LL |     unsafe extern "custom" fn dm7() {
+   |     ++++++
+
+error: functions with the `"custom"` ABI must be unsafe
+  --> $DIR/feature-gate-abi-custom.rs:32:5
+   |
+LL |     extern "custom" fn m7() {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: add the `unsafe` keyword to this definition
+   |
+LL |     unsafe extern "custom" fn m7() {
+   |     ++++++
+
+error: functions with the `"custom"` ABI must be unsafe
+  --> $DIR/feature-gate-abi-custom.rs:42:5
+   |
+LL |     extern "custom" fn im7() {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: add the `unsafe` keyword to this definition
+   |
+LL |     unsafe extern "custom" fn im7() {
+   |     ++++++
+
+error[E0658]: the extern "custom" ABI is experimental and subject to change
+  --> $DIR/feature-gate-abi-custom.rs:11:15
+   |
+LL | unsafe extern "custom" fn f7() {
+   |               ^^^^^^^^
+   |
+   = note: see issue #140829 <https://github.com/rust-lang/rust/issues/140829> for more information
+   = help: add `#![feature(abi_custom)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: the extern "custom" ABI is experimental and subject to change
+  --> $DIR/feature-gate-abi-custom.rs:16:12
+   |
+LL |     extern "custom" fn m7();
+   |            ^^^^^^^^
+   |
+   = note: see issue #140829 <https://github.com/rust-lang/rust/issues/140829> for more information
+   = help: add `#![feature(abi_custom)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: the extern "custom" ABI is experimental and subject to change
+  --> $DIR/feature-gate-abi-custom.rs:20:12
+   |
+LL |     extern "custom" fn dm7() {
+   |            ^^^^^^^^
+   |
+   = note: see issue #140829 <https://github.com/rust-lang/rust/issues/140829> for more information
+   = help: add `#![feature(abi_custom)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: the extern "custom" ABI is experimental and subject to change
+  --> $DIR/feature-gate-abi-custom.rs:32:12
+   |
+LL |     extern "custom" fn m7() {
+   |            ^^^^^^^^
+   |
+   = note: see issue #140829 <https://github.com/rust-lang/rust/issues/140829> for more information
+   = help: add `#![feature(abi_custom)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: the extern "custom" ABI is experimental and subject to change
+  --> $DIR/feature-gate-abi-custom.rs:42:12
+   |
+LL |     extern "custom" fn im7() {
+   |            ^^^^^^^^
+   |
+   = note: see issue #140829 <https://github.com/rust-lang/rust/issues/140829> for more information
+   = help: add `#![feature(abi_custom)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: the extern "custom" ABI is experimental and subject to change
+  --> $DIR/feature-gate-abi-custom.rs:49:18
+   |
+LL | type A7 = extern "custom" fn();
+   |                  ^^^^^^^^
+   |
+   = note: see issue #140829 <https://github.com/rust-lang/rust/issues/140829> for more information
+   = help: add `#![feature(abi_custom)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: the extern "custom" ABI is experimental and subject to change
+  --> $DIR/feature-gate-abi-custom.rs:51:8
+   |
+LL | extern "custom" {}
+   |        ^^^^^^^^
+   |
+   = note: see issue #140829 <https://github.com/rust-lang/rust/issues/140829> for more information
+   = help: add `#![feature(abi_custom)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error: aborting due to 11 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.AMDGPU.stderr b/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.AMDGPU.stderr
new file mode 100644
index 00000000000..fca32c5c1e6
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.AMDGPU.stderr
@@ -0,0 +1,73 @@
+error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:16:8
+   |
+LL | extern "gpu-kernel" fn f1(_: ()) {}
+   |        ^^^^^^^^^^^^
+   |
+   = note: see issue #135467 <https://github.com/rust-lang/rust/issues/135467> for more information
+   = help: add `#![feature(abi_gpu_kernel)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:21:12
+   |
+LL |     extern "gpu-kernel" fn m1(_: ());
+   |            ^^^^^^^^^^^^
+   |
+   = note: see issue #135467 <https://github.com/rust-lang/rust/issues/135467> for more information
+   = help: add `#![feature(abi_gpu_kernel)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:23:12
+   |
+LL |     extern "gpu-kernel" fn dm1(_: ()) {}
+   |            ^^^^^^^^^^^^
+   |
+   = note: see issue #135467 <https://github.com/rust-lang/rust/issues/135467> for more information
+   = help: add `#![feature(abi_gpu_kernel)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:31:12
+   |
+LL |     extern "gpu-kernel" fn m1(_: ()) {}
+   |            ^^^^^^^^^^^^
+   |
+   = note: see issue #135467 <https://github.com/rust-lang/rust/issues/135467> for more information
+   = help: add `#![feature(abi_gpu_kernel)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:37:12
+   |
+LL |     extern "gpu-kernel" fn im1(_: ()) {}
+   |            ^^^^^^^^^^^^
+   |
+   = note: see issue #135467 <https://github.com/rust-lang/rust/issues/135467> for more information
+   = help: add `#![feature(abi_gpu_kernel)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:42:18
+   |
+LL | type A1 = extern "gpu-kernel" fn(_: ());
+   |                  ^^^^^^^^^^^^
+   |
+   = note: see issue #135467 <https://github.com/rust-lang/rust/issues/135467> for more information
+   = help: add `#![feature(abi_gpu_kernel)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:47:8
+   |
+LL | extern "gpu-kernel" {}
+   |        ^^^^^^^^^^^^
+   |
+   = note: see issue #135467 <https://github.com/rust-lang/rust/issues/135467> for more information
+   = help: add `#![feature(abi_gpu_kernel)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error: aborting due to 7 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.stderr b/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.HOST.stderr
index aa9c67f0151..cc81289f6b7 100644
--- a/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.stderr
+++ b/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.HOST.stderr
@@ -1,5 +1,5 @@
 error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change
-  --> $DIR/feature-gate-abi_gpu_kernel.rs:11:8
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:16:8
    |
 LL | extern "gpu-kernel" fn f1(_: ()) {}
    |        ^^^^^^^^^^^^
@@ -9,7 +9,7 @@ LL | extern "gpu-kernel" fn f1(_: ()) {}
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change
-  --> $DIR/feature-gate-abi_gpu_kernel.rs:16:12
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:21:12
    |
 LL |     extern "gpu-kernel" fn m1(_: ());
    |            ^^^^^^^^^^^^
@@ -19,7 +19,7 @@ LL |     extern "gpu-kernel" fn m1(_: ());
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change
-  --> $DIR/feature-gate-abi_gpu_kernel.rs:18:12
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:23:12
    |
 LL |     extern "gpu-kernel" fn dm1(_: ()) {}
    |            ^^^^^^^^^^^^
@@ -29,7 +29,7 @@ LL |     extern "gpu-kernel" fn dm1(_: ()) {}
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change
-  --> $DIR/feature-gate-abi_gpu_kernel.rs:26:12
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:31:12
    |
 LL |     extern "gpu-kernel" fn m1(_: ()) {}
    |            ^^^^^^^^^^^^
@@ -39,7 +39,7 @@ LL |     extern "gpu-kernel" fn m1(_: ()) {}
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change
-  --> $DIR/feature-gate-abi_gpu_kernel.rs:32:12
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:37:12
    |
 LL |     extern "gpu-kernel" fn im1(_: ()) {}
    |            ^^^^^^^^^^^^
@@ -49,7 +49,7 @@ LL |     extern "gpu-kernel" fn im1(_: ()) {}
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change
-  --> $DIR/feature-gate-abi_gpu_kernel.rs:37:18
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:42:18
    |
 LL | type A1 = extern "gpu-kernel" fn(_: ());
    |                  ^^^^^^^^^^^^
@@ -59,7 +59,7 @@ LL | type A1 = extern "gpu-kernel" fn(_: ());
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change
-  --> $DIR/feature-gate-abi_gpu_kernel.rs:42:8
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:47:8
    |
 LL | extern "gpu-kernel" {}
    |        ^^^^^^^^^^^^
@@ -69,7 +69,7 @@ LL | extern "gpu-kernel" {}
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 warning: the calling convention "gpu-kernel" is not supported on this target
-  --> $DIR/feature-gate-abi_gpu_kernel.rs:37:11
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:42:11
    |
 LL | type A1 = extern "gpu-kernel" fn(_: ());
    |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -79,31 +79,31 @@ LL | type A1 = extern "gpu-kernel" fn(_: ());
    = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
 
 error[E0570]: `"gpu-kernel"` is not a supported ABI for the current target
-  --> $DIR/feature-gate-abi_gpu_kernel.rs:42:1
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:47:1
    |
 LL | extern "gpu-kernel" {}
    | ^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0570]: `"gpu-kernel"` is not a supported ABI for the current target
-  --> $DIR/feature-gate-abi_gpu_kernel.rs:11:1
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:16:1
    |
 LL | extern "gpu-kernel" fn f1(_: ()) {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0570]: `"gpu-kernel"` is not a supported ABI for the current target
-  --> $DIR/feature-gate-abi_gpu_kernel.rs:18:5
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:23:5
    |
 LL |     extern "gpu-kernel" fn dm1(_: ()) {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0570]: `"gpu-kernel"` is not a supported ABI for the current target
-  --> $DIR/feature-gate-abi_gpu_kernel.rs:26:5
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:31:5
    |
 LL |     extern "gpu-kernel" fn m1(_: ()) {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0570]: `"gpu-kernel"` is not a supported ABI for the current target
-  --> $DIR/feature-gate-abi_gpu_kernel.rs:32:5
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:37:5
    |
 LL |     extern "gpu-kernel" fn im1(_: ()) {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -114,7 +114,7 @@ Some errors have detailed explanations: E0570, E0658.
 For more information about an error, try `rustc --explain E0570`.
 Future incompatibility report: Future breakage diagnostic:
 warning: the calling convention "gpu-kernel" is not supported on this target
-  --> $DIR/feature-gate-abi_gpu_kernel.rs:37:11
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:42:11
    |
 LL | type A1 = extern "gpu-kernel" fn(_: ());
    |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.NVPTX.stderr b/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.NVPTX.stderr
new file mode 100644
index 00000000000..fca32c5c1e6
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.NVPTX.stderr
@@ -0,0 +1,73 @@
+error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:16:8
+   |
+LL | extern "gpu-kernel" fn f1(_: ()) {}
+   |        ^^^^^^^^^^^^
+   |
+   = note: see issue #135467 <https://github.com/rust-lang/rust/issues/135467> for more information
+   = help: add `#![feature(abi_gpu_kernel)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:21:12
+   |
+LL |     extern "gpu-kernel" fn m1(_: ());
+   |            ^^^^^^^^^^^^
+   |
+   = note: see issue #135467 <https://github.com/rust-lang/rust/issues/135467> for more information
+   = help: add `#![feature(abi_gpu_kernel)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:23:12
+   |
+LL |     extern "gpu-kernel" fn dm1(_: ()) {}
+   |            ^^^^^^^^^^^^
+   |
+   = note: see issue #135467 <https://github.com/rust-lang/rust/issues/135467> for more information
+   = help: add `#![feature(abi_gpu_kernel)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:31:12
+   |
+LL |     extern "gpu-kernel" fn m1(_: ()) {}
+   |            ^^^^^^^^^^^^
+   |
+   = note: see issue #135467 <https://github.com/rust-lang/rust/issues/135467> for more information
+   = help: add `#![feature(abi_gpu_kernel)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:37:12
+   |
+LL |     extern "gpu-kernel" fn im1(_: ()) {}
+   |            ^^^^^^^^^^^^
+   |
+   = note: see issue #135467 <https://github.com/rust-lang/rust/issues/135467> for more information
+   = help: add `#![feature(abi_gpu_kernel)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:42:18
+   |
+LL | type A1 = extern "gpu-kernel" fn(_: ());
+   |                  ^^^^^^^^^^^^
+   |
+   = note: see issue #135467 <https://github.com/rust-lang/rust/issues/135467> for more information
+   = help: add `#![feature(abi_gpu_kernel)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:47:8
+   |
+LL | extern "gpu-kernel" {}
+   |        ^^^^^^^^^^^^
+   |
+   = note: see issue #135467 <https://github.com/rust-lang/rust/issues/135467> for more information
+   = help: add `#![feature(abi_gpu_kernel)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error: aborting due to 7 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.rs b/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.rs
index d9027b417b4..7b1ee681dd7 100644
--- a/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.rs
+++ b/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.rs
@@ -1,5 +1,10 @@
+//@ revisions: HOST AMDGPU NVPTX
 //@ add-core-stubs
 //@ compile-flags: --crate-type=rlib
+//@[AMDGPU] compile-flags: --target amdgcn-amd-amdhsa -Ctarget-cpu=gfx1100
+//@[AMDGPU] needs-llvm-components: amdgpu
+//@[NVPTX]  compile-flags: --target nvptx64-nvidia-cuda
+//@[NVPTX] needs-llvm-components: nvptx
 
 #![feature(no_core, lang_items)]
 #![no_core]
@@ -9,14 +14,14 @@ use minicore::*;
 
 // Functions
 extern "gpu-kernel" fn f1(_: ()) {} //~ ERROR "gpu-kernel" ABI is experimental and subject to change
-//~^ ERROR is not a supported ABI
+//[HOST]~^ ERROR is not a supported ABI
 
 // Methods in trait definition
 trait Tr {
     extern "gpu-kernel" fn m1(_: ()); //~ ERROR "gpu-kernel" ABI is experimental and subject to change
 
     extern "gpu-kernel" fn dm1(_: ()) {} //~ ERROR "gpu-kernel" ABI is experimental and subject to change
-    //~^ ERROR is not a supported ABI
+    //[HOST]~^ ERROR is not a supported ABI
 }
 
 struct S;
@@ -24,20 +29,20 @@ struct S;
 // Methods in trait impl
 impl Tr for S {
     extern "gpu-kernel" fn m1(_: ()) {} //~ ERROR "gpu-kernel" ABI is experimental and subject to change
-    //~^ ERROR is not a supported ABI
+    //[HOST]~^ ERROR is not a supported ABI
 }
 
 // Methods in inherent impl
 impl S {
     extern "gpu-kernel" fn im1(_: ()) {} //~ ERROR "gpu-kernel" ABI is experimental and subject to change
-    //~^ ERROR is not a supported ABI
+    //[HOST]~^ ERROR is not a supported ABI
 }
 
 // Function pointer types
 type A1 = extern "gpu-kernel" fn(_: ()); //~ ERROR "gpu-kernel" ABI is experimental and subject to change
-//~^ WARN the calling convention "gpu-kernel" is not supported on this target
-//~^^ WARN this was previously accepted by the compiler but is being phased out
+//[HOST]~^ WARNING the calling convention "gpu-kernel" is not supported on this target [unsupported_fn_ptr_calling_conventions]
+//[HOST]~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
 
 // Foreign modules
 extern "gpu-kernel" {} //~ ERROR "gpu-kernel" ABI is experimental and subject to change
-//~^ ERROR is not a supported ABI
+//[HOST]~^ ERROR is not a supported ABI
diff --git a/tests/ui/feature-gates/feature-gate-extern_system_varargs.rs b/tests/ui/feature-gates/feature-gate-extern_system_varargs.rs
index f5cfbe72ca8..2206776ccca 100644
--- a/tests/ui/feature-gates/feature-gate-extern_system_varargs.rs
+++ b/tests/ui/feature-gates/feature-gate-extern_system_varargs.rs
@@ -1,5 +1,5 @@
 fn system(f: extern "system" fn(usize, ...)) {
-    //~^  ERROR using calling conventions other than `C` or `cdecl` for varargs functions is unstable
+    //~^  ERROR unstable
 
     f(22, 44);
 }
diff --git a/tests/ui/feature-gates/feature-gate-extern_system_varargs.stderr b/tests/ui/feature-gates/feature-gate-extern_system_varargs.stderr
index f3206b25264..1209275f719 100644
--- a/tests/ui/feature-gates/feature-gate-extern_system_varargs.stderr
+++ b/tests/ui/feature-gates/feature-gate-extern_system_varargs.stderr
@@ -1,4 +1,4 @@
-error[E0658]: using calling conventions other than `C` or `cdecl` for varargs functions is unstable
+error[E0658]: C-variadic functions with the "system" calling convention are unstable
   --> $DIR/feature-gate-extern_system_varargs.rs:1:14
    |
 LL | fn system(f: extern "system" fn(usize, ...)) {
diff --git a/tests/ui/feature-gates/feature-gate-pattern-complexity-limit.rs b/tests/ui/feature-gates/feature-gate-pattern-complexity-limit.rs
index d1f6f4755f0..ffb444cd9aa 100644
--- a/tests/ui/feature-gates/feature-gate-pattern-complexity-limit.rs
+++ b/tests/ui/feature-gates/feature-gate-pattern-complexity-limit.rs
@@ -1,6 +1,8 @@
 // check that `pattern_complexity_limit` is feature-gated
 
 #![pattern_complexity_limit = "42"]
-//~^ ERROR: the `#[pattern_complexity_limit]` attribute is just used for rustc unit tests
+//~^ ERROR: use of an internal attribute [E0658]
+//~| NOTE the `#[pattern_complexity_limit]` attribute is an internal implementation detail that will never be stable
+//~| NOTE: the `#[pattern_complexity_limit]` attribute is used for rustc unit tests
 
 fn main() {}
diff --git a/tests/ui/feature-gates/feature-gate-pattern-complexity-limit.stderr b/tests/ui/feature-gates/feature-gate-pattern-complexity-limit.stderr
index e6f17710e09..9ddea866ea9 100644
--- a/tests/ui/feature-gates/feature-gate-pattern-complexity-limit.stderr
+++ b/tests/ui/feature-gates/feature-gate-pattern-complexity-limit.stderr
@@ -1,11 +1,12 @@
-error[E0658]: the `#[pattern_complexity_limit]` attribute is just used for rustc unit tests and will never be stable
+error[E0658]: use of an internal attribute
   --> $DIR/feature-gate-pattern-complexity-limit.rs:3:1
    |
 LL | #![pattern_complexity_limit = "42"]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+   = note: the `#[pattern_complexity_limit]` attribute is an internal implementation detail that will never be stable
+   = note: the `#[pattern_complexity_limit]` attribute is used for rustc unit tests
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/feature-gates/feature-gate-rustc-attrs-1.rs b/tests/ui/feature-gates/feature-gate-rustc-attrs-1.rs
index 025b4b52f12..17556723622 100644
--- a/tests/ui/feature-gates/feature-gate-rustc-attrs-1.rs
+++ b/tests/ui/feature-gates/feature-gate-rustc-attrs-1.rs
@@ -1,6 +1,12 @@
 // Test that `#[rustc_*]` attributes are gated by `rustc_attrs` feature gate.
 
-#[rustc_variance] //~ ERROR the `#[rustc_variance]` attribute is just used for rustc unit tests and will never be stable
-#[rustc_nonnull_optimization_guaranteed] //~ ERROR the `#[rustc_nonnull_optimization_guaranteed]` attribute is just used to document guaranteed niche optimizations in libcore and libstd and will never be stable
-
+#[rustc_variance]
+//~^ ERROR use of an internal attribute [E0658]
+//~| NOTE the `#[rustc_variance]` attribute is an internal implementation detail that will never be stable
+//~| NOTE the `#[rustc_variance]` attribute is used for rustc unit tests
+#[rustc_nonnull_optimization_guaranteed]
+//~^ ERROR use of an internal attribute [E0658]
+//~| NOTE the `#[rustc_nonnull_optimization_guaranteed]` attribute is an internal implementation detail that will never be stable
+//~| NOTE  the `#[rustc_nonnull_optimization_guaranteed]` attribute is just used to document guaranteed niche optimizations in the standard library
+//~| NOTE the compiler does not even check whether the type indeed is being non-null-optimized; it is your responsibility to ensure that the attribute is only used on types that are optimized
 fn main() {}
diff --git a/tests/ui/feature-gates/feature-gate-rustc-attrs-1.stderr b/tests/ui/feature-gates/feature-gate-rustc-attrs-1.stderr
index 0f760e0602d..159d383e408 100644
--- a/tests/ui/feature-gates/feature-gate-rustc-attrs-1.stderr
+++ b/tests/ui/feature-gates/feature-gate-rustc-attrs-1.stderr
@@ -1,21 +1,23 @@
-error[E0658]: the `#[rustc_variance]` attribute is just used for rustc unit tests and will never be stable
+error[E0658]: use of an internal attribute
   --> $DIR/feature-gate-rustc-attrs-1.rs:3:1
    |
 LL | #[rustc_variance]
    | ^^^^^^^^^^^^^^^^^
    |
    = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+   = note: the `#[rustc_variance]` attribute is an internal implementation detail that will never be stable
+   = note: the `#[rustc_variance]` attribute is used for rustc unit tests
 
-error[E0658]: the `#[rustc_nonnull_optimization_guaranteed]` attribute is just used to document guaranteed niche optimizations in libcore and libstd and will never be stable
-              (note that the compiler does not even check whether the type indeed is being non-null-optimized; it is your responsibility to ensure that the attribute is only used on types that are optimized)
-  --> $DIR/feature-gate-rustc-attrs-1.rs:4:1
+error[E0658]: use of an internal attribute
+  --> $DIR/feature-gate-rustc-attrs-1.rs:7:1
    |
 LL | #[rustc_nonnull_optimization_guaranteed]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+   = note: the `#[rustc_nonnull_optimization_guaranteed]` attribute is an internal implementation detail that will never be stable
+   = note: the `#[rustc_nonnull_optimization_guaranteed]` attribute is just used to document guaranteed niche optimizations in the standard library
+   = note: the compiler does not even check whether the type indeed is being non-null-optimized; it is your responsibility to ensure that the attribute is only used on types that are optimized
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/feature-gates/feature-gate-rustc-attrs.rs b/tests/ui/feature-gates/feature-gate-rustc-attrs.rs
index c985298a30a..e7b2eca6f85 100644
--- a/tests/ui/feature-gates/feature-gate-rustc-attrs.rs
+++ b/tests/ui/feature-gates/feature-gate-rustc-attrs.rs
@@ -8,15 +8,19 @@ mod unknown { pub macro rustc() {} }
 #[rustc::unknown]
 //~^ ERROR attributes starting with `rustc` are reserved for use by the `rustc` compiler
 //~| ERROR expected attribute, found macro `rustc::unknown`
+//~| NOTE not an attribute
 fn f() {}
 
 #[unknown::rustc]
 //~^ ERROR attributes starting with `rustc` are reserved for use by the `rustc` compiler
 //~| ERROR expected attribute, found macro `unknown::rustc`
+//~| NOTE not an attribute
 fn g() {}
 
 #[rustc_dummy]
-//~^ ERROR the `#[rustc_dummy]` attribute is just used for rustc unit tests
+//~^ ERROR use of an internal attribute [E0658]
+//~| NOTE the `#[rustc_dummy]` attribute is an internal implementation detail that will never be stable
+//~| NOTE the `#[rustc_dummy]` attribute is used for rustc unit tests
 #[rustc_unknown]
 //~^ ERROR attributes starting with `rustc` are reserved for use by the `rustc` compiler
 //~| ERROR cannot find attribute `rustc_unknown` in this scope
diff --git a/tests/ui/feature-gates/feature-gate-rustc-attrs.stderr b/tests/ui/feature-gates/feature-gate-rustc-attrs.stderr
index c7a5ef3e44b..d58603883f1 100644
--- a/tests/ui/feature-gates/feature-gate-rustc-attrs.stderr
+++ b/tests/ui/feature-gates/feature-gate-rustc-attrs.stderr
@@ -11,37 +11,38 @@ LL | #[rustc::unknown]
    |   ^^^^^^^^^^^^^^ not an attribute
 
 error: attributes starting with `rustc` are reserved for use by the `rustc` compiler
-  --> $DIR/feature-gate-rustc-attrs.rs:13:12
+  --> $DIR/feature-gate-rustc-attrs.rs:14:12
    |
 LL | #[unknown::rustc]
    |            ^^^^^
 
 error: expected attribute, found macro `unknown::rustc`
-  --> $DIR/feature-gate-rustc-attrs.rs:13:3
+  --> $DIR/feature-gate-rustc-attrs.rs:14:3
    |
 LL | #[unknown::rustc]
    |   ^^^^^^^^^^^^^^ not an attribute
 
 error: attributes starting with `rustc` are reserved for use by the `rustc` compiler
-  --> $DIR/feature-gate-rustc-attrs.rs:20:3
+  --> $DIR/feature-gate-rustc-attrs.rs:24:3
    |
 LL | #[rustc_unknown]
    |   ^^^^^^^^^^^^^
 
 error: cannot find attribute `rustc_unknown` in this scope
-  --> $DIR/feature-gate-rustc-attrs.rs:20:3
+  --> $DIR/feature-gate-rustc-attrs.rs:24:3
    |
 LL | #[rustc_unknown]
    |   ^^^^^^^^^^^^^
 
-error[E0658]: the `#[rustc_dummy]` attribute is just used for rustc unit tests and will never be stable
-  --> $DIR/feature-gate-rustc-attrs.rs:18:1
+error[E0658]: use of an internal attribute
+  --> $DIR/feature-gate-rustc-attrs.rs:20:1
    |
 LL | #[rustc_dummy]
    | ^^^^^^^^^^^^^^
    |
    = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+   = note: the `#[rustc_dummy]` attribute is an internal implementation detail that will never be stable
+   = note: the `#[rustc_dummy]` attribute is used for rustc unit tests
 
 error: aborting due to 7 previous errors
 
diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs
index 02a56c7e6aa..7fb11b7bde7 100644
--- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs
+++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs
@@ -11,9 +11,11 @@
 
 #![macro_export]
 //~^ ERROR: `macro_export` attribute cannot be used at crate level
-#![rustc_main] //~ ERROR: the `#[rustc_main]` attribute is used internally to specify
+#![rustc_main]
 //~^ ERROR: `rustc_main` attribute cannot be used at crate level
-//~| NOTE: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+//~| ERROR: use of an internal attribute [E0658]
+//~| NOTE: the `#[rustc_main]` attribute is an internal implementation detail that will never be stable
+//~| NOTE: the `#[rustc_main]` attribute is used internally to specify test entry point function
 #![repr()]
 //~^ ERROR: `repr` attribute cannot be used at crate level
 #![path = "3800"]
diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr
index 5c2a3ae699c..bdca6163473 100644
--- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr
+++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr
@@ -1,14 +1,15 @@
-error[E0658]: the `#[rustc_main]` attribute is used internally to specify test entry point function
+error[E0658]: use of an internal attribute
   --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:14:1
    |
 LL | #![rustc_main]
    | ^^^^^^^^^^^^^^
    |
    = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+   = note: the `#[rustc_main]` attribute is an internal implementation detail that will never be stable
+   = note: the `#[rustc_main]` attribute is used internally to specify test entry point function
 
 error: valid forms for the attribute are `#[inline]` and `#[inline(always|never)]`
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:44:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:46:5
    |
 LL |     #[inline = "2100"] fn f() { }
    |     ^^^^^^^^^^^^^^^^^^
@@ -18,7 +19,7 @@ LL |     #[inline = "2100"] fn f() { }
    = note: `#[deny(ill_formed_attribute_input)]` on by default
 
 error[E0518]: attribute should be applied to function or closure
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:30:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:32:1
    |
 LL |   #[inline]
    |   ^^^^^^^^^
@@ -29,7 +30,7 @@ LL | | }
    | |_- not a function or closure
 
 error: attribute should be applied to an `extern crate` item
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:63:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:65:1
    |
 LL |   #[no_link]
    |   ^^^^^^^^^^
@@ -43,7 +44,7 @@ LL | | }
    | |_- not an `extern crate` item
 
 error: attribute should be applied to a free function, impl method or static
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:89:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:91:1
    |
 LL |   #[export_name = "2200"]
    |   ^^^^^^^^^^^^^^^^^^^^^^^
@@ -57,7 +58,7 @@ LL | | }
    | |_- not a free function, impl method or static
 
 error[E0517]: attribute should be applied to a struct, enum, or union
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:123:8
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:125:8
    |
 LL |   #[repr(C)]
    |          ^
@@ -70,7 +71,7 @@ LL | | }
    | |_- not a struct, enum, or union
 
 error[E0517]: attribute should be applied to a struct, enum, or union
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:147:8
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:149:8
    |
 LL |   #[repr(Rust)]
    |          ^^^^
@@ -83,19 +84,19 @@ LL | | }
    | |_- not a struct, enum, or union
 
 error: attribute should be applied to an `extern crate` item
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:24:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:26:1
    |
 LL | #![no_link]
    | ^^^^^^^^^^^ not an `extern crate` item
 
 error: attribute should be applied to a free function, impl method or static
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:26:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:28:1
    |
 LL | #![export_name = "2200"]
    | ^^^^^^^^^^^^^^^^^^^^^^^^ not a free function, impl method or static
 
 error[E0518]: attribute should be applied to function or closure
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:28:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:30:1
    |
 LL | #![inline]
    | ^^^^^^^^^^ not a function or closure
@@ -131,7 +132,7 @@ LL + #[rustc_main]
    |
 
 error: `path` attribute cannot be used at crate level
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:19:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:21:1
    |
 LL | #![path = "3800"]
    | ^^^^^^^^^^^^^^^^^
@@ -146,7 +147,7 @@ LL + #[path = "3800"]
    |
 
 error: `automatically_derived` attribute cannot be used at crate level
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:21:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:23:1
    |
 LL | #![automatically_derived]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -161,7 +162,7 @@ LL + #[automatically_derived]
    |
 
 error: `repr` attribute cannot be used at crate level
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:17:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:19:1
    |
 LL | #![repr()]
    | ^^^^^^^^^^
@@ -176,139 +177,139 @@ LL + #[repr()]
    |
 
 error[E0518]: attribute should be applied to function or closure
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:40:17
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:42:17
    |
 LL |     mod inner { #![inline] }
    |     ------------^^^^^^^^^^-- not a function or closure
 
 error[E0518]: attribute should be applied to function or closure
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:50:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:52:5
    |
 LL |     #[inline] struct S;
    |     ^^^^^^^^^ --------- not a function or closure
 
 error[E0518]: attribute should be applied to function or closure
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:54:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:56:5
    |
 LL |     #[inline] type T = S;
    |     ^^^^^^^^^ ----------- not a function or closure
 
 error[E0518]: attribute should be applied to function or closure
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:58:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:60:5
    |
 LL |     #[inline] impl S { }
    |     ^^^^^^^^^ ---------- not a function or closure
 
 error: attribute should be applied to an `extern crate` item
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:68:17
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:70:17
    |
 LL |     mod inner { #![no_link] }
    |     ------------^^^^^^^^^^^-- not an `extern crate` item
 
 error: attribute should be applied to an `extern crate` item
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:72:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:74:5
    |
 LL |     #[no_link] fn f() { }
    |     ^^^^^^^^^^ ---------- not an `extern crate` item
 
 error: attribute should be applied to an `extern crate` item
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:76:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:78:5
    |
 LL |     #[no_link] struct S;
    |     ^^^^^^^^^^ --------- not an `extern crate` item
 
 error: attribute should be applied to an `extern crate` item
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:80:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:82:5
    |
 LL |     #[no_link]type T = S;
    |     ^^^^^^^^^^----------- not an `extern crate` item
 
 error: attribute should be applied to an `extern crate` item
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:84:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:86:5
    |
 LL |     #[no_link] impl S { }
    |     ^^^^^^^^^^ ---------- not an `extern crate` item
 
 error: attribute should be applied to a free function, impl method or static
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:94:17
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:96:17
    |
 LL |     mod inner { #![export_name="2200"] }
    |     ------------^^^^^^^^^^^^^^^^^^^^^^-- not a free function, impl method or static
 
 error: attribute should be applied to a free function, impl method or static
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:100:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:102:5
    |
 LL |     #[export_name = "2200"] struct S;
    |     ^^^^^^^^^^^^^^^^^^^^^^^ --------- not a free function, impl method or static
 
 error: attribute should be applied to a free function, impl method or static
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:104:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:106:5
    |
 LL |     #[export_name = "2200"] type T = S;
    |     ^^^^^^^^^^^^^^^^^^^^^^^ ----------- not a free function, impl method or static
 
 error: attribute should be applied to a free function, impl method or static
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:108:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:110:5
    |
 LL |     #[export_name = "2200"] impl S { }
    |     ^^^^^^^^^^^^^^^^^^^^^^^ ---------- not a free function, impl method or static
 
 error: attribute should be applied to a free function, impl method or static
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:113:9
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:115:9
    |
 LL |         #[export_name = "2200"] fn foo();
    |         ^^^^^^^^^^^^^^^^^^^^^^^ --------- not a free function, impl method or static
 
 error: attribute should be applied to a free function, impl method or static
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:117:9
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:119:9
    |
 LL |         #[export_name = "2200"] fn bar() {}
    |         ^^^^^^^^^^^^^^^^^^^^^^^ ----------- not a free function, impl method or static
 
 error[E0517]: attribute should be applied to a struct, enum, or union
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:127:25
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:129:25
    |
 LL |     mod inner { #![repr(C)] }
    |     --------------------^---- not a struct, enum, or union
 
 error[E0517]: attribute should be applied to a struct, enum, or union
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:131:12
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:133:12
    |
 LL |     #[repr(C)] fn f() { }
    |            ^   ---------- not a struct, enum, or union
 
 error[E0517]: attribute should be applied to a struct, enum, or union
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:137:12
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:139:12
    |
 LL |     #[repr(C)] type T = S;
    |            ^   ----------- not a struct, enum, or union
 
 error[E0517]: attribute should be applied to a struct, enum, or union
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:141:12
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:143:12
    |
 LL |     #[repr(C)] impl S { }
    |            ^   ---------- not a struct, enum, or union
 
 error[E0517]: attribute should be applied to a struct, enum, or union
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:151:25
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:153:25
    |
 LL |     mod inner { #![repr(Rust)] }
    |     --------------------^^^^---- not a struct, enum, or union
 
 error[E0517]: attribute should be applied to a struct, enum, or union
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:155:12
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:157:12
    |
 LL |     #[repr(Rust)] fn f() { }
    |            ^^^^   ---------- not a struct, enum, or union
 
 error[E0517]: attribute should be applied to a struct, enum, or union
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:161:12
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:163:12
    |
 LL |     #[repr(Rust)] type T = S;
    |            ^^^^   ----------- not a struct, enum, or union
 
 error[E0517]: attribute should be applied to a struct, enum, or union
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:165:12
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:167:12
    |
 LL |     #[repr(Rust)] impl S { }
    |            ^^^^   ---------- not a struct, enum, or union
diff --git a/tests/ui/force-inlining/gate.rs b/tests/ui/force-inlining/gate.rs
index cea094c14f2..5918b0d4979 100644
--- a/tests/ui/force-inlining/gate.rs
+++ b/tests/ui/force-inlining/gate.rs
@@ -2,11 +2,15 @@
 #![allow(internal_features)]
 
 #[rustc_force_inline]
-//~^ ERROR #[rustc_force_inline] forces a free function to be inlined
+//~^ ERROR use of an internal attribute [E0658]
+//~| NOTE the `#[rustc_force_inline]` attribute is an internal implementation detail that will never be stable
+//~| NOTE `#[rustc_force_inline]` forces a free function to be inlined
 pub fn bare() {
 }
 
 #[rustc_force_inline = "the test requires it"]
-//~^ ERROR #[rustc_force_inline] forces a free function to be inlined
+//~^ ERROR use of an internal attribute [E0658]
+//~| NOTE the `#[rustc_force_inline]` attribute is an internal implementation detail that will never be stable
+//~| NOTE `#[rustc_force_inline]` forces a free function to be inlined
 pub fn justified() {
 }
diff --git a/tests/ui/force-inlining/gate.stderr b/tests/ui/force-inlining/gate.stderr
index 964d43fa18f..6c2df08b1a3 100644
--- a/tests/ui/force-inlining/gate.stderr
+++ b/tests/ui/force-inlining/gate.stderr
@@ -1,20 +1,22 @@
-error[E0658]: #[rustc_force_inline] forces a free function to be inlined
+error[E0658]: use of an internal attribute
   --> $DIR/gate.rs:4:1
    |
 LL | #[rustc_force_inline]
    | ^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+   = note: the `#[rustc_force_inline]` attribute is an internal implementation detail that will never be stable
+   = note: `#[rustc_force_inline]` forces a free function to be inlined
 
-error[E0658]: #[rustc_force_inline] forces a free function to be inlined
-  --> $DIR/gate.rs:9:1
+error[E0658]: use of an internal attribute
+  --> $DIR/gate.rs:11:1
    |
 LL | #[rustc_force_inline = "the test requires it"]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+   = note: the `#[rustc_force_inline]` attribute is an internal implementation detail that will never be stable
+   = note: `#[rustc_force_inline]` forces a free function to be inlined
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-1.rs b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-1.rs
index e00a31e26aa..cca60852efd 100644
--- a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-1.rs
+++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-1.rs
@@ -20,7 +20,7 @@ where
     Self: Sized,
 {
     type I: for<'a> FamilyLt<'a>;
-    fn inject(_: &()) -> <Self::I as FamilyLt>::Out;
+    fn inject(_: &()) -> <Self::I as FamilyLt<'_>>::Out;
 }
 
 impl<T: 'static> Inject for RefMutFamily<T> {
diff --git a/tests/ui/impl-trait/auto-trait-selection-freeze.next.stderr b/tests/ui/impl-trait/auto-trait-selection-freeze.next.stderr
index 54ceec0aff5..5caf0eb2fd4 100644
--- a/tests/ui/impl-trait/auto-trait-selection-freeze.next.stderr
+++ b/tests/ui/impl-trait/auto-trait-selection-freeze.next.stderr
@@ -1,9 +1,22 @@
-error[E0284]: type annotations needed: cannot satisfy `impl Sized == _`
-  --> $DIR/auto-trait-selection-freeze.rs:19:5
+error[E0283]: type annotations needed
+  --> $DIR/auto-trait-selection-freeze.rs:19:16
    |
 LL |     if false { is_trait(foo()) } else { Default::default() }
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `impl Sized == _`
+   |                ^^^^^^^^ ----- type must be known at this point
+   |                |
+   |                cannot infer type of the type parameter `T` declared on the function `is_trait`
+   |
+   = note: cannot satisfy `_: Trait<_>`
+note: required by a bound in `is_trait`
+  --> $DIR/auto-trait-selection-freeze.rs:11:16
+   |
+LL | fn is_trait<T: Trait<U>, U: Default>(_: T) -> U {
+   |                ^^^^^^^^ required by this bound in `is_trait`
+help: consider specifying the generic arguments
+   |
+LL |     if false { is_trait::<T, U>(foo()) } else { Default::default() }
+   |                        ++++++++
 
 error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0284`.
+For more information about this error, try `rustc --explain E0283`.
diff --git a/tests/ui/impl-trait/auto-trait-selection.next.stderr b/tests/ui/impl-trait/auto-trait-selection.next.stderr
index 7acb9fd41b7..d34fdcc4496 100644
--- a/tests/ui/impl-trait/auto-trait-selection.next.stderr
+++ b/tests/ui/impl-trait/auto-trait-selection.next.stderr
@@ -1,9 +1,22 @@
-error[E0284]: type annotations needed: cannot satisfy `impl Sized == _`
-  --> $DIR/auto-trait-selection.rs:15:5
+error[E0283]: type annotations needed
+  --> $DIR/auto-trait-selection.rs:15:16
    |
 LL |     if false { is_trait(foo()) } else { Default::default() }
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `impl Sized == _`
+   |                ^^^^^^^^ ----- type must be known at this point
+   |                |
+   |                cannot infer type of the type parameter `T` declared on the function `is_trait`
+   |
+   = note: cannot satisfy `_: Trait<_>`
+note: required by a bound in `is_trait`
+  --> $DIR/auto-trait-selection.rs:7:16
+   |
+LL | fn is_trait<T: Trait<U>, U: Default>(_: T) -> U {
+   |                ^^^^^^^^ required by this bound in `is_trait`
+help: consider specifying the generic arguments
+   |
+LL |     if false { is_trait::<T, U>(foo()) } else { Default::default() }
+   |                        ++++++++
 
 error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0284`.
+For more information about this error, try `rustc --explain E0283`.
diff --git a/tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.next.stderr b/tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.next.stderr
index 9632d2ce624..f2e249f2cbf 100644
--- a/tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.next.stderr
+++ b/tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.next.stderr
@@ -25,7 +25,7 @@ LL |     type LineStream<'c, 'd> = impl Stream;
    |
    = note: `LineStream` must be used in combination with a concrete type within the same impl
 
-error[E0271]: type mismatch resolving `<Y as X>::LineStreamFut<'a, Repr> == ()`
+error[E0271]: type mismatch resolving `<Y as X>::LineStreamFut<'a, Repr> normalizes-to ()`
   --> $DIR/ice-unexpected-param-type-whensubstituting-in-region-112823.rs:29:43
    |
 LL |     fn line_stream<'a, Repr>(&'a self) -> Self::LineStreamFut<'a, Repr> {}
diff --git a/tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.rs b/tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.rs
index 0b507ed948a..7cf155ce01e 100644
--- a/tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.rs
+++ b/tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.rs
@@ -29,7 +29,7 @@ impl X for Y {
     fn line_stream<'a, Repr>(&'a self) -> Self::LineStreamFut<'a, Repr> {}
     //~^ ERROR method `line_stream` is not a member of trait `X`
     //[current]~^^ ERROR `()` is not a future
-    //[next]~^^^ ERROR type mismatch resolving `<Y as X>::LineStreamFut<'a, Repr> == ()`
+    //[next]~^^^ ERROR type mismatch resolving `<Y as X>::LineStreamFut<'a, Repr> normalizes-to ()`
     //[next]~| ERROR type mismatch resolving `<Y as X>::LineStreamFut<'a, Repr> normalizes-to _`
     //[next]~| ERROR type mismatch resolving `<Y as X>::LineStreamFut<'a, Repr> normalizes-to _`
 }
diff --git a/tests/ui/impl-trait/recursive-in-exhaustiveness.next.stderr b/tests/ui/impl-trait/recursive-in-exhaustiveness.next.stderr
index a3609b93cb3..db57be73acc 100644
--- a/tests/ui/impl-trait/recursive-in-exhaustiveness.next.stderr
+++ b/tests/ui/impl-trait/recursive-in-exhaustiveness.next.stderr
@@ -1,8 +1,8 @@
-error[E0284]: type annotations needed: cannot satisfy `impl Sized == _`
-  --> $DIR/recursive-in-exhaustiveness.rs:19:17
+error[E0284]: type annotations needed: cannot normalize `build<_>::{opaque#0}`
+  --> $DIR/recursive-in-exhaustiveness.rs:20:5
    |
-LL |     let (x,) = (build(x),);
-   |                 ^^^^^^^^ cannot satisfy `impl Sized == _`
+LL |     build(x)
+   |     ^^^^^^^^ cannot normalize `build<_>::{opaque#0}`
 
 error[E0271]: type mismatch resolving `build2<(_,)>::{opaque#0} normalizes-to _`
   --> $DIR/recursive-in-exhaustiveness.rs:30:6
diff --git a/tests/ui/impl-trait/recursive-in-exhaustiveness.rs b/tests/ui/impl-trait/recursive-in-exhaustiveness.rs
index fa8fa0e8174..dabef22af86 100644
--- a/tests/ui/impl-trait/recursive-in-exhaustiveness.rs
+++ b/tests/ui/impl-trait/recursive-in-exhaustiveness.rs
@@ -17,8 +17,8 @@
 fn build<T>(x: T) -> impl Sized {
     //[current]~^ ERROR cannot resolve opaque type
     let (x,) = (build(x),);
-    //[next]~^ ERROR type annotations needed
     build(x)
+    //[next]~^ ERROR type annotations needed: cannot normalize `build<_>::{opaque#0}`
 }
 
 // Opaque<T> = (Opaque<T>,)
diff --git a/tests/ui/impl-trait/two_tait_defining_each_other2.next.stderr b/tests/ui/impl-trait/two_tait_defining_each_other2.next.stderr
index 1a4c0f5f7ee..fac4776905d 100644
--- a/tests/ui/impl-trait/two_tait_defining_each_other2.next.stderr
+++ b/tests/ui/impl-trait/two_tait_defining_each_other2.next.stderr
@@ -1,9 +1,9 @@
-error[E0284]: type annotations needed: cannot satisfy `_ == A`
-  --> $DIR/two_tait_defining_each_other2.rs:12:8
+error[E0282]: type annotations needed
+  --> $DIR/two_tait_defining_each_other2.rs:12:11
    |
 LL | fn muh(x: A) -> B {
-   |        ^ cannot satisfy `_ == A`
+   |           ^ cannot infer type
 
 error: aborting due to 1 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/tests/ui/impl-trait/two_tait_defining_each_other2.rs b/tests/ui/impl-trait/two_tait_defining_each_other2.rs
index 6c454bba502..ec2963249f9 100644
--- a/tests/ui/impl-trait/two_tait_defining_each_other2.rs
+++ b/tests/ui/impl-trait/two_tait_defining_each_other2.rs
@@ -10,7 +10,7 @@ trait Foo {}
 
 #[define_opaque(A, B)]
 fn muh(x: A) -> B {
-    //[next]~^ ERROR: cannot satisfy `_ == A`
+    //[next]~^ ERROR: type annotations needed
     x // B's hidden type is A (opaquely)
     //[current]~^ ERROR opaque type's hidden type cannot be another opaque type
 }
diff --git a/tests/ui/intrinsics/intrinsic-alignment.rs b/tests/ui/intrinsics/intrinsic-alignment.rs
index 30a523f364c..904da71c306 100644
--- a/tests/ui/intrinsics/intrinsic-alignment.rs
+++ b/tests/ui/intrinsics/intrinsic-alignment.rs
@@ -23,12 +23,12 @@ use std::intrinsics as rusti;
 mod m {
     #[cfg(target_arch = "x86")]
     pub fn main() {
-        assert_eq!(crate::rusti::min_align_of::<u64>(), 4);
+        assert_eq!(crate::rusti::align_of::<u64>(), 4);
     }
 
     #[cfg(not(target_arch = "x86"))]
     pub fn main() {
-        assert_eq!(crate::rusti::min_align_of::<u64>(), 8);
+        assert_eq!(crate::rusti::align_of::<u64>(), 8);
     }
 }
 
@@ -36,21 +36,21 @@ mod m {
 mod m {
     #[cfg(target_arch = "x86_64")]
     pub fn main() {
-        assert_eq!(crate::rusti::min_align_of::<u64>(), 8);
+        assert_eq!(crate::rusti::align_of::<u64>(), 8);
     }
 }
 
 #[cfg(target_os = "windows")]
 mod m {
     pub fn main() {
-        assert_eq!(crate::rusti::min_align_of::<u64>(), 8);
+        assert_eq!(crate::rusti::align_of::<u64>(), 8);
     }
 }
 
 #[cfg(target_family = "wasm")]
 mod m {
     pub fn main() {
-        assert_eq!(crate::rusti::min_align_of::<u64>(), 8);
+        assert_eq!(crate::rusti::align_of::<u64>(), 8);
     }
 }
 
diff --git a/tests/ui/invalid/invalid-rustc_legacy_const_generics-issue-123077.stderr b/tests/ui/invalid/invalid-rustc_legacy_const_generics-issue-123077.stderr
index d92836aa063..8d120ae98d9 100644
--- a/tests/ui/invalid/invalid-rustc_legacy_const_generics-issue-123077.stderr
+++ b/tests/ui/invalid/invalid-rustc_legacy_const_generics-issue-123077.stderr
@@ -19,7 +19,7 @@ LL |     std::arch::x86_64::_mm_blend_ps(loop {}, loop {}, 5 + || ());
 help: try using a const generic argument instead
    |
 LL -     std::arch::x86_64::_mm_blend_ps(loop {}, loop {}, 5 + || ());
-LL +     std::arch::x86_64::_mm_blend_ps::<{ 5 + (|| ()) }>(loop {}, loop {});
+LL +     std::arch::x86_64::_mm_blend_ps::<{ 5 + || () }>(loop {}, loop {});
    |
 
 error: invalid argument to a legacy const generic: cannot have const blocks, closures, async blocks or items
@@ -81,7 +81,7 @@ LL |     std::arch::x86_64::_mm_inserti_si64(loop {}, loop {}, || (), 1 + || ())
 help: try using a const generic argument instead
    |
 LL -     std::arch::x86_64::_mm_inserti_si64(loop {}, loop {}, || (), 1 + || ());
-LL +     std::arch::x86_64::_mm_inserti_si64::<{ || () }, { 1 + (|| ()) }>(loop {}, loop {});
+LL +     std::arch::x86_64::_mm_inserti_si64::<{ || () }, { 1 + || () }>(loop {}, loop {});
    |
 
 error: aborting due to 7 previous errors
diff --git a/tests/ui/issues/issue-15756.stderr b/tests/ui/issues/issue-15756.stderr
index af50fe467d1..a487d360bef 100644
--- a/tests/ui/issues/issue-15756.stderr
+++ b/tests/ui/issues/issue-15756.stderr
@@ -6,7 +6,6 @@ LL |     &mut something
    |
    = help: the trait `Sized` is not implemented for `[T]`
    = note: all local variables must have a statically known size
-   = help: unsized locals are gated as an unstable feature
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/issues/issue-28561.rs b/tests/ui/issues/issue-28561.rs
index f9b0ceb22fc..642b2193a4f 100644
--- a/tests/ui/issues/issue-28561.rs
+++ b/tests/ui/issues/issue-28561.rs
@@ -37,6 +37,7 @@ struct Array<T> {
 }
 
 #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
+#[allow(unpredictable_function_pointer_comparisons)]
 struct Fn<A, B, C, D, E, F, G, H, I, J, K, L> {
     f00: fn(),
     f01: fn(A),
diff --git a/tests/ui/iterators/collect-into-slice.stderr b/tests/ui/iterators/collect-into-slice.stderr
index 56f1bf77060..e5729a2badc 100644
--- a/tests/ui/iterators/collect-into-slice.stderr
+++ b/tests/ui/iterators/collect-into-slice.stderr
@@ -16,7 +16,6 @@ LL |     let some_generated_vec = (0..10).collect();
    |
    = help: the trait `Sized` is not implemented for `[i32]`
    = note: all local variables must have a statically known size
-   = help: unsized locals are gated as an unstable feature
 
 error[E0277]: the size for values of type `[i32]` cannot be known at compilation time
   --> $DIR/collect-into-slice.rs:6:38
diff --git a/tests/ui/label/label_misspelled.stderr b/tests/ui/label/label_misspelled.stderr
index 3f4020e7be0..9f9d32dce6b 100644
--- a/tests/ui/label/label_misspelled.stderr
+++ b/tests/ui/label/label_misspelled.stderr
@@ -78,6 +78,14 @@ LL |         break for_loop;
    |               not found in this scope
    |               help: use the similarly named label: `'for_loop`
 
+warning: denote infinite loops with `loop { ... }`
+  --> $DIR/label_misspelled.rs:4:5
+   |
+LL |     'while_loop: while true {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^ help: use `loop`
+   |
+   = note: `#[warn(while_true)]` on by default
+
 warning: unused label
   --> $DIR/label_misspelled.rs:4:5
    |
@@ -90,14 +98,6 @@ note: the lint level is defined here
 LL | #![warn(unused_labels)]
    |         ^^^^^^^^^^^^^
 
-warning: denote infinite loops with `loop { ... }`
-  --> $DIR/label_misspelled.rs:4:5
-   |
-LL |     'while_loop: while true {
-   |     ^^^^^^^^^^^^^^^^^^^^^^^ help: use `loop`
-   |
-   = note: `#[warn(while_true)]` on by default
-
 warning: unused label
   --> $DIR/label_misspelled.rs:9:5
    |
@@ -122,17 +122,17 @@ warning: denote infinite loops with `loop { ... }`
 LL |     'while_loop: while true {
    |     ^^^^^^^^^^^^^^^^^^^^^^^ help: use `loop`
 
-warning: unused label
+warning: denote infinite loops with `loop { ... }`
   --> $DIR/label_misspelled.rs:47:5
    |
 LL |     'while_loop: while true {
-   |     ^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^^^^^^^^ help: use `loop`
 
-warning: denote infinite loops with `loop { ... }`
+warning: unused label
   --> $DIR/label_misspelled.rs:47:5
    |
 LL |     'while_loop: while true {
-   |     ^^^^^^^^^^^^^^^^^^^^^^^ help: use `loop`
+   |     ^^^^^^^^^^^
 
 warning: unused label
   --> $DIR/label_misspelled.rs:52:5
diff --git a/tests/ui/layout/invalid-unsized-in-always-sized-tail.stderr b/tests/ui/layout/invalid-unsized-in-always-sized-tail.stderr
index 1a43fd4ad18..5f6a6099ba2 100644
--- a/tests/ui/layout/invalid-unsized-in-always-sized-tail.stderr
+++ b/tests/ui/layout/invalid-unsized-in-always-sized-tail.stderr
@@ -24,7 +24,7 @@ error[E0080]: the type `MySlice<[bool]>` has an unknown layout
 LL | static CHECK: () = assert!(align_of::<P2>() == 1);
    |                            ^^^^^^^^^^^^^^^^ evaluation of `CHECK` failed inside this call
    |
-note: inside `align_of::<P2>`
+note: inside `std::mem::align_of::<P2>`
   --> $SRC_DIR/core/src/mem/mod.rs:LL:COL
 
 error: aborting due to 2 previous errors
diff --git a/tests/ui/lifetimes/mismatched-lifetime-syntaxes.rs b/tests/ui/lifetimes/mismatched-lifetime-syntaxes.rs
index 6d8487b99c6..b98423afb17 100644
--- a/tests/ui/lifetimes/mismatched-lifetime-syntaxes.rs
+++ b/tests/ui/lifetimes/mismatched-lifetime-syntaxes.rs
@@ -220,6 +220,45 @@ mod diagnostic_output {
     }
 }
 
+/// Trait functions are represented differently in the HIR. Make sure
+/// we visit them.
+mod trait_functions {
+    #[derive(Copy, Clone)]
+    struct ContainsLifetime<'a>(&'a u8);
+
+    trait TheTrait {
+        fn implicit_ref_to_implicit_path(v: &u8) -> ContainsLifetime;
+        //~^ ERROR lifetime flowing from input to output with different syntax
+
+        fn method_implicit_ref_to_implicit_path(&self) -> ContainsLifetime;
+        //~^ ERROR lifetime flowing from input to output with different syntax
+    }
+
+    impl TheTrait for &u8 {
+        fn implicit_ref_to_implicit_path(v: &u8) -> ContainsLifetime {
+            //~^ ERROR lifetime flowing from input to output with different syntax
+            ContainsLifetime(v)
+        }
+
+        fn method_implicit_ref_to_implicit_path(&self) -> ContainsLifetime {
+            //~^ ERROR lifetime flowing from input to output with different syntax
+            ContainsLifetime(self)
+        }
+    }
+}
+
+/// Extern functions are represented differently in the HIR. Make sure
+/// we visit them.
+mod foreign_functions {
+    #[derive(Copy, Clone)]
+    struct ContainsLifetime<'a>(&'a u8);
+
+    extern "Rust" {
+        fn implicit_ref_to_implicit_path(v: &u8) -> ContainsLifetime;
+        //~^ ERROR lifetime flowing from input to output with different syntax
+    }
+}
+
 /// These usages are expected to **not** trigger the lint
 mod acceptable_uses {
     #[derive(Copy, Clone)]
diff --git a/tests/ui/lifetimes/mismatched-lifetime-syntaxes.stderr b/tests/ui/lifetimes/mismatched-lifetime-syntaxes.stderr
index 0ec16a266b6..108b3f14169 100644
--- a/tests/ui/lifetimes/mismatched-lifetime-syntaxes.stderr
+++ b/tests/ui/lifetimes/mismatched-lifetime-syntaxes.stderr
@@ -469,5 +469,70 @@ help: one option is to consistently use `'a`
 LL |     fn multiple_outputs<'a>(v: &'a u8) -> (&'a u8, &'a u8) {
    |                                             ++      ++
 
-error: aborting due to 34 previous errors
+error: lifetime flowing from input to output with different syntax can be confusing
+  --> $DIR/mismatched-lifetime-syntaxes.rs:230:45
+   |
+LL |         fn implicit_ref_to_implicit_path(v: &u8) -> ContainsLifetime;
+   |                                             ^^^     ---------------- the lifetime gets resolved as `'_`
+   |                                             |
+   |                                             this lifetime flows to the output
+   |
+help: one option is to remove the lifetime for references and use the anonymous lifetime for paths
+   |
+LL |         fn implicit_ref_to_implicit_path(v: &u8) -> ContainsLifetime<'_>;
+   |                                                                     ++++
+
+error: lifetime flowing from input to output with different syntax can be confusing
+  --> $DIR/mismatched-lifetime-syntaxes.rs:233:49
+   |
+LL |         fn method_implicit_ref_to_implicit_path(&self) -> ContainsLifetime;
+   |                                                 ^^^^^     ---------------- the lifetime gets resolved as `'_`
+   |                                                 |
+   |                                                 this lifetime flows to the output
+   |
+help: one option is to remove the lifetime for references and use the anonymous lifetime for paths
+   |
+LL |         fn method_implicit_ref_to_implicit_path(&self) -> ContainsLifetime<'_>;
+   |                                                                           ++++
+
+error: lifetime flowing from input to output with different syntax can be confusing
+  --> $DIR/mismatched-lifetime-syntaxes.rs:238:45
+   |
+LL |         fn implicit_ref_to_implicit_path(v: &u8) -> ContainsLifetime {
+   |                                             ^^^     ---------------- the lifetime gets resolved as `'_`
+   |                                             |
+   |                                             this lifetime flows to the output
+   |
+help: one option is to remove the lifetime for references and use the anonymous lifetime for paths
+   |
+LL |         fn implicit_ref_to_implicit_path(v: &u8) -> ContainsLifetime<'_> {
+   |                                                                     ++++
+
+error: lifetime flowing from input to output with different syntax can be confusing
+  --> $DIR/mismatched-lifetime-syntaxes.rs:243:49
+   |
+LL |         fn method_implicit_ref_to_implicit_path(&self) -> ContainsLifetime {
+   |                                                 ^^^^^     ---------------- the lifetime gets resolved as `'_`
+   |                                                 |
+   |                                                 this lifetime flows to the output
+   |
+help: one option is to remove the lifetime for references and use the anonymous lifetime for paths
+   |
+LL |         fn method_implicit_ref_to_implicit_path(&self) -> ContainsLifetime<'_> {
+   |                                                                           ++++
+
+error: lifetime flowing from input to output with different syntax can be confusing
+  --> $DIR/mismatched-lifetime-syntaxes.rs:257:45
+   |
+LL |         fn implicit_ref_to_implicit_path(v: &u8) -> ContainsLifetime;
+   |                                             ^^^     ---------------- the lifetime gets resolved as `'_`
+   |                                             |
+   |                                             this lifetime flows to the output
+   |
+help: one option is to remove the lifetime for references and use the anonymous lifetime for paths
+   |
+LL |         fn implicit_ref_to_implicit_path(v: &u8) -> ContainsLifetime<'_>;
+   |                                                                     ++++
+
+error: aborting due to 39 previous errors
 
diff --git a/tests/ui/lifetimes/temporary-lifetime-extension-tuple-ctor.rs b/tests/ui/lifetimes/temporary-lifetime-extension-tuple-ctor.rs
new file mode 100644
index 00000000000..bb537f855a4
--- /dev/null
+++ b/tests/ui/lifetimes/temporary-lifetime-extension-tuple-ctor.rs
@@ -0,0 +1,36 @@
+//@ edition:2024
+
+fn temp() -> String {
+    String::from("Hello")
+}
+
+#[derive(Debug)]
+struct X<'a>(&'a String);
+
+trait T<'a> {
+    const A: X<'a>;
+    const B: X<'a>;
+}
+
+impl<'a> T<'a> for X<'a> {
+    // Check both Self() and X() syntax:
+    const A: X<'a> = Self(&String::new());
+    const B: X<'a> = X(&String::new());
+}
+
+fn main() {
+    let a = &temp();
+    let b = Some(&temp());
+    let c = Option::Some::<&String>(&temp());
+    use Option::Some as S;
+    let d = S(&temp());
+    let e = X(&temp());
+    let f = Some(Ok::<_, ()>(std::borrow::Cow::Borrowed(if true {
+        &temp()
+    } else {
+        panic!()
+    })));
+    let some = Some; // Turn the ctor into a regular function.
+    let g = some(&temp()); //~ERROR temporary value dropped while borrowe
+    println!("{a:?} {b:?} {c:?} {d:?} {e:?} {f:?} {g:?}");
+}
diff --git a/tests/ui/lifetimes/temporary-lifetime-extension-tuple-ctor.stderr b/tests/ui/lifetimes/temporary-lifetime-extension-tuple-ctor.stderr
new file mode 100644
index 00000000000..66f9140f63c
--- /dev/null
+++ b/tests/ui/lifetimes/temporary-lifetime-extension-tuple-ctor.stderr
@@ -0,0 +1,19 @@
+error[E0716]: temporary value dropped while borrowed
+  --> $DIR/temporary-lifetime-extension-tuple-ctor.rs:34:19
+   |
+LL |     let g = some(&temp());
+   |                   ^^^^^^ - temporary value is freed at the end of this statement
+   |                   |
+   |                   creates a temporary value which is freed while still in use
+LL |     println!("{a:?} {b:?} {c:?} {d:?} {e:?} {f:?} {g:?}");
+   |                                                   ----- borrow later used here
+   |
+help: consider using a `let` binding to create a longer lived value
+   |
+LL ~     let binding = temp();
+LL ~     let g = some(&binding);
+   |
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0716`.
diff --git a/tests/ui/linkage-attr/raw-dylib/windows/unsupported-abi.rs b/tests/ui/linkage-attr/raw-dylib/windows/unsupported-abi.rs
index df69782e154..9babc20d1a1 100644
--- a/tests/ui/linkage-attr/raw-dylib/windows/unsupported-abi.rs
+++ b/tests/ui/linkage-attr/raw-dylib/windows/unsupported-abi.rs
@@ -1,6 +1,14 @@
-//@ only-x86_64
-//@ only-windows
+//@ add-core-stubs
+//@ compile-flags: --target x86_64-pc-windows-msvc
 //@ compile-flags: --crate-type lib --emit link
+//@ needs-llvm-components: x86
+#![no_core]
+#![feature(no_core)]
+extern crate minicore;
+
+// It may seem weird this is a cross-platform-testable thing, since doesn't it test linkage?
+// However the main thing we are testing is an *error*, so it works fine!
+
 #[link(name = "foo", kind = "raw-dylib")]
 extern "stdcall" {
 //~^ WARN: calling convention not supported on this target
diff --git a/tests/ui/linkage-attr/raw-dylib/windows/unsupported-abi.stderr b/tests/ui/linkage-attr/raw-dylib/windows/unsupported-abi.stderr
index e7a32f4c84b..95ea9080486 100644
--- a/tests/ui/linkage-attr/raw-dylib/windows/unsupported-abi.stderr
+++ b/tests/ui/linkage-attr/raw-dylib/windows/unsupported-abi.stderr
@@ -1,5 +1,5 @@
 warning: use of calling convention not supported on this target
-  --> $DIR/unsupported-abi.rs:5:1
+  --> $DIR/unsupported-abi.rs:13:1
    |
 LL | / extern "stdcall" {
 LL | |
@@ -15,27 +15,10 @@ LL | | }
    = note: `#[warn(unsupported_calling_conventions)]` on by default
 
 error: ABI not supported by `#[link(kind = "raw-dylib")]` on this architecture
-  --> $DIR/unsupported-abi.rs:8:5
+  --> $DIR/unsupported-abi.rs:16:5
    |
 LL |     fn f(x: i32);
    |     ^^^^^^^^^^^^^
 
 error: aborting due to 1 previous error; 1 warning emitted
 
-Future incompatibility report: Future breakage diagnostic:
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported-abi.rs:5:1
-   |
-LL | / extern "stdcall" {
-LL | |
-LL | |
-LL | |     fn f(x: i32);
-LL | |
-LL | | }
-   | |_^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"`
-   = note: `#[warn(unsupported_calling_conventions)]` on by default
-
diff --git a/tests/ui/lint/expansion-time.rs b/tests/ui/lint/expansion-time.rs
index 5ffb0c7881e..2c59bf00065 100644
--- a/tests/ui/lint/expansion-time.rs
+++ b/tests/ui/lint/expansion-time.rs
@@ -5,10 +5,6 @@ macro_rules! foo {
     ( $($i:ident)* ) => { $($i)+ }; //~ WARN meta-variable repeats with different Kleene operator
 }
 
-#[warn(missing_fragment_specifier)]
-macro_rules! m { ($i) => {} } //~ WARN missing fragment specifier
-                              //~| WARN this was previously accepted
-
 #[deprecated = "reason"]
 macro_rules! deprecated {
     () => {}
diff --git a/tests/ui/lint/expansion-time.stderr b/tests/ui/lint/expansion-time.stderr
index f24d1b68a8d..b1154d1a54c 100644
--- a/tests/ui/lint/expansion-time.stderr
+++ b/tests/ui/lint/expansion-time.stderr
@@ -12,20 +12,6 @@ note: the lint level is defined here
 LL | #[warn(meta_variable_misuse)]
    |        ^^^^^^^^^^^^^^^^^^^^
 
-warning: missing fragment specifier
-  --> $DIR/expansion-time.rs:9:19
-   |
-LL | macro_rules! m { ($i) => {} }
-   |                   ^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
-note: the lint level is defined here
-  --> $DIR/expansion-time.rs:8:8
-   |
-LL | #[warn(missing_fragment_specifier)]
-   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^
-
 warning: include macro expected single expression in source
   --> $DIR/expansion-time-include.rs:4:1
    |
@@ -33,25 +19,10 @@ LL | 2
    | ^
    |
 note: the lint level is defined here
-  --> $DIR/expansion-time.rs:22:8
+  --> $DIR/expansion-time.rs:18:8
    |
 LL | #[warn(incomplete_include)]
    |        ^^^^^^^^^^^^^^^^^^
 
-warning: 3 warnings emitted
-
-Future incompatibility report: Future breakage diagnostic:
-warning: missing fragment specifier
-  --> $DIR/expansion-time.rs:9:19
-   |
-LL | macro_rules! m { ($i) => {} }
-   |                   ^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
-note: the lint level is defined here
-  --> $DIR/expansion-time.rs:8:8
-   |
-LL | #[warn(missing_fragment_specifier)]
-   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^
+warning: 2 warnings emitted
 
diff --git a/tests/ui/lint/fn-ptr-comparisons-some.rs b/tests/ui/lint/fn-ptr-comparisons-some.rs
index 152e16b9884..c6ddd759baa 100644
--- a/tests/ui/lint/fn-ptr-comparisons-some.rs
+++ b/tests/ui/lint/fn-ptr-comparisons-some.rs
@@ -12,6 +12,6 @@ fn main() {
     let _ = Some::<FnPtr>(func) == Some(func as unsafe extern "C" fn());
     //~^ WARN function pointer comparisons
 
-    // Undecided as of https://github.com/rust-lang/rust/pull/134536
     assert_eq!(Some::<FnPtr>(func), Some(func as unsafe extern "C" fn()));
+    //~^ WARN function pointer comparisons
 }
diff --git a/tests/ui/lint/fn-ptr-comparisons-some.stderr b/tests/ui/lint/fn-ptr-comparisons-some.stderr
index eefad05b676..522c4399bce 100644
--- a/tests/ui/lint/fn-ptr-comparisons-some.stderr
+++ b/tests/ui/lint/fn-ptr-comparisons-some.stderr
@@ -9,5 +9,16 @@ LL |     let _ = Some::<FnPtr>(func) == Some(func as unsafe extern "C" fn());
    = note: for more information visit <https://doc.rust-lang.org/nightly/core/ptr/fn.fn_addr_eq.html>
    = note: `#[warn(unpredictable_function_pointer_comparisons)]` on by default
 
-warning: 1 warning emitted
+warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique
+  --> $DIR/fn-ptr-comparisons-some.rs:15:5
+   |
+LL |     assert_eq!(Some::<FnPtr>(func), Some(func as unsafe extern "C" fn()));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: the address of the same function can vary between different codegen units
+   = note: furthermore, different functions could have the same address after being merged together
+   = note: for more information visit <https://doc.rust-lang.org/nightly/core/ptr/fn.fn_addr_eq.html>
+   = note: this warning originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+warning: 2 warnings emitted
 
diff --git a/tests/ui/lint/fn-ptr-comparisons-weird.rs b/tests/ui/lint/fn-ptr-comparisons-weird.rs
index 171fbfb8727..4d756cb49df 100644
--- a/tests/ui/lint/fn-ptr-comparisons-weird.rs
+++ b/tests/ui/lint/fn-ptr-comparisons-weird.rs
@@ -1,5 +1,23 @@
 //@ check-pass
 
+#[derive(PartialEq, Eq)]
+struct A {
+    f: fn(),
+    //~^ WARN function pointer comparisons
+}
+
+#[allow(unpredictable_function_pointer_comparisons)]
+#[derive(PartialEq, Eq)]
+struct AllowedAbove {
+    f: fn(),
+}
+
+#[derive(PartialEq, Eq)]
+#[allow(unpredictable_function_pointer_comparisons)]
+struct AllowedBelow {
+    f: fn(),
+}
+
 fn main() {
     let f: fn() = main;
     let g: fn() = main;
@@ -12,4 +30,8 @@ fn main() {
     //~^ WARN function pointer comparisons
     let _ = f < g;
     //~^ WARN function pointer comparisons
+    let _ = assert_eq!(g, g);
+    //~^ WARN function pointer comparisons
+    let _ = assert_ne!(g, g);
+    //~^ WARN function pointer comparisons
 }
diff --git a/tests/ui/lint/fn-ptr-comparisons-weird.stderr b/tests/ui/lint/fn-ptr-comparisons-weird.stderr
index f2371663922..2014e519c25 100644
--- a/tests/ui/lint/fn-ptr-comparisons-weird.stderr
+++ b/tests/ui/lint/fn-ptr-comparisons-weird.stderr
@@ -1,5 +1,19 @@
 warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique
-  --> $DIR/fn-ptr-comparisons-weird.rs:7:13
+  --> $DIR/fn-ptr-comparisons-weird.rs:5:5
+   |
+LL | #[derive(PartialEq, Eq)]
+   |          --------- in this derive macro expansion
+LL | struct A {
+LL |     f: fn(),
+   |     ^^^^^^^
+   |
+   = note: the address of the same function can vary between different codegen units
+   = note: furthermore, different functions could have the same address after being merged together
+   = note: for more information visit <https://doc.rust-lang.org/nightly/core/ptr/fn.fn_addr_eq.html>
+   = note: `#[warn(unpredictable_function_pointer_comparisons)]` on by default
+
+warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique
+  --> $DIR/fn-ptr-comparisons-weird.rs:25:13
    |
 LL |     let _ = f > g;
    |             ^^^^^
@@ -7,10 +21,9 @@ LL |     let _ = f > g;
    = note: the address of the same function can vary between different codegen units
    = note: furthermore, different functions could have the same address after being merged together
    = note: for more information visit <https://doc.rust-lang.org/nightly/core/ptr/fn.fn_addr_eq.html>
-   = note: `#[warn(unpredictable_function_pointer_comparisons)]` on by default
 
 warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique
-  --> $DIR/fn-ptr-comparisons-weird.rs:9:13
+  --> $DIR/fn-ptr-comparisons-weird.rs:27:13
    |
 LL |     let _ = f >= g;
    |             ^^^^^^
@@ -20,7 +33,7 @@ LL |     let _ = f >= g;
    = note: for more information visit <https://doc.rust-lang.org/nightly/core/ptr/fn.fn_addr_eq.html>
 
 warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique
-  --> $DIR/fn-ptr-comparisons-weird.rs:11:13
+  --> $DIR/fn-ptr-comparisons-weird.rs:29:13
    |
 LL |     let _ = f <= g;
    |             ^^^^^^
@@ -30,7 +43,7 @@ LL |     let _ = f <= g;
    = note: for more information visit <https://doc.rust-lang.org/nightly/core/ptr/fn.fn_addr_eq.html>
 
 warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique
-  --> $DIR/fn-ptr-comparisons-weird.rs:13:13
+  --> $DIR/fn-ptr-comparisons-weird.rs:31:13
    |
 LL |     let _ = f < g;
    |             ^^^^^
@@ -39,5 +52,27 @@ LL |     let _ = f < g;
    = note: furthermore, different functions could have the same address after being merged together
    = note: for more information visit <https://doc.rust-lang.org/nightly/core/ptr/fn.fn_addr_eq.html>
 
-warning: 4 warnings emitted
+warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique
+  --> $DIR/fn-ptr-comparisons-weird.rs:33:13
+   |
+LL |     let _ = assert_eq!(g, g);
+   |             ^^^^^^^^^^^^^^^^
+   |
+   = note: the address of the same function can vary between different codegen units
+   = note: furthermore, different functions could have the same address after being merged together
+   = note: for more information visit <https://doc.rust-lang.org/nightly/core/ptr/fn.fn_addr_eq.html>
+   = note: this warning originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique
+  --> $DIR/fn-ptr-comparisons-weird.rs:35:13
+   |
+LL |     let _ = assert_ne!(g, g);
+   |             ^^^^^^^^^^^^^^^^
+   |
+   = note: the address of the same function can vary between different codegen units
+   = note: furthermore, different functions could have the same address after being merged together
+   = note: for more information visit <https://doc.rust-lang.org/nightly/core/ptr/fn.fn_addr_eq.html>
+   = note: this warning originates in the macro `assert_ne` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+warning: 7 warnings emitted
 
diff --git a/tests/ui/lint/fn-ptr-comparisons.fixed b/tests/ui/lint/fn-ptr-comparisons.fixed
index 22f16177a04..41cdb7bf6ae 100644
--- a/tests/ui/lint/fn-ptr-comparisons.fixed
+++ b/tests/ui/lint/fn-ptr-comparisons.fixed
@@ -11,7 +11,6 @@ extern "C" fn c() {}
 
 extern "C" fn args(_a: i32) -> i32 { 0 }
 
-#[derive(PartialEq, Eq)]
 struct A {
     f: fn(),
 }
@@ -52,7 +51,6 @@ fn main() {
     let _ = std::ptr::fn_addr_eq(t, test as unsafe extern "C" fn());
     //~^ WARN function pointer comparisons
 
-    let _ = a1 == a2; // should not warn
     let _ = std::ptr::fn_addr_eq(a1.f, a2.f);
     //~^ WARN function pointer comparisons
 }
diff --git a/tests/ui/lint/fn-ptr-comparisons.rs b/tests/ui/lint/fn-ptr-comparisons.rs
index 90a8ab5c926..c2601d6adfb 100644
--- a/tests/ui/lint/fn-ptr-comparisons.rs
+++ b/tests/ui/lint/fn-ptr-comparisons.rs
@@ -11,7 +11,6 @@ extern "C" fn c() {}
 
 extern "C" fn args(_a: i32) -> i32 { 0 }
 
-#[derive(PartialEq, Eq)]
 struct A {
     f: fn(),
 }
@@ -52,7 +51,6 @@ fn main() {
     let _ = t == test;
     //~^ WARN function pointer comparisons
 
-    let _ = a1 == a2; // should not warn
     let _ = a1.f == a2.f;
     //~^ WARN function pointer comparisons
 }
diff --git a/tests/ui/lint/fn-ptr-comparisons.stderr b/tests/ui/lint/fn-ptr-comparisons.stderr
index e6993323898..5913acca16b 100644
--- a/tests/ui/lint/fn-ptr-comparisons.stderr
+++ b/tests/ui/lint/fn-ptr-comparisons.stderr
@@ -1,5 +1,5 @@
 warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique
-  --> $DIR/fn-ptr-comparisons.rs:26:13
+  --> $DIR/fn-ptr-comparisons.rs:25:13
    |
 LL |     let _ = f == a;
    |             ^^^^^^
@@ -15,7 +15,7 @@ LL +     let _ = std::ptr::fn_addr_eq(f, a as fn());
    |
 
 warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique
-  --> $DIR/fn-ptr-comparisons.rs:28:13
+  --> $DIR/fn-ptr-comparisons.rs:27:13
    |
 LL |     let _ = f != a;
    |             ^^^^^^
@@ -30,7 +30,7 @@ LL +     let _ = !std::ptr::fn_addr_eq(f, a as fn());
    |
 
 warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique
-  --> $DIR/fn-ptr-comparisons.rs:30:13
+  --> $DIR/fn-ptr-comparisons.rs:29:13
    |
 LL |     let _ = f == g;
    |             ^^^^^^
@@ -45,7 +45,7 @@ LL +     let _ = std::ptr::fn_addr_eq(f, g);
    |
 
 warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique
-  --> $DIR/fn-ptr-comparisons.rs:32:13
+  --> $DIR/fn-ptr-comparisons.rs:31:13
    |
 LL |     let _ = f == f;
    |             ^^^^^^
@@ -60,7 +60,7 @@ LL +     let _ = std::ptr::fn_addr_eq(f, f);
    |
 
 warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique
-  --> $DIR/fn-ptr-comparisons.rs:34:13
+  --> $DIR/fn-ptr-comparisons.rs:33:13
    |
 LL |     let _ = g == g;
    |             ^^^^^^
@@ -75,7 +75,7 @@ LL +     let _ = std::ptr::fn_addr_eq(g, g);
    |
 
 warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique
-  --> $DIR/fn-ptr-comparisons.rs:36:13
+  --> $DIR/fn-ptr-comparisons.rs:35:13
    |
 LL |     let _ = g == g;
    |             ^^^^^^
@@ -90,7 +90,7 @@ LL +     let _ = std::ptr::fn_addr_eq(g, g);
    |
 
 warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique
-  --> $DIR/fn-ptr-comparisons.rs:38:13
+  --> $DIR/fn-ptr-comparisons.rs:37:13
    |
 LL |     let _ = &g == &g;
    |             ^^^^^^^^
@@ -105,7 +105,7 @@ LL +     let _ = std::ptr::fn_addr_eq(g, g);
    |
 
 warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique
-  --> $DIR/fn-ptr-comparisons.rs:40:13
+  --> $DIR/fn-ptr-comparisons.rs:39:13
    |
 LL |     let _ = a as fn() == g;
    |             ^^^^^^^^^^^^^^
@@ -120,7 +120,7 @@ LL +     let _ = std::ptr::fn_addr_eq(a as fn(), g);
    |
 
 warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique
-  --> $DIR/fn-ptr-comparisons.rs:44:13
+  --> $DIR/fn-ptr-comparisons.rs:43:13
    |
 LL |     let _ = cfn == c;
    |             ^^^^^^^^
@@ -135,7 +135,7 @@ LL +     let _ = std::ptr::fn_addr_eq(cfn, c as extern "C" fn());
    |
 
 warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique
-  --> $DIR/fn-ptr-comparisons.rs:48:13
+  --> $DIR/fn-ptr-comparisons.rs:47:13
    |
 LL |     let _ = argsfn == args;
    |             ^^^^^^^^^^^^^^
@@ -150,7 +150,7 @@ LL +     let _ = std::ptr::fn_addr_eq(argsfn, args as extern "C" fn(i32) -> i32)
    |
 
 warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique
-  --> $DIR/fn-ptr-comparisons.rs:52:13
+  --> $DIR/fn-ptr-comparisons.rs:51:13
    |
 LL |     let _ = t == test;
    |             ^^^^^^^^^
@@ -165,7 +165,7 @@ LL +     let _ = std::ptr::fn_addr_eq(t, test as unsafe extern "C" fn());
    |
 
 warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique
-  --> $DIR/fn-ptr-comparisons.rs:56:13
+  --> $DIR/fn-ptr-comparisons.rs:54:13
    |
 LL |     let _ = a1.f == a2.f;
    |             ^^^^^^^^^^^^
diff --git a/tests/ui/lint/future-incompatible-lint-group.rs b/tests/ui/lint/future-incompatible-lint-group.rs
index d1adcf21cdc..22a7ccb463b 100644
--- a/tests/ui/lint/future-incompatible-lint-group.rs
+++ b/tests/ui/lint/future-incompatible-lint-group.rs
@@ -4,14 +4,23 @@
 // lints for changes that are not tied to an edition
 #![deny(future_incompatible)]
 
-// Error since this is a `future_incompatible` lint
-macro_rules! m {
-    ($i) => {};
-    //~^ ERROR missing fragment specifier
+enum E { V }
+
+trait Tr1 {
+    type V;
+    fn foo() -> Self::V;
+}
+
+impl Tr1 for E {
+    type V = u8;
+
+    // Error since this is a `future_incompatible` lint
+    fn foo() -> Self::V { 0 }
+    //~^ ERROR ambiguous associated item
     //~| WARN this was previously accepted
 }
 
-trait Tr {
+trait Tr2 {
     // Warn only since this is not a `future_incompatible` lint
     fn f(u8) {}
     //~^ WARN anonymous parameters are deprecated
diff --git a/tests/ui/lint/future-incompatible-lint-group.stderr b/tests/ui/lint/future-incompatible-lint-group.stderr
index 264911b46d4..87b9ebec08b 100644
--- a/tests/ui/lint/future-incompatible-lint-group.stderr
+++ b/tests/ui/lint/future-incompatible-lint-group.stderr
@@ -1,20 +1,5 @@
-error: missing fragment specifier
-  --> $DIR/future-incompatible-lint-group.rs:9:6
-   |
-LL |     ($i) => {};
-   |      ^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
-note: the lint level is defined here
-  --> $DIR/future-incompatible-lint-group.rs:5:9
-   |
-LL | #![deny(future_incompatible)]
-   |         ^^^^^^^^^^^^^^^^^^^
-   = note: `#[deny(missing_fragment_specifier)]` implied by `#[deny(future_incompatible)]`
-
 warning: anonymous parameters are deprecated and will be removed in the next edition
-  --> $DIR/future-incompatible-lint-group.rs:16:10
+  --> $DIR/future-incompatible-lint-group.rs:25:10
    |
 LL |     fn f(u8) {}
    |          ^^ help: try naming the parameter or explicitly ignoring it: `_: u8`
@@ -23,21 +8,30 @@ LL |     fn f(u8) {}
    = note: for more information, see issue #41686 <https://github.com/rust-lang/rust/issues/41686>
    = note: `#[warn(anonymous_parameters)]` on by default
 
-error: aborting due to 1 previous error; 1 warning emitted
-
-Future incompatibility report: Future breakage diagnostic:
-error: missing fragment specifier
-  --> $DIR/future-incompatible-lint-group.rs:9:6
+error: ambiguous associated item
+  --> $DIR/future-incompatible-lint-group.rs:18:17
    |
-LL |     ($i) => {};
-   |      ^^
+LL |     fn foo() -> Self::V { 0 }
+   |                 ^^^^^^^ help: use fully-qualified syntax: `<E as Tr1>::V`
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
+   = note: for more information, see issue #57644 <https://github.com/rust-lang/rust/issues/57644>
+note: `V` could refer to the variant defined here
+  --> $DIR/future-incompatible-lint-group.rs:7:10
+   |
+LL | enum E { V }
+   |          ^
+note: `V` could also refer to the associated type defined here
+  --> $DIR/future-incompatible-lint-group.rs:10:5
+   |
+LL |     type V;
+   |     ^^^^^^
 note: the lint level is defined here
   --> $DIR/future-incompatible-lint-group.rs:5:9
    |
 LL | #![deny(future_incompatible)]
    |         ^^^^^^^^^^^^^^^^^^^
-   = note: `#[deny(missing_fragment_specifier)]` implied by `#[deny(future_incompatible)]`
+   = note: `#[deny(ambiguous_associated_items)]` implied by `#[deny(future_incompatible)]`
+
+error: aborting due to 1 previous error; 1 warning emitted
 
diff --git a/tests/ui/macros/auxiliary/serde.rs b/tests/ui/macros/auxiliary/serde.rs
new file mode 100644
index 00000000000..355b650bcf3
--- /dev/null
+++ b/tests/ui/macros/auxiliary/serde.rs
@@ -0,0 +1,19 @@
+//@ force-host
+//@ no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+#![feature(proc_macro_quote)]
+
+extern crate proc_macro;
+
+use proc_macro::*;
+
+#[proc_macro_derive(Serialize, attributes(serde))]
+pub fn serialize(ts: TokenStream) -> TokenStream {
+    quote!{}
+}
+
+#[proc_macro_derive(Deserialize, attributes(serde))]
+pub fn deserialize(ts: TokenStream) -> TokenStream {
+    quote!{}
+}
diff --git a/tests/ui/macros/issue-39404.rs b/tests/ui/macros/issue-39404.rs
index 2229f2c3900..ceeb6231bc8 100644
--- a/tests/ui/macros/issue-39404.rs
+++ b/tests/ui/macros/issue-39404.rs
@@ -1,7 +1,7 @@
 #![allow(unused)]
 
-macro_rules! m { ($i) => {} }
-//~^ ERROR missing fragment specifier
-//~| WARN previously accepted
+macro_rules! m {
+    ($i) => {}; //~ ERROR missing fragment specifier
+}
 
 fn main() {}
diff --git a/tests/ui/macros/issue-39404.stderr b/tests/ui/macros/issue-39404.stderr
index 176c8e9f073..62d0bc1018c 100644
--- a/tests/ui/macros/issue-39404.stderr
+++ b/tests/ui/macros/issue-39404.stderr
@@ -1,23 +1,15 @@
 error: missing fragment specifier
-  --> $DIR/issue-39404.rs:3:19
+  --> $DIR/issue-39404.rs:4:6
    |
-LL | macro_rules! m { ($i) => {} }
-   |                   ^^
+LL |     ($i) => {};
+   |      ^^
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
-   = note: `#[deny(missing_fragment_specifier)]` on by default
+   = note: fragment specifiers must be provided
+   = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis`, along with `expr_2021` and `pat_param` for edition compatibility
+help: try adding a specifier here
+   |
+LL |     ($i:spec) => {};
+   |        +++++
 
 error: aborting due to 1 previous error
 
-Future incompatibility report: Future breakage diagnostic:
-error: missing fragment specifier
-  --> $DIR/issue-39404.rs:3:19
-   |
-LL | macro_rules! m { ($i) => {} }
-   |                   ^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
-   = note: `#[deny(missing_fragment_specifier)]` on by default
-
diff --git a/tests/ui/macros/macro-match-nonterminal.rs b/tests/ui/macros/macro-match-nonterminal.rs
index 5d9eb55fee0..fa2af945a1f 100644
--- a/tests/ui/macros/macro-match-nonterminal.rs
+++ b/tests/ui/macros/macro-match-nonterminal.rs
@@ -3,8 +3,6 @@ macro_rules! test {
         //~^ ERROR missing fragment
         //~| ERROR missing fragment
         //~| ERROR missing fragment
-        //~| WARN this was previously accepted
-        //~| WARN this was previously accepted
         ()
     };
 }
diff --git a/tests/ui/macros/macro-match-nonterminal.stderr b/tests/ui/macros/macro-match-nonterminal.stderr
index f221f92c3cd..8196d795c4c 100644
--- a/tests/ui/macros/macro-match-nonterminal.stderr
+++ b/tests/ui/macros/macro-match-nonterminal.stderr
@@ -3,16 +3,13 @@ error: missing fragment specifier
    |
 LL |     ($a, $b) => {
    |      ^^
-
-error: missing fragment specifier
-  --> $DIR/macro-match-nonterminal.rs:2:6
    |
-LL |     ($a, $b) => {
-   |      ^^
+   = note: fragment specifiers must be provided
+   = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis`, along with `expr_2021` and `pat_param` for edition compatibility
+help: try adding a specifier here
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
-   = note: `#[deny(missing_fragment_specifier)]` on by default
+LL |     ($a:spec, $b) => {
+   |        +++++
 
 error: missing fragment specifier
   --> $DIR/macro-match-nonterminal.rs:2:10
@@ -20,30 +17,18 @@ error: missing fragment specifier
 LL |     ($a, $b) => {
    |          ^^
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
-
-error: aborting due to 3 previous errors
+   = note: fragment specifiers must be provided
+   = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis`, along with `expr_2021` and `pat_param` for edition compatibility
+help: try adding a specifier here
+   |
+LL |     ($a, $b:spec) => {
+   |            +++++
 
-Future incompatibility report: Future breakage diagnostic:
 error: missing fragment specifier
   --> $DIR/macro-match-nonterminal.rs:2:6
    |
 LL |     ($a, $b) => {
    |      ^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
-   = note: `#[deny(missing_fragment_specifier)]` on by default
 
-Future breakage diagnostic:
-error: missing fragment specifier
-  --> $DIR/macro-match-nonterminal.rs:2:10
-   |
-LL |     ($a, $b) => {
-   |          ^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
-   = note: `#[deny(missing_fragment_specifier)]` on by default
+error: aborting due to 3 previous errors
 
diff --git a/tests/ui/macros/macro-missing-fragment-deduplication.rs b/tests/ui/macros/macro-missing-fragment-deduplication.rs
index b77c51e055b..481f08fa111 100644
--- a/tests/ui/macros/macro-missing-fragment-deduplication.rs
+++ b/tests/ui/macros/macro-missing-fragment-deduplication.rs
@@ -1,10 +1,8 @@
 //@ compile-flags: -Zdeduplicate-diagnostics=yes
 
 macro_rules! m {
-    ($name) => {}
-    //~^ ERROR missing fragment
-    //~| ERROR missing fragment
-    //~| WARN this was previously accepted
+    ($name) => {}; //~ ERROR missing fragment
+                   //~| ERROR missing fragment
 }
 
 fn main() {
diff --git a/tests/ui/macros/macro-missing-fragment-deduplication.stderr b/tests/ui/macros/macro-missing-fragment-deduplication.stderr
index c46712f70fd..820f7eb3cf7 100644
--- a/tests/ui/macros/macro-missing-fragment-deduplication.stderr
+++ b/tests/ui/macros/macro-missing-fragment-deduplication.stderr
@@ -1,29 +1,21 @@
 error: missing fragment specifier
   --> $DIR/macro-missing-fragment-deduplication.rs:4:6
    |
-LL |     ($name) => {}
+LL |     ($name) => {};
    |      ^^^^^
-
-error: missing fragment specifier
-  --> $DIR/macro-missing-fragment-deduplication.rs:4:6
    |
-LL |     ($name) => {}
-   |      ^^^^^
+   = note: fragment specifiers must be provided
+   = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis`, along with `expr_2021` and `pat_param` for edition compatibility
+help: try adding a specifier here
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
-   = note: `#[deny(missing_fragment_specifier)]` on by default
+LL |     ($name:spec) => {};
+   |           +++++
 
-error: aborting due to 2 previous errors
-
-Future incompatibility report: Future breakage diagnostic:
 error: missing fragment specifier
   --> $DIR/macro-missing-fragment-deduplication.rs:4:6
    |
-LL |     ($name) => {}
+LL |     ($name) => {};
    |      ^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
-   = note: `#[deny(missing_fragment_specifier)]` on by default
+
+error: aborting due to 2 previous errors
 
diff --git a/tests/ui/macros/macro-missing-fragment.e2015.stderr b/tests/ui/macros/macro-missing-fragment.e2015.stderr
deleted file mode 100644
index 3d32f203d4a..00000000000
--- a/tests/ui/macros/macro-missing-fragment.e2015.stderr
+++ /dev/null
@@ -1,85 +0,0 @@
-error: missing fragment specifier
-  --> $DIR/macro-missing-fragment.rs:8:20
-   |
-LL |     ( $( any_token $field_rust_type )* ) => {};
-   |                    ^^^^^^^^^^^^^^^^
-
-warning: missing fragment specifier
-  --> $DIR/macro-missing-fragment.rs:8:20
-   |
-LL |     ( $( any_token $field_rust_type )* ) => {};
-   |                    ^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
-note: the lint level is defined here
-  --> $DIR/macro-missing-fragment.rs:5:9
-   |
-LL | #![warn(missing_fragment_specifier)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-warning: missing fragment specifier
-  --> $DIR/macro-missing-fragment.rs:18:7
-   |
-LL |     ( $name ) => {};
-   |       ^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
-
-warning: missing fragment specifier
-  --> $DIR/macro-missing-fragment.rs:25:7
-   |
-LL |     ( $name ) => {};
-   |       ^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
-
-error: aborting due to 1 previous error; 3 warnings emitted
-
-Future incompatibility report: Future breakage diagnostic:
-warning: missing fragment specifier
-  --> $DIR/macro-missing-fragment.rs:8:20
-   |
-LL |     ( $( any_token $field_rust_type )* ) => {};
-   |                    ^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
-note: the lint level is defined here
-  --> $DIR/macro-missing-fragment.rs:5:9
-   |
-LL | #![warn(missing_fragment_specifier)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Future breakage diagnostic:
-warning: missing fragment specifier
-  --> $DIR/macro-missing-fragment.rs:18:7
-   |
-LL |     ( $name ) => {};
-   |       ^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
-note: the lint level is defined here
-  --> $DIR/macro-missing-fragment.rs:5:9
-   |
-LL | #![warn(missing_fragment_specifier)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Future breakage diagnostic:
-warning: missing fragment specifier
-  --> $DIR/macro-missing-fragment.rs:25:7
-   |
-LL |     ( $name ) => {};
-   |       ^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
-note: the lint level is defined here
-  --> $DIR/macro-missing-fragment.rs:5:9
-   |
-LL | #![warn(missing_fragment_specifier)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
-
diff --git a/tests/ui/macros/macro-missing-fragment.rs b/tests/ui/macros/macro-missing-fragment.rs
index 42387e8dbbf..533aa147bcb 100644
--- a/tests/ui/macros/macro-missing-fragment.rs
+++ b/tests/ui/macros/macro-missing-fragment.rs
@@ -1,31 +1,17 @@
-//@ revisions: e2015 e2024
-//@[e2015] edition:2015
-//@[e2024] edition:2024
-
-#![warn(missing_fragment_specifier)]
+//! Ensure that macros produce an error if fragment specifiers are missing.
 
 macro_rules! used_arm {
-    ( $( any_token $field_rust_type )* ) => {};
-    //[e2015]~^ ERROR missing fragment
-    //[e2015]~| WARN missing fragment
-    //[e2015]~| WARN this was previously accepted
-    //[e2024]~^^^^ ERROR missing fragment
-    //[e2024]~| ERROR missing fragment
+    ( $( any_token $field_rust_type )* ) => {}; //~ ERROR missing fragment
+                                                //~| ERROR missing fragment
 }
 
 macro_rules! used_macro_unused_arm {
     () => {};
-    ( $name ) => {};
-    //[e2015]~^ WARN missing fragment
-    //[e2015]~| WARN this was previously accepted
-    //[e2024]~^^^ ERROR missing fragment
+    ( $name ) => {}; //~ ERROR missing fragment
 }
 
 macro_rules! unused_macro {
-    ( $name ) => {};
-    //[e2015]~^ WARN missing fragment
-    //[e2015]~| WARN this was previously accepted
-    //[e2024]~^^^ ERROR missing fragment
+    ( $name ) => {}; //~ ERROR missing fragment
 }
 
 fn main() {
diff --git a/tests/ui/macros/macro-missing-fragment.e2024.stderr b/tests/ui/macros/macro-missing-fragment.stderr
index a9195063a5b..4a99d7d949c 100644
--- a/tests/ui/macros/macro-missing-fragment.e2024.stderr
+++ b/tests/ui/macros/macro-missing-fragment.stderr
@@ -1,10 +1,10 @@
 error: missing fragment specifier
-  --> $DIR/macro-missing-fragment.rs:8:20
+  --> $DIR/macro-missing-fragment.rs:4:20
    |
 LL |     ( $( any_token $field_rust_type )* ) => {};
    |                    ^^^^^^^^^^^^^^^^
    |
-   = note: fragment specifiers must be specified in the 2024 edition
+   = note: fragment specifiers must be provided
    = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis`, along with `expr_2021` and `pat_param` for edition compatibility
 help: try adding a specifier here
    |
@@ -12,12 +12,12 @@ LL |     ( $( any_token $field_rust_type:spec )* ) => {};
    |                                    +++++
 
 error: missing fragment specifier
-  --> $DIR/macro-missing-fragment.rs:18:7
+  --> $DIR/macro-missing-fragment.rs:10:7
    |
 LL |     ( $name ) => {};
    |       ^^^^^
    |
-   = note: fragment specifiers must be specified in the 2024 edition
+   = note: fragment specifiers must be provided
    = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis`, along with `expr_2021` and `pat_param` for edition compatibility
 help: try adding a specifier here
    |
@@ -25,12 +25,12 @@ LL |     ( $name:spec ) => {};
    |            +++++
 
 error: missing fragment specifier
-  --> $DIR/macro-missing-fragment.rs:25:7
+  --> $DIR/macro-missing-fragment.rs:14:7
    |
 LL |     ( $name ) => {};
    |       ^^^^^
    |
-   = note: fragment specifiers must be specified in the 2024 edition
+   = note: fragment specifiers must be provided
    = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis`, along with `expr_2021` and `pat_param` for edition compatibility
 help: try adding a specifier here
    |
@@ -38,7 +38,7 @@ LL |     ( $name:spec ) => {};
    |            +++++
 
 error: missing fragment specifier
-  --> $DIR/macro-missing-fragment.rs:8:20
+  --> $DIR/macro-missing-fragment.rs:4:20
    |
 LL |     ( $( any_token $field_rust_type )* ) => {};
    |                    ^^^^^^^^^^^^^^^^
diff --git a/tests/ui/macros/missing-derive-1.rs b/tests/ui/macros/missing-derive-1.rs
new file mode 100644
index 00000000000..e23ef7bf07b
--- /dev/null
+++ b/tests/ui/macros/missing-derive-1.rs
@@ -0,0 +1,33 @@
+//@aux-build:serde.rs
+
+// derive macros imported and used
+
+extern crate serde;
+use serde::{Serialize, Deserialize};
+
+#[serde(untagged)] //~ ERROR cannot find attribute `serde`
+enum A { //~ HELP `serde` is an attribute that can be used by the derive macros `Deserialize` and `Serialize`
+    A,
+    B,
+}
+
+enum B { //~ HELP `serde` is an attribute that can be used by the derive macros `Deserialize` and `Serialize`
+    A,
+    #[serde(untagged)] //~ ERROR cannot find attribute `serde`
+    B,
+}
+
+enum C {
+    A,
+    #[sede(untagged)] //~ ERROR cannot find attribute `sede`
+    B, //~^ HELP the derive macros `Deserialize` and `Serialize` accept the similarly named `serde` attribute
+}
+
+#[derive(Serialize, Deserialize)]
+#[serde(untagged)]
+enum D {
+    A,
+    B,
+}
+
+fn main() {}
diff --git a/tests/ui/macros/missing-derive-1.stderr b/tests/ui/macros/missing-derive-1.stderr
new file mode 100644
index 00000000000..15584100ffa
--- /dev/null
+++ b/tests/ui/macros/missing-derive-1.stderr
@@ -0,0 +1,47 @@
+error: cannot find attribute `serde` in this scope
+  --> $DIR/missing-derive-1.rs:8:3
+   |
+LL | #[serde(untagged)]
+   |   ^^^^^
+   |
+note: `serde` is imported here, but it is a crate, not an attribute
+  --> $DIR/missing-derive-1.rs:5:1
+   |
+LL | extern crate serde;
+   | ^^^^^^^^^^^^^^^^^^^
+help: `serde` is an attribute that can be used by the derive macros `Deserialize` and `Serialize`, you might be missing a `derive` attribute
+   |
+LL + #[derive(Deserialize, Serialize)]
+LL | enum A {
+   |
+
+error: cannot find attribute `serde` in this scope
+  --> $DIR/missing-derive-1.rs:16:7
+   |
+LL |     #[serde(untagged)]
+   |       ^^^^^
+   |
+note: `serde` is imported here, but it is a crate, not an attribute
+  --> $DIR/missing-derive-1.rs:5:1
+   |
+LL | extern crate serde;
+   | ^^^^^^^^^^^^^^^^^^^
+help: `serde` is an attribute that can be used by the derive macros `Deserialize` and `Serialize`, you might be missing a `derive` attribute
+   |
+LL + #[derive(Deserialize, Serialize)]
+LL | enum B {
+   |
+
+error: cannot find attribute `sede` in this scope
+  --> $DIR/missing-derive-1.rs:22:7
+   |
+LL |     #[sede(untagged)]
+   |       ^^^^
+   |
+help: the derive macros `Deserialize` and `Serialize` accept the similarly named `serde` attribute
+   |
+LL |     #[serde(untagged)]
+   |         +
+
+error: aborting due to 3 previous errors
+
diff --git a/tests/ui/macros/missing-derive-2.rs b/tests/ui/macros/missing-derive-2.rs
new file mode 100644
index 00000000000..027d465d8b3
--- /dev/null
+++ b/tests/ui/macros/missing-derive-2.rs
@@ -0,0 +1,26 @@
+//@aux-build:serde.rs
+
+// derive macros imported but unused
+
+extern crate serde;
+use serde::{Serialize, Deserialize};
+
+#[serde(untagged)] //~ ERROR cannot find attribute `serde`
+enum A { //~ HELP `serde` is an attribute that can be used by the derive macros `Deserialize` and `Serialize`
+    A,
+    B,
+}
+
+enum B { //~ HELP `serde` is an attribute that can be used by the derive macros `Deserialize` and `Serialize`
+    A,
+    #[serde(untagged)] //~ ERROR cannot find attribute `serde`
+    B,
+}
+
+enum C {
+    A,
+    #[sede(untagged)] //~ ERROR cannot find attribute `sede`
+    B, //~^ HELP the derive macros `Deserialize` and `Serialize` accept the similarly named `serde` attribute
+}
+
+fn main() {}
diff --git a/tests/ui/macros/missing-derive-2.stderr b/tests/ui/macros/missing-derive-2.stderr
new file mode 100644
index 00000000000..6c8e9e12019
--- /dev/null
+++ b/tests/ui/macros/missing-derive-2.stderr
@@ -0,0 +1,47 @@
+error: cannot find attribute `sede` in this scope
+  --> $DIR/missing-derive-2.rs:22:7
+   |
+LL |     #[sede(untagged)]
+   |       ^^^^
+   |
+help: the derive macros `Deserialize` and `Serialize` accept the similarly named `serde` attribute
+   |
+LL |     #[serde(untagged)]
+   |         +
+
+error: cannot find attribute `serde` in this scope
+  --> $DIR/missing-derive-2.rs:16:7
+   |
+LL |     #[serde(untagged)]
+   |       ^^^^^
+   |
+note: `serde` is imported here, but it is a crate, not an attribute
+  --> $DIR/missing-derive-2.rs:5:1
+   |
+LL | extern crate serde;
+   | ^^^^^^^^^^^^^^^^^^^
+help: `serde` is an attribute that can be used by the derive macros `Deserialize` and `Serialize`, you might be missing a `derive` attribute
+   |
+LL + #[derive(Deserialize, Serialize)]
+LL | enum B {
+   |
+
+error: cannot find attribute `serde` in this scope
+  --> $DIR/missing-derive-2.rs:8:3
+   |
+LL | #[serde(untagged)]
+   |   ^^^^^
+   |
+note: `serde` is imported here, but it is a crate, not an attribute
+  --> $DIR/missing-derive-2.rs:5:1
+   |
+LL | extern crate serde;
+   | ^^^^^^^^^^^^^^^^^^^
+help: `serde` is an attribute that can be used by the derive macros `Deserialize` and `Serialize`, you might be missing a `derive` attribute
+   |
+LL + #[derive(Deserialize, Serialize)]
+LL | enum A {
+   |
+
+error: aborting due to 3 previous errors
+
diff --git a/tests/ui/macros/missing-derive-3.rs b/tests/ui/macros/missing-derive-3.rs
new file mode 100644
index 00000000000..8add8198890
--- /dev/null
+++ b/tests/ui/macros/missing-derive-3.rs
@@ -0,0 +1,24 @@
+//@aux-build:serde.rs
+
+// derive macros not imported, but namespace imported. Not yet handled.
+extern crate serde;
+
+#[serde(untagged)] //~ ERROR cannot find attribute `serde`
+enum A {
+    A,
+    B,
+}
+
+enum B {
+    A,
+    #[serde(untagged)] //~ ERROR cannot find attribute `serde`
+    B,
+}
+
+enum C {
+    A,
+    #[sede(untagged)] //~ ERROR cannot find attribute `sede`
+    B,
+}
+
+fn main() {}
diff --git a/tests/ui/macros/missing-derive-3.stderr b/tests/ui/macros/missing-derive-3.stderr
new file mode 100644
index 00000000000..0a7ed8d0876
--- /dev/null
+++ b/tests/ui/macros/missing-derive-3.stderr
@@ -0,0 +1,32 @@
+error: cannot find attribute `sede` in this scope
+  --> $DIR/missing-derive-3.rs:20:7
+   |
+LL |     #[sede(untagged)]
+   |       ^^^^
+
+error: cannot find attribute `serde` in this scope
+  --> $DIR/missing-derive-3.rs:14:7
+   |
+LL |     #[serde(untagged)]
+   |       ^^^^^
+   |
+note: `serde` is imported here, but it is a crate, not an attribute
+  --> $DIR/missing-derive-3.rs:4:1
+   |
+LL | extern crate serde;
+   | ^^^^^^^^^^^^^^^^^^^
+
+error: cannot find attribute `serde` in this scope
+  --> $DIR/missing-derive-3.rs:6:3
+   |
+LL | #[serde(untagged)]
+   |   ^^^^^
+   |
+note: `serde` is imported here, but it is a crate, not an attribute
+  --> $DIR/missing-derive-3.rs:4:1
+   |
+LL | extern crate serde;
+   | ^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/tests/ui/methods/method-deref-to-same-trait-object-with-separate-params.rs b/tests/ui/methods/method-deref-to-same-trait-object-with-separate-params.rs
index 5ef1d0c6dc9..6ceec119308 100644
--- a/tests/ui/methods/method-deref-to-same-trait-object-with-separate-params.rs
+++ b/tests/ui/methods/method-deref-to-same-trait-object-with-separate-params.rs
@@ -1,8 +1,7 @@
 //@ dont-require-annotations: NOTE
 
 #![feature(arbitrary_self_types, coerce_unsized, dispatch_from_dyn, unsize)]
-#![feature(unsized_locals, unsized_fn_params)]
-//~^ WARN the feature `unsized_locals` is incomplete
+#![feature(unsized_fn_params)]
 
 // This tests a few edge-cases around `arbitrary_self_types`. Most specifically,
 // it checks that the `ObjectCandidate` you get from method matching can't
diff --git a/tests/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr b/tests/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr
index 213139a9b0b..32cff62284e 100644
--- a/tests/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr
+++ b/tests/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr
@@ -1,14 +1,5 @@
-warning: the feature `unsized_locals` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:4:12
-   |
-LL | #![feature(unsized_locals, unsized_fn_params)]
-   |            ^^^^^^^^^^^^^^
-   |
-   = note: see issue #48055 <https://github.com/rust-lang/rust/issues/48055> for more information
-   = note: `#[warn(incomplete_features)]` on by default
-
 error[E0308]: mismatched types
-  --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:89:24
+  --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:88:24
    |
 LL |     let _seetype: () = z;
    |                   --   ^ expected `()`, found `u32`
@@ -16,7 +7,7 @@ LL |     let _seetype: () = z;
    |                   expected due to this
 
 error[E0308]: mismatched types
-  --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:106:24
+  --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:105:24
    |
 LL |     let _seetype: () = z;
    |                   --   ^ expected `()`, found `u64`
@@ -24,23 +15,23 @@ LL |     let _seetype: () = z;
    |                   expected due to this
 
 error[E0034]: multiple applicable items in scope
-  --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:124:15
+  --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:123:15
    |
 LL |     let z = x.foo();
    |               ^^^ multiple `foo` found
    |
 note: candidate #1 is defined in the trait `FinalFoo`
-  --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:61:5
+  --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:60:5
    |
 LL |     fn foo(&self) -> u8;
    |     ^^^^^^^^^^^^^^^^^^^^
 note: candidate #2 is defined in an impl of the trait `NuisanceFoo` for the type `T`
-  --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:74:9
+  --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:73:9
    |
 LL |         fn foo(self) {}
    |         ^^^^^^^^^^^^
 note: candidate #3 is defined in an impl of the trait `X` for the type `T`
-  --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:47:9
+  --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:46:9
    |
 LL |         fn foo(self: Smaht<Self, u64>) -> u64 {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -61,7 +52,7 @@ LL +     let z = X::foo(x);
    |
 
 error[E0308]: mismatched types
-  --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:141:24
+  --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:140:24
    |
 LL |     let _seetype: () = z;
    |                   --   ^ expected `()`, found `u8`
@@ -69,7 +60,7 @@ LL |     let _seetype: () = z;
    |                   expected due to this
 
 error[E0308]: mismatched types
-  --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:159:24
+  --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:158:24
    |
 LL |     let _seetype: () = z;
    |                   --   ^ expected `()`, found `u32`
@@ -77,14 +68,14 @@ LL |     let _seetype: () = z;
    |                   expected due to this
 
 error[E0308]: mismatched types
-  --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:176:24
+  --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:175:24
    |
 LL |     let _seetype: () = z;
    |                   --   ^ expected `()`, found `u32`
    |                   |
    |                   expected due to this
 
-error: aborting due to 6 previous errors; 1 warning emitted
+error: aborting due to 6 previous errors
 
 Some errors have detailed explanations: E0034, E0308.
 For more information about an error, try `rustc --explain E0034`.
diff --git a/tests/ui/methods/missing-bound-on-tuple.rs b/tests/ui/methods/missing-bound-on-tuple.rs
new file mode 100644
index 00000000000..25deabf5926
--- /dev/null
+++ b/tests/ui/methods/missing-bound-on-tuple.rs
@@ -0,0 +1,39 @@
+trait WorksOnDefault {
+    fn do_something() {}
+}
+
+impl<T: Default> WorksOnDefault for T {}
+//~^ NOTE the following trait bounds were not satisfied
+//~| NOTE unsatisfied trait bound introduced here
+
+trait Foo {}
+
+trait WorksOnFoo {
+    fn do_be_do() {}
+}
+
+impl<T: Foo> WorksOnFoo for T {}
+//~^ NOTE the following trait bounds were not satisfied
+//~| NOTE unsatisfied trait bound introduced here
+
+impl<A: Foo, B: Foo, C: Foo> Foo for (A, B, C) {}
+//~^ NOTE `Foo` is implemented for `(i32, u32, String)`
+impl Foo for i32 {}
+impl Foo for &i32 {}
+impl Foo for u32 {}
+impl Foo for String {}
+
+fn main() {
+    let _success = <(i32, u32, String)>::do_something();
+    let _failure = <(i32, &u32, String)>::do_something(); //~ ERROR E0599
+    //~^ NOTE `Default` is implemented for `(i32, u32, String)`
+    //~| NOTE function or associated item cannot be called on
+    let _success = <(i32, u32, String)>::do_be_do();
+    let _failure = <(i32, &u32, String)>::do_be_do(); //~ ERROR E0599
+    //~^ NOTE function or associated item cannot be called on
+    let _success = <(i32, u32, String)>::default();
+    let _failure = <(i32, &u32, String)>::default(); //~ ERROR E0599
+    //~^ NOTE `Default` is implemented for `(i32, u32, String)`
+    //~| NOTE function or associated item cannot be called on
+    //~| NOTE the following trait bounds were not satisfied
+}
diff --git a/tests/ui/methods/missing-bound-on-tuple.stderr b/tests/ui/methods/missing-bound-on-tuple.stderr
new file mode 100644
index 00000000000..f3e0897e5e6
--- /dev/null
+++ b/tests/ui/methods/missing-bound-on-tuple.stderr
@@ -0,0 +1,58 @@
+error[E0599]: the function or associated item `do_something` exists for tuple `(i32, &u32, String)`, but its trait bounds were not satisfied
+  --> $DIR/missing-bound-on-tuple.rs:28:43
+   |
+LL |     let _failure = <(i32, &u32, String)>::do_something();
+   |                                           ^^^^^^^^^^^^ function or associated item cannot be called on `(i32, &u32, String)` due to unsatisfied trait bounds
+   |
+note: the following trait bounds were not satisfied:
+      `&(i32, &u32, String): Default`
+      `&mut (i32, &u32, String): Default`
+      `(i32, &u32, String): Default`
+  --> $DIR/missing-bound-on-tuple.rs:5:9
+   |
+LL | impl<T: Default> WorksOnDefault for T {}
+   |         ^^^^^^^  --------------     -
+   |         |
+   |         unsatisfied trait bound introduced here
+note: `Default` is implemented for `(i32, u32, String)` but not for `(i32, &u32, String)`
+  --> $SRC_DIR/core/src/tuple.rs:LL:COL
+   = note: this error originates in the macro `tuple_impls` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0599]: the function or associated item `do_be_do` exists for tuple `(i32, &u32, String)`, but its trait bounds were not satisfied
+  --> $DIR/missing-bound-on-tuple.rs:32:43
+   |
+LL |     let _failure = <(i32, &u32, String)>::do_be_do();
+   |                                           ^^^^^^^^ function or associated item cannot be called on `(i32, &u32, String)` due to unsatisfied trait bounds
+   |
+note: the following trait bounds were not satisfied:
+      `&(i32, &u32, String): Foo`
+      `&mut (i32, &u32, String): Foo`
+      `(i32, &u32, String): Foo`
+  --> $DIR/missing-bound-on-tuple.rs:15:9
+   |
+LL | impl<T: Foo> WorksOnFoo for T {}
+   |         ^^^  ----------     -
+   |         |
+   |         unsatisfied trait bound introduced here
+note: `Foo` is implemented for `(i32, u32, String)` but not for `(i32, &u32, String)`
+  --> $DIR/missing-bound-on-tuple.rs:19:1
+   |
+LL | impl<A: Foo, B: Foo, C: Foo> Foo for (A, B, C) {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0599]: the function or associated item `default` exists for tuple `(i32, &u32, String)`, but its trait bounds were not satisfied
+  --> $DIR/missing-bound-on-tuple.rs:35:43
+   |
+LL |     let _failure = <(i32, &u32, String)>::default();
+   |                                           ^^^^^^^ function or associated item cannot be called on `(i32, &u32, String)` due to unsatisfied trait bounds
+   |
+   = note: the following trait bounds were not satisfied:
+           `&u32: Default`
+           which is required by `(i32, &u32, String): Default`
+note: `Default` is implemented for `(i32, u32, String)` but not for `(i32, &u32, String)`
+  --> $SRC_DIR/core/src/tuple.rs:LL:COL
+   = note: this error originates in the macro `tuple_impls` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0599`.
diff --git a/tests/ui/mir/mir_refs_correct.rs b/tests/ui/mir/mir_refs_correct.rs
index fc23c8c3631..f1832d90a0f 100644
--- a/tests/ui/mir/mir_refs_correct.rs
+++ b/tests/ui/mir/mir_refs_correct.rs
@@ -1,6 +1,8 @@
 //@ run-pass
 //@ aux-build:mir_external_refs.rs
 
+#![allow(unpredictable_function_pointer_comparisons)]
+
 extern crate mir_external_refs as ext;
 
 struct S(#[allow(dead_code)] u8);
diff --git a/tests/ui/moves/move-out-of-slice-2.rs b/tests/ui/moves/move-out-of-slice-2.rs
index 2f7394fbfd3..6428dc4b711 100644
--- a/tests/ui/moves/move-out-of-slice-2.rs
+++ b/tests/ui/moves/move-out-of-slice-2.rs
@@ -1,5 +1,3 @@
-#![feature(unsized_locals)]
-//~^ WARN the feature `unsized_locals` is incomplete
 #![allow(unused)]
 
 struct A;
@@ -9,28 +7,24 @@ struct C;
 fn main() {
     let a: Box<[A]> = Box::new([A]);
     match *a {
-        //~^ ERROR cannot move out of type `[A]`, a non-copy slice
-        [a @ ..] => {}
+        [a @ ..] => {} //~ERROR the size for values of type `[A]` cannot be known at compilation time [E0277]
         _ => {}
     }
     let b: Box<[A]> = Box::new([A, A, A]);
     match *b {
-        //~^ ERROR cannot move out of type `[A]`, a non-copy slice
-        [_, _, b @ .., _] => {}
+        [_, _, b @ .., _] => {} //~ERROR the size for values of type `[A]` cannot be known at compilation time [E0277]
         _ => {}
     }
 
     // `[C]` isn't `Copy`, even if `C` is.
     let c: Box<[C]> = Box::new([C]);
     match *c {
-        //~^ ERROR cannot move out of type `[C]`, a non-copy slice
-        [c @ ..] => {}
+        [c @ ..] => {} //~ERROR the size for values of type `[C]` cannot be known at compilation time [E0277]
         _ => {}
     }
     let d: Box<[C]> = Box::new([C, C, C]);
     match *d {
-        //~^ ERROR cannot move out of type `[C]`, a non-copy slice
-        [_, _, d @ .., _] => {}
+        [_, _, d @ .., _] => {} //~ERROR the size for values of type `[C]` cannot be known at compilation time [E0277]
         _ => {}
     }
 }
diff --git a/tests/ui/moves/move-out-of-slice-2.stderr b/tests/ui/moves/move-out-of-slice-2.stderr
index b46854cd6b4..207194611c5 100644
--- a/tests/ui/moves/move-out-of-slice-2.stderr
+++ b/tests/ui/moves/move-out-of-slice-2.stderr
@@ -1,80 +1,39 @@
-warning: the feature `unsized_locals` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/move-out-of-slice-2.rs:1:12
+error[E0277]: the size for values of type `[A]` cannot be known at compilation time
+  --> $DIR/move-out-of-slice-2.rs:10:10
    |
-LL | #![feature(unsized_locals)]
-   |            ^^^^^^^^^^^^^^
-   |
-   = note: see issue #48055 <https://github.com/rust-lang/rust/issues/48055> for more information
-   = note: `#[warn(incomplete_features)]` on by default
-
-error[E0508]: cannot move out of type `[A]`, a non-copy slice
-  --> $DIR/move-out-of-slice-2.rs:11:11
-   |
-LL |     match *a {
-   |           ^^ cannot move out of here
-LL |
 LL |         [a @ ..] => {}
-   |          -
-   |          |
-   |          data moved here
-   |          move occurs because `a` has type `[A]`, which does not implement the `Copy` trait
-   |
-help: consider borrowing the pattern binding
+   |          ^^^^^^ doesn't have a size known at compile-time
    |
-LL |         [ref a @ ..] => {}
-   |          +++
+   = help: the trait `Sized` is not implemented for `[A]`
+   = note: all local variables must have a statically known size
 
-error[E0508]: cannot move out of type `[A]`, a non-copy slice
-  --> $DIR/move-out-of-slice-2.rs:17:11
+error[E0277]: the size for values of type `[A]` cannot be known at compilation time
+  --> $DIR/move-out-of-slice-2.rs:15:16
    |
-LL |     match *b {
-   |           ^^ cannot move out of here
-LL |
 LL |         [_, _, b @ .., _] => {}
-   |                -
-   |                |
-   |                data moved here
-   |                move occurs because `b` has type `[A]`, which does not implement the `Copy` trait
+   |                ^^^^^^ doesn't have a size known at compile-time
    |
-help: consider borrowing the pattern binding
-   |
-LL |         [_, _, ref b @ .., _] => {}
-   |                +++
+   = help: the trait `Sized` is not implemented for `[A]`
+   = note: all local variables must have a statically known size
 
-error[E0508]: cannot move out of type `[C]`, a non-copy slice
-  --> $DIR/move-out-of-slice-2.rs:25:11
+error[E0277]: the size for values of type `[C]` cannot be known at compilation time
+  --> $DIR/move-out-of-slice-2.rs:22:10
    |
-LL |     match *c {
-   |           ^^ cannot move out of here
-LL |
 LL |         [c @ ..] => {}
-   |          -
-   |          |
-   |          data moved here
-   |          move occurs because `c` has type `[C]`, which does not implement the `Copy` trait
-   |
-help: consider borrowing the pattern binding
+   |          ^^^^^^ doesn't have a size known at compile-time
    |
-LL |         [ref c @ ..] => {}
-   |          +++
+   = help: the trait `Sized` is not implemented for `[C]`
+   = note: all local variables must have a statically known size
 
-error[E0508]: cannot move out of type `[C]`, a non-copy slice
-  --> $DIR/move-out-of-slice-2.rs:31:11
+error[E0277]: the size for values of type `[C]` cannot be known at compilation time
+  --> $DIR/move-out-of-slice-2.rs:27:16
    |
-LL |     match *d {
-   |           ^^ cannot move out of here
-LL |
 LL |         [_, _, d @ .., _] => {}
-   |                -
-   |                |
-   |                data moved here
-   |                move occurs because `d` has type `[C]`, which does not implement the `Copy` trait
-   |
-help: consider borrowing the pattern binding
+   |                ^^^^^^ doesn't have a size known at compile-time
    |
-LL |         [_, _, ref d @ .., _] => {}
-   |                +++
+   = help: the trait `Sized` is not implemented for `[C]`
+   = note: all local variables must have a statically known size
 
-error: aborting due to 4 previous errors; 1 warning emitted
+error: aborting due to 4 previous errors
 
-For more information about this error, try `rustc --explain E0508`.
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/nll/sugg-mut-for-binding-issue-137486.fixed b/tests/ui/nll/sugg-mut-for-binding-issue-137486.fixed
new file mode 100644
index 00000000000..ee9d9a373de
--- /dev/null
+++ b/tests/ui/nll/sugg-mut-for-binding-issue-137486.fixed
@@ -0,0 +1,23 @@
+//@ run-rustfix
+#![allow(unused_assignments)]
+
+use std::pin::Pin;
+fn main() {
+    let mut s = String::from("hello");
+    let mut ref_s = &mut s;
+
+    let mut binding = String::from("world");
+    ref_s = &mut binding; //~ ERROR temporary value dropped while borrowed [E0716]
+
+    print!("r1 = {}", ref_s);
+
+    let mut val: u8 = 5;
+    let mut s = Pin::new(&mut val);
+    let mut ref_s = &mut s;
+
+    let mut val2: u8 = 10;
+    let mut binding = Pin::new(&mut val2);
+    ref_s = &mut binding; //~ ERROR temporary value dropped while borrowed [E0716]
+
+    print!("r1 = {}", ref_s);
+}
diff --git a/tests/ui/nll/sugg-mut-for-binding-issue-137486.rs b/tests/ui/nll/sugg-mut-for-binding-issue-137486.rs
new file mode 100644
index 00000000000..8f7ea756107
--- /dev/null
+++ b/tests/ui/nll/sugg-mut-for-binding-issue-137486.rs
@@ -0,0 +1,21 @@
+//@ run-rustfix
+#![allow(unused_assignments)]
+
+use std::pin::Pin;
+fn main() {
+    let mut s = String::from("hello");
+    let mut ref_s = &mut s;
+
+    ref_s = &mut String::from("world"); //~ ERROR temporary value dropped while borrowed [E0716]
+
+    print!("r1 = {}", ref_s);
+
+    let mut val: u8 = 5;
+    let mut s = Pin::new(&mut val);
+    let mut ref_s = &mut s;
+
+    let mut val2: u8 = 10;
+    ref_s = &mut Pin::new(&mut val2); //~ ERROR temporary value dropped while borrowed [E0716]
+
+    print!("r1 = {}", ref_s);
+}
diff --git a/tests/ui/nll/sugg-mut-for-binding-issue-137486.stderr b/tests/ui/nll/sugg-mut-for-binding-issue-137486.stderr
new file mode 100644
index 00000000000..8432725f60a
--- /dev/null
+++ b/tests/ui/nll/sugg-mut-for-binding-issue-137486.stderr
@@ -0,0 +1,37 @@
+error[E0716]: temporary value dropped while borrowed
+  --> $DIR/sugg-mut-for-binding-issue-137486.rs:9:18
+   |
+LL |     ref_s = &mut String::from("world");
+   |                  ^^^^^^^^^^^^^^^^^^^^^- temporary value is freed at the end of this statement
+   |                  |
+   |                  creates a temporary value which is freed while still in use
+LL |
+LL |     print!("r1 = {}", ref_s);
+   |                       ----- borrow later used here
+   |
+help: consider using a `let` binding to create a longer lived value
+   |
+LL ~     let mut binding = String::from("world");
+LL ~     ref_s = &mut binding;
+   |
+
+error[E0716]: temporary value dropped while borrowed
+  --> $DIR/sugg-mut-for-binding-issue-137486.rs:18:18
+   |
+LL |     ref_s = &mut Pin::new(&mut val2);
+   |                  ^^^^^^^^^^^^^^^^^^^- temporary value is freed at the end of this statement
+   |                  |
+   |                  creates a temporary value which is freed while still in use
+LL |
+LL |     print!("r1 = {}", ref_s);
+   |                       ----- borrow later used here
+   |
+help: consider using a `let` binding to create a longer lived value
+   |
+LL ~     let mut binding = Pin::new(&mut val2);
+LL ~     ref_s = &mut binding;
+   |
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0716`.
diff --git a/tests/ui/nullable-pointer-iotareduction.rs b/tests/ui/nullable-pointer-iotareduction.rs
index fa837dab51b..1b73164c9fc 100644
--- a/tests/ui/nullable-pointer-iotareduction.rs
+++ b/tests/ui/nullable-pointer-iotareduction.rs
@@ -8,6 +8,8 @@
 // trying to get assert failure messages that at least identify which case
 // failed.
 
+#![allow(unpredictable_function_pointer_comparisons)]
+
 enum E<T> { Thing(isize, T), #[allow(dead_code)] Nothing((), ((), ()), [i8; 0]) }
 impl<T> E<T> {
     fn is_none(&self) -> bool {
diff --git a/tests/ui/on-unimplemented/feature-gate-on-unimplemented.rs b/tests/ui/on-unimplemented/feature-gate-on-unimplemented.rs
index 3cc50e3499a..436caab5d64 100644
--- a/tests/ui/on-unimplemented/feature-gate-on-unimplemented.rs
+++ b/tests/ui/on-unimplemented/feature-gate-on-unimplemented.rs
@@ -1,8 +1,9 @@
 // Test that `#[rustc_on_unimplemented]` is gated by `rustc_attrs` feature gate.
 
 #[rustc_on_unimplemented = "test error `{Self}` with `{Bar}`"]
-//~^ ERROR this is an internal attribute that will never be stable
-trait Foo<Bar>
-{}
+//~^ ERROR use of an internal attribute [E0658]
+//~| NOTE the `#[rustc_on_unimplemented]` attribute is an internal implementation detail that will never be stable
+//~| NOTE see `#[diagnostic::on_unimplemented]` for the stable equivalent of this attribute
+trait Foo<Bar> {}
 
 fn main() {}
diff --git a/tests/ui/on-unimplemented/feature-gate-on-unimplemented.stderr b/tests/ui/on-unimplemented/feature-gate-on-unimplemented.stderr
index 2733f7478f0..d1983088af8 100644
--- a/tests/ui/on-unimplemented/feature-gate-on-unimplemented.stderr
+++ b/tests/ui/on-unimplemented/feature-gate-on-unimplemented.stderr
@@ -1,11 +1,12 @@
-error[E0658]: this is an internal attribute that will never be stable
+error[E0658]: use of an internal attribute
   --> $DIR/feature-gate-on-unimplemented.rs:3:1
    |
 LL | #[rustc_on_unimplemented = "test error `{Self}` with `{Bar}`"]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+   = note: the `#[rustc_on_unimplemented]` attribute is an internal implementation detail that will never be stable
+   = note: see `#[diagnostic::on_unimplemented]` for the stable equivalent of this attribute
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/parser/bad-fn-ptr-qualifier.fixed b/tests/ui/parser/bad-fn-ptr-qualifier.fixed
index 8a97a2f09cc..e9b87a72aab 100644
--- a/tests/ui/parser/bad-fn-ptr-qualifier.fixed
+++ b/tests/ui/parser/bad-fn-ptr-qualifier.fixed
@@ -23,4 +23,12 @@ pub type FTT6 = for<'a> unsafe extern "C" fn();
 //~^ ERROR an `fn` pointer type cannot be `const`
 //~| ERROR an `fn` pointer type cannot be `async`
 
+// Tests with qualifiers in the wrong order
+pub type W1 = unsafe fn();
+//~^ ERROR an `fn` pointer type cannot be `const`
+pub type W2 = unsafe fn();
+//~^ ERROR an `fn` pointer type cannot be `async`
+pub type W3 = for<'a> unsafe fn();
+//~^ ERROR an `fn` pointer type cannot be `const`
+
 fn main() {}
diff --git a/tests/ui/parser/bad-fn-ptr-qualifier.rs b/tests/ui/parser/bad-fn-ptr-qualifier.rs
index f2611c93b17..f110375509c 100644
--- a/tests/ui/parser/bad-fn-ptr-qualifier.rs
+++ b/tests/ui/parser/bad-fn-ptr-qualifier.rs
@@ -23,4 +23,12 @@ pub type FTT6 = for<'a> const async unsafe extern "C" fn();
 //~^ ERROR an `fn` pointer type cannot be `const`
 //~| ERROR an `fn` pointer type cannot be `async`
 
+// Tests with qualifiers in the wrong order
+pub type W1 = unsafe const fn();
+//~^ ERROR an `fn` pointer type cannot be `const`
+pub type W2 = unsafe async fn();
+//~^ ERROR an `fn` pointer type cannot be `async`
+pub type W3 = for<'a> unsafe const fn();
+//~^ ERROR an `fn` pointer type cannot be `const`
+
 fn main() {}
diff --git a/tests/ui/parser/bad-fn-ptr-qualifier.stderr b/tests/ui/parser/bad-fn-ptr-qualifier.stderr
index b9d2625d9f4..f3fbbf67159 100644
--- a/tests/ui/parser/bad-fn-ptr-qualifier.stderr
+++ b/tests/ui/parser/bad-fn-ptr-qualifier.stderr
@@ -2,10 +2,9 @@ error: an `fn` pointer type cannot be `const`
   --> $DIR/bad-fn-ptr-qualifier.rs:5:15
    |
 LL | pub type T0 = const fn();
-   |               -----^^^^^
-   |               |
-   |               `const` because of this
+   |               ^^^^^ `const` because of this
    |
+   = note: allowed qualifiers are: `unsafe` and `extern`
 help: remove the `const` qualifier
    |
 LL - pub type T0 = const fn();
@@ -16,10 +15,9 @@ error: an `fn` pointer type cannot be `const`
   --> $DIR/bad-fn-ptr-qualifier.rs:6:15
    |
 LL | pub type T1 = const extern "C" fn();
-   |               -----^^^^^^^^^^^^^^^^
-   |               |
-   |               `const` because of this
+   |               ^^^^^ `const` because of this
    |
+   = note: allowed qualifiers are: `unsafe` and `extern`
 help: remove the `const` qualifier
    |
 LL - pub type T1 = const extern "C" fn();
@@ -30,10 +28,9 @@ error: an `fn` pointer type cannot be `const`
   --> $DIR/bad-fn-ptr-qualifier.rs:7:15
    |
 LL | pub type T2 = const unsafe extern "C" fn();
-   |               -----^^^^^^^^^^^^^^^^^^^^^^^
-   |               |
-   |               `const` because of this
+   |               ^^^^^ `const` because of this
    |
+   = note: allowed qualifiers are: `unsafe` and `extern`
 help: remove the `const` qualifier
    |
 LL - pub type T2 = const unsafe extern "C" fn();
@@ -44,10 +41,9 @@ error: an `fn` pointer type cannot be `async`
   --> $DIR/bad-fn-ptr-qualifier.rs:8:15
    |
 LL | pub type T3 = async fn();
-   |               -----^^^^^
-   |               |
-   |               `async` because of this
+   |               ^^^^^ `async` because of this
    |
+   = note: allowed qualifiers are: `unsafe` and `extern`
 help: remove the `async` qualifier
    |
 LL - pub type T3 = async fn();
@@ -58,10 +54,9 @@ error: an `fn` pointer type cannot be `async`
   --> $DIR/bad-fn-ptr-qualifier.rs:9:15
    |
 LL | pub type T4 = async extern "C" fn();
-   |               -----^^^^^^^^^^^^^^^^
-   |               |
-   |               `async` because of this
+   |               ^^^^^ `async` because of this
    |
+   = note: allowed qualifiers are: `unsafe` and `extern`
 help: remove the `async` qualifier
    |
 LL - pub type T4 = async extern "C" fn();
@@ -72,10 +67,9 @@ error: an `fn` pointer type cannot be `async`
   --> $DIR/bad-fn-ptr-qualifier.rs:10:15
    |
 LL | pub type T5 = async unsafe extern "C" fn();
-   |               -----^^^^^^^^^^^^^^^^^^^^^^^
-   |               |
-   |               `async` because of this
+   |               ^^^^^ `async` because of this
    |
+   = note: allowed qualifiers are: `unsafe` and `extern`
 help: remove the `async` qualifier
    |
 LL - pub type T5 = async unsafe extern "C" fn();
@@ -86,10 +80,9 @@ error: an `fn` pointer type cannot be `const`
   --> $DIR/bad-fn-ptr-qualifier.rs:11:15
    |
 LL | pub type T6 = const async unsafe extern "C" fn();
-   |               -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |               |
-   |               `const` because of this
+   |               ^^^^^ `const` because of this
    |
+   = note: allowed qualifiers are: `unsafe` and `extern`
 help: remove the `const` qualifier
    |
 LL - pub type T6 = const async unsafe extern "C" fn();
@@ -97,13 +90,12 @@ LL + pub type T6 = async unsafe extern "C" fn();
    |
 
 error: an `fn` pointer type cannot be `async`
-  --> $DIR/bad-fn-ptr-qualifier.rs:11:15
+  --> $DIR/bad-fn-ptr-qualifier.rs:11:21
    |
 LL | pub type T6 = const async unsafe extern "C" fn();
-   |               ^^^^^^-----^^^^^^^^^^^^^^^^^^^^^^^
-   |                     |
-   |                     `async` because of this
+   |                     ^^^^^ `async` because of this
    |
+   = note: allowed qualifiers are: `unsafe` and `extern`
 help: remove the `async` qualifier
    |
 LL - pub type T6 = const async unsafe extern "C" fn();
@@ -111,13 +103,12 @@ LL + pub type T6 = const unsafe extern "C" fn();
    |
 
 error: an `fn` pointer type cannot be `const`
-  --> $DIR/bad-fn-ptr-qualifier.rs:15:17
+  --> $DIR/bad-fn-ptr-qualifier.rs:15:25
    |
 LL | pub type FTT0 = for<'a> const fn();
-   |                 ^^^^^^^^-----^^^^^
-   |                         |
-   |                         `const` because of this
+   |                         ^^^^^ `const` because of this
    |
+   = note: allowed qualifiers are: `unsafe` and `extern`
 help: remove the `const` qualifier
    |
 LL - pub type FTT0 = for<'a> const fn();
@@ -125,13 +116,12 @@ LL + pub type FTT0 = for<'a> fn();
    |
 
 error: an `fn` pointer type cannot be `const`
-  --> $DIR/bad-fn-ptr-qualifier.rs:16:17
+  --> $DIR/bad-fn-ptr-qualifier.rs:16:25
    |
 LL | pub type FTT1 = for<'a> const extern "C" fn();
-   |                 ^^^^^^^^-----^^^^^^^^^^^^^^^^
-   |                         |
-   |                         `const` because of this
+   |                         ^^^^^ `const` because of this
    |
+   = note: allowed qualifiers are: `unsafe` and `extern`
 help: remove the `const` qualifier
    |
 LL - pub type FTT1 = for<'a> const extern "C" fn();
@@ -139,13 +129,12 @@ LL + pub type FTT1 = for<'a> extern "C" fn();
    |
 
 error: an `fn` pointer type cannot be `const`
-  --> $DIR/bad-fn-ptr-qualifier.rs:17:17
+  --> $DIR/bad-fn-ptr-qualifier.rs:17:25
    |
 LL | pub type FTT2 = for<'a> const unsafe extern "C" fn();
-   |                 ^^^^^^^^-----^^^^^^^^^^^^^^^^^^^^^^^
-   |                         |
-   |                         `const` because of this
+   |                         ^^^^^ `const` because of this
    |
+   = note: allowed qualifiers are: `unsafe` and `extern`
 help: remove the `const` qualifier
    |
 LL - pub type FTT2 = for<'a> const unsafe extern "C" fn();
@@ -153,13 +142,12 @@ LL + pub type FTT2 = for<'a> unsafe extern "C" fn();
    |
 
 error: an `fn` pointer type cannot be `async`
-  --> $DIR/bad-fn-ptr-qualifier.rs:18:17
+  --> $DIR/bad-fn-ptr-qualifier.rs:18:25
    |
 LL | pub type FTT3 = for<'a> async fn();
-   |                 ^^^^^^^^-----^^^^^
-   |                         |
-   |                         `async` because of this
+   |                         ^^^^^ `async` because of this
    |
+   = note: allowed qualifiers are: `unsafe` and `extern`
 help: remove the `async` qualifier
    |
 LL - pub type FTT3 = for<'a> async fn();
@@ -167,13 +155,12 @@ LL + pub type FTT3 = for<'a> fn();
    |
 
 error: an `fn` pointer type cannot be `async`
-  --> $DIR/bad-fn-ptr-qualifier.rs:19:17
+  --> $DIR/bad-fn-ptr-qualifier.rs:19:25
    |
 LL | pub type FTT4 = for<'a> async extern "C" fn();
-   |                 ^^^^^^^^-----^^^^^^^^^^^^^^^^
-   |                         |
-   |                         `async` because of this
+   |                         ^^^^^ `async` because of this
    |
+   = note: allowed qualifiers are: `unsafe` and `extern`
 help: remove the `async` qualifier
    |
 LL - pub type FTT4 = for<'a> async extern "C" fn();
@@ -181,13 +168,12 @@ LL + pub type FTT4 = for<'a> extern "C" fn();
    |
 
 error: an `fn` pointer type cannot be `async`
-  --> $DIR/bad-fn-ptr-qualifier.rs:20:17
+  --> $DIR/bad-fn-ptr-qualifier.rs:20:25
    |
 LL | pub type FTT5 = for<'a> async unsafe extern "C" fn();
-   |                 ^^^^^^^^-----^^^^^^^^^^^^^^^^^^^^^^^
-   |                         |
-   |                         `async` because of this
+   |                         ^^^^^ `async` because of this
    |
+   = note: allowed qualifiers are: `unsafe` and `extern`
 help: remove the `async` qualifier
    |
 LL - pub type FTT5 = for<'a> async unsafe extern "C" fn();
@@ -195,13 +181,12 @@ LL + pub type FTT5 = for<'a> unsafe extern "C" fn();
    |
 
 error: an `fn` pointer type cannot be `const`
-  --> $DIR/bad-fn-ptr-qualifier.rs:22:17
+  --> $DIR/bad-fn-ptr-qualifier.rs:22:25
    |
 LL | pub type FTT6 = for<'a> const async unsafe extern "C" fn();
-   |                 ^^^^^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |                         |
-   |                         `const` because of this
+   |                         ^^^^^ `const` because of this
    |
+   = note: allowed qualifiers are: `unsafe` and `extern`
 help: remove the `const` qualifier
    |
 LL - pub type FTT6 = for<'a> const async unsafe extern "C" fn();
@@ -209,18 +194,56 @@ LL + pub type FTT6 = for<'a> async unsafe extern "C" fn();
    |
 
 error: an `fn` pointer type cannot be `async`
-  --> $DIR/bad-fn-ptr-qualifier.rs:22:17
+  --> $DIR/bad-fn-ptr-qualifier.rs:22:31
    |
 LL | pub type FTT6 = for<'a> const async unsafe extern "C" fn();
-   |                 ^^^^^^^^^^^^^^-----^^^^^^^^^^^^^^^^^^^^^^^
-   |                               |
-   |                               `async` because of this
+   |                               ^^^^^ `async` because of this
    |
+   = note: allowed qualifiers are: `unsafe` and `extern`
 help: remove the `async` qualifier
    |
 LL - pub type FTT6 = for<'a> const async unsafe extern "C" fn();
 LL + pub type FTT6 = for<'a> const unsafe extern "C" fn();
    |
 
-error: aborting due to 16 previous errors
+error: an `fn` pointer type cannot be `const`
+  --> $DIR/bad-fn-ptr-qualifier.rs:27:22
+   |
+LL | pub type W1 = unsafe const fn();
+   |                      ^^^^^ `const` because of this
+   |
+   = note: allowed qualifiers are: `unsafe` and `extern`
+help: remove the `const` qualifier
+   |
+LL - pub type W1 = unsafe const fn();
+LL + pub type W1 = unsafe fn();
+   |
+
+error: an `fn` pointer type cannot be `async`
+  --> $DIR/bad-fn-ptr-qualifier.rs:29:22
+   |
+LL | pub type W2 = unsafe async fn();
+   |                      ^^^^^ `async` because of this
+   |
+   = note: allowed qualifiers are: `unsafe` and `extern`
+help: remove the `async` qualifier
+   |
+LL - pub type W2 = unsafe async fn();
+LL + pub type W2 = unsafe fn();
+   |
+
+error: an `fn` pointer type cannot be `const`
+  --> $DIR/bad-fn-ptr-qualifier.rs:31:30
+   |
+LL | pub type W3 = for<'a> unsafe const fn();
+   |                              ^^^^^ `const` because of this
+   |
+   = note: allowed qualifiers are: `unsafe` and `extern`
+help: remove the `const` qualifier
+   |
+LL - pub type W3 = for<'a> unsafe const fn();
+LL + pub type W3 = for<'a> unsafe fn();
+   |
+
+error: aborting due to 19 previous errors
 
diff --git a/tests/ui/parser/macro/issue-33569.rs b/tests/ui/parser/macro/issue-33569.rs
index 069d181e962..e0a5352ab06 100644
--- a/tests/ui/parser/macro/issue-33569.rs
+++ b/tests/ui/parser/macro/issue-33569.rs
@@ -2,7 +2,6 @@ macro_rules! foo {
     { $+ } => { //~ ERROR expected identifier, found `+`
                 //~^ ERROR missing fragment specifier
                 //~| ERROR missing fragment specifier
-                //~| WARN this was previously accepted
         $(x)(y) //~ ERROR expected one of: `*`, `+`, or `?`
     }
 }
diff --git a/tests/ui/parser/macro/issue-33569.stderr b/tests/ui/parser/macro/issue-33569.stderr
index d1b6abfeeeb..0d53c04c1c9 100644
--- a/tests/ui/parser/macro/issue-33569.stderr
+++ b/tests/ui/parser/macro/issue-33569.stderr
@@ -5,7 +5,7 @@ LL |     { $+ } => {
    |        ^
 
 error: expected one of: `*`, `+`, or `?`
-  --> $DIR/issue-33569.rs:6:13
+  --> $DIR/issue-33569.rs:5:13
    |
 LL |         $(x)(y)
    |             ^^^
@@ -15,27 +15,19 @@ error: missing fragment specifier
    |
 LL |     { $+ } => {
    |        ^
-
-error: missing fragment specifier
-  --> $DIR/issue-33569.rs:2:8
    |
-LL |     { $+ } => {
-   |        ^
+   = note: fragment specifiers must be provided
+   = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis`, along with `expr_2021` and `pat_param` for edition compatibility
+help: try adding a specifier here
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
-   = note: `#[deny(missing_fragment_specifier)]` on by default
+LL |     { $+:spec } => {
+   |         +++++
 
-error: aborting due to 4 previous errors
-
-Future incompatibility report: Future breakage diagnostic:
 error: missing fragment specifier
   --> $DIR/issue-33569.rs:2:8
    |
 LL |     { $+ } => {
    |        ^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
-   = note: `#[deny(missing_fragment_specifier)]` on by default
+
+error: aborting due to 4 previous errors
 
diff --git a/tests/ui/parser/recover/recover-const-async-fn-ptr.rs b/tests/ui/parser/recover/recover-const-async-fn-ptr.rs
index 45d75349599..a74b618c282 100644
--- a/tests/ui/parser/recover/recover-const-async-fn-ptr.rs
+++ b/tests/ui/parser/recover/recover-const-async-fn-ptr.rs
@@ -20,6 +20,14 @@ type FT6 = for<'a> const async unsafe extern "C" fn();
 //~^ ERROR an `fn` pointer type cannot be `const`
 //~| ERROR an `fn` pointer type cannot be `async`
 
+// Tests with qualifiers in the wrong order
+type W1 = unsafe const fn();
+//~^ ERROR an `fn` pointer type cannot be `const`
+type W2 = unsafe async fn();
+//~^ ERROR an `fn` pointer type cannot be `async`
+type W3 = for<'a> unsafe const fn();
+//~^ ERROR an `fn` pointer type cannot be `const`
+
 fn main() {
     let _recovery_witness: () = 0; //~ ERROR mismatched types
 }
diff --git a/tests/ui/parser/recover/recover-const-async-fn-ptr.stderr b/tests/ui/parser/recover/recover-const-async-fn-ptr.stderr
index 4e5927914cc..3700bee6e15 100644
--- a/tests/ui/parser/recover/recover-const-async-fn-ptr.stderr
+++ b/tests/ui/parser/recover/recover-const-async-fn-ptr.stderr
@@ -2,10 +2,9 @@ error: an `fn` pointer type cannot be `const`
   --> $DIR/recover-const-async-fn-ptr.rs:3:11
    |
 LL | type T0 = const fn();
-   |           -----^^^^^
-   |           |
-   |           `const` because of this
+   |           ^^^^^ `const` because of this
    |
+   = note: allowed qualifiers are: `unsafe` and `extern`
 help: remove the `const` qualifier
    |
 LL - type T0 = const fn();
@@ -16,10 +15,9 @@ error: an `fn` pointer type cannot be `const`
   --> $DIR/recover-const-async-fn-ptr.rs:4:11
    |
 LL | type T1 = const extern "C" fn();
-   |           -----^^^^^^^^^^^^^^^^
-   |           |
-   |           `const` because of this
+   |           ^^^^^ `const` because of this
    |
+   = note: allowed qualifiers are: `unsafe` and `extern`
 help: remove the `const` qualifier
    |
 LL - type T1 = const extern "C" fn();
@@ -30,10 +28,9 @@ error: an `fn` pointer type cannot be `const`
   --> $DIR/recover-const-async-fn-ptr.rs:5:11
    |
 LL | type T2 = const unsafe extern "C" fn();
-   |           -----^^^^^^^^^^^^^^^^^^^^^^^
-   |           |
-   |           `const` because of this
+   |           ^^^^^ `const` because of this
    |
+   = note: allowed qualifiers are: `unsafe` and `extern`
 help: remove the `const` qualifier
    |
 LL - type T2 = const unsafe extern "C" fn();
@@ -44,10 +41,9 @@ error: an `fn` pointer type cannot be `async`
   --> $DIR/recover-const-async-fn-ptr.rs:6:11
    |
 LL | type T3 = async fn();
-   |           -----^^^^^
-   |           |
-   |           `async` because of this
+   |           ^^^^^ `async` because of this
    |
+   = note: allowed qualifiers are: `unsafe` and `extern`
 help: remove the `async` qualifier
    |
 LL - type T3 = async fn();
@@ -58,10 +54,9 @@ error: an `fn` pointer type cannot be `async`
   --> $DIR/recover-const-async-fn-ptr.rs:7:11
    |
 LL | type T4 = async extern "C" fn();
-   |           -----^^^^^^^^^^^^^^^^
-   |           |
-   |           `async` because of this
+   |           ^^^^^ `async` because of this
    |
+   = note: allowed qualifiers are: `unsafe` and `extern`
 help: remove the `async` qualifier
    |
 LL - type T4 = async extern "C" fn();
@@ -72,10 +67,9 @@ error: an `fn` pointer type cannot be `async`
   --> $DIR/recover-const-async-fn-ptr.rs:8:11
    |
 LL | type T5 = async unsafe extern "C" fn();
-   |           -----^^^^^^^^^^^^^^^^^^^^^^^
-   |           |
-   |           `async` because of this
+   |           ^^^^^ `async` because of this
    |
+   = note: allowed qualifiers are: `unsafe` and `extern`
 help: remove the `async` qualifier
    |
 LL - type T5 = async unsafe extern "C" fn();
@@ -86,10 +80,9 @@ error: an `fn` pointer type cannot be `const`
   --> $DIR/recover-const-async-fn-ptr.rs:9:11
    |
 LL | type T6 = const async unsafe extern "C" fn();
-   |           -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |           |
-   |           `const` because of this
+   |           ^^^^^ `const` because of this
    |
+   = note: allowed qualifiers are: `unsafe` and `extern`
 help: remove the `const` qualifier
    |
 LL - type T6 = const async unsafe extern "C" fn();
@@ -97,13 +90,12 @@ LL + type T6 = async unsafe extern "C" fn();
    |
 
 error: an `fn` pointer type cannot be `async`
-  --> $DIR/recover-const-async-fn-ptr.rs:9:11
+  --> $DIR/recover-const-async-fn-ptr.rs:9:17
    |
 LL | type T6 = const async unsafe extern "C" fn();
-   |           ^^^^^^-----^^^^^^^^^^^^^^^^^^^^^^^
-   |                 |
-   |                 `async` because of this
+   |                 ^^^^^ `async` because of this
    |
+   = note: allowed qualifiers are: `unsafe` and `extern`
 help: remove the `async` qualifier
    |
 LL - type T6 = const async unsafe extern "C" fn();
@@ -111,13 +103,12 @@ LL + type T6 = const unsafe extern "C" fn();
    |
 
 error: an `fn` pointer type cannot be `const`
-  --> $DIR/recover-const-async-fn-ptr.rs:13:12
+  --> $DIR/recover-const-async-fn-ptr.rs:13:20
    |
 LL | type FT0 = for<'a> const fn();
-   |            ^^^^^^^^-----^^^^^
-   |                    |
-   |                    `const` because of this
+   |                    ^^^^^ `const` because of this
    |
+   = note: allowed qualifiers are: `unsafe` and `extern`
 help: remove the `const` qualifier
    |
 LL - type FT0 = for<'a> const fn();
@@ -125,13 +116,12 @@ LL + type FT0 = for<'a> fn();
    |
 
 error: an `fn` pointer type cannot be `const`
-  --> $DIR/recover-const-async-fn-ptr.rs:14:12
+  --> $DIR/recover-const-async-fn-ptr.rs:14:20
    |
 LL | type FT1 = for<'a> const extern "C" fn();
-   |            ^^^^^^^^-----^^^^^^^^^^^^^^^^
-   |                    |
-   |                    `const` because of this
+   |                    ^^^^^ `const` because of this
    |
+   = note: allowed qualifiers are: `unsafe` and `extern`
 help: remove the `const` qualifier
    |
 LL - type FT1 = for<'a> const extern "C" fn();
@@ -139,13 +129,12 @@ LL + type FT1 = for<'a> extern "C" fn();
    |
 
 error: an `fn` pointer type cannot be `const`
-  --> $DIR/recover-const-async-fn-ptr.rs:15:12
+  --> $DIR/recover-const-async-fn-ptr.rs:15:20
    |
 LL | type FT2 = for<'a> const unsafe extern "C" fn();
-   |            ^^^^^^^^-----^^^^^^^^^^^^^^^^^^^^^^^
-   |                    |
-   |                    `const` because of this
+   |                    ^^^^^ `const` because of this
    |
+   = note: allowed qualifiers are: `unsafe` and `extern`
 help: remove the `const` qualifier
    |
 LL - type FT2 = for<'a> const unsafe extern "C" fn();
@@ -153,13 +142,12 @@ LL + type FT2 = for<'a> unsafe extern "C" fn();
    |
 
 error: an `fn` pointer type cannot be `async`
-  --> $DIR/recover-const-async-fn-ptr.rs:16:12
+  --> $DIR/recover-const-async-fn-ptr.rs:16:20
    |
 LL | type FT3 = for<'a> async fn();
-   |            ^^^^^^^^-----^^^^^
-   |                    |
-   |                    `async` because of this
+   |                    ^^^^^ `async` because of this
    |
+   = note: allowed qualifiers are: `unsafe` and `extern`
 help: remove the `async` qualifier
    |
 LL - type FT3 = for<'a> async fn();
@@ -167,13 +155,12 @@ LL + type FT3 = for<'a> fn();
    |
 
 error: an `fn` pointer type cannot be `async`
-  --> $DIR/recover-const-async-fn-ptr.rs:17:12
+  --> $DIR/recover-const-async-fn-ptr.rs:17:20
    |
 LL | type FT4 = for<'a> async extern "C" fn();
-   |            ^^^^^^^^-----^^^^^^^^^^^^^^^^
-   |                    |
-   |                    `async` because of this
+   |                    ^^^^^ `async` because of this
    |
+   = note: allowed qualifiers are: `unsafe` and `extern`
 help: remove the `async` qualifier
    |
 LL - type FT4 = for<'a> async extern "C" fn();
@@ -181,13 +168,12 @@ LL + type FT4 = for<'a> extern "C" fn();
    |
 
 error: an `fn` pointer type cannot be `async`
-  --> $DIR/recover-const-async-fn-ptr.rs:18:12
+  --> $DIR/recover-const-async-fn-ptr.rs:18:20
    |
 LL | type FT5 = for<'a> async unsafe extern "C" fn();
-   |            ^^^^^^^^-----^^^^^^^^^^^^^^^^^^^^^^^
-   |                    |
-   |                    `async` because of this
+   |                    ^^^^^ `async` because of this
    |
+   = note: allowed qualifiers are: `unsafe` and `extern`
 help: remove the `async` qualifier
    |
 LL - type FT5 = for<'a> async unsafe extern "C" fn();
@@ -195,13 +181,12 @@ LL + type FT5 = for<'a> unsafe extern "C" fn();
    |
 
 error: an `fn` pointer type cannot be `const`
-  --> $DIR/recover-const-async-fn-ptr.rs:19:12
+  --> $DIR/recover-const-async-fn-ptr.rs:19:20
    |
 LL | type FT6 = for<'a> const async unsafe extern "C" fn();
-   |            ^^^^^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |                    |
-   |                    `const` because of this
+   |                    ^^^^^ `const` because of this
    |
+   = note: allowed qualifiers are: `unsafe` and `extern`
 help: remove the `const` qualifier
    |
 LL - type FT6 = for<'a> const async unsafe extern "C" fn();
@@ -209,27 +194,65 @@ LL + type FT6 = for<'a> async unsafe extern "C" fn();
    |
 
 error: an `fn` pointer type cannot be `async`
-  --> $DIR/recover-const-async-fn-ptr.rs:19:12
+  --> $DIR/recover-const-async-fn-ptr.rs:19:26
    |
 LL | type FT6 = for<'a> const async unsafe extern "C" fn();
-   |            ^^^^^^^^^^^^^^-----^^^^^^^^^^^^^^^^^^^^^^^
-   |                          |
-   |                          `async` because of this
+   |                          ^^^^^ `async` because of this
    |
+   = note: allowed qualifiers are: `unsafe` and `extern`
 help: remove the `async` qualifier
    |
 LL - type FT6 = for<'a> const async unsafe extern "C" fn();
 LL + type FT6 = for<'a> const unsafe extern "C" fn();
    |
 
+error: an `fn` pointer type cannot be `const`
+  --> $DIR/recover-const-async-fn-ptr.rs:24:18
+   |
+LL | type W1 = unsafe const fn();
+   |                  ^^^^^ `const` because of this
+   |
+   = note: allowed qualifiers are: `unsafe` and `extern`
+help: remove the `const` qualifier
+   |
+LL - type W1 = unsafe const fn();
+LL + type W1 = unsafe fn();
+   |
+
+error: an `fn` pointer type cannot be `async`
+  --> $DIR/recover-const-async-fn-ptr.rs:26:18
+   |
+LL | type W2 = unsafe async fn();
+   |                  ^^^^^ `async` because of this
+   |
+   = note: allowed qualifiers are: `unsafe` and `extern`
+help: remove the `async` qualifier
+   |
+LL - type W2 = unsafe async fn();
+LL + type W2 = unsafe fn();
+   |
+
+error: an `fn` pointer type cannot be `const`
+  --> $DIR/recover-const-async-fn-ptr.rs:28:26
+   |
+LL | type W3 = for<'a> unsafe const fn();
+   |                          ^^^^^ `const` because of this
+   |
+   = note: allowed qualifiers are: `unsafe` and `extern`
+help: remove the `const` qualifier
+   |
+LL - type W3 = for<'a> unsafe const fn();
+LL + type W3 = for<'a> unsafe fn();
+   |
+
 error[E0308]: mismatched types
-  --> $DIR/recover-const-async-fn-ptr.rs:24:33
+  --> $DIR/recover-const-async-fn-ptr.rs:32:33
    |
 LL |     let _recovery_witness: () = 0;
    |                            --   ^ expected `()`, found integer
    |                            |
    |                            expected due to this
 
-error: aborting due to 17 previous errors
+error: aborting due to 20 previous errors
 
 For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/print-calling-conventions.stdout b/tests/ui/print-calling-conventions.stdout
index feee8cc3aa9..7b5ae495660 100644
--- a/tests/ui/print-calling-conventions.stdout
+++ b/tests/ui/print-calling-conventions.stdout
@@ -9,6 +9,7 @@ avr-interrupt
 avr-non-blocking-interrupt
 cdecl
 cdecl-unwind
+custom
 efiapi
 fastcall
 fastcall-unwind
diff --git a/tests/ui/proc-macro/derive-helper-legacy-limits.stderr b/tests/ui/proc-macro/derive-helper-legacy-limits.stderr
index f5cd455435d..df3c5c47ddb 100644
--- a/tests/ui/proc-macro/derive-helper-legacy-limits.stderr
+++ b/tests/ui/proc-macro/derive-helper-legacy-limits.stderr
@@ -3,6 +3,12 @@ error: cannot find attribute `empty_helper` in this scope
    |
 LL | #[empty_helper]
    |   ^^^^^^^^^^^^
+   |
+help: `empty_helper` is an attribute that can be used by the derive macro `Empty`, you might be missing a `derive` attribute
+   |
+LL + #[derive(Empty)]
+LL | struct S2;
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/proc-macro/derive-helper-shadowing.stderr b/tests/ui/proc-macro/derive-helper-shadowing.stderr
index f284b1c54dd..1206778bb23 100644
--- a/tests/ui/proc-macro/derive-helper-shadowing.stderr
+++ b/tests/ui/proc-macro/derive-helper-shadowing.stderr
@@ -16,6 +16,7 @@ error: cannot find attribute `empty_helper` in this scope
 LL |             #[derive(GenHelperUse)]
    |                      ^^^^^^^^^^^^
    |
+   = note: `empty_helper` is an attribute that can be used by the derive macro `Empty`, you might be missing a `derive` attribute
    = note: this error originates in the derive macro `GenHelperUse` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider importing this attribute macro through its public re-export
    |
@@ -31,6 +32,7 @@ LL |         #[empty_helper]
 LL |             gen_helper_use!();
    |             ----------------- in this macro invocation
    |
+   = note: `empty_helper` is an attribute that can be used by the derive macro `Empty`, you might be missing a `derive` attribute
    = note: this error originates in the macro `gen_helper_use` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider importing this attribute macro through its public re-export
    |
diff --git a/tests/ui/proc-macro/disappearing-resolution.stderr b/tests/ui/proc-macro/disappearing-resolution.stderr
index 734e0cd2ab6..6c0f1a52915 100644
--- a/tests/ui/proc-macro/disappearing-resolution.stderr
+++ b/tests/ui/proc-macro/disappearing-resolution.stderr
@@ -3,6 +3,12 @@ error: cannot find attribute `empty_helper` in this scope
    |
 LL | #[empty_helper]
    |   ^^^^^^^^^^^^
+   |
+help: `empty_helper` is an attribute that can be used by the derive macro `Empty`, you might be missing a `derive` attribute
+   |
+LL + #[derive(Empty)]
+LL | struct S;
+   |
 
 error[E0603]: derive macro import `Empty` is private
   --> $DIR/disappearing-resolution.rs:11:8
diff --git a/tests/ui/rustdoc/feature-gate-doc_primitive.rs b/tests/ui/rustdoc/feature-gate-doc_primitive.rs
index 78fcd90752e..dbf92f19378 100644
--- a/tests/ui/rustdoc/feature-gate-doc_primitive.rs
+++ b/tests/ui/rustdoc/feature-gate-doc_primitive.rs
@@ -1,5 +1,7 @@
 #[rustc_doc_primitive = "usize"]
-//~^ ERROR `rustc_doc_primitive` is a rustc internal attribute
+//~^ ERROR use of an internal attribute [E0658]
+//~| NOTE the `#[rustc_doc_primitive]` attribute is an internal implementation detail that will never be stable
+//~| NOTE the `#[rustc_doc_primitive]` attribute is used by the standard library to provide a way to generate documentation for primitive types
 /// Some docs
 mod usize {}
 
diff --git a/tests/ui/rustdoc/feature-gate-doc_primitive.stderr b/tests/ui/rustdoc/feature-gate-doc_primitive.stderr
index e74b1322b25..0b1af78b504 100644
--- a/tests/ui/rustdoc/feature-gate-doc_primitive.stderr
+++ b/tests/ui/rustdoc/feature-gate-doc_primitive.stderr
@@ -1,11 +1,12 @@
-error[E0658]: `rustc_doc_primitive` is a rustc internal attribute
+error[E0658]: use of an internal attribute
   --> $DIR/feature-gate-doc_primitive.rs:1:1
    |
 LL | #[rustc_doc_primitive = "usize"]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+   = note: the `#[rustc_doc_primitive]` attribute is an internal implementation detail that will never be stable
+   = note: the `#[rustc_doc_primitive]` attribute is used by the standard library to provide a way to generate documentation for primitive types
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/self/arbitrary_self_types_pointers_and_wrappers.rs b/tests/ui/self/arbitrary_self_types_pointers_and_wrappers.rs
index 76d7754384e..4dc170c3a65 100644
--- a/tests/ui/self/arbitrary_self_types_pointers_and_wrappers.rs
+++ b/tests/ui/self/arbitrary_self_types_pointers_and_wrappers.rs
@@ -50,10 +50,6 @@ impl<T: DispatchFromDyn<U>, U> DispatchFromDyn<Wrapper<U>> for Wrapper<T> {}
 
 
 trait Trait {
-    // This method isn't dyn-compatible yet. Unsized by-value `self` is dyn-compatible (but not
-    // callable without unsized_locals), but wrappers arond `Self` currently are not.
-    // FIXME (mikeyhew) uncomment this when unsized rvalues dyn-compatibility is implemented
-    // fn wrapper(self: Wrapper<Self>) -> i32;
     fn ptr_wrapper(self: Ptr<Wrapper<Self>>) -> i32;
     fn wrapper_ptr(self: Wrapper<Ptr<Self>>) -> i32;
     fn wrapper_ptr_wrapper(self: Wrapper<Ptr<Wrapper<Self>>>) -> i32;
diff --git a/tests/ui/simd/size-align.rs b/tests/ui/simd/size-align.rs
index ff23ea5980b..53acb686fb8 100644
--- a/tests/ui/simd/size-align.rs
+++ b/tests/ui/simd/size-align.rs
@@ -7,12 +7,12 @@
 
 use std::mem;
 
-/// `T` should satisfy `size_of T (mod min_align_of T) === 0` to be stored at `Vec<T>` properly
+/// `T` should satisfy `size_of T (mod align_of T) === 0` to be stored at `Vec<T>` properly
 /// Please consult the issue #20460
 fn check<T>() {
-    assert_eq!(mem::size_of::<T>() % mem::min_align_of::<T>(), 0);
-    assert_eq!(mem::size_of::<T>() % mem::min_align_of::<T>(), 0);
-    assert_eq!(mem::size_of::<T>() % mem::min_align_of::<T>(), 0);
+    assert_eq!(mem::size_of::<T>() % mem::align_of::<T>(), 0);
+    assert_eq!(mem::size_of::<T>() % mem::align_of::<T>(), 0);
+    assert_eq!(mem::size_of::<T>() % mem::align_of::<T>(), 0);
 }
 
 #[repr(simd)]
diff --git a/tests/ui/sized/unsized-binding.stderr b/tests/ui/sized/unsized-binding.stderr
index 8de236cd0b6..da3ba53b0bf 100644
--- a/tests/ui/sized/unsized-binding.stderr
+++ b/tests/ui/sized/unsized-binding.stderr
@@ -6,7 +6,6 @@ LL |     let x = *"";
    |
    = help: the trait `Sized` is not implemented for `str`
    = note: all local variables must have a statically known size
-   = help: unsized locals are gated as an unstable feature
 help: references are always `Sized`, even if they point to unsized data; consider not dereferencing the expression
    |
 LL -     let x = *"";
diff --git a/tests/ui/sized/unsized-str-in-return-expr-arg-and-local.rs b/tests/ui/sized/unsized-str-in-return-expr-arg-and-local.rs
index 35abbb80d99..fff6806e9a4 100644
--- a/tests/ui/sized/unsized-str-in-return-expr-arg-and-local.rs
+++ b/tests/ui/sized/unsized-str-in-return-expr-arg-and-local.rs
@@ -16,7 +16,6 @@ fn main() {
     //~^ ERROR the size for values of type `str` cannot be known at compilation time
     //~| HELP consider not dereferencing the expression
     //~| HELP the trait `Sized` is not implemented for `str`
-    //~| HELP unsized locals are gated as an unstable feature
     bar(x);
     S.baz(x);
     bar(*"");
diff --git a/tests/ui/sized/unsized-str-in-return-expr-arg-and-local.stderr b/tests/ui/sized/unsized-str-in-return-expr-arg-and-local.stderr
index 9b7258aff12..29cedf3ecfd 100644
--- a/tests/ui/sized/unsized-str-in-return-expr-arg-and-local.stderr
+++ b/tests/ui/sized/unsized-str-in-return-expr-arg-and-local.stderr
@@ -22,7 +22,6 @@ LL |     let x = *"";
    |
    = help: the trait `Sized` is not implemented for `str`
    = note: all local variables must have a statically known size
-   = help: unsized locals are gated as an unstable feature
 help: references are always `Sized`, even if they point to unsized data; consider not dereferencing the expression
    |
 LL -     let x = *"";
@@ -30,7 +29,7 @@ LL +     let x = "";
    |
 
 error[E0277]: the size for values of type `str` cannot be known at compilation time
-  --> $DIR/unsized-str-in-return-expr-arg-and-local.rs:22:9
+  --> $DIR/unsized-str-in-return-expr-arg-and-local.rs:21:9
    |
 LL |     bar(*"");
    |     --- ^^^ doesn't have a size known at compile-time
@@ -50,7 +49,7 @@ LL +     bar("");
    |
 
 error[E0277]: the size for values of type `str` cannot be known at compilation time
-  --> $DIR/unsized-str-in-return-expr-arg-and-local.rs:26:11
+  --> $DIR/unsized-str-in-return-expr-arg-and-local.rs:25:11
    |
 LL |     S.baz(*"");
    |       --- ^^^ doesn't have a size known at compile-time
diff --git a/tests/ui/stability-attribute/renamed_feature.rs b/tests/ui/stability-attribute/renamed_feature.rs
new file mode 100644
index 00000000000..249c2abecff
--- /dev/null
+++ b/tests/ui/stability-attribute/renamed_feature.rs
@@ -0,0 +1,3 @@
+#![feature(try_trait)] //~ ERROR feature `try_trait` has been renamed to `try_trait_v2` [E0635]
+
+fn main() {}
diff --git a/tests/ui/stability-attribute/renamed_feature.stderr b/tests/ui/stability-attribute/renamed_feature.stderr
new file mode 100644
index 00000000000..293a2feffb2
--- /dev/null
+++ b/tests/ui/stability-attribute/renamed_feature.stderr
@@ -0,0 +1,9 @@
+error[E0635]: feature `try_trait` has been renamed to `try_trait_v2`
+  --> $DIR/renamed_feature.rs:1:12
+   |
+LL | #![feature(try_trait)]
+   |            ^^^^^^^^^
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0635`.
diff --git a/tests/ui/static/static-drop-scope.rs b/tests/ui/static/static-drop-scope.rs
index 74b224c9be0..ddcabeb12cc 100644
--- a/tests/ui/static/static-drop-scope.rs
+++ b/tests/ui/static/static-drop-scope.rs
@@ -4,12 +4,6 @@ impl Drop for WithDtor {
     fn drop(&mut self) {}
 }
 
-static PROMOTION_FAIL_S: Option<&'static WithDtor> = Some(&WithDtor);
-//~^ ERROR destructor of
-
-const PROMOTION_FAIL_C: Option<&'static WithDtor> = Some(&WithDtor);
-//~^ ERROR destructor of
-
 static EARLY_DROP_S: i32 = (WithDtor, 0).1;
 //~^ ERROR destructor of
 
diff --git a/tests/ui/static/static-drop-scope.stderr b/tests/ui/static/static-drop-scope.stderr
index 24658bc601e..0fdf081e234 100644
--- a/tests/ui/static/static-drop-scope.stderr
+++ b/tests/ui/static/static-drop-scope.stderr
@@ -1,21 +1,5 @@
-error[E0493]: destructor of `WithDtor` cannot be evaluated at compile-time
-  --> $DIR/static-drop-scope.rs:7:60
-   |
-LL | static PROMOTION_FAIL_S: Option<&'static WithDtor> = Some(&WithDtor);
-   |                                                            ^^^^^^^^- value is dropped here
-   |                                                            |
-   |                                                            the destructor for this type cannot be evaluated in statics
-
-error[E0493]: destructor of `WithDtor` cannot be evaluated at compile-time
-  --> $DIR/static-drop-scope.rs:10:59
-   |
-LL | const PROMOTION_FAIL_C: Option<&'static WithDtor> = Some(&WithDtor);
-   |                                                           ^^^^^^^^- value is dropped here
-   |                                                           |
-   |                                                           the destructor for this type cannot be evaluated in constants
-
 error[E0493]: destructor of `(WithDtor, i32)` cannot be evaluated at compile-time
-  --> $DIR/static-drop-scope.rs:13:28
+  --> $DIR/static-drop-scope.rs:7:28
    |
 LL | static EARLY_DROP_S: i32 = (WithDtor, 0).1;
    |                            ^^^^^^^^^^^^^ - value is dropped here
@@ -23,7 +7,7 @@ LL | static EARLY_DROP_S: i32 = (WithDtor, 0).1;
    |                            the destructor for this type cannot be evaluated in statics
 
 error[E0493]: destructor of `(WithDtor, i32)` cannot be evaluated at compile-time
-  --> $DIR/static-drop-scope.rs:16:27
+  --> $DIR/static-drop-scope.rs:10:27
    |
 LL | const EARLY_DROP_C: i32 = (WithDtor, 0).1;
    |                           ^^^^^^^^^^^^^ - value is dropped here
@@ -31,7 +15,7 @@ LL | const EARLY_DROP_C: i32 = (WithDtor, 0).1;
    |                           the destructor for this type cannot be evaluated in constants
 
 error[E0493]: destructor of `(Option<WithDtor>, i32)` cannot be evaluated at compile-time
-  --> $DIR/static-drop-scope.rs:27:34
+  --> $DIR/static-drop-scope.rs:21:34
    |
 LL | const EARLY_DROP_C_OPTION: i32 = (Some(WithDtor), 0).1;
    |                                  ^^^^^^^^^^^^^^^^^^^ - value is dropped here
@@ -39,7 +23,7 @@ LL | const EARLY_DROP_C_OPTION: i32 = (Some(WithDtor), 0).1;
    |                                  the destructor for this type cannot be evaluated in constants
 
 error[E0493]: destructor of `(Option<WithDtor>, i32)` cannot be evaluated at compile-time
-  --> $DIR/static-drop-scope.rs:32:43
+  --> $DIR/static-drop-scope.rs:26:43
    |
 LL | const EARLY_DROP_C_OPTION_CONSTANT: i32 = (HELPER, 0).1;
    |                                           ^^^^^^^^^^^ - value is dropped here
@@ -47,7 +31,7 @@ LL | const EARLY_DROP_C_OPTION_CONSTANT: i32 = (HELPER, 0).1;
    |                                           the destructor for this type cannot be evaluated in constants
 
 error[E0493]: destructor of `T` cannot be evaluated at compile-time
-  --> $DIR/static-drop-scope.rs:19:24
+  --> $DIR/static-drop-scope.rs:13:24
    |
 LL | const fn const_drop<T>(_: T) {}
    |                        ^      - value is dropped here
@@ -55,7 +39,7 @@ LL | const fn const_drop<T>(_: T) {}
    |                        the destructor for this type cannot be evaluated in constant functions
 
 error[E0493]: destructor of `(T, ())` cannot be evaluated at compile-time
-  --> $DIR/static-drop-scope.rs:23:5
+  --> $DIR/static-drop-scope.rs:17:5
    |
 LL |     (x, ()).1
    |     ^^^^^^^ the destructor for this type cannot be evaluated in constant functions
@@ -63,6 +47,6 @@ LL |
 LL | }
    | - value is dropped here
 
-error: aborting due to 8 previous errors
+error: aborting due to 6 previous errors
 
 For more information about this error, try `rustc --explain E0493`.
diff --git a/tests/ui/stats/auxiliary/include.rs b/tests/ui/stats/auxiliary/include.rs
new file mode 100644
index 00000000000..7fb7c781137
--- /dev/null
+++ b/tests/ui/stats/auxiliary/include.rs
@@ -0,0 +1,3 @@
+fn zzz(x: u32) -> u32 {
+    x
+}
diff --git a/tests/ui/stats/input-stats.stderr b/tests/ui/stats/input-stats.stderr
index f2598bd7eaf..88f91bef30b 100644
--- a/tests/ui/stats/input-stats.stderr
+++ b/tests/ui/stats/input-stats.stderr
@@ -82,10 +82,10 @@ hir-stats - Expr                      32 (NN.N%)             1
 hir-stats - Let                       32 (NN.N%)             1
 hir-stats - Semi                      32 (NN.N%)             1
 hir-stats FnDecl                   120 (NN.N%)             3            40
-hir-stats Attribute                128 (NN.N%)             4            32
 hir-stats FieldDef                 128 (NN.N%)             2            64
 hir-stats GenericArgs              144 (NN.N%)             3            48
 hir-stats Variant                  144 (NN.N%)             2            72
+hir-stats Attribute                160 (NN.N%)             4            40
 hir-stats GenericBound             256 (NN.N%)             4            64
 hir-stats - Trait                    256 (NN.N%)             4
 hir-stats Block                    288 (NN.N%)             6            48
@@ -117,5 +117,5 @@ hir-stats - Use                      352 (NN.N%)             4
 hir-stats Path                   1_040 (NN.N%)            26            40
 hir-stats PathSegment            1_776 (NN.N%)            37            48
 hir-stats ----------------------------------------------------------------
-hir-stats Total                  8_644                   172
+hir-stats Total                  8_676                   172
 hir-stats
diff --git a/tests/ui/stats/macro-stats.rs b/tests/ui/stats/macro-stats.rs
new file mode 100644
index 00000000000..ee265d682fd
--- /dev/null
+++ b/tests/ui/stats/macro-stats.rs
@@ -0,0 +1,130 @@
+//@ check-pass
+//@ compile-flags: -Zmacro-stats
+
+#[test]
+fn test_foo() {
+    let what = "this";
+    let how = "completely";
+    let when = "immediately";
+    println!("{what} disappears {how} and {when}");
+}
+
+#[rustfmt::skip] // non-macro attr, ignored by `-Zmacro-stats`
+fn rustfmt_skip() {
+    // Nothing to see here.
+}
+
+#[derive(Default, Clone, Copy, Hash)]
+enum E1 {
+    #[default] // non-macro attr, ignored by `-Zmacro-stats`
+    A,
+    B,
+}
+
+#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
+struct E2 {
+    a: u32,
+    b: String,
+    c: Vec<bool>,
+}
+
+#[derive(Clone)] struct S0;
+#[derive(Clone)] struct S1(u32);
+#[derive(Clone)] struct S2(u32, u32);
+#[derive(Clone)] struct S3(u32, u32, u32);
+#[derive(Clone)] struct S4(u32, u32, u32, u32);
+#[derive(Clone)] struct S5(u32, u32, u32, u32, u32);
+
+macro_rules! u32 {
+    () => { u32 }
+}
+
+macro_rules! none {
+    () => { None }
+}
+fn opt(x: Option<u32>) {
+    match x {
+        Some(_) => {}
+        none!() => {}           // AstFragmentKind::Pat
+    }
+}
+
+macro_rules! this_is_a_really_really_long_macro_name {
+    ($t:ty) => {
+        fn f(_: $t) {}
+    }
+}
+this_is_a_really_really_long_macro_name!(u32!()); // AstFragmentKind::{Items,Ty}
+
+macro_rules! trait_tys {
+    () => {
+        type A;
+        type B;
+    }
+}
+trait Tr {
+    trait_tys!();               // AstFragmentKind::TraitItems
+}
+
+macro_rules! impl_const { () => { const X: u32 = 0; } }
+struct U;
+impl U {
+    impl_const!();              // AstFragmentKind::ImplItems
+}
+
+macro_rules! trait_impl_tys {
+    () => {
+        type A = u32;
+        type B = bool;
+    }
+}
+struct Tr1;
+impl Tr for Tr1 {
+    trait_impl_tys!();          // AstFragment::TraitImplItems
+}
+
+macro_rules! foreign_item {
+    () => { fn fc(a: u32) -> u32; }
+}
+extern "C" {
+    foreign_item!();            // AstFragment::ForeignItems
+}
+
+// Include macros are ignored by `-Zmacro-stats`.
+mod includes {
+    mod z1 {
+        include!("auxiliary/include.rs");
+    }
+    mod z2 {
+        std::include!("auxiliary/include.rs");
+    }
+
+    const B1: &[u8] = include_bytes!("auxiliary/include.rs");
+    const B2: &[u8] = std::include_bytes!("auxiliary/include.rs");
+
+    const S1: &str = include_str!("auxiliary/include.rs");
+    const S2: &str = std::include_str!("auxiliary/include.rs");
+}
+
+fn main() {
+    macro_rules! n99 {
+        () => { 99 }
+    }
+    let x = n99!() + n99!();    // AstFragmentKind::Expr
+
+    macro_rules! p {
+        () => {
+            // blah
+            let x = 1;
+            let y = x;
+            let _ = y;
+        }
+    }
+    p!();                       // AstFragmentKind::Stmts
+
+    macro_rules! q {
+        () => {};
+        ($($x:ident),*) => { $( let $x: u32 = 12345; )* };
+    }
+    q!(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z);
+}
diff --git a/tests/ui/stats/macro-stats.stderr b/tests/ui/stats/macro-stats.stderr
new file mode 100644
index 00000000000..f87e34622b9
--- /dev/null
+++ b/tests/ui/stats/macro-stats.stderr
@@ -0,0 +1,26 @@
+macro-stats ===================================================================================
+macro-stats MACRO EXPANSION STATS: macro_stats
+macro-stats Macro Name                         Uses      Lines  Avg Lines      Bytes  Avg Bytes
+macro-stats -----------------------------------------------------------------------------------
+macro-stats #[derive(Clone)]                      8         56        7.0      1_660      207.5
+macro-stats #[derive(PartialOrd)]                 1         16       16.0        654      654.0
+macro-stats #[derive(Hash)]                       2         15        7.5        547      273.5
+macro-stats #[derive(Ord)]                        1         14       14.0        489      489.0
+macro-stats q!                                    1         24       24.0        435      435.0
+macro-stats #[derive(Default)]                    2         14        7.0        367      183.5
+macro-stats #[derive(Eq)]                         1         10       10.0        312      312.0
+macro-stats #[derive(Debug)]                      1          7        7.0        261      261.0
+macro-stats #[derive(PartialEq)]                  1          8        8.0        247      247.0
+macro-stats #[derive(Copy)]                       1          1        1.0         46       46.0
+macro-stats p!                                    1          2        2.0         28       28.0
+macro-stats trait_impl_tys!                       1          1        1.0         11       11.0
+macro-stats foreign_item!                         1          0        0.0          6        6.0
+macro-stats impl_const!                           1          0        0.0          4        4.0
+macro-stats trait_tys!                            1          1        1.0          3        3.0
+macro-stats u32!                                  1          0        0.0         -3       -3.0
+macro-stats none!                                 1          0        0.0         -3       -3.0
+macro-stats n99!                                  2          0        0.0         -8       -4.0
+macro-stats this_is_a_really_really_long_macro_name!
+macro-stats                                       1          0        0.0        -30      -30.0
+macro-stats #[test]                               1         -6       -6.0       -158     -158.0
+macro-stats ===================================================================================
diff --git a/tests/ui/str/str-array-assignment.stderr b/tests/ui/str/str-array-assignment.stderr
index 515cb9e12f8..76eb7833225 100644
--- a/tests/ui/str/str-array-assignment.stderr
+++ b/tests/ui/str/str-array-assignment.stderr
@@ -25,7 +25,6 @@ LL |   let v = s[..2];
    |
    = help: the trait `Sized` is not implemented for `str`
    = note: all local variables must have a statically known size
-   = help: unsized locals are gated as an unstable feature
 help: consider borrowing here
    |
 LL |   let v = &s[..2];
diff --git a/tests/ui/structs-enums/enum-alignment.rs b/tests/ui/structs-enums/enum-alignment.rs
index bb779b1e0cd..95f14e521b5 100644
--- a/tests/ui/structs-enums/enum-alignment.rs
+++ b/tests/ui/structs-enums/enum-alignment.rs
@@ -11,7 +11,7 @@ fn addr_of<T>(ptr: &T) -> usize {
 fn is_aligned<T>(ptr: &T) -> bool {
     unsafe {
         let addr: usize = mem::transmute(ptr);
-        (addr % mem::min_align_of::<T>()) == 0
+        (addr % mem::align_of::<T>()) == 0
     }
 }
 
diff --git a/tests/ui/structs-enums/rec-align-u32.rs b/tests/ui/structs-enums/rec-align-u32.rs
index 44879189739..b18cd11198e 100644
--- a/tests/ui/structs-enums/rec-align-u32.rs
+++ b/tests/ui/structs-enums/rec-align-u32.rs
@@ -34,12 +34,12 @@ pub fn main() {
         // Send it through the shape code
         let y = format!("{:?}", x);
 
-        println!("align inner = {:?}", intrinsics::min_align_of::<Inner>());
+        println!("align inner = {:?}", intrinsics::align_of::<Inner>());
         println!("size outer = {:?}", mem::size_of::<Outer>());
         println!("y = {:?}", y);
 
         // per clang/gcc the alignment of `inner` is 4 on x86.
-        assert_eq!(intrinsics::min_align_of::<Inner>(), m::align());
+        assert_eq!(intrinsics::align_of::<Inner>(), m::align());
 
         // per clang/gcc the size of `outer` should be 12
         // because `inner`s alignment was 4.
diff --git a/tests/ui/structs-enums/rec-align-u64.rs b/tests/ui/structs-enums/rec-align-u64.rs
index 0f7bb6b3505..df219892d73 100644
--- a/tests/ui/structs-enums/rec-align-u64.rs
+++ b/tests/ui/structs-enums/rec-align-u64.rs
@@ -84,12 +84,12 @@ pub fn main() {
 
         let y = format!("{:?}", x);
 
-        println!("align inner = {:?}", intrinsics::min_align_of::<Inner>());
+        println!("align inner = {:?}", intrinsics::align_of::<Inner>());
         println!("size outer = {:?}", mem::size_of::<Outer>());
         println!("y = {:?}", y);
 
         // per clang/gcc the alignment of `Inner` is 4 on x86.
-        assert_eq!(intrinsics::min_align_of::<Inner>(), m::m::align());
+        assert_eq!(intrinsics::align_of::<Inner>(), m::m::align());
 
         // per clang/gcc the size of `Outer` should be 12
         // because `Inner`s alignment was 4.
diff --git a/tests/ui/structs-enums/tag-align-dyn-u64.rs b/tests/ui/structs-enums/tag-align-dyn-u64.rs
index 5e7d918b4fb..5a4f0878c53 100644
--- a/tests/ui/structs-enums/tag-align-dyn-u64.rs
+++ b/tests/ui/structs-enums/tag-align-dyn-u64.rs
@@ -19,7 +19,7 @@ fn mk_rec() -> Rec {
 
 fn is_u64_aligned(u: &Tag<u64>) -> bool {
     let p: usize = unsafe { mem::transmute(u) };
-    let u64_align = std::mem::min_align_of::<u64>();
+    let u64_align = std::mem::align_of::<u64>();
     return (p & (u64_align - 1)) == 0;
 }
 
diff --git a/tests/ui/structs-enums/tag-align-dyn-variants.rs b/tests/ui/structs-enums/tag-align-dyn-variants.rs
index c0574f3354e..019f1a82895 100644
--- a/tests/ui/structs-enums/tag-align-dyn-variants.rs
+++ b/tests/ui/structs-enums/tag-align-dyn-variants.rs
@@ -34,7 +34,7 @@ fn variant_data_is_aligned<A,B>(amnt: usize, u: &Tag<A,B>) -> bool {
 }
 
 pub fn main() {
-    let u64_align = std::mem::min_align_of::<u64>();
+    let u64_align = std::mem::align_of::<u64>();
     let x = mk_rec(22u64, 23u64);
     assert!(is_aligned(u64_align, &x.tA));
     assert!(variant_data_is_aligned(u64_align, &x.tA));
diff --git a/tests/ui/structs-enums/tag-align-u64.rs b/tests/ui/structs-enums/tag-align-u64.rs
index 8ab9f242822..e274cc0e157 100644
--- a/tests/ui/structs-enums/tag-align-u64.rs
+++ b/tests/ui/structs-enums/tag-align-u64.rs
@@ -19,7 +19,7 @@ fn mk_rec() -> Rec {
 
 fn is_u64_aligned(u: &Tag) -> bool {
     let p: usize = unsafe { mem::transmute(u) };
-    let u64_align = std::mem::min_align_of::<u64>();
+    let u64_align = std::mem::align_of::<u64>();
     return (p & (u64_align - 1)) == 0;
 }
 
diff --git a/tests/ui/target-feature/retpoline-target-feature-flag.by_feature.stderr b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature.stderr
new file mode 100644
index 00000000000..e2b6078b7a8
--- /dev/null
+++ b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature.stderr
@@ -0,0 +1,7 @@
+warning: target feature `retpoline-external-thunk` cannot be enabled with `-Ctarget-feature`: use `x86-retpoline` target modifier flag instead
+   |
+   = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116344 <https://github.com/rust-lang/rust/issues/116344>
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/target-feature/retpoline-target-feature-flag.by_feature1.stderr b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature1.stderr
new file mode 100644
index 00000000000..2a0f5f01aef
--- /dev/null
+++ b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature1.stderr
@@ -0,0 +1,7 @@
+warning: target feature `retpoline-external-thunk` cannot be enabled with `-Ctarget-feature`: use `retpoline-external-thunk` target modifier flag instead
+   |
+   = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116344 <https://github.com/rust-lang/rust/issues/116344>
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/target-feature/retpoline-target-feature-flag.by_feature2.stderr b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature2.stderr
new file mode 100644
index 00000000000..f7b6cb16447
--- /dev/null
+++ b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature2.stderr
@@ -0,0 +1,7 @@
+warning: target feature `retpoline-indirect-branches` cannot be enabled with `-Ctarget-feature`: use `retpoline` target modifier flag instead
+   |
+   = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116344 <https://github.com/rust-lang/rust/issues/116344>
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/target-feature/retpoline-target-feature-flag.by_feature3.stderr b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature3.stderr
new file mode 100644
index 00000000000..4f2cd1d1a52
--- /dev/null
+++ b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature3.stderr
@@ -0,0 +1,7 @@
+warning: target feature `retpoline-indirect-calls` cannot be enabled with `-Ctarget-feature`: use `retpoline` target modifier flag instead
+   |
+   = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116344 <https://github.com/rust-lang/rust/issues/116344>
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/target-feature/retpoline-target-feature-flag.rs b/tests/ui/target-feature/retpoline-target-feature-flag.rs
new file mode 100644
index 00000000000..de3c44c3ed0
--- /dev/null
+++ b/tests/ui/target-feature/retpoline-target-feature-flag.rs
@@ -0,0 +1,21 @@
+//@ add-core-stubs
+//@ revisions: by_flag by_feature1 by_feature2 by_feature3
+//@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=lib
+//@ needs-llvm-components: x86
+//@ [by_flag]compile-flags: -Zretpoline
+
+//@ [by_feature1]compile-flags: -Ctarget-feature=+retpoline-external-thunk
+//@ [by_feature2]compile-flags: -Ctarget-feature=+retpoline-indirect-branches
+//@ [by_feature3]compile-flags: -Ctarget-feature=+retpoline-indirect-calls
+//@ [by_flag]build-pass
+// For now this is just a warning.
+//@ [by_feature1]build-pass
+//@ [by_feature2]build-pass
+//@ [by_feature3]build-pass
+#![feature(no_core)]
+#![no_core]
+extern crate minicore;
+
+//[by_feature1]~? WARN target feature `retpoline-external-thunk` cannot be enabled with `-Ctarget-feature`: use `retpoline-external-thunk` target modifier flag instead
+//[by_feature2]~? WARN target feature `retpoline-indirect-branches` cannot be enabled with `-Ctarget-feature`: use `retpoline` target modifier flag instead
+//[by_feature3]~? WARN target feature `retpoline-indirect-calls` cannot be enabled with `-Ctarget-feature`: use `retpoline` target modifier flag instead
diff --git a/tests/ui/tool-attributes/diagnostic_item.rs b/tests/ui/tool-attributes/diagnostic_item.rs
index 26a52ce60cf..806b140feba 100644
--- a/tests/ui/tool-attributes/diagnostic_item.rs
+++ b/tests/ui/tool-attributes/diagnostic_item.rs
@@ -1,3 +1,5 @@
-#[rustc_diagnostic_item = "foomp"] //~ ERROR compiler internal support for linting
+#[rustc_diagnostic_item = "foomp"]
+//~^ ERROR use of an internal attribute [E0658]
+//~| NOTE the `#[rustc_diagnostic_item]` attribute allows the compiler to reference types from the standard library for diagnostic purposes
 struct Foomp;
 fn main() {}
diff --git a/tests/ui/tool-attributes/diagnostic_item.stderr b/tests/ui/tool-attributes/diagnostic_item.stderr
index c6ae5a38594..d37044281a1 100644
--- a/tests/ui/tool-attributes/diagnostic_item.stderr
+++ b/tests/ui/tool-attributes/diagnostic_item.stderr
@@ -1,11 +1,11 @@
-error[E0658]: diagnostic items compiler internal support for linting
+error[E0658]: use of an internal attribute
   --> $DIR/diagnostic_item.rs:1:1
    |
 LL | #[rustc_diagnostic_item = "foomp"]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+   = note: the `#[rustc_diagnostic_item]` attribute allows the compiler to reference types from the standard library for diagnostic purposes
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/traits/associated_type_bound/hrtb-associated.rs b/tests/ui/traits/associated_type_bound/hrtb-associated.rs
index 59e5a09c0cb..bf9f59c05e3 100644
--- a/tests/ui/traits/associated_type_bound/hrtb-associated.rs
+++ b/tests/ui/traits/associated_type_bound/hrtb-associated.rs
@@ -10,7 +10,7 @@ pub trait Provides<'a> {
 pub trait Selector: for<'a> Provides<'a> {
     type Namespace: PartialEq + for<'a> PartialEq<<Self as Provides<'a>>::Item>;
 
-    fn get_namespace(&self) -> <Self as Provides>::Item;
+    fn get_namespace(&self) -> <Self as Provides<'_>>::Item;
 }
 
 pub struct MySelector;
diff --git a/tests/ui/traits/default-method/rustc_must_implement_one_of_gated.rs b/tests/ui/traits/default-method/rustc_must_implement_one_of_gated.rs
index 27e70556b7a..47cc9f5f960 100644
--- a/tests/ui/traits/default-method/rustc_must_implement_one_of_gated.rs
+++ b/tests/ui/traits/default-method/rustc_must_implement_one_of_gated.rs
@@ -1,5 +1,7 @@
 #[rustc_must_implement_one_of(eq, neq)]
-//~^ ERROR the `#[rustc_must_implement_one_of]` attribute is used to change minimal complete definition of a trait, it's currently in experimental form and should be changed before being exposed outside of the std
+//~^ ERROR use of an internal attribute [E0658]
+//~| NOTE the `#[rustc_must_implement_one_of]` attribute is an internal implementation detail that will never be stable
+//~| NOTE the `#[rustc_must_implement_one_of]` attribute is used to change minimal complete definition of a trait. Its syntax and semantics are highly experimental and will be subject to change before stabilization
 trait Equal {
     fn eq(&self, other: &Self) -> bool {
         !self.neq(other)
diff --git a/tests/ui/traits/default-method/rustc_must_implement_one_of_gated.stderr b/tests/ui/traits/default-method/rustc_must_implement_one_of_gated.stderr
index 162c3d36cb1..4d44ceff98a 100644
--- a/tests/ui/traits/default-method/rustc_must_implement_one_of_gated.stderr
+++ b/tests/ui/traits/default-method/rustc_must_implement_one_of_gated.stderr
@@ -1,11 +1,12 @@
-error[E0658]: the `#[rustc_must_implement_one_of]` attribute is used to change minimal complete definition of a trait, it's currently in experimental form and should be changed before being exposed outside of the std
+error[E0658]: use of an internal attribute
   --> $DIR/rustc_must_implement_one_of_gated.rs:1:1
    |
 LL | #[rustc_must_implement_one_of(eq, neq)]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+   = note: the `#[rustc_must_implement_one_of]` attribute is an internal implementation detail that will never be stable
+   = note: the `#[rustc_must_implement_one_of]` attribute is used to change minimal complete definition of a trait. Its syntax and semantics are highly experimental and will be subject to change before stabilization
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.rs b/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.rs
index ea6df938704..14f0cbcc8b7 100644
--- a/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.rs
+++ b/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.rs
@@ -13,9 +13,9 @@ fn main() {
 }
 
 fn weird0() -> impl Sized + !Sized {}
-//~^ ERROR type mismatch resolving
+//~^ ERROR the trait bound `(): !Sized` is not satisfied
 fn weird1() -> impl !Sized + Sized {}
-//~^ ERROR type mismatch resolving
+//~^ ERROR the trait bound `(): !Sized` is not satisfied
 fn weird2() -> impl !Sized {}
-//~^ ERROR type mismatch resolving
+//~^ ERROR the trait bound `(): !Sized` is not satisfied
 //~| ERROR the size for values of type
diff --git a/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.stderr b/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.stderr
index 41d9e74f807..3ba3d8d8bd5 100644
--- a/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.stderr
+++ b/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.stderr
@@ -7,23 +7,23 @@ LL | fn weird2() -> impl !Sized {}
    = help: the trait `Sized` is not implemented for `impl !Sized`
    = note: the return type of a function must have a statically known size
 
-error[E0271]: type mismatch resolving `impl !Sized + Sized == ()`
+error[E0277]: the trait bound `(): !Sized` is not satisfied
   --> $DIR/opaque-type-unsatisfied-bound.rs:15:16
    |
 LL | fn weird0() -> impl Sized + !Sized {}
-   |                ^^^^^^^^^^^^^^^^^^^ types differ
+   |                ^^^^^^^^^^^^^^^^^^^ the trait bound `(): !Sized` is not satisfied
 
-error[E0271]: type mismatch resolving `impl !Sized + Sized == ()`
+error[E0277]: the trait bound `(): !Sized` is not satisfied
   --> $DIR/opaque-type-unsatisfied-bound.rs:17:16
    |
 LL | fn weird1() -> impl !Sized + Sized {}
-   |                ^^^^^^^^^^^^^^^^^^^ types differ
+   |                ^^^^^^^^^^^^^^^^^^^ the trait bound `(): !Sized` is not satisfied
 
-error[E0271]: type mismatch resolving `impl !Sized == ()`
+error[E0277]: the trait bound `(): !Sized` is not satisfied
   --> $DIR/opaque-type-unsatisfied-bound.rs:19:16
    |
 LL | fn weird2() -> impl !Sized {}
-   |                ^^^^^^^^^^^ types differ
+   |                ^^^^^^^^^^^ the trait bound `(): !Sized` is not satisfied
 
 error[E0277]: the trait bound `impl !Trait: Trait` is not satisfied
   --> $DIR/opaque-type-unsatisfied-bound.rs:12:13
@@ -41,5 +41,4 @@ LL | fn consume(_: impl Trait) {}
 
 error: aborting due to 5 previous errors
 
-Some errors have detailed explanations: E0271, E0277.
-For more information about an error, try `rustc --explain E0271`.
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-fn-bound.rs b/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-fn-bound.rs
index ce42bce0ad4..39422914afc 100644
--- a/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-fn-bound.rs
+++ b/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-fn-bound.rs
@@ -3,6 +3,6 @@
 #![feature(negative_bounds, unboxed_closures)]
 
 fn produce() -> impl !Fn<(u32,)> {}
-//~^ ERROR type mismatch resolving
+//~^ ERROR the trait bound `(): !Fn(u32)` is not satisfied
 
 fn main() {}
diff --git a/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-fn-bound.stderr b/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-fn-bound.stderr
index e1b84e0df7a..760e5aa62f2 100644
--- a/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-fn-bound.stderr
+++ b/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-fn-bound.stderr
@@ -1,9 +1,9 @@
-error[E0271]: type mismatch resolving `impl !Fn<(u32,)> == ()`
+error[E0277]: the trait bound `(): !Fn(u32)` is not satisfied
   --> $DIR/opaque-type-unsatisfied-fn-bound.rs:5:17
    |
 LL | fn produce() -> impl !Fn<(u32,)> {}
-   |                 ^^^^^^^^^^^^^^^^ types differ
+   |                 ^^^^^^^^^^^^^^^^ the trait bound `(): !Fn(u32)` is not satisfied
 
 error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0271`.
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.is_send.stderr b/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.is_send.stderr
index 736c8c10da9..6d2bbd8b08b 100644
--- a/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.is_send.stderr
+++ b/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.is_send.stderr
@@ -1,9 +1,16 @@
-error[E0284]: type annotations needed: cannot satisfy `Foo == _`
+error[E0283]: type annotations needed: cannot satisfy `Foo: Send`
   --> $DIR/dont-type_of-tait-in-defining-scope.rs:16:18
    |
 LL |     needs_send::<Foo>();
-   |                  ^^^ cannot satisfy `Foo == _`
+   |                  ^^^
+   |
+   = note: cannot satisfy `Foo: Send`
+note: required by a bound in `needs_send`
+  --> $DIR/dont-type_of-tait-in-defining-scope.rs:12:18
+   |
+LL | fn needs_send<T: Send>() {}
+   |                  ^^^^ required by this bound in `needs_send`
 
 error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0284`.
+For more information about this error, try `rustc --explain E0283`.
diff --git a/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.not_send.stderr b/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.not_send.stderr
index 736c8c10da9..6d2bbd8b08b 100644
--- a/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.not_send.stderr
+++ b/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.not_send.stderr
@@ -1,9 +1,16 @@
-error[E0284]: type annotations needed: cannot satisfy `Foo == _`
+error[E0283]: type annotations needed: cannot satisfy `Foo: Send`
   --> $DIR/dont-type_of-tait-in-defining-scope.rs:16:18
    |
 LL |     needs_send::<Foo>();
-   |                  ^^^ cannot satisfy `Foo == _`
+   |                  ^^^
+   |
+   = note: cannot satisfy `Foo: Send`
+note: required by a bound in `needs_send`
+  --> $DIR/dont-type_of-tait-in-defining-scope.rs:12:18
+   |
+LL | fn needs_send<T: Send>() {}
+   |                  ^^^^ required by this bound in `needs_send`
 
 error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0284`.
+For more information about this error, try `rustc --explain E0283`.
diff --git a/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.rs b/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.rs
index 2a08a3b2b94..fddf892e1ef 100644
--- a/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.rs
+++ b/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.rs
@@ -14,7 +14,7 @@ fn needs_send<T: Send>() {}
 #[define_opaque(Foo)]
 fn test(_: Foo) {
     needs_send::<Foo>();
-    //~^ ERROR type annotations needed: cannot satisfy `Foo == _`
+    //~^ ERROR type annotations needed
 }
 
 #[define_opaque(Foo)]
diff --git a/tests/ui/traits/object/no-incomplete-inference.current.stderr b/tests/ui/traits/object/no-incomplete-inference.current.stderr
new file mode 100644
index 00000000000..3c6b811a49e
--- /dev/null
+++ b/tests/ui/traits/object/no-incomplete-inference.current.stderr
@@ -0,0 +1,16 @@
+error[E0283]: type annotations needed
+  --> $DIR/no-incomplete-inference.rs:16:5
+   |
+LL |     impls_equals::<dyn Equals<u32>, _>();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `U` declared on the function `impls_equals`
+   |
+   = note: cannot satisfy `dyn Equals<u32>: Equals<_>`
+note: required by a bound in `impls_equals`
+  --> $DIR/no-incomplete-inference.rs:13:20
+   |
+LL | fn impls_equals<T: Equals<U> + ?Sized, U: ?Sized>() {}
+   |                    ^^^^^^^^^ required by this bound in `impls_equals`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0283`.
diff --git a/tests/ui/traits/object/no-incomplete-inference.next.stderr b/tests/ui/traits/object/no-incomplete-inference.next.stderr
new file mode 100644
index 00000000000..3c6b811a49e
--- /dev/null
+++ b/tests/ui/traits/object/no-incomplete-inference.next.stderr
@@ -0,0 +1,16 @@
+error[E0283]: type annotations needed
+  --> $DIR/no-incomplete-inference.rs:16:5
+   |
+LL |     impls_equals::<dyn Equals<u32>, _>();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `U` declared on the function `impls_equals`
+   |
+   = note: cannot satisfy `dyn Equals<u32>: Equals<_>`
+note: required by a bound in `impls_equals`
+  --> $DIR/no-incomplete-inference.rs:13:20
+   |
+LL | fn impls_equals<T: Equals<U> + ?Sized, U: ?Sized>() {}
+   |                    ^^^^^^^^^ required by this bound in `impls_equals`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0283`.
diff --git a/tests/ui/traits/object/no-incomplete-inference.rs b/tests/ui/traits/object/no-incomplete-inference.rs
new file mode 100644
index 00000000000..9ed8dd6c0b1
--- /dev/null
+++ b/tests/ui/traits/object/no-incomplete-inference.rs
@@ -0,0 +1,18 @@
+//@ revisions: current next
+//@ ignore-compare-mode-next-solver (explicit revisions)
+//@[next] compile-flags: -Znext-solver
+
+
+// Make sure that having an applicable user-written
+// and builtin impl is ambiguous.
+
+trait Equals<T: ?Sized> {}
+
+impl<T: ?Sized> Equals<T> for T {}
+
+fn impls_equals<T: Equals<U> + ?Sized, U: ?Sized>() {}
+
+fn main() {
+    impls_equals::<dyn Equals<u32>, _>();
+    //~^ ERROR type annotations needed
+}
diff --git a/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.next.stderr b/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.next.stderr
index 9e83de5375f..f7e0245bc83 100644
--- a/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.next.stderr
+++ b/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.next.stderr
@@ -7,7 +7,7 @@ LL | impl<In, Out> Trait<Bar, In> for Out {
 LL | impl<In, Out> Trait<(), In> for Out {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation
 
-error[E0284]: type annotations needed: cannot satisfy `Bar == _`
+error[E0282]: type annotations needed
   --> $DIR/issue-84660-unsoundness.rs:24:37
    |
 LL |       fn convert(_i: In) -> Self::Out {
@@ -16,9 +16,9 @@ LL | |
 LL | |
 LL | |         unreachable!();
 LL | |     }
-   | |_____^ cannot satisfy `Bar == _`
+   | |_____^ cannot infer type
 
 error: aborting due to 2 previous errors
 
-Some errors have detailed explanations: E0119, E0284.
+Some errors have detailed explanations: E0119, E0282.
 For more information about an error, try `rustc --explain E0119`.
diff --git a/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.rs b/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.rs
index 7a540d2a574..a385138b295 100644
--- a/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.rs
+++ b/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.rs
@@ -22,7 +22,7 @@ impl<In, Out> Trait<Bar, In> for Out {
     type Out = Out;
     #[define_opaque(Bar)]
     fn convert(_i: In) -> Self::Out {
-        //[next]~^  ERROR: type annotations needed: cannot satisfy `Bar == _`
+        //[next]~^  ERROR: type annotations needed
         //[current]~^^ ERROR: item does not constrain `Bar::{opaque#0}`
         unreachable!();
     }
diff --git a/tests/ui/type-alias-impl-trait/nested-tait-inference2.next.stderr b/tests/ui/type-alias-impl-trait/nested-tait-inference2.next.stderr
index b733739e4c8..d2127976e7d 100644
--- a/tests/ui/type-alias-impl-trait/nested-tait-inference2.next.stderr
+++ b/tests/ui/type-alias-impl-trait/nested-tait-inference2.next.stderr
@@ -1,9 +1,9 @@
-error[E0284]: type annotations needed: cannot satisfy `impl Foo<FooX> == ()`
+error[E0282]: type annotations needed
   --> $DIR/nested-tait-inference2.rs:20:5
    |
 LL |     ()
-   |     ^^ cannot satisfy `impl Foo<FooX> == ()`
+   |     ^^ cannot infer type
 
 error: aborting due to 1 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/tests/ui/type-alias-impl-trait/nested-tait-inference2.rs b/tests/ui/type-alias-impl-trait/nested-tait-inference2.rs
index 4aeecb9140c..606336178e5 100644
--- a/tests/ui/type-alias-impl-trait/nested-tait-inference2.rs
+++ b/tests/ui/type-alias-impl-trait/nested-tait-inference2.rs
@@ -18,7 +18,7 @@ impl Foo<u32> for () {}
 fn foo() -> impl Foo<FooX> {
     //[current]~^ ERROR: the trait bound `(): Foo<FooX>` is not satisfied
     ()
-    //[next]~^ ERROR: cannot satisfy `impl Foo<FooX> == ()`
+    //[next]~^ ERROR: type annotations needed
 }
 
 fn main() {}
diff --git a/tests/ui/unpretty/exhaustive.expanded.stdout b/tests/ui/unpretty/exhaustive.expanded.stdout
index 9712ba58e62..cd1a5d0af08 100644
--- a/tests/ui/unpretty/exhaustive.expanded.stdout
+++ b/tests/ui/unpretty/exhaustive.expanded.stdout
@@ -190,7 +190,7 @@ mod expressions {
         (static async || value);
         (static async move || value);
         || -> u8 { value };
-        1 + (|| {});
+        1 + || {};
     }
 
     /// ExprKind::Block
diff --git a/tests/ui/unsized-locals/align.rs b/tests/ui/unsized-locals/align.rs
index a3820e3e6dc..fdb83a848bc 100644
--- a/tests/ui/unsized-locals/align.rs
+++ b/tests/ui/unsized-locals/align.rs
@@ -1,8 +1,5 @@
 // Test that unsized locals uphold alignment requirements.
 // Regression test for #71416.
-//@ run-pass
-#![feature(unsized_locals)]
-#![allow(incomplete_features)]
 use std::any::Any;
 
 #[repr(align(256))]
@@ -23,7 +20,7 @@ fn mk() -> Box<dyn Any> {
 }
 
 fn main() {
-    let x = *mk();
+    let x = *mk(); //~ERROR the size for values of type `dyn Any` cannot be known at compilation time
     let dwncst = x.downcast_ref::<A>().unwrap();
     let addr = dwncst.f();
     assert_eq!(addr as usize % 256, 0);
diff --git a/tests/ui/unsized-locals/align.stderr b/tests/ui/unsized-locals/align.stderr
new file mode 100644
index 00000000000..7a48ad1107e
--- /dev/null
+++ b/tests/ui/unsized-locals/align.stderr
@@ -0,0 +1,17 @@
+error[E0277]: the size for values of type `dyn Any` cannot be known at compilation time
+  --> $DIR/align.rs:23:9
+   |
+LL |     let x = *mk();
+   |         ^ doesn't have a size known at compile-time
+   |
+   = help: the trait `Sized` is not implemented for `dyn Any`
+   = note: all local variables must have a statically known size
+help: references are always `Sized`, even if they point to unsized data; consider not dereferencing the expression
+   |
+LL -     let x = *mk();
+LL +     let x = mk();
+   |
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/unsized-locals/autoderef.rs b/tests/ui/unsized-locals/autoderef.rs
index 31b58ba4002..24f8a4c5d08 100644
--- a/tests/ui/unsized-locals/autoderef.rs
+++ b/tests/ui/unsized-locals/autoderef.rs
@@ -1,7 +1,4 @@
-//@ run-pass
-
-#![allow(incomplete_features)]
-#![feature(unsized_locals, unsized_fn_params)]
+#![feature(unsized_fn_params)]
 
 pub trait Foo {
     fn foo(self) -> String;
@@ -26,7 +23,7 @@ impl Foo for dyn FnMut() -> String {
 }
 
 fn main() {
-    let x = *(Box::new(['h', 'e', 'l', 'l', 'o']) as Box<[char]>);
+    let x = *(Box::new(['h', 'e', 'l', 'l', 'o']) as Box<[char]>); //~ERROR the size for values of type `[char]` cannot be known at compilation time
     assert_eq!(&x.foo() as &str, "hello");
 
     let x = Box::new(['h', 'e', 'l', 'l', 'o']) as Box<[char]>;
@@ -35,13 +32,13 @@ fn main() {
     let x = "hello".to_owned().into_boxed_str();
     assert_eq!(&x.foo() as &str, "hello");
 
-    let x = *("hello".to_owned().into_boxed_str());
+    let x = *("hello".to_owned().into_boxed_str()); //~ERROR the size for values of type `str` cannot be known at compilation time
     assert_eq!(&x.foo() as &str, "hello");
 
     let x = "hello".to_owned().into_boxed_str();
     assert_eq!(&x.foo() as &str, "hello");
 
-    let x = *(Box::new(|| "hello".to_owned()) as Box<dyn FnMut() -> String>);
+    let x = *(Box::new(|| "hello".to_owned()) as Box<dyn FnMut() -> String>); //~ERROR the size for values of type `dyn FnMut() -> String` cannot be known at compilation time
     assert_eq!(&x.foo() as &str, "hello");
 
     let x = Box::new(|| "hello".to_owned()) as Box<dyn FnMut() -> String>;
diff --git a/tests/ui/unsized-locals/autoderef.stderr b/tests/ui/unsized-locals/autoderef.stderr
new file mode 100644
index 00000000000..785badce199
--- /dev/null
+++ b/tests/ui/unsized-locals/autoderef.stderr
@@ -0,0 +1,45 @@
+error[E0277]: the size for values of type `[char]` cannot be known at compilation time
+  --> $DIR/autoderef.rs:26:9
+   |
+LL |     let x = *(Box::new(['h', 'e', 'l', 'l', 'o']) as Box<[char]>);
+   |         ^ doesn't have a size known at compile-time
+   |
+   = help: the trait `Sized` is not implemented for `[char]`
+   = note: all local variables must have a statically known size
+help: references are always `Sized`, even if they point to unsized data; consider not dereferencing the expression
+   |
+LL -     let x = *(Box::new(['h', 'e', 'l', 'l', 'o']) as Box<[char]>);
+LL +     let x = (Box::new(['h', 'e', 'l', 'l', 'o']) as Box<[char]>);
+   |
+
+error[E0277]: the size for values of type `str` cannot be known at compilation time
+  --> $DIR/autoderef.rs:35:9
+   |
+LL |     let x = *("hello".to_owned().into_boxed_str());
+   |         ^ doesn't have a size known at compile-time
+   |
+   = help: the trait `Sized` is not implemented for `str`
+   = note: all local variables must have a statically known size
+help: references are always `Sized`, even if they point to unsized data; consider not dereferencing the expression
+   |
+LL -     let x = *("hello".to_owned().into_boxed_str());
+LL +     let x = ("hello".to_owned().into_boxed_str());
+   |
+
+error[E0277]: the size for values of type `dyn FnMut() -> String` cannot be known at compilation time
+  --> $DIR/autoderef.rs:41:9
+   |
+LL |     let x = *(Box::new(|| "hello".to_owned()) as Box<dyn FnMut() -> String>);
+   |         ^ doesn't have a size known at compile-time
+   |
+   = help: the trait `Sized` is not implemented for `dyn FnMut() -> String`
+   = note: all local variables must have a statically known size
+help: references are always `Sized`, even if they point to unsized data; consider not dereferencing the expression
+   |
+LL -     let x = *(Box::new(|| "hello".to_owned()) as Box<dyn FnMut() -> String>);
+LL +     let x = (Box::new(|| "hello".to_owned()) as Box<dyn FnMut() -> String>);
+   |
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/unsized-locals/auxiliary/ufuncs.rs b/tests/ui/unsized-locals/auxiliary/ufuncs.rs
index 5954abf3a1f..78534182e8d 100644
--- a/tests/ui/unsized-locals/auxiliary/ufuncs.rs
+++ b/tests/ui/unsized-locals/auxiliary/ufuncs.rs
@@ -1,3 +1,3 @@
-#![feature(unsized_locals, unsized_fn_params)]
+#![feature(unsized_fn_params)]
 
 pub fn udrop<T: ?Sized>(_x: T) {}
diff --git a/tests/ui/unsized-locals/borrow-after-move.rs b/tests/ui/unsized-locals/borrow-after-move.rs
index ad73b720fa3..6c510911f89 100644
--- a/tests/ui/unsized-locals/borrow-after-move.rs
+++ b/tests/ui/unsized-locals/borrow-after-move.rs
@@ -1,5 +1,4 @@
-#![feature(unsized_locals, unsized_fn_params)]
-//~^ WARN the feature `unsized_locals` is incomplete
+#![feature(unsized_fn_params)]
 
 pub trait Foo {
     fn foo(self) -> String;
@@ -16,28 +15,23 @@ fn drop_unsized<T: ?Sized>(_: T) {}
 fn main() {
     {
         let x = "hello".to_owned().into_boxed_str();
-        let y = *x;
+        let y = *x; //~ERROR the size for values of type `str` cannot be known at compilation time [E0277]
         drop_unsized(y);
         println!("{}", &x);
-        //~^ERROR borrow of moved value
         println!("{}", &y);
-        //~^ERROR borrow of moved value
     }
 
     {
         let x = "hello".to_owned().into_boxed_str();
-        let y = *x;
+        let y = *x; //~ERROR the size for values of type `str` cannot be known at compilation time [E0277]
         y.foo();
         println!("{}", &x);
-        //~^ERROR borrow of moved value
         println!("{}", &y);
-        //~^ERROR borrow of moved value
     }
 
     {
         let x = "hello".to_owned().into_boxed_str();
         x.foo();
         println!("{}", &x);
-        //~^ERROR borrow of moved value
     }
 }
diff --git a/tests/ui/unsized-locals/borrow-after-move.stderr b/tests/ui/unsized-locals/borrow-after-move.stderr
index 9e3c345dd80..1a4ce39fa7b 100644
--- a/tests/ui/unsized-locals/borrow-after-move.stderr
+++ b/tests/ui/unsized-locals/borrow-after-move.stderr
@@ -1,85 +1,31 @@
-warning: the feature `unsized_locals` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/borrow-after-move.rs:1:12
-   |
-LL | #![feature(unsized_locals, unsized_fn_params)]
-   |            ^^^^^^^^^^^^^^
-   |
-   = note: see issue #48055 <https://github.com/rust-lang/rust/issues/48055> for more information
-   = note: `#[warn(incomplete_features)]` on by default
-
-error[E0382]: borrow of moved value: `x`
-  --> $DIR/borrow-after-move.rs:21:24
+error[E0277]: the size for values of type `str` cannot be known at compilation time
+  --> $DIR/borrow-after-move.rs:18:13
    |
 LL |         let y = *x;
-   |                 -- value moved here
-LL |         drop_unsized(y);
-LL |         println!("{}", &x);
-   |                        ^^ value borrowed here after move
+   |             ^ doesn't have a size known at compile-time
    |
-   = note: move occurs because `*x` has type `str`, which does not implement the `Copy` trait
-
-error[E0382]: borrow of moved value: `y`
-  --> $DIR/borrow-after-move.rs:23:24
+   = help: the trait `Sized` is not implemented for `str`
+   = note: all local variables must have a statically known size
+help: references are always `Sized`, even if they point to unsized data; consider not dereferencing the expression
    |
-LL |         let y = *x;
-   |             - move occurs because `y` has type `str`, which does not implement the `Copy` trait
-LL |         drop_unsized(y);
-   |                      - value moved here
-...
-LL |         println!("{}", &y);
-   |                        ^^ value borrowed here after move
-   |
-note: consider changing this parameter type in function `drop_unsized` to borrow instead if owning the value isn't necessary
-  --> $DIR/borrow-after-move.rs:14:31
+LL -         let y = *x;
+LL +         let y = x;
    |
-LL | fn drop_unsized<T: ?Sized>(_: T) {}
-   |    ------------               ^ this parameter takes ownership of the value
-   |    |
-   |    in this function
 
-error[E0382]: borrow of moved value: `x`
-  --> $DIR/borrow-after-move.rs:31:24
+error[E0277]: the size for values of type `str` cannot be known at compilation time
+  --> $DIR/borrow-after-move.rs:26:13
    |
 LL |         let y = *x;
-   |                 -- value moved here
-LL |         y.foo();
-LL |         println!("{}", &x);
-   |                        ^^ value borrowed here after move
-   |
-   = note: move occurs because `*x` has type `str`, which does not implement the `Copy` trait
-
-error[E0382]: borrow of moved value: `y`
-  --> $DIR/borrow-after-move.rs:33:24
-   |
-LL |         let y = *x;
-   |             - move occurs because `y` has type `str`, which does not implement the `Copy` trait
-LL |         y.foo();
-   |           ----- `y` moved due to this method call
-...
-LL |         println!("{}", &y);
-   |                        ^^ value borrowed here after move
-   |
-note: `Foo::foo` takes ownership of the receiver `self`, which moves `y`
-  --> $DIR/borrow-after-move.rs:5:12
-   |
-LL |     fn foo(self) -> String;
-   |            ^^^^
-
-error[E0382]: borrow of moved value: `x`
-  --> $DIR/borrow-after-move.rs:40:24
+   |             ^ doesn't have a size known at compile-time
    |
-LL |         let x = "hello".to_owned().into_boxed_str();
-   |             - move occurs because `x` has type `Box<str>`, which does not implement the `Copy` trait
-LL |         x.foo();
-   |         - value moved here
-LL |         println!("{}", &x);
-   |                        ^^ value borrowed here after move
+   = help: the trait `Sized` is not implemented for `str`
+   = note: all local variables must have a statically known size
+help: references are always `Sized`, even if they point to unsized data; consider not dereferencing the expression
    |
-help: consider cloning the value if the performance cost is acceptable
+LL -         let y = *x;
+LL +         let y = x;
    |
-LL |         x.clone().foo();
-   |          ++++++++
 
-error: aborting due to 5 previous errors; 1 warning emitted
+error: aborting due to 2 previous errors
 
-For more information about this error, try `rustc --explain E0382`.
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/unsized-locals/by-value-trait-dyn-compatibility-rpass.rs b/tests/ui/unsized-locals/by-value-trait-dyn-compatibility-rpass.rs
index 7ccea43d182..26d2bc6c8e0 100644
--- a/tests/ui/unsized-locals/by-value-trait-dyn-compatibility-rpass.rs
+++ b/tests/ui/unsized-locals/by-value-trait-dyn-compatibility-rpass.rs
@@ -1,8 +1,3 @@
-//@ run-pass
-
-#![allow(incomplete_features)]
-#![feature(unsized_locals)]
-
 pub trait Foo {
     fn foo(self) -> String;
 }
@@ -16,7 +11,7 @@ impl Foo for A {
 }
 
 fn main() {
-    let x = *(Box::new(A) as Box<dyn Foo>);
+    let x = *(Box::new(A) as Box<dyn Foo>); //~ERROR the size for values of type `dyn Foo` cannot be known at compilation time
     assert_eq!(x.foo(), format!("hello"));
 
     // I'm not sure whether we want this to work
diff --git a/tests/ui/unsized-locals/by-value-trait-dyn-compatibility-rpass.stderr b/tests/ui/unsized-locals/by-value-trait-dyn-compatibility-rpass.stderr
new file mode 100644
index 00000000000..231cc051f7a
--- /dev/null
+++ b/tests/ui/unsized-locals/by-value-trait-dyn-compatibility-rpass.stderr
@@ -0,0 +1,17 @@
+error[E0277]: the size for values of type `dyn Foo` cannot be known at compilation time
+  --> $DIR/by-value-trait-dyn-compatibility-rpass.rs:14:9
+   |
+LL |     let x = *(Box::new(A) as Box<dyn Foo>);
+   |         ^ doesn't have a size known at compile-time
+   |
+   = help: the trait `Sized` is not implemented for `dyn Foo`
+   = note: all local variables must have a statically known size
+help: references are always `Sized`, even if they point to unsized data; consider not dereferencing the expression
+   |
+LL -     let x = *(Box::new(A) as Box<dyn Foo>);
+LL +     let x = (Box::new(A) as Box<dyn Foo>);
+   |
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/unsized-locals/by-value-trait-dyn-compatibility-with-default.rs b/tests/ui/unsized-locals/by-value-trait-dyn-compatibility-with-default.rs
index 1f9b5f11fb5..554c2706e1e 100644
--- a/tests/ui/unsized-locals/by-value-trait-dyn-compatibility-with-default.rs
+++ b/tests/ui/unsized-locals/by-value-trait-dyn-compatibility-with-default.rs
@@ -1,7 +1,5 @@
-//@ run-pass
-
-#![allow(incomplete_features)]
-#![feature(unsized_locals, unsized_fn_params)]
+#![allow(internal_features)]
+#![feature(unsized_fn_params)]
 
 pub trait Foo {
     fn foo(self) -> String {
@@ -14,7 +12,7 @@ struct A;
 impl Foo for A {}
 
 fn main() {
-    let x = *(Box::new(A) as Box<dyn Foo>);
+    let x = *(Box::new(A) as Box<dyn Foo>); //~ERROR the size for values of type `dyn Foo` cannot be known at compilation time
     assert_eq!(x.foo(), format!("hello"));
 
     // I'm not sure whether we want this to work
diff --git a/tests/ui/unsized-locals/by-value-trait-dyn-compatibility-with-default.stderr b/tests/ui/unsized-locals/by-value-trait-dyn-compatibility-with-default.stderr
new file mode 100644
index 00000000000..6d8370fac63
--- /dev/null
+++ b/tests/ui/unsized-locals/by-value-trait-dyn-compatibility-with-default.stderr
@@ -0,0 +1,17 @@
+error[E0277]: the size for values of type `dyn Foo` cannot be known at compilation time
+  --> $DIR/by-value-trait-dyn-compatibility-with-default.rs:15:9
+   |
+LL |     let x = *(Box::new(A) as Box<dyn Foo>);
+   |         ^ doesn't have a size known at compile-time
+   |
+   = help: the trait `Sized` is not implemented for `dyn Foo`
+   = note: all local variables must have a statically known size
+help: references are always `Sized`, even if they point to unsized data; consider not dereferencing the expression
+   |
+LL -     let x = *(Box::new(A) as Box<dyn Foo>);
+LL +     let x = (Box::new(A) as Box<dyn Foo>);
+   |
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/unsized-locals/by-value-trait-dyn-compatibility.rs b/tests/ui/unsized-locals/by-value-trait-dyn-compatibility.rs
index d0ba6944a1e..d390f18c69f 100644
--- a/tests/ui/unsized-locals/by-value-trait-dyn-compatibility.rs
+++ b/tests/ui/unsized-locals/by-value-trait-dyn-compatibility.rs
@@ -1,6 +1,3 @@
-#![feature(unsized_locals)]
-//~^ WARN the feature `unsized_locals` is incomplete
-
 pub trait Foo {
     fn foo(self) -> String
     where
@@ -16,7 +13,7 @@ impl Foo for A {
 }
 
 fn main() {
-    let x = *(Box::new(A) as Box<dyn Foo>);
+    let x = *(Box::new(A) as Box<dyn Foo>); //~ERROR the size for values of type `dyn Foo` cannot be known at compilation time [E0277]
     x.foo();
     //~^ERROR the `foo` method cannot be invoked on a trait object
 }
diff --git a/tests/ui/unsized-locals/by-value-trait-dyn-compatibility.stderr b/tests/ui/unsized-locals/by-value-trait-dyn-compatibility.stderr
index 223624cfca4..1c681ba1215 100644
--- a/tests/ui/unsized-locals/by-value-trait-dyn-compatibility.stderr
+++ b/tests/ui/unsized-locals/by-value-trait-dyn-compatibility.stderr
@@ -1,14 +1,5 @@
-warning: the feature `unsized_locals` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/by-value-trait-dyn-compatibility.rs:1:12
-   |
-LL | #![feature(unsized_locals)]
-   |            ^^^^^^^^^^^^^^
-   |
-   = note: see issue #48055 <https://github.com/rust-lang/rust/issues/48055> for more information
-   = note: `#[warn(incomplete_features)]` on by default
-
 error: the `foo` method cannot be invoked on a trait object
-  --> $DIR/by-value-trait-dyn-compatibility.rs:20:7
+  --> $DIR/by-value-trait-dyn-compatibility.rs:17:7
    |
 LL |         Self: Sized;
    |               ----- this has a `Sized` requirement
@@ -16,5 +7,20 @@ LL |         Self: Sized;
 LL |     x.foo();
    |       ^^^
 
-error: aborting due to 1 previous error; 1 warning emitted
+error[E0277]: the size for values of type `dyn Foo` cannot be known at compilation time
+  --> $DIR/by-value-trait-dyn-compatibility.rs:16:9
+   |
+LL |     let x = *(Box::new(A) as Box<dyn Foo>);
+   |         ^ doesn't have a size known at compile-time
+   |
+   = help: the trait `Sized` is not implemented for `dyn Foo`
+   = note: all local variables must have a statically known size
+help: references are always `Sized`, even if they point to unsized data; consider not dereferencing the expression
+   |
+LL -     let x = *(Box::new(A) as Box<dyn Foo>);
+LL +     let x = (Box::new(A) as Box<dyn Foo>);
+   |
+
+error: aborting due to 2 previous errors
 
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/unsized-locals/double-move.rs b/tests/ui/unsized-locals/double-move.rs
index 9e46ef9be48..254528abfb4 100644
--- a/tests/ui/unsized-locals/double-move.rs
+++ b/tests/ui/unsized-locals/double-move.rs
@@ -1,5 +1,4 @@
-#![feature(unsized_locals, unsized_fn_params)]
-//~^ WARN the feature `unsized_locals` is incomplete
+#![feature(unsized_fn_params)]
 
 pub trait Foo {
     fn foo(self) -> String;
@@ -16,39 +15,39 @@ fn drop_unsized<T: ?Sized>(_: T) {}
 fn main() {
     {
         let x = "hello".to_owned().into_boxed_str();
-        let y = *x;
+        let y = *x; //~ERROR the size for values of type `str` cannot be known at compilation time [E0277]
+        drop_unsized(y);
         drop_unsized(y);
-        drop_unsized(y); //~ERROR use of moved value
     }
 
     {
         let x = "hello".to_owned().into_boxed_str();
-        let _y = *x;
-        drop_unsized(x); //~ERROR use of moved value
+        let _y = *x; //~ERROR the size for values of type `str` cannot be known at compilation time [E0277]
+        drop_unsized(x);
     }
 
     {
         let x = "hello".to_owned().into_boxed_str();
         drop_unsized(x);
-        let _y = *x; //~ERROR use of moved value
+        let _y = *x; //~ERROR the size for values of type `str` cannot be known at compilation time [E0277]
     }
 
     {
         let x = "hello".to_owned().into_boxed_str();
-        let y = *x;
+        let y = *x; //~ERROR the size for values of type `str` cannot be known at compilation time [E0277]
+        y.foo();
         y.foo();
-        y.foo(); //~ERROR use of moved value
     }
 
     {
         let x = "hello".to_owned().into_boxed_str();
-        let _y = *x;
-        x.foo(); //~ERROR use of moved value
+        let _y = *x; //~ERROR the size for values of type `str` cannot be known at compilation time [E0277]
+        x.foo();
     }
 
     {
         let x = "hello".to_owned().into_boxed_str();
         x.foo();
-        let _y = *x; //~ERROR use of moved value
+        let _y = *x; //~ERROR the size for values of type `str` cannot be known at compilation time [E0277]
     }
 }
diff --git a/tests/ui/unsized-locals/double-move.stderr b/tests/ui/unsized-locals/double-move.stderr
index 49b906bbe02..8d97b16d00c 100644
--- a/tests/ui/unsized-locals/double-move.stderr
+++ b/tests/ui/unsized-locals/double-move.stderr
@@ -1,86 +1,87 @@
-warning: the feature `unsized_locals` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/double-move.rs:1:12
+error[E0277]: the size for values of type `str` cannot be known at compilation time
+  --> $DIR/double-move.rs:18:13
    |
-LL | #![feature(unsized_locals, unsized_fn_params)]
-   |            ^^^^^^^^^^^^^^
+LL |         let y = *x;
+   |             ^ doesn't have a size known at compile-time
    |
-   = note: see issue #48055 <https://github.com/rust-lang/rust/issues/48055> for more information
-   = note: `#[warn(incomplete_features)]` on by default
-
-error[E0382]: use of moved value: `y`
-  --> $DIR/double-move.rs:21:22
+   = help: the trait `Sized` is not implemented for `str`
+   = note: all local variables must have a statically known size
+help: references are always `Sized`, even if they point to unsized data; consider not dereferencing the expression
+   |
+LL -         let y = *x;
+LL +         let y = x;
    |
-LL |         let y = *x;
-   |             - move occurs because `y` has type `str`, which does not implement the `Copy` trait
-LL |         drop_unsized(y);
-   |                      - value moved here
-LL |         drop_unsized(y);
-   |                      ^ value used here after move
-   |
-note: consider changing this parameter type in function `drop_unsized` to borrow instead if owning the value isn't necessary
-  --> $DIR/double-move.rs:14:31
-   |
-LL | fn drop_unsized<T: ?Sized>(_: T) {}
-   |    ------------               ^ this parameter takes ownership of the value
-   |    |
-   |    in this function
 
-error[E0382]: use of moved value: `x`
-  --> $DIR/double-move.rs:27:22
+error[E0277]: the size for values of type `str` cannot be known at compilation time
+  --> $DIR/double-move.rs:25:13
    |
 LL |         let _y = *x;
-   |                  -- value moved here
-LL |         drop_unsized(x);
-   |                      ^ value used here after move
+   |             ^^ doesn't have a size known at compile-time
+   |
+   = help: the trait `Sized` is not implemented for `str`
+   = note: all local variables must have a statically known size
+help: references are always `Sized`, even if they point to unsized data; consider not dereferencing the expression
+   |
+LL -         let _y = *x;
+LL +         let _y = x;
    |
-   = note: move occurs because `*x` has type `str`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `*x`
-  --> $DIR/double-move.rs:33:18
+error[E0277]: the size for values of type `str` cannot be known at compilation time
+  --> $DIR/double-move.rs:32:13
    |
-LL |         let x = "hello".to_owned().into_boxed_str();
-   |             - move occurs because `x` has type `Box<str>`, which does not implement the `Copy` trait
-LL |         drop_unsized(x);
-   |                      - value moved here
 LL |         let _y = *x;
-   |                  ^^ value used here after move
+   |             ^^ doesn't have a size known at compile-time
+   |
+   = help: the trait `Sized` is not implemented for `str`
+   = note: all local variables must have a statically known size
+help: references are always `Sized`, even if they point to unsized data; consider not dereferencing the expression
+   |
+LL -         let _y = *x;
+LL +         let _y = x;
+   |
 
-error[E0382]: use of moved value: `y`
-  --> $DIR/double-move.rs:40:9
+error[E0277]: the size for values of type `str` cannot be known at compilation time
+  --> $DIR/double-move.rs:37:13
    |
 LL |         let y = *x;
-   |             - move occurs because `y` has type `str`, which does not implement the `Copy` trait
-LL |         y.foo();
-   |           ----- `y` moved due to this method call
-LL |         y.foo();
-   |         ^ value used here after move
+   |             ^ doesn't have a size known at compile-time
+   |
+   = help: the trait `Sized` is not implemented for `str`
+   = note: all local variables must have a statically known size
+help: references are always `Sized`, even if they point to unsized data; consider not dereferencing the expression
    |
-note: `Foo::foo` takes ownership of the receiver `self`, which moves `y`
-  --> $DIR/double-move.rs:5:12
+LL -         let y = *x;
+LL +         let y = x;
    |
-LL |     fn foo(self) -> String;
-   |            ^^^^
 
-error[E0382]: use of moved value: `x`
-  --> $DIR/double-move.rs:46:9
+error[E0277]: the size for values of type `str` cannot be known at compilation time
+  --> $DIR/double-move.rs:44:13
    |
 LL |         let _y = *x;
-   |                  -- value moved here
-LL |         x.foo();
-   |         ^ value used here after move
+   |             ^^ doesn't have a size known at compile-time
+   |
+   = help: the trait `Sized` is not implemented for `str`
+   = note: all local variables must have a statically known size
+help: references are always `Sized`, even if they point to unsized data; consider not dereferencing the expression
+   |
+LL -         let _y = *x;
+LL +         let _y = x;
    |
-   = note: move occurs because `*x` has type `str`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `*x`
-  --> $DIR/double-move.rs:52:18
+error[E0277]: the size for values of type `str` cannot be known at compilation time
+  --> $DIR/double-move.rs:51:13
    |
-LL |         let x = "hello".to_owned().into_boxed_str();
-   |             - move occurs because `x` has type `Box<str>`, which does not implement the `Copy` trait
-LL |         x.foo();
-   |         - value moved here
 LL |         let _y = *x;
-   |                  ^^ value used here after move
+   |             ^^ doesn't have a size known at compile-time
+   |
+   = help: the trait `Sized` is not implemented for `str`
+   = note: all local variables must have a statically known size
+help: references are always `Sized`, even if they point to unsized data; consider not dereferencing the expression
+   |
+LL -         let _y = *x;
+LL +         let _y = x;
+   |
 
-error: aborting due to 6 previous errors; 1 warning emitted
+error: aborting due to 6 previous errors
 
-For more information about this error, try `rustc --explain E0382`.
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/unsized-locals/ice-size_and_align_of-closure-not-supported-88212.rs b/tests/ui/unsized-locals/ice-size_and_align_of-closure-not-supported-88212.rs
index ec475673d0d..4b15e191a0b 100644
--- a/tests/ui/unsized-locals/ice-size_and_align_of-closure-not-supported-88212.rs
+++ b/tests/ui/unsized-locals/ice-size_and_align_of-closure-not-supported-88212.rs
@@ -1,7 +1,5 @@
 // ICE size_and_align_of::<[closure@test.rs:15:5: 17:7]> not supported #88212
 // issue: rust-lang/rust#88212
-#![feature(unsized_locals)]
-//~^ WARN the feature `unsized_locals` is incomplete and may not be safe to use and/or cause compiler crashes
 
 trait Example {}
 struct Foo();
@@ -13,9 +11,8 @@ fn example() -> Box<dyn Example> {
 }
 
 fn main() {
-    let x: dyn Example = *example();
+    let x: dyn Example = *example(); //~ERROR the size for values of type `dyn Example` cannot be known at compilation time
     (move || {
-        let _y = x;
-        //~^ ERROR the size for values of type `dyn Example` cannot be known at compilation time
+        let _y = x; //~ERROR the size for values of type `dyn Example` cannot be known at compilation time
     })();
 }
diff --git a/tests/ui/unsized-locals/ice-size_and_align_of-closure-not-supported-88212.stderr b/tests/ui/unsized-locals/ice-size_and_align_of-closure-not-supported-88212.stderr
index a0253ac1f35..ffee9852c2a 100644
--- a/tests/ui/unsized-locals/ice-size_and_align_of-closure-not-supported-88212.stderr
+++ b/tests/ui/unsized-locals/ice-size_and_align_of-closure-not-supported-88212.stderr
@@ -1,23 +1,25 @@
-warning: the feature `unsized_locals` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/ice-size_and_align_of-closure-not-supported-88212.rs:3:12
+error[E0277]: the size for values of type `dyn Example` cannot be known at compilation time
+  --> $DIR/ice-size_and_align_of-closure-not-supported-88212.rs:14:9
+   |
+LL |     let x: dyn Example = *example();
+   |         ^ doesn't have a size known at compile-time
    |
-LL | #![feature(unsized_locals)]
-   |            ^^^^^^^^^^^^^^
+   = help: the trait `Sized` is not implemented for `dyn Example`
+   = note: all local variables must have a statically known size
+help: consider borrowing here
    |
-   = note: see issue #48055 <https://github.com/rust-lang/rust/issues/48055> for more information
-   = note: `#[warn(incomplete_features)]` on by default
+LL |     let x: &dyn Example = *example();
+   |            +
 
 error[E0277]: the size for values of type `dyn Example` cannot be known at compilation time
-  --> $DIR/ice-size_and_align_of-closure-not-supported-88212.rs:18:18
+  --> $DIR/ice-size_and_align_of-closure-not-supported-88212.rs:16:13
    |
-LL |     (move || {
-   |           -- this closure captures all values by move
 LL |         let _y = x;
-   |                  ^ doesn't have a size known at compile-time
+   |             ^^ doesn't have a size known at compile-time
    |
    = help: the trait `Sized` is not implemented for `dyn Example`
-   = note: all values captured by value by a closure must have a statically known size
+   = note: all local variables must have a statically known size
 
-error: aborting due to 1 previous error; 1 warning emitted
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/unsized-locals/issue-30276-feature-flagged.rs b/tests/ui/unsized-locals/issue-30276-feature-flagged.rs
index 8b5b321ec49..6b67ebbec1c 100644
--- a/tests/ui/unsized-locals/issue-30276-feature-flagged.rs
+++ b/tests/ui/unsized-locals/issue-30276-feature-flagged.rs
@@ -1,6 +1,3 @@
-#![feature(unsized_locals)]
-//~^ WARN the feature `unsized_locals` is incomplete
-
 struct Test([i32]);
 
 fn main() {
diff --git a/tests/ui/unsized-locals/issue-30276-feature-flagged.stderr b/tests/ui/unsized-locals/issue-30276-feature-flagged.stderr
index ee8e4f3eee2..a7bf27a0c4a 100644
--- a/tests/ui/unsized-locals/issue-30276-feature-flagged.stderr
+++ b/tests/ui/unsized-locals/issue-30276-feature-flagged.stderr
@@ -1,14 +1,5 @@
-warning: the feature `unsized_locals` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/issue-30276-feature-flagged.rs:1:12
-   |
-LL | #![feature(unsized_locals)]
-   |            ^^^^^^^^^^^^^^
-   |
-   = note: see issue #48055 <https://github.com/rust-lang/rust/issues/48055> for more information
-   = note: `#[warn(incomplete_features)]` on by default
-
 error[E0277]: the size for values of type `[i32]` cannot be known at compilation time
-  --> $DIR/issue-30276-feature-flagged.rs:7:29
+  --> $DIR/issue-30276-feature-flagged.rs:4:29
    |
 LL |     let _x: fn(_) -> Test = Test;
    |                             ^^^^ doesn't have a size known at compile-time
@@ -17,6 +8,6 @@ LL |     let _x: fn(_) -> Test = Test;
    = note: all function arguments must have a statically known size
    = help: unsized fn params are gated as an unstable feature
 
-error: aborting due to 1 previous error; 1 warning emitted
+error: aborting due to 1 previous error
 
 For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/unsized-locals/issue-50940-with-feature.rs b/tests/ui/unsized-locals/issue-50940-with-feature.rs
index 63b0e830be4..9a1ba7af191 100644
--- a/tests/ui/unsized-locals/issue-50940-with-feature.rs
+++ b/tests/ui/unsized-locals/issue-50940-with-feature.rs
@@ -1,5 +1,4 @@
-#![feature(unsized_locals, unsized_fn_params)]
-//~^ WARN the feature `unsized_locals` is incomplete
+#![feature(unsized_fn_params)]
 
 fn main() {
     struct A<X: ?Sized>(X);
diff --git a/tests/ui/unsized-locals/issue-50940-with-feature.stderr b/tests/ui/unsized-locals/issue-50940-with-feature.stderr
index b39eb2e70bb..f3fb3ac1a09 100644
--- a/tests/ui/unsized-locals/issue-50940-with-feature.stderr
+++ b/tests/ui/unsized-locals/issue-50940-with-feature.stderr
@@ -1,26 +1,17 @@
-warning: the feature `unsized_locals` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/issue-50940-with-feature.rs:1:12
-   |
-LL | #![feature(unsized_locals, unsized_fn_params)]
-   |            ^^^^^^^^^^^^^^
-   |
-   = note: see issue #48055 <https://github.com/rust-lang/rust/issues/48055> for more information
-   = note: `#[warn(incomplete_features)]` on by default
-
 error[E0277]: the size for values of type `str` cannot be known at compilation time
-  --> $DIR/issue-50940-with-feature.rs:6:5
+  --> $DIR/issue-50940-with-feature.rs:5:5
    |
 LL |     A as fn(str) -> A<str>;
    |     ^ doesn't have a size known at compile-time
    |
    = help: within `A<str>`, the trait `Sized` is not implemented for `str`
 note: required because it appears within the type `A<str>`
-  --> $DIR/issue-50940-with-feature.rs:5:12
+  --> $DIR/issue-50940-with-feature.rs:4:12
    |
 LL |     struct A<X: ?Sized>(X);
    |            ^
    = note: the return type of a function must have a statically known size
 
-error: aborting due to 1 previous error; 1 warning emitted
+error: aborting due to 1 previous error
 
 For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/unsized-locals/reference-unsized-locals.rs b/tests/ui/unsized-locals/reference-unsized-locals.rs
index 5b5fca22a01..757bae4bb91 100644
--- a/tests/ui/unsized-locals/reference-unsized-locals.rs
+++ b/tests/ui/unsized-locals/reference-unsized-locals.rs
@@ -1,10 +1,5 @@
-//@ run-pass
-
-#![allow(incomplete_features)]
-#![feature(unsized_locals)]
-
 fn main() {
     let foo: Box<[u8]> = Box::new(*b"foo");
-    let foo: [u8] = *foo;
+    let foo: [u8] = *foo; //~ERROR the size for values of type `[u8]` cannot be known at compilation time [E0277]
     assert_eq!(&foo, b"foo" as &[u8]);
 }
diff --git a/tests/ui/unsized-locals/reference-unsized-locals.stderr b/tests/ui/unsized-locals/reference-unsized-locals.stderr
new file mode 100644
index 00000000000..d1cba9ba79e
--- /dev/null
+++ b/tests/ui/unsized-locals/reference-unsized-locals.stderr
@@ -0,0 +1,16 @@
+error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
+  --> $DIR/reference-unsized-locals.rs:3:9
+   |
+LL |     let foo: [u8] = *foo;
+   |         ^^^ doesn't have a size known at compile-time
+   |
+   = help: the trait `Sized` is not implemented for `[u8]`
+   = note: all local variables must have a statically known size
+help: consider borrowing here
+   |
+LL |     let foo: &[u8] = *foo;
+   |              +
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/unsized-locals/simple-unsized-locals.rs b/tests/ui/unsized-locals/simple-unsized-locals.rs
index 374031b80bd..e6c8bdc7a88 100644
--- a/tests/ui/unsized-locals/simple-unsized-locals.rs
+++ b/tests/ui/unsized-locals/simple-unsized-locals.rs
@@ -1,9 +1,4 @@
-//@ run-pass
-
-#![allow(incomplete_features)]
-#![feature(unsized_locals)]
-
 fn main() {
     let foo: Box<[u8]> = Box::new(*b"foo");
-    let _foo: [u8] = *foo;
+    let _foo: [u8] = *foo; //~ERROR the size for values of type `[u8]` cannot be known at compilation time [E0277]
 }
diff --git a/tests/ui/unsized-locals/simple-unsized-locals.stderr b/tests/ui/unsized-locals/simple-unsized-locals.stderr
new file mode 100644
index 00000000000..83a64bd0711
--- /dev/null
+++ b/tests/ui/unsized-locals/simple-unsized-locals.stderr
@@ -0,0 +1,16 @@
+error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
+  --> $DIR/simple-unsized-locals.rs:3:9
+   |
+LL |     let _foo: [u8] = *foo;
+   |         ^^^^ doesn't have a size known at compile-time
+   |
+   = help: the trait `Sized` is not implemented for `[u8]`
+   = note: all local variables must have a statically known size
+help: consider borrowing here
+   |
+LL |     let _foo: &[u8] = *foo;
+   |               +
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/unsized-locals/suggest-borrow.stderr b/tests/ui/unsized-locals/suggest-borrow.stderr
index 8741b35cdcf..a3b4403cf9b 100644
--- a/tests/ui/unsized-locals/suggest-borrow.stderr
+++ b/tests/ui/unsized-locals/suggest-borrow.stderr
@@ -6,7 +6,6 @@ LL |     let x: [u8] = vec!(1, 2, 3)[..];
    |
    = help: the trait `Sized` is not implemented for `[u8]`
    = note: all local variables must have a statically known size
-   = help: unsized locals are gated as an unstable feature
 help: consider borrowing here
    |
 LL |     let x: &[u8] = vec!(1, 2, 3)[..];
@@ -51,7 +50,6 @@ LL |     let x: [u8] = &vec!(1, 2, 3)[..];
    |
    = help: the trait `Sized` is not implemented for `[u8]`
    = note: all local variables must have a statically known size
-   = help: unsized locals are gated as an unstable feature
 help: consider borrowing here
    |
 LL |     let x: &[u8] = &vec!(1, 2, 3)[..];
diff --git a/tests/ui/unsized-locals/unsized-exprs-rpass.rs b/tests/ui/unsized-locals/unsized-exprs-rpass.rs
index 861583efc40..54ecd000343 100644
--- a/tests/ui/unsized-locals/unsized-exprs-rpass.rs
+++ b/tests/ui/unsized-locals/unsized-exprs-rpass.rs
@@ -1,6 +1,6 @@
 //@ run-pass
-#![allow(incomplete_features, unused_braces, unused_parens)]
-#![feature(unsized_locals, unsized_fn_params)]
+#![allow(internal_features, unused_braces, unused_parens)]
+#![feature(unsized_fn_params)]
 
 struct A<X: ?Sized>(#[allow(dead_code)] X);
 
diff --git a/tests/ui/unsized-locals/unsized-locals-using-unsized-fn-params.stderr b/tests/ui/unsized-locals/unsized-locals-using-unsized-fn-params.stderr
index ace5a87187b..fe6780c438c 100644
--- a/tests/ui/unsized-locals/unsized-locals-using-unsized-fn-params.stderr
+++ b/tests/ui/unsized-locals/unsized-locals-using-unsized-fn-params.stderr
@@ -6,7 +6,6 @@ LL | fn f1(box box _b: Box<Box<[u8]>>) {}
    |
    = help: the trait `Sized` is not implemented for `[u8]`
    = note: all local variables must have a statically known size
-   = help: unsized locals are gated as an unstable feature
 
 error[E0277]: the size for values of type `[i32]` cannot be known at compilation time
   --> $DIR/unsized-locals-using-unsized-fn-params.rs:8:12
@@ -16,7 +15,6 @@ LL | fn f2((_x, _y): (i32, [i32])) {}
    |
    = help: the trait `Sized` is not implemented for `[i32]`
    = note: all local variables must have a statically known size
-   = help: unsized locals are gated as an unstable feature
 
 error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
   --> $DIR/unsized-locals-using-unsized-fn-params.rs:13:9
@@ -26,7 +24,6 @@ LL |     let _foo: [u8] = *foo;
    |
    = help: the trait `Sized` is not implemented for `[u8]`
    = note: all local variables must have a statically known size
-   = help: unsized locals are gated as an unstable feature
 help: consider borrowing here
    |
 LL |     let _foo: &[u8] = *foo;
diff --git a/tests/ui/unsized-locals/yote.rs b/tests/ui/unsized-locals/yote.rs
new file mode 100644
index 00000000000..aa5b68a3078
--- /dev/null
+++ b/tests/ui/unsized-locals/yote.rs
@@ -0,0 +1,4 @@
+//@ normalize-stderr: "you are using [0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+)?( \([^)]*\))?" -> "you are using $$RUSTC_VERSION"
+
+#![feature(unsized_locals)] //~ERROR feature has been removed
+#![crate_type = "lib"]
diff --git a/tests/ui/unsized-locals/yote.stderr b/tests/ui/unsized-locals/yote.stderr
new file mode 100644
index 00000000000..655aad5360c
--- /dev/null
+++ b/tests/ui/unsized-locals/yote.stderr
@@ -0,0 +1,12 @@
+error[E0557]: feature has been removed
+  --> $DIR/yote.rs:3:12
+   |
+LL | #![feature(unsized_locals)]
+   |            ^^^^^^^^^^^^^^ feature has been removed
+   |
+   = note: removed in CURRENT_RUSTC_VERSION (you are using $RUSTC_VERSION)
+   = note: removed due to implementation concerns; see https://github.com/rust-lang/rust/issues/111942
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0557`.
diff --git a/tests/ui/unsized/unsized6.stderr b/tests/ui/unsized/unsized6.stderr
index de921709865..2dcdd3c3c0b 100644
--- a/tests/ui/unsized/unsized6.stderr
+++ b/tests/ui/unsized/unsized6.stderr
@@ -8,7 +8,6 @@ LL |     let y: Y;
    |         ^ doesn't have a size known at compile-time
    |
    = note: all local variables must have a statically known size
-   = help: unsized locals are gated as an unstable feature
 help: consider removing the `?Sized` bound to make the type parameter `Sized`
    |
 LL - fn f1<W: ?Sized, X: ?Sized, Y: ?Sized, Z: ?Sized>(x: &X) {
@@ -60,7 +59,6 @@ LL |     let y: X;
    |         ^ doesn't have a size known at compile-time
    |
    = note: all local variables must have a statically known size
-   = help: unsized locals are gated as an unstable feature
 help: consider removing the `?Sized` bound to make the type parameter `Sized`
    |
 LL - fn f2<X: ?Sized, Y: ?Sized>(x: &X) {
@@ -96,7 +94,6 @@ LL |     let y: X = *x1;
    |         ^ doesn't have a size known at compile-time
    |
    = note: all local variables must have a statically known size
-   = help: unsized locals are gated as an unstable feature
 help: consider removing the `?Sized` bound to make the type parameter `Sized`
    |
 LL - fn f3<X: ?Sized>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
@@ -117,7 +114,6 @@ LL |     let y = *x2;
    |         ^ doesn't have a size known at compile-time
    |
    = note: all local variables must have a statically known size
-   = help: unsized locals are gated as an unstable feature
 help: consider removing the `?Sized` bound to make the type parameter `Sized`
    |
 LL - fn f3<X: ?Sized>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
@@ -139,7 +135,6 @@ LL |     let (y, z) = (*x3, 4);
    |          ^ doesn't have a size known at compile-time
    |
    = note: all local variables must have a statically known size
-   = help: unsized locals are gated as an unstable feature
 help: consider removing the `?Sized` bound to make the type parameter `Sized`
    |
 LL - fn f3<X: ?Sized>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
@@ -155,7 +150,6 @@ LL |     let y: X = *x1;
    |         ^ doesn't have a size known at compile-time
    |
    = note: all local variables must have a statically known size
-   = help: unsized locals are gated as an unstable feature
 help: consider removing the `?Sized` bound to make the type parameter `Sized`
    |
 LL - fn f4<X: ?Sized + T>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
@@ -176,7 +170,6 @@ LL |     let y = *x2;
    |         ^ doesn't have a size known at compile-time
    |
    = note: all local variables must have a statically known size
-   = help: unsized locals are gated as an unstable feature
 help: consider removing the `?Sized` bound to make the type parameter `Sized`
    |
 LL - fn f4<X: ?Sized + T>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
@@ -198,7 +191,6 @@ LL |     let (y, z) = (*x3, 4);
    |          ^ doesn't have a size known at compile-time
    |
    = note: all local variables must have a statically known size
-   = help: unsized locals are gated as an unstable feature
 help: consider removing the `?Sized` bound to make the type parameter `Sized`
    |
 LL - fn f4<X: ?Sized + T>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
diff --git a/triagebot.toml b/triagebot.toml
index 52d18d2ca4b..e5af77b6d44 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -1229,6 +1229,7 @@ compiler = [
     "@oli-obk",
     "@petrochenkov",
     "@SparrowLii",
+    "@WaffleLapkin",
     "@wesleywiser",
 ]
 libs = [
@@ -1240,13 +1241,6 @@ libs = [
     "@thomcc",
     "@ibraheemdev",
 ]
-bootstrap = [
-    "@Mark-Simulacrum",
-    "@albertlarsan68",
-    "@kobzol",
-    "@jieyouxu",
-    "@clubby789",
-]
 infra-ci = [
     "@Mark-Simulacrum",
     "@Kobzol",
@@ -1266,6 +1260,7 @@ codegen = [
     "@dianqk",
     "@saethlin",
     "@workingjubilee",
+    "@WaffleLapkin",
 ]
 query-system = [
     "@oli-obk",