about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs7
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs6
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mod.rs16
-rw-r--r--compiler/rustc_borrowck/src/invalidation.rs2
-rw-r--r--compiler/rustc_borrowck/src/lib.rs2
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs11
-rw-r--r--compiler/rustc_builtin_macros/src/test.rs45
-rw-r--r--compiler/rustc_codegen_cranelift/src/base.rs2
-rw-r--r--compiler/rustc_codegen_cranelift/src/codegen_i128.rs9
-rw-r--r--compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs20
-rw-r--r--compiler/rustc_codegen_cranelift/src/num.rs16
-rw-r--r--compiler/rustc_codegen_gcc/tools/generate_intrinsics.py3
-rw-r--r--compiler/rustc_codegen_ssa/src/common.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/block.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/intrinsic.rs49
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/rvalue.rs33
-rw-r--r--compiler/rustc_const_eval/messages.ftl3
-rw-r--r--compiler/rustc_const_eval/src/errors.rs12
-rw-r--r--compiler/rustc_const_eval/src/interpret/intrinsics.rs31
-rw-r--r--compiler/rustc_const_eval/src/interpret/operator.rs66
-rw-r--r--compiler/rustc_const_eval/src/interpret/step.rs28
-rw-r--r--compiler/rustc_const_eval/src/interpret/terminator.rs2
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/check.rs12
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/ops.rs94
-rw-r--r--compiler/rustc_const_eval/src/transform/promote_consts.rs13
-rw-r--r--compiler/rustc_const_eval/src/transform/validate.rs59
-rw-r--r--compiler/rustc_const_eval/src/util/mod.rs26
-rw-r--r--compiler/rustc_hir_analysis/src/check/mod.rs31
-rw-r--r--compiler/rustc_hir_typeck/src/demand.rs7
-rw-r--r--compiler/rustc_hir_typeck/src/method/confirm.rs26
-rw-r--r--compiler/rustc_hir_typeck/src/method/mod.rs21
-rw-r--r--compiler/rustc_infer/src/infer/combine.rs11
-rw-r--r--compiler/rustc_infer/src/infer/opaque_types.rs10
-rw-r--r--compiler/rustc_middle/src/mir/syntax.rs45
-rw-r--r--compiler/rustc_middle/src/mir/tcx.rs18
-rw-r--r--compiler/rustc_middle/src/mir/visit.rs2
-rw-r--r--compiler/rustc_middle/src/traits/mod.rs8
-rw-r--r--compiler/rustc_middle/src/traits/query.rs2
-rw-r--r--compiler/rustc_middle/src/traits/solve.rs20
-rw-r--r--compiler/rustc_middle/src/traits/solve/inspect.rs79
-rw-r--r--compiler/rustc_middle/src/traits/solve/inspect/format.rs131
-rw-r--r--compiler/rustc_mir_build/src/build/custom/parse/instruction.rs4
-rw-r--r--compiler/rustc_mir_build/src/build/expr/as_rvalue.rs2
-rw-r--r--compiler/rustc_mir_build/src/build/expr/into.rs6
-rw-r--r--compiler/rustc_mir_build/src/build/matches/test.rs4
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs85
-rw-r--r--compiler/rustc_mir_dataflow/src/elaborate_drops.rs2
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/direction.rs10
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/engine.rs14
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/tests.rs4
-rw-r--r--compiler/rustc_mir_dataflow/src/move_paths/builder.rs2
-rw-r--r--compiler/rustc_mir_transform/src/coverage/tests.rs2
-rw-r--r--compiler/rustc_mir_transform/src/function_item_references.rs2
-rw-r--r--compiler/rustc_mir_transform/src/generator.rs2
-rw-r--r--compiler/rustc_mir_transform/src/lib.rs6
-rw-r--r--compiler/rustc_mir_transform/src/lower_intrinsics.rs12
-rw-r--r--compiler/rustc_mir_transform/src/lower_slice_len.rs2
-rw-r--r--compiler/rustc_mir_transform/src/shim.rs4
-rw-r--r--compiler/rustc_resolve/messages.ftl18
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs31
-rw-r--r--compiler/rustc_resolve/src/errors.rs60
-rw-r--r--compiler/rustc_resolve/src/imports.rs93
-rw-r--r--compiler/rustc_resolve/src/late.rs6
-rw-r--r--compiler/rustc_resolve/src/macros.rs13
-rw-r--r--compiler/rustc_session/src/options.rs2
-rw-r--r--compiler/rustc_smir/src/rustc_smir/mod.rs7
-rw-r--r--compiler/rustc_smir/src/stable_mir/mir/body.rs5
-rw-r--r--compiler/rustc_span/src/lib.rs17
-rw-r--r--compiler/rustc_trait_selection/src/solve/alias_relate.rs72
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/mod.rs66
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt.rs229
-rw-r--r--compiler/rustc_trait_selection/src/solve/fulfill.rs122
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect.rs367
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect/dump.rs5
-rw-r--r--compiler/rustc_trait_selection/src/solve/mod.rs1
-rw-r--r--compiler/rustc_trait_selection/src/solve/opaques.rs24
-rw-r--r--compiler/rustc_trait_selection/src/solve/project_goals.rs358
-rw-r--r--compiler/rustc_trait_selection/src/solve/search_graph/mod.rs15
-rw-r--r--compiler/rustc_trait_selection/src/solve/trait_goals.rs376
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/normalize.rs8
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs8
-rw-r--r--compiler/rustc_ty_utils/src/consts.rs5
-rw-r--r--library/core/src/cell.rs4
-rw-r--r--library/core/src/future/poll_fn.rs87
-rw-r--r--library/core/src/net/ip_addr.rs10
-rw-r--r--library/core/src/num/mod.rs17
-rwxr-xr-xlibrary/core/src/unicode/printable.py2
-rw-r--r--library/core/tests/net/ip_addr.rs6
-rw-r--r--library/core/tests/net/socket_addr.rs2
-rw-r--r--library/std/src/net/socket_addr/tests.rs2
-rw-r--r--src/bootstrap/bootstrap.py4
-rwxr-xr-xsrc/bootstrap/configure.py10
-rw-r--r--src/bootstrap/mk/Makefile.in7
-rw-r--r--src/bootstrap/test.rs3
-rw-r--r--src/bootstrap/util.rs2
-rwxr-xr-x[-rw-r--r--]src/ci/cpu-usage-over-time.py0
-rwxr-xr-x[-rw-r--r--]src/ci/docker/host-x86_64/test-various/uefi_qemu_test/run.py0
-rwxr-xr-xsrc/ci/docker/run.sh2
-rwxr-xr-xsrc/ci/docker/scripts/android-sdk-manager.py17
-rwxr-xr-xsrc/ci/docker/scripts/fuchsia-test-runner.py4
-rw-r--r--src/ci/github-actions/problem_matchers.json40
-rwxr-xr-x[-rw-r--r--]src/ci/stage-build.py4
-rwxr-xr-x[-rw-r--r--]src/etc/dec2flt_table.py4
-rw-r--r--src/etc/gdb_load_rust_pretty_printers.py1
-rwxr-xr-xsrc/etc/generate-deriving-span-tests.py4
-rwxr-xr-x[-rw-r--r--]src/etc/htmldocck.py8
-rw-r--r--src/etc/lldb_batchmode.py2
-rw-r--r--src/etc/lldb_providers.py2
-rwxr-xr-x[-rw-r--r--]src/etc/test-float-parse/runtests.py0
-rw-r--r--src/librustdoc/clean/mod.rs6
-rw-r--r--src/librustdoc/clean/types.rs8
-rw-r--r--src/librustdoc/passes/strip_hidden.rs4
-rw-r--r--src/librustdoc/passes/stripper.rs9
-rw-r--r--src/tools/build_helper/src/ci.rs6
-rw-r--r--src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs2
-rw-r--r--src/tools/clippy/tests/ui/from_over_into_unfixable.rs6
-rw-r--r--src/tools/clippy/tests/ui/from_over_into_unfixable.stderr33
-rwxr-xr-xsrc/tools/miri/test-cargo-miri/run-test.py20
-rwxr-xr-xsrc/tools/publish_toolstate.py16
-rw-r--r--src/tools/rust-analyzer/.github/workflows/autopublish.yaml4
-rw-r--r--src/tools/rust-analyzer/.github/workflows/ci.yaml8
-rw-r--r--src/tools/rust-analyzer/Cargo.lock48
-rw-r--r--src/tools/rust-analyzer/crates/base-db/src/fixture.rs6
-rw-r--r--src/tools/rust-analyzer/crates/base-db/src/input.rs23
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body.rs50
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs72
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs1
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/db.rs23
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expander.rs12
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/find_path.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/hir.rs20
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs115
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/lib.rs301
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs70
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs48
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres.rs23
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs102
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/path.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/resolver.rs38
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/ast_id_map.rs7
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs10
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs154
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs147
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/db.rs108
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/eager.rs84
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/lib.rs72
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs11
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/Cargo.toml8
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs24
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs36
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs11
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/db.rs1
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs7
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/display.rs43
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer.rs47
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs5
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs17
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/interner.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs35
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lower.rs119
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs16
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs18
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs8
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs23
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs48
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs10
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/utils.rs30
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/from_id.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/lib.rs92
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/semantics.rs18
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs7
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/symbols.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs363
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs51
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs25
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs102
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs722
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs28
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/lib.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs21
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/utils.rs95
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs122
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs108
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs34
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs169
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/search.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt42
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt134
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/tests.rs43
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs12
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/lib.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html3
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs3
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/types.rs2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0017_array_type.rast5
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0030_traits.rast5
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0043_complex_assignment.rast5
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/server.rs6
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/workspace.rs6
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml3
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs17
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs328
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs8
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs20
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/progress_report.rs41
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs48
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs35
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs4
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs24
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs82
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs45
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs33
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs13
-rw-r--r--src/tools/rust-analyzer/crates/syntax/rust.ungram2
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs2
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/make.rs6
-rw-r--r--src/tools/rust-analyzer/crates/test-utils/src/fixture.rs4
-rw-r--r--src/tools/rust-analyzer/crates/test-utils/src/minicore.rs51
-rw-r--r--src/tools/rust-analyzer/docs/user/manual.adoc21
-rw-r--r--src/tools/rust-analyzer/editors/code/.vscodeignore1
-rw-r--r--src/tools/rust-analyzer/lib/la-arena/src/lib.rs6
-rw-r--r--tests/codegen/unchecked_shifts.rs4
-rw-r--r--tests/mir-opt/inline/unchecked_shifts.rs12
-rw-r--r--tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_bigger.Inline.panic-abort.diff36
-rw-r--r--tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_bigger.Inline.panic-unwind.diff36
-rw-r--r--tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_bigger.PreCodegen.after.panic-abort.mir22
-rw-r--r--tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_bigger.PreCodegen.after.panic-unwind.mir22
-rw-r--r--tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-abort.diff28
-rw-r--r--tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-unwind.diff28
-rw-r--r--tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-abort.mir24
-rw-r--r--tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-unwind.mir24
-rw-r--r--tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-abort.diff36
-rw-r--r--tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-unwind.diff36
-rw-r--r--tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-abort.mir22
-rw-r--r--tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-unwind.mir22
-rw-r--r--tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.Inline.panic-abort.diff28
-rw-r--r--tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.Inline.panic-unwind.diff28
-rw-r--r--tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.panic-abort.mir24
-rw-r--r--tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.panic-unwind.mir24
-rw-r--r--tests/mir-opt/lower_intrinsics.rs5
-rw-r--r--tests/mir-opt/lower_intrinsics.unchecked.LowerIntrinsics.panic-abort.diff112
-rw-r--r--tests/mir-opt/lower_intrinsics.unchecked.LowerIntrinsics.panic-unwind.diff112
-rw-r--r--tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.mir104
-rw-r--r--tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir63
-rw-r--r--tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir63
-rwxr-xr-x[-rw-r--r--]tests/run-make/coverage-reports/sort_subviews.py0
-rwxr-xr-xtests/run-make/libtest-junit/validate_junit.py2
-rwxr-xr-xtests/run-make/rustdoc-map-file/validate_json.py2
-rw-r--r--tests/run-make/sysroot-crates-are-unstable/test.py2
-rw-r--r--tests/ui/enum/suggest-default-attribute.stderr2
-rw-r--r--tests/ui/impl-trait/coherence-treats-tait-ambig.current.stderr13
-rw-r--r--tests/ui/impl-trait/coherence-treats-tait-ambig.next.stderr13
-rw-r--r--tests/ui/impl-trait/coherence-treats-tait-ambig.rs19
-rw-r--r--tests/ui/impl-trait/in-trait/suggest-missing-item.fixed22
-rw-r--r--tests/ui/impl-trait/in-trait/suggest-missing-item.rs19
-rw-r--r--tests/ui/impl-trait/in-trait/suggest-missing-item.stderr18
-rw-r--r--tests/ui/rfcs/rfc-2632-const-trait-impl/match-non-const-eq.gated.stderr26
-rw-r--r--tests/ui/rfcs/rfc-2632-const-trait-impl/match-non-const-eq.rs12
-rw-r--r--tests/ui/rfcs/rfc-2632-const-trait-impl/match-non-const-eq.stock.stderr13
-rw-r--r--tests/ui/test-attrs/test-function-signature.rs31
-rw-r--r--tests/ui/test-attrs/test-function-signature.stderr39
-rw-r--r--tests/ui/traits/non_lifetime_binders/foreach-partial-eq.rs12
-rw-r--r--tests/ui/traits/non_lifetime_binders/foreach-partial-eq.stderr28
-rw-r--r--tests/ui/type-alias-impl-trait/debug-ty-with-weak.rs12
-rw-r--r--tests/ui/type-alias-impl-trait/issue-76202-trait-impl-for-tait.rs2
-rw-r--r--tests/ui/typeck/dont-record-adjustments-when-pointing-at-arg.rs29
-rw-r--r--tests/ui/typeck/dont-record-adjustments-when-pointing-at-arg.stderr17
279 files changed, 7193 insertions, 2605 deletions
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index 15d73ed732f..fd5c21cfdf6 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -14,7 +14,7 @@ use rustc_infer::traits::ObligationCause;
 use rustc_middle::hir::nested_filter::OnlyBodies;
 use rustc_middle::mir::tcx::PlaceTy;
 use rustc_middle::mir::{
-    self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory,
+    self, AggregateKind, BindingForm, BorrowKind, CallSource, ClearCrossCrate, ConstraintCategory,
     FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef,
     ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm,
 };
@@ -2579,7 +2579,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
     fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut Diagnostic) {
         let tcx = self.infcx.tcx;
         if let (
-            Some(Terminator { kind: TerminatorKind::Call { from_hir_call: false, .. }, .. }),
+            Some(Terminator {
+                kind: TerminatorKind::Call { call_source: CallSource::OverloadedOperator, .. },
+                ..
+            }),
             Some((method_did, method_substs)),
         ) = (
             &self.body[loan.reserve_location.block].terminator,
diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
index 1d430a93a87..6c01fd63b1e 100644
--- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
@@ -6,8 +6,8 @@ use rustc_hir::intravisit::Visitor;
 use rustc_index::IndexSlice;
 use rustc_infer::infer::NllRegionVariableOrigin;
 use rustc_middle::mir::{
-    Body, CastKind, ConstraintCategory, FakeReadCause, Local, LocalInfo, Location, Operand, Place,
-    Rvalue, Statement, StatementKind, TerminatorKind,
+    Body, CallSource, CastKind, ConstraintCategory, FakeReadCause, Local, LocalInfo, Location,
+    Operand, Place, Rvalue, Statement, StatementKind, TerminatorKind,
 };
 use rustc_middle::ty::adjustment::PointerCast;
 use rustc_middle::ty::{self, RegionVid, TyCtxt};
@@ -494,7 +494,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 } else if self.was_captured_by_trait_object(borrow) {
                     LaterUseKind::TraitCapture
                 } else if location.statement_index == block.statements.len() {
-                    if let TerminatorKind::Call { func, from_hir_call: true, .. } =
+                    if let TerminatorKind::Call { func, call_source: CallSource::Normal, .. } =
                         &block.terminator().kind
                     {
                         // Just point to the function, to reduce the chance of overlapping spans.
diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs
index 20370e4c6ac..1d3cc851888 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mod.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs
@@ -13,8 +13,9 @@ use rustc_index::IndexSlice;
 use rustc_infer::infer::LateBoundRegionConversionTime;
 use rustc_middle::mir::tcx::PlaceTy;
 use rustc_middle::mir::{
-    AggregateKind, Constant, FakeReadCause, Local, LocalInfo, LocalKind, Location, Operand, Place,
-    PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind,
+    AggregateKind, CallSource, Constant, FakeReadCause, Local, LocalInfo, LocalKind, Location,
+    Operand, Place, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator,
+    TerminatorKind,
 };
 use rustc_middle::ty::print::Print;
 use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
@@ -414,7 +415,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                     if !is_terminator {
                         continue;
                     } else if let Some(Terminator {
-                        kind: TerminatorKind::Call { func, from_hir_call: false, .. },
+                        kind:
+                            TerminatorKind::Call {
+                                func,
+                                call_source: CallSource::OverloadedOperator,
+                                ..
+                            },
                         ..
                     }) = &bbd.terminator
                     {
@@ -839,7 +845,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         debug!("move_spans: target_temp = {:?}", target_temp);
 
         if let Some(Terminator {
-            kind: TerminatorKind::Call { fn_span, from_hir_call, .. }, ..
+            kind: TerminatorKind::Call { fn_span, call_source, .. }, ..
         }) = &self.body[location.block].terminator
         {
             let Some((method_did, method_substs)) =
@@ -859,7 +865,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 method_did,
                 method_substs,
                 *fn_span,
-                *from_hir_call,
+                call_source.from_hir_call(),
                 Some(self.infcx.tcx.fn_arg_names(method_did)[0]),
             );
 
diff --git a/compiler/rustc_borrowck/src/invalidation.rs b/compiler/rustc_borrowck/src/invalidation.rs
index b2ff25ecb96..e4fbe6ea4f4 100644
--- a/compiler/rustc_borrowck/src/invalidation.rs
+++ b/compiler/rustc_borrowck/src/invalidation.rs
@@ -128,7 +128,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> {
                 destination,
                 target: _,
                 unwind: _,
-                from_hir_call: _,
+                call_source: _,
                 fn_span: _,
             } => {
                 self.consume_operand(location, func);
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index 99a988f2c62..4fcdda311bf 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -710,7 +710,7 @@ impl<'cx, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx, R> for MirBorro
                 destination,
                 target: _,
                 unwind: _,
-                from_hir_call: _,
+                call_source: _,
                 fn_span: _,
             } => {
                 self.consume_operand(loc, (func, span), flow_state);
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 33f75437478..5d0eff9ea38 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -1065,7 +1065,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                         )?;
 
                         ocx.infcx.add_item_bounds_for_hidden_type(
-                            opaque_type_key,
+                            opaque_type_key.def_id.to_def_id(),
+                            opaque_type_key.substs,
                             cause,
                             param_env,
                             hidden_ty.ty,
@@ -1370,7 +1371,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                 }
                 // FIXME: check the values
             }
-            TerminatorKind::Call { func, args, destination, from_hir_call, target, .. } => {
+            TerminatorKind::Call { func, args, destination, call_source, target, .. } => {
                 self.check_operand(func, term_location);
                 for arg in args {
                     self.check_operand(arg, term_location);
@@ -1446,7 +1447,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                         .add_element(region_vid, term_location);
                 }
 
-                self.check_call_inputs(body, term, &sig, args, term_location, *from_hir_call);
+                self.check_call_inputs(body, term, &sig, args, term_location, *call_source);
             }
             TerminatorKind::Assert { cond, msg, .. } => {
                 self.check_operand(cond, term_location);
@@ -1573,7 +1574,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         sig: &ty::FnSig<'tcx>,
         args: &[Operand<'tcx>],
         term_location: Location,
-        from_hir_call: bool,
+        call_source: CallSource,
     ) {
         debug!("check_call_inputs({:?}, {:?})", sig, args);
         if args.len() < sig.inputs().len() || (args.len() > sig.inputs().len() && !sig.c_variadic) {
@@ -1591,7 +1592,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
             let op_arg_ty = op_arg.ty(body, self.tcx());
 
             let op_arg_ty = self.normalize(op_arg_ty, term_location);
-            let category = if from_hir_call {
+            let category = if call_source.from_hir_call() {
                 ConstraintCategory::CallArgument(self.infcx.tcx.erase_regions(func_ty))
             } else {
                 ConstraintCategory::Boring
diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs
index 49ee276af4e..fcc68010a34 100644
--- a/compiler/rustc_builtin_macros/src/test.rs
+++ b/compiler/rustc_builtin_macros/src/test.rs
@@ -3,7 +3,7 @@ use crate::errors;
 /// Ideally, this code would be in libtest but for efficiency and error messages it lives here.
 use crate::util::{check_builtin_macro_attribute, warn_on_duplicate_attribute};
 use rustc_ast::ptr::P;
-use rustc_ast::{self as ast, attr};
+use rustc_ast::{self as ast, attr, GenericParamKind};
 use rustc_ast_pretty::pprust;
 use rustc_errors::Applicability;
 use rustc_expand::base::*;
@@ -122,11 +122,7 @@ pub fn expand_test_or_bench(
     let ast::ItemKind::Fn(fn_) = &item.kind else {
         not_testable_error(cx, attr_sp, Some(&item));
         return if is_stmt {
-            vec![Annotatable::Stmt(P(ast::Stmt {
-                id: ast::DUMMY_NODE_ID,
-                span: item.span,
-                kind: ast::StmtKind::Item(item),
-            }))]
+            vec![Annotatable::Stmt(P(cx.stmt_item(item.span, item)))]
         } else {
             vec![Annotatable::Item(item)]
         };
@@ -138,7 +134,11 @@ pub fn expand_test_or_bench(
     if (!is_bench && !has_test_signature(cx, &item))
         || (is_bench && !has_bench_signature(cx, &item))
     {
-        return vec![Annotatable::Item(item)];
+        return if is_stmt {
+            vec![Annotatable::Stmt(P(cx.stmt_item(item.span, item)))]
+        } else {
+            vec![Annotatable::Item(item)]
+        };
     }
 
     let sp = cx.with_def_site_ctxt(item.span);
@@ -550,24 +550,21 @@ fn has_test_signature(cx: &ExtCtxt<'_>, i: &ast::Item) -> bool {
                 return false;
             }
 
-            match (has_output, has_should_panic_attr) {
-                (true, true) => {
-                    sd.span_err(i.span, "functions using `#[should_panic]` must return `()`");
-                    false
-                }
-                (true, false) => {
-                    if !generics.params.is_empty() {
-                        sd.span_err(
-                            i.span,
-                            "functions used as tests must have signature fn() -> ()",
-                        );
-                        false
-                    } else {
-                        true
-                    }
-                }
-                (false, _) => true,
+            if has_should_panic_attr && has_output {
+                sd.span_err(i.span, "functions using `#[should_panic]` must return `()`");
+                return false;
+            }
+
+            if generics.params.iter().any(|param| !matches!(param.kind, GenericParamKind::Lifetime))
+            {
+                sd.span_err(
+                    i.span,
+                    "functions used as tests can not have any non-lifetime generic parameters",
+                );
+                return false;
             }
+
+            true
         }
         _ => {
             // should be unreachable because `is_test_fn_item` should catch all non-fn items
diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs
index 5abb4644e1b..ce10780f9de 100644
--- a/compiler/rustc_codegen_cranelift/src/base.rs
+++ b/compiler/rustc_codegen_cranelift/src/base.rs
@@ -421,7 +421,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
                 target,
                 fn_span,
                 unwind: _,
-                from_hir_call: _,
+                call_source: _,
             } => {
                 fx.tcx.prof.generic_activity("codegen call").run(|| {
                     crate::abi::codegen_terminator_call(
diff --git a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs
index f751d8c179d..13568b198db 100644
--- a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs
+++ b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs
@@ -22,8 +22,8 @@ pub(crate) fn maybe_codegen<'tcx>(
 
     match bin_op {
         BinOp::BitAnd | BinOp::BitOr | BinOp::BitXor => None,
-        BinOp::Add | BinOp::Sub => None,
-        BinOp::Mul => {
+        BinOp::Add | BinOp::AddUnchecked | BinOp::Sub | BinOp::SubUnchecked => None,
+        BinOp::Mul | BinOp::MulUnchecked => {
             let args = [lhs.load_scalar(fx), rhs.load_scalar(fx)];
             let ret_val = fx.lib_call(
                 "__multi3",
@@ -69,7 +69,7 @@ pub(crate) fn maybe_codegen<'tcx>(
             }
         }
         BinOp::Lt | BinOp::Le | BinOp::Eq | BinOp::Ge | BinOp::Gt | BinOp::Ne => None,
-        BinOp::Shl | BinOp::Shr => None,
+        BinOp::Shl | BinOp::ShlUnchecked | BinOp::Shr | BinOp::ShrUnchecked => None,
     }
 }
 
@@ -131,9 +131,10 @@ pub(crate) fn maybe_codegen_checked<'tcx>(
             fx.lib_call(name, param_types, vec![], &args);
             Some(out_place.to_cvalue(fx))
         }
+        BinOp::AddUnchecked | BinOp::SubUnchecked | BinOp::MulUnchecked => unreachable!(),
         BinOp::Offset => unreachable!("offset should only be used on pointers, not 128bit ints"),
         BinOp::Div | BinOp::Rem => unreachable!(),
         BinOp::Lt | BinOp::Le | BinOp::Eq | BinOp::Ge | BinOp::Gt | BinOp::Ne => unreachable!(),
-        BinOp::Shl | BinOp::Shr => unreachable!(),
+        BinOp::Shl | BinOp::ShlUnchecked | BinOp::Shr | BinOp::ShrUnchecked => unreachable!(),
     }
 }
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
index 1e83c30bd67..5862f18299e 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
@@ -472,25 +472,11 @@ fn codegen_regular_intrinsic_call<'tcx>(
             ret.write_cvalue(fx, CValue::by_val(align, usize_layout));
         }
 
-        sym::unchecked_add
-        | sym::unchecked_sub
-        | sym::unchecked_mul
-        | sym::exact_div
-        | sym::unchecked_shl
-        | sym::unchecked_shr => {
+        sym::exact_div => {
             intrinsic_args!(fx, args => (x, y); intrinsic);
 
-            // FIXME trap on overflow
-            let bin_op = match intrinsic {
-                sym::unchecked_add => BinOp::Add,
-                sym::unchecked_sub => BinOp::Sub,
-                sym::unchecked_mul => BinOp::Mul,
-                sym::exact_div => BinOp::Div,
-                sym::unchecked_shl => BinOp::Shl,
-                sym::unchecked_shr => BinOp::Shr,
-                _ => unreachable!(),
-            };
-            let res = crate::num::codegen_int_binop(fx, bin_op, x, y);
+            // FIXME trap on inexact
+            let res = crate::num::codegen_int_binop(fx, BinOp::Div, x, y);
             ret.write_cvalue(fx, res);
         }
         sym::saturating_add | sym::saturating_sub => {
diff --git a/compiler/rustc_codegen_cranelift/src/num.rs b/compiler/rustc_codegen_cranelift/src/num.rs
index ba53e01c7a2..ac1a6cce096 100644
--- a/compiler/rustc_codegen_cranelift/src/num.rs
+++ b/compiler/rustc_codegen_cranelift/src/num.rs
@@ -128,10 +128,11 @@ pub(crate) fn codegen_int_binop<'tcx>(
     let rhs = in_rhs.load_scalar(fx);
 
     let b = fx.bcx.ins();
+    // FIXME trap on overflow for the Unchecked versions
     let val = match bin_op {
-        BinOp::Add => b.iadd(lhs, rhs),
-        BinOp::Sub => b.isub(lhs, rhs),
-        BinOp::Mul => b.imul(lhs, rhs),
+        BinOp::Add | BinOp::AddUnchecked => b.iadd(lhs, rhs),
+        BinOp::Sub | BinOp::SubUnchecked => b.isub(lhs, rhs),
+        BinOp::Mul | BinOp::MulUnchecked => b.imul(lhs, rhs),
         BinOp::Div => {
             if signed {
                 b.sdiv(lhs, rhs)
@@ -149,16 +150,19 @@ pub(crate) fn codegen_int_binop<'tcx>(
         BinOp::BitXor => b.bxor(lhs, rhs),
         BinOp::BitAnd => b.band(lhs, rhs),
         BinOp::BitOr => b.bor(lhs, rhs),
-        BinOp::Shl => b.ishl(lhs, rhs),
-        BinOp::Shr => {
+        BinOp::Shl | BinOp::ShlUnchecked => b.ishl(lhs, rhs),
+        BinOp::Shr | BinOp::ShrUnchecked => {
             if signed {
                 b.sshr(lhs, rhs)
             } else {
                 b.ushr(lhs, rhs)
             }
         }
+        BinOp::Offset => unreachable!("Offset is not an integer operation"),
         // Compare binops handles by `codegen_binop`.
-        _ => unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs.layout().ty, in_rhs.layout().ty),
+        BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge => {
+            unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs.layout().ty, in_rhs.layout().ty);
+        }
     };
 
     CValue::by_val(val, in_lhs.layout())
diff --git a/compiler/rustc_codegen_gcc/tools/generate_intrinsics.py b/compiler/rustc_codegen_gcc/tools/generate_intrinsics.py
index 6188924b0d5..83abe145e64 100644
--- a/compiler/rustc_codegen_gcc/tools/generate_intrinsics.py
+++ b/compiler/rustc_codegen_gcc/tools/generate_intrinsics.py
@@ -3,7 +3,6 @@ import os
 import re
 import sys
 import subprocess
-from os import walk
 
 
 def run_command(command, cwd=None):
@@ -180,7 +179,7 @@ def update_intrinsics(llvm_path, llvmint, llvmint2):
             intrinsics[arch].sort(key=lambda x: (x[0], x[2]))
             out.write('    // {}\n'.format(arch))
             for entry in intrinsics[arch]:
-                if entry[2] == True: # if it is a duplicate
+                if entry[2] is True: # if it is a duplicate
                     out.write('    // [DUPLICATE]: "{}" => "{}",\n'.format(entry[0], entry[1]))
                 elif "_round_mask" in entry[1]:
                     out.write('    // [INVALID CONVERSION]: "{}" => "{}",\n'.format(entry[0], entry[1]))
diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs
index e1abb73a504..5a68075991f 100644
--- a/compiler/rustc_codegen_ssa/src/common.rs
+++ b/compiler/rustc_codegen_ssa/src/common.rs
@@ -132,7 +132,7 @@ pub fn build_langcall<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
 // all shifts). For 32- and 64-bit types, this matches the semantics
 // of Java. (See related discussion on #1877 and #10183.)
 
-pub fn build_unchecked_lshift<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
+pub fn build_masked_lshift<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     bx: &mut Bx,
     lhs: Bx::Value,
     rhs: Bx::Value,
@@ -143,7 +143,7 @@ pub fn build_unchecked_lshift<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     bx.shl(lhs, rhs)
 }
 
-pub fn build_unchecked_rshift<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
+pub fn build_masked_rshift<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     bx: &mut Bx,
     lhs_t: Ty<'tcx>,
     lhs: Bx::Value,
diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs
index a4a8aad8726..0cec560ba45 100644
--- a/compiler/rustc_codegen_ssa/src/mir/block.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/block.rs
@@ -1280,7 +1280,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 destination,
                 target,
                 unwind,
-                from_hir_call: _,
+                call_source: _,
                 fn_span,
             } => self.codegen_call_terminator(
                 helper,
diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
index 9ac2424e76b..8a65dd593b8 100644
--- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
@@ -211,52 +211,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 args[1].val.unaligned_volatile_store(bx, dst);
                 return;
             }
-            | sym::unchecked_shl
-            | sym::unchecked_shr
-            | sym::unchecked_add
-            | sym::unchecked_sub
-            | sym::unchecked_mul
-            | sym::exact_div => {
+            sym::exact_div => {
                 let ty = arg_tys[0];
                 match int_type_width_signed(ty, bx.tcx()) {
-                    Some((_width, signed)) => match name {
-                        sym::exact_div => {
-                            if signed {
-                                bx.exactsdiv(args[0].immediate(), args[1].immediate())
-                            } else {
-                                bx.exactudiv(args[0].immediate(), args[1].immediate())
-                            }
-                        }
-                        sym::unchecked_shl => bx.shl(args[0].immediate(), args[1].immediate()),
-                        sym::unchecked_shr => {
-                            if signed {
-                                bx.ashr(args[0].immediate(), args[1].immediate())
-                            } else {
-                                bx.lshr(args[0].immediate(), args[1].immediate())
-                            }
-                        }
-                        sym::unchecked_add => {
-                            if signed {
-                                bx.unchecked_sadd(args[0].immediate(), args[1].immediate())
-                            } else {
-                                bx.unchecked_uadd(args[0].immediate(), args[1].immediate())
-                            }
-                        }
-                        sym::unchecked_sub => {
-                            if signed {
-                                bx.unchecked_ssub(args[0].immediate(), args[1].immediate())
-                            } else {
-                                bx.unchecked_usub(args[0].immediate(), args[1].immediate())
-                            }
-                        }
-                        sym::unchecked_mul => {
-                            if signed {
-                                bx.unchecked_smul(args[0].immediate(), args[1].immediate())
-                            } else {
-                                bx.unchecked_umul(args[0].immediate(), args[1].immediate())
-                            }
+                    Some((_width, signed)) => {
+                        if signed {
+                            bx.exactsdiv(args[0].immediate(), args[1].immediate())
+                        } else {
+                            bx.exactudiv(args[0].immediate(), args[1].immediate())
                         }
-                        _ => bug!(),
                     },
                     None => {
                         bx.tcx().sess.emit_err(InvalidMonomorphization::BasicIntegerType { span, name, ty });
diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
index 5241a5aee00..2d3d0ec68b8 100644
--- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
@@ -798,6 +798,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     bx.add(lhs, rhs)
                 }
             }
+            mir::BinOp::AddUnchecked => {
+                if is_signed {
+                    bx.unchecked_sadd(lhs, rhs)
+                } else {
+                    bx.unchecked_uadd(lhs, rhs)
+                }
+            }
             mir::BinOp::Sub => {
                 if is_float {
                     bx.fsub(lhs, rhs)
@@ -805,6 +812,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     bx.sub(lhs, rhs)
                 }
             }
+            mir::BinOp::SubUnchecked => {
+                if is_signed {
+                    bx.unchecked_ssub(lhs, rhs)
+                } else {
+                    bx.unchecked_usub(lhs, rhs)
+                }
+            }
             mir::BinOp::Mul => {
                 if is_float {
                     bx.fmul(lhs, rhs)
@@ -812,6 +826,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     bx.mul(lhs, rhs)
                 }
             }
+            mir::BinOp::MulUnchecked => {
+                if is_signed {
+                    bx.unchecked_smul(lhs, rhs)
+                } else {
+                    bx.unchecked_umul(lhs, rhs)
+                }
+            }
             mir::BinOp::Div => {
                 if is_float {
                     bx.fdiv(lhs, rhs)
@@ -848,8 +869,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     bx.inbounds_gep(llty, lhs, &[rhs])
                 }
             }
-            mir::BinOp::Shl => common::build_unchecked_lshift(bx, lhs, rhs),
-            mir::BinOp::Shr => common::build_unchecked_rshift(bx, input_ty, lhs, rhs),
+            mir::BinOp::Shl => common::build_masked_lshift(bx, lhs, rhs),
+            mir::BinOp::ShlUnchecked => {
+                let rhs = base::cast_shift_expr_rhs(bx, lhs, rhs);
+                bx.shl(lhs, rhs)
+            }
+            mir::BinOp::Shr => common::build_masked_rshift(bx, input_ty, lhs, rhs),
+            mir::BinOp::ShrUnchecked => {
+                let rhs = base::cast_shift_expr_rhs(bx, lhs, rhs);
+                if is_signed { bx.ashr(lhs, rhs) } else { bx.lshr(lhs, rhs) }
+            }
             mir::BinOp::Ne
             | mir::BinOp::Lt
             | mir::BinOp::Gt
diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl
index bf660c59cab..e99005316b3 100644
--- a/compiler/rustc_const_eval/messages.ftl
+++ b/compiler/rustc_const_eval/messages.ftl
@@ -210,6 +210,9 @@ const_eval_long_running =
     .label = the const evaluator is currently interpreting this expression
     .help = the constant being evaluated
 
+const_eval_match_eq_non_const = cannot match on `{$ty}` in {const_eval_const_context}s
+    .note = `{$ty}` cannot be compared in compile-time, and therefore cannot be used in `match`es
+
 const_eval_max_num_nodes_in_const = maximum number of nodes exceeded in constant {$global_const_id}
 
 const_eval_memory_access_test = memory access failed
diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs
index eed3091d481..ca38cce710e 100644
--- a/compiler/rustc_const_eval/src/errors.rs
+++ b/compiler/rustc_const_eval/src/errors.rs
@@ -271,6 +271,18 @@ pub struct RawBytesNote {
     pub bytes: String,
 }
 
+// FIXME(fee1-dead) do not use stringly typed `ConstContext`
+
+#[derive(Diagnostic)]
+#[diag(const_eval_match_eq_non_const, code = "E0015")]
+#[note]
+pub struct NonConstMatchEq<'tcx> {
+    #[primary_span]
+    pub span: Span,
+    pub ty: Ty<'tcx>,
+    pub kind: ConstContext,
+}
+
 #[derive(Diagnostic)]
 #[diag(const_eval_for_loop_into_iter_non_const, code = "E0015")]
 pub struct NonConstForLoopIntoIter<'tcx> {
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index 7192bbc00d5..8f4cf23770e 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -234,37 +234,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 let r = self.read_immediate(&args[1])?;
                 self.exact_div(&l, &r, dest)?;
             }
-            sym::unchecked_shl
-            | sym::unchecked_shr
-            | sym::unchecked_add
-            | sym::unchecked_sub
-            | sym::unchecked_mul => {
-                let l = self.read_immediate(&args[0])?;
-                let r = self.read_immediate(&args[1])?;
-                let bin_op = match intrinsic_name {
-                    sym::unchecked_shl => BinOp::Shl,
-                    sym::unchecked_shr => BinOp::Shr,
-                    sym::unchecked_add => BinOp::Add,
-                    sym::unchecked_sub => BinOp::Sub,
-                    sym::unchecked_mul => BinOp::Mul,
-                    _ => bug!(),
-                };
-                let (val, overflowed, _ty) = self.overflowing_binary_op(bin_op, &l, &r)?;
-                if overflowed {
-                    let layout = self.layout_of(substs.type_at(0))?;
-                    let r_val = r.to_scalar().to_bits(layout.size)?;
-                    if let sym::unchecked_shl | sym::unchecked_shr = intrinsic_name {
-                        throw_ub_custom!(
-                            fluent::const_eval_overflow_shift,
-                            val = r_val,
-                            name = intrinsic_name
-                        );
-                    } else {
-                        throw_ub_custom!(fluent::const_eval_overflow, name = intrinsic_name);
-                    }
-                }
-                self.write_scalar(val, dest)?;
-            }
             sym::rotate_left | sym::rotate_right => {
                 // rotate_left: (X << (S % BW)) | (X >> ((BW - S) % BW))
                 // rotate_right: (X << ((BW - S) % BW)) | (X >> (S % BW))
diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs
index 7186148daf0..7bca7efdf5a 100644
--- a/compiler/rustc_const_eval/src/interpret/operator.rs
+++ b/compiler/rustc_const_eval/src/interpret/operator.rs
@@ -3,10 +3,13 @@ use rustc_middle::mir;
 use rustc_middle::mir::interpret::{InterpResult, Scalar};
 use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
 use rustc_middle::ty::{self, FloatTy, Ty};
+use rustc_span::symbol::sym;
 use rustc_target::abi::Abi;
 
 use super::{ImmTy, Immediate, InterpCx, Machine, PlaceTy};
 
+use crate::fluent_generated as fluent;
+
 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// Applies the binary operation `op` to the two operands and writes a tuple of the result
     /// and a boolean signifying the potential overflow to the destination.
@@ -139,8 +142,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     ) -> InterpResult<'tcx, (Scalar<M::Provenance>, bool, Ty<'tcx>)> {
         use rustc_middle::mir::BinOp::*;
 
+        let throw_ub_on_overflow = match bin_op {
+            AddUnchecked => Some(sym::unchecked_add),
+            SubUnchecked => Some(sym::unchecked_sub),
+            MulUnchecked => Some(sym::unchecked_mul),
+            ShlUnchecked => Some(sym::unchecked_shl),
+            ShrUnchecked => Some(sym::unchecked_shr),
+            _ => None,
+        };
+
         // Shift ops can have an RHS with a different numeric type.
-        if bin_op == Shl || bin_op == Shr {
+        if matches!(bin_op, Shl | ShlUnchecked | Shr | ShrUnchecked) {
             let size = u128::from(left_layout.size.bits());
             // Even if `r` is signed, we treat it as if it was unsigned (i.e., we use its
             // zero-extended form). This matches the codegen backend:
@@ -155,6 +167,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             // integers are maximally 128bits wide, so negative shifts *always* overflow and we have
             // consistent results for the same value represented at different bit widths.
             assert!(size <= 128);
+            let original_r = r;
             let overflow = r >= size;
             // The shift offset is implicitly masked to the type size, to make sure this operation
             // is always defined. This is the one MIR operator that does *not* directly map to a
@@ -166,19 +179,28 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             let result = if left_layout.abi.is_signed() {
                 let l = self.sign_extend(l, left_layout) as i128;
                 let result = match bin_op {
-                    Shl => l.checked_shl(r).unwrap(),
-                    Shr => l.checked_shr(r).unwrap(),
+                    Shl | ShlUnchecked => l.checked_shl(r).unwrap(),
+                    Shr | ShrUnchecked => l.checked_shr(r).unwrap(),
                     _ => bug!(),
                 };
                 result as u128
             } else {
                 match bin_op {
-                    Shl => l.checked_shl(r).unwrap(),
-                    Shr => l.checked_shr(r).unwrap(),
+                    Shl | ShlUnchecked => l.checked_shl(r).unwrap(),
+                    Shr | ShrUnchecked => l.checked_shr(r).unwrap(),
                     _ => bug!(),
                 }
             };
             let truncated = self.truncate(result, left_layout);
+
+            if overflow && let Some(intrinsic_name) = throw_ub_on_overflow {
+                throw_ub_custom!(
+                    fluent::const_eval_overflow_shift,
+                    val = original_r,
+                    name = intrinsic_name
+                );
+            }
+
             return Ok((Scalar::from_uint(truncated, left_layout.size), overflow, left_layout.ty));
         }
 
@@ -216,9 +238,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 Rem if r == 0 => throw_ub!(RemainderByZero),
                 Div => Some(i128::overflowing_div),
                 Rem => Some(i128::overflowing_rem),
-                Add => Some(i128::overflowing_add),
-                Sub => Some(i128::overflowing_sub),
-                Mul => Some(i128::overflowing_mul),
+                Add | AddUnchecked => Some(i128::overflowing_add),
+                Sub | SubUnchecked => Some(i128::overflowing_sub),
+                Mul | MulUnchecked => Some(i128::overflowing_mul),
                 _ => None,
             };
             if let Some(op) = op {
@@ -242,11 +264,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 // If that truncation loses any information, we have an overflow.
                 let result = result as u128;
                 let truncated = self.truncate(result, left_layout);
-                return Ok((
-                    Scalar::from_uint(truncated, size),
-                    oflo || self.sign_extend(truncated, left_layout) != result,
-                    left_layout.ty,
-                ));
+                let overflow = oflo || self.sign_extend(truncated, left_layout) != result;
+                if overflow && let Some(intrinsic_name) = throw_ub_on_overflow {
+                    throw_ub_custom!(fluent::const_eval_overflow, name = intrinsic_name);
+                }
+                return Ok((Scalar::from_uint(truncated, size), overflow, left_layout.ty));
             }
         }
 
@@ -263,12 +285,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             BitAnd => (Scalar::from_uint(l & r, size), left_layout.ty),
             BitXor => (Scalar::from_uint(l ^ r, size), left_layout.ty),
 
-            Add | Sub | Mul | Rem | Div => {
+            Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Rem | Div => {
                 assert!(!left_layout.abi.is_signed());
                 let op: fn(u128, u128) -> (u128, bool) = match bin_op {
-                    Add => u128::overflowing_add,
-                    Sub => u128::overflowing_sub,
-                    Mul => u128::overflowing_mul,
+                    Add | AddUnchecked => u128::overflowing_add,
+                    Sub | SubUnchecked => u128::overflowing_sub,
+                    Mul | MulUnchecked => u128::overflowing_mul,
                     Div if r == 0 => throw_ub!(DivisionByZero),
                     Rem if r == 0 => throw_ub!(RemainderByZero),
                     Div => u128::overflowing_div,
@@ -279,11 +301,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 // Truncate to target type.
                 // If that truncation loses any information, we have an overflow.
                 let truncated = self.truncate(result, left_layout);
-                return Ok((
-                    Scalar::from_uint(truncated, size),
-                    oflo || truncated != result,
-                    left_layout.ty,
-                ));
+                let overflow = oflo || truncated != result;
+                if overflow && let Some(intrinsic_name) = throw_ub_on_overflow {
+                    throw_ub_custom!(fluent::const_eval_overflow, name = intrinsic_name);
+                }
+                return Ok((Scalar::from_uint(truncated, size), overflow, left_layout.ty));
             }
 
             _ => span_bug!(
diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs
index 1e60a1e72ea..619da8abb7d 100644
--- a/compiler/rustc_const_eval/src/interpret/step.rs
+++ b/compiler/rustc_const_eval/src/interpret/step.rs
@@ -9,27 +9,7 @@ use rustc_middle::mir::interpret::{InterpResult, Scalar};
 use rustc_middle::ty::layout::LayoutOf;
 
 use super::{ImmTy, InterpCx, Machine};
-
-/// Classify whether an operator is "left-homogeneous", i.e., the LHS has the
-/// same type as the result.
-#[inline]
-fn binop_left_homogeneous(op: mir::BinOp) -> bool {
-    use rustc_middle::mir::BinOp::*;
-    match op {
-        Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Offset | Shl | Shr => true,
-        Eq | Ne | Lt | Le | Gt | Ge => false,
-    }
-}
-/// Classify whether an operator is "right-homogeneous", i.e., the RHS has the
-/// same type as the LHS.
-#[inline]
-fn binop_right_homogeneous(op: mir::BinOp) -> bool {
-    use rustc_middle::mir::BinOp::*;
-    match op {
-        Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Eq | Ne | Lt | Le | Gt | Ge => true,
-        Offset | Shl | Shr => false,
-    }
-}
+use crate::util;
 
 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// Returns `true` as long as there are more things to do.
@@ -179,9 +159,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             }
 
             BinaryOp(bin_op, box (ref left, ref right)) => {
-                let layout = binop_left_homogeneous(bin_op).then_some(dest.layout);
+                let layout = util::binop_left_homogeneous(bin_op).then_some(dest.layout);
                 let left = self.read_immediate(&self.eval_operand(left, layout)?)?;
-                let layout = binop_right_homogeneous(bin_op).then_some(left.layout);
+                let layout = util::binop_right_homogeneous(bin_op).then_some(left.layout);
                 let right = self.read_immediate(&self.eval_operand(right, layout)?)?;
                 self.binop_ignore_overflow(bin_op, &left, &right, &dest)?;
             }
@@ -189,7 +169,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             CheckedBinaryOp(bin_op, box (ref left, ref right)) => {
                 // Due to the extra boolean in the result, we can never reuse the `dest.layout`.
                 let left = self.read_immediate(&self.eval_operand(left, None)?)?;
-                let layout = binop_right_homogeneous(bin_op).then_some(left.layout);
+                let layout = util::binop_right_homogeneous(bin_op).then_some(left.layout);
                 let right = self.read_immediate(&self.eval_operand(right, layout)?)?;
                 self.binop_with_overflow(bin_op, &left, &right, &dest)?;
             }
diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs
index 7269ff8d53c..719d8a14b41 100644
--- a/compiler/rustc_const_eval/src/interpret/terminator.rs
+++ b/compiler/rustc_const_eval/src/interpret/terminator.rs
@@ -62,7 +62,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 destination,
                 target,
                 unwind,
-                from_hir_call: _,
+                call_source: _,
                 fn_span: _,
             } => {
                 let old_stack = self.frame_idx();
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
index 106cf111474..2aaf1340eb4 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
@@ -702,7 +702,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
         self.super_terminator(terminator, location);
 
         match &terminator.kind {
-            TerminatorKind::Call { func, args, fn_span, from_hir_call, .. } => {
+            TerminatorKind::Call { func, args, fn_span, call_source, .. } => {
                 let ConstCx { tcx, body, param_env, .. } = *self.ccx;
                 let caller = self.def_id();
 
@@ -755,7 +755,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
                             callee,
                             substs,
                             span: *fn_span,
-                            from_hir_call: *from_hir_call,
+                            call_source: *call_source,
                             feature: Some(sym::const_trait_impl),
                         });
                         return;
@@ -797,7 +797,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
                                     callee,
                                     substs,
                                     span: *fn_span,
-                                    from_hir_call: *from_hir_call,
+                                    call_source: *call_source,
                                     feature: None,
                                 });
 
@@ -823,7 +823,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
                                     callee,
                                     substs,
                                     span: *fn_span,
-                                    from_hir_call: *from_hir_call,
+                                    call_source: *call_source,
                                     feature: None,
                                 });
                                 return;
@@ -866,7 +866,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
                                     callee,
                                     substs,
                                     span: *fn_span,
-                                    from_hir_call: *from_hir_call,
+                                    call_source: *call_source,
                                     feature: None,
                                 });
                                 return;
@@ -926,7 +926,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
                             callee,
                             substs,
                             span: *fn_span,
-                            from_hir_call: *from_hir_call,
+                            call_source: *call_source,
                             feature: None,
                         });
                         return;
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
index 236e43bdfcc..32bd9cda6f2 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
@@ -7,7 +7,7 @@ use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_infer::traits::{ImplSource, Obligation, ObligationCause};
-use rustc_middle::mir;
+use rustc_middle::mir::{self, CallSource};
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
 use rustc_middle::ty::{suggest_constraining_type_param, Adt, Closure, FnDef, FnPtr, Param, Ty};
@@ -100,7 +100,7 @@ pub struct FnCallNonConst<'tcx> {
     pub callee: DefId,
     pub substs: SubstsRef<'tcx>,
     pub span: Span,
-    pub from_hir_call: bool,
+    pub call_source: CallSource,
     pub feature: Option<Symbol>,
 }
 
@@ -110,7 +110,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
         ccx: &ConstCx<'_, 'tcx>,
         _: Span,
     ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
-        let FnCallNonConst { caller, callee, substs, span, from_hir_call, feature } = *self;
+        let FnCallNonConst { caller, callee, substs, span, call_source, feature } = *self;
         let ConstCx { tcx, param_env, .. } = *ccx;
 
         let diag_trait = |err, self_ty: Ty<'_>, trait_id| {
@@ -157,7 +157,8 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
             }
         };
 
-        let call_kind = call_kind(tcx, ccx.param_env, callee, substs, span, from_hir_call, None);
+        let call_kind =
+            call_kind(tcx, ccx.param_env, callee, substs, span, call_source.from_hir_call(), None);
 
         debug!(?call_kind);
 
@@ -219,48 +220,59 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
                 err
             }
             CallKind::Operator { trait_id, self_ty, .. } => {
-                let mut sugg = None;
-
-                if Some(trait_id) == ccx.tcx.lang_items().eq_trait() {
-                    match (substs[0].unpack(), substs[1].unpack()) {
-                        (GenericArgKind::Type(self_ty), GenericArgKind::Type(rhs_ty))
-                            if self_ty == rhs_ty
-                                && self_ty.is_ref()
-                                && self_ty.peel_refs().is_primitive() =>
-                        {
-                            let mut num_refs = 0;
-                            let mut tmp_ty = self_ty;
-                            while let rustc_middle::ty::Ref(_, inner_ty, _) = tmp_ty.kind() {
-                                num_refs += 1;
-                                tmp_ty = *inner_ty;
-                            }
-                            let deref = "*".repeat(num_refs);
-
-                            if let Ok(call_str) = ccx.tcx.sess.source_map().span_to_snippet(span) {
-                                if let Some(eq_idx) = call_str.find("==") {
-                                    if let Some(rhs_idx) =
-                                        call_str[(eq_idx + 2)..].find(|c: char| !c.is_whitespace())
-                                    {
-                                        let rhs_pos =
-                                            span.lo() + BytePos::from_usize(eq_idx + 2 + rhs_idx);
-                                        let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos);
-                                        sugg = Some(errors::ConsiderDereferencing {
-                                            deref,
-                                            span: span.shrink_to_lo(),
-                                            rhs_span,
-                                        });
+                let mut err = if let CallSource::MatchCmp = call_source {
+                    tcx.sess.create_err(errors::NonConstMatchEq {
+                        span,
+                        kind: ccx.const_kind(),
+                        ty: self_ty,
+                    })
+                } else {
+                    let mut sugg = None;
+
+                    if Some(trait_id) == ccx.tcx.lang_items().eq_trait() {
+                        match (substs[0].unpack(), substs[1].unpack()) {
+                            (GenericArgKind::Type(self_ty), GenericArgKind::Type(rhs_ty))
+                                if self_ty == rhs_ty
+                                    && self_ty.is_ref()
+                                    && self_ty.peel_refs().is_primitive() =>
+                            {
+                                let mut num_refs = 0;
+                                let mut tmp_ty = self_ty;
+                                while let rustc_middle::ty::Ref(_, inner_ty, _) = tmp_ty.kind() {
+                                    num_refs += 1;
+                                    tmp_ty = *inner_ty;
+                                }
+                                let deref = "*".repeat(num_refs);
+
+                                if let Ok(call_str) =
+                                    ccx.tcx.sess.source_map().span_to_snippet(span)
+                                {
+                                    if let Some(eq_idx) = call_str.find("==") {
+                                        if let Some(rhs_idx) = call_str[(eq_idx + 2)..]
+                                            .find(|c: char| !c.is_whitespace())
+                                        {
+                                            let rhs_pos = span.lo()
+                                                + BytePos::from_usize(eq_idx + 2 + rhs_idx);
+                                            let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos);
+                                            sugg = Some(errors::ConsiderDereferencing {
+                                                deref,
+                                                span: span.shrink_to_lo(),
+                                                rhs_span,
+                                            });
+                                        }
                                     }
                                 }
                             }
+                            _ => {}
                         }
-                        _ => {}
                     }
-                }
-                let mut err = tcx.sess.create_err(errors::NonConstOperator {
-                    span,
-                    kind: ccx.const_kind(),
-                    sugg,
-                });
+                    tcx.sess.create_err(errors::NonConstOperator {
+                        span,
+                        kind: ccx.const_kind(),
+                        sugg,
+                    })
+                };
+
                 diag_trait(&mut err, self_ty, trait_id);
                 err
             }
diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs
index 44b143c77f3..f02464c7f99 100644
--- a/compiler/rustc_const_eval/src/transform/promote_consts.rs
+++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs
@@ -569,13 +569,18 @@ impl<'tcx> Validator<'_, 'tcx> {
                     | BinOp::Gt
                     | BinOp::Offset
                     | BinOp::Add
+                    | BinOp::AddUnchecked
                     | BinOp::Sub
+                    | BinOp::SubUnchecked
                     | BinOp::Mul
+                    | BinOp::MulUnchecked
                     | BinOp::BitXor
                     | BinOp::BitAnd
                     | BinOp::BitOr
                     | BinOp::Shl
-                    | BinOp::Shr => {}
+                    | BinOp::ShlUnchecked
+                    | BinOp::Shr
+                    | BinOp::ShrUnchecked => {}
                 }
 
                 self.validate_operand(lhs)?;
@@ -792,7 +797,9 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
             };
 
             match terminator.kind {
-                TerminatorKind::Call { mut func, mut args, from_hir_call, fn_span, .. } => {
+                TerminatorKind::Call {
+                    mut func, mut args, call_source: desugar, fn_span, ..
+                } => {
                     self.visit_operand(&mut func, loc);
                     for arg in &mut args {
                         self.visit_operand(arg, loc);
@@ -808,7 +815,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
                             unwind: UnwindAction::Continue,
                             destination: Place::from(new_temp),
                             target: Some(new_target),
-                            from_hir_call,
+                            call_source: desugar,
                             fn_span,
                         },
                         source_info: SourceInfo::outermost(terminator.source_info.span),
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index 3c350e25ba6..f197541da5b 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -498,8 +498,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
 
     fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
         macro_rules! check_kinds {
-            ($t:expr, $text:literal, $($patterns:tt)*) => {
-                if !matches!(($t).kind(), $($patterns)*) {
+            ($t:expr, $text:literal, $typat:pat) => {
+                if !matches!(($t).kind(), $typat) {
                     self.fail(location, format!($text, $t));
                 }
             };
@@ -527,6 +527,25 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                 use BinOp::*;
                 let a = vals.0.ty(&self.body.local_decls, self.tcx);
                 let b = vals.1.ty(&self.body.local_decls, self.tcx);
+                if crate::util::binop_right_homogeneous(*op) {
+                    if let Eq | Lt | Le | Ne | Ge | Gt = op {
+                        // The function pointer types can have lifetimes
+                        if !self.mir_assign_valid_types(a, b) {
+                            self.fail(
+                                location,
+                                format!("Cannot {op:?} compare incompatible types {a:?} and {b:?}"),
+                            );
+                        }
+                    } else if a != b {
+                        self.fail(
+                            location,
+                            format!(
+                                "Cannot perform binary op {op:?} on unequal types {a:?} and {b:?}"
+                            ),
+                        );
+                    }
+                }
+
                 match op {
                     Offset => {
                         check_kinds!(a, "Cannot offset non-pointer type {:?}", ty::RawPtr(..));
@@ -538,7 +557,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                         for x in [a, b] {
                             check_kinds!(
                                 x,
-                                "Cannot compare type {:?}",
+                                "Cannot {op:?} compare type {:?}",
                                 ty::Bool
                                     | ty::Char
                                     | ty::Int(..)
@@ -548,19 +567,13 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                                     | ty::FnPtr(..)
                             )
                         }
-                        // The function pointer types can have lifetimes
-                        if !self.mir_assign_valid_types(a, b) {
-                            self.fail(
-                                location,
-                                format!("Cannot compare unequal types {:?} and {:?}", a, b),
-                            );
-                        }
                     }
-                    Shl | Shr => {
+                    AddUnchecked | SubUnchecked | MulUnchecked | Shl | ShlUnchecked | Shr
+                    | ShrUnchecked => {
                         for x in [a, b] {
                             check_kinds!(
                                 x,
-                                "Cannot shift non-integer type {:?}",
+                                "Cannot {op:?} non-integer type {:?}",
                                 ty::Uint(..) | ty::Int(..)
                             )
                         }
@@ -569,37 +582,19 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                         for x in [a, b] {
                             check_kinds!(
                                 x,
-                                "Cannot perform bitwise op on type {:?}",
+                                "Cannot perform bitwise op {op:?} on type {:?}",
                                 ty::Uint(..) | ty::Int(..) | ty::Bool
                             )
                         }
-                        if a != b {
-                            self.fail(
-                                location,
-                                format!(
-                                    "Cannot perform bitwise op on unequal types {:?} and {:?}",
-                                    a, b
-                                ),
-                            );
-                        }
                     }
                     Add | Sub | Mul | Div | Rem => {
                         for x in [a, b] {
                             check_kinds!(
                                 x,
-                                "Cannot perform arithmetic on type {:?}",
+                                "Cannot perform arithmetic {op:?} on type {:?}",
                                 ty::Uint(..) | ty::Int(..) | ty::Float(..)
                             )
                         }
-                        if a != b {
-                            self.fail(
-                                location,
-                                format!(
-                                    "Cannot perform arithmetic on unequal types {:?} and {:?}",
-                                    a, b
-                                ),
-                            );
-                        }
                     }
                 }
             }
diff --git a/compiler/rustc_const_eval/src/util/mod.rs b/compiler/rustc_const_eval/src/util/mod.rs
index 7641f560714..289e3422595 100644
--- a/compiler/rustc_const_eval/src/util/mod.rs
+++ b/compiler/rustc_const_eval/src/util/mod.rs
@@ -1,3 +1,5 @@
+use rustc_middle::mir;
+
 mod alignment;
 mod check_validity_requirement;
 mod compare_types;
@@ -7,3 +9,27 @@ pub use self::alignment::is_disaligned;
 pub use self::check_validity_requirement::check_validity_requirement;
 pub use self::compare_types::{is_equal_up_to_subtyping, is_subtype};
 pub use self::type_name::type_name;
+
+/// Classify whether an operator is "left-homogeneous", i.e., the LHS has the
+/// same type as the result.
+#[inline]
+pub(crate) fn binop_left_homogeneous(op: mir::BinOp) -> bool {
+    use rustc_middle::mir::BinOp::*;
+    match op {
+        Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor
+        | BitAnd | BitOr | Offset | Shl | ShlUnchecked | Shr | ShrUnchecked => true,
+        Eq | Ne | Lt | Le | Gt | Ge => false,
+    }
+}
+
+/// Classify whether an operator is "right-homogeneous", i.e., the RHS has the
+/// same type as the LHS.
+#[inline]
+pub(crate) fn binop_right_homogeneous(op: mir::BinOp) -> bool {
+    use rustc_middle::mir::BinOp::*;
+    match op {
+        Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor
+        | BitAnd | BitOr | Eq | Ne | Lt | Le | Gt | Ge => true,
+        Offset | Shl | ShlUnchecked | Shr | ShrUnchecked => false,
+    }
+}
diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs
index c9e74896ac0..d8e9e5a0152 100644
--- a/compiler/rustc_hir_analysis/src/check/mod.rs
+++ b/compiler/rustc_hir_analysis/src/check/mod.rs
@@ -403,7 +403,32 @@ fn fn_sig_suggestion<'tcx>(
         .flatten()
         .collect::<Vec<String>>()
         .join(", ");
-    let output = sig.output();
+    let mut output = sig.output();
+
+    let asyncness = if tcx.asyncness(assoc.def_id).is_async() {
+        output = if let ty::Alias(_, alias_ty) = *output.kind() {
+            tcx.explicit_item_bounds(alias_ty.def_id)
+                .subst_iter_copied(tcx, alias_ty.substs)
+                .find_map(|(bound, _)| {
+                    bound.to_opt_poly_projection_pred()?.no_bound_vars()?.term.ty()
+                })
+                .unwrap_or_else(|| {
+                    span_bug!(
+                        ident.span,
+                        "expected async fn to have `impl Future` output, but it returns {output}"
+                    )
+                })
+        } else {
+            span_bug!(
+                ident.span,
+                "expected async fn to have `impl Future` output, but it returns {output}"
+            )
+        };
+        "async "
+    } else {
+        ""
+    };
+
     let output = if !output.is_unit() { format!(" -> {output}") } else { String::new() };
 
     let unsafety = sig.unsafety.prefix_str();
@@ -414,7 +439,9 @@ fn fn_sig_suggestion<'tcx>(
     // lifetimes between the `impl` and the `trait`, but this should be good enough to
     // fill in a significant portion of the missing code, and other subsequent
     // suggestions can help the user fix the code.
-    format!("{unsafety}fn {ident}{generics}({args}){output}{where_clauses} {{ todo!() }}")
+    format!(
+        "{unsafety}{asyncness}fn {ident}{generics}({args}){output}{where_clauses} {{ todo!() }}"
+    )
 }
 
 pub fn ty_kind_suggestion(ty: Ty<'_>) -> Option<&'static str> {
diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs
index b62f689ec6b..9ce03060e0f 100644
--- a/compiler/rustc_hir_typeck/src/demand.rs
+++ b/compiler/rustc_hir_typeck/src/demand.rs
@@ -370,13 +370,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     // Fudge the receiver, so we can do new inference on it.
                     let possible_rcvr_ty = possible_rcvr_ty.fold_with(&mut fudger);
                     let method = self
-                        .lookup_method(
+                        .lookup_method_for_diagnostic(
                             possible_rcvr_ty,
                             segment,
                             DUMMY_SP,
                             call_expr,
                             binding,
-                            args,
                         )
                         .ok()?;
                     // Unify the method signature with our incompatible arg, to
@@ -435,14 +434,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let Some(rcvr_ty) = self.node_ty_opt(rcvr.hir_id) else { continue; };
                 let rcvr_ty = rcvr_ty.fold_with(&mut fudger);
                 let Ok(method) =
-                    self.lookup_method(rcvr_ty, segment, DUMMY_SP, parent_expr, rcvr, args)
+                    self.lookup_method_for_diagnostic(rcvr_ty, segment, DUMMY_SP, parent_expr, rcvr)
                 else {
                     continue;
                 };
 
                 let ideal_rcvr_ty = rcvr_ty.fold_with(&mut fudger);
                 let ideal_method = self
-                    .lookup_method(ideal_rcvr_ty, segment, DUMMY_SP, parent_expr, rcvr, args)
+                    .lookup_method_for_diagnostic(ideal_rcvr_ty, segment, DUMMY_SP, parent_expr, rcvr)
                     .ok()
                     .and_then(|method| {
                         let _ = self.at(&ObligationCause::dummy(), self.param_env)
diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs
index 98529b66602..6cd7bd5d196 100644
--- a/compiler/rustc_hir_typeck/src/method/confirm.rs
+++ b/compiler/rustc_hir_typeck/src/method/confirm.rs
@@ -26,6 +26,7 @@ struct ConfirmContext<'a, 'tcx> {
     span: Span,
     self_expr: &'tcx hir::Expr<'tcx>,
     call_expr: &'tcx hir::Expr<'tcx>,
+    skip_record_for_diagnostics: bool,
 }
 
 impl<'a, 'tcx> Deref for ConfirmContext<'a, 'tcx> {
@@ -59,6 +60,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let mut confirm_cx = ConfirmContext::new(self, span, self_expr, call_expr);
         confirm_cx.confirm(unadjusted_self_ty, pick, segment)
     }
+
+    pub fn confirm_method_for_diagnostic(
+        &self,
+        span: Span,
+        self_expr: &'tcx hir::Expr<'tcx>,
+        call_expr: &'tcx hir::Expr<'tcx>,
+        unadjusted_self_ty: Ty<'tcx>,
+        pick: &probe::Pick<'tcx>,
+        segment: &hir::PathSegment<'_>,
+    ) -> ConfirmResult<'tcx> {
+        let mut confirm_cx = ConfirmContext::new(self, span, self_expr, call_expr);
+        confirm_cx.skip_record_for_diagnostics = true;
+        confirm_cx.confirm(unadjusted_self_ty, pick, segment)
+    }
 }
 
 impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
@@ -68,7 +83,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
         self_expr: &'tcx hir::Expr<'tcx>,
         call_expr: &'tcx hir::Expr<'tcx>,
     ) -> ConfirmContext<'a, 'tcx> {
-        ConfirmContext { fcx, span, self_expr, call_expr }
+        ConfirmContext { fcx, span, self_expr, call_expr, skip_record_for_diagnostics: false }
     }
 
     fn confirm(
@@ -219,7 +234,9 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
         self.register_predicates(autoderef.into_obligations());
 
         // Write out the final adjustments.
-        self.apply_adjustments(self.self_expr, adjustments);
+        if !self.skip_record_for_diagnostics {
+            self.apply_adjustments(self.self_expr, adjustments);
+        }
 
         target
     }
@@ -453,7 +470,10 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
             });
 
             debug!("instantiate_method_substs: user_type_annotation={:?}", user_type_annotation);
-            self.fcx.write_user_type_annotation(self.call_expr.hir_id, user_type_annotation);
+
+            if !self.skip_record_for_diagnostics {
+                self.fcx.write_user_type_annotation(self.call_expr.hir_id, user_type_annotation);
+            }
         }
 
         self.normalize(self.span, substs)
diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs
index cca97d10517..59736b42cf7 100644
--- a/compiler/rustc_hir_typeck/src/method/mod.rs
+++ b/compiler/rustc_hir_typeck/src/method/mod.rs
@@ -254,6 +254,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         Ok(result.callee)
     }
 
+    pub fn lookup_method_for_diagnostic(
+        &self,
+        self_ty: Ty<'tcx>,
+        segment: &hir::PathSegment<'_>,
+        span: Span,
+        call_expr: &'tcx hir::Expr<'tcx>,
+        self_expr: &'tcx hir::Expr<'tcx>,
+    ) -> Result<MethodCallee<'tcx>, MethodError<'tcx>> {
+        let pick = self.lookup_probe_for_diagnostic(
+            segment.ident,
+            self_ty,
+            call_expr,
+            ProbeScope::TraitsInScope,
+            None,
+        )?;
+
+        Ok(self
+            .confirm_method_for_diagnostic(span, self_expr, call_expr, self_ty, &pick, segment)
+            .callee)
+    }
+
     #[instrument(level = "debug", skip(self, call_expr))]
     pub fn lookup_probe(
         &self,
diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs
index 152c56572b6..12cb86d7d72 100644
--- a/compiler/rustc_infer/src/infer/combine.rs
+++ b/compiler/rustc_infer/src/infer/combine.rs
@@ -124,13 +124,10 @@ impl<'tcx> InferCtxt<'tcx> {
             }
 
             // During coherence, opaque types should be treated as *possibly*
-            // equal to each other, even if their generic params differ, as
-            // they could resolve to the same hidden type, even for different
-            // generic params.
-            (
-                &ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }),
-                &ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }),
-            ) if self.intercrate && a_def_id == b_def_id => {
+            // equal to any other type (except for possibly itself). This is an
+            // extremely heavy hammer, but can be relaxed in a fowards-compatible
+            // way later.
+            (&ty::Alias(ty::Opaque, _), _) | (_, &ty::Alias(ty::Opaque, _)) if self.intercrate => {
                 relation.register_predicates([ty::Binder::dummy(ty::PredicateKind::Ambiguous)]);
                 Ok(a)
             }
diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs
index 6b8293f90f1..b19f685a4a4 100644
--- a/compiler/rustc_infer/src/infer/opaque_types.rs
+++ b/compiler/rustc_infer/src/infer/opaque_types.rs
@@ -536,7 +536,8 @@ impl<'tcx> InferCtxt<'tcx> {
         )?;
 
         self.add_item_bounds_for_hidden_type(
-            opaque_type_key,
+            opaque_type_key.def_id.to_def_id(),
+            opaque_type_key.substs,
             cause,
             param_env,
             hidden_ty,
@@ -598,7 +599,8 @@ impl<'tcx> InferCtxt<'tcx> {
 
     pub fn add_item_bounds_for_hidden_type(
         &self,
-        OpaqueTypeKey { def_id, substs }: OpaqueTypeKey<'tcx>,
+        def_id: DefId,
+        substs: ty::SubstsRef<'tcx>,
         cause: ObligationCause<'tcx>,
         param_env: ty::ParamEnv<'tcx>,
         hidden_ty: Ty<'tcx>,
@@ -631,7 +633,7 @@ impl<'tcx> InferCtxt<'tcx> {
                     // Replace all other mentions of the same opaque type with the hidden type,
                     // as the bounds must hold on the hidden type after all.
                     ty::Alias(ty::Opaque, ty::AliasTy { def_id: def_id2, substs: substs2, .. })
-                        if def_id.to_def_id() == def_id2 && substs == substs2 =>
+                        if def_id == def_id2 && substs == substs2 =>
                     {
                         hidden_ty
                     }
@@ -640,7 +642,7 @@ impl<'tcx> InferCtxt<'tcx> {
                     ty::Alias(
                         ty::Projection,
                         ty::AliasTy { def_id: def_id2, substs: substs2, .. },
-                    ) if def_id.to_def_id() == def_id2 && substs == substs2 => hidden_ty,
+                    ) if def_id == def_id2 && substs == substs2 => hidden_ty,
                     _ => ty,
                 },
                 lt_op: |lt| lt,
diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs
index 1a65f74f4fe..10f368fc41c 100644
--- a/compiler/rustc_middle/src/mir/syntax.rs
+++ b/compiler/rustc_middle/src/mir/syntax.rs
@@ -512,6 +512,31 @@ pub struct CopyNonOverlapping<'tcx> {
     pub count: Operand<'tcx>,
 }
 
+/// Represents how a `TerminatorKind::Call` was constructed, used for diagnostics
+#[derive(Clone, Copy, TyEncodable, TyDecodable, Debug, PartialEq, Hash, HashStable)]
+#[derive(TypeFoldable, TypeVisitable)]
+pub enum CallSource {
+    /// This came from something such as `a > b` or `a + b`. In THIR, if `from_hir_call`
+    /// is false then this is the desugaring.
+    OverloadedOperator,
+    /// This was from comparison generated by a match, used by const-eval for better errors
+    /// when the comparison cannot be done in compile time.
+    ///
+    /// (see <https://github.com/rust-lang/rust/issues/90237>)
+    MatchCmp,
+    /// Other types of desugaring that did not come from the HIR, but we don't care about
+    /// for diagnostics (yet).
+    Misc,
+    /// Normal function call, no special source
+    Normal,
+}
+
+impl CallSource {
+    pub fn from_hir_call(self) -> bool {
+        matches!(self, CallSource::Normal)
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////
 // Terminators
 
@@ -638,11 +663,10 @@ pub enum TerminatorKind<'tcx> {
         target: Option<BasicBlock>,
         /// Action to be taken if the call unwinds.
         unwind: UnwindAction,
-        /// `true` if this is from a call in HIR rather than from an overloaded
-        /// operator. True for overloaded function call.
-        from_hir_call: bool,
+        /// Where this call came from in HIR/THIR.
+        call_source: CallSource,
         /// This `Span` is the span of the function, without the dot and receiver
-        /// (e.g. `foo(a, b)` in `x.foo(a, b)`
+        /// e.g. `foo(a, b)` in `x.foo(a, b)`
         fn_span: Span,
     },
 
@@ -1267,10 +1291,16 @@ pub enum UnOp {
 pub enum BinOp {
     /// The `+` operator (addition)
     Add,
+    /// Like `Add`, but with UB on overflow.  (Integers only.)
+    AddUnchecked,
     /// The `-` operator (subtraction)
     Sub,
+    /// Like `Sub`, but with UB on overflow.  (Integers only.)
+    SubUnchecked,
     /// The `*` operator (multiplication)
     Mul,
+    /// Like `Mul`, but with UB on overflow.  (Integers only.)
+    MulUnchecked,
     /// The `/` operator (division)
     ///
     /// For integer types, division by zero is UB, as is `MIN / -1` for signed.
@@ -1296,10 +1326,17 @@ pub enum BinOp {
     ///
     /// The offset is truncated to the size of the first operand before shifting.
     Shl,
+    /// Like `Shl`, but is UB if the RHS >= LHS::BITS
+    ShlUnchecked,
     /// The `>>` operator (shift right)
     ///
     /// The offset is truncated to the size of the first operand before shifting.
+    ///
+    /// This is an arithmetic shift if the LHS is signed
+    /// and a logical shift if the LHS is unsigned.
     Shr,
+    /// Like `Shl`, but is UB if the RHS >= LHS::BITS
+    ShrUnchecked,
     /// The `==` operator (equality)
     Eq,
     /// The `<` operator (less than)
diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs
index 5ca82413448..4a16abdd4e3 100644
--- a/compiler/rustc_middle/src/mir/tcx.rs
+++ b/compiler/rustc_middle/src/mir/tcx.rs
@@ -235,8 +235,11 @@ impl<'tcx> BinOp {
         // FIXME: handle SIMD correctly
         match self {
             &BinOp::Add
+            | &BinOp::AddUnchecked
             | &BinOp::Sub
+            | &BinOp::SubUnchecked
             | &BinOp::Mul
+            | &BinOp::MulUnchecked
             | &BinOp::Div
             | &BinOp::Rem
             | &BinOp::BitXor
@@ -246,7 +249,11 @@ impl<'tcx> BinOp {
                 assert_eq!(lhs_ty, rhs_ty);
                 lhs_ty
             }
-            &BinOp::Shl | &BinOp::Shr | &BinOp::Offset => {
+            &BinOp::Shl
+            | &BinOp::ShlUnchecked
+            | &BinOp::Shr
+            | &BinOp::ShrUnchecked
+            | &BinOp::Offset => {
                 lhs_ty // lhs_ty can be != rhs_ty
             }
             &BinOp::Eq | &BinOp::Lt | &BinOp::Le | &BinOp::Ne | &BinOp::Ge | &BinOp::Gt => {
@@ -293,7 +300,14 @@ impl BinOp {
             BinOp::Gt => hir::BinOpKind::Gt,
             BinOp::Le => hir::BinOpKind::Le,
             BinOp::Ge => hir::BinOpKind::Ge,
-            BinOp::Offset => unreachable!(),
+            BinOp::AddUnchecked
+            | BinOp::SubUnchecked
+            | BinOp::MulUnchecked
+            | BinOp::ShlUnchecked
+            | BinOp::ShrUnchecked
+            | BinOp::Offset => {
+                unreachable!()
+            }
         }
     }
 }
diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs
index 8d44e929afd..ce55b770cbc 100644
--- a/compiler/rustc_middle/src/mir/visit.rs
+++ b/compiler/rustc_middle/src/mir/visit.rs
@@ -519,7 +519,7 @@ macro_rules! make_mir_visitor {
                         destination,
                         target: _,
                         unwind: _,
-                        from_hir_call: _,
+                        call_source: _,
                         fn_span: _
                     } => {
                         self.visit_operand(func, location);
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index 492b7228488..73959038582 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -981,7 +981,7 @@ pub enum CodegenObligationError {
     FulfillmentError,
 }
 
-#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
+#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, HashStable, TypeFoldable, TypeVisitable)]
 pub enum DefiningAnchor {
     /// `DefId` of the item.
     Bind(LocalDefId),
@@ -991,3 +991,9 @@ pub enum DefiningAnchor {
     /// Used to catch type mismatch errors when handling opaque types.
     Error,
 }
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, HashStable)]
+pub enum IsNormalizesToHack {
+    Yes,
+    No,
+}
diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs
index eae5a280e11..60a38747fdf 100644
--- a/compiler/rustc_middle/src/traits/query.rs
+++ b/compiler/rustc_middle/src/traits/query.rs
@@ -92,7 +92,7 @@ pub type CanonicalTypeOpProvePredicateGoal<'tcx> =
 pub type CanonicalTypeOpNormalizeGoal<'tcx, T> =
     Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Normalize<T>>>;
 
-#[derive(Copy, Clone, Debug, HashStable, PartialEq, Eq)]
+#[derive(Copy, Clone, Debug, Hash, HashStable, PartialEq, Eq)]
 pub struct NoSolution;
 
 impl<'tcx> From<TypeError<'tcx>> for NoSolution {
diff --git a/compiler/rustc_middle/src/traits/solve.rs b/compiler/rustc_middle/src/traits/solve.rs
index 2c5b64a59cd..af482a88960 100644
--- a/compiler/rustc_middle/src/traits/solve.rs
+++ b/compiler/rustc_middle/src/traits/solve.rs
@@ -11,6 +11,8 @@ use crate::ty::{
     TypeVisitor,
 };
 
+pub mod inspect;
+
 pub type EvaluationCache<'tcx> = Cache<CanonicalInput<'tcx>, QueryResult<'tcx>>;
 
 /// A goal is a statement, i.e. `predicate`, we want to prove
@@ -18,7 +20,7 @@ pub type EvaluationCache<'tcx> = Cache<CanonicalInput<'tcx>, QueryResult<'tcx>>;
 ///
 /// Most of the time the `param_env` contains the `where`-bounds of the function
 /// we're currently typechecking while the `predicate` is some trait bound.
-#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
+#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, HashStable, TypeFoldable, TypeVisitable)]
 pub struct Goal<'tcx, P> {
     pub predicate: P,
     pub param_env: ty::ParamEnv<'tcx>,
@@ -39,7 +41,7 @@ impl<'tcx, P> Goal<'tcx, P> {
     }
 }
 
-#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
+#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, HashStable, TypeFoldable, TypeVisitable)]
 pub struct Response<'tcx> {
     pub certainty: Certainty,
     pub var_values: CanonicalVarValues<'tcx>,
@@ -47,7 +49,7 @@ pub struct Response<'tcx> {
     pub external_constraints: ExternalConstraints<'tcx>,
 }
 
-#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
+#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, HashStable, TypeFoldable, TypeVisitable)]
 pub enum Certainty {
     Yes,
     Maybe(MaybeCause),
@@ -86,7 +88,7 @@ impl Certainty {
 }
 
 /// Why we failed to evaluate a goal.
-#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
+#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, HashStable, TypeFoldable, TypeVisitable)]
 pub enum MaybeCause {
     /// We failed due to ambiguity. This ambiguity can either
     /// be a true ambiguity, i.e. there are multiple different answers,
@@ -96,7 +98,7 @@ pub enum MaybeCause {
     Overflow,
 }
 
-#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
+#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, HashStable, TypeFoldable, TypeVisitable)]
 pub struct QueryInput<'tcx, T> {
     pub goal: Goal<'tcx, T>,
     pub anchor: DefiningAnchor,
@@ -104,12 +106,12 @@ pub struct QueryInput<'tcx, T> {
 }
 
 /// Additional constraints returned on success.
-#[derive(Debug, PartialEq, Eq, Clone, Hash, Default)]
+#[derive(Debug, PartialEq, Eq, Clone, Hash, HashStable, Default)]
 pub struct PredefinedOpaquesData<'tcx> {
     pub opaque_types: Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>,
 }
 
-#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
+#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, HashStable)]
 pub struct PredefinedOpaques<'tcx>(pub(crate) Interned<'tcx, PredefinedOpaquesData<'tcx>>);
 
 impl<'tcx> std::ops::Deref for PredefinedOpaques<'tcx> {
@@ -132,7 +134,7 @@ pub type CanonicalResponse<'tcx> = Canonical<'tcx, Response<'tcx>>;
 /// solver, merge the two responses again.
 pub type QueryResult<'tcx> = Result<CanonicalResponse<'tcx>, NoSolution>;
 
-#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
+#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, HashStable)]
 pub struct ExternalConstraints<'tcx>(pub(crate) Interned<'tcx, ExternalConstraintsData<'tcx>>);
 
 impl<'tcx> std::ops::Deref for ExternalConstraints<'tcx> {
@@ -144,7 +146,7 @@ impl<'tcx> std::ops::Deref for ExternalConstraints<'tcx> {
 }
 
 /// Additional constraints returned on success.
-#[derive(Debug, PartialEq, Eq, Clone, Hash, Default)]
+#[derive(Debug, PartialEq, Eq, Clone, Hash, HashStable, Default)]
 pub struct ExternalConstraintsData<'tcx> {
     // FIXME: implement this.
     pub region_constraints: QueryRegionConstraints<'tcx>,
diff --git a/compiler/rustc_middle/src/traits/solve/inspect.rs b/compiler/rustc_middle/src/traits/solve/inspect.rs
new file mode 100644
index 00000000000..3cdf1ebbd05
--- /dev/null
+++ b/compiler/rustc_middle/src/traits/solve/inspect.rs
@@ -0,0 +1,79 @@
+use super::{CanonicalInput, Certainty, Goal, NoSolution, QueryInput, QueryResult};
+use crate::{traits::IsNormalizesToHack, ty};
+use format::ProofTreeFormatter;
+use std::fmt::{Debug, Write};
+
+mod format;
+
+#[derive(Eq, PartialEq, Debug, Hash, HashStable)]
+pub enum CacheHit {
+    Provisional,
+    Global,
+}
+
+#[derive(Eq, PartialEq, Hash, HashStable)]
+pub struct GoalEvaluation<'tcx> {
+    pub uncanonicalized_goal: Goal<'tcx, ty::Predicate<'tcx>>,
+    pub canonicalized_goal: CanonicalInput<'tcx>,
+
+    pub kind: GoalEvaluationKind<'tcx>,
+    pub is_normalizes_to_hack: IsNormalizesToHack,
+    pub returned_goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
+
+    pub result: QueryResult<'tcx>,
+}
+#[derive(Eq, PartialEq, Hash, HashStable)]
+pub enum GoalEvaluationKind<'tcx> {
+    CacheHit(CacheHit),
+    Uncached { revisions: Vec<GoalEvaluationStep<'tcx>> },
+}
+impl Debug for GoalEvaluation<'_> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        ProofTreeFormatter { f, on_newline: true }.format_goal_evaluation(self)
+    }
+}
+
+#[derive(Eq, PartialEq, Hash, HashStable)]
+pub struct AddedGoalsEvaluation<'tcx> {
+    pub evaluations: Vec<Vec<GoalEvaluation<'tcx>>>,
+    pub result: Result<Certainty, NoSolution>,
+}
+impl Debug for AddedGoalsEvaluation<'_> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        ProofTreeFormatter { f, on_newline: true }.format_nested_goal_evaluation(self)
+    }
+}
+
+#[derive(Eq, PartialEq, Hash, HashStable)]
+pub struct GoalEvaluationStep<'tcx> {
+    pub instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>,
+
+    pub nested_goal_evaluations: Vec<AddedGoalsEvaluation<'tcx>>,
+    pub candidates: Vec<GoalCandidate<'tcx>>,
+
+    pub result: QueryResult<'tcx>,
+}
+impl Debug for GoalEvaluationStep<'_> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        ProofTreeFormatter { f, on_newline: true }.format_evaluation_step(self)
+    }
+}
+
+#[derive(Eq, PartialEq, Hash, HashStable)]
+pub struct GoalCandidate<'tcx> {
+    pub nested_goal_evaluations: Vec<AddedGoalsEvaluation<'tcx>>,
+    pub candidates: Vec<GoalCandidate<'tcx>>,
+    pub kind: CandidateKind<'tcx>,
+}
+#[derive(Eq, PartialEq, Debug, Hash, HashStable)]
+pub enum CandidateKind<'tcx> {
+    /// Probe entered when normalizing the self ty during candidate assembly
+    NormalizedSelfTyAssembly,
+    /// A normal candidate for proving a goal
+    Candidate { name: String, result: QueryResult<'tcx> },
+}
+impl Debug for GoalCandidate<'_> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        ProofTreeFormatter { f, on_newline: true }.format_candidate(self)
+    }
+}
diff --git a/compiler/rustc_middle/src/traits/solve/inspect/format.rs b/compiler/rustc_middle/src/traits/solve/inspect/format.rs
new file mode 100644
index 00000000000..2ee625674fa
--- /dev/null
+++ b/compiler/rustc_middle/src/traits/solve/inspect/format.rs
@@ -0,0 +1,131 @@
+use super::*;
+
+pub(super) struct ProofTreeFormatter<'a, 'b> {
+    pub(super) f: &'a mut (dyn Write + 'b),
+    pub(super) on_newline: bool,
+}
+
+impl Write for ProofTreeFormatter<'_, '_> {
+    fn write_str(&mut self, s: &str) -> std::fmt::Result {
+        for line in s.split_inclusive("\n") {
+            if self.on_newline {
+                self.f.write_str("    ")?;
+            }
+            self.on_newline = line.ends_with("\n");
+            self.f.write_str(line)?;
+        }
+
+        Ok(())
+    }
+}
+
+impl ProofTreeFormatter<'_, '_> {
+    fn nested(&mut self) -> ProofTreeFormatter<'_, '_> {
+        ProofTreeFormatter { f: self, on_newline: true }
+    }
+
+    pub(super) fn format_goal_evaluation(&mut self, goal: &GoalEvaluation<'_>) -> std::fmt::Result {
+        let f = &mut *self.f;
+
+        let goal_text = match goal.is_normalizes_to_hack {
+            IsNormalizesToHack::Yes => "NORMALIZES-TO HACK GOAL",
+            IsNormalizesToHack::No => "GOAL",
+        };
+
+        writeln!(f, "{}: {:?}", goal_text, goal.uncanonicalized_goal,)?;
+        writeln!(f, "CANONICALIZED: {:?}", goal.canonicalized_goal)?;
+
+        match &goal.kind {
+            GoalEvaluationKind::CacheHit(CacheHit::Global) => {
+                writeln!(f, "GLOBAL CACHE HIT: {:?}", goal.result)
+            }
+            GoalEvaluationKind::CacheHit(CacheHit::Provisional) => {
+                writeln!(f, "PROVISIONAL CACHE HIT: {:?}", goal.result)
+            }
+            GoalEvaluationKind::Uncached { revisions } => {
+                for (n, step) in revisions.iter().enumerate() {
+                    let f = &mut *self.f;
+                    writeln!(f, "REVISION {n}: {:?}", step.result)?;
+                    let mut f = self.nested();
+                    f.format_evaluation_step(step)?;
+                }
+
+                let f = &mut *self.f;
+                writeln!(f, "RESULT: {:?}", goal.result)
+            }
+        }?;
+
+        if goal.returned_goals.len() > 0 {
+            let f = &mut *self.f;
+            writeln!(f, "NESTED GOALS ADDED TO CALLER: [")?;
+            let mut f = self.nested();
+            for goal in goal.returned_goals.iter() {
+                writeln!(f, "ADDED GOAL: {:?},", goal)?;
+            }
+            writeln!(self.f, "]")?;
+        }
+
+        Ok(())
+    }
+
+    pub(super) fn format_evaluation_step(
+        &mut self,
+        evaluation_step: &GoalEvaluationStep<'_>,
+    ) -> std::fmt::Result {
+        let f = &mut *self.f;
+        writeln!(f, "INSTANTIATED: {:?}", evaluation_step.instantiated_goal)?;
+
+        for candidate in &evaluation_step.candidates {
+            let mut f = self.nested();
+            f.format_candidate(candidate)?;
+        }
+        for nested_goal_evaluation in &evaluation_step.nested_goal_evaluations {
+            let mut f = self.nested();
+            f.format_nested_goal_evaluation(nested_goal_evaluation)?;
+        }
+
+        Ok(())
+    }
+
+    pub(super) fn format_candidate(&mut self, candidate: &GoalCandidate<'_>) -> std::fmt::Result {
+        let f = &mut *self.f;
+
+        match &candidate.kind {
+            CandidateKind::NormalizedSelfTyAssembly => {
+                writeln!(f, "NORMALIZING SELF TY FOR ASSEMBLY:")
+            }
+            CandidateKind::Candidate { name, result } => {
+                writeln!(f, "CANDIDATE {}: {:?}", name, result)
+            }
+        }?;
+
+        let mut f = self.nested();
+        for candidate in &candidate.candidates {
+            f.format_candidate(candidate)?;
+        }
+        for nested_evaluations in &candidate.nested_goal_evaluations {
+            f.format_nested_goal_evaluation(nested_evaluations)?;
+        }
+
+        Ok(())
+    }
+
+    pub(super) fn format_nested_goal_evaluation(
+        &mut self,
+        nested_goal_evaluation: &AddedGoalsEvaluation<'_>,
+    ) -> std::fmt::Result {
+        let f = &mut *self.f;
+        writeln!(f, "TRY_EVALUATE_ADDED_GOALS: {:?}", nested_goal_evaluation.result)?;
+
+        for (n, revision) in nested_goal_evaluation.evaluations.iter().enumerate() {
+            let f = &mut *self.f;
+            writeln!(f, "REVISION {n}")?;
+            let mut f = self.nested();
+            for goal_evaluation in revision {
+                f.format_goal_evaluation(goal_evaluation)?;
+            }
+        }
+
+        Ok(())
+    }
+}
diff --git a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
index ebf830cb9c1..4cb9d7babe1 100644
--- a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
+++ b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
@@ -128,7 +128,9 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
                     destination,
                     target: Some(target),
                     unwind: UnwindAction::Continue,
-                    from_hir_call: *from_hir_call,
+                    call_source: if *from_hir_call { CallSource::Normal } else {
+                        CallSource::OverloadedOperator
+                    },
                     fn_span: *fn_span,
                 })
             },
diff --git a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs
index 3742d640e3b..fd08b32807c 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs
+++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs
@@ -173,7 +173,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                         destination: storage,
                         target: Some(success),
                         unwind: UnwindAction::Continue,
-                        from_hir_call: false,
+                        call_source: CallSource::Misc,
                         fn_span: expr_span,
                     },
                 );
diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs
index 29ff916d2cc..731f3996244 100644
--- a/compiler/rustc_mir_build/src/build/expr/into.rs
+++ b/compiler/rustc_mir_build/src/build/expr/into.rs
@@ -277,7 +277,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                             .ty
                             .is_inhabited_from(this.tcx, this.parent_module, this.param_env)
                             .then_some(success),
-                        from_hir_call,
+                        call_source: if from_hir_call {
+                            CallSource::Normal
+                        } else {
+                            CallSource::OverloadedOperator
+                        },
                         fn_span,
                     },
                 );
diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs
index dbdb5b4a9a1..f431023f2b6 100644
--- a/compiler/rustc_mir_build/src/build/matches/test.rs
+++ b/compiler/rustc_mir_build/src/build/matches/test.rs
@@ -264,7 +264,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                             destination: ref_str,
                             target: Some(eq_block),
                             unwind: UnwindAction::Continue,
-                            from_hir_call: false,
+                            call_source: CallSource::Misc,
                             fn_span: source_info.span
                         }
                     );
@@ -496,7 +496,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 destination: eq_result,
                 target: Some(eq_block),
                 unwind: UnwindAction::Continue,
-                from_hir_call: false,
+                call_source: CallSource::MatchCmp,
                 fn_span: source_info.span,
             },
         );
diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
index 7976b148f75..a2e00d3bfc5 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
@@ -359,6 +359,15 @@ impl<'tcx> ConstToPat<'tcx> {
                     def.non_enum_variant().fields.iter().map(|field| field.ty(self.tcx(), substs)),
                 ))?,
             },
+            ty::Slice(elem_ty) => PatKind::Slice {
+                prefix: cv
+                    .unwrap_branch()
+                    .iter()
+                    .map(|val| self.recur(*val, *elem_ty, false))
+                    .collect::<Result<_, _>>()?,
+                slice: None,
+                suffix: Box::new([]),
+            },
             ty::Array(elem_ty, _) => PatKind::Array {
                 prefix: cv
                     .unwrap_branch()
@@ -372,58 +381,6 @@ impl<'tcx> ConstToPat<'tcx> {
                 // `&str` is represented as a valtree, let's keep using this
                 // optimization for now.
                 ty::Str => PatKind::Constant { value: mir::ConstantKind::Ty(tcx.mk_const(cv, ty)) },
-                // `b"foo"` produces a `&[u8; 3]`, but you can't use constants of array type when
-                // matching against references, you can only use byte string literals.
-                // The typechecker has a special case for byte string literals, by treating them
-                // as slices. This means we turn `&[T; N]` constants into slice patterns, which
-                // has no negative effects on pattern matching, even if we're actually matching on
-                // arrays.
-                ty::Array(elem_ty, _) if !self.treat_byte_string_as_slice => {
-                    let old = self.behind_reference.replace(true);
-                    // References have the same valtree representation as their pointee.
-                    let array = cv;
-                    let val = PatKind::Deref {
-                        subpattern: Box::new(Pat {
-                            kind: PatKind::Array {
-                                prefix: array.unwrap_branch()
-                                    .iter()
-                                    .map(|val| self.recur(*val, elem_ty, false))
-                                    .collect::<Result<_, _>>()?,
-                                slice: None,
-                                suffix: Box::new([]),
-                            },
-                            span,
-                            ty: tcx.mk_slice(elem_ty),
-                        }),
-                    };
-                    self.behind_reference.set(old);
-                    val
-                }
-                ty::Array(elem_ty, _) |
-                // Cannot merge this with the catch all branch below, because the `const_deref`
-                // changes the type from slice to array, we need to keep the original type in the
-                // pattern.
-                ty::Slice(elem_ty) => {
-                    let old = self.behind_reference.replace(true);
-                    // References have the same valtree representation as their pointee.
-                    let array = cv;
-                    let val = PatKind::Deref {
-                        subpattern: Box::new(Pat {
-                            kind: PatKind::Slice {
-                                prefix: array.unwrap_branch()
-                                    .iter()
-                                    .map(|val| self.recur(*val, elem_ty, false))
-                                    .collect::<Result<_, _>>()?,
-                                slice: None,
-                                suffix: Box::new([]),
-                            },
-                            span,
-                            ty: tcx.mk_slice(elem_ty),
-                        }),
-                    };
-                    self.behind_reference.set(old);
-                    val
-                }
                 // Backwards compatibility hack: support references to non-structural types,
                 // but hard error if we aren't behind a double reference. We could just use
                 // the fallback code path below, but that would allow *more* of this fishy
@@ -431,11 +388,9 @@ impl<'tcx> ConstToPat<'tcx> {
                 // instead of a hard error.
                 ty::Adt(_, _) if !self.type_marked_structural(*pointee_ty) => {
                     if self.behind_reference.get() {
-                        if !self.saw_const_match_error.get()
-                            && !self.saw_const_match_lint.get()
-                        {
-                           self.saw_const_match_lint.set(true);
-                           tcx.emit_spanned_lint(
+                        if !self.saw_const_match_error.get() && !self.saw_const_match_lint.get() {
+                            self.saw_const_match_lint.set(true);
+                            tcx.emit_spanned_lint(
                                 lint::builtin::INDIRECT_STRUCTURAL_MATCH,
                                 self.id,
                                 span,
@@ -456,7 +411,7 @@ impl<'tcx> ConstToPat<'tcx> {
                 // convert the dereferenced constant to a pattern that is the sub-pattern of the
                 // deref pattern.
                 _ => {
-                    if !pointee_ty.is_sized(tcx, param_env) {
+                    if !pointee_ty.is_sized(tcx, param_env) && !pointee_ty.is_slice() {
                         let err = UnsizedPattern { span, non_sm_ty: *pointee_ty };
                         tcx.sess.emit_err(err);
 
@@ -464,8 +419,20 @@ impl<'tcx> ConstToPat<'tcx> {
                         PatKind::Wild
                     } else {
                         let old = self.behind_reference.replace(true);
+                        // `b"foo"` produces a `&[u8; 3]`, but you can't use constants of array type when
+                        // matching against references, you can only use byte string literals.
+                        // The typechecker has a special case for byte string literals, by treating them
+                        // as slices. This means we turn `&[T; N]` constants into slice patterns, which
+                        // has no negative effects on pattern matching, even if we're actually matching on
+                        // arrays.
+                        let pointee_ty = match *pointee_ty.kind() {
+                            ty::Array(elem_ty, _) if self.treat_byte_string_as_slice => {
+                                tcx.mk_slice(elem_ty)
+                            }
+                            _ => *pointee_ty,
+                        };
                         // References have the same valtree representation as their pointee.
-                        let subpattern = self.recur(cv, *pointee_ty, false)?;
+                        let subpattern = self.recur(cv, pointee_ty, false)?;
                         self.behind_reference.set(old);
                         PatKind::Deref { subpattern }
                     }
diff --git a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs
index 4fc45eaf522..27232b70d96 100644
--- a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs
+++ b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs
@@ -654,7 +654,7 @@ where
                     destination: unit_temp,
                     target: Some(succ),
                     unwind: unwind.into_action(),
-                    from_hir_call: true,
+                    call_source: CallSource::Misc,
                     fn_span: self.source_info.span,
                 },
                 source_info: self.source_info,
diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs
index 0c379288a09..804b44a6bf0 100644
--- a/compiler/rustc_mir_dataflow/src/framework/direction.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs
@@ -502,15 +502,7 @@ impl Direction for Forward {
                 propagate(target, exit_state);
             }
 
-            Call {
-                unwind,
-                destination,
-                target,
-                func: _,
-                args: _,
-                from_hir_call: _,
-                fn_span: _,
-            } => {
+            Call { unwind, destination, target, func: _, args: _, call_source: _, fn_span: _ } => {
                 if let UnwindAction::Cleanup(unwind) = unwind {
                     propagate(unwind, exit_state);
                 }
diff --git a/compiler/rustc_mir_dataflow/src/framework/engine.rs b/compiler/rustc_mir_dataflow/src/framework/engine.rs
index 4bdfa35fc70..c755d7588c2 100644
--- a/compiler/rustc_mir_dataflow/src/framework/engine.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/engine.rs
@@ -287,9 +287,11 @@ where
 
         let mut results = Results { analysis, entry_sets, _marker: PhantomData };
 
-        let res = write_graphviz_results(tcx, body, &mut results, pass_name);
-        if let Err(e) = res {
-            error!("Failed to write graphviz dataflow results: {}", e);
+        if tcx.sess.opts.unstable_opts.dump_mir_dataflow {
+            let res = write_graphviz_results(tcx, &body, &mut results, pass_name);
+            if let Err(e) = res {
+                error!("Failed to write graphviz dataflow results: {}", e);
+            }
         }
 
         results
@@ -299,7 +301,7 @@ where
 // Graphviz
 
 /// Writes a DOT file containing the results of a dataflow analysis if the user requested it via
-/// `rustc_mir` attributes.
+/// `rustc_mir` attributes and `-Z dump-mir-dataflow`.
 fn write_graphviz_results<'tcx, A>(
     tcx: TyCtxt<'tcx>,
     body: &mir::Body<'tcx>,
@@ -328,9 +330,7 @@ where
             io::BufWriter::new(fs::File::create(&path)?)
         }
 
-        None if tcx.sess.opts.unstable_opts.dump_mir_dataflow
-            && dump_enabled(tcx, A::NAME, def_id) =>
-        {
+        None if dump_enabled(tcx, A::NAME, def_id) => {
             create_dump_file(tcx, ".dot", false, A::NAME, &pass_name.unwrap_or("-----"), body)?
         }
 
diff --git a/compiler/rustc_mir_dataflow/src/framework/tests.rs b/compiler/rustc_mir_dataflow/src/framework/tests.rs
index 45c2fe55aca..cb0ec144ef0 100644
--- a/compiler/rustc_mir_dataflow/src/framework/tests.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/tests.rs
@@ -40,7 +40,7 @@ fn mock_body<'tcx>() -> mir::Body<'tcx> {
             destination: dummy_place.clone(),
             target: Some(mir::START_BLOCK),
             unwind: mir::UnwindAction::Continue,
-            from_hir_call: false,
+            call_source: mir::CallSource::Misc,
             fn_span: DUMMY_SP,
         },
     );
@@ -54,7 +54,7 @@ fn mock_body<'tcx>() -> mir::Body<'tcx> {
             destination: dummy_place.clone(),
             target: Some(mir::START_BLOCK),
             unwind: mir::UnwindAction::Continue,
-            from_hir_call: false,
+            call_source: mir::CallSource::Misc,
             fn_span: DUMMY_SP,
         },
     );
diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
index 096bc0acfcc..fe9631653ea 100644
--- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
+++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
@@ -399,7 +399,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
                 destination,
                 target,
                 unwind: _,
-                from_hir_call: _,
+                call_source: _,
                 fn_span: _,
             } => {
                 self.gather_operand(func);
diff --git a/compiler/rustc_mir_transform/src/coverage/tests.rs b/compiler/rustc_mir_transform/src/coverage/tests.rs
index 90b58933df7..25891d3ca0f 100644
--- a/compiler/rustc_mir_transform/src/coverage/tests.rs
+++ b/compiler/rustc_mir_transform/src/coverage/tests.rs
@@ -140,7 +140,7 @@ impl<'tcx> MockBlocks<'tcx> {
                 destination: self.dummy_place.clone(),
                 target: Some(TEMP_BLOCK),
                 unwind: UnwindAction::Continue,
-                from_hir_call: false,
+                call_source: CallSource::Misc,
                 fn_span: DUMMY_SP,
             },
         )
diff --git a/compiler/rustc_mir_transform/src/function_item_references.rs b/compiler/rustc_mir_transform/src/function_item_references.rs
index b1c9c4acc40..7f631cccddf 100644
--- a/compiler/rustc_mir_transform/src/function_item_references.rs
+++ b/compiler/rustc_mir_transform/src/function_item_references.rs
@@ -34,7 +34,7 @@ impl<'tcx> Visitor<'tcx> for FunctionItemRefChecker<'_, 'tcx> {
             destination: _,
             target: _,
             unwind: _,
-            from_hir_call: _,
+            call_source: _,
             fn_span: _,
         } = &terminator.kind
         {
diff --git a/compiler/rustc_mir_transform/src/generator.rs b/compiler/rustc_mir_transform/src/generator.rs
index fe3f8ed047a..04134eb2fb1 100644
--- a/compiler/rustc_mir_transform/src/generator.rs
+++ b/compiler/rustc_mir_transform/src/generator.rs
@@ -1692,7 +1692,7 @@ impl<'tcx> Visitor<'tcx> for EnsureGeneratorFieldAssignmentsNeverAlias<'_> {
                 destination,
                 target: Some(_),
                 unwind: _,
-                from_hir_call: _,
+                call_source: _,
                 fn_span: _,
             } => {
                 self.check_assigned_place(*destination, |this| {
diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs
index 9c8c0ea0be0..fa8257cf984 100644
--- a/compiler/rustc_mir_transform/src/lib.rs
+++ b/compiler/rustc_mir_transform/src/lib.rs
@@ -30,8 +30,8 @@ use rustc_hir::intravisit::{self, Visitor};
 use rustc_index::IndexVec;
 use rustc_middle::mir::visit::Visitor as _;
 use rustc_middle::mir::{
-    traversal, AnalysisPhase, Body, ClearCrossCrate, ConstQualifs, Constant, LocalDecl, MirPass,
-    MirPhase, Operand, Place, ProjectionElem, Promoted, RuntimePhase, Rvalue, SourceInfo,
+    traversal, AnalysisPhase, Body, CallSource, ClearCrossCrate, ConstQualifs, Constant, LocalDecl,
+    MirPass, MirPhase, Operand, Place, ProjectionElem, Promoted, RuntimePhase, Rvalue, SourceInfo,
     Statement, StatementKind, TerminatorKind, START_BLOCK,
 };
 use rustc_middle::query::Providers;
@@ -189,7 +189,7 @@ fn remap_mir_for_const_eval_select<'tcx>(
                     };
                     method(place)
                 }).collect();
-                terminator.kind = TerminatorKind::Call { func, args: arguments, destination, target, unwind, from_hir_call: false, fn_span };
+                terminator.kind = TerminatorKind::Call { func, args: arguments, destination, target, unwind, call_source: CallSource::Misc, fn_span };
             }
             _ => {}
         }
diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs
index 3a7d58f7125..ce98e9b0c84 100644
--- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs
+++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs
@@ -85,8 +85,13 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
                     sym::wrapping_add
                     | sym::wrapping_sub
                     | sym::wrapping_mul
+                    | sym::unchecked_add
+                    | sym::unchecked_sub
+                    | sym::unchecked_mul
                     | sym::unchecked_div
-                    | sym::unchecked_rem => {
+                    | sym::unchecked_rem
+                    | sym::unchecked_shl
+                    | sym::unchecked_shr => {
                         let target = target.unwrap();
                         let lhs;
                         let rhs;
@@ -99,8 +104,13 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
                             sym::wrapping_add => BinOp::Add,
                             sym::wrapping_sub => BinOp::Sub,
                             sym::wrapping_mul => BinOp::Mul,
+                            sym::unchecked_add => BinOp::AddUnchecked,
+                            sym::unchecked_sub => BinOp::SubUnchecked,
+                            sym::unchecked_mul => BinOp::MulUnchecked,
                             sym::unchecked_div => BinOp::Div,
                             sym::unchecked_rem => BinOp::Rem,
+                            sym::unchecked_shl => BinOp::ShlUnchecked,
+                            sym::unchecked_shr => BinOp::ShrUnchecked,
                             _ => bug!("unexpected intrinsic"),
                         };
                         block.statements.push(Statement {
diff --git a/compiler/rustc_mir_transform/src/lower_slice_len.rs b/compiler/rustc_mir_transform/src/lower_slice_len.rs
index 6e40dfa0d13..b7cc0db9559 100644
--- a/compiler/rustc_mir_transform/src/lower_slice_len.rs
+++ b/compiler/rustc_mir_transform/src/lower_slice_len.rs
@@ -54,7 +54,7 @@ fn lower_slice_len_call<'tcx>(
             args,
             destination,
             target: Some(bb),
-            from_hir_call: true,
+            call_source: CallSource::Normal,
             ..
         } => {
             // some heuristics for fast rejection
diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs
index 5f12f1937c0..9d6ef9db4ea 100644
--- a/compiler/rustc_mir_transform/src/shim.rs
+++ b/compiler/rustc_mir_transform/src/shim.rs
@@ -500,7 +500,7 @@ impl<'tcx> CloneShimBuilder<'tcx> {
                 destination: dest,
                 target: Some(next),
                 unwind: UnwindAction::Cleanup(cleanup),
-                from_hir_call: true,
+                call_source: CallSource::Normal,
                 fn_span: self.span,
             },
             false,
@@ -789,7 +789,7 @@ fn build_call_shim<'tcx>(
             } else {
                 UnwindAction::Continue
             },
-            from_hir_call: true,
+            call_source: CallSource::Misc,
             fn_span: span,
         },
         false,
diff --git a/compiler/rustc_resolve/messages.ftl b/compiler/rustc_resolve/messages.ftl
index 539b88aa9d3..60b6d74da7b 100644
--- a/compiler/rustc_resolve/messages.ftl
+++ b/compiler/rustc_resolve/messages.ftl
@@ -5,6 +5,9 @@ resolve_add_as_non_derive =
     add as non-Derive macro
     `#[{$macro_path}]`
 
+resolve_added_macro_use =
+    have you added the `#[macro_use]` on the module/import?
+
 resolve_ampersand_used_without_explicit_lifetime_name =
     `&` without an explicit lifetime name cannot be used here
     .note = explicit lifetime name needed here
@@ -45,9 +48,18 @@ resolve_cannot_capture_dynamic_environment_in_fn_item =
     can't capture dynamic environment in a fn item
     .help = use the `|| {"{"} ... {"}"}` closure form instead
 
+resolve_cannot_find_ident_in_this_scope =
+    cannot find {$expected} `{$ident}` in this scope
+
 resolve_cannot_use_self_type_here =
     can't use `Self` here
 
+resolve_change_import_binding =
+    you can use `as` to change the binding name of the import
+
+resolve_consider_adding_a_derive =
+    consider adding a derive
+
 resolve_const_not_member_of_trait =
     const `{$const_}` is not a member of trait `{$trait_}`
     .label = not a member of trait `{$trait_}`
@@ -74,6 +86,9 @@ resolve_expected_found =
     expected module, found {$res} `{$path_str}`
     .label = not a module
 
+resolve_explicit_unsafe_traits =
+    unsafe traits like `{$ident}` should be implemented explicitly
+
 resolve_forward_declared_generic_param =
     generic parameters with a default cannot use forward declared identifiers
     .label = defaulted generic parameters cannot be forward declared
@@ -96,6 +111,9 @@ resolve_ident_bound_more_than_once_in_same_pattern =
 
 resolve_imported_crate = `$crate` may not be imported
 
+resolve_imports_cannot_refer_to =
+    imports cannot refer to {$what}
+
 resolve_indeterminate =
     cannot determine resolution for the visibility
 
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index e42b2df1a5a..539b4a1d5e7 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -30,6 +30,10 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{BytePos, Span, SyntaxContext};
 use thin_vec::ThinVec;
 
+use crate::errors::{
+    AddedMacroUse, ChangeImportBinding, ChangeImportBindingSuggestion, ConsiderAddingADerive,
+    ExplicitUnsafeTraits,
+};
 use crate::imports::{Import, ImportKind};
 use crate::late::{PatternSource, Rib};
 use crate::path_names_to_string;
@@ -376,16 +380,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             _ => unreachable!(),
         }
 
-        let rename_msg = "you can use `as` to change the binding name of the import";
         if let Some(suggestion) = suggestion {
-            err.span_suggestion(
-                binding_span,
-                rename_msg,
-                suggestion,
-                Applicability::MaybeIncorrect,
-            );
+            err.subdiagnostic(ChangeImportBindingSuggestion { span: binding_span, suggestion });
         } else {
-            err.span_label(binding_span, rename_msg);
+            err.subdiagnostic(ChangeImportBinding { span: binding_span });
         }
     }
 
@@ -1382,12 +1380,11 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         );
 
         if macro_kind == MacroKind::Derive && (ident.name == sym::Send || ident.name == sym::Sync) {
-            let msg = format!("unsafe traits like `{}` should be implemented explicitly", ident);
-            err.span_note(ident.span, msg);
+            err.subdiagnostic(ExplicitUnsafeTraits { span: ident.span, ident });
             return;
         }
         if self.macro_names.contains(&ident.normalize_to_macros_2_0()) {
-            err.help("have you added the `#[macro_use]` on the module/import?");
+            err.subdiagnostic(AddedMacroUse);
             return;
         }
         if ident.name == kw::Default
@@ -1396,14 +1393,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             let span = self.def_span(def_id);
             let source_map = self.tcx.sess.source_map();
             let head_span = source_map.guess_head_span(span);
-            if let Ok(head) = source_map.span_to_snippet(head_span) {
-                err.span_suggestion(head_span, "consider adding a derive", format!("#[derive(Default)]\n{head}"), Applicability::MaybeIncorrect);
-            } else {
-                err.span_help(
-                    head_span,
-                    "consider adding `#[derive(Default)]` to this enum",
-                );
-            }
+            err.subdiagnostic(ConsiderAddingADerive {
+                span: head_span.shrink_to_lo(),
+                suggestion: format!("#[derive(Default)]\n")
+            });
         }
         for ns in [Namespace::MacroNS, Namespace::TypeNS, Namespace::ValueNS] {
             if let Ok(binding) = self.early_resolve_ident_in_lexical_scope(
diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs
index e88cbb955b5..93b626c7794 100644
--- a/compiler/rustc_resolve/src/errors.rs
+++ b/compiler/rustc_resolve/src/errors.rs
@@ -586,3 +586,63 @@ pub(crate) enum ParamKindInEnumDiscriminant {
     #[note(resolve_lifetime_param_in_enum_discriminant)]
     Lifetime,
 }
+
+#[derive(Subdiagnostic)]
+#[label(resolve_change_import_binding)]
+pub(crate) struct ChangeImportBinding {
+    #[primary_span]
+    pub(crate) span: Span,
+}
+
+#[derive(Subdiagnostic)]
+#[suggestion(
+    resolve_change_import_binding,
+    code = "{suggestion}",
+    applicability = "maybe-incorrect"
+)]
+pub(crate) struct ChangeImportBindingSuggestion {
+    #[primary_span]
+    pub(crate) span: Span,
+    pub(crate) suggestion: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_imports_cannot_refer_to)]
+pub(crate) struct ImportsCannotReferTo<'a> {
+    #[primary_span]
+    pub(crate) span: Span,
+    pub(crate) what: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_cannot_find_ident_in_this_scope)]
+pub(crate) struct CannotFindIdentInThisScope<'a> {
+    #[primary_span]
+    pub(crate) span: Span,
+    pub(crate) expected: &'a str,
+    pub(crate) ident: Ident,
+}
+
+#[derive(Subdiagnostic)]
+#[note(resolve_explicit_unsafe_traits)]
+pub(crate) struct ExplicitUnsafeTraits {
+    #[primary_span]
+    pub(crate) span: Span,
+    pub(crate) ident: Ident,
+}
+
+#[derive(Subdiagnostic)]
+#[help(resolve_added_macro_use)]
+pub(crate) struct AddedMacroUse;
+
+#[derive(Subdiagnostic)]
+#[suggestion(
+    resolve_consider_adding_a_derive,
+    code = "{suggestion}",
+    applicability = "maybe-incorrect"
+)]
+pub(crate) struct ConsiderAddingADerive {
+    #[primary_span]
+    pub(crate) span: Span,
+    pub(crate) suggestion: String,
+}
diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs
index 47d8e5993fd..23ef9bf53a1 100644
--- a/compiler/rustc_resolve/src/imports.rs
+++ b/compiler/rustc_resolve/src/imports.rs
@@ -304,21 +304,23 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         let res = binding.res();
         self.check_reserved_macro_name(key.ident, res);
         self.set_binding_parent_module(binding, module);
-        self.update_resolution(module, key, |this, resolution| {
-            if let Some(old_binding) = resolution.binding {
-                if res == Res::Err && old_binding.res() != Res::Err {
-                    // Do not override real bindings with `Res::Err`s from error recovery.
-                    return Ok(());
-                }
+
+        let mut resolution = self.resolution(module, key).borrow_mut();
+        let old_binding = resolution.binding();
+        let mut t = Ok(());
+        if let Some(old_binding) = resolution.binding {
+            if res == Res::Err && old_binding.res() != Res::Err {
+                // Do not override real bindings with `Res::Err`s from error recovery.
+            } else {
                 match (old_binding.is_glob_import(), binding.is_glob_import()) {
                     (true, true) => {
                         if res != old_binding.res() {
-                            resolution.binding = Some(this.ambiguity(
+                            resolution.binding = Some(self.ambiguity(
                                 AmbiguityKind::GlobVsGlob,
                                 old_binding,
                                 binding,
                             ));
-                        } else if !old_binding.vis.is_at_least(binding.vis, this.tcx) {
+                        } else if !old_binding.vis.is_at_least(binding.vis, self.tcx) {
                             // We are glob-importing the same item but with greater visibility.
                             resolution.binding = Some(binding);
                         }
@@ -330,7 +332,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                             && key.ns == MacroNS
                             && nonglob_binding.expansion != LocalExpnId::ROOT
                         {
-                            resolution.binding = Some(this.ambiguity(
+                            resolution.binding = Some(self.ambiguity(
                                 AmbiguityKind::GlobVsExpanded,
                                 nonglob_binding,
                                 glob_binding,
@@ -342,12 +344,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                         if let Some(old_binding) = resolution.shadowed_glob {
                             assert!(old_binding.is_glob_import());
                             if glob_binding.res() != old_binding.res() {
-                                resolution.shadowed_glob = Some(this.ambiguity(
+                                resolution.shadowed_glob = Some(self.ambiguity(
                                     AmbiguityKind::GlobVsGlob,
                                     old_binding,
                                     glob_binding,
                                 ));
-                            } else if !old_binding.vis.is_at_least(binding.vis, this.tcx) {
+                            } else if !old_binding.vis.is_at_least(binding.vis, self.tcx) {
                                 resolution.shadowed_glob = Some(glob_binding);
                             }
                         } else {
@@ -355,53 +357,27 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                         }
                     }
                     (false, false) => {
-                        return Err(old_binding);
+                        t = Err(old_binding);
                     }
                 }
-            } else {
-                resolution.binding = Some(binding);
             }
+        } else {
+            resolution.binding = Some(binding);
+        };
 
-            Ok(())
-        })
-    }
-
-    fn ambiguity(
-        &self,
-        kind: AmbiguityKind,
-        primary_binding: &'a NameBinding<'a>,
-        secondary_binding: &'a NameBinding<'a>,
-    ) -> &'a NameBinding<'a> {
-        self.arenas.alloc_name_binding(NameBinding {
-            ambiguity: Some((secondary_binding, kind)),
-            ..primary_binding.clone()
-        })
-    }
-
-    // Use `f` to mutate the resolution of the name in the module.
-    // If the resolution becomes a success, define it in the module's glob importers.
-    fn update_resolution<T, F>(&mut self, module: Module<'a>, key: BindingKey, f: F) -> T
-    where
-        F: FnOnce(&mut Resolver<'a, 'tcx>, &mut NameResolution<'a>) -> T,
-    {
         // Ensure that `resolution` isn't borrowed when defining in the module's glob importers,
         // during which the resolution might end up getting re-defined via a glob cycle.
-        let (binding, t) = {
-            let resolution = &mut *self.resolution(module, key).borrow_mut();
-            let old_binding = resolution.binding();
-
-            let t = f(self, resolution);
-
-            match resolution.binding() {
-                _ if old_binding.is_some() => return t,
-                None => return t,
-                Some(binding) => match old_binding {
-                    Some(old_binding) if ptr::eq(old_binding, binding) => return t,
-                    _ => (binding, t),
-                },
-            }
+        let (binding, t) = match resolution.binding() {
+            _ if old_binding.is_some() => return t,
+            None => return t,
+            Some(binding) => match old_binding {
+                Some(old_binding) if ptr::eq(old_binding, binding) => return t,
+                _ => (binding, t),
+            },
         };
 
+        drop(resolution);
+
         // Define `binding` in `module`s glob importers.
         for import in module.glob_importers.borrow_mut().iter() {
             let mut ident = key.ident;
@@ -420,6 +396,18 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         t
     }
 
+    fn ambiguity(
+        &self,
+        kind: AmbiguityKind,
+        primary_binding: &'a NameBinding<'a>,
+        secondary_binding: &'a NameBinding<'a>,
+    ) -> &'a NameBinding<'a> {
+        self.arenas.alloc_name_binding(NameBinding {
+            ambiguity: Some((secondary_binding, kind)),
+            ..primary_binding.clone()
+        })
+    }
+
     // Define a dummy resolution containing a `Res::Err` as a placeholder for a failed
     // or indeterminate resolution, also mark such failed imports as used to avoid duplicate diagnostics.
     fn import_dummy_binding(&mut self, import: &'a Import<'a>, is_indeterminate: bool) {
@@ -769,9 +757,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                                 .emit();
                         }
                         let key = BindingKey::new(target, ns);
-                        this.update_resolution(parent, key, |_, resolution| {
-                            resolution.single_imports.remove(&Interned::new_unchecked(import));
-                        });
+                        let mut resolution = this.resolution(parent, key).borrow_mut();
+                        resolution.single_imports.remove(&Interned::new_unchecked(import));
                     }
                 }
             }
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index ddd75ea3b33..9f4573ea025 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -6,6 +6,7 @@
 //! If you wonder why there's no `early.rs`, that's because it's split into three files -
 //! `build_reduced_graph.rs`, `macros.rs` and `imports.rs`.
 
+use crate::errors::ImportsCannotReferTo;
 use crate::BindingKey;
 use crate::{path_names_to_string, rustdoc, BindingError, Finalize, LexicalScopeBinding};
 use crate::{Module, ModuleOrUniformRoot, NameBinding, ParentScope, PathResult};
@@ -2244,12 +2245,13 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
                 _ => &[TypeNS],
             };
             let report_error = |this: &Self, ns| {
-                let what = if ns == TypeNS { "type parameters" } else { "local variables" };
                 if this.should_report_errs() {
+                    let what = if ns == TypeNS { "type parameters" } else { "local variables" };
                     this.r
                         .tcx
                         .sess
-                        .span_err(ident.span, format!("imports cannot refer to {}", what));
+                        .create_err(ImportsCannotReferTo { span: ident.span, what })
+                        .emit();
                 }
             };
 
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index ca4f3331b9a..4dcef8f6efd 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -1,7 +1,9 @@
 //! A bunch of methods and structures more or less related to resolving macros and
 //! interface provided by `Resolver` to macro expander.
 
-use crate::errors::{self, AddAsNonDerive, MacroExpectedFound, RemoveSurroundingDerive};
+use crate::errors::{
+    self, AddAsNonDerive, CannotFindIdentInThisScope, MacroExpectedFound, RemoveSurroundingDerive,
+};
 use crate::Namespace::*;
 use crate::{BuiltinMacroState, Determinacy};
 use crate::{DeriveData, Finalize, ParentScope, ResolutionError, Resolver, ScopeSet};
@@ -793,8 +795,13 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                 }
                 Err(..) => {
                     let expected = kind.descr_expected();
-                    let msg = format!("cannot find {} `{}` in this scope", expected, ident);
-                    let mut err = self.tcx.sess.struct_span_err(ident.span, msg);
+
+                    let mut err = self.tcx.sess.create_err(CannotFindIdentInThisScope {
+                        span: ident.span,
+                        expected,
+                        ident,
+                    });
+
                     self.unresolved_macro_suggestions(&mut err, kind, &parent_scope, ident, krate);
                     err.emit();
                 }
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 3d9f0a4e268..edf95949d32 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -1436,6 +1436,8 @@ options! {
         "output statistics about monomorphization collection"),
     dump_mono_stats_format: DumpMonoStatsFormat = (DumpMonoStatsFormat::Markdown, parse_dump_mono_stats, [UNTRACKED],
         "the format to use for -Z dump-mono-stats (`markdown` (default) or `json`)"),
+    dump_solver_proof_tree: bool = (false, parse_bool, [UNTRACKED],
+        "dump a proof tree for every goal evaluated by the new trait solver"),
     dwarf_version: Option<u32> = (None, parse_opt_number, [TRACKED],
         "version of DWARF debug information to emit (default: 2 or 4, depending on platform)"),
     dylib_lto: bool = (false, parse_bool, [UNTRACKED],
diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs
index 6bd030b13d1..874e34bef60 100644
--- a/compiler/rustc_smir/src/rustc_smir/mod.rs
+++ b/compiler/rustc_smir/src/rustc_smir/mod.rs
@@ -250,15 +250,20 @@ impl Stable for mir::BinOp {
         use mir::BinOp;
         match self {
             BinOp::Add => stable_mir::mir::BinOp::Add,
+            BinOp::AddUnchecked => stable_mir::mir::BinOp::AddUnchecked,
             BinOp::Sub => stable_mir::mir::BinOp::Sub,
+            BinOp::SubUnchecked => stable_mir::mir::BinOp::SubUnchecked,
             BinOp::Mul => stable_mir::mir::BinOp::Mul,
+            BinOp::MulUnchecked => stable_mir::mir::BinOp::MulUnchecked,
             BinOp::Div => stable_mir::mir::BinOp::Div,
             BinOp::Rem => stable_mir::mir::BinOp::Rem,
             BinOp::BitXor => stable_mir::mir::BinOp::BitXor,
             BinOp::BitAnd => stable_mir::mir::BinOp::BitAnd,
             BinOp::BitOr => stable_mir::mir::BinOp::BitOr,
             BinOp::Shl => stable_mir::mir::BinOp::Shl,
+            BinOp::ShlUnchecked => stable_mir::mir::BinOp::ShlUnchecked,
             BinOp::Shr => stable_mir::mir::BinOp::Shr,
+            BinOp::ShrUnchecked => stable_mir::mir::BinOp::ShrUnchecked,
             BinOp::Eq => stable_mir::mir::BinOp::Eq,
             BinOp::Lt => stable_mir::mir::BinOp::Lt,
             BinOp::Le => stable_mir::mir::BinOp::Le,
@@ -346,7 +351,7 @@ impl<'tcx> Stable for mir::Terminator<'tcx> {
                 target: target.as_usize(),
                 unwind: unwind.stable(),
             },
-            Call { func, args, destination, target, unwind, from_hir_call: _, fn_span: _ } => {
+            Call { func, args, destination, target, unwind, call_source: _, fn_span: _ } => {
                 Terminator::Call {
                     func: func.stable(),
                     args: args.iter().map(|arg| arg.stable()).collect(),
diff --git a/compiler/rustc_smir/src/stable_mir/mir/body.rs b/compiler/rustc_smir/src/stable_mir/mir/body.rs
index 9df7b4945b7..468e915d1a0 100644
--- a/compiler/rustc_smir/src/stable_mir/mir/body.rs
+++ b/compiler/rustc_smir/src/stable_mir/mir/body.rs
@@ -88,15 +88,20 @@ pub enum AssertMessage {
 #[derive(Clone, Debug)]
 pub enum BinOp {
     Add,
+    AddUnchecked,
     Sub,
+    SubUnchecked,
     Mul,
+    MulUnchecked,
     Div,
     Rem,
     BitXor,
     BitAnd,
     BitOr,
     Shl,
+    ShlUnchecked,
     Shr,
+    ShrUnchecked,
     Eq,
     Lt,
     Le,
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index e7a53c63e83..3bb9c4920c4 100644
--- a/compiler/rustc_span/src/lib.rs
+++ b/compiler/rustc_span/src/lib.rs
@@ -65,11 +65,11 @@ use rustc_data_structures::sync::{Lock, Lrc};
 
 use std::borrow::Cow;
 use std::cmp::{self, Ordering};
-use std::fmt;
 use std::hash::Hash;
 use std::ops::{Add, Range, Sub};
 use std::path::{Path, PathBuf};
 use std::str::FromStr;
+use std::{fmt, iter};
 
 use md5::Digest;
 use md5::Md5;
@@ -733,12 +733,15 @@ impl Span {
     /// else returns the `ExpnData` for the macro definition
     /// corresponding to the source callsite.
     pub fn source_callee(self) -> Option<ExpnData> {
-        fn source_callee(expn_data: ExpnData) -> ExpnData {
-            let next_expn_data = expn_data.call_site.ctxt().outer_expn_data();
-            if !next_expn_data.is_root() { source_callee(next_expn_data) } else { expn_data }
-        }
         let expn_data = self.ctxt().outer_expn_data();
-        if !expn_data.is_root() { Some(source_callee(expn_data)) } else { None }
+
+        // Create an iterator of call site expansions
+        iter::successors(Some(expn_data), |expn_data| {
+            Some(expn_data.call_site.ctxt().outer_expn_data())
+        })
+        // Find the last expansion which is not root
+        .take_while(|expn_data| !expn_data.is_root())
+        .last()
     }
 
     /// Checks if a span is "internal" to a macro in which `#[unstable]`
@@ -777,7 +780,7 @@ impl Span {
 
     pub fn macro_backtrace(mut self) -> impl Iterator<Item = ExpnData> {
         let mut prev_span = DUMMY_SP;
-        std::iter::from_fn(move || {
+        iter::from_fn(move || {
             loop {
                 let expn_data = self.ctxt().outer_expn_data();
                 if expn_data.is_root() {
diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs
index 66a4d36a1e5..1ceb77e9193 100644
--- a/compiler/rustc_trait_selection/src/solve/alias_relate.rs
+++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs
@@ -1,5 +1,6 @@
 use super::{EvalCtxt, SolverMode};
 use rustc_infer::traits::query::NoSolution;
+use rustc_middle::traits::solve::inspect::CandidateKind;
 use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
 use rustc_middle::ty;
 
@@ -109,10 +110,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         direction: ty::AliasRelationDirection,
         invert: Invert,
     ) -> QueryResult<'tcx> {
-        self.probe(|ecx| {
-            ecx.normalizes_to_inner(param_env, alias, other, direction, invert)?;
-            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
-        })
+        self.probe(
+            |ecx| {
+                ecx.normalizes_to_inner(param_env, alias, other, direction, invert)?;
+                ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+            },
+            |r| CandidateKind::Candidate { name: "normalizes-to".into(), result: *r },
+        )
     }
 
     fn normalizes_to_inner(
@@ -153,18 +157,21 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         alias_rhs: ty::AliasTy<'tcx>,
         direction: ty::AliasRelationDirection,
     ) -> QueryResult<'tcx> {
-        self.probe(|ecx| {
-            match direction {
-                ty::AliasRelationDirection::Equate => {
-                    ecx.eq(param_env, alias_lhs, alias_rhs)?;
-                }
-                ty::AliasRelationDirection::Subtype => {
-                    ecx.sub(param_env, alias_lhs, alias_rhs)?;
+        self.probe(
+            |ecx| {
+                match direction {
+                    ty::AliasRelationDirection::Equate => {
+                        ecx.eq(param_env, alias_lhs, alias_rhs)?;
+                    }
+                    ty::AliasRelationDirection::Subtype => {
+                        ecx.sub(param_env, alias_lhs, alias_rhs)?;
+                    }
                 }
-            }
 
-            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
-        })
+                ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+            },
+            |r| CandidateKind::Candidate { name: "substs relate".into(), result: *r },
+        )
     }
 
     fn assemble_bidirectional_normalizes_to_candidate(
@@ -174,22 +181,25 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         rhs: ty::Term<'tcx>,
         direction: ty::AliasRelationDirection,
     ) -> QueryResult<'tcx> {
-        self.probe(|ecx| {
-            ecx.normalizes_to_inner(
-                param_env,
-                lhs.to_alias_ty(ecx.tcx()).unwrap(),
-                rhs,
-                direction,
-                Invert::No,
-            )?;
-            ecx.normalizes_to_inner(
-                param_env,
-                rhs.to_alias_ty(ecx.tcx()).unwrap(),
-                lhs,
-                direction,
-                Invert::Yes,
-            )?;
-            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
-        })
+        self.probe(
+            |ecx| {
+                ecx.normalizes_to_inner(
+                    param_env,
+                    lhs.to_alias_ty(ecx.tcx()).unwrap(),
+                    rhs,
+                    direction,
+                    Invert::No,
+                )?;
+                ecx.normalizes_to_inner(
+                    param_env,
+                    rhs.to_alias_ty(ecx.tcx()).unwrap(),
+                    lhs,
+                    direction,
+                    Invert::Yes,
+                )?;
+                ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+            },
+            |r| CandidateKind::Candidate { name: "bidir normalizes-to".into(), result: *r },
+        )
     }
 }
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
index 543611daae8..d9bc8d3b1eb 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
@@ -8,6 +8,7 @@ use rustc_hir::def_id::DefId;
 use rustc_infer::traits::query::NoSolution;
 use rustc_infer::traits::util::elaborate;
 use rustc_infer::traits::Reveal;
+use rustc_middle::traits::solve::inspect::CandidateKind;
 use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult};
 use rustc_middle::ty::fast_reject::TreatProjections;
 use rustc_middle::ty::TypeFoldable;
@@ -336,37 +337,40 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
             return
         };
 
-        let normalized_self_candidates: Result<_, NoSolution> = self.probe(|ecx| {
-            ecx.with_incremented_depth(
-                |ecx| {
-                    let result = ecx.evaluate_added_goals_and_make_canonical_response(
-                        Certainty::Maybe(MaybeCause::Overflow),
-                    )?;
-                    Ok(vec![Candidate { source: CandidateSource::BuiltinImpl, result }])
-                },
-                |ecx| {
-                    let normalized_ty = ecx.next_ty_infer();
-                    let normalizes_to_goal = goal.with(
-                        tcx,
-                        ty::Binder::dummy(ty::ProjectionPredicate {
-                            projection_ty,
-                            term: normalized_ty.into(),
-                        }),
-                    );
-                    ecx.add_goal(normalizes_to_goal);
-                    let _ = ecx.try_evaluate_added_goals().inspect_err(|_| {
-                        debug!("self type normalization failed");
-                    })?;
-                    let normalized_ty = ecx.resolve_vars_if_possible(normalized_ty);
-                    debug!(?normalized_ty, "self type normalized");
-                    // NOTE: Alternatively we could call `evaluate_goal` here and only
-                    // have a `Normalized` candidate. This doesn't work as long as we
-                    // use `CandidateSource` in winnowing.
-                    let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty));
-                    Ok(ecx.assemble_and_evaluate_candidates(goal))
-                },
-            )
-        });
+        let normalized_self_candidates: Result<_, NoSolution> = self.probe(
+            |ecx| {
+                ecx.with_incremented_depth(
+                    |ecx| {
+                        let result = ecx.evaluate_added_goals_and_make_canonical_response(
+                            Certainty::Maybe(MaybeCause::Overflow),
+                        )?;
+                        Ok(vec![Candidate { source: CandidateSource::BuiltinImpl, result }])
+                    },
+                    |ecx| {
+                        let normalized_ty = ecx.next_ty_infer();
+                        let normalizes_to_goal = goal.with(
+                            tcx,
+                            ty::Binder::dummy(ty::ProjectionPredicate {
+                                projection_ty,
+                                term: normalized_ty.into(),
+                            }),
+                        );
+                        ecx.add_goal(normalizes_to_goal);
+                        let _ = ecx.try_evaluate_added_goals().inspect_err(|_| {
+                            debug!("self type normalization failed");
+                        })?;
+                        let normalized_ty = ecx.resolve_vars_if_possible(normalized_ty);
+                        debug!(?normalized_ty, "self type normalized");
+                        // NOTE: Alternatively we could call `evaluate_goal` here and only
+                        // have a `Normalized` candidate. This doesn't work as long as we
+                        // use `CandidateSource` in winnowing.
+                        let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty));
+                        Ok(ecx.assemble_and_evaluate_candidates(goal))
+                    },
+                )
+            },
+            |_| CandidateKind::NormalizedSelfTyAssembly,
+        );
 
         if let Ok(normalized_self_candidates) = normalized_self_candidates {
             candidates.extend(normalized_self_candidates);
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
index 8625958ff5a..106849190e0 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
@@ -9,11 +9,12 @@ use rustc_infer::infer::{
 use rustc_infer::traits::query::NoSolution;
 use rustc_infer::traits::ObligationCause;
 use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
+use rustc_middle::traits::solve::inspect::{self, CandidateKind};
 use rustc_middle::traits::solve::{
     CanonicalInput, CanonicalResponse, Certainty, MaybeCause, PredefinedOpaques,
     PredefinedOpaquesData, QueryResult,
 };
-use rustc_middle::traits::DefiningAnchor;
+use rustc_middle::traits::{DefiningAnchor, IsNormalizesToHack};
 use rustc_middle::ty::{
     self, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable,
     TypeVisitableExt, TypeVisitor,
@@ -23,6 +24,7 @@ use std::ops::ControlFlow;
 
 use crate::traits::specialization_graph;
 
+use super::inspect::ProofTreeBuilder;
 use super::search_graph::{self, OverflowHandler};
 use super::SolverMode;
 use super::{search_graph::SearchGraph, Goal};
@@ -73,12 +75,8 @@ pub struct EvalCtxt<'a, 'tcx> {
     // ambiguous goals. Instead, a probe needs to be introduced somewhere in the
     // evaluation code.
     tainted: Result<(), NoSolution>,
-}
 
-#[derive(Debug, Copy, Clone, PartialEq, Eq)]
-pub(super) enum IsNormalizesToHack {
-    Yes,
-    No,
+    inspect: ProofTreeBuilder<'tcx>,
 }
 
 #[derive(Debug, Clone)]
@@ -110,6 +108,12 @@ impl NestedGoals<'_> {
     }
 }
 
+#[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)]
+pub enum GenerateProofTree {
+    Yes,
+    No,
+}
+
 pub trait InferCtxtEvalExt<'tcx> {
     /// Evaluates a goal from **outside** of the trait solver.
     ///
@@ -118,7 +122,11 @@ pub trait InferCtxtEvalExt<'tcx> {
     fn evaluate_root_goal(
         &self,
         goal: Goal<'tcx, ty::Predicate<'tcx>>,
-    ) -> Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution>;
+        generate_proof_tree: GenerateProofTree,
+    ) -> (
+        Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution>,
+        Option<inspect::GoalEvaluation<'tcx>>,
+    );
 }
 
 impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
@@ -126,7 +134,11 @@ impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
     fn evaluate_root_goal(
         &self,
         goal: Goal<'tcx, ty::Predicate<'tcx>>,
-    ) -> Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution> {
+        generate_proof_tree: GenerateProofTree,
+    ) -> (
+        Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution>,
+        Option<inspect::GoalEvaluation<'tcx>>,
+    ) {
         let mode = if self.intercrate { SolverMode::Coherence } else { SolverMode::Normal };
         let mut search_graph = search_graph::SearchGraph::new(self.tcx, mode);
 
@@ -143,16 +155,26 @@ impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
             var_values: CanonicalVarValues::dummy(),
             nested_goals: NestedGoals::new(),
             tainted: Ok(()),
+            inspect: (self.tcx.sess.opts.unstable_opts.dump_solver_proof_tree
+                || matches!(generate_proof_tree, GenerateProofTree::Yes))
+            .then(ProofTreeBuilder::new_root)
+            .unwrap_or_else(ProofTreeBuilder::new_noop),
         };
         let result = ecx.evaluate_goal(IsNormalizesToHack::No, goal);
 
+        let tree = ecx.inspect.finalize();
+        if let Some(tree) = &tree {
+            // module to allow more granular RUSTC_LOG filtering to just proof tree output
+            super::inspect::dump::print_tree(tree);
+        }
+
         assert!(
             ecx.nested_goals.is_empty(),
             "root `EvalCtxt` should not have any goals added to it"
         );
 
         assert!(search_graph.is_empty());
-        result
+        (result, tree)
     }
 }
 
@@ -170,58 +192,72 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
     /// Instead of calling this function directly, use either [EvalCtxt::evaluate_goal]
     /// if you're inside of the solver or [InferCtxtEvalExt::evaluate_root_goal] if you're
     /// outside of it.
-    #[instrument(level = "debug", skip(tcx, search_graph), ret)]
+    #[instrument(level = "debug", skip(tcx, search_graph, goal_evaluation), ret)]
     fn evaluate_canonical_goal(
         tcx: TyCtxt<'tcx>,
         search_graph: &'a mut search_graph::SearchGraph<'tcx>,
         canonical_input: CanonicalInput<'tcx>,
+        mut goal_evaluation: &mut ProofTreeBuilder<'tcx>,
     ) -> QueryResult<'tcx> {
+        goal_evaluation.canonicalized_goal(canonical_input);
+
         // Deal with overflow, caching, and coinduction.
         //
         // The actual solver logic happens in `ecx.compute_goal`.
-        search_graph.with_new_goal(tcx, canonical_input, |search_graph| {
-            let intercrate = match search_graph.solver_mode() {
-                SolverMode::Normal => false,
-                SolverMode::Coherence => true,
-            };
-            let (ref infcx, input, var_values) = tcx
-                .infer_ctxt()
-                .intercrate(intercrate)
-                .with_next_trait_solver(true)
-                .with_opaque_type_inference(canonical_input.value.anchor)
-                .build_with_canonical(DUMMY_SP, &canonical_input);
-
-            let mut ecx = EvalCtxt {
-                infcx,
-                var_values,
-                predefined_opaques_in_body: input.predefined_opaques_in_body,
-                max_input_universe: canonical_input.max_universe,
-                search_graph,
-                nested_goals: NestedGoals::new(),
-                tainted: Ok(()),
-            };
-
-            for &(key, ty) in &input.predefined_opaques_in_body.opaque_types {
-                ecx.insert_hidden_type(key, input.goal.param_env, ty)
-                    .expect("failed to prepopulate opaque types");
-            }
+        search_graph.with_new_goal(
+            tcx,
+            canonical_input,
+            goal_evaluation,
+            |search_graph, goal_evaluation| {
+                let intercrate = match search_graph.solver_mode() {
+                    SolverMode::Normal => false,
+                    SolverMode::Coherence => true,
+                };
+                let (ref infcx, input, var_values) = tcx
+                    .infer_ctxt()
+                    .intercrate(intercrate)
+                    .with_next_trait_solver(true)
+                    .with_opaque_type_inference(canonical_input.value.anchor)
+                    .build_with_canonical(DUMMY_SP, &canonical_input);
+
+                let mut ecx = EvalCtxt {
+                    infcx,
+                    var_values,
+                    predefined_opaques_in_body: input.predefined_opaques_in_body,
+                    max_input_universe: canonical_input.max_universe,
+                    search_graph,
+                    nested_goals: NestedGoals::new(),
+                    tainted: Ok(()),
+                    inspect: goal_evaluation.new_goal_evaluation_step(input),
+                };
+
+                for &(key, ty) in &input.predefined_opaques_in_body.opaque_types {
+                    ecx.insert_hidden_type(key, input.goal.param_env, ty)
+                        .expect("failed to prepopulate opaque types");
+                }
 
-            if !ecx.nested_goals.is_empty() {
-                panic!("prepopulating opaque types shouldn't add goals: {:?}", ecx.nested_goals);
-            }
+                if !ecx.nested_goals.is_empty() {
+                    panic!(
+                        "prepopulating opaque types shouldn't add goals: {:?}",
+                        ecx.nested_goals
+                    );
+                }
 
-            let result = ecx.compute_goal(input.goal);
+                let result = ecx.compute_goal(input.goal);
+                ecx.inspect.query_result(result);
+                goal_evaluation.goal_evaluation_step(ecx.inspect);
 
-            // When creating a query response we clone the opaque type constraints
-            // instead of taking them. This would cause an ICE here, since we have
-            // assertions against dropping an `InferCtxt` without taking opaques.
-            // FIXME: Once we remove support for the old impl we can remove this.
-            if input.anchor != DefiningAnchor::Error {
-                let _ = infcx.take_opaque_types();
-            }
+                // When creating a query response we clone the opaque type constraints
+                // instead of taking them. This would cause an ICE here, since we have
+                // assertions against dropping an `InferCtxt` without taking opaques.
+                // FIXME: Once we remove support for the old impl we can remove this.
+                if input.anchor != DefiningAnchor::Error {
+                    let _ = infcx.take_opaque_types();
+                }
 
-            result
-        })
+                result
+            },
+        )
     }
 
     /// Recursively evaluates `goal`, returning whether any inference vars have
@@ -232,16 +268,37 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
         goal: Goal<'tcx, ty::Predicate<'tcx>>,
     ) -> Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution> {
         let (orig_values, canonical_goal) = self.canonicalize_goal(goal);
-        let canonical_response =
-            EvalCtxt::evaluate_canonical_goal(self.tcx(), self.search_graph, canonical_goal)?;
+        let mut goal_evaluation = self.inspect.new_goal_evaluation(goal, is_normalizes_to_hack);
+        let canonical_response = EvalCtxt::evaluate_canonical_goal(
+            self.tcx(),
+            self.search_graph,
+            canonical_goal,
+            &mut goal_evaluation,
+        );
+        goal_evaluation.query_result(canonical_response);
+        let canonical_response = match canonical_response {
+            Err(e) => {
+                self.inspect.goal_evaluation(goal_evaluation);
+                return Err(e);
+            }
+            Ok(response) => response,
+        };
 
         let has_changed = !canonical_response.value.var_values.is_identity()
             || !canonical_response.value.external_constraints.opaque_types.is_empty();
-        let (certainty, nested_goals) = self.instantiate_and_apply_query_response(
+        let (certainty, nested_goals) = match self.instantiate_and_apply_query_response(
             goal.param_env,
             orig_values,
             canonical_response,
-        )?;
+        ) {
+            Err(e) => {
+                self.inspect.goal_evaluation(goal_evaluation);
+                return Err(e);
+            }
+            Ok(response) => response,
+        };
+        goal_evaluation.returned_goals(&nested_goals);
+        self.inspect.goal_evaluation(goal_evaluation);
 
         if !has_changed && !nested_goals.is_empty() {
             bug!("an unchanged goal shouldn't have any side-effects on instantiation");
@@ -261,8 +318,13 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
         {
             debug!("rerunning goal to check result is stable");
             let (_orig_values, canonical_goal) = self.canonicalize_goal(goal);
-            let new_canonical_response =
-                EvalCtxt::evaluate_canonical_goal(self.tcx(), self.search_graph, canonical_goal)?;
+            let new_canonical_response = EvalCtxt::evaluate_canonical_goal(
+                self.tcx(),
+                self.search_graph,
+                canonical_goal,
+                // FIXME(-Ztrait-solver=next): we do not track what happens in `evaluate_canonical_goal`
+                &mut ProofTreeBuilder::new_noop(),
+            )?;
             // We only check for modulo regions as we convert all regions in
             // the input to new existentials, even if they're expected to be
             // `'static` or a placeholder region.
@@ -353,12 +415,17 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
     // the certainty of all the goals.
     #[instrument(level = "debug", skip(self))]
     pub(super) fn try_evaluate_added_goals(&mut self) -> Result<Certainty, NoSolution> {
+        let inspect = self.inspect.new_evaluate_added_goals();
+        let inspect = core::mem::replace(&mut self.inspect, inspect);
+
         let mut goals = core::mem::replace(&mut self.nested_goals, NestedGoals::new());
         let mut new_goals = NestedGoals::new();
 
         let response = self.repeat_while_none(
             |_| Ok(Certainty::Maybe(MaybeCause::Overflow)),
             |this| {
+                this.inspect.evaluate_added_goals_loop_start();
+
                 let mut has_changed = Err(Certainty::Yes);
 
                 if let Some(goal) = goals.normalizes_to_hack_goal.take() {
@@ -447,17 +514,28 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
             },
         );
 
+        self.inspect.eval_added_goals_result(response);
+
         if response.is_err() {
             self.tainted = Err(NoSolution);
         }
 
+        let goal_evaluations = std::mem::replace(&mut self.inspect, inspect);
+        self.inspect.added_goals_evaluation(goal_evaluations);
+
         self.nested_goals = goals;
         response
     }
 }
 
 impl<'tcx> EvalCtxt<'_, 'tcx> {
-    pub(super) fn probe<T>(&mut self, f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> T) -> T {
+    /// `probe_kind` is only called when proof tree building is enabled so it can be
+    /// as expensive as necessary to output the desired information.
+    pub(super) fn probe<T>(
+        &mut self,
+        f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> T,
+        probe_kind: impl FnOnce(&T) -> CandidateKind<'tcx>,
+    ) -> T {
         let mut ecx = EvalCtxt {
             infcx: self.infcx,
             var_values: self.var_values,
@@ -466,8 +544,15 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
             search_graph: self.search_graph,
             nested_goals: self.nested_goals.clone(),
             tainted: self.tainted,
+            inspect: self.inspect.new_goal_candidate(),
         };
-        self.infcx.probe(|_| f(&mut ecx))
+        let r = self.infcx.probe(|_| f(&mut ecx));
+        if !self.inspect.is_noop() {
+            let cand_kind = probe_kind(&r);
+            ecx.inspect.candidate_kind(cand_kind);
+            self.inspect.goal_candidate(ecx.inspect);
+        }
+        r
     }
 
     pub(super) fn tcx(&self) -> TyCtxt<'tcx> {
@@ -750,13 +835,15 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
 
     pub(super) fn add_item_bounds_for_hidden_type(
         &mut self,
-        opaque_type_key: OpaqueTypeKey<'tcx>,
+        opaque_def_id: DefId,
+        opaque_substs: ty::SubstsRef<'tcx>,
         param_env: ty::ParamEnv<'tcx>,
         hidden_ty: Ty<'tcx>,
     ) {
         let mut obligations = Vec::new();
         self.infcx.add_item_bounds_for_hidden_type(
-            opaque_type_key,
+            opaque_def_id,
+            opaque_substs,
             ObligationCause::dummy(),
             param_env,
             hidden_ty,
@@ -781,14 +868,22 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
             if candidate_key.def_id != key.def_id {
                 continue;
             }
-            values.extend(self.probe(|ecx| {
-                for (a, b) in std::iter::zip(candidate_key.substs, key.substs) {
-                    ecx.eq(param_env, a, b)?;
-                }
-                ecx.eq(param_env, candidate_ty, ty)?;
-                ecx.add_item_bounds_for_hidden_type(candidate_key, param_env, candidate_ty);
-                ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
-            }));
+            values.extend(self.probe(
+                |ecx| {
+                    for (a, b) in std::iter::zip(candidate_key.substs, key.substs) {
+                        ecx.eq(param_env, a, b)?;
+                    }
+                    ecx.eq(param_env, candidate_ty, ty)?;
+                    ecx.add_item_bounds_for_hidden_type(
+                        candidate_key.def_id.to_def_id(),
+                        candidate_key.substs,
+                        param_env,
+                        candidate_ty,
+                    );
+                    ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+                },
+                |r| CandidateKind::Candidate { name: "opaque type storage".into(), result: *r },
+            ));
         }
         values
     }
diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs
index 65c8d9c8f69..0e671144a29 100644
--- a/compiler/rustc_trait_selection/src/solve/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs
@@ -10,6 +10,7 @@ use rustc_infer::traits::{
 use rustc_middle::ty;
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
 
+use super::eval_ctxt::GenerateProofTree;
 use super::{Certainty, InferCtxtEvalExt};
 
 /// A trait engine using the new trait solver.
@@ -46,8 +47,11 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
         self.obligations
             .drain(..)
             .map(|obligation| {
-                let code =
-                    infcx.probe(|_| match infcx.evaluate_root_goal(obligation.clone().into()) {
+                let code = infcx.probe(|_| {
+                    match infcx
+                        .evaluate_root_goal(obligation.clone().into(), GenerateProofTree::No)
+                        .0
+                    {
                         Ok((_, Certainty::Maybe(MaybeCause::Ambiguity), _)) => {
                             FulfillmentErrorCode::CodeAmbiguity { overflow: false }
                         }
@@ -60,7 +64,8 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
                         Err(_) => {
                             bug!("did not expect selection error when collecting ambiguity errors")
                         }
-                    });
+                    }
+                });
 
                 FulfillmentError {
                     obligation: obligation.clone(),
@@ -81,61 +86,62 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
             let mut has_changed = false;
             for obligation in mem::take(&mut self.obligations) {
                 let goal = obligation.clone().into();
-                let (changed, certainty, nested_goals) = match infcx.evaluate_root_goal(goal) {
-                    Ok(result) => result,
-                    Err(NoSolution) => {
-                        errors.push(FulfillmentError {
-                            obligation: obligation.clone(),
-                            code: match goal.predicate.kind().skip_binder() {
-                                ty::PredicateKind::Clause(ty::Clause::Projection(_)) => {
-                                    FulfillmentErrorCode::CodeProjectionError(
-                                        // FIXME: This could be a `Sorts` if the term is a type
-                                        MismatchedProjectionTypes { err: TypeError::Mismatch },
-                                    )
-                                }
-                                ty::PredicateKind::AliasRelate(_, _, _) => {
-                                    FulfillmentErrorCode::CodeProjectionError(
-                                        MismatchedProjectionTypes { err: TypeError::Mismatch },
-                                    )
-                                }
-                                ty::PredicateKind::Subtype(pred) => {
-                                    let (a, b) = infcx.instantiate_binder_with_placeholders(
-                                        goal.predicate.kind().rebind((pred.a, pred.b)),
-                                    );
-                                    let expected_found = ExpectedFound::new(true, a, b);
-                                    FulfillmentErrorCode::CodeSubtypeError(
-                                        expected_found,
-                                        TypeError::Sorts(expected_found),
-                                    )
-                                }
-                                ty::PredicateKind::Coerce(pred) => {
-                                    let (a, b) = infcx.instantiate_binder_with_placeholders(
-                                        goal.predicate.kind().rebind((pred.a, pred.b)),
-                                    );
-                                    let expected_found = ExpectedFound::new(false, a, b);
-                                    FulfillmentErrorCode::CodeSubtypeError(
-                                        expected_found,
-                                        TypeError::Sorts(expected_found),
-                                    )
-                                }
-                                ty::PredicateKind::Clause(_)
-                                | ty::PredicateKind::ObjectSafe(_)
-                                | ty::PredicateKind::ClosureKind(_, _, _)
-                                | ty::PredicateKind::Ambiguous => {
-                                    FulfillmentErrorCode::CodeSelectionError(
-                                        SelectionError::Unimplemented,
-                                    )
-                                }
-                                ty::PredicateKind::ConstEquate(..)
-                                | ty::PredicateKind::TypeWellFormedFromEnv(_) => {
-                                    bug!("unexpected goal: {goal:?}")
-                                }
-                            },
-                            root_obligation: obligation,
-                        });
-                        continue;
-                    }
-                };
+                let (changed, certainty, nested_goals) =
+                    match infcx.evaluate_root_goal(goal, GenerateProofTree::No).0 {
+                        Ok(result) => result,
+                        Err(NoSolution) => {
+                            errors.push(FulfillmentError {
+                                obligation: obligation.clone(),
+                                code: match goal.predicate.kind().skip_binder() {
+                                    ty::PredicateKind::Clause(ty::Clause::Projection(_)) => {
+                                        FulfillmentErrorCode::CodeProjectionError(
+                                            // FIXME: This could be a `Sorts` if the term is a type
+                                            MismatchedProjectionTypes { err: TypeError::Mismatch },
+                                        )
+                                    }
+                                    ty::PredicateKind::AliasRelate(_, _, _) => {
+                                        FulfillmentErrorCode::CodeProjectionError(
+                                            MismatchedProjectionTypes { err: TypeError::Mismatch },
+                                        )
+                                    }
+                                    ty::PredicateKind::Subtype(pred) => {
+                                        let (a, b) = infcx.instantiate_binder_with_placeholders(
+                                            goal.predicate.kind().rebind((pred.a, pred.b)),
+                                        );
+                                        let expected_found = ExpectedFound::new(true, a, b);
+                                        FulfillmentErrorCode::CodeSubtypeError(
+                                            expected_found,
+                                            TypeError::Sorts(expected_found),
+                                        )
+                                    }
+                                    ty::PredicateKind::Coerce(pred) => {
+                                        let (a, b) = infcx.instantiate_binder_with_placeholders(
+                                            goal.predicate.kind().rebind((pred.a, pred.b)),
+                                        );
+                                        let expected_found = ExpectedFound::new(false, a, b);
+                                        FulfillmentErrorCode::CodeSubtypeError(
+                                            expected_found,
+                                            TypeError::Sorts(expected_found),
+                                        )
+                                    }
+                                    ty::PredicateKind::Clause(_)
+                                    | ty::PredicateKind::ObjectSafe(_)
+                                    | ty::PredicateKind::ClosureKind(_, _, _)
+                                    | ty::PredicateKind::Ambiguous => {
+                                        FulfillmentErrorCode::CodeSelectionError(
+                                            SelectionError::Unimplemented,
+                                        )
+                                    }
+                                    ty::PredicateKind::ConstEquate(..)
+                                    | ty::PredicateKind::TypeWellFormedFromEnv(_) => {
+                                        bug!("unexpected goal: {goal:?}")
+                                    }
+                                },
+                                root_obligation: obligation,
+                            });
+                            continue;
+                        }
+                    };
                 // Push any nested goals that we get from unifying our canonical response
                 // with our obligation onto the fulfillment context.
                 self.obligations.extend(nested_goals.into_iter().map(|goal| {
diff --git a/compiler/rustc_trait_selection/src/solve/inspect.rs b/compiler/rustc_trait_selection/src/solve/inspect.rs
new file mode 100644
index 00000000000..c3dea5f790a
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/inspect.rs
@@ -0,0 +1,367 @@
+use rustc_middle::{
+    traits::{
+        query::NoSolution,
+        solve::{
+            inspect::{self, CacheHit, CandidateKind},
+            CanonicalInput, Certainty, Goal, QueryInput, QueryResult,
+        },
+        IsNormalizesToHack,
+    },
+    ty,
+};
+
+pub mod dump;
+
+#[derive(Eq, PartialEq, Debug, Hash, HashStable)]
+pub struct WipGoalEvaluation<'tcx> {
+    pub uncanonicalized_goal: Goal<'tcx, ty::Predicate<'tcx>>,
+    pub canonicalized_goal: Option<CanonicalInput<'tcx>>,
+
+    pub evaluation_steps: Vec<WipGoalEvaluationStep<'tcx>>,
+
+    pub cache_hit: Option<CacheHit>,
+    pub is_normalizes_to_hack: IsNormalizesToHack,
+    pub returned_goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
+
+    pub result: Option<QueryResult<'tcx>>,
+}
+impl<'tcx> WipGoalEvaluation<'tcx> {
+    pub fn finalize(self) -> inspect::GoalEvaluation<'tcx> {
+        inspect::GoalEvaluation {
+            uncanonicalized_goal: self.uncanonicalized_goal,
+            canonicalized_goal: self.canonicalized_goal.unwrap(),
+            kind: match self.cache_hit {
+                Some(hit) => inspect::GoalEvaluationKind::CacheHit(hit),
+                None => inspect::GoalEvaluationKind::Uncached {
+                    revisions: self
+                        .evaluation_steps
+                        .into_iter()
+                        .map(WipGoalEvaluationStep::finalize)
+                        .collect(),
+                },
+            },
+            is_normalizes_to_hack: self.is_normalizes_to_hack,
+            returned_goals: self.returned_goals,
+            result: self.result.unwrap(),
+        }
+    }
+}
+
+#[derive(Eq, PartialEq, Debug, Hash, HashStable)]
+pub struct WipAddedGoalsEvaluation<'tcx> {
+    pub evaluations: Vec<Vec<WipGoalEvaluation<'tcx>>>,
+    pub result: Option<Result<Certainty, NoSolution>>,
+}
+impl<'tcx> WipAddedGoalsEvaluation<'tcx> {
+    pub fn finalize(self) -> inspect::AddedGoalsEvaluation<'tcx> {
+        inspect::AddedGoalsEvaluation {
+            evaluations: self
+                .evaluations
+                .into_iter()
+                .map(|evaluations| {
+                    evaluations.into_iter().map(WipGoalEvaluation::finalize).collect()
+                })
+                .collect(),
+            result: self.result.unwrap(),
+        }
+    }
+}
+
+#[derive(Eq, PartialEq, Debug, Hash, HashStable)]
+pub struct WipGoalEvaluationStep<'tcx> {
+    pub instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>,
+
+    pub nested_goal_evaluations: Vec<WipAddedGoalsEvaluation<'tcx>>,
+    pub candidates: Vec<WipGoalCandidate<'tcx>>,
+
+    pub result: Option<QueryResult<'tcx>>,
+}
+impl<'tcx> WipGoalEvaluationStep<'tcx> {
+    pub fn finalize(self) -> inspect::GoalEvaluationStep<'tcx> {
+        inspect::GoalEvaluationStep {
+            instantiated_goal: self.instantiated_goal,
+            nested_goal_evaluations: self
+                .nested_goal_evaluations
+                .into_iter()
+                .map(WipAddedGoalsEvaluation::finalize)
+                .collect(),
+            candidates: self.candidates.into_iter().map(WipGoalCandidate::finalize).collect(),
+            result: self.result.unwrap(),
+        }
+    }
+}
+
+#[derive(Eq, PartialEq, Debug, Hash, HashStable)]
+pub struct WipGoalCandidate<'tcx> {
+    pub nested_goal_evaluations: Vec<WipAddedGoalsEvaluation<'tcx>>,
+    pub candidates: Vec<WipGoalCandidate<'tcx>>,
+    pub kind: Option<CandidateKind<'tcx>>,
+}
+impl<'tcx> WipGoalCandidate<'tcx> {
+    pub fn finalize(self) -> inspect::GoalCandidate<'tcx> {
+        inspect::GoalCandidate {
+            nested_goal_evaluations: self
+                .nested_goal_evaluations
+                .into_iter()
+                .map(WipAddedGoalsEvaluation::finalize)
+                .collect(),
+            candidates: self.candidates.into_iter().map(WipGoalCandidate::finalize).collect(),
+            kind: self.kind.unwrap(),
+        }
+    }
+}
+
+#[derive(Debug)]
+pub enum DebugSolver<'tcx> {
+    Root,
+    GoalEvaluation(WipGoalEvaluation<'tcx>),
+    AddedGoalsEvaluation(WipAddedGoalsEvaluation<'tcx>),
+    GoalEvaluationStep(WipGoalEvaluationStep<'tcx>),
+    GoalCandidate(WipGoalCandidate<'tcx>),
+}
+
+pub struct ProofTreeBuilder<'tcx>(Option<Box<DebugSolver<'tcx>>>);
+impl<'tcx> ProofTreeBuilder<'tcx> {
+    pub fn finalize(self) -> Option<inspect::GoalEvaluation<'tcx>> {
+        match *(self.0?) {
+            DebugSolver::GoalEvaluation(wip_goal_evaluation) => {
+                Some(wip_goal_evaluation.finalize())
+            }
+            root => unreachable!("unexpected proof tree builder root node: {:?}", root),
+        }
+    }
+
+    pub fn new_root() -> ProofTreeBuilder<'tcx> {
+        Self(Some(Box::new(DebugSolver::Root)))
+    }
+
+    pub fn new_noop() -> ProofTreeBuilder<'tcx> {
+        Self(None)
+    }
+
+    pub fn is_noop(&self) -> bool {
+        self.0.is_none()
+    }
+
+    pub fn new_goal_evaluation(
+        &mut self,
+        goal: Goal<'tcx, ty::Predicate<'tcx>>,
+        is_normalizes_to_hack: IsNormalizesToHack,
+    ) -> ProofTreeBuilder<'tcx> {
+        if self.0.is_none() {
+            return ProofTreeBuilder(None);
+        }
+
+        Self(Some(Box::new(DebugSolver::GoalEvaluation(WipGoalEvaluation {
+            uncanonicalized_goal: goal,
+            canonicalized_goal: None,
+            evaluation_steps: vec![],
+            is_normalizes_to_hack,
+            cache_hit: None,
+            returned_goals: vec![],
+            result: None,
+        }))))
+    }
+    pub fn canonicalized_goal(&mut self, canonical_goal: CanonicalInput<'tcx>) {
+        let this = match self.0.as_mut() {
+            None => return,
+            Some(this) => &mut **this,
+        };
+
+        match this {
+            DebugSolver::GoalEvaluation(goal_evaluation) => {
+                assert!(goal_evaluation.canonicalized_goal.is_none());
+                goal_evaluation.canonicalized_goal = Some(canonical_goal)
+            }
+            _ => unreachable!(),
+        }
+    }
+    pub fn cache_hit(&mut self, cache_hit: CacheHit) {
+        let this = match self.0.as_mut() {
+            None => return,
+            Some(this) => &mut **this,
+        };
+
+        match this {
+            DebugSolver::GoalEvaluation(goal_evaluation) => {
+                goal_evaluation.cache_hit = Some(cache_hit)
+            }
+            _ => unreachable!(),
+        };
+    }
+    pub fn returned_goals(&mut self, goals: &[Goal<'tcx, ty::Predicate<'tcx>>]) {
+        let this = match self.0.as_mut() {
+            None => return,
+            Some(this) => &mut **this,
+        };
+
+        match this {
+            DebugSolver::GoalEvaluation(evaluation) => {
+                assert!(evaluation.returned_goals.is_empty());
+                evaluation.returned_goals.extend(goals);
+            }
+            _ => unreachable!(),
+        }
+    }
+    pub fn goal_evaluation(&mut self, goal_evaluation: ProofTreeBuilder<'tcx>) {
+        let this = match self.0.as_mut() {
+            None => return,
+            Some(this) => &mut **this,
+        };
+
+        match (this, *goal_evaluation.0.unwrap()) {
+            (
+                DebugSolver::AddedGoalsEvaluation(WipAddedGoalsEvaluation { evaluations, .. }),
+                DebugSolver::GoalEvaluation(goal_evaluation),
+            ) => evaluations.last_mut().unwrap().push(goal_evaluation),
+            (this @ DebugSolver::Root, goal_evaluation) => *this = goal_evaluation,
+            _ => unreachable!(),
+        }
+    }
+
+    pub fn new_goal_evaluation_step(
+        &mut self,
+        instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>,
+    ) -> ProofTreeBuilder<'tcx> {
+        if self.0.is_none() {
+            return Self(None);
+        }
+
+        Self(Some(Box::new(DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep {
+            instantiated_goal,
+            nested_goal_evaluations: vec![],
+            candidates: vec![],
+            result: None,
+        }))))
+    }
+    pub fn goal_evaluation_step(&mut self, goal_eval_step: ProofTreeBuilder<'tcx>) {
+        let this = match self.0.as_mut() {
+            None => return,
+            Some(this) => &mut **this,
+        };
+
+        match (this, *goal_eval_step.0.unwrap()) {
+            (DebugSolver::GoalEvaluation(goal_eval), DebugSolver::GoalEvaluationStep(step)) => {
+                goal_eval.evaluation_steps.push(step);
+            }
+            _ => unreachable!(),
+        }
+    }
+
+    pub fn new_goal_candidate(&mut self) -> ProofTreeBuilder<'tcx> {
+        if self.0.is_none() {
+            return Self(None);
+        }
+
+        Self(Some(Box::new(DebugSolver::GoalCandidate(WipGoalCandidate {
+            nested_goal_evaluations: vec![],
+            candidates: vec![],
+            kind: None,
+        }))))
+    }
+    pub fn candidate_kind(&mut self, kind: CandidateKind<'tcx>) {
+        let this = match self.0.as_mut() {
+            None => return,
+            Some(this) => &mut **this,
+        };
+
+        match this {
+            DebugSolver::GoalCandidate(WipGoalCandidate { kind: old_kind @ None, .. }) => {
+                *old_kind = Some(kind)
+            }
+            _ => unreachable!(),
+        }
+    }
+    pub fn goal_candidate(&mut self, candidate: ProofTreeBuilder<'tcx>) {
+        let this = match self.0.as_mut() {
+            None => return,
+            Some(this) => &mut **this,
+        };
+
+        match (this, *candidate.0.unwrap()) {
+            (
+                DebugSolver::GoalCandidate(WipGoalCandidate { candidates, .. })
+                | DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep { candidates, .. }),
+                DebugSolver::GoalCandidate(candidate),
+            ) => candidates.push(candidate),
+            _ => unreachable!(),
+        }
+    }
+
+    pub fn new_evaluate_added_goals(&mut self) -> ProofTreeBuilder<'tcx> {
+        if self.0.is_none() {
+            return Self(None);
+        }
+
+        Self(Some(Box::new(DebugSolver::AddedGoalsEvaluation(WipAddedGoalsEvaluation {
+            evaluations: vec![],
+            result: None,
+        }))))
+    }
+    pub fn evaluate_added_goals_loop_start(&mut self) {
+        let this = match self.0.as_mut() {
+            None => return,
+            Some(this) => &mut **this,
+        };
+
+        match this {
+            DebugSolver::AddedGoalsEvaluation(this) => {
+                this.evaluations.push(vec![]);
+            }
+            _ => unreachable!(),
+        }
+    }
+    pub fn eval_added_goals_result(&mut self, result: Result<Certainty, NoSolution>) {
+        let this = match self.0.as_mut() {
+            None => return,
+            Some(this) => &mut **this,
+        };
+
+        match this {
+            DebugSolver::AddedGoalsEvaluation(this) => {
+                assert!(this.result.is_none());
+                this.result = Some(result);
+            }
+            _ => unreachable!(),
+        }
+    }
+    pub fn added_goals_evaluation(&mut self, goals_evaluation: ProofTreeBuilder<'tcx>) {
+        let this = match self.0.as_mut() {
+            None => return,
+            Some(this) => &mut **this,
+        };
+
+        match (this, *goals_evaluation.0.unwrap()) {
+            (
+                DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep {
+                    nested_goal_evaluations,
+                    ..
+                })
+                | DebugSolver::GoalCandidate(WipGoalCandidate { nested_goal_evaluations, .. }),
+                DebugSolver::AddedGoalsEvaluation(added_goals_evaluation),
+            ) => nested_goal_evaluations.push(added_goals_evaluation),
+            _ => unreachable!(),
+        }
+    }
+
+    pub fn query_result(&mut self, result: QueryResult<'tcx>) {
+        let this = match self.0.as_mut() {
+            None => return,
+            Some(this) => &mut **this,
+        };
+
+        match this {
+            DebugSolver::GoalEvaluation(goal_evaluation) => {
+                assert!(goal_evaluation.result.is_none());
+                goal_evaluation.result = Some(result);
+            }
+            DebugSolver::Root
+            | DebugSolver::AddedGoalsEvaluation(_)
+            | DebugSolver::GoalCandidate(_) => unreachable!(),
+            DebugSolver::GoalEvaluationStep(evaluation_step) => {
+                assert!(evaluation_step.result.is_none());
+                evaluation_step.result = Some(result);
+            }
+        }
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/inspect/dump.rs b/compiler/rustc_trait_selection/src/solve/inspect/dump.rs
new file mode 100644
index 00000000000..b755ee86215
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/inspect/dump.rs
@@ -0,0 +1,5 @@
+use rustc_middle::traits::solve::inspect::GoalEvaluation;
+
+pub fn print_tree(tree: &GoalEvaluation<'_>) {
+    debug!(?tree);
+}
diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs
index a30a14df80b..49fecedc0ca 100644
--- a/compiler/rustc_trait_selection/src/solve/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/mod.rs
@@ -25,6 +25,7 @@ mod assembly;
 mod canonicalize;
 mod eval_ctxt;
 mod fulfill;
+pub mod inspect;
 mod opaques;
 mod project_goals;
 mod search_graph;
diff --git a/compiler/rustc_trait_selection/src/solve/opaques.rs b/compiler/rustc_trait_selection/src/solve/opaques.rs
index 538c16c8ce2..16194f5ad69 100644
--- a/compiler/rustc_trait_selection/src/solve/opaques.rs
+++ b/compiler/rustc_trait_selection/src/solve/opaques.rs
@@ -20,8 +20,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
                 let Some(opaque_ty_def_id) = opaque_ty.def_id.as_local() else {
                     return Err(NoSolution);
                 };
-                let opaque_ty =
-                    ty::OpaqueTypeKey { def_id: opaque_ty_def_id, substs: opaque_ty.substs };
                 // FIXME: at some point we should call queries without defining
                 // new opaque types but having the existing opaque type definitions.
                 // This will require moving this below "Prefer opaques registered already".
@@ -41,7 +39,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
                     Ok(()) => {}
                 }
                 // Prefer opaques registered already.
-                let matches = self.unify_existing_opaque_tys(goal.param_env, opaque_ty, expected);
+                let opaque_type_key =
+                    ty::OpaqueTypeKey { def_id: opaque_ty_def_id, substs: opaque_ty.substs };
+                let matches =
+                    self.unify_existing_opaque_tys(goal.param_env, opaque_type_key, expected);
                 if !matches.is_empty() {
                     if let Some(response) = self.try_merge_responses(&matches) {
                         return Ok(response);
@@ -50,11 +51,24 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
                     }
                 }
                 // Otherwise, define a new opaque type
-                self.insert_hidden_type(opaque_ty, goal.param_env, expected)?;
-                self.add_item_bounds_for_hidden_type(opaque_ty, goal.param_env, expected);
+                self.insert_hidden_type(opaque_type_key, goal.param_env, expected)?;
+                self.add_item_bounds_for_hidden_type(
+                    opaque_ty.def_id,
+                    opaque_ty.substs,
+                    goal.param_env,
+                    expected,
+                );
                 self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
             }
             (Reveal::UserFacing, SolverMode::Coherence) => {
+                // An impossible opaque type bound is the only way this goal will fail
+                // e.g. assigning `impl Copy := NotCopy`
+                self.add_item_bounds_for_hidden_type(
+                    opaque_ty.def_id,
+                    opaque_ty.substs,
+                    goal.param_env,
+                    expected,
+                );
                 self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
             }
             (Reveal::All, _) => {
diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs
index e9600968f48..b99c3927862 100644
--- a/compiler/rustc_trait_selection/src/solve/project_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs
@@ -9,6 +9,7 @@ use rustc_hir::LangItem;
 use rustc_infer::traits::query::NoSolution;
 use rustc_infer::traits::specialization_graph::LeafDef;
 use rustc_infer::traits::Reveal;
+use rustc_middle::traits::solve::inspect::CandidateKind;
 use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult};
 use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
 use rustc_middle::ty::ProjectionPredicate;
@@ -109,21 +110,30 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
         assumption: ty::Binder<'tcx, ty::Clause<'tcx>>,
         then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>,
     ) -> QueryResult<'tcx> {
-        if let Some(projection_pred) = assumption.as_projection_clause()
-            && projection_pred.projection_def_id() == goal.predicate.def_id()
-        {
-            ecx.probe(|ecx| {
-                let assumption_projection_pred =
-                    ecx.instantiate_binder_with_infer(projection_pred);
-                ecx.eq(
-                    goal.param_env,
-                    goal.predicate.projection_ty,
-                    assumption_projection_pred.projection_ty,
-                )?;
-                ecx.eq(goal.param_env, goal.predicate.term, assumption_projection_pred.term)
-                    .expect("expected goal term to be fully unconstrained");
-                then(ecx)
-            })
+        if let Some(projection_pred) = assumption.as_projection_clause() {
+            if projection_pred.projection_def_id() == goal.predicate.def_id() {
+                ecx.probe(
+                    |ecx| {
+                        let assumption_projection_pred =
+                            ecx.instantiate_binder_with_infer(projection_pred);
+                        ecx.eq(
+                            goal.param_env,
+                            goal.predicate.projection_ty,
+                            assumption_projection_pred.projection_ty,
+                        )?;
+                        ecx.eq(
+                            goal.param_env,
+                            goal.predicate.term,
+                            assumption_projection_pred.term,
+                        )
+                        .expect("expected goal term to be fully unconstrained");
+                        then(ecx)
+                    },
+                    |r| CandidateKind::Candidate { name: "assumption".into(), result: *r },
+                )
+            } else {
+                Err(NoSolution)
+            }
         } else {
             Err(NoSolution)
         }
@@ -143,87 +153,90 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
             return Err(NoSolution);
         }
 
-        ecx.probe(|ecx| {
-            let impl_substs = ecx.fresh_substs_for_item(impl_def_id);
-            let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs);
-
-            ecx.eq(goal.param_env, goal_trait_ref, impl_trait_ref)?;
-
-            let where_clause_bounds = tcx
-                .predicates_of(impl_def_id)
-                .instantiate(tcx, impl_substs)
-                .predicates
-                .into_iter()
-                .map(|pred| goal.with(tcx, pred));
-            ecx.add_goals(where_clause_bounds);
-
-            // In case the associated item is hidden due to specialization, we have to
-            // return ambiguity this would otherwise be incomplete, resulting in
-            // unsoundness during coherence (#105782).
-            let Some(assoc_def) = fetch_eligible_assoc_item_def(
-                ecx,
-                goal.param_env,
-                goal_trait_ref,
-                goal.predicate.def_id(),
-                impl_def_id
-            )? else {
-                return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
-            };
+        ecx.probe(
+            |ecx| {
+                let impl_substs = ecx.fresh_substs_for_item(impl_def_id);
+                let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs);
+
+                ecx.eq(goal.param_env, goal_trait_ref, impl_trait_ref)?;
+
+                let where_clause_bounds = tcx
+                    .predicates_of(impl_def_id)
+                    .instantiate(tcx, impl_substs)
+                    .predicates
+                    .into_iter()
+                    .map(|pred| goal.with(tcx, pred));
+                ecx.add_goals(where_clause_bounds);
+
+                // In case the associated item is hidden due to specialization, we have to
+                // return ambiguity this would otherwise be incomplete, resulting in
+                // unsoundness during coherence (#105782).
+                let Some(assoc_def) = fetch_eligible_assoc_item_def(
+                    ecx,
+                    goal.param_env,
+                    goal_trait_ref,
+                    goal.predicate.def_id(),
+                    impl_def_id
+                )? else {
+                    return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
+                };
+
+                if !assoc_def.item.defaultness(tcx).has_value() {
+                    let guar = tcx.sess.delay_span_bug(
+                        tcx.def_span(assoc_def.item.def_id),
+                        "missing value for assoc item in impl",
+                    );
+                    let error_term = match assoc_def.item.kind {
+                        ty::AssocKind::Const => tcx
+                            .const_error(
+                                tcx.type_of(goal.predicate.def_id())
+                                    .subst(tcx, goal.predicate.projection_ty.substs),
+                                guar,
+                            )
+                            .into(),
+                        ty::AssocKind::Type => tcx.ty_error(guar).into(),
+                        ty::AssocKind::Fn => unreachable!(),
+                    };
+                    ecx.eq(goal.param_env, goal.predicate.term, error_term)
+                        .expect("expected goal term to be fully unconstrained");
+                    return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
+                }
 
-            if !assoc_def.item.defaultness(tcx).has_value() {
-                let guar = tcx.sess.delay_span_bug(
-                    tcx.def_span(assoc_def.item.def_id),
-                    "missing value for assoc item in impl",
+                // Getting the right substitutions here is complex, e.g. given:
+                // - a goal `<Vec<u32> as Trait<i32>>::Assoc<u64>`
+                // - the applicable impl `impl<T> Trait<i32> for Vec<T>`
+                // - and the impl which defines `Assoc` being `impl<T, U> Trait<U> for Vec<T>`
+                //
+                // We first rebase the goal substs onto the impl, going from `[Vec<u32>, i32, u64]`
+                // to `[u32, u64]`.
+                //
+                // And then map these substs to the substs of the defining impl of `Assoc`, going
+                // from `[u32, u64]` to `[u32, i32, u64]`.
+                let impl_substs_with_gat = goal.predicate.projection_ty.substs.rebase_onto(
+                    tcx,
+                    goal_trait_ref.def_id,
+                    impl_substs,
+                );
+                let substs = ecx.translate_substs(
+                    goal.param_env,
+                    impl_def_id,
+                    impl_substs_with_gat,
+                    assoc_def.defining_node,
                 );
-                let error_term = match assoc_def.item.kind {
-                    ty::AssocKind::Const => tcx
-                        .const_error(
-                            tcx.type_of(goal.predicate.def_id())
-                                .subst(tcx, goal.predicate.projection_ty.substs),
-                            guar,
-                        )
-                        .into(),
-                    ty::AssocKind::Type => tcx.ty_error(guar).into(),
-                    ty::AssocKind::Fn => unreachable!(),
-                };
-                ecx.eq(goal.param_env, goal.predicate.term, error_term)
-                    .expect("expected goal term to be fully unconstrained");
-                return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
-            }
 
-            // Getting the right substitutions here is complex, e.g. given:
-            // - a goal `<Vec<u32> as Trait<i32>>::Assoc<u64>`
-            // - the applicable impl `impl<T> Trait<i32> for Vec<T>`
-            // - and the impl which defines `Assoc` being `impl<T, U> Trait<U> for Vec<T>`
-            //
-            // We first rebase the goal substs onto the impl, going from `[Vec<u32>, i32, u64]`
-            // to `[u32, u64]`.
-            //
-            // And then map these substs to the substs of the defining impl of `Assoc`, going
-            // from `[u32, u64]` to `[u32, i32, u64]`.
-            let impl_substs_with_gat = goal.predicate.projection_ty.substs.rebase_onto(
-                tcx,
-                goal_trait_ref.def_id,
-                impl_substs,
-            );
-            let substs = ecx.translate_substs(
-                goal.param_env,
-                impl_def_id,
-                impl_substs_with_gat,
-                assoc_def.defining_node,
-            );
-
-            // Finally we construct the actual value of the associated type.
-            let term = match assoc_def.item.kind {
-                ty::AssocKind::Type => tcx.type_of(assoc_def.item.def_id).map_bound(|ty| ty.into()),
-                ty::AssocKind::Const => bug!("associated const projection is not supported yet"),
-                ty::AssocKind::Fn => unreachable!("we should never project to a fn"),
-            };
+                // Finally we construct the actual value of the associated type.
+                let term = match assoc_def.item.kind {
+                    ty::AssocKind::Type => tcx.type_of(assoc_def.item.def_id).map_bound(|ty| ty.into()),
+                    ty::AssocKind::Const => bug!("associated const projection is not supported yet"),
+                    ty::AssocKind::Fn => unreachable!("we should never project to a fn"),
+                };
 
-            ecx.eq(goal.param_env, goal.predicate.term, term.subst(tcx, substs))
-                .expect("expected goal term to be fully unconstrained");
-            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
-        })
+                ecx.eq(goal.param_env, goal.predicate.term, term.subst(tcx, substs))
+                    .expect("expected goal term to be fully unconstrained");
+                ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+            },
+            |r| CandidateKind::Candidate { name: "impl".into(), result: *r },
+        )
     }
 
     fn consider_auto_trait_candidate(
@@ -318,53 +331,69 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
         goal: Goal<'tcx, Self>,
     ) -> QueryResult<'tcx> {
         let tcx = ecx.tcx();
-        ecx.probe(|ecx| {
-            let metadata_ty = match goal.predicate.self_ty().kind() {
-                ty::Bool
-                | ty::Char
-                | ty::Int(..)
-                | ty::Uint(..)
-                | ty::Float(..)
-                | ty::Array(..)
-                | ty::RawPtr(..)
-                | ty::Ref(..)
-                | ty::FnDef(..)
-                | ty::FnPtr(..)
-                | ty::Closure(..)
-                | ty::Infer(ty::IntVar(..) | ty::FloatVar(..))
-                | ty::Generator(..)
-                | ty::GeneratorWitness(..)
-                | ty::GeneratorWitnessMIR(..)
-                | ty::Never
-                | ty::Foreign(..) => tcx.types.unit,
-
-                ty::Error(e) => tcx.ty_error(*e),
-
-                ty::Str | ty::Slice(_) => tcx.types.usize,
-
-                ty::Dynamic(_, _, _) => {
-                    let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, None);
-                    tcx.type_of(dyn_metadata)
-                        .subst(tcx, &[ty::GenericArg::from(goal.predicate.self_ty())])
-                }
+        ecx.probe(
+            |ecx| {
+                let metadata_ty = match goal.predicate.self_ty().kind() {
+                    ty::Bool
+                    | ty::Char
+                    | ty::Int(..)
+                    | ty::Uint(..)
+                    | ty::Float(..)
+                    | ty::Array(..)
+                    | ty::RawPtr(..)
+                    | ty::Ref(..)
+                    | ty::FnDef(..)
+                    | ty::FnPtr(..)
+                    | ty::Closure(..)
+                    | ty::Infer(ty::IntVar(..) | ty::FloatVar(..))
+                    | ty::Generator(..)
+                    | ty::GeneratorWitness(..)
+                    | ty::GeneratorWitnessMIR(..)
+                    | ty::Never
+                    | ty::Foreign(..) => tcx.types.unit,
+
+                    ty::Error(e) => tcx.ty_error(*e),
+
+                    ty::Str | ty::Slice(_) => tcx.types.usize,
+
+                    ty::Dynamic(_, _, _) => {
+                        let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, None);
+                        tcx.type_of(dyn_metadata)
+                            .subst(tcx, &[ty::GenericArg::from(goal.predicate.self_ty())])
+                    }
 
-                ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => {
-                    // FIXME(ptr_metadata): It would also be possible to return a `Ok(Ambig)` with no constraints.
-                    let sized_predicate = ty::TraitRef::from_lang_item(
-                        tcx,
-                        LangItem::Sized,
-                        DUMMY_SP,
-                        [ty::GenericArg::from(goal.predicate.self_ty())],
-                    );
-                    ecx.add_goal(goal.with(tcx, sized_predicate));
-                    tcx.types.unit
-                }
+                    ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => {
+                        // FIXME(ptr_metadata): It would also be possible to return a `Ok(Ambig)` with no constraints.
+                        let sized_predicate = ty::TraitRef::from_lang_item(
+                            tcx,
+                            LangItem::Sized,
+                            DUMMY_SP,
+                            [ty::GenericArg::from(goal.predicate.self_ty())],
+                        );
+                        ecx.add_goal(goal.with(tcx, sized_predicate));
+                        tcx.types.unit
+                    }
+
+                    ty::Adt(def, substs) if def.is_struct() => {
+                        match def.non_enum_variant().fields.raw.last() {
+                            None => tcx.types.unit,
+                            Some(field_def) => {
+                                let self_ty = field_def.ty(tcx, substs);
+                                ecx.add_goal(goal.with(
+                                    tcx,
+                                    ty::Binder::dummy(goal.predicate.with_self_ty(tcx, self_ty)),
+                                ));
+                                return ecx.evaluate_added_goals_and_make_canonical_response(
+                                    Certainty::Yes,
+                                );
+                            }
+                        }
+                    }
+                    ty::Adt(_, _) => tcx.types.unit,
 
-                ty::Adt(def, substs) if def.is_struct() => {
-                    match def.non_enum_variant().fields.raw.last() {
+                    ty::Tuple(elements) => match elements.last() {
                         None => tcx.types.unit,
-                        Some(field_def) => {
-                            let self_ty = field_def.ty(tcx, substs);
+                        Some(&self_ty) => {
                             ecx.add_goal(goal.with(
                                 tcx,
                                 ty::Binder::dummy(goal.predicate.with_self_ty(tcx, self_ty)),
@@ -372,35 +401,23 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
                             return ecx
                                 .evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
                         }
-                    }
-                }
-                ty::Adt(_, _) => tcx.types.unit,
-
-                ty::Tuple(elements) => match elements.last() {
-                    None => tcx.types.unit,
-                    Some(&self_ty) => {
-                        ecx.add_goal(goal.with(
-                            tcx,
-                            ty::Binder::dummy(goal.predicate.with_self_ty(tcx, self_ty)),
-                        ));
-                        return ecx
-                            .evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
-                    }
-                },
-
-                ty::Infer(
-                    ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_),
-                )
-                | ty::Bound(..) => bug!(
-                    "unexpected self ty `{:?}` when normalizing `<T as Pointee>::Metadata`",
-                    goal.predicate.self_ty()
-                ),
-            };
+                    },
+
+                    ty::Infer(
+                        ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_),
+                    )
+                    | ty::Bound(..) => bug!(
+                        "unexpected self ty `{:?}` when normalizing `<T as Pointee>::Metadata`",
+                        goal.predicate.self_ty()
+                    ),
+                };
 
-            ecx.eq(goal.param_env, goal.predicate.term, metadata_ty.into())
-                .expect("expected goal term to be fully unconstrained");
-            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
-        })
+                ecx.eq(goal.param_env, goal.predicate.term, metadata_ty.into())
+                    .expect("expected goal term to be fully unconstrained");
+                ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+            },
+            |r| CandidateKind::Candidate { name: "builtin pointee".into(), result: *r },
+        )
     }
 
     fn consider_builtin_future_candidate(
@@ -535,11 +552,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
             ),
         };
 
-        ecx.probe(|ecx| {
-            ecx.eq(goal.param_env, goal.predicate.term, discriminant_ty.into())
-                .expect("expected goal term to be fully unconstrained");
-            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
-        })
+        ecx.probe(
+            |ecx| {
+                ecx.eq(goal.param_env, goal.predicate.term, discriminant_ty.into())
+                    .expect("expected goal term to be fully unconstrained");
+                ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+            },
+            |r| CandidateKind::Candidate { name: "builtin discriminant kind".into(), result: *r },
+        )
     }
 
     fn consider_builtin_destruct_candidate(
diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
index 19e4b23009a..d167ee46b39 100644
--- a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
@@ -2,6 +2,7 @@ mod cache;
 mod overflow;
 
 pub(super) use overflow::OverflowHandler;
+use rustc_middle::traits::solve::inspect::CacheHit;
 
 use self::cache::ProvisionalEntry;
 use cache::ProvisionalCache;
@@ -12,6 +13,7 @@ use rustc_middle::traits::solve::{CanonicalInput, Certainty, MaybeCause, QueryRe
 use rustc_middle::ty::TyCtxt;
 use std::{collections::hash_map::Entry, mem};
 
+use super::inspect::ProofTreeBuilder;
 use super::SolverMode;
 
 rustc_index::newtype_index! {
@@ -88,11 +90,12 @@ impl<'tcx> SearchGraph<'tcx> {
     /// Tries putting the new goal on the stack, returning an error if it is already cached.
     ///
     /// This correctly updates the provisional cache if there is a cycle.
-    #[instrument(level = "debug", skip(self, tcx), ret)]
+    #[instrument(level = "debug", skip(self, tcx, inspect), ret)]
     fn try_push_stack(
         &mut self,
         tcx: TyCtxt<'tcx>,
         input: CanonicalInput<'tcx>,
+        inspect: &mut ProofTreeBuilder<'tcx>,
     ) -> Result<(), QueryResult<'tcx>> {
         // Look at the provisional cache to check for cycles.
         let cache = &mut self.provisional_cache;
@@ -119,6 +122,8 @@ impl<'tcx> SearchGraph<'tcx> {
             // Finally we can return either the provisional response for that goal if we have a
             // coinductive cycle or an ambiguous result if the cycle is inductive.
             Entry::Occupied(entry_index) => {
+                inspect.cache_hit(CacheHit::Provisional);
+
                 let entry_index = *entry_index.get();
 
                 let stack_depth = cache.depth(entry_index);
@@ -205,16 +210,18 @@ impl<'tcx> SearchGraph<'tcx> {
         &mut self,
         tcx: TyCtxt<'tcx>,
         canonical_input: CanonicalInput<'tcx>,
-        mut loop_body: impl FnMut(&mut Self) -> QueryResult<'tcx>,
+        inspect: &mut ProofTreeBuilder<'tcx>,
+        mut loop_body: impl FnMut(&mut Self, &mut ProofTreeBuilder<'tcx>) -> QueryResult<'tcx>,
     ) -> QueryResult<'tcx> {
         if self.should_use_global_cache() {
             if let Some(result) = tcx.new_solver_evaluation_cache.get(&canonical_input, tcx) {
                 debug!(?canonical_input, ?result, "cache hit");
+                inspect.cache_hit(CacheHit::Global);
                 return result;
             }
         }
 
-        match self.try_push_stack(tcx, canonical_input) {
+        match self.try_push_stack(tcx, canonical_input, inspect) {
             Ok(()) => {}
             // Our goal is already on the stack, eager return.
             Err(response) => return response,
@@ -231,7 +238,7 @@ impl<'tcx> SearchGraph<'tcx> {
                     result
                 },
                 |this| {
-                    let result = loop_body(this);
+                    let result = loop_body(this, inspect);
                     this.try_finalize_goal(canonical_input, result).then(|| result)
                 },
             )
diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
index 279fc1229d4..d9f24455a81 100644
--- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
@@ -6,6 +6,7 @@ use rustc_hir::def_id::DefId;
 use rustc_hir::{LangItem, Movability};
 use rustc_infer::traits::query::NoSolution;
 use rustc_infer::traits::util::supertraits;
+use rustc_middle::traits::solve::inspect::CandidateKind;
 use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult};
 use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections};
 use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt};
@@ -61,21 +62,24 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
             },
         };
 
-        ecx.probe(|ecx| {
-            let impl_substs = ecx.fresh_substs_for_item(impl_def_id);
-            let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs);
-
-            ecx.eq(goal.param_env, goal.predicate.trait_ref, impl_trait_ref)?;
-            let where_clause_bounds = tcx
-                .predicates_of(impl_def_id)
-                .instantiate(tcx, impl_substs)
-                .predicates
-                .into_iter()
-                .map(|pred| goal.with(tcx, pred));
-            ecx.add_goals(where_clause_bounds);
-
-            ecx.evaluate_added_goals_and_make_canonical_response(maximal_certainty)
-        })
+        ecx.probe(
+            |ecx| {
+                let impl_substs = ecx.fresh_substs_for_item(impl_def_id);
+                let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs);
+
+                ecx.eq(goal.param_env, goal.predicate.trait_ref, impl_trait_ref)?;
+                let where_clause_bounds = tcx
+                    .predicates_of(impl_def_id)
+                    .instantiate(tcx, impl_substs)
+                    .predicates
+                    .into_iter()
+                    .map(|pred| goal.with(tcx, pred));
+                ecx.add_goals(where_clause_bounds);
+
+                ecx.evaluate_added_goals_and_make_canonical_response(maximal_certainty)
+            },
+            |r| CandidateKind::Candidate { name: "impl".into(), result: *r },
+        )
     }
 
     fn probe_and_match_goal_against_assumption(
@@ -84,21 +88,26 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
         assumption: ty::Binder<'tcx, ty::Clause<'tcx>>,
         then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>,
     ) -> QueryResult<'tcx> {
-        if let Some(trait_clause) = assumption.as_trait_clause()
-            && trait_clause.def_id() == goal.predicate.def_id()
-            && trait_clause.polarity() == goal.predicate.polarity
-        {
-            // FIXME: Constness
-            ecx.probe(|ecx| {
-                let assumption_trait_pred =
-                    ecx.instantiate_binder_with_infer(trait_clause);
-                ecx.eq(
-                    goal.param_env,
-                    goal.predicate.trait_ref,
-                    assumption_trait_pred.trait_ref,
-                )?;
-                then(ecx)
-            })
+        if let Some(trait_clause) = assumption.as_trait_clause() {
+            if trait_clause.def_id() == goal.predicate.def_id()
+                && trait_clause.polarity() == goal.predicate.polarity
+            {
+                // FIXME: Constness
+                ecx.probe(
+                    |ecx| {
+                        let assumption_trait_pred = ecx.instantiate_binder_with_infer(trait_clause);
+                        ecx.eq(
+                            goal.param_env,
+                            goal.predicate.trait_ref,
+                            assumption_trait_pred.trait_ref,
+                        )?;
+                        then(ecx)
+                    },
+                    |r| CandidateKind::Candidate { name: "assumption".into(), result: *r },
+                )
+            } else {
+                Err(NoSolution)
+            }
         } else {
             Err(NoSolution)
         }
@@ -132,13 +141,16 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
 
         let tcx = ecx.tcx();
 
-        ecx.probe(|ecx| {
-            let nested_obligations = tcx
-                .predicates_of(goal.predicate.def_id())
-                .instantiate(tcx, goal.predicate.trait_ref.substs);
-            ecx.add_goals(nested_obligations.predicates.into_iter().map(|p| goal.with(tcx, p)));
-            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
-        })
+        ecx.probe(
+            |ecx| {
+                let nested_obligations = tcx
+                    .predicates_of(goal.predicate.def_id())
+                    .instantiate(tcx, goal.predicate.trait_ref.substs);
+                ecx.add_goals(nested_obligations.predicates.into_iter().map(|p| goal.with(tcx, p)));
+                ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+            },
+            |r| CandidateKind::Candidate { name: "trait alias".into(), result: *r },
+        )
     }
 
     fn consider_builtin_sized_candidate(
@@ -344,109 +356,116 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
         if b_ty.is_ty_var() {
             return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
         }
-        ecx.probe(|ecx| {
-            match (a_ty.kind(), b_ty.kind()) {
-                // Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b`
-                (&ty::Dynamic(_, _, ty::Dyn), &ty::Dynamic(_, _, ty::Dyn)) => {
-                    // Dyn upcasting is handled separately, since due to upcasting,
-                    // when there are two supertraits that differ by substs, we
-                    // may return more than one query response.
-                    Err(NoSolution)
-                }
-                // `T` -> `dyn Trait` unsizing
-                (_, &ty::Dynamic(data, region, ty::Dyn)) => {
-                    // Can only unsize to an object-safe type
-                    if data
-                        .principal_def_id()
-                        .is_some_and(|def_id| !tcx.check_is_object_safe(def_id))
-                    {
-                        return Err(NoSolution);
+        ecx.probe(
+            |ecx| {
+                match (a_ty.kind(), b_ty.kind()) {
+                    // Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b`
+                    (&ty::Dynamic(_, _, ty::Dyn), &ty::Dynamic(_, _, ty::Dyn)) => {
+                        // Dyn upcasting is handled separately, since due to upcasting,
+                        // when there are two supertraits that differ by substs, we
+                        // may return more than one query response.
+                        Err(NoSolution)
                     }
-
-                    let Some(sized_def_id) = tcx.lang_items().sized_trait() else {
+                    // `T` -> `dyn Trait` unsizing
+                    (_, &ty::Dynamic(data, region, ty::Dyn)) => {
+                        // Can only unsize to an object-safe type
+                        if data
+                            .principal_def_id()
+                            .is_some_and(|def_id| !tcx.check_is_object_safe(def_id))
+                        {
+                            return Err(NoSolution);
+                        }
+
+                        let Some(sized_def_id) = tcx.lang_items().sized_trait() else {
                         return Err(NoSolution);
                     };
-                    // Check that the type implements all of the predicates of the def-id.
-                    // (i.e. the principal, all of the associated types match, and any auto traits)
-                    ecx.add_goals(
-                        data.iter().map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty))),
-                    );
-                    // The type must be Sized to be unsized.
-                    ecx.add_goal(goal.with(tcx, ty::TraitRef::new(tcx, sized_def_id, [a_ty])));
-                    // The type must outlive the lifetime of the `dyn` we're unsizing into.
-                    ecx.add_goal(
-                        goal.with(tcx, ty::Binder::dummy(ty::OutlivesPredicate(a_ty, region))),
-                    );
-                    ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
-                }
-                // `[T; n]` -> `[T]` unsizing
-                (&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => {
-                    // We just require that the element type stays the same
-                    ecx.eq(goal.param_env, a_elem_ty, b_elem_ty)?;
-                    ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
-                }
-                // Struct unsizing `Struct<T>` -> `Struct<U>` where `T: Unsize<U>`
-                (&ty::Adt(a_def, a_substs), &ty::Adt(b_def, b_substs))
-                    if a_def.is_struct() && a_def.did() == b_def.did() =>
-                {
-                    let unsizing_params = tcx.unsizing_params_for_adt(a_def.did());
-                    // We must be unsizing some type parameters. This also implies
-                    // that the struct has a tail field.
-                    if unsizing_params.is_empty() {
-                        return Err(NoSolution);
+                        // Check that the type implements all of the predicates of the def-id.
+                        // (i.e. the principal, all of the associated types match, and any auto traits)
+                        ecx.add_goals(
+                            data.iter().map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty))),
+                        );
+                        // The type must be Sized to be unsized.
+                        ecx.add_goal(goal.with(tcx, ty::TraitRef::new(tcx, sized_def_id, [a_ty])));
+                        // The type must outlive the lifetime of the `dyn` we're unsizing into.
+                        ecx.add_goal(
+                            goal.with(tcx, ty::Binder::dummy(ty::OutlivesPredicate(a_ty, region))),
+                        );
+                        ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
                     }
-
-                    let tail_field = a_def
-                        .non_enum_variant()
-                        .fields
-                        .raw
-                        .last()
-                        .expect("expected unsized ADT to have a tail field");
-                    let tail_field_ty = tcx.type_of(tail_field.did);
-
-                    let a_tail_ty = tail_field_ty.subst(tcx, a_substs);
-                    let b_tail_ty = tail_field_ty.subst(tcx, b_substs);
-
-                    // Substitute just the unsizing params from B into A. The type after
-                    // this substitution must be equal to B. This is so we don't unsize
-                    // unrelated type parameters.
-                    let new_a_substs =
-                        tcx.mk_substs_from_iter(a_substs.iter().enumerate().map(|(i, a)| {
-                            if unsizing_params.contains(i as u32) { b_substs[i] } else { a }
-                        }));
-                    let unsized_a_ty = tcx.mk_adt(a_def, new_a_substs);
-
-                    // Finally, we require that `TailA: Unsize<TailB>` for the tail field
-                    // types.
-                    ecx.eq(goal.param_env, unsized_a_ty, b_ty)?;
-                    ecx.add_goal(goal.with(
-                        tcx,
-                        ty::TraitRef::new(tcx, goal.predicate.def_id(), [a_tail_ty, b_tail_ty]),
-                    ));
-                    ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
-                }
-                // Tuple unsizing `(.., T)` -> `(.., U)` where `T: Unsize<U>`
-                (&ty::Tuple(a_tys), &ty::Tuple(b_tys))
-                    if a_tys.len() == b_tys.len() && !a_tys.is_empty() =>
-                {
-                    let (a_last_ty, a_rest_tys) = a_tys.split_last().unwrap();
-                    let b_last_ty = b_tys.last().unwrap();
-
-                    // Substitute just the tail field of B., and require that they're equal.
-                    let unsized_a_ty =
-                        tcx.mk_tup_from_iter(a_rest_tys.iter().chain([b_last_ty]).copied());
-                    ecx.eq(goal.param_env, unsized_a_ty, b_ty)?;
-
-                    // Similar to ADTs, require that the rest of the fields are equal.
-                    ecx.add_goal(goal.with(
-                        tcx,
-                        ty::TraitRef::new(tcx, goal.predicate.def_id(), [*a_last_ty, *b_last_ty]),
-                    ));
-                    ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+                    // `[T; n]` -> `[T]` unsizing
+                    (&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => {
+                        // We just require that the element type stays the same
+                        ecx.eq(goal.param_env, a_elem_ty, b_elem_ty)?;
+                        ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+                    }
+                    // Struct unsizing `Struct<T>` -> `Struct<U>` where `T: Unsize<U>`
+                    (&ty::Adt(a_def, a_substs), &ty::Adt(b_def, b_substs))
+                        if a_def.is_struct() && a_def.did() == b_def.did() =>
+                    {
+                        let unsizing_params = tcx.unsizing_params_for_adt(a_def.did());
+                        // We must be unsizing some type parameters. This also implies
+                        // that the struct has a tail field.
+                        if unsizing_params.is_empty() {
+                            return Err(NoSolution);
+                        }
+
+                        let tail_field = a_def
+                            .non_enum_variant()
+                            .fields
+                            .raw
+                            .last()
+                            .expect("expected unsized ADT to have a tail field");
+                        let tail_field_ty = tcx.type_of(tail_field.did);
+
+                        let a_tail_ty = tail_field_ty.subst(tcx, a_substs);
+                        let b_tail_ty = tail_field_ty.subst(tcx, b_substs);
+
+                        // Substitute just the unsizing params from B into A. The type after
+                        // this substitution must be equal to B. This is so we don't unsize
+                        // unrelated type parameters.
+                        let new_a_substs =
+                            tcx.mk_substs_from_iter(a_substs.iter().enumerate().map(|(i, a)| {
+                                if unsizing_params.contains(i as u32) { b_substs[i] } else { a }
+                            }));
+                        let unsized_a_ty = tcx.mk_adt(a_def, new_a_substs);
+
+                        // Finally, we require that `TailA: Unsize<TailB>` for the tail field
+                        // types.
+                        ecx.eq(goal.param_env, unsized_a_ty, b_ty)?;
+                        ecx.add_goal(goal.with(
+                            tcx,
+                            ty::TraitRef::new(tcx, goal.predicate.def_id(), [a_tail_ty, b_tail_ty]),
+                        ));
+                        ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+                    }
+                    // Tuple unsizing `(.., T)` -> `(.., U)` where `T: Unsize<U>`
+                    (&ty::Tuple(a_tys), &ty::Tuple(b_tys))
+                        if a_tys.len() == b_tys.len() && !a_tys.is_empty() =>
+                    {
+                        let (a_last_ty, a_rest_tys) = a_tys.split_last().unwrap();
+                        let b_last_ty = b_tys.last().unwrap();
+
+                        // Substitute just the tail field of B., and require that they're equal.
+                        let unsized_a_ty =
+                            tcx.mk_tup_from_iter(a_rest_tys.iter().chain([b_last_ty]).copied());
+                        ecx.eq(goal.param_env, unsized_a_ty, b_ty)?;
+
+                        // Similar to ADTs, require that the rest of the fields are equal.
+                        ecx.add_goal(goal.with(
+                            tcx,
+                            ty::TraitRef::new(
+                                tcx,
+                                goal.predicate.def_id(),
+                                [*a_last_ty, *b_last_ty],
+                            ),
+                        ));
+                        ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+                    }
+                    _ => Err(NoSolution),
                 }
-                _ => Err(NoSolution),
-            }
-        })
+            },
+            |r| CandidateKind::Candidate { name: "builtin unsize".into(), result: *r },
+        )
     }
 
     fn consider_builtin_dyn_upcast_candidates(
@@ -476,32 +495,38 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
         }
 
         let mut unsize_dyn_to_principal = |principal: Option<ty::PolyExistentialTraitRef<'tcx>>| {
-            ecx.probe(|ecx| -> Result<_, NoSolution> {
-                // Require that all of the trait predicates from A match B, except for
-                // the auto traits. We do this by constructing a new A type with B's
-                // auto traits, and equating these types.
-                let new_a_data = principal
-                    .into_iter()
-                    .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait))
-                    .chain(a_data.iter().filter(|a| {
-                        matches!(a.skip_binder(), ty::ExistentialPredicate::Projection(_))
-                    }))
-                    .chain(
-                        b_data
-                            .auto_traits()
-                            .map(ty::ExistentialPredicate::AutoTrait)
-                            .map(ty::Binder::dummy),
+            ecx.probe(
+                |ecx| -> Result<_, NoSolution> {
+                    // Require that all of the trait predicates from A match B, except for
+                    // the auto traits. We do this by constructing a new A type with B's
+                    // auto traits, and equating these types.
+                    let new_a_data = principal
+                        .into_iter()
+                        .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait))
+                        .chain(a_data.iter().filter(|a| {
+                            matches!(a.skip_binder(), ty::ExistentialPredicate::Projection(_))
+                        }))
+                        .chain(
+                            b_data
+                                .auto_traits()
+                                .map(ty::ExistentialPredicate::AutoTrait)
+                                .map(ty::Binder::dummy),
+                        );
+                    let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data);
+                    let new_a_ty = tcx.mk_dynamic(new_a_data, b_region, ty::Dyn);
+
+                    // We also require that A's lifetime outlives B's lifetime.
+                    ecx.eq(goal.param_env, new_a_ty, b_ty)?;
+                    ecx.add_goal(
+                        goal.with(
+                            tcx,
+                            ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)),
+                        ),
                     );
-                let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data);
-                let new_a_ty = tcx.mk_dynamic(new_a_data, b_region, ty::Dyn);
-
-                // We also require that A's lifetime outlives B's lifetime.
-                ecx.eq(goal.param_env, new_a_ty, b_ty)?;
-                ecx.add_goal(
-                    goal.with(tcx, ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region))),
-                );
-                ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
-            })
+                    ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+                },
+                |r| CandidateKind::Candidate { name: "upcast dyn to principle".into(), result: *r },
+            )
         };
 
         let mut responses = vec![];
@@ -698,20 +723,23 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         goal: Goal<'tcx, TraitPredicate<'tcx>>,
         constituent_tys: impl Fn(&EvalCtxt<'_, 'tcx>, Ty<'tcx>) -> Result<Vec<Ty<'tcx>>, NoSolution>,
     ) -> QueryResult<'tcx> {
-        self.probe(|ecx| {
-            ecx.add_goals(
-                constituent_tys(ecx, goal.predicate.self_ty())?
-                    .into_iter()
-                    .map(|ty| {
-                        goal.with(
-                            ecx.tcx(),
-                            ty::Binder::dummy(goal.predicate.with_self_ty(ecx.tcx(), ty)),
-                        )
-                    })
-                    .collect::<Vec<_>>(),
-            );
-            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
-        })
+        self.probe(
+            |ecx| {
+                ecx.add_goals(
+                    constituent_tys(ecx, goal.predicate.self_ty())?
+                        .into_iter()
+                        .map(|ty| {
+                            goal.with(
+                                ecx.tcx(),
+                                ty::Binder::dummy(goal.predicate.with_self_ty(ecx.tcx(), ty)),
+                            )
+                        })
+                        .collect::<Vec<_>>(),
+                );
+                ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+            },
+            |r| CandidateKind::Candidate { name: "constituent tys".into(), result: *r },
+        )
     }
 
     #[instrument(level = "debug", skip(self))]
diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
index 2d97a808225..1b6e92946c4 100644
--- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
@@ -322,8 +322,12 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
                 };
                 // `tcx.normalize_projection_ty` may normalize to a type that still has
                 // unevaluated consts, so keep normalizing here if that's the case.
-                if res != ty && res.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) {
-                    res.try_super_fold_with(self)?
+                // Similarly, `tcx.normalize_weak_ty` will only unwrap one layer of type
+                // and we need to continue folding it to reveal the TAIT behind it.
+                if res != ty
+                    && (res.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) || kind == ty::Weak)
+                {
+                    res.try_fold_with(self)?
                 } else {
                     res
                 }
diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
index f2dfa6921f4..9f209b4b623 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -417,17 +417,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 // Fast path to avoid evaluating an obligation that trivially holds.
                 // There may be more bounds, but these are checked by the regular path.
                 ty::FnPtr(..) => return false,
+
                 // These may potentially implement `FnPtr`
                 ty::Placeholder(..)
                 | ty::Dynamic(_, _, _)
                 | ty::Alias(_, _)
                 | ty::Infer(_)
-                | ty::Param(..) => {}
+                | ty::Param(..)
+                | ty::Bound(_, _) => {}
 
-                ty::Bound(_, _) => span_bug!(
-                    obligation.cause.span(),
-                    "cannot have escaping bound var in self type of {obligation:#?}"
-                ),
                 // These can't possibly implement `FnPtr` as they are concrete types
                 // and not `FnPtr`
                 ty::Bool
diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs
index ce77df0df5d..387adda8f57 100644
--- a/compiler/rustc_ty_utils/src/consts.rs
+++ b/compiler/rustc_ty_utils/src/consts.rs
@@ -78,8 +78,9 @@ pub(crate) fn destructure_const<'tcx>(
 fn check_binop(op: mir::BinOp) -> bool {
     use mir::BinOp::*;
     match op {
-        Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Shl | Shr | Eq | Lt | Le | Ne
-        | Ge | Gt => true,
+        Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor
+        | BitAnd | BitOr | Shl | ShlUnchecked | Shr | ShrUnchecked | Eq | Lt | Le | Ne | Ge
+        | Gt => true,
         Offset => false,
     }
 }
diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs
index c1cc892eb86..909b32547e7 100644
--- a/library/core/src/cell.rs
+++ b/library/core/src/cell.rs
@@ -1374,7 +1374,7 @@ impl Clone for BorrowRef<'_> {
         debug_assert!(is_reading(borrow));
         // Prevent the borrow counter from overflowing into
         // a writing borrow.
-        assert!(borrow != isize::MAX);
+        assert!(borrow != BorrowFlag::MAX);
         self.borrow.set(borrow + 1);
         BorrowRef { borrow: self.borrow }
     }
@@ -1756,7 +1756,7 @@ impl<'b> BorrowRefMut<'b> {
         let borrow = self.borrow.get();
         debug_assert!(is_writing(borrow));
         // Prevent the borrow counter from underflowing.
-        assert!(borrow != isize::MIN);
+        assert!(borrow != BorrowFlag::MIN);
         self.borrow.set(borrow - 1);
         BorrowRefMut { borrow: self.borrow }
     }
diff --git a/library/core/src/future/poll_fn.rs b/library/core/src/future/poll_fn.rs
index 90cb797391a..d27a9dfc176 100644
--- a/library/core/src/future/poll_fn.rs
+++ b/library/core/src/future/poll_fn.rs
@@ -24,6 +24,93 @@ use crate::task::{Context, Poll};
 /// assert_eq!(read_future.await, "Hello, World!".to_owned());
 /// # }
 /// ```
+///
+/// ## Capturing a pinned state
+///
+/// Example of a closure wrapping inner futures:
+///
+/// ```
+/// # async fn run() {
+/// use core::future::{self, Future};
+/// use core::task::Poll;
+///
+/// /// Resolves to the first future that completes. In the event of a tie, `a` wins.
+/// fn naive_select<T>(
+///     a: impl Future<Output = T>,
+///     b: impl Future<Output = T>,
+/// ) -> impl Future<Output = T>
+/// {
+///     let (mut a, mut b) = (Box::pin(a), Box::pin(b));
+///     future::poll_fn(move |cx| {
+///         if let Poll::Ready(r) = a.as_mut().poll(cx) {
+///             Poll::Ready(r)
+///         } else if let Poll::Ready(r) = b.as_mut().poll(cx) {
+///             Poll::Ready(r)
+///         } else {
+///             Poll::Pending
+///         }
+///     })
+/// }
+///
+/// let a = async { 42 };
+/// let b = future::pending();
+/// let v = naive_select(a, b).await;
+/// assert_eq!(v, 42);
+///
+/// let a = future::pending();
+/// let b = async { 27 };
+/// let v = naive_select(a, b).await;
+/// assert_eq!(v, 27);
+///
+/// let a = async { 42 };
+/// let b = async { 27 };
+/// let v = naive_select(a, b).await;
+/// assert_eq!(v, 42); // biased towards `a` in case of tie!
+/// # }
+/// ```
+///
+/// This time without [`Box::pin`]ning:
+///
+/// [`Box::pin`]: ../../std/boxed/struct.Box.html#method.pin
+///
+/// ```
+/// # async fn run() {
+/// use core::future::{self, Future};
+/// use core::pin::pin;
+/// use core::task::Poll;
+///
+/// /// Resolves to the first future that completes. In the event of a tie, `a` wins.
+/// fn naive_select<T>(
+///     a: impl Future<Output = T>,
+///     b: impl Future<Output = T>,
+/// ) -> impl Future<Output = T>
+/// {
+///     async {
+///         let (mut a, mut b) = (pin!(a), pin!(b));
+///         future::poll_fn(move |cx| {
+///             if let Poll::Ready(r) = a.as_mut().poll(cx) {
+///                 Poll::Ready(r)
+///             } else if let Poll::Ready(r) = b.as_mut().poll(cx) {
+///                 Poll::Ready(r)
+///             } else {
+///                 Poll::Pending
+///             }
+///         }).await
+///     }
+/// }
+///
+/// let a = async { 42 };
+/// let b = future::pending();
+/// let v = naive_select(a, b).await;
+/// assert_eq!(v, 42);
+/// # }
+/// ```
+///
+///   - Notice how, by virtue of being in an `async` context, we have been able to make the [`pin!`]
+///     macro work, thereby avoiding any need for the `unsafe`
+///     <code>[Pin::new_unchecked](&mut fut)</code> constructor.
+///
+/// [`pin!`]: crate::pin::pin!
 #[stable(feature = "future_poll_fn", since = "1.64.0")]
 pub fn poll_fn<T, F>(f: F) -> PollFn<F>
 where
diff --git a/library/core/src/net/ip_addr.rs b/library/core/src/net/ip_addr.rs
index 954d88d548e..c51913fa8ab 100644
--- a/library/core/src/net/ip_addr.rs
+++ b/library/core/src/net/ip_addr.rs
@@ -1770,14 +1770,8 @@ impl fmt::Display for Ipv6Addr {
                 f.write_str("::")
             } else if self.is_loopback() {
                 f.write_str("::1")
-            } else if let Some(ipv4) = self.to_ipv4() {
-                match segments[5] {
-                    // IPv4 Compatible address
-                    0 => write!(f, "::{}", ipv4),
-                    // IPv4 Mapped address
-                    0xffff => write!(f, "::ffff:{}", ipv4),
-                    _ => unreachable!(),
-                }
+            } else if let Some(ipv4) = self.to_ipv4_mapped() {
+                write!(f, "::ffff:{}", ipv4)
             } else {
                 #[derive(Copy, Clone, Default)]
                 struct Span {
diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs
index c9baa09f407..95dcaf5dd73 100644
--- a/library/core/src/num/mod.rs
+++ b/library/core/src/num/mod.rs
@@ -3,7 +3,6 @@
 #![stable(feature = "rust1", since = "1.0.0")]
 
 use crate::ascii;
-use crate::convert::TryInto;
 use crate::intrinsics;
 use crate::mem;
 use crate::ops::{Add, Mul, Sub};
@@ -278,18 +277,12 @@ macro_rules! widening_impl {
 
 macro_rules! conv_rhs_for_unchecked_shift {
     ($SelfT:ty, $x:expr) => {{
-        #[inline]
-        fn conv(x: u32) -> $SelfT {
-            // FIXME(const-hack) replace with `.try_into().ok().unwrap_unchecked()`.
-            // SAFETY: Any legal shift amount must be losslessly representable in the self type.
-            unsafe { x.try_into().ok().unwrap_unchecked() }
-        }
-        #[inline]
-        const fn const_conv(x: u32) -> $SelfT {
-            x as _
+        // If the `as` cast will truncate, ensure we still tell the backend
+        // that the pre-truncation value was also small.
+        if <$SelfT>::BITS < 32 {
+            intrinsics::assume($x <= (<$SelfT>::MAX as u32));
         }
-
-        intrinsics::const_eval_select(($x,), const_conv, conv)
+        $x as $SelfT
     }};
 }
 
diff --git a/library/core/src/unicode/printable.py b/library/core/src/unicode/printable.py
index 7c37f5f099c..4d39ace066c 100755
--- a/library/core/src/unicode/printable.py
+++ b/library/core/src/unicode/printable.py
@@ -119,7 +119,7 @@ def print_singletons(uppers, lowers, uppersname, lowersname):
     print("#[rustfmt::skip]")
     print("const {}: &[u8] = &[".format(lowersname))
     for i in range(0, len(lowers), 8):
-        print("    {}".format(" ".join("{:#04x},".format(l) for l in lowers[i:i+8])))
+        print("    {}".format(" ".join("{:#04x},".format(x) for x in lowers[i:i+8])))
     print("];")
 
 def print_normal(normal, normalname):
diff --git a/library/core/tests/net/ip_addr.rs b/library/core/tests/net/ip_addr.rs
index 5a6ac08c088..7530fc08487 100644
--- a/library/core/tests/net/ip_addr.rs
+++ b/library/core/tests/net/ip_addr.rs
@@ -139,7 +139,7 @@ fn ipv6_addr_to_string() {
 
     // ipv4-compatible address
     let a1 = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc000, 0x280);
-    assert_eq!(a1.to_string(), "::192.0.2.128");
+    assert_eq!(a1.to_string(), "::c000:280");
 
     // v6 address with no zero segments
     assert_eq!(Ipv6Addr::new(8, 9, 10, 11, 12, 13, 14, 15).to_string(), "8:9:a:b:c:d:e:f");
@@ -316,7 +316,7 @@ fn ip_properties() {
 
     check!("::", unspec);
     check!("::1", loopback);
-    check!("::0.0.0.2", global);
+    check!("::2", global);
     check!("1::", global);
     check!("fc00::");
     check!("fdff:ffff::");
@@ -607,7 +607,7 @@ fn ipv6_properties() {
 
     check!("::1", &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], loopback);
 
-    check!("::0.0.0.2", &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2], global | unicast_global);
+    check!("::2", &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2], global | unicast_global);
 
     check!("1::", &[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], global | unicast_global);
 
diff --git a/library/core/tests/net/socket_addr.rs b/library/core/tests/net/socket_addr.rs
index 68c7cd94d32..35a69cead48 100644
--- a/library/core/tests/net/socket_addr.rs
+++ b/library/core/tests/net/socket_addr.rs
@@ -34,7 +34,7 @@ fn ipv6_socket_addr_to_string() {
     // IPv4-compatible address.
     assert_eq!(
         SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc000, 0x280), 8080, 0, 0).to_string(),
-        "[::192.0.2.128]:8080"
+        "[::c000:280]:8080"
     );
 
     // IPv6 address with no zero segments.
diff --git a/library/std/src/net/socket_addr/tests.rs b/library/std/src/net/socket_addr/tests.rs
index dfc6dabbed1..6a065cfba21 100644
--- a/library/std/src/net/socket_addr/tests.rs
+++ b/library/std/src/net/socket_addr/tests.rs
@@ -85,7 +85,7 @@ fn ipv6_socket_addr_to_string() {
     // IPv4-compatible address.
     assert_eq!(
         SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc000, 0x280), 8080, 0, 0).to_string(),
-        "[::192.0.2.128]:8080"
+        "[::c000:280]:8080"
     );
 
     // IPv6 address with no zero segments.
diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py
index 473fdbe1edc..5714613cdf5 100644
--- a/src/bootstrap/bootstrap.py
+++ b/src/bootstrap/bootstrap.py
@@ -620,7 +620,7 @@ class RustBuild(object):
             # The latter one does not exist on NixOS when using tmpfs as root.
             try:
                 with open("/etc/os-release", "r") as f:
-                    if not any(l.strip() in ("ID=nixos", "ID='nixos'", 'ID="nixos"') for l in f):
+                    if not any(ln.strip() in ("ID=nixos", "ID='nixos'", 'ID="nixos"') for ln in f):
                         return False
             except FileNotFoundError:
                 return False
@@ -872,7 +872,7 @@ class RustBuild(object):
         }
         for var_name, toml_key in var_data.items():
             toml_val = self.get_toml(toml_key, build_section)
-            if toml_val != None:
+            if toml_val is not None:
                 env["{}_{}".format(var_name, host_triple_sanitized)] = toml_val
 
         # preserve existing RUSTFLAGS
diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py
index 4481e1668b6..a16f77317c8 100755
--- a/src/bootstrap/configure.py
+++ b/src/bootstrap/configure.py
@@ -9,7 +9,7 @@ rust_dir = os.path.dirname(os.path.abspath(__file__))
 rust_dir = os.path.dirname(rust_dir)
 rust_dir = os.path.dirname(rust_dir)
 sys.path.append(os.path.join(rust_dir, "src", "bootstrap"))
-import bootstrap
+import bootstrap # noqa: E402
 
 
 class Option(object):
@@ -319,7 +319,7 @@ def apply_args(known_args, option_checking, config):
     for key in known_args:
         # The `set` option is special and can be passed a bunch of times
         if key == 'set':
-            for option, value in known_args[key]:
+            for _option, value in known_args[key]:
                 keyval = value.split('=', 1)
                 if len(keyval) == 1 or keyval[1] == "true":
                     value = True
@@ -401,7 +401,7 @@ def parse_example_config(known_args, config):
     top_level_keys = []
 
     for line in open(rust_dir + '/config.example.toml').read().split("\n"):
-        if cur_section == None:
+        if cur_section is None:
             if line.count('=') == 1:
                 top_level_key = line.split('=')[0]
                 top_level_key = top_level_key.strip(' #')
@@ -523,8 +523,8 @@ def write_uncommented(target, f):
         block.append(line)
         if len(line) == 0:
             if not is_comment:
-                for l in block:
-                    f.write(l + "\n")
+                for ln in block:
+                    f.write(ln + "\n")
             block = []
             is_comment = True
             continue
diff --git a/src/bootstrap/mk/Makefile.in b/src/bootstrap/mk/Makefile.in
index 24de13c89e2..9476137968b 100644
--- a/src/bootstrap/mk/Makefile.in
+++ b/src/bootstrap/mk/Makefile.in
@@ -60,8 +60,11 @@ prepare:
 ## MSVC native builders
 
 # this intentionally doesn't use `$(BOOTSTRAP)` so we can test the shebang on Windows
-ci-msvc:
-	$(Q)$(CFG_SRC_DIR)/x.py test --stage 2
+ci-msvc-py:
+	$(Q)$(CFG_SRC_DIR)/x.py test --stage 2 tidy
+ci-msvc-ps1:
+	$(Q)$(CFG_SRC_DIR)/x.ps1 test --stage 2 --exclude tidy
+ci-msvc: ci-msvc-py ci-msvc-ps1
 
 ## MingW native builders
 
diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs
index 01d0dbafd1e..9212362f6c9 100644
--- a/src/bootstrap/test.rs
+++ b/src/bootstrap/test.rs
@@ -44,7 +44,8 @@ const MIR_OPT_BLESS_TARGET_MAPPING: &[(&str, &str)] = &[
     ("i686-pc-windows-msvc", "x86_64-pc-windows-msvc"),
     ("i686-pc-windows-gnu", "x86_64-pc-windows-gnu"),
     ("i686-apple-darwin", "x86_64-apple-darwin"),
-    ("i686-apple-darwin", "aarch64-apple-darwin"),
+    // ARM Macs don't have a corresponding 32-bit target that they can (easily)
+    // build for, so there is no entry for "aarch64-apple-darwin" here.
 ];
 
 fn try_run(builder: &Builder<'_>, cmd: &mut Command) -> bool {
diff --git a/src/bootstrap/util.rs b/src/bootstrap/util.rs
index 7e29f671f02..1ec49f80d63 100644
--- a/src/bootstrap/util.rs
+++ b/src/bootstrap/util.rs
@@ -159,8 +159,6 @@ pub fn symlink_dir(config: &Config, original: &Path, link: &Path) -> io::Result<
 pub enum CiEnv {
     /// Not a CI environment.
     None,
-    /// The Azure Pipelines environment, for Linux (including Docker), Windows, and macOS builds.
-    AzurePipelines,
     /// The GitHub Actions environment, for Linux (including Docker), Windows and macOS builds.
     GitHubActions,
 }
diff --git a/src/ci/cpu-usage-over-time.py b/src/ci/cpu-usage-over-time.py
index adfd895ead0..adfd895ead0 100644..100755
--- a/src/ci/cpu-usage-over-time.py
+++ b/src/ci/cpu-usage-over-time.py
diff --git a/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/run.py b/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/run.py
index ffae7b0d4ac..ffae7b0d4ac 100644..100755
--- a/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/run.py
+++ b/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/run.py
diff --git a/src/ci/docker/run.sh b/src/ci/docker/run.sh
index 2fe3438e0fe..0a098467d94 100755
--- a/src/ci/docker/run.sh
+++ b/src/ci/docker/run.sh
@@ -254,8 +254,6 @@ docker \
   --env DEPLOY \
   --env DEPLOY_ALT \
   --env CI \
-  --env TF_BUILD \
-  --env BUILD_SOURCEBRANCHNAME \
   --env GITHUB_ACTIONS \
   --env GITHUB_REF \
   --env TOOLSTATE_REPO_ACCESS_TOKEN \
diff --git a/src/ci/docker/scripts/android-sdk-manager.py b/src/ci/docker/scripts/android-sdk-manager.py
index c9e2961f6eb..66cba58427b 100755
--- a/src/ci/docker/scripts/android-sdk-manager.py
+++ b/src/ci/docker/scripts/android-sdk-manager.py
@@ -2,6 +2,14 @@
 # Simpler reimplementation of Android's sdkmanager
 # Extra features of this implementation are pinning and mirroring
 
+import argparse
+import hashlib
+import os
+import subprocess
+import tempfile
+import urllib.request
+import xml.etree.ElementTree as ET
+
 # These URLs are the Google repositories containing the list of available
 # packages and their versions. The list has been generated by listing the URLs
 # fetched while executing `tools/bin/sdkmanager --list`
@@ -27,15 +35,6 @@ MIRROR_BUCKET = "rust-lang-ci-mirrors"
 MIRROR_BUCKET_REGION = "us-west-1"
 MIRROR_BASE_DIR = "rustc/android/"
 
-import argparse
-import hashlib
-import os
-import subprocess
-import sys
-import tempfile
-import urllib.request
-import xml.etree.ElementTree as ET
-
 class Package:
     def __init__(self, path, url, sha1, deps=None):
         if deps is None:
diff --git a/src/ci/docker/scripts/fuchsia-test-runner.py b/src/ci/docker/scripts/fuchsia-test-runner.py
index 73cf3de6a46..af01f9ccbbc 100755
--- a/src/ci/docker/scripts/fuchsia-test-runner.py
+++ b/src/ci/docker/scripts/fuchsia-test-runner.py
@@ -519,8 +519,8 @@ class TestEnvironment:
                         env_vars += f'\n            "{var_name}={var_value}",'
 
                 # Default to no backtrace for test suite
-                if os.getenv("RUST_BACKTRACE") == None:
-                    env_vars += f'\n            "RUST_BACKTRACE=0",'
+                if os.getenv("RUST_BACKTRACE") is None:
+                    env_vars += '\n            "RUST_BACKTRACE=0",'
 
                 # Use /tmp as the test temporary directory
                 env_vars += f'\n            "RUST_TEST_TMPDIR=/tmp",'
diff --git a/src/ci/github-actions/problem_matchers.json b/src/ci/github-actions/problem_matchers.json
index 37561924b7d..b6c7ace841e 100644
--- a/src/ci/github-actions/problem_matchers.json
+++ b/src/ci/github-actions/problem_matchers.json
@@ -10,6 +10,46 @@
                     "message": 3
                 }
             ]
+        },
+        {
+            "owner": "cargo-common",
+            "pattern": [
+                {
+                    "regexp": "^(warning|warn|error)(\\[(\\S*)\\])?: (.*)$",
+                    "severity": 1,
+                    "message": 4,
+                    "code": 3
+                },
+                {
+                    "regexp": "^\\s+-->\\s(\\S+):(\\d+):(\\d+)$",
+                    "file": 1,
+                    "line": 2,
+                    "column": 3
+                }
+            ]
+        },
+        {
+            "owner": "compiler-panic",
+            "pattern": [
+                {
+                    "regexp": "error: internal compiler error: (.*):(\\d+):(\\d+): (.*)$",
+                    "message": 4,
+                    "file": 1,
+                    "line": 2,
+                    "column": 3
+                }
+            ]
+        },
+        {
+            "owner": "cargo-fmt",
+            "pattern": [
+                {
+                    "regexp": "^(Diff in (\\S+)) at line (\\d+):",
+                    "message": 1,
+                    "file": 2,
+                    "line": 3
+                }
+            ]
         }
     ]
 }
diff --git a/src/ci/stage-build.py b/src/ci/stage-build.py
index febc0492b94..3f30b69e8f4 100644..100755
--- a/src/ci/stage-build.py
+++ b/src/ci/stage-build.py
@@ -440,7 +440,7 @@ def retry_action(action, name: str, max_fails: int = 5):
         try:
             action()
             return
-        except:
+        except BaseException: # also catch ctrl+c/sysexit
             LOGGER.error(f"Action `{name}` has failed\n{traceback.format_exc()}")
 
     raise Exception(f"Action `{name}` has failed after {max_fails} attempts")
@@ -818,7 +818,7 @@ def run_tests(pipeline: Pipeline):
     # Extract rustc, libstd, cargo and src archives to create the optimized sysroot
     rustc_dir = extract_dist_dir(f"rustc-nightly-{PGO_HOST}") / "rustc"
     libstd_dir = extract_dist_dir(f"rust-std-nightly-{PGO_HOST}") / f"rust-std-{PGO_HOST}"
-    cargo_dir = extract_dist_dir(f"cargo-nightly-{PGO_HOST}") / f"cargo"
+    cargo_dir = extract_dist_dir(f"cargo-nightly-{PGO_HOST}") / "cargo"
     extracted_src_dir = extract_dist_dir("rust-src-nightly") / "rust-src"
 
     # We need to manually copy libstd to the extracted rustc sysroot
diff --git a/src/etc/dec2flt_table.py b/src/etc/dec2flt_table.py
index aa5188d96c3..9264a8439bb 100644..100755
--- a/src/etc/dec2flt_table.py
+++ b/src/etc/dec2flt_table.py
@@ -14,8 +14,7 @@ Adapted from Daniel Lemire's fast_float ``table_generation.py``,
 available here: <https://github.com/fastfloat/fast_float/blob/main/script/table_generation.py>.
 """
 from __future__ import print_function
-from math import ceil, floor, log, log2
-from fractions import Fraction
+from math import ceil, floor, log
 from collections import deque
 
 HEADER = """
@@ -97,7 +96,6 @@ def print_proper_powers(min_exp, max_exp, bias):
     print('#[rustfmt::skip]')
     typ = '[(u64, u64); N_POWERS_OF_FIVE]'
     print('pub static POWER_OF_FIVE_128: {} = ['.format(typ))
-    lo_mask = (1 << 64) - 1
     for c, exp in powers:
         hi = '0x{:x}'.format(c // (1 << 64))
         lo = '0x{:x}'.format(c % (1 << 64))
diff --git a/src/etc/gdb_load_rust_pretty_printers.py b/src/etc/gdb_load_rust_pretty_printers.py
index 491b6ba9e9e..e05039ce474 100644
--- a/src/etc/gdb_load_rust_pretty_printers.py
+++ b/src/etc/gdb_load_rust_pretty_printers.py
@@ -4,6 +4,7 @@ from os import path
 self_dir = path.dirname(path.realpath(__file__))
 sys.path.append(self_dir)
 
+# ruff: noqa: E402
 import gdb
 import gdb_lookup
 
diff --git a/src/etc/generate-deriving-span-tests.py b/src/etc/generate-deriving-span-tests.py
index d38f5add747..d61693460bc 100755
--- a/src/etc/generate-deriving-span-tests.py
+++ b/src/etc/generate-deriving-span-tests.py
@@ -102,7 +102,9 @@ for (trait, supers, errs) in [('Clone', [], 1),
     traits[trait] = (ALL, supers, errs)
 
 for (trait, (types, super_traits, error_count)) in traits.items():
-    mk = lambda ty: create_test_case(ty, trait, super_traits, error_count)
+    def mk(ty, t=trait, st=super_traits, ec=error_count):
+        return create_test_case(ty, t, st, ec)
+
     if types & ENUM:
         write_file(trait + '-enum', mk(ENUM_TUPLE))
         write_file(trait + '-enum-struct-variant', mk(ENUM_STRUCT))
diff --git a/src/etc/htmldocck.py b/src/etc/htmldocck.py
index c97fb4b8054..eade9e04559 100644..100755
--- a/src/etc/htmldocck.py
+++ b/src/etc/htmldocck.py
@@ -144,7 +144,7 @@ VOID_ELEMENTS = {'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'ke
 
 # Python 2 -> 3 compatibility
 try:
-    unichr
+    unichr # noqa: B018 FIXME: py2
 except NameError:
     unichr = chr
 
@@ -348,7 +348,9 @@ class CachedFiles(object):
             try:
                 tree = ET.fromstringlist(f.readlines(), CustomHTMLParser())
             except Exception as e:
-                raise RuntimeError('Cannot parse an HTML file {!r}: {}'.format(path, e))
+                raise RuntimeError( # noqa: B904 FIXME: py2
+                    'Cannot parse an HTML file {!r}: {}'.format(path, e)
+                )
             self.trees[path] = tree
             return self.trees[path]
 
@@ -422,7 +424,7 @@ def check_snapshot(snapshot_name, actual_tree, normalize_to_text):
         if bless:
             expected_str = None
         else:
-            raise FailedCheck('No saved snapshot value')
+            raise FailedCheck('No saved snapshot value') # noqa: B904 FIXME: py2
 
     if not normalize_to_text:
         actual_str = ET.tostring(actual_tree).decode('utf-8')
diff --git a/src/etc/lldb_batchmode.py b/src/etc/lldb_batchmode.py
index fc355c87b52..db1e0035ea0 100644
--- a/src/etc/lldb_batchmode.py
+++ b/src/etc/lldb_batchmode.py
@@ -124,7 +124,7 @@ def start_breakpoint_listener(target):
                         breakpoint = lldb.SBBreakpoint.GetBreakpointFromEvent(event)
                         print_debug("breakpoint added, id = " + str(breakpoint.id))
                         new_breakpoints.append(breakpoint.id)
-        except:
+        except BaseException: # explicitly catch ctrl+c/sysexit
             print_debug("breakpoint listener shutting down")
 
     # Start the listener and let it run as a daemon
diff --git a/src/etc/lldb_providers.py b/src/etc/lldb_providers.py
index c4381e202b9..4c86b214646 100644
--- a/src/etc/lldb_providers.py
+++ b/src/etc/lldb_providers.py
@@ -1,6 +1,6 @@
 import sys
 
-from lldb import SBValue, SBData, SBError, eBasicTypeLong, eBasicTypeUnsignedLong, \
+from lldb import SBData, SBError, eBasicTypeLong, eBasicTypeUnsignedLong, \
     eBasicTypeUnsignedChar
 
 # from lldb.formatters import Logger
diff --git a/src/etc/test-float-parse/runtests.py b/src/etc/test-float-parse/runtests.py
index cf7279534dc..cf7279534dc 100644..100755
--- a/src/etc/test-float-parse/runtests.py
+++ b/src/etc/test-float-parse/runtests.py
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 29c11e1f335..851002d5e79 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -54,7 +54,7 @@ pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext<
     let mut inserted = FxHashSet::default();
     items.extend(doc.foreigns.iter().map(|(item, renamed)| {
         let item = clean_maybe_renamed_foreign_item(cx, item, *renamed);
-        if let Some(name) = item.name && !item.attrs.lists(sym::doc).has_word(sym::hidden) {
+        if let Some(name) = item.name && !item.is_doc_hidden() {
             inserted.insert((item.type_(), name));
         }
         item
@@ -64,7 +64,7 @@ pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext<
             return None;
         }
         let item = clean_doc_module(x, cx);
-        if item.attrs.lists(sym::doc).has_word(sym::hidden) {
+        if item.is_doc_hidden() {
             // Hidden modules are stripped at a later stage.
             // If a hidden module has the same name as a visible one, we want
             // to keep both of them around.
@@ -85,7 +85,7 @@ pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext<
         }
         let v = clean_maybe_renamed_item(cx, item, *renamed, *import_id);
         for item in &v {
-            if let Some(name) = item.name && !item.attrs.lists(sym::doc).has_word(sym::hidden) {
+            if let Some(name) = item.name && !item.is_doc_hidden() {
                 inserted.insert((item.type_(), name));
             }
         }
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 1999a6b671d..fc5f03568a9 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -783,6 +783,10 @@ impl Item {
         }
         attrs
     }
+
+    pub fn is_doc_hidden(&self) -> bool {
+        self.attrs.is_doc_hidden()
+    }
 }
 
 #[derive(Clone, Debug)]
@@ -1129,6 +1133,10 @@ impl Attributes {
         false
     }
 
+    fn is_doc_hidden(&self) -> bool {
+        self.has_doc_flag(sym::hidden)
+    }
+
     pub(crate) fn from_ast(attrs: &[ast::Attribute]) -> Attributes {
         Attributes::from_ast_iter(attrs.iter().map(|attr| (attr, None)), false)
     }
diff --git a/src/librustdoc/passes/strip_hidden.rs b/src/librustdoc/passes/strip_hidden.rs
index 972b0c5ec19..e2e38d3e79f 100644
--- a/src/librustdoc/passes/strip_hidden.rs
+++ b/src/librustdoc/passes/strip_hidden.rs
@@ -6,7 +6,7 @@ use rustc_span::symbol::sym;
 use std::mem;
 
 use crate::clean;
-use crate::clean::{Item, ItemIdSet, NestedAttributesExt};
+use crate::clean::{Item, ItemIdSet};
 use crate::core::DocContext;
 use crate::fold::{strip_item, DocFolder};
 use crate::passes::{ImplStripper, Pass};
@@ -85,7 +85,7 @@ impl<'a, 'tcx> Stripper<'a, 'tcx> {
 
 impl<'a, 'tcx> DocFolder for Stripper<'a, 'tcx> {
     fn fold_item(&mut self, i: Item) -> Option<Item> {
-        let has_doc_hidden = i.attrs.lists(sym::doc).has_word(sym::hidden);
+        let has_doc_hidden = i.is_doc_hidden();
         let is_impl_or_exported_macro = match *i.kind {
             clean::ImplItem(..) => true,
             // If the macro has the `#[macro_export]` attribute, it means it's accessible at the
diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs
index 73fc26a6b04..90c361d9d28 100644
--- a/src/librustdoc/passes/stripper.rs
+++ b/src/librustdoc/passes/stripper.rs
@@ -1,10 +1,9 @@
 //! A collection of utility functions for the `strip_*` passes.
 use rustc_hir::def_id::DefId;
 use rustc_middle::ty::{TyCtxt, Visibility};
-use rustc_span::symbol::sym;
 use std::mem;
 
-use crate::clean::{self, Item, ItemId, ItemIdSet, NestedAttributesExt};
+use crate::clean::{self, Item, ItemId, ItemIdSet};
 use crate::fold::{strip_item, DocFolder};
 use crate::formats::cache::Cache;
 use crate::visit_lib::RustdocEffectiveVisibilities;
@@ -163,7 +162,7 @@ impl<'a> ImplStripper<'a, '_> {
             // If the "for" item is exported and the impl block isn't `#[doc(hidden)]`, then we
             // need to keep it.
             self.cache.effective_visibilities.is_exported(self.tcx, for_def_id)
-                && !item.attrs.lists(sym::doc).has_word(sym::hidden)
+                && !item.is_doc_hidden()
         } else {
             false
         }
@@ -240,7 +239,7 @@ impl<'tcx> ImportStripper<'tcx> {
             // FIXME: This should be handled the same way as for HTML output.
             imp.imported_item_is_doc_hidden(self.tcx)
         } else {
-            i.attrs.lists(sym::doc).has_word(sym::hidden)
+            i.is_doc_hidden()
         }
     }
 }
@@ -249,7 +248,7 @@ impl<'tcx> DocFolder for ImportStripper<'tcx> {
     fn fold_item(&mut self, i: Item) -> Option<Item> {
         match *i.kind {
             clean::ImportItem(imp) if self.import_should_be_hidden(&i, &imp) => None,
-            clean::ImportItem(_) if i.attrs.lists(sym::doc).has_word(sym::hidden) => None,
+            clean::ImportItem(_) if i.is_doc_hidden() => None,
             clean::ExternCrateItem { .. } | clean::ImportItem(..)
                 if i.visibility(self.tcx) != Some(Visibility::Public) =>
             {
diff --git a/src/tools/build_helper/src/ci.rs b/src/tools/build_helper/src/ci.rs
index d106e5b339b..893195b69c2 100644
--- a/src/tools/build_helper/src/ci.rs
+++ b/src/tools/build_helper/src/ci.rs
@@ -4,8 +4,6 @@ use std::process::Command;
 pub enum CiEnv {
     /// Not a CI environment.
     None,
-    /// The Azure Pipelines environment, for Linux (including Docker), Windows, and macOS builds.
-    AzurePipelines,
     /// The GitHub Actions environment, for Linux (including Docker), Windows and macOS builds.
     GitHubActions,
 }
@@ -13,9 +11,7 @@ pub enum CiEnv {
 impl CiEnv {
     /// Obtains the current CI environment.
     pub fn current() -> CiEnv {
-        if std::env::var("TF_BUILD").map_or(false, |e| e == "True") {
-            CiEnv::AzurePipelines
-        } else if std::env::var("GITHUB_ACTIONS").map_or(false, |e| e == "true") {
+        if std::env::var("GITHUB_ACTIONS").map_or(false, |e| e == "true") {
             CiEnv::GitHubActions
         } else {
             CiEnv::None
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 860a489494c..425a8eaee30 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
@@ -317,7 +317,7 @@ fn check_terminator<'tcx>(
         TerminatorKind::Call {
             func,
             args,
-            from_hir_call: _,
+            call_source: _,
             destination: _,
             target: _,
             unwind: _,
diff --git a/src/tools/clippy/tests/ui/from_over_into_unfixable.rs b/src/tools/clippy/tests/ui/from_over_into_unfixable.rs
index bd62c655216..3b280b7488a 100644
--- a/src/tools/clippy/tests/ui/from_over_into_unfixable.rs
+++ b/src/tools/clippy/tests/ui/from_over_into_unfixable.rs
@@ -32,10 +32,4 @@ impl Into<u8> for ContainsVal {
     }
 }
 
-type Opaque = impl Sized;
-struct IntoOpaque;
-impl Into<Opaque> for IntoOpaque {
-    fn into(self) -> Opaque {}
-}
-
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/from_over_into_unfixable.stderr b/src/tools/clippy/tests/ui/from_over_into_unfixable.stderr
index bb966af4b0f..251f1d84e74 100644
--- a/src/tools/clippy/tests/ui/from_over_into_unfixable.stderr
+++ b/src/tools/clippy/tests/ui/from_over_into_unfixable.stderr
@@ -1,12 +1,29 @@
-error[E0658]: `impl Trait` in type aliases is unstable
-  --> $DIR/from_over_into_unfixable.rs:35:15
+error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
+  --> $DIR/from_over_into_unfixable.rs:11:1
    |
-LL | type Opaque = impl Sized;
-   |               ^^^^^^^^^^
+LL | impl Into<InMacro> for String {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information
-   = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable
+   = help: replace the `Into` implementation with `From<std::string::String>`
+   = note: `-D clippy::from-over-into` implied by `-D warnings`
 
-error: aborting due to previous error
+error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
+  --> $DIR/from_over_into_unfixable.rs:19:1
+   |
+LL | impl Into<WeirdUpperSelf> for &'static [u8] {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: replace the `Into` implementation with `From<&'static [u8]>`
+
+error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
+  --> $DIR/from_over_into_unfixable.rs:28:1
+   |
+LL | impl Into<u8> for ContainsVal {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: `impl From<Local> for Foreign` is allowed by the orphan rules, for more information see
+           https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence
+   = help: replace the `Into` implementation with `From<ContainsVal>`
+
+error: aborting due to 3 previous errors
 
-For more information about this error, try `rustc --explain E0658`.
diff --git a/src/tools/miri/test-cargo-miri/run-test.py b/src/tools/miri/test-cargo-miri/run-test.py
index 9df90c725e4..f022c51e59f 100755
--- a/src/tools/miri/test-cargo-miri/run-test.py
+++ b/src/tools/miri/test-cargo-miri/run-test.py
@@ -5,7 +5,11 @@ Assumes the `MIRI_SYSROOT` env var to be set appropriately,
 and the working directory to contain the cargo-miri-test project.
 '''
 
-import sys, subprocess, os, re, difflib
+import difflib
+import os
+import re
+import sys
+import subprocess
 
 CGREEN  = '\33[32m'
 CBOLD   = '\33[1m'
@@ -46,7 +50,9 @@ def check_output(actual, path, name):
     print(f"--- END diff {name} ---")
     return False
 
-def test(name, cmd, stdout_ref, stderr_ref, stdin=b'', env={}):
+def test(name, cmd, stdout_ref, stderr_ref, stdin=b'', env=None):
+    if env is None:
+        env = {}
     print("Testing {}...".format(name))
     ## Call `cargo miri`, capture all output
     p_env = os.environ.copy()
@@ -64,13 +70,15 @@ def test(name, cmd, stdout_ref, stderr_ref, stdin=b'', env={}):
 
     stdout_matches = check_output(stdout, stdout_ref, "stdout")
     stderr_matches = check_output(stderr, stderr_ref, "stderr")
-    
+
     if p.returncode == 0 and stdout_matches and stderr_matches:
         # All good!
         return
     fail("exit code was {}".format(p.returncode))
 
-def test_no_rebuild(name, cmd, env={}):
+def test_no_rebuild(name, cmd, env=None):
+    if env is None:
+        env = {}
     print("Testing {}...".format(name))
     p_env = os.environ.copy()
     p_env.update(env)
@@ -84,13 +92,13 @@ def test_no_rebuild(name, cmd, env={}):
     stdout = stdout.decode("UTF-8")
     stderr = stderr.decode("UTF-8")
     if p.returncode != 0:
-        fail("rebuild failed");
+        fail("rebuild failed")
     # Also check for 'Running' as a sanity check.
     if stderr.count(" Compiling ") > 0 or stderr.count(" Running ") == 0:
         print("--- BEGIN stderr ---")
         print(stderr, end="")
         print("--- END stderr ---")
-        fail("Something was being rebuilt when it should not be (or we got no output)");
+        fail("Something was being rebuilt when it should not be (or we got no output)")
 
 def test_cargo_miri_run():
     test("`cargo miri run` (no isolation)",
diff --git a/src/tools/publish_toolstate.py b/src/tools/publish_toolstate.py
index 2018c239ba0..f9421117eaa 100755
--- a/src/tools/publish_toolstate.py
+++ b/src/tools/publish_toolstate.py
@@ -22,7 +22,7 @@ except ImportError:
     import urllib.request as urllib2
     from urllib.error import HTTPError
 try:
-    import typing
+    import typing # noqa: F401 FIXME: py2
 except ImportError:
     pass
 
@@ -152,8 +152,8 @@ def update_latest(
         latest = json.load(f, object_pairs_hook=collections.OrderedDict)
 
         current_status = {
-            os: read_current_status(current_commit, 'history/' + os + '.tsv')
-            for os in ['windows', 'linux']
+            os_: read_current_status(current_commit, 'history/' + os_ + '.tsv')
+            for os_ in ['windows', 'linux']
         }
 
         slug = 'rust-lang/rust'
@@ -170,10 +170,10 @@ def update_latest(
             changed = False
             create_issue_for_status = None  # set to the status that caused the issue
 
-            for os, s in current_status.items():
-                old = status[os]
+            for os_, s in current_status.items():
+                old = status[os_]
                 new = s.get(tool, old)
-                status[os] = new
+                status[os_] = new
                 maintainers = ' '.join('@'+name for name in MAINTAINERS.get(tool, ()))
                 # comparing the strings, but they are ordered appropriately:
                 # "test-pass" > "test-fail" > "build-fail"
@@ -181,12 +181,12 @@ def update_latest(
                     # things got fixed or at least the status quo improved
                     changed = True
                     message += '🎉 {} on {}: {} → {} (cc {}).\n' \
-                        .format(tool, os, old, new, maintainers)
+                        .format(tool, os_, old, new, maintainers)
                 elif new < old:
                     # tests or builds are failing and were not failing before
                     changed = True
                     title = '💔 {} on {}: {} → {}' \
-                        .format(tool, os, old, new)
+                        .format(tool, os_, old, new)
                     message += '{} (cc {}).\n' \
                         .format(title, maintainers)
                     # See if we need to create an issue.
diff --git a/src/tools/rust-analyzer/.github/workflows/autopublish.yaml b/src/tools/rust-analyzer/.github/workflows/autopublish.yaml
index 7090c94d93c..15cedab1272 100644
--- a/src/tools/rust-analyzer/.github/workflows/autopublish.yaml
+++ b/src/tools/rust-analyzer/.github/workflows/autopublish.yaml
@@ -28,7 +28,7 @@ jobs:
       - name: Publish Crates
         env:
           CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
-          PATCH: ${{ github.run_number }}
+          RUN_NUMBER: ${{ github.run_number }}
         shell: bash
         run: |
           git config --global user.email "runner@gha.local"
@@ -53,4 +53,4 @@ jobs:
           # Remove library crates from the workspaces so we don't auto-publish them as well
           sed -i 's/ "lib\/\*",//' ./Cargo.toml
           find crates/rust-analyzer -type f -name '*.rs' -exec sed -i 's/rust_analyzer/ra_ap_rust_analyzer/g' {} +
-          cargo workspaces publish --yes --force '*' --exact --no-git-commit --allow-dirty --skip-published custom 0.0.$PATCH
+          cargo workspaces publish --yes --force '*' --exact --no-git-commit --allow-dirty --skip-published custom 0.0.$(($RUN_NUMBER + 133))
diff --git a/src/tools/rust-analyzer/.github/workflows/ci.yaml b/src/tools/rust-analyzer/.github/workflows/ci.yaml
index 622da105fdd..31bb7eed8d7 100644
--- a/src/tools/rust-analyzer/.github/workflows/ci.yaml
+++ b/src/tools/rust-analyzer/.github/workflows/ci.yaml
@@ -24,6 +24,7 @@ jobs:
       pull-requests: read
     outputs:
       typescript: ${{ steps.filter.outputs.typescript }}
+      proc_macros: ${{ steps.filter.outputs.proc_macros }}
     steps:
       - uses: actions/checkout@v3
       - uses: dorny/paths-filter@4067d885736b84de7c414f582ac45897079b0a78
@@ -45,8 +46,8 @@ jobs:
     runs-on: ${{ matrix.os }}
     env:
       CC: deny_c
-      RUST_CHANNEL: "${{ needs.changes.outputs.proc_macros == 'true' && 'nightly' || 'stable'}}"
-      USE_SYSROOT_ABI: "${{ needs.changes.outputs.proc_macros == 'true' && '--features sysroot-abi' || ''}}"
+      RUST_CHANNEL: "${{ needs.changes.outputs.proc_macros == 'true' && 'nightly' || 'stable' }}"
+      USE_SYSROOT_ABI: "${{ needs.changes.outputs.proc_macros == 'true' && '--features sysroot-abi' || '' }}"
 
     strategy:
       fail-fast: false
@@ -62,7 +63,8 @@ jobs:
       - name: Install Rust toolchain
         run: |
           rustup update --no-self-update ${{ env.RUST_CHANNEL }}
-          rustup component add rustfmt rust-src
+          rustup component add --toolchain ${{ env.RUST_CHANNEL }} rustfmt rust-src
+          rustup default ${{ env.RUST_CHANNEL }}
 
       - name: Cache Dependencies
         uses: Swatinem/rust-cache@988c164c3d0e93c4dbab36aaf5bbeb77425b2894
diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock
index e36aef6a6aa..50c81ca279e 100644
--- a/src/tools/rust-analyzer/Cargo.lock
+++ b/src/tools/rust-analyzer/Cargo.lock
@@ -177,21 +177,21 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
 name = "chalk-derive"
-version = "0.89.0"
+version = "0.91.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea176c50987dc4765961aa165001e8eb5a722a26308c5797a47303ea91686aab"
+checksum = "c59178fded594fe78c47b841520e5a4399d00fe15fffee19b945958a878cd02d"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.15",
  "synstructure",
 ]
 
 [[package]]
 name = "chalk-ir"
-version = "0.89.0"
+version = "0.91.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "473b480241695428c14e8f84f1c9a47ef232450a50faf3a4041e5c9dc11e0a3b"
+checksum = "8824be92876823b828d551bb792f79eb1f69c69d1948abf69fccbf84e448e57b"
 dependencies = [
  "bitflags 1.3.2",
  "chalk-derive",
@@ -200,9 +200,9 @@ dependencies = [
 
 [[package]]
 name = "chalk-recursive"
-version = "0.89.0"
+version = "0.91.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6764b4fe67cac3a3758185084efbfbd39bf0352795824ba849ddd2b64cd4bb28"
+checksum = "1e110d1260809c238072d1c8ef84060e39983e8ea9d4c6f74b19b0ebbf8904dc"
 dependencies = [
  "chalk-derive",
  "chalk-ir",
@@ -213,9 +213,9 @@ dependencies = [
 
 [[package]]
 name = "chalk-solve"
-version = "0.89.0"
+version = "0.91.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "55a7e6160966eceb6e7dcc2f479a2af4c477aaf5bccbc640d82515995ab1a6cc"
+checksum = "12200b19abf4b0633095f7bd099f3ef609d314754b6adb358c68cc04d10589e5"
 dependencies = [
  "chalk-derive",
  "chalk-ir",
@@ -327,7 +327,7 @@ checksum = "f3cdeb9ec472d588e539a818b2dee436825730da08ad0017c4b1a17676bdc8b7"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 1.0.109",
 ]
 
 [[package]]
@@ -1505,7 +1505,6 @@ dependencies = [
  "parking_lot 0.12.1",
  "parking_lot_core 0.9.6",
  "proc-macro-api",
- "proc-macro-srv-cli",
  "profile",
  "project-model",
  "rayon",
@@ -1578,7 +1577,7 @@ dependencies = [
  "heck",
  "proc-macro2",
  "quote",
- "syn",
+ "syn 1.0.109",
 ]
 
 [[package]]
@@ -1637,7 +1636,7 @@ checksum = "d7e29c4601e36bcec74a223228dce795f4cd3616341a4af93520ca1a837c087d"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 1.0.109",
 ]
 
 [[package]]
@@ -1660,7 +1659,7 @@ checksum = "395627de918015623b32e7669714206363a7fc00382bf477e72c1f7533e8eafc"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 1.0.109",
 ]
 
 [[package]]
@@ -1731,14 +1730,25 @@ dependencies = [
 ]
 
 [[package]]
+name = "syn"
+version = "2.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
 name = "synstructure"
-version = "0.12.6"
+version = "0.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
+checksum = "285ba80e733fac80aa4270fbcdf83772a79b80aa35c97075320abfee4a915b06"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.15",
  "unicode-xid",
 ]
 
@@ -1811,7 +1821,7 @@ checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 1.0.109",
 ]
 
 [[package]]
@@ -1913,7 +1923,7 @@ checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 1.0.109",
 ]
 
 [[package]]
diff --git a/src/tools/rust-analyzer/crates/base-db/src/fixture.rs b/src/tools/rust-analyzer/crates/base-db/src/fixture.rs
index 5b11343173b..d3abc3870b7 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/fixture.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/fixture.rs
@@ -215,7 +215,7 @@ impl ChangeFixture {
                 None,
                 default_cfg,
                 Default::default(),
-                Env::default(),
+                Env::new_for_test_fixture(),
                 false,
                 CrateOrigin::Local { repo: None, name: None },
                 default_target_data_layout
@@ -259,7 +259,7 @@ impl ChangeFixture {
                 None,
                 Default::default(),
                 Default::default(),
-                Env::default(),
+                Env::new_for_test_fixture(),
                 false,
                 CrateOrigin::Lang(LangCrateOrigin::Core),
                 target_layout.clone(),
@@ -298,7 +298,7 @@ impl ChangeFixture {
                 None,
                 Default::default(),
                 Default::default(),
-                Env::default(),
+                Env::new_for_test_fixture(),
                 true,
                 CrateOrigin::Local { repo: None, name: None },
                 target_layout,
diff --git a/src/tools/rust-analyzer/crates/base-db/src/input.rs b/src/tools/rust-analyzer/crates/base-db/src/input.rs
index e8d521b42f8..f2e523675bc 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/input.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs
@@ -151,6 +151,12 @@ pub enum CrateOrigin {
     Lang(LangCrateOrigin),
 }
 
+impl CrateOrigin {
+    pub fn is_local(&self) -> bool {
+        matches!(self, CrateOrigin::Local { .. })
+    }
+}
+
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub enum LangCrateOrigin {
     Alloc,
@@ -333,6 +339,17 @@ pub struct Env {
     entries: FxHashMap<String, String>,
 }
 
+impl Env {
+    pub fn new_for_test_fixture() -> Self {
+        Env {
+            entries: FxHashMap::from_iter([(
+                String::from("__ra_is_test_fixture"),
+                String::from("__ra_is_test_fixture"),
+            )]),
+        }
+    }
+}
+
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct Dependency {
     pub crate_id: CrateId,
@@ -456,6 +473,12 @@ impl CrateGraph {
         self.arena.iter().map(|(idx, _)| idx)
     }
 
+    // FIXME: used for `handle_hack_cargo_workspace`, should be removed later
+    #[doc(hidden)]
+    pub fn iter_mut(&mut self) -> impl Iterator<Item = (CrateId, &mut CrateData)> + '_ {
+        self.arena.iter_mut()
+    }
+
     /// Returns an iterator over all transitive dependencies of the given crate,
     /// including the crate itself.
     pub fn transitive_deps(&self, of: CrateId) -> impl Iterator<Item = CrateId> {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/body.rs
index 36626ed1a9b..94dc39b1175 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs
@@ -37,6 +37,9 @@ pub struct Body {
     pub pats: Arena<Pat>,
     pub bindings: Arena<Binding>,
     pub labels: Arena<Label>,
+    /// Id of the closure/generator that owns the corresponding binding. If a binding is owned by the
+    /// top level expression, it will not be listed in here.
+    pub binding_owners: FxHashMap<BindingId, ExprId>,
     /// The patterns for the function's parameters. While the parameter types are
     /// part of the function signature, the patterns are not (they don't change
     /// the external type of the function).
@@ -118,7 +121,8 @@ impl Body {
         let _p = profile::span("body_with_source_map_query");
         let mut params = None;
 
-        let (file_id, module, body, is_async_fn) = {
+        let mut is_async_fn = false;
+        let InFile { file_id, value: body } = {
             match def {
                 DefWithBodyId::FunctionId(f) => {
                     let data = db.function_data(f);
@@ -138,31 +142,27 @@ impl Body {
                             }),
                         )
                     });
-                    (
-                        src.file_id,
-                        f.module(db),
-                        src.value.body().map(ast::Expr::from),
-                        data.has_async_kw(),
-                    )
+                    is_async_fn = data.has_async_kw();
+                    src.map(|it| it.body().map(ast::Expr::from))
                 }
                 DefWithBodyId::ConstId(c) => {
                     let c = c.lookup(db);
                     let src = c.source(db);
-                    (src.file_id, c.module(db), src.value.body(), false)
+                    src.map(|it| it.body())
                 }
                 DefWithBodyId::StaticId(s) => {
                     let s = s.lookup(db);
                     let src = s.source(db);
-                    (src.file_id, s.module(db), src.value.body(), false)
+                    src.map(|it| it.body())
                 }
                 DefWithBodyId::VariantId(v) => {
-                    let e = v.parent.lookup(db);
                     let src = v.parent.child_source(db);
-                    let variant = &src.value[v.local_id];
-                    (src.file_id, e.container, variant.expr(), false)
+                    src.map(|it| it[v.local_id].expr())
                 }
+                DefWithBodyId::InTypeConstId(c) => c.lookup(db).id.map(|_| c.source(db).expr()),
             }
         };
+        let module = def.module(db);
         let expander = Expander::new(db, file_id, module);
         let (mut body, source_map) =
             Body::new(db, def, expander, params, body, module.krate, is_async_fn);
@@ -209,14 +209,24 @@ impl Body {
     }
 
     fn shrink_to_fit(&mut self) {
-        let Self { _c: _, body_expr: _, block_scopes, exprs, labels, params, pats, bindings } =
-            self;
+        let Self {
+            _c: _,
+            body_expr: _,
+            block_scopes,
+            exprs,
+            labels,
+            params,
+            pats,
+            bindings,
+            binding_owners,
+        } = self;
         block_scopes.shrink_to_fit();
         exprs.shrink_to_fit();
         labels.shrink_to_fit();
         params.shrink_to_fit();
         pats.shrink_to_fit();
         bindings.shrink_to_fit();
+        binding_owners.shrink_to_fit();
     }
 
     pub fn walk_bindings_in_pat(&self, pat_id: PatId, mut f: impl FnMut(BindingId)) {
@@ -260,6 +270,17 @@ impl Body {
         f(pat_id);
         self.walk_pats_shallow(pat_id, |p| self.walk_pats(p, f));
     }
+
+    pub fn is_binding_upvar(&self, binding: BindingId, relative_to: ExprId) -> bool {
+        match self.binding_owners.get(&binding) {
+            Some(x) => {
+                // We assign expression ids in a way that outer closures will receive
+                // a lower id
+                x.into_raw() < relative_to.into_raw()
+            }
+            None => true,
+        }
+    }
 }
 
 impl Default for Body {
@@ -272,6 +293,7 @@ impl Default for Body {
             labels: Default::default(),
             params: Default::default(),
             block_scopes: Default::default(),
+            binding_owners: Default::default(),
             _c: Default::default(),
         }
     }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs
index 7b88e525bf1..b375ec63a67 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs
@@ -11,7 +11,6 @@ use hir_expand::{
     AstId, ExpandError, InFile,
 };
 use intern::Interned;
-use la_arena::Arena;
 use profile::Count;
 use rustc_hash::FxHashMap;
 use smallvec::SmallVec;
@@ -40,7 +39,7 @@ use crate::{
     nameres::{DefMap, MacroSubNs},
     path::{GenericArgs, Path},
     type_ref::{Mutability, Rawness, TypeRef},
-    AdtId, BlockId, BlockLoc, DefWithBodyId, ModuleDefId, UnresolvedMacro,
+    AdtId, BlockId, BlockLoc, ConstBlockLoc, DefWithBodyId, ModuleDefId, UnresolvedMacro,
 };
 
 pub(super) fn lower(
@@ -60,10 +59,11 @@ pub(super) fn lower(
         source_map: BodySourceMap::default(),
         ast_id_map: db.ast_id_map(expander.current_file_id),
         body: Body {
-            exprs: Arena::default(),
-            pats: Arena::default(),
-            bindings: Arena::default(),
-            labels: Arena::default(),
+            exprs: Default::default(),
+            pats: Default::default(),
+            bindings: Default::default(),
+            binding_owners: Default::default(),
+            labels: Default::default(),
             params: Vec::new(),
             body_expr: dummy_expr_id(),
             block_scopes: Vec::new(),
@@ -188,7 +188,7 @@ impl ExprCollector<'_> {
                 param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false))
             {
                 let ptr = AstPtr::new(&self_param);
-                let binding_id = self.alloc_binding(
+                let binding_id: la_arena::Idx<Binding> = self.alloc_binding(
                     name![self],
                     BindingAnnotation::new(
                         self_param.mut_token().is_some() && self_param.amp_token().is_none(),
@@ -297,7 +297,10 @@ impl ExprCollector<'_> {
                         let (result_expr_id, prev_binding_owner) =
                             this.initialize_binding_owner(syntax_ptr);
                         let inner_expr = this.collect_block(e);
-                        let x = this.db.intern_anonymous_const((this.owner, inner_expr));
+                        let x = this.db.intern_anonymous_const(ConstBlockLoc {
+                            parent: this.owner,
+                            root: inner_expr,
+                        });
                         this.body.exprs[result_expr_id] = Expr::Const(x);
                         this.current_binding_owner = prev_binding_owner;
                         result_expr_id
@@ -742,16 +745,14 @@ impl ExprCollector<'_> {
     /// }
     /// ```
     fn collect_for_loop(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::ForExpr) -> ExprId {
-        let (into_iter_fn, iter_next_fn, option_some, option_none) = 'if_chain: {
-            if let Some(into_iter_fn) = LangItem::IntoIterIntoIter.path(self.db, self.krate) {
-                if let Some(iter_next_fn) = LangItem::IteratorNext.path(self.db, self.krate) {
-                    if let Some(option_some) = LangItem::OptionSome.path(self.db, self.krate) {
-                        if let Some(option_none) = LangItem::OptionNone.path(self.db, self.krate) {
-                            break 'if_chain (into_iter_fn, iter_next_fn, option_some, option_none);
-                        }
-                    }
-                }
-            }
+        let Some((into_iter_fn, iter_next_fn, option_some, option_none)) = (|| {
+            Some((
+                LangItem::IntoIterIntoIter.path(self.db, self.krate)?,
+                LangItem::IteratorNext.path(self.db, self.krate)?,
+                LangItem::OptionSome.path(self.db, self.krate)?,
+                LangItem::OptionNone.path(self.db, self.krate)?,
+            ))
+        })() else {
             // Some of the needed lang items are missing, so we can't desugar
             return self.alloc_expr(Expr::Missing, syntax_ptr);
         };
@@ -784,8 +785,8 @@ impl ExprCollector<'_> {
             }),
         };
         let iter_name = Name::generate_new_name();
-        let iter_binding = self.alloc_binding(iter_name.clone(), BindingAnnotation::Mutable);
-        let iter_expr = self.alloc_expr(Expr::Path(Path::from(iter_name)), syntax_ptr.clone());
+        let iter_expr =
+            self.alloc_expr(Expr::Path(Path::from(iter_name.clone())), syntax_ptr.clone());
         let iter_expr_mut = self.alloc_expr(
             Expr::Ref { expr: iter_expr, rawness: Rawness::Ref, mutability: Mutability::Mut },
             syntax_ptr.clone(),
@@ -805,7 +806,9 @@ impl ExprCollector<'_> {
         );
         let loop_outer =
             self.alloc_expr(Expr::Loop { body: loop_inner, label }, syntax_ptr.clone());
+        let iter_binding = self.alloc_binding(iter_name, BindingAnnotation::Mutable);
         let iter_pat = self.alloc_pat_desugared(Pat::Bind { id: iter_binding, subpat: None });
+        self.add_definition_to_binding(iter_binding, iter_pat);
         self.alloc_expr(
             Expr::Match {
                 expr: iterator,
@@ -827,18 +830,14 @@ impl ExprCollector<'_> {
     /// }
     /// ```
     fn collect_try_operator(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::TryExpr) -> ExprId {
-        let (try_branch, cf_continue, cf_break, try_from_residual) = 'if_chain: {
-            if let Some(try_branch) = LangItem::TryTraitBranch.path(self.db, self.krate) {
-                if let Some(cf_continue) = LangItem::ControlFlowContinue.path(self.db, self.krate) {
-                    if let Some(cf_break) = LangItem::ControlFlowBreak.path(self.db, self.krate) {
-                        if let Some(try_from_residual) =
-                            LangItem::TryTraitFromResidual.path(self.db, self.krate)
-                        {
-                            break 'if_chain (try_branch, cf_continue, cf_break, try_from_residual);
-                        }
-                    }
-                }
-            }
+        let Some((try_branch, cf_continue, cf_break, try_from_residual)) = (|| {
+            Some((
+                LangItem::TryTraitBranch.path(self.db, self.krate)?,
+                LangItem::ControlFlowContinue.path(self.db, self.krate)?,
+                LangItem::ControlFlowBreak.path(self.db, self.krate)?,
+                LangItem::TryTraitFromResidual.path(self.db, self.krate)?,
+            ))
+        })() else {
             // Some of the needed lang items are missing, so we can't desugar
             return self.alloc_expr(Expr::Missing, syntax_ptr);
         };
@@ -1541,13 +1540,16 @@ impl ExprCollector<'_> {
     }
 
     fn alloc_binding(&mut self, name: Name, mode: BindingAnnotation) -> BindingId {
-        self.body.bindings.alloc(Binding {
+        let binding = self.body.bindings.alloc(Binding {
             name,
             mode,
             definitions: SmallVec::new(),
-            owner: self.current_binding_owner,
             problems: None,
-        })
+        });
+        if let Some(owner) = self.current_binding_owner {
+            self.body.binding_owners.insert(binding, owner);
+        }
+        binding
     }
 
     fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs
index 88380aa355d..cd6df0e6325 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs
@@ -40,6 +40,7 @@ pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBo
             };
             format!("const {name} = ")
         }
+        DefWithBodyId::InTypeConstId(_) => format!("In type const = "),
         DefWithBodyId::VariantId(it) => {
             let src = it.parent.child_source(db);
             let variant = &src.value[it.local_id];
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/db.rs b/src/tools/rust-analyzer/crates/hir-def/src/db.rs
index 6d18e3f56ca..04ec47f84ca 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs
@@ -16,21 +16,22 @@ use crate::{
         TraitAliasData, TraitData, TypeAliasData,
     },
     generics::GenericParams,
-    hir::ExprId,
     import_map::ImportMap,
     item_tree::{AttrOwner, ItemTree},
     lang_item::{LangItem, LangItemTarget, LangItems},
     nameres::{diagnostics::DefDiagnostic, DefMap},
     visibility::{self, Visibility},
-    AnonymousConstId, AttrDefId, BlockId, BlockLoc, ConstId, ConstLoc, DefWithBodyId, EnumId,
-    EnumLoc, ExternBlockId, ExternBlockLoc, FunctionId, FunctionLoc, GenericDefId, ImplId, ImplLoc,
-    LocalEnumVariantId, LocalFieldId, Macro2Id, Macro2Loc, MacroRulesId, MacroRulesLoc,
-    ProcMacroId, ProcMacroLoc, StaticId, StaticLoc, StructId, StructLoc, TraitAliasId,
-    TraitAliasLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, VariantId,
+    AttrDefId, BlockId, BlockLoc, ConstBlockId, ConstBlockLoc, ConstId, ConstLoc, DefWithBodyId,
+    EnumId, EnumLoc, ExternBlockId, ExternBlockLoc, FunctionId, FunctionLoc, GenericDefId, ImplId,
+    ImplLoc, InTypeConstId, InTypeConstLoc, LocalEnumVariantId, LocalFieldId, Macro2Id, Macro2Loc,
+    MacroRulesId, MacroRulesLoc, ProcMacroId, ProcMacroLoc, StaticId, StaticLoc, StructId,
+    StructLoc, TraitAliasId, TraitAliasLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId,
+    UnionLoc, VariantId,
 };
 
 #[salsa::query_group(InternDatabaseStorage)]
 pub trait InternDatabase: SourceDatabase {
+    // region: items
     #[salsa::interned]
     fn intern_function(&self, loc: FunctionLoc) -> FunctionId;
     #[salsa::interned]
@@ -54,15 +55,19 @@ pub trait InternDatabase: SourceDatabase {
     #[salsa::interned]
     fn intern_extern_block(&self, loc: ExternBlockLoc) -> ExternBlockId;
     #[salsa::interned]
-    fn intern_block(&self, loc: BlockLoc) -> BlockId;
-    #[salsa::interned]
     fn intern_macro2(&self, loc: Macro2Loc) -> Macro2Id;
     #[salsa::interned]
     fn intern_proc_macro(&self, loc: ProcMacroLoc) -> ProcMacroId;
     #[salsa::interned]
     fn intern_macro_rules(&self, loc: MacroRulesLoc) -> MacroRulesId;
+    // endregion: items
+
+    #[salsa::interned]
+    fn intern_block(&self, loc: BlockLoc) -> BlockId;
+    #[salsa::interned]
+    fn intern_anonymous_const(&self, id: ConstBlockLoc) -> ConstBlockId;
     #[salsa::interned]
-    fn intern_anonymous_const(&self, id: (DefWithBodyId, ExprId)) -> AnonymousConstId;
+    fn intern_in_type_const(&self, id: InTypeConstLoc) -> InTypeConstId;
 }
 
 #[salsa::query_group(DefDatabaseStorage)]
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expander.rs b/src/tools/rust-analyzer/crates/hir-def/src/expander.rs
index 34ed1e72f20..a588827c8d3 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expander.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expander.rs
@@ -113,10 +113,10 @@ impl Expander {
         call_id: MacroCallId,
         error: Option<ExpandError>,
     ) -> ExpandResult<Option<InFile<Parse<SyntaxNode>>>> {
-        let file_id = call_id.as_file();
-        let ExpandResult { value, err } = db.parse_or_expand_with_err(file_id);
+        let macro_file = call_id.as_macro_file();
+        let ExpandResult { value, err } = db.parse_macro_expansion(macro_file);
 
-        ExpandResult { value: Some(InFile::new(file_id, value)), err: error.or(err) }
+        ExpandResult { value: Some(InFile::new(macro_file.into(), value.0)), err: error.or(err) }
     }
 
     pub fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) {
@@ -155,7 +155,7 @@ impl Expander {
     }
 
     pub(crate) fn parse_path(&mut self, db: &dyn DefDatabase, path: ast::Path) -> Option<Path> {
-        let ctx = LowerCtx::with_hygiene(db, &self.cfg_expander.hygiene);
+        let ctx = LowerCtx::new(db, &self.cfg_expander.hygiene, self.current_file_id);
         Path::from_src(path, &ctx)
     }
 
@@ -179,8 +179,8 @@ impl Expander {
         } else if self.recursion_limit.check(self.recursion_depth as usize + 1).is_err() {
             self.recursion_depth = u32::MAX;
             cov_mark::hit!(your_stack_belongs_to_me);
-            return ExpandResult::only_err(ExpandError::Other(
-                "reached recursion limit during macro expansion".into(),
+            return ExpandResult::only_err(ExpandError::other(
+                "reached recursion limit during macro expansion",
             ));
         }
 
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
index e8cc2eab461..8c49ae1c4af 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
@@ -81,7 +81,7 @@ fn find_path_inner(
     }
 
     let def_map = from.def_map(db);
-    let crate_root = def_map.crate_root();
+    let crate_root = def_map.crate_root().into();
     // - if the item is a module, jump straight to module search
     if let ItemInNs::Types(ModuleDefId::ModuleId(module_id)) = item {
         let mut visited_modules = FxHashSet::default();
@@ -374,7 +374,7 @@ fn calculate_best_path(
         }
     }
     if let Some(module) = item.module(db) {
-        if module.def_map(db).block_id().is_some() && prefixed.is_some() {
+        if module.containing_block().is_some() && prefixed.is_some() {
             cov_mark::hit!(prefixed_in_block_expression);
             prefixed = Some(PrefixKind::Plain);
         }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs
index 4ad8a7aa8eb..500e880061a 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs
@@ -26,7 +26,7 @@ use crate::{
     builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
     path::{GenericArgs, Path},
     type_ref::{Mutability, Rawness, TypeRef},
-    AnonymousConstId, BlockId,
+    BlockId, ConstBlockId,
 };
 
 pub use syntax::ast::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp};
@@ -181,7 +181,7 @@ pub enum Expr {
         statements: Box<[Statement]>,
         tail: Option<ExprId>,
     },
-    Const(AnonymousConstId),
+    Const(ConstBlockId),
     Unsafe {
         id: Option<BlockId>,
         statements: Box<[Statement]>,
@@ -501,25 +501,9 @@ pub struct Binding {
     pub name: Name,
     pub mode: BindingAnnotation,
     pub definitions: SmallVec<[PatId; 1]>,
-    /// Id of the closure/generator that owns this binding. If it is owned by the
-    /// top level expression, this field would be `None`.
-    pub owner: Option<ExprId>,
     pub problems: Option<BindingProblems>,
 }
 
-impl Binding {
-    pub fn is_upvar(&self, relative_to: ExprId) -> bool {
-        match self.owner {
-            Some(x) => {
-                // We assign expression ids in a way that outer closures will receive
-                // a lower id
-                x.into_raw() < relative_to.into_raw()
-            }
-            None => true,
-        }
-    }
-}
-
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub struct RecordFieldPat {
     pub name: Name,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs
index 0573c9a6f8a..fa1f4933a26 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs
@@ -118,7 +118,7 @@ pub enum TypeRef {
     Reference(Box<TypeRef>, Option<LifetimeRef>, Mutability),
     // FIXME: for full const generics, the latter element (length) here is going to have to be an
     // expression that is further lowered later in hir_ty.
-    Array(Box<TypeRef>, ConstRefOrPath),
+    Array(Box<TypeRef>, ConstRef),
     Slice(Box<TypeRef>),
     /// A fn pointer. Last element of the vector is the return type.
     Fn(Vec<(Option<Name>, TypeRef)>, bool /*varargs*/, bool /*is_unsafe*/),
@@ -186,11 +186,7 @@ impl TypeRef {
                 TypeRef::RawPtr(Box::new(inner_ty), mutability)
             }
             ast::Type::ArrayType(inner) => {
-                // FIXME: This is a hack. We should probably reuse the machinery of
-                // `hir_def::body::lower` to lower this into an `Expr` and then evaluate it at the
-                // `hir_ty` level, which would allow knowing the type of:
-                // let v: [u8; 2 + 2] = [0u8; 4];
-                let len = ConstRefOrPath::from_expr_opt(inner.expr());
+                let len = ConstRef::from_const_arg(ctx, inner.const_arg());
                 TypeRef::Array(Box::new(TypeRef::from_ast_opt(ctx, inner.ty())), len)
             }
             ast::Type::SliceType(inner) => {
@@ -380,73 +376,84 @@ impl TypeBound {
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub enum ConstRefOrPath {
-    Scalar(ConstRef),
+pub enum ConstRef {
+    Scalar(LiteralConstRef),
     Path(Name),
+    Complex(AstId<ast::ConstArg>),
 }
 
-impl ConstRefOrPath {
-    pub(crate) fn from_expr_opt(expr: Option<ast::Expr>) -> Self {
-        match expr {
-            Some(x) => Self::from_expr(x),
-            None => Self::Scalar(ConstRef::Unknown),
+impl ConstRef {
+    pub(crate) fn from_const_arg(lower_ctx: &LowerCtx<'_>, arg: Option<ast::ConstArg>) -> Self {
+        if let Some(arg) = arg {
+            let ast_id = lower_ctx.ast_id(&arg);
+            if let Some(expr) = arg.expr() {
+                return Self::from_expr(expr, ast_id);
+            }
         }
+        Self::Scalar(LiteralConstRef::Unknown)
     }
 
     pub fn display<'a>(&'a self, db: &'a dyn ExpandDatabase) -> impl fmt::Display + 'a {
-        struct Display<'a>(&'a dyn ExpandDatabase, &'a ConstRefOrPath);
+        struct Display<'a>(&'a dyn ExpandDatabase, &'a ConstRef);
         impl fmt::Display for Display<'_> {
             fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                 match self.1 {
-                    ConstRefOrPath::Scalar(s) => s.fmt(f),
-                    ConstRefOrPath::Path(n) => n.display(self.0).fmt(f),
+                    ConstRef::Scalar(s) => s.fmt(f),
+                    ConstRef::Path(n) => n.display(self.0).fmt(f),
+                    ConstRef::Complex(_) => f.write_str("{const}"),
                 }
             }
         }
         Display(db, self)
     }
 
-    // FIXME: as per the comments on `TypeRef::Array`, this evaluation should not happen at this
-    // parse stage.
-    fn from_expr(expr: ast::Expr) -> Self {
+    // We special case literals and single identifiers, to speed up things.
+    fn from_expr(expr: ast::Expr, ast_id: Option<AstId<ast::ConstArg>>) -> Self {
+        fn is_path_ident(p: &ast::PathExpr) -> bool {
+            let Some(path) = p.path() else {
+                return false;
+            };
+            if path.coloncolon_token().is_some() {
+                return false;
+            }
+            if let Some(s) = path.segment() {
+                if s.coloncolon_token().is_some() || s.generic_arg_list().is_some() {
+                    return false;
+                }
+            }
+            true
+        }
         match expr {
-            ast::Expr::PathExpr(p) => {
+            ast::Expr::PathExpr(p) if is_path_ident(&p) => {
                 match p.path().and_then(|x| x.segment()).and_then(|x| x.name_ref()) {
                     Some(x) => Self::Path(x.as_name()),
-                    None => Self::Scalar(ConstRef::Unknown),
+                    None => Self::Scalar(LiteralConstRef::Unknown),
                 }
             }
-            ast::Expr::PrefixExpr(prefix_expr) => match prefix_expr.op_kind() {
-                Some(ast::UnaryOp::Neg) => {
-                    let unsigned = Self::from_expr_opt(prefix_expr.expr());
-                    // Add sign
-                    match unsigned {
-                        Self::Scalar(ConstRef::UInt(num)) => {
-                            Self::Scalar(ConstRef::Int(-(num as i128)))
-                        }
-                        other => other,
-                    }
-                }
-                _ => Self::from_expr_opt(prefix_expr.expr()),
-            },
             ast::Expr::Literal(literal) => Self::Scalar(match literal.kind() {
                 ast::LiteralKind::IntNumber(num) => {
-                    num.value().map(ConstRef::UInt).unwrap_or(ConstRef::Unknown)
+                    num.value().map(LiteralConstRef::UInt).unwrap_or(LiteralConstRef::Unknown)
                 }
                 ast::LiteralKind::Char(c) => {
-                    c.value().map(ConstRef::Char).unwrap_or(ConstRef::Unknown)
+                    c.value().map(LiteralConstRef::Char).unwrap_or(LiteralConstRef::Unknown)
                 }
-                ast::LiteralKind::Bool(f) => ConstRef::Bool(f),
-                _ => ConstRef::Unknown,
+                ast::LiteralKind::Bool(f) => LiteralConstRef::Bool(f),
+                _ => LiteralConstRef::Unknown,
             }),
-            _ => Self::Scalar(ConstRef::Unknown),
+            _ => {
+                if let Some(ast_id) = ast_id {
+                    Self::Complex(ast_id)
+                } else {
+                    Self::Scalar(LiteralConstRef::Unknown)
+                }
+            }
         }
     }
 }
 
-/// A concrete constant value
+/// A literal constant value
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub enum ConstRef {
+pub enum LiteralConstRef {
     Int(i128),
     UInt(u128),
     Bool(bool),
@@ -460,18 +467,20 @@ pub enum ConstRef {
     Unknown,
 }
 
-impl ConstRef {
+impl LiteralConstRef {
     pub fn builtin_type(&self) -> BuiltinType {
         match self {
-            ConstRef::UInt(_) | ConstRef::Unknown => BuiltinType::Uint(BuiltinUint::U128),
-            ConstRef::Int(_) => BuiltinType::Int(BuiltinInt::I128),
-            ConstRef::Char(_) => BuiltinType::Char,
-            ConstRef::Bool(_) => BuiltinType::Bool,
+            LiteralConstRef::UInt(_) | LiteralConstRef::Unknown => {
+                BuiltinType::Uint(BuiltinUint::U128)
+            }
+            LiteralConstRef::Int(_) => BuiltinType::Int(BuiltinInt::I128),
+            LiteralConstRef::Char(_) => BuiltinType::Char,
+            LiteralConstRef::Bool(_) => BuiltinType::Bool,
         }
     }
 }
 
-impl From<Literal> for ConstRef {
+impl From<Literal> for LiteralConstRef {
     fn from(literal: Literal) -> Self {
         match literal {
             Literal::Char(c) => Self::Char(c),
@@ -483,14 +492,14 @@ impl From<Literal> for ConstRef {
     }
 }
 
-impl std::fmt::Display for ConstRef {
+impl std::fmt::Display for LiteralConstRef {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
         match self {
-            ConstRef::Int(num) => num.fmt(f),
-            ConstRef::UInt(num) => num.fmt(f),
-            ConstRef::Bool(flag) => flag.fmt(f),
-            ConstRef::Char(c) => write!(f, "'{c}'"),
-            ConstRef::Unknown => f.write_char('_'),
+            LiteralConstRef::Int(num) => num.fmt(f),
+            LiteralConstRef::UInt(num) => num.fmt(f),
+            LiteralConstRef::Bool(flag) => flag.fmt(f),
+            LiteralConstRef::Char(c) => write!(f, "'{c}'"),
+            LiteralConstRef::Unknown => f.write_char('_'),
         }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
index 3ed321d189d..2001fb29a9e 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
@@ -334,10 +334,6 @@ impl ItemScope {
         )
     }
 
-    pub(crate) fn collect_legacy_macros(&self) -> FxHashMap<Name, SmallVec<[MacroId; 1]>> {
-        self.legacy_macros.clone()
-    }
-
     /// Marks everything that is not a procedural macro as private to `this_module`.
     pub(crate) fn censor_non_proc_macros(&mut self, this_module: ModuleId) {
         self.types
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
index 590ed64af5d..e74b71888c2 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
@@ -101,7 +101,6 @@ pub struct ItemTree {
     top_level: SmallVec<[ModItem; 1]>,
     attrs: FxHashMap<AttrOwner, RawAttrs>,
 
-    // FIXME: Remove this indirection, an item tree is almost always non-empty?
     data: Option<Box<ItemTreeData>>,
 }
 
@@ -718,7 +717,6 @@ pub struct Mod {
 pub enum ModKind {
     /// `mod m { ... }`
     Inline { items: Box<[ModItem]> },
-
     /// `mod m;`
     Outline,
 }
@@ -892,10 +890,6 @@ impl ModItem {
         }
     }
 
-    pub fn downcast<N: ItemTreeNode>(self) -> Option<FileItemTreeId<N>> {
-        N::id_from_mod_item(self)
-    }
-
     pub fn ast_id(&self, tree: &ItemTree) -> FileAstId<ast::Item> {
         match self {
             ModItem::Import(it) => tree[it.index].ast_id().upcast(),
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
index 9cd3dfd6f7c..9d8b57a0da9 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
@@ -57,13 +57,12 @@ mod test_db;
 mod macro_expansion_tests;
 mod pretty;
 
-use std::hash::{Hash, Hasher};
-
-use base_db::{
-    impl_intern_key,
-    salsa::{self, InternId},
-    CrateId, ProcMacroKind,
+use std::{
+    hash::{Hash, Hasher},
+    panic::{RefUnwindSafe, UnwindSafe},
 };
+
+use base_db::{impl_intern_key, salsa, CrateId, ProcMacroKind};
 use hir_expand::{
     ast_id_map::FileAstId,
     attrs::{Attr, AttrId, AttrInput},
@@ -71,7 +70,7 @@ use hir_expand::{
     builtin_derive_macro::BuiltinDeriveExpander,
     builtin_fn_macro::{BuiltinFnLikeExpander, EagerExpander},
     db::ExpandDatabase,
-    eager::expand_eager_macro,
+    eager::expand_eager_macro_input,
     hygiene::Hygiene,
     proc_macro::ProcMacroExpander,
     AstId, ExpandError, ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind,
@@ -89,11 +88,51 @@ use crate::{
     builtin_type::BuiltinType,
     data::adt::VariantData,
     item_tree::{
-        Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, MacroDef, MacroRules, ModItem,
-        Static, Struct, Trait, TraitAlias, TypeAlias, Union,
+        Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, MacroDef, MacroRules, Static,
+        Struct, Trait, TraitAlias, TypeAlias, Union,
     },
 };
 
+/// A `ModuleId` that is always a crate's root module.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct CrateRootModuleId {
+    krate: CrateId,
+}
+
+impl CrateRootModuleId {
+    pub fn def_map(&self, db: &dyn db::DefDatabase) -> Arc<DefMap> {
+        db.crate_def_map(self.krate)
+    }
+
+    pub fn krate(self) -> CrateId {
+        self.krate
+    }
+}
+
+impl From<CrateRootModuleId> for ModuleId {
+    fn from(CrateRootModuleId { krate }: CrateRootModuleId) -> Self {
+        ModuleId { krate, block: None, local_id: DefMap::ROOT }
+    }
+}
+
+impl From<CrateRootModuleId> for ModuleDefId {
+    fn from(value: CrateRootModuleId) -> Self {
+        ModuleDefId::ModuleId(value.into())
+    }
+}
+
+impl TryFrom<ModuleId> for CrateRootModuleId {
+    type Error = ();
+
+    fn try_from(ModuleId { krate, block, local_id }: ModuleId) -> Result<Self, Self::Error> {
+        if block.is_none() && local_id == DefMap::ROOT {
+            Ok(CrateRootModuleId { krate })
+        } else {
+            Err(())
+        }
+    }
+}
+
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct ModuleId {
     krate: CrateId,
@@ -315,8 +354,7 @@ impl_intern!(MacroRulesId, MacroRulesLoc, intern_macro_rules, lookup_intern_macr
 pub struct ProcMacroId(salsa::InternId);
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct ProcMacroLoc {
-    // FIXME: this should be a crate? or just a crate-root module
-    pub container: ModuleId,
+    pub container: CrateRootModuleId,
     pub id: ItemTreeId<Function>,
     pub expander: ProcMacroExpander,
     pub kind: ProcMacroKind,
@@ -476,29 +514,199 @@ impl_from!(
     for ModuleDefId
 );
 
-// FIXME: make this a DefWithBodyId
+/// Id of the anonymous const block expression and patterns. This is very similar to `ClosureId` and
+/// shouldn't be a `DefWithBodyId` since its type inference is dependent on its parent.
 #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
-pub struct AnonymousConstId(InternId);
-impl_intern_key!(AnonymousConstId);
+pub struct ConstBlockId(salsa::InternId);
+impl_intern!(ConstBlockId, ConstBlockLoc, intern_anonymous_const, lookup_intern_anonymous_const);
+
+#[derive(Debug, Hash, PartialEq, Eq, Clone)]
+pub struct ConstBlockLoc {
+    /// The parent of the anonymous const block.
+    pub parent: DefWithBodyId,
+    /// The root expression of this const block in the parent body.
+    pub root: hir::ExprId,
+}
+
+#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
+pub enum TypeOwnerId {
+    FunctionId(FunctionId),
+    StaticId(StaticId),
+    ConstId(ConstId),
+    InTypeConstId(InTypeConstId),
+    AdtId(AdtId),
+    TraitId(TraitId),
+    TraitAliasId(TraitAliasId),
+    TypeAliasId(TypeAliasId),
+    ImplId(ImplId),
+    EnumVariantId(EnumVariantId),
+    // FIXME(const-generic-body): ModuleId should not be a type owner. This needs to be fixed to make `TypeOwnerId` actually
+    // useful for assigning ids to in type consts.
+    ModuleId(ModuleId),
+}
+
+impl TypeOwnerId {
+    fn as_generic_def_id(self) -> Option<GenericDefId> {
+        Some(match self {
+            TypeOwnerId::FunctionId(x) => GenericDefId::FunctionId(x),
+            TypeOwnerId::ConstId(x) => GenericDefId::ConstId(x),
+            TypeOwnerId::AdtId(x) => GenericDefId::AdtId(x),
+            TypeOwnerId::TraitId(x) => GenericDefId::TraitId(x),
+            TypeOwnerId::TraitAliasId(x) => GenericDefId::TraitAliasId(x),
+            TypeOwnerId::TypeAliasId(x) => GenericDefId::TypeAliasId(x),
+            TypeOwnerId::ImplId(x) => GenericDefId::ImplId(x),
+            TypeOwnerId::EnumVariantId(x) => GenericDefId::EnumVariantId(x),
+            TypeOwnerId::InTypeConstId(_) | TypeOwnerId::ModuleId(_) | TypeOwnerId::StaticId(_) => {
+                return None
+            }
+        })
+    }
+}
+
+impl_from!(
+    FunctionId,
+    StaticId,
+    ConstId,
+    InTypeConstId,
+    AdtId,
+    TraitId,
+    TraitAliasId,
+    TypeAliasId,
+    ImplId,
+    EnumVariantId,
+    ModuleId
+    for TypeOwnerId
+);
+
+// Every `DefWithBodyId` is a type owner, since bodies can contain type (e.g. `{ let x: Type = _; }`)
+impl From<DefWithBodyId> for TypeOwnerId {
+    fn from(value: DefWithBodyId) -> Self {
+        match value {
+            DefWithBodyId::FunctionId(x) => x.into(),
+            DefWithBodyId::StaticId(x) => x.into(),
+            DefWithBodyId::ConstId(x) => x.into(),
+            DefWithBodyId::InTypeConstId(x) => x.into(),
+            DefWithBodyId::VariantId(x) => x.into(),
+        }
+    }
+}
+
+impl From<GenericDefId> for TypeOwnerId {
+    fn from(value: GenericDefId) -> Self {
+        match value {
+            GenericDefId::FunctionId(x) => x.into(),
+            GenericDefId::AdtId(x) => x.into(),
+            GenericDefId::TraitId(x) => x.into(),
+            GenericDefId::TraitAliasId(x) => x.into(),
+            GenericDefId::TypeAliasId(x) => x.into(),
+            GenericDefId::ImplId(x) => x.into(),
+            GenericDefId::EnumVariantId(x) => x.into(),
+            GenericDefId::ConstId(x) => x.into(),
+        }
+    }
+}
+
+// FIXME: This should not be a thing
+/// A thing that we want to store in interned ids, but we don't know its type in `hir-def`. This is
+/// currently only used in `InTypeConstId` for storing the type (which has type `Ty` defined in
+/// the `hir-ty` crate) of the constant in its id, which is a temporary hack so we may want
+/// to remove this after removing that.
+pub trait OpaqueInternableThing:
+    std::any::Any + std::fmt::Debug + Sync + Send + UnwindSafe + RefUnwindSafe
+{
+    fn as_any(&self) -> &dyn std::any::Any;
+    fn box_any(&self) -> Box<dyn std::any::Any>;
+    fn dyn_hash(&self, state: &mut dyn Hasher);
+    fn dyn_eq(&self, other: &dyn OpaqueInternableThing) -> bool;
+    fn dyn_clone(&self) -> Box<dyn OpaqueInternableThing>;
+}
+
+impl Hash for dyn OpaqueInternableThing {
+    fn hash<H: Hasher>(&self, state: &mut H) {
+        self.dyn_hash(state);
+    }
+}
+
+impl PartialEq for dyn OpaqueInternableThing {
+    fn eq(&self, other: &Self) -> bool {
+        self.dyn_eq(other)
+    }
+}
+
+impl Eq for dyn OpaqueInternableThing {}
+
+impl Clone for Box<dyn OpaqueInternableThing> {
+    fn clone(&self) -> Self {
+        self.dyn_clone()
+    }
+}
+
+// FIXME(const-generic-body): Use an stable id for in type consts.
+//
+// The current id uses `AstId<ast::ConstArg>` which will be changed by every change in the code. Ideally
+// we should use an id which is relative to the type owner, so that every change will only invalidate the
+// id if it happens inside of the type owner.
+//
+// The solution probably is to have some query on `TypeOwnerId` to traverse its constant children and store
+// their `AstId` in a list (vector or arena), and use the index of that list in the id here. That query probably
+// needs name resolution, and might go far and handles the whole path lowering or type lowering for a `TypeOwnerId`.
+//
+// Whatever path the solution takes, it should answer 3 questions at the same time:
+// * Is the id stable enough?
+// * How to find a constant id using an ast node / position in the source code? This is needed when we want to
+//   provide ide functionalities inside an in type const (which we currently don't support) e.g. go to definition
+//   for a local defined there. A complex id might have some trouble in this reverse mapping.
+// * How to find the return type of a constant using its id? We have this data when we are doing type lowering
+//   and the name of the struct that contains this constant is resolved, so a query that only traverses the
+//   type owner by its syntax tree might have a hard time here.
+
+/// A constant in a type as a substitution for const generics (like `Foo<{ 2 + 2 }>`) or as an array
+/// length (like `[u8; 2 + 2]`). These constants are body owner and are a variant of `DefWithBodyId`. These
+/// are not called `AnonymousConstId` to prevent confusion with [`ConstBlockId`].
+#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
+pub struct InTypeConstId(salsa::InternId);
+impl_intern!(InTypeConstId, InTypeConstLoc, intern_in_type_const, lookup_intern_in_type_const);
+
+#[derive(Debug, Hash, Eq, Clone)]
+pub struct InTypeConstLoc {
+    pub id: AstId<ast::ConstArg>,
+    /// The thing this const arg appears in
+    pub owner: TypeOwnerId,
+    pub thing: Box<dyn OpaqueInternableThing>,
+}
+
+impl PartialEq for InTypeConstLoc {
+    fn eq(&self, other: &Self) -> bool {
+        self.id == other.id && self.owner == other.owner && &*self.thing == &*other.thing
+    }
+}
+
+impl InTypeConstId {
+    pub fn source(&self, db: &dyn db::DefDatabase) -> ast::ConstArg {
+        let src = self.lookup(db).id;
+        let file_id = src.file_id;
+        let root = &db.parse_or_expand(file_id);
+        db.ast_id_map(file_id).get(src.value).to_node(root)
+    }
+}
 
 /// A constant, which might appears as a const item, an annonymous const block in expressions
 /// or patterns, or as a constant in types with const generics.
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub enum GeneralConstId {
     ConstId(ConstId),
-    AnonymousConstId(AnonymousConstId),
+    ConstBlockId(ConstBlockId),
+    InTypeConstId(InTypeConstId),
 }
 
-impl_from!(ConstId, AnonymousConstId for GeneralConstId);
+impl_from!(ConstId, ConstBlockId, InTypeConstId for GeneralConstId);
 
 impl GeneralConstId {
     pub fn generic_def(self, db: &dyn db::DefDatabase) -> Option<GenericDefId> {
         match self {
-            GeneralConstId::ConstId(x) => Some(x.into()),
-            GeneralConstId::AnonymousConstId(x) => {
-                let (parent, _) = db.lookup_intern_anonymous_const(x);
-                parent.as_generic_def_id()
-            }
+            GeneralConstId::ConstId(it) => Some(it.into()),
+            GeneralConstId::ConstBlockId(it) => it.lookup(db).parent.as_generic_def_id(),
+            GeneralConstId::InTypeConstId(it) => it.lookup(db).owner.as_generic_def_id(),
         }
     }
 
@@ -511,7 +719,8 @@ impl GeneralConstId {
                 .and_then(|x| x.as_str())
                 .unwrap_or("_")
                 .to_owned(),
-            GeneralConstId::AnonymousConstId(id) => format!("{{anonymous const {id:?}}}"),
+            GeneralConstId::ConstBlockId(id) => format!("{{anonymous const {id:?}}}"),
+            GeneralConstId::InTypeConstId(id) => format!("{{in type const {id:?}}}"),
         }
     }
 }
@@ -522,10 +731,11 @@ pub enum DefWithBodyId {
     FunctionId(FunctionId),
     StaticId(StaticId),
     ConstId(ConstId),
+    InTypeConstId(InTypeConstId),
     VariantId(EnumVariantId),
 }
 
-impl_from!(FunctionId, ConstId, StaticId for DefWithBodyId);
+impl_from!(FunctionId, ConstId, StaticId, InTypeConstId for DefWithBodyId);
 
 impl From<EnumVariantId> for DefWithBodyId {
     fn from(id: EnumVariantId) -> Self {
@@ -540,6 +750,9 @@ impl DefWithBodyId {
             DefWithBodyId::StaticId(_) => None,
             DefWithBodyId::ConstId(c) => Some(c.into()),
             DefWithBodyId::VariantId(c) => Some(c.into()),
+            // FIXME: stable rust doesn't allow generics in constants, but we should
+            // use `TypeOwnerId::as_generic_def_id` when it does.
+            DefWithBodyId::InTypeConstId(_) => None,
         }
     }
 }
@@ -729,29 +942,37 @@ impl HasModule for MacroId {
         match self {
             MacroId::MacroRulesId(it) => it.lookup(db).container,
             MacroId::Macro2Id(it) => it.lookup(db).container,
-            MacroId::ProcMacroId(it) => it.lookup(db).container,
+            MacroId::ProcMacroId(it) => it.lookup(db).container.into(),
         }
     }
 }
 
-impl HasModule for DefWithBodyId {
+impl HasModule for TypeOwnerId {
     fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
         match self {
-            DefWithBodyId::FunctionId(it) => it.lookup(db).module(db),
-            DefWithBodyId::StaticId(it) => it.lookup(db).module(db),
-            DefWithBodyId::ConstId(it) => it.lookup(db).module(db),
-            DefWithBodyId::VariantId(it) => it.parent.lookup(db).container,
+            TypeOwnerId::FunctionId(x) => x.lookup(db).module(db),
+            TypeOwnerId::StaticId(x) => x.lookup(db).module(db),
+            TypeOwnerId::ConstId(x) => x.lookup(db).module(db),
+            TypeOwnerId::InTypeConstId(x) => x.lookup(db).owner.module(db),
+            TypeOwnerId::AdtId(x) => x.module(db),
+            TypeOwnerId::TraitId(x) => x.lookup(db).container,
+            TypeOwnerId::TraitAliasId(x) => x.lookup(db).container,
+            TypeOwnerId::TypeAliasId(x) => x.lookup(db).module(db),
+            TypeOwnerId::ImplId(x) => x.lookup(db).container,
+            TypeOwnerId::EnumVariantId(x) => x.parent.lookup(db).container,
+            TypeOwnerId::ModuleId(x) => *x,
         }
     }
 }
 
-impl DefWithBodyId {
-    pub fn as_mod_item(self, db: &dyn db::DefDatabase) -> ModItem {
+impl HasModule for DefWithBodyId {
+    fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
         match self {
-            DefWithBodyId::FunctionId(it) => it.lookup(db).id.value.into(),
-            DefWithBodyId::StaticId(it) => it.lookup(db).id.value.into(),
-            DefWithBodyId::ConstId(it) => it.lookup(db).id.value.into(),
-            DefWithBodyId::VariantId(it) => it.parent.lookup(db).id.value.into(),
+            DefWithBodyId::FunctionId(it) => it.lookup(db).module(db),
+            DefWithBodyId::StaticId(it) => it.lookup(db).module(db),
+            DefWithBodyId::ConstId(it) => it.lookup(db).module(db),
+            DefWithBodyId::VariantId(it) => it.parent.lookup(db).container,
+            DefWithBodyId::InTypeConstId(it) => it.lookup(db).owner.module(db),
         }
     }
 }
@@ -865,7 +1086,7 @@ impl AsMacroCall for InFile<&ast::MacroCall> {
         let path = self.value.path().and_then(|path| path::ModPath::from_src(db, path, &h));
 
         let Some(path) = path else {
-            return Ok(ExpandResult::only_err(ExpandError::Other("malformed macro invocation".into())));
+            return Ok(ExpandResult::only_err(ExpandError::other("malformed macro invocation")));
         };
 
         macro_call_as_call_id_(
@@ -913,7 +1134,7 @@ fn macro_call_as_call_id_(
 
     let res = if let MacroDefKind::BuiltInEager(..) = def.kind {
         let macro_call = InFile::new(call.ast_id.file_id, call.ast_id.to_node(db));
-        expand_eager_macro(db, krate, macro_call, def, &resolver)?
+        expand_eager_macro_input(db, krate, macro_call, def, &resolver)?
     } else {
         ExpandResult {
             value: Some(def.as_lazy_macro(
@@ -1028,13 +1249,13 @@ fn attr_macro_as_call_id(
     def: MacroDefId,
 ) -> MacroCallId {
     let arg = match macro_attr.input.as_deref() {
-        Some(AttrInput::TokenTree(tt, map)) => (
+        Some(AttrInput::TokenTree(tt)) => (
             {
-                let mut tt = tt.clone();
+                let mut tt = tt.0.clone();
                 tt.delimiter = tt::Delimiter::UNSPECIFIED;
                 tt
             },
-            map.clone(),
+            tt.1.clone(),
         ),
         _ => (tt::Subtree::empty(), Default::default()),
     };
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs
index 80474bc154d..f41f9719043 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs
@@ -115,6 +115,66 @@ impl <A: core::clone::Clone, B: core::clone::Clone, > core::clone::Clone for Com
 }
 
 #[test]
+fn test_clone_expand_with_associated_types() {
+    check(
+        r#"
+//- minicore: derive, clone
+trait Trait {
+    type InWc;
+    type InFieldQualified;
+    type InFieldShorthand;
+    type InGenericArg;
+}
+trait Marker {}
+struct Vec<T>(T);
+
+#[derive(Clone)]
+struct Foo<T: Trait>
+where
+    <T as Trait>::InWc: Marker,
+{
+    qualified: <T as Trait>::InFieldQualified,
+    shorthand: T::InFieldShorthand,
+    generic: Vec<T::InGenericArg>,
+}
+"#,
+        expect![[r#"
+trait Trait {
+    type InWc;
+    type InFieldQualified;
+    type InFieldShorthand;
+    type InGenericArg;
+}
+trait Marker {}
+struct Vec<T>(T);
+
+#[derive(Clone)]
+struct Foo<T: Trait>
+where
+    <T as Trait>::InWc: Marker,
+{
+    qualified: <T as Trait>::InFieldQualified,
+    shorthand: T::InFieldShorthand,
+    generic: Vec<T::InGenericArg>,
+}
+
+impl <T: core::clone::Clone, > core::clone::Clone for Foo<T, > where T: Trait, T::InFieldShorthand: core::clone::Clone, T::InGenericArg: core::clone::Clone, {
+    fn clone(&self ) -> Self {
+        match self {
+            Foo {
+                qualified: qualified, shorthand: shorthand, generic: generic,
+            }
+            =>Foo {
+                qualified: qualified.clone(), shorthand: shorthand.clone(), generic: generic.clone(),
+            }
+            ,
+        }
+    }
+}"#]],
+    );
+}
+
+#[test]
 fn test_clone_expand_with_const_generics() {
     check(
         r#"
@@ -336,18 +396,18 @@ enum Command {
 }
 
 impl < > core::hash::Hash for Command< > where {
-    fn hash<H: core::hash::Hasher>(&self , state: &mut H) {
-        core::mem::discriminant(self ).hash(state);
+    fn hash<H: core::hash::Hasher>(&self , ra_expand_state: &mut H) {
+        core::mem::discriminant(self ).hash(ra_expand_state);
         match self {
             Command::Move {
                 x: x, y: y,
             }
             => {
-                x.hash(state);
-                y.hash(state);
+                x.hash(ra_expand_state);
+                y.hash(ra_expand_state);
             }
             , Command::Do(f0, )=> {
-                f0.hash(state);
+                f0.hash(ra_expand_state);
             }
             , Command::Jump=> {}
             ,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
index 977f300636f..07d9baa5897 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
@@ -79,7 +79,7 @@ fn main() { env!("TEST_ENV_VAR"); }
 #[rustc_builtin_macro]
 macro_rules! env {() => {}}
 
-fn main() { "__RA_UNIMPLEMENTED__"; }
+fn main() { "UNRESOLVED_ENV_VAR"; }
 "##]],
     );
 }
@@ -208,6 +208,44 @@ fn main() {
 }
 
 #[test]
+fn regression_15002() {
+    check(
+        r#"
+#[rustc_builtin_macro]
+macro_rules! format_args {
+    ($fmt:expr) => ({ /* compiler built-in */ });
+    ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
+}
+
+fn main() {
+    format_args!(x = 2);
+    format_args!(x =);
+    format_args!(x =, x = 2);
+    format_args!("{}", x =);
+    format_args!(=, "{}", x =);
+    format_args!(x = 2, "{}", 5);
+}
+"#,
+        expect![[r##"
+#[rustc_builtin_macro]
+macro_rules! format_args {
+    ($fmt:expr) => ({ /* compiler built-in */ });
+    ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
+}
+
+fn main() {
+    /* error: no rule matches input tokens */;
+    /* error: no rule matches input tokens */;
+    /* error: no rule matches input tokens */;
+    /* error: no rule matches input tokens */::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::Argument::new(&(), ::core::fmt::Display::fmt), ]);
+    /* error: no rule matches input tokens */;
+    ::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::Argument::new(&(5), ::core::fmt::Display::fmt), ]);
+}
+"##]],
+    );
+}
+
+#[test]
 fn test_format_args_expand_with_comma_exprs() {
     check(
         r#"
@@ -404,10 +442,6 @@ macro_rules! surprise {
     () => { "s" };
 }
 
-macro_rules! stuff {
-    ($string:expr) => { concat!($string) };
-}
-
 fn main() { concat!(surprise!()); }
 "##,
         expect![[r##"
@@ -418,10 +452,6 @@ macro_rules! surprise {
     () => { "s" };
 }
 
-macro_rules! stuff {
-    ($string:expr) => { concat!($string) };
-}
-
 fn main() { "s"; }
 "##]],
     );
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
index 9b520bc3030..0ab1bd8490c 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
@@ -77,8 +77,8 @@ use crate::{
     path::ModPath,
     per_ns::PerNs,
     visibility::Visibility,
-    AstId, BlockId, BlockLoc, FunctionId, LocalModuleId, Lookup, MacroExpander, MacroId, ModuleId,
-    ProcMacroId,
+    AstId, BlockId, BlockLoc, CrateRootModuleId, FunctionId, LocalModuleId, Lookup, MacroExpander,
+    MacroId, ModuleId, ProcMacroId,
 };
 
 /// Contains the results of (early) name resolution.
@@ -93,7 +93,10 @@ use crate::{
 #[derive(Debug, PartialEq, Eq)]
 pub struct DefMap {
     _c: Count<Self>,
+    /// When this is a block def map, this will hold the block id of the the block and module that
+    /// contains this block.
     block: Option<BlockInfo>,
+    /// The modules and their data declared in this crate.
     modules: Arena<ModuleData>,
     krate: CrateId,
     /// The prelude module for this crate. This either comes from an import
@@ -111,15 +114,18 @@ pub struct DefMap {
     /// attributes.
     derive_helpers_in_scope: FxHashMap<AstId<ast::Item>, Vec<(Name, MacroId, MacroCallId)>>,
 
+    /// The diagnostics that need to be emitted for this crate.
     diagnostics: Vec<DefDiagnostic>,
 
+    /// The crate data that is shared between a crate's def map and all its block def maps.
     data: Arc<DefMapCrateData>,
 }
 
 /// Data that belongs to a crate which is shared between a crate's def map and all its block def maps.
 #[derive(Clone, Debug, PartialEq, Eq)]
 struct DefMapCrateData {
-    extern_prelude: FxHashMap<Name, ModuleId>,
+    /// The extern prelude which contains all root modules of external crates that are in scope.
+    extern_prelude: FxHashMap<Name, CrateRootModuleId>,
 
     /// Side table for resolving derive helpers.
     exported_derives: FxHashMap<MacroDefId, Box<[Name]>>,
@@ -279,6 +285,7 @@ pub struct ModuleData {
 }
 
 impl DefMap {
+    /// The module id of a crate or block root.
     pub const ROOT: LocalModuleId = LocalModuleId::from_raw(la_arena::RawIdx::from_u32(0));
 
     pub(crate) fn crate_def_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<DefMap> {
@@ -419,11 +426,11 @@ impl DefMap {
     }
 
     pub(crate) fn extern_prelude(&self) -> impl Iterator<Item = (&Name, ModuleId)> + '_ {
-        self.data.extern_prelude.iter().map(|(name, def)| (name, *def))
+        self.data.extern_prelude.iter().map(|(name, &def)| (name, def.into()))
     }
 
     pub(crate) fn macro_use_prelude(&self) -> impl Iterator<Item = (&Name, MacroId)> + '_ {
-        self.macro_use_prelude.iter().map(|(name, def)| (name, *def))
+        self.macro_use_prelude.iter().map(|(name, &def)| (name, def))
     }
 
     pub fn module_id(&self, local_id: LocalModuleId) -> ModuleId {
@@ -431,8 +438,8 @@ impl DefMap {
         ModuleId { krate: self.krate, local_id, block }
     }
 
-    pub(crate) fn crate_root(&self) -> ModuleId {
-        ModuleId { krate: self.krate, block: None, local_id: DefMap::ROOT }
+    pub fn crate_root(&self) -> CrateRootModuleId {
+        CrateRootModuleId { krate: self.krate }
     }
 
     pub(crate) fn resolve_path(
@@ -476,7 +483,7 @@ impl DefMap {
     ///
     /// If `f` returns `Some(val)`, iteration is stopped and `Some(val)` is returned. If `f` returns
     /// `None`, iteration continues.
-    pub fn with_ancestor_maps<T>(
+    pub(crate) fn with_ancestor_maps<T>(
         &self,
         db: &dyn DefDatabase,
         local_mod: LocalModuleId,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
index 06542b4b1e9..62fb3c7882c 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
@@ -3,7 +3,7 @@
 //! `DefCollector::collect` contains the fixed-point iteration loop which
 //! resolves imports and expands macros.
 
-use std::{iter, mem};
+use std::{cmp::Ordering, iter, mem};
 
 use base_db::{CrateId, Dependency, Edition, FileId};
 use cfg::{CfgExpr, CfgOptions};
@@ -51,11 +51,11 @@ use crate::{
     per_ns::PerNs,
     tt,
     visibility::{RawVisibility, Visibility},
-    AdtId, AstId, AstIdWithPath, ConstLoc, EnumLoc, EnumVariantId, ExternBlockLoc, FunctionId,
-    FunctionLoc, ImplLoc, Intern, ItemContainerId, LocalModuleId, Macro2Id, Macro2Loc,
-    MacroExpander, MacroId, MacroRulesId, MacroRulesLoc, ModuleDefId, ModuleId, ProcMacroId,
-    ProcMacroLoc, StaticLoc, StructLoc, TraitAliasLoc, TraitLoc, TypeAliasLoc, UnionLoc,
-    UnresolvedMacro,
+    AdtId, AstId, AstIdWithPath, ConstLoc, CrateRootModuleId, EnumLoc, EnumVariantId,
+    ExternBlockLoc, FunctionId, FunctionLoc, ImplLoc, Intern, ItemContainerId, LocalModuleId,
+    Macro2Id, Macro2Loc, MacroExpander, MacroId, MacroRulesId, MacroRulesLoc, ModuleDefId,
+    ModuleId, ProcMacroId, ProcMacroLoc, StaticLoc, StructLoc, TraitAliasLoc, TraitLoc,
+    TypeAliasLoc, UnionLoc, UnresolvedMacro,
 };
 
 static GLOB_RECURSION_LIMIT: Limit = Limit::new(100);
@@ -274,8 +274,6 @@ impl DefCollector<'_> {
 
         let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id;
         let item_tree = self.db.file_item_tree(file_id.into());
-        let module_id = DefMap::ROOT;
-
         let attrs = item_tree.top_level_attrs(self.db, self.def_map.krate);
         let crate_data = Arc::get_mut(&mut self.def_map.data).unwrap();
 
@@ -285,10 +283,9 @@ impl DefCollector<'_> {
 
         for (name, dep) in &self.deps {
             if dep.is_prelude() {
-                crate_data.extern_prelude.insert(
-                    name.clone(),
-                    ModuleId { krate: dep.crate_id, block: None, local_id: DefMap::ROOT },
-                );
+                crate_data
+                    .extern_prelude
+                    .insert(name.clone(), CrateRootModuleId { krate: dep.crate_id });
             }
         }
 
@@ -374,7 +371,7 @@ impl DefCollector<'_> {
         ModCollector {
             def_collector: self,
             macro_depth: 0,
-            module_id,
+            module_id: DefMap::ROOT,
             tree_id: TreeId::new(file_id.into(), None),
             item_tree: &item_tree,
             mod_dir: ModDir::root(),
@@ -384,8 +381,6 @@ impl DefCollector<'_> {
 
     fn seed_with_inner(&mut self, tree_id: TreeId) {
         let item_tree = tree_id.item_tree(self.db);
-        let module_id = DefMap::ROOT;
-
         let is_cfg_enabled = item_tree
             .top_level_attrs(self.db, self.def_map.krate)
             .cfg()
@@ -394,7 +389,7 @@ impl DefCollector<'_> {
             ModCollector {
                 def_collector: self,
                 macro_depth: 0,
-                module_id,
+                module_id: DefMap::ROOT,
                 tree_id,
                 item_tree: &item_tree,
                 mod_dir: ModDir::root(),
@@ -604,8 +599,6 @@ impl DefCollector<'_> {
         if self.def_map.block.is_some() {
             return;
         }
-        let crate_root = self.def_map.module_id(DefMap::ROOT);
-
         let kind = def.kind.to_basedb_kind();
         let (expander, kind) =
             match self.proc_macros.as_ref().map(|it| it.iter().find(|(n, _)| n == &def.name)) {
@@ -614,7 +607,8 @@ impl DefCollector<'_> {
             };
 
         let proc_macro_id =
-            ProcMacroLoc { container: crate_root, id, expander, kind }.intern(self.db);
+            ProcMacroLoc { container: self.def_map.crate_root(), id, expander, kind }
+                .intern(self.db);
         self.define_proc_macro(def.name.clone(), proc_macro_id);
         let crate_data = Arc::get_mut(&mut self.def_map.data).unwrap();
         if let ProcMacroKind::CustomDerive { helpers } = def.kind {
@@ -831,16 +825,12 @@ impl DefCollector<'_> {
         }
     }
 
-    fn resolve_extern_crate(&self, name: &Name) -> Option<ModuleId> {
+    fn resolve_extern_crate(&self, name: &Name) -> Option<CrateRootModuleId> {
         if *name == name!(self) {
             cov_mark::hit!(extern_crate_self_as);
             Some(self.def_map.crate_root())
         } else {
-            self.deps.get(name).map(|dep| ModuleId {
-                krate: dep.crate_id,
-                block: None,
-                local_id: DefMap::ROOT,
-            })
+            self.deps.get(name).map(|dep| CrateRootModuleId { krate: dep.crate_id })
         }
     }
 
@@ -883,10 +873,12 @@ impl DefCollector<'_> {
                 {
                     if let (Some(ModuleDefId::ModuleId(def)), Some(name)) = (def.take_types(), name)
                     {
-                        Arc::get_mut(&mut self.def_map.data)
-                            .unwrap()
-                            .extern_prelude
-                            .insert(name.clone(), def);
+                        if let Ok(def) = def.try_into() {
+                            Arc::get_mut(&mut self.def_map.data)
+                                .unwrap()
+                                .extern_prelude
+                                .insert(name.clone(), def);
+                        }
                     }
                 }
 
@@ -1791,13 +1783,11 @@ impl ModCollector<'_, '_> {
 
         let target_crate =
             match self.def_collector.resolve_extern_crate(&self.item_tree[extern_crate].name) {
-                Some(m) => {
-                    if m == self.def_collector.def_map.module_id(self.module_id) {
-                        cov_mark::hit!(ignore_macro_use_extern_crate_self);
-                        return;
-                    }
-                    m.krate
+                Some(m) if m.krate == self.def_collector.def_map.krate => {
+                    cov_mark::hit!(ignore_macro_use_extern_crate_self);
+                    return;
                 }
+                Some(m) => m.krate,
                 None => return,
             };
 
@@ -1938,9 +1928,13 @@ impl ModCollector<'_, '_> {
         let modules = &mut def_map.modules;
         let res = modules.alloc(ModuleData::new(origin, vis));
         modules[res].parent = Some(self.module_id);
-        for (name, mac) in modules[self.module_id].scope.collect_legacy_macros() {
-            for &mac in &mac {
-                modules[res].scope.define_legacy_macro(name.clone(), mac);
+
+        if let Some((target, source)) = Self::borrow_modules(modules.as_mut(), res, self.module_id)
+        {
+            for (name, macs) in source.scope.legacy_macros() {
+                for &mac in macs {
+                    target.scope.define_legacy_macro(name.clone(), mac);
+                }
             }
         }
         modules[self.module_id].children.insert(name.clone(), res);
@@ -2236,14 +2230,40 @@ impl ModCollector<'_, '_> {
     }
 
     fn import_all_legacy_macros(&mut self, module_id: LocalModuleId) {
-        let macros = self.def_collector.def_map[module_id].scope.collect_legacy_macros();
-        for (name, macs) in macros {
+        let Some((source, target)) = Self::borrow_modules(self.def_collector.def_map.modules.as_mut(), module_id, self.module_id) else {
+            return
+        };
+
+        for (name, macs) in source.scope.legacy_macros() {
             macs.last().map(|&mac| {
-                self.def_collector.define_legacy_macro(self.module_id, name.clone(), mac)
+                target.scope.define_legacy_macro(name.clone(), mac);
             });
         }
     }
 
+    /// Mutably borrow two modules at once, retu
+    fn borrow_modules(
+        modules: &mut [ModuleData],
+        a: LocalModuleId,
+        b: LocalModuleId,
+    ) -> Option<(&mut ModuleData, &mut ModuleData)> {
+        let a = a.into_raw().into_u32() as usize;
+        let b = b.into_raw().into_u32() as usize;
+
+        let (a, b) = match a.cmp(&b) {
+            Ordering::Equal => return None,
+            Ordering::Less => {
+                let (prefix, b) = modules.split_at_mut(b);
+                (&mut prefix[a], &mut b[0])
+            }
+            Ordering::Greater => {
+                let (prefix, a) = modules.split_at_mut(a);
+                (&mut a[0], &mut prefix[b])
+            }
+        };
+        Some((a, b))
+    }
+
     fn is_cfg_enabled(&self, cfg: &CfgExpr) -> bool {
         self.def_collector.cfg_options.check(cfg) != Some(false)
     }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/path.rs b/src/tools/rust-analyzer/crates/hir-def/src/path.rs
index b9b80825497..ff4ae69546c 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/path.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/path.rs
@@ -9,7 +9,7 @@ use std::{
 use crate::{
     lang_item::LangItemTarget,
     lower::LowerCtx,
-    type_ref::{ConstRefOrPath, LifetimeRef, TypeBound, TypeRef},
+    type_ref::{ConstRef, LifetimeRef, TypeBound, TypeRef},
 };
 use hir_expand::name::Name;
 use intern::Interned;
@@ -90,7 +90,7 @@ pub struct AssociatedTypeBinding {
 pub enum GenericArg {
     Type(TypeRef),
     Lifetime(LifetimeRef),
-    Const(ConstRefOrPath),
+    Const(ConstRef),
 }
 
 impl Path {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs
index 26d2706175c..1cb17ff0d26 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs
@@ -2,7 +2,7 @@
 
 use std::iter;
 
-use crate::{lower::LowerCtx, type_ref::ConstRefOrPath};
+use crate::{lower::LowerCtx, type_ref::ConstRef};
 
 use either::Either;
 use hir_expand::name::{name, AsName};
@@ -217,7 +217,7 @@ pub(super) fn lower_generic_args(
                 }
             }
             ast::GenericArg::ConstArg(arg) => {
-                let arg = ConstRefOrPath::from_expr_opt(arg.expr());
+                let arg = ConstRef::from_const_arg(lower_ctx, Some(arg));
                 args.push(GenericArg::Const(arg))
             }
         }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
index 06f5b2526a4..0d6f55411c1 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
@@ -21,11 +21,11 @@ use crate::{
     path::{ModPath, Path, PathKind},
     per_ns::PerNs,
     visibility::{RawVisibility, Visibility},
-    AdtId, AssocItemId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExternBlockId,
-    FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, ItemContainerId, LifetimeParamId,
-    LocalModuleId, Lookup, Macro2Id, MacroId, MacroRulesId, ModuleDefId, ModuleId, ProcMacroId,
-    StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId,
-    VariantId,
+    AdtId, AssocItemId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId, EnumId,
+    EnumVariantId, ExternBlockId, FunctionId, GenericDefId, GenericParamId, HasModule, ImplId,
+    ItemContainerId, LifetimeParamId, LocalModuleId, Lookup, Macro2Id, MacroId, MacroRulesId,
+    ModuleDefId, ModuleId, ProcMacroId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId,
+    TypeOrConstParamId, TypeOwnerId, TypeParamId, VariantId,
 };
 
 #[derive(Debug, Clone)]
@@ -946,6 +946,15 @@ impl HasResolver for ModuleId {
     }
 }
 
+impl HasResolver for CrateRootModuleId {
+    fn resolver(self, db: &dyn DefDatabase) -> Resolver {
+        Resolver {
+            scopes: vec![],
+            module_scope: ModuleItemMap { def_map: self.def_map(db), module_id: DefMap::ROOT },
+        }
+    }
+}
+
 impl HasResolver for TraitId {
     fn resolver(self, db: &dyn DefDatabase) -> Resolver {
         self.lookup(db).container.resolver(db).push_generic_params_scope(db, self.into())
@@ -1009,6 +1018,24 @@ impl HasResolver for ExternBlockId {
     }
 }
 
+impl HasResolver for TypeOwnerId {
+    fn resolver(self, db: &dyn DefDatabase) -> Resolver {
+        match self {
+            TypeOwnerId::FunctionId(x) => x.resolver(db),
+            TypeOwnerId::StaticId(x) => x.resolver(db),
+            TypeOwnerId::ConstId(x) => x.resolver(db),
+            TypeOwnerId::InTypeConstId(x) => x.lookup(db).owner.resolver(db),
+            TypeOwnerId::AdtId(x) => x.resolver(db),
+            TypeOwnerId::TraitId(x) => x.resolver(db),
+            TypeOwnerId::TraitAliasId(x) => x.resolver(db),
+            TypeOwnerId::TypeAliasId(x) => x.resolver(db),
+            TypeOwnerId::ImplId(x) => x.resolver(db),
+            TypeOwnerId::EnumVariantId(x) => x.resolver(db),
+            TypeOwnerId::ModuleId(x) => x.resolver(db),
+        }
+    }
+}
+
 impl HasResolver for DefWithBodyId {
     fn resolver(self, db: &dyn DefDatabase) -> Resolver {
         match self {
@@ -1016,6 +1043,7 @@ impl HasResolver for DefWithBodyId {
             DefWithBodyId::FunctionId(f) => f.resolver(db),
             DefWithBodyId::StaticId(s) => s.resolver(db),
             DefWithBodyId::VariantId(v) => v.parent.resolver(db),
+            DefWithBodyId::InTypeConstId(c) => c.lookup(db).owner.resolver(db),
         }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/ast_id_map.rs b/src/tools/rust-analyzer/crates/hir-expand/src/ast_id_map.rs
index 400442de94b..c2b0d5985e3 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/ast_id_map.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/ast_id_map.rs
@@ -20,7 +20,7 @@ use syntax::{ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr};
 /// `AstId` points to an AST node in a specific file.
 pub struct FileAstId<N: AstNode> {
     raw: ErasedFileAstId,
-    _ty: PhantomData<fn() -> N>,
+    covariant: PhantomData<fn() -> N>,
 }
 
 impl<N: AstNode> Clone for FileAstId<N> {
@@ -54,7 +54,7 @@ impl<N: AstNode> FileAstId<N> {
     where
         N: Into<M>,
     {
-        FileAstId { raw: self.raw, _ty: PhantomData }
+        FileAstId { raw: self.raw, covariant: PhantomData }
     }
 }
 
@@ -98,6 +98,7 @@ impl AstIdMap {
                 || ast::Variant::can_cast(kind)
                 || ast::RecordField::can_cast(kind)
                 || ast::TupleField::can_cast(kind)
+                || ast::ConstArg::can_cast(kind)
             {
                 res.alloc(&it);
                 true
@@ -121,7 +122,7 @@ impl AstIdMap {
 
     pub fn ast_id<N: AstNode>(&self, item: &N) -> FileAstId<N> {
         let raw = self.erased_ast_id(item.syntax());
-        FileAstId { raw, _ty: PhantomData }
+        FileAstId { raw, covariant: PhantomData }
     }
 
     pub fn get<N: AstNode>(&self, id: FileAstId<N>) -> AstPtr<N> {
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs
index 0c369a18bb9..4c918e55b92 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs
@@ -192,14 +192,14 @@ pub enum AttrInput {
     /// `#[attr = "string"]`
     Literal(SmolStr),
     /// `#[attr(subtree)]`
-    TokenTree(tt::Subtree, mbe::TokenMap),
+    TokenTree(Box<(tt::Subtree, mbe::TokenMap)>),
 }
 
 impl fmt::Display for AttrInput {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
             AttrInput::Literal(lit) => write!(f, " = \"{}\"", lit.escape_debug()),
-            AttrInput::TokenTree(subtree, _) => subtree.fmt(f),
+            AttrInput::TokenTree(tt) => tt.0.fmt(f),
         }
     }
 }
@@ -220,7 +220,7 @@ impl Attr {
             Some(Interned::new(AttrInput::Literal(value)))
         } else if let Some(tt) = ast.token_tree() {
             let (tree, map) = syntax_node_to_token_tree(tt.syntax());
-            Some(Interned::new(AttrInput::TokenTree(tree, map)))
+            Some(Interned::new(AttrInput::TokenTree(Box::new((tree, map)))))
         } else {
             None
         };
@@ -256,7 +256,7 @@ impl Attr {
     /// #[path(ident)]
     pub fn single_ident_value(&self) -> Option<&tt::Ident> {
         match self.input.as_deref()? {
-            AttrInput::TokenTree(subtree, _) => match &*subtree.token_trees {
+            AttrInput::TokenTree(tt) => match &*tt.0.token_trees {
                 [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] => Some(ident),
                 _ => None,
             },
@@ -267,7 +267,7 @@ impl Attr {
     /// #[path TokenTree]
     pub fn token_tree_value(&self) -> Option<&Subtree> {
         match self.input.as_deref()? {
-            AttrInput::TokenTree(subtree, _) => Some(subtree),
+            AttrInput::TokenTree(tt) => Some(&tt.0),
             _ => None,
         }
     }
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs
index 54706943ac4..3d1e272b900 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs
@@ -4,17 +4,16 @@ use ::tt::Ident;
 use base_db::{CrateOrigin, LangCrateOrigin};
 use itertools::izip;
 use mbe::TokenMap;
-use std::collections::HashSet;
+use rustc_hash::FxHashSet;
 use stdx::never;
 use tracing::debug;
 
-use crate::tt::{self, TokenId};
-use syntax::{
-    ast::{
-        self, AstNode, FieldList, HasAttrs, HasGenericParams, HasModuleItem, HasName,
-        HasTypeBounds, PathType,
-    },
-    match_ast,
+use crate::{
+    name::{AsName, Name},
+    tt::{self, TokenId},
+};
+use syntax::ast::{
+    self, AstNode, FieldList, HasAttrs, HasGenericParams, HasModuleItem, HasName, HasTypeBounds,
 };
 
 use crate::{db::ExpandDatabase, name, quote, ExpandError, ExpandResult, MacroCallId};
@@ -195,39 +194,52 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
     let (parsed, token_map) = mbe::token_tree_to_syntax_node(tt, mbe::TopEntryPoint::MacroItems);
     let macro_items = ast::MacroItems::cast(parsed.syntax_node()).ok_or_else(|| {
         debug!("derive node didn't parse");
-        ExpandError::Other("invalid item definition".into())
+        ExpandError::other("invalid item definition")
     })?;
     let item = macro_items.items().next().ok_or_else(|| {
         debug!("no module item parsed");
-        ExpandError::Other("no item found".into())
+        ExpandError::other("no item found")
     })?;
-    let node = item.syntax();
-    let (name, params, shape) = match_ast! {
-        match node {
-            ast::Struct(it) => (it.name(), it.generic_param_list(), AdtShape::Struct(VariantShape::from(it.field_list(), &token_map)?)),
-            ast::Enum(it) => {
-                let default_variant = it.variant_list().into_iter().flat_map(|x| x.variants()).position(|x| x.attrs().any(|x| x.simple_name() == Some("default".into())));
-                (
-                    it.name(),
-                    it.generic_param_list(),
-                    AdtShape::Enum {
-                        default_variant,
-                        variants: it.variant_list()
-                            .into_iter()
-                            .flat_map(|x| x.variants())
-                            .map(|x| Ok((name_to_token(&token_map,x.name())?, VariantShape::from(x.field_list(), &token_map)?))).collect::<Result<_, ExpandError>>()?
-                    }
-                )
-            },
-            ast::Union(it) => (it.name(), it.generic_param_list(), AdtShape::Union),
-            _ => {
-                debug!("unexpected node is {:?}", node);
-                return Err(ExpandError::Other("expected struct, enum or union".into()))
-            },
+    let adt = ast::Adt::cast(item.syntax().clone()).ok_or_else(|| {
+        debug!("expected adt, found: {:?}", item);
+        ExpandError::other("expected struct, enum or union")
+    })?;
+    let (name, generic_param_list, shape) = match &adt {
+        ast::Adt::Struct(it) => (
+            it.name(),
+            it.generic_param_list(),
+            AdtShape::Struct(VariantShape::from(it.field_list(), &token_map)?),
+        ),
+        ast::Adt::Enum(it) => {
+            let default_variant = it
+                .variant_list()
+                .into_iter()
+                .flat_map(|x| x.variants())
+                .position(|x| x.attrs().any(|x| x.simple_name() == Some("default".into())));
+            (
+                it.name(),
+                it.generic_param_list(),
+                AdtShape::Enum {
+                    default_variant,
+                    variants: it
+                        .variant_list()
+                        .into_iter()
+                        .flat_map(|x| x.variants())
+                        .map(|x| {
+                            Ok((
+                                name_to_token(&token_map, x.name())?,
+                                VariantShape::from(x.field_list(), &token_map)?,
+                            ))
+                        })
+                        .collect::<Result<_, ExpandError>>()?,
+                },
+            )
         }
+        ast::Adt::Union(it) => (it.name(), it.generic_param_list(), AdtShape::Union),
     };
-    let mut param_type_set: HashSet<String> = HashSet::new();
-    let param_types = params
+
+    let mut param_type_set: FxHashSet<Name> = FxHashSet::default();
+    let param_types = generic_param_list
         .into_iter()
         .flat_map(|param_list| param_list.type_or_const_params())
         .map(|param| {
@@ -235,7 +247,7 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
                 let this = param.name();
                 match this {
                     Some(x) => {
-                        param_type_set.insert(x.to_string());
+                        param_type_set.insert(x.as_name());
                         mbe::syntax_node_to_token_tree(x.syntax()).0
                     }
                     None => tt::Subtree::empty(),
@@ -259,37 +271,33 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
             (name, ty, bounds)
         })
         .collect();
-    let is_associated_type = |p: &PathType| {
-        if let Some(p) = p.path() {
-            if let Some(parent) = p.qualifier() {
-                if let Some(x) = parent.segment() {
-                    if let Some(x) = x.path_type() {
-                        if let Some(x) = x.path() {
-                            if let Some(pname) = x.as_single_name_ref() {
-                                if param_type_set.contains(&pname.to_string()) {
-                                    // <T as Trait>::Assoc
-                                    return true;
-                                }
-                            }
-                        }
-                    }
-                }
-                if let Some(pname) = parent.as_single_name_ref() {
-                    if param_type_set.contains(&pname.to_string()) {
-                        // T::Assoc
-                        return true;
-                    }
-                }
-            }
-        }
-        false
+
+    // For a generic parameter `T`, when shorthand associated type `T::Assoc` appears in field
+    // types (of any variant for enums), we generate trait bound for it. It sounds reasonable to
+    // also generate trait bound for qualified associated type `<T as Trait>::Assoc`, but rustc
+    // does not do that for some unknown reason.
+    //
+    // See the analogous function in rustc [find_type_parameters()] and rust-lang/rust#50730.
+    // [find_type_parameters()]: https://github.com/rust-lang/rust/blob/1.70.0/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs#L378
+
+    // It's cumbersome to deal with the distinct structures of ADTs, so let's just get untyped
+    // `SyntaxNode` that contains fields and look for descendant `ast::PathType`s. Of note is that
+    // we should not inspect `ast::PathType`s in parameter bounds and where clauses.
+    let field_list = match adt {
+        ast::Adt::Enum(it) => it.variant_list().map(|list| list.syntax().clone()),
+        ast::Adt::Struct(it) => it.field_list().map(|list| list.syntax().clone()),
+        ast::Adt::Union(it) => it.record_field_list().map(|list| list.syntax().clone()),
     };
-    let associated_types = node
-        .descendants()
-        .filter_map(PathType::cast)
-        .filter(is_associated_type)
+    let associated_types = field_list
+        .into_iter()
+        .flat_map(|it| it.descendants())
+        .filter_map(ast::PathType::cast)
+        .filter_map(|p| {
+            let name = p.path()?.qualifier()?.as_single_name_ref()?.as_name();
+            param_type_set.contains(&name).then_some(p)
+        })
         .map(|x| mbe::syntax_node_to_token_tree(x.syntax()).0)
-        .collect::<Vec<_>>();
+        .collect();
     let name_token = name_to_token(&token_map, name)?;
     Ok(BasicAdtInfo { name: name_token, shape, param_types, associated_types })
 }
@@ -297,7 +305,7 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
 fn name_to_token(token_map: &TokenMap, name: Option<ast::Name>) -> Result<tt::Ident, ExpandError> {
     let name = name.ok_or_else(|| {
         debug!("parsed item has no name");
-        ExpandError::Other("missing name".into())
+        ExpandError::other("missing name")
     })?;
     let name_token_id =
         token_map.token_by_range(name.syntax().text_range()).unwrap_or_else(TokenId::unspecified);
@@ -334,18 +342,18 @@ fn name_to_token(token_map: &TokenMap, name: Option<ast::Name>) -> Result<tt::Id
 /// }
 /// ```
 ///
-/// where B1, ..., BN are the bounds given by `bounds_paths`.'. Z is a phantom type, and
+/// where B1, ..., BN are the bounds given by `bounds_paths`. Z is a phantom type, and
 /// therefore does not get bound by the derived trait.
 fn expand_simple_derive(
     tt: &tt::Subtree,
     trait_path: tt::Subtree,
-    trait_body: impl FnOnce(&BasicAdtInfo) -> tt::Subtree,
+    make_trait_body: impl FnOnce(&BasicAdtInfo) -> tt::Subtree,
 ) -> ExpandResult<tt::Subtree> {
     let info = match parse_adt(tt) {
         Ok(info) => info,
         Err(e) => return ExpandResult::new(tt::Subtree::empty(), e),
     };
-    let trait_body = trait_body(&info);
+    let trait_body = make_trait_body(&info);
     let mut where_block = vec![];
     let (params, args): (Vec<_>, Vec<_>) = info
         .param_types
@@ -605,7 +613,7 @@ fn hash_expand(
                 span: tt::TokenId::unspecified(),
             };
             return quote! {
-                fn hash<H: #krate::hash::Hasher>(&self, state: &mut H) {
+                fn hash<H: #krate::hash::Hasher>(&self, ra_expand_state: &mut H) {
                     match #star self {}
                 }
             };
@@ -613,7 +621,7 @@ fn hash_expand(
         let arms = adt.shape.as_pattern(&adt.name).into_iter().zip(adt.shape.field_names()).map(
             |(pat, names)| {
                 let expr = {
-                    let it = names.iter().map(|x| quote! { #x . hash(state); });
+                    let it = names.iter().map(|x| quote! { #x . hash(ra_expand_state); });
                     quote! { {
                         ##it
                     } }
@@ -625,8 +633,8 @@ fn hash_expand(
             },
         );
         quote! {
-            fn hash<H: #krate::hash::Hasher>(&self, state: &mut H) {
-                #krate::mem::discriminant(self).hash(state);
+            fn hash<H: #krate::hash::Hasher>(&self, ra_expand_state: &mut H) {
+                #krate::mem::discriminant(self).hash(ra_expand_state);
                 match self {
                     ##arms
                 }
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs
index c7643bd0a18..a9f0c154b02 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs
@@ -14,7 +14,8 @@ use syntax::{
 };
 
 use crate::{
-    db::ExpandDatabase, name, quote, tt, ExpandError, ExpandResult, MacroCallId, MacroCallLoc,
+    db::ExpandDatabase, name, quote, tt, EagerCallInfo, ExpandError, ExpandResult, MacroCallId,
+    MacroCallLoc,
 };
 
 macro_rules! register_builtin {
@@ -49,7 +50,7 @@ macro_rules! register_builtin {
                 db: &dyn ExpandDatabase,
                 arg_id: MacroCallId,
                 tt: &tt::Subtree,
-            ) -> ExpandResult<ExpandedEager> {
+            ) -> ExpandResult<tt::Subtree> {
                 let expander = match *self {
                     $( EagerExpander::$e_kind => $e_expand, )*
                 };
@@ -67,16 +68,9 @@ macro_rules! register_builtin {
     };
 }
 
-#[derive(Debug)]
-pub struct ExpandedEager {
-    pub(crate) subtree: tt::Subtree,
-    /// The included file ID of the include macro.
-    pub(crate) included_file: Option<(FileId, TokenMap)>,
-}
-
-impl ExpandedEager {
-    fn new(subtree: tt::Subtree) -> Self {
-        ExpandedEager { subtree, included_file: None }
+impl EagerExpander {
+    pub fn is_include(&self) -> bool {
+        matches!(self, EagerExpander::Include)
     }
 }
 
@@ -237,18 +231,16 @@ fn format_args_expand(
     db: &dyn ExpandDatabase,
     id: MacroCallId,
     tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
+) -> ExpandResult<tt::Subtree> {
     format_args_expand_general(db, id, tt, "")
-        .map(|x| ExpandedEager { subtree: x, included_file: None })
 }
 
 fn format_args_nl_expand(
     db: &dyn ExpandDatabase,
     id: MacroCallId,
     tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
+) -> ExpandResult<tt::Subtree> {
     format_args_expand_general(db, id, tt, "\\n")
-        .map(|x| ExpandedEager { subtree: x, included_file: None })
 }
 
 fn format_args_expand_general(
@@ -262,9 +254,6 @@ fn format_args_expand_general(
     let expand_error =
         ExpandResult::new(tt::Subtree::empty(), mbe::ExpandError::NoMatchingRule.into());
 
-    if args.is_empty() {
-        return expand_error;
-    }
     let mut key_args = FxHashMap::default();
     let mut args = args.into_iter().filter_map(|mut arg| {
         // Remove `key =`.
@@ -281,7 +270,9 @@ fn format_args_expand_general(
         Some(arg)
     }).collect::<Vec<_>>().into_iter();
     // ^^^^^^^ we need this collect, to enforce the side effect of the filter_map closure (building the `key_args`)
-    let format_subtree = args.next().unwrap();
+    let Some(format_subtree) = args.next() else {
+        return expand_error;
+    };
     let format_string = (|| {
         let token_tree = format_subtree.token_trees.get(0)?;
         match token_tree {
@@ -510,23 +501,23 @@ fn compile_error_expand(
     _db: &dyn ExpandDatabase,
     _id: MacroCallId,
     tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
+) -> ExpandResult<tt::Subtree> {
     let err = match &*tt.token_trees {
         [tt::TokenTree::Leaf(tt::Leaf::Literal(it))] => match unquote_str(it) {
-            Some(unquoted) => ExpandError::Other(unquoted.into()),
-            None => ExpandError::Other("`compile_error!` argument must be a string".into()),
+            Some(unquoted) => ExpandError::other(unquoted),
+            None => ExpandError::other("`compile_error!` argument must be a string"),
         },
-        _ => ExpandError::Other("`compile_error!` argument must be a string".into()),
+        _ => ExpandError::other("`compile_error!` argument must be a string"),
     };
 
-    ExpandResult { value: ExpandedEager::new(quote! {}), err: Some(err) }
+    ExpandResult { value: quote! {}, err: Some(err) }
 }
 
 fn concat_expand(
     _db: &dyn ExpandDatabase,
     _arg_id: MacroCallId,
     tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
+) -> ExpandResult<tt::Subtree> {
     let mut err = None;
     let mut text = String::new();
     for (i, mut t) in tt.token_trees.iter().enumerate() {
@@ -565,14 +556,14 @@ fn concat_expand(
             }
         }
     }
-    ExpandResult { value: ExpandedEager::new(quote!(#text)), err }
+    ExpandResult { value: quote!(#text), err }
 }
 
 fn concat_bytes_expand(
     _db: &dyn ExpandDatabase,
     _arg_id: MacroCallId,
     tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
+) -> ExpandResult<tt::Subtree> {
     let mut bytes = Vec::new();
     let mut err = None;
     for (i, t) in tt.token_trees.iter().enumerate() {
@@ -605,7 +596,7 @@ fn concat_bytes_expand(
         }
     }
     let ident = tt::Ident { text: bytes.join(", ").into(), span: tt::TokenId::unspecified() };
-    ExpandResult { value: ExpandedEager::new(quote!([#ident])), err }
+    ExpandResult { value: quote!([#ident]), err }
 }
 
 fn concat_bytes_expand_subtree(
@@ -638,7 +629,7 @@ fn concat_idents_expand(
     _db: &dyn ExpandDatabase,
     _arg_id: MacroCallId,
     tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
+) -> ExpandResult<tt::Subtree> {
     let mut err = None;
     let mut ident = String::new();
     for (i, t) in tt.token_trees.iter().enumerate() {
@@ -653,7 +644,7 @@ fn concat_idents_expand(
         }
     }
     let ident = tt::Ident { text: ident.into(), span: tt::TokenId::unspecified() };
-    ExpandResult { value: ExpandedEager::new(quote!(#ident)), err }
+    ExpandResult { value: quote!(#ident), err }
 }
 
 fn relative_file(
@@ -666,10 +657,10 @@ fn relative_file(
     let path = AnchoredPath { anchor: call_site, path: path_str };
     let res = db
         .resolve_path(path)
-        .ok_or_else(|| ExpandError::Other(format!("failed to load file `{path_str}`").into()))?;
+        .ok_or_else(|| ExpandError::other(format!("failed to load file `{path_str}`")))?;
     // Prevent include itself
     if res == call_site && !allow_recursion {
-        Err(ExpandError::Other(format!("recursive inclusion of `{path_str}`").into()))
+        Err(ExpandError::other(format!("recursive inclusion of `{path_str}`")))
     } else {
         Ok(res)
     }
@@ -688,38 +679,37 @@ fn parse_string(tt: &tt::Subtree) -> Result<String, ExpandError> {
 fn include_expand(
     db: &dyn ExpandDatabase,
     arg_id: MacroCallId,
-    tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
-    let res = (|| {
-        let path = parse_string(tt)?;
-        let file_id = relative_file(db, arg_id, &path, false)?;
-
-        let (subtree, map) =
-            parse_to_token_tree(&db.file_text(file_id)).ok_or(mbe::ExpandError::ConversionError)?;
-        Ok((subtree, map, file_id))
-    })();
-
-    match res {
-        Ok((subtree, map, file_id)) => {
-            ExpandResult::ok(ExpandedEager { subtree, included_file: Some((file_id, map)) })
-        }
-        Err(e) => ExpandResult::new(
-            ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
-            e,
-        ),
+    _tt: &tt::Subtree,
+) -> ExpandResult<tt::Subtree> {
+    match db.include_expand(arg_id) {
+        Ok((res, _)) => ExpandResult::ok(res.0.clone()),
+        Err(e) => ExpandResult::new(tt::Subtree::empty(), e),
     }
 }
 
+pub(crate) fn include_arg_to_tt(
+    db: &dyn ExpandDatabase,
+    arg_id: MacroCallId,
+) -> Result<(triomphe::Arc<(::tt::Subtree<::tt::TokenId>, TokenMap)>, FileId), ExpandError> {
+    let loc = db.lookup_intern_macro_call(arg_id);
+    let Some(EagerCallInfo {arg, arg_id: Some(arg_id), .. }) = loc.eager.as_deref() else {
+        panic!("include_arg_to_tt called on non include macro call: {:?}", &loc.eager);
+    };
+    let path = parse_string(&arg.0)?;
+    let file_id = relative_file(db, *arg_id, &path, false)?;
+
+    let (subtree, map) =
+        parse_to_token_tree(&db.file_text(file_id)).ok_or(mbe::ExpandError::ConversionError)?;
+    Ok((triomphe::Arc::new((subtree, map)), file_id))
+}
+
 fn include_bytes_expand(
     _db: &dyn ExpandDatabase,
     _arg_id: MacroCallId,
     tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
+) -> ExpandResult<tt::Subtree> {
     if let Err(e) = parse_string(tt) {
-        return ExpandResult::new(
-            ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
-            e,
-        );
+        return ExpandResult::new(tt::Subtree::empty(), e);
     }
 
     // FIXME: actually read the file here if the user asked for macro expansion
@@ -730,22 +720,17 @@ fn include_bytes_expand(
             span: tt::TokenId::unspecified(),
         }))],
     };
-    ExpandResult::ok(ExpandedEager::new(res))
+    ExpandResult::ok(res)
 }
 
 fn include_str_expand(
     db: &dyn ExpandDatabase,
     arg_id: MacroCallId,
     tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
+) -> ExpandResult<tt::Subtree> {
     let path = match parse_string(tt) {
         Ok(it) => it,
-        Err(e) => {
-            return ExpandResult::new(
-                ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
-                e,
-            )
-        }
+        Err(e) => return ExpandResult::new(tt::Subtree::empty(), e),
     };
 
     // FIXME: we're not able to read excluded files (which is most of them because
@@ -755,14 +740,14 @@ fn include_str_expand(
     let file_id = match relative_file(db, arg_id, &path, true) {
         Ok(file_id) => file_id,
         Err(_) => {
-            return ExpandResult::ok(ExpandedEager::new(quote!("")));
+            return ExpandResult::ok(quote!(""));
         }
     };
 
     let text = db.file_text(file_id);
     let text = &*text;
 
-    ExpandResult::ok(ExpandedEager::new(quote!(#text)))
+    ExpandResult::ok(quote!(#text))
 }
 
 fn get_env_inner(db: &dyn ExpandDatabase, arg_id: MacroCallId, key: &str) -> Option<String> {
@@ -774,15 +759,10 @@ fn env_expand(
     db: &dyn ExpandDatabase,
     arg_id: MacroCallId,
     tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
+) -> ExpandResult<tt::Subtree> {
     let key = match parse_string(tt) {
         Ok(it) => it,
-        Err(e) => {
-            return ExpandResult::new(
-                ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
-                e,
-            )
-        }
+        Err(e) => return ExpandResult::new(tt::Subtree::empty(), e),
     };
 
     let mut err = None;
@@ -790,35 +770,28 @@ fn env_expand(
         // The only variable rust-analyzer ever sets is `OUT_DIR`, so only diagnose that to avoid
         // unnecessary diagnostics for eg. `CARGO_PKG_NAME`.
         if key == "OUT_DIR" {
-            err = Some(ExpandError::Other(
-                r#"`OUT_DIR` not set, enable "build scripts" to fix"#.into(),
-            ));
+            err = Some(ExpandError::other(r#"`OUT_DIR` not set, enable "build scripts" to fix"#));
         }
 
         // If the variable is unset, still return a dummy string to help type inference along.
         // We cannot use an empty string here, because for
         // `include!(concat!(env!("OUT_DIR"), "/foo.rs"))` will become
         // `include!("foo.rs"), which might go to infinite loop
-        "__RA_UNIMPLEMENTED__".to_string()
+        "UNRESOLVED_ENV_VAR".to_string()
     });
     let expanded = quote! { #s };
 
-    ExpandResult { value: ExpandedEager::new(expanded), err }
+    ExpandResult { value: expanded, err }
 }
 
 fn option_env_expand(
     db: &dyn ExpandDatabase,
     arg_id: MacroCallId,
     tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
+) -> ExpandResult<tt::Subtree> {
     let key = match parse_string(tt) {
         Ok(it) => it,
-        Err(e) => {
-            return ExpandResult::new(
-                ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
-                e,
-            )
-        }
+        Err(e) => return ExpandResult::new(tt::Subtree::empty(), e),
     };
     // FIXME: Use `DOLLAR_CRATE` when that works in eager macros.
     let expanded = match get_env_inner(db, arg_id, &key) {
@@ -826,5 +799,5 @@ fn option_env_expand(
         Some(s) => quote! { ::core::option::Option::Some(#s) },
     };
 
-    ExpandResult::ok(ExpandedEager::new(expanded))
+    ExpandResult::ok(expanded)
 }
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
index 965dfa824d8..78b2db7306b 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
@@ -14,9 +14,9 @@ use triomphe::Arc;
 use crate::{
     ast_id_map::AstIdMap, builtin_attr_macro::pseudo_derive_attr_expansion,
     builtin_fn_macro::EagerExpander, fixup, hygiene::HygieneFrame, tt, BuiltinAttrExpander,
-    BuiltinDeriveExpander, BuiltinFnLikeExpander, ExpandError, ExpandResult, ExpandTo, HirFileId,
-    HirFileIdRepr, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind, MacroFile,
-    ProcMacroExpander,
+    BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallInfo, ExpandError, ExpandResult,
+    ExpandTo, HirFileId, HirFileIdRepr, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId,
+    MacroDefKind, MacroFile, ProcMacroExpander,
 };
 
 /// Total limit on the number of tokens produced by any macro invocation.
@@ -53,9 +53,7 @@ impl TokenExpander {
         match self {
             TokenExpander::DeclarativeMacro { mac, .. } => mac.expand(tt).map_err(Into::into),
             TokenExpander::Builtin(it) => it.expand(db, id, tt).map_err(Into::into),
-            TokenExpander::BuiltinEager(it) => {
-                it.expand(db, id, tt).map_err(Into::into).map(|res| res.subtree)
-            }
+            TokenExpander::BuiltinEager(it) => it.expand(db, id, tt).map_err(Into::into),
             TokenExpander::BuiltinAttr(it) => it.expand(db, id, tt),
             TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt),
             TokenExpander::ProcMacro(_) => {
@@ -132,6 +130,14 @@ pub trait ExpandDatabase: SourceDatabase {
     /// Expand macro call to a token tree.
     // This query is LRU cached
     fn macro_expand(&self, macro_call: MacroCallId) -> ExpandResult<Arc<tt::Subtree>>;
+    #[salsa::invoke(crate::builtin_fn_macro::include_arg_to_tt)]
+    fn include_expand(
+        &self,
+        arg_id: MacroCallId,
+    ) -> Result<
+        (triomphe::Arc<(::tt::Subtree<::tt::TokenId>, mbe::TokenMap)>, base_db::FileId),
+        ExpandError,
+    >;
     /// Special case of the previous query for procedural macros. We can't LRU
     /// proc macros, since they are not deterministic in general, and
     /// non-determinism breaks salsa in a very, very, very bad way.
@@ -281,31 +287,6 @@ fn parse_macro_expansion(
     let _p = profile::span("parse_macro_expansion");
     let mbe::ValueResult { value: tt, err } = db.macro_expand(macro_file.macro_call_id);
 
-    if let Some(err) = &err {
-        if tracing::enabled!(tracing::Level::DEBUG) {
-            // Note:
-            // The final goal we would like to make all parse_macro success,
-            // such that the following log will not call anyway.
-            let loc = db.lookup_intern_macro_call(macro_file.macro_call_id);
-            let node = loc.to_node(db);
-
-            // collect parent information for warning log
-            let parents = std::iter::successors(loc.kind.file_id().call_node(db), |it| {
-                it.file_id.call_node(db)
-            })
-            .map(|n| format!("{:#}", n.value))
-            .collect::<Vec<_>>()
-            .join("\n");
-
-            tracing::debug!(
-                "fail on macro_parse: (reason: {:?} macro_call: {:#}) parents: {}",
-                err,
-                node.value,
-                parents
-            );
-        }
-    }
-
     let expand_to = macro_expand_to(db, macro_file.macro_call_id);
 
     tracing::debug!("expanded = {}", tt.as_debug_string());
@@ -320,9 +301,14 @@ fn macro_arg(
     db: &dyn ExpandDatabase,
     id: MacroCallId,
 ) -> Option<Arc<(tt::Subtree, mbe::TokenMap, fixup::SyntaxFixupUndoInfo)>> {
-    let arg = db.macro_arg_text(id)?;
     let loc = db.lookup_intern_macro_call(id);
 
+    if let Some(EagerCallInfo { arg, arg_id: Some(_), error: _ }) = loc.eager.as_deref() {
+        return Some(Arc::new((arg.0.clone(), arg.1.clone(), Default::default())));
+    }
+
+    let arg = db.macro_arg_text(id)?;
+
     let node = SyntaxNode::new_root(arg);
     let censor = censor_for_macro_input(&loc, &node);
     let mut fixups = fixup::fixup_syntax(&node);
@@ -398,7 +384,17 @@ fn macro_arg_text(db: &dyn ExpandDatabase, id: MacroCallId) -> Option<GreenNode>
             return None;
         }
     }
-    Some(arg.green().into())
+    if let Some(EagerCallInfo { arg, .. }) = loc.eager.as_deref() {
+        Some(
+            mbe::token_tree_to_syntax_node(&arg.0, mbe::TopEntryPoint::Expr)
+                .0
+                .syntax_node()
+                .green()
+                .into(),
+        )
+    } else {
+        Some(arg.green().into())
+    }
 }
 
 fn macro_def(
@@ -445,23 +441,21 @@ fn macro_def(
 fn macro_expand(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<Arc<tt::Subtree>> {
     let _p = profile::span("macro_expand");
     let loc = db.lookup_intern_macro_call(id);
-    if let Some(eager) = &loc.eager {
-        return ExpandResult { value: eager.arg_or_expansion.clone(), err: eager.error.clone() };
+    if let Some(EagerCallInfo { arg, arg_id: None, error }) = loc.eager.as_deref() {
+        // This is an input expansion for an eager macro. These are already pre-expanded
+        return ExpandResult { value: Arc::new(arg.0.clone()), err: error.clone() };
     }
-
     let expander = match db.macro_def(loc.def) {
         Ok(it) => it,
-        // FIXME: This is weird -- we effectively report macro *definition*
-        // errors lazily, when we try to expand the macro. Instead, they should
-        // be reported at the definition site when we construct a def map.
-        // (Note we do report them also at the definition site in the late diagnostic pass)
+        // FIXME: We should make sure to enforce a variant that invalid macro
+        // definitions do not get expanders that could reach this call path!
         Err(err) => {
             return ExpandResult {
                 value: Arc::new(tt::Subtree {
                     delimiter: tt::Delimiter::UNSPECIFIED,
                     token_trees: vec![],
                 }),
-                err: Some(ExpandError::Other(format!("invalid macro definition: {err}").into())),
+                err: Some(ExpandError::other(format!("invalid macro definition: {err}"))),
             }
         }
     };
@@ -473,13 +467,21 @@ fn macro_expand(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<Arc<tt
                     token_trees: Vec::new(),
                 },
             ),
-            err: Some(ExpandError::Other(
+            // FIXME: We should make sure to enforce a variant that invalid macro
+            // calls do not reach this call path!
+            err: Some(ExpandError::other(
                 "invalid token tree"
-                .into(),
             )),
         };
     };
-    let ExpandResult { value: mut tt, err } = expander.expand(db, id, &macro_arg.0);
+    let (arg_tt, arg_tm, undo_info) = &*macro_arg;
+    let ExpandResult { value: mut tt, mut err } = expander.expand(db, id, arg_tt);
+
+    if let Some(EagerCallInfo { error, .. }) = loc.eager.as_deref() {
+        // FIXME: We should report both errors!
+        err = error.clone().or(err);
+    }
+
     // Set a hard limit for the expanded tt
     let count = tt.count();
     if TOKEN_LIMIT.check(count).is_err() {
@@ -488,18 +490,15 @@ fn macro_expand(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<Arc<tt
                 delimiter: tt::Delimiter::UNSPECIFIED,
                 token_trees: vec![],
             }),
-            err: Some(ExpandError::Other(
-                format!(
-                    "macro invocation exceeds token limit: produced {} tokens, limit is {}",
-                    count,
-                    TOKEN_LIMIT.inner(),
-                )
-                .into(),
-            )),
+            err: Some(ExpandError::other(format!(
+                "macro invocation exceeds token limit: produced {} tokens, limit is {}",
+                count,
+                TOKEN_LIMIT.inner(),
+            ))),
         };
     }
 
-    fixup::reverse_fixups(&mut tt, &macro_arg.1, &macro_arg.2);
+    fixup::reverse_fixups(&mut tt, arg_tm, undo_info);
 
     ExpandResult { value: Arc::new(tt), err }
 }
@@ -520,9 +519,8 @@ fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<t
                 delimiter: tt::Delimiter::UNSPECIFIED,
                 token_trees: Vec::new(),
             },
-            err: Some(ExpandError::Other(
+            err: Some(ExpandError::other(
                 "invalid token tree"
-                .into(),
             )),
         };
     };
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs
index 59a92ff0ab6..7ee3fd375f6 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs
@@ -31,22 +31,24 @@ use crate::{
     MacroCallLoc, MacroDefId, MacroDefKind, UnresolvedMacro,
 };
 
-pub fn expand_eager_macro(
+pub fn expand_eager_macro_input(
     db: &dyn ExpandDatabase,
     krate: CrateId,
     macro_call: InFile<ast::MacroCall>,
     def: MacroDefId,
     resolver: &dyn Fn(ModPath) -> Option<MacroDefId>,
 ) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro> {
-    let MacroDefKind::BuiltInEager(eager, _) = def.kind else {
-        panic!("called `expand_eager_macro` on non-eager macro def {def:?}")
+    assert!(matches!(def.kind, MacroDefKind::BuiltInEager(..)));
+    let token_tree = macro_call.value.token_tree();
+
+    let Some(token_tree) = token_tree else {
+        return Ok(ExpandResult { value: None, err:
+            Some(ExpandError::other(
+                "invalid token tree"
+            )),
+        });
     };
-    let hygiene = Hygiene::new(db, macro_call.file_id);
-    let parsed_args = macro_call
-        .value
-        .token_tree()
-        .map(|tt| mbe::syntax_node_to_token_tree(tt.syntax()).0)
-        .unwrap_or_else(tt::Subtree::empty);
+    let (parsed_args, arg_token_map) = mbe::syntax_node_to_token_tree(token_tree.syntax());
 
     let ast_map = db.ast_id_map(macro_call.file_id);
     let call_id = InFile::new(macro_call.file_id, ast_map.ast_id(&macro_call.value));
@@ -60,41 +62,40 @@ pub fn expand_eager_macro(
         def,
         krate,
         eager: Some(Box::new(EagerCallInfo {
-            arg_or_expansion: Arc::new(parsed_args.clone()),
-            included_file: None,
+            arg: Arc::new((parsed_args, arg_token_map)),
+            arg_id: None,
             error: None,
         })),
         kind: MacroCallKind::FnLike { ast_id: call_id, expand_to: ExpandTo::Expr },
     });
-
-    let parsed_args = mbe::token_tree_to_syntax_node(&parsed_args, mbe::TopEntryPoint::Expr).0;
-    let ExpandResult { value, mut err } = eager_macro_recur(
+    let arg_as_expr = match db.macro_arg_text(arg_id) {
+        Some(it) => it,
+        None => {
+            return Ok(ExpandResult {
+                value: None,
+                err: Some(ExpandError::other("invalid token tree")),
+            })
+        }
+    };
+    let ExpandResult { value: expanded_eager_input, err } = eager_macro_recur(
         db,
-        &hygiene,
-        InFile::new(arg_id.as_file(), parsed_args.syntax_node()),
+        &Hygiene::new(db, macro_call.file_id),
+        InFile::new(arg_id.as_file(), SyntaxNode::new_root(arg_as_expr)),
         krate,
         resolver,
     )?;
-    let Some(value ) = value else {
+    let Some(expanded_eager_input) = expanded_eager_input else {
         return Ok(ExpandResult { value: None, err })
     };
-    let subtree = {
-        let mut subtree = mbe::syntax_node_to_token_tree(&value).0;
-        subtree.delimiter = crate::tt::Delimiter::unspecified();
-        subtree
-    };
-
-    let res = eager.expand(db, arg_id, &subtree);
-    if err.is_none() {
-        err = res.err;
-    }
+    let (mut subtree, token_map) = mbe::syntax_node_to_token_tree(&expanded_eager_input);
+    subtree.delimiter = crate::tt::Delimiter::unspecified();
 
     let loc = MacroCallLoc {
         def,
         krate,
         eager: Some(Box::new(EagerCallInfo {
-            arg_or_expansion: Arc::new(res.value.subtree),
-            included_file: res.value.included_file,
+            arg: Arc::new((subtree, token_map)),
+            arg_id: Some(arg_id),
             error: err.clone(),
         })),
         kind: MacroCallKind::FnLike { ast_id: call_id, expand_to },
@@ -118,8 +119,9 @@ fn lazy_expand(
         MacroCallKind::FnLike { ast_id: macro_call.with_value(ast_id), expand_to },
     );
 
-    let file_id = id.as_file();
-    db.parse_or_expand_with_err(file_id).map(|parse| InFile::new(file_id, parse))
+    let macro_file = id.as_macro_file();
+
+    db.parse_macro_expansion(macro_file).map(|parse| InFile::new(macro_file.into(), parse.0))
 }
 
 fn eager_macro_recur(
@@ -142,13 +144,13 @@ fn eager_macro_recur(
         let def = match child.path().and_then(|path| ModPath::from_src(db, path, hygiene)) {
             Some(path) => macro_resolver(path.clone()).ok_or(UnresolvedMacro { path })?,
             None => {
-                error = Some(ExpandError::Other("malformed macro invocation".into()));
+                error = Some(ExpandError::other("malformed macro invocation"));
                 continue;
             }
         };
         let ExpandResult { value, err } = match def.kind {
             MacroDefKind::BuiltInEager(..) => {
-                let id = match expand_eager_macro(
+                let ExpandResult { value, err } = match expand_eager_macro_input(
                     db,
                     krate,
                     curr.with_value(child.clone()),
@@ -158,9 +160,17 @@ fn eager_macro_recur(
                     Ok(it) => it,
                     Err(err) => return Err(err),
                 };
-                id.map(|call| {
-                    call.map(|call| db.parse_or_expand(call.as_file()).clone_for_update())
-                })
+                match value {
+                    Some(call) => {
+                        let ExpandResult { value, err: err2 } =
+                            db.parse_macro_expansion(call.as_macro_file());
+                        ExpandResult {
+                            value: Some(value.0.syntax_node().clone_for_update()),
+                            err: err.or(err2),
+                        }
+                    }
+                    None => ExpandResult { value: None, err },
+                }
             }
             MacroDefKind::Declarative(_)
             | MacroDefKind::BuiltIn(..)
@@ -180,7 +190,7 @@ fn eager_macro_recur(
                     krate,
                     macro_resolver,
                 )?;
-                let err = if err.is_none() { error } else { err };
+                let err = err.or(error);
                 ExpandResult { value, err }
             }
         };
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
index c8373778d32..e0c199328ef 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
@@ -58,7 +58,13 @@ pub enum ExpandError {
     UnresolvedProcMacro(CrateId),
     Mbe(mbe::ExpandError),
     RecursionOverflowPoisoned,
-    Other(Box<str>),
+    Other(Box<Box<str>>),
+}
+
+impl ExpandError {
+    pub fn other(msg: impl Into<Box<str>>) -> Self {
+        ExpandError::Other(Box::new(msg.into()))
+    }
 }
 
 impl From<mbe::ExpandError> for ExpandError {
@@ -97,9 +103,15 @@ impl fmt::Display for ExpandError {
 /// The two variants are encoded in a single u32 which are differentiated by the MSB.
 /// If the MSB is 0, the value represents a `FileId`, otherwise the remaining 31 bits represent a
 /// `MacroCallId`.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
 pub struct HirFileId(u32);
 
+impl fmt::Debug for HirFileId {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.repr().fmt(f)
+    }
+}
+
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct MacroFile {
     pub macro_call_id: MacroCallId,
@@ -115,6 +127,7 @@ impl_intern_key!(MacroCallId);
 pub struct MacroCallLoc {
     pub def: MacroDefId,
     pub(crate) krate: CrateId,
+    /// Some if `def` is a builtin eager macro.
     eager: Option<Box<EagerCallInfo>>,
     pub kind: MacroCallKind,
 }
@@ -140,8 +153,10 @@ pub enum MacroDefKind {
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 struct EagerCallInfo {
     /// NOTE: This can be *either* the expansion result, *or* the argument to the eager macro!
-    arg_or_expansion: Arc<tt::Subtree>,
-    included_file: Option<(FileId, TokenMap)>,
+    arg: Arc<(tt::Subtree, TokenMap)>,
+    /// call id of the eager macro's input file. If this is none, macro call containing this call info
+    /// is an eager macro's input, otherwise it is its output.
+    arg_id: Option<MacroCallId>,
     error: Option<ExpandError>,
 }
 
@@ -206,10 +221,15 @@ impl HirFileId {
                 HirFileIdRepr::FileId(id) => break id,
                 HirFileIdRepr::MacroFile(MacroFile { macro_call_id }) => {
                     let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_call_id);
-                    file_id = match loc.eager.as_deref() {
-                        Some(&EagerCallInfo { included_file: Some((file, _)), .. }) => file.into(),
+                    let is_include_expansion = loc.def.is_include()
+                        && matches!(
+                            loc.eager.as_deref(),
+                            Some(EagerCallInfo { arg_id: Some(_), .. })
+                        );
+                    file_id = match is_include_expansion.then(|| db.include_expand(macro_call_id)) {
+                        Some(Ok((_, file))) => file.into(),
                         _ => loc.kind.file_id(),
-                    };
+                    }
                 }
             }
         }
@@ -325,7 +345,17 @@ impl HirFileId {
         match self.macro_file() {
             Some(macro_file) => {
                 let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
-                matches!(loc.eager.as_deref(), Some(EagerCallInfo { included_file: Some(..), .. }))
+                loc.def.is_include()
+            }
+            _ => false,
+        }
+    }
+
+    pub fn is_eager(&self, db: &dyn db::ExpandDatabase) -> bool {
+        match self.macro_file() {
+            Some(macro_file) => {
+                let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
+                matches!(loc.eager.as_deref(), Some(EagerCallInfo { .. }))
             }
             _ => false,
         }
@@ -423,6 +453,10 @@ impl MacroDefId {
     pub fn is_attribute_derive(&self) -> bool {
         matches!(self.kind, MacroDefKind::BuiltInAttr(expander, ..) if expander.is_derive())
     }
+
+    pub fn is_include(&self) -> bool {
+        matches!(self.kind, MacroDefKind::BuiltInEager(expander, ..) if expander.is_include())
+    }
 }
 
 impl MacroCallLoc {
@@ -569,6 +603,10 @@ impl MacroCallId {
     pub fn as_file(self) -> HirFileId {
         MacroFile { macro_call_id: self }.into()
     }
+
+    pub fn as_macro_file(self) -> MacroFile {
+        MacroFile { macro_call_id: self }
+    }
 }
 
 /// ExpansionInfo mainly describes how to map text range between src and expanded macro
@@ -662,7 +700,7 @@ impl ExpansionInfo {
 
         let token_id = match token_id_in_attr_input {
             Some(token_id) => token_id,
-            // the token is not inside an attribute's input so do the lookup in the macro_arg as usual
+            // the token is not inside `an attribute's input so do the lookup in the macro_arg as usual
             None => {
                 let relative_range =
                     token.value.text_range().checked_sub(self.arg.value.text_range().start())?;
@@ -694,14 +732,18 @@ impl ExpansionInfo {
         let call_id = self.expanded.file_id.macro_file()?.macro_call_id;
         let loc = db.lookup_intern_macro_call(call_id);
 
-        if let Some((file, map)) = loc.eager.and_then(|e| e.included_file) {
-            // Special case: map tokens from `include!` expansions to the included file
-            let range = map.first_range_by_token(token_id, token.value.kind())?;
-            let source = db.parse(file);
+        // Special case: map tokens from `include!` expansions to the included file
+        if loc.def.is_include()
+            && matches!(loc.eager.as_deref(), Some(EagerCallInfo { arg_id: Some(_), .. }))
+        {
+            if let Ok((tt_and_map, file_id)) = db.include_expand(call_id) {
+                let range = tt_and_map.1.first_range_by_token(token_id, token.value.kind())?;
+                let source = db.parse(file_id);
 
-            let token = source.syntax_node().covering_element(range).into_token()?;
+                let token = source.syntax_node().covering_element(range).into_token()?;
 
-            return Some((InFile::new(file.into(), token), Origin::Call));
+                return Some((InFile::new(file_id.into(), token), Origin::Call));
+            }
         }
 
         // Attributes are a bit special for us, they have two inputs, the input tokentree and the annotated item.
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs
index c9539210abf..41675c630dc 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs
@@ -46,7 +46,7 @@ impl ProcMacroExpander {
                         never!("Non-dummy expander even though there are no proc macros");
                         return ExpandResult::new(
                             tt::Subtree::empty(),
-                            ExpandError::Other("Internal error".into()),
+                            ExpandError::other("Internal error"),
                         );
                     }
                 };
@@ -60,7 +60,7 @@ impl ProcMacroExpander {
                         );
                         return ExpandResult::new(
                             tt::Subtree::empty(),
-                            ExpandError::Other("Internal error".into()),
+                            ExpandError::other("Internal error"),
                         );
                     }
                 };
@@ -75,14 +75,11 @@ impl ProcMacroExpander {
                         ProcMacroExpansionError::System(text)
                             if proc_macro.kind == ProcMacroKind::Attr =>
                         {
-                            ExpandResult {
-                                value: tt.clone(),
-                                err: Some(ExpandError::Other(text.into())),
-                            }
+                            ExpandResult { value: tt.clone(), err: Some(ExpandError::other(text)) }
                         }
                         ProcMacroExpansionError::System(text)
                         | ProcMacroExpansionError::Panic(text) => {
-                            ExpandResult::new(tt::Subtree::empty(), ExpandError::Other(text.into()))
+                            ExpandResult::new(tt::Subtree::empty(), ExpandError::other(text))
                         }
                     },
                 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
index 6ca0dbb8503..c8bea34507c 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
@@ -22,10 +22,10 @@ either = "1.7.0"
 tracing = "0.1.35"
 rustc-hash = "1.1.0"
 scoped-tls = "1.0.0"
-chalk-solve = { version = "0.89.0", default-features = false }
-chalk-ir = "0.89.0"
-chalk-recursive = { version = "0.89.0", default-features = false }
-chalk-derive = "0.89.0"
+chalk-solve = { version = "0.91.0", default-features = false }
+chalk-ir = "0.91.0"
+chalk-recursive = { version = "0.91.0", default-features = false }
+chalk-derive = "0.91.0"
 la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
 once_cell = "1.17.0"
 triomphe.workspace = true
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs
index f5b3f176b12..3860bccec8b 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs
@@ -22,17 +22,37 @@ pub(crate) enum AutoderefKind {
     Overloaded,
 }
 
+/// Returns types that `ty` transitively dereferences to. This function is only meant to be used
+/// outside `hir-ty`.
+///
+/// It is guaranteed that:
+/// - the yielded types don't contain inference variables (but may contain `TyKind::Error`).
+/// - a type won't be yielded more than once; in other words, the returned iterator will stop if it
+///   detects a cycle in the deref chain.
 pub fn autoderef(
     db: &dyn HirDatabase,
     env: Arc<TraitEnvironment>,
     ty: Canonical<Ty>,
-) -> impl Iterator<Item = Canonical<Ty>> + '_ {
+) -> impl Iterator<Item = Ty> {
     let mut table = InferenceTable::new(db, env);
     let ty = table.instantiate_canonical(ty);
     let mut autoderef = Autoderef::new(&mut table, ty);
     let mut v = Vec::new();
     while let Some((ty, _steps)) = autoderef.next() {
-        v.push(autoderef.table.canonicalize(ty).value);
+        // `ty` may contain unresolved inference variables. Since there's no chance they would be
+        // resolved, just replace with fallback type.
+        let resolved = autoderef.table.resolve_completely(ty);
+
+        // If the deref chain contains a cycle (e.g. `A` derefs to `B` and `B` derefs to `A`), we
+        // would revisit some already visited types. Stop here to avoid duplication.
+        //
+        // XXX: The recursion limit for `Autoderef` is currently 10, so `Vec::contains()` shouldn't
+        // be too expensive. Replace this duplicate check with `FxHashSet` if it proves to be more
+        // performant.
+        if v.contains(&resolved) {
+            break;
+        }
+        v.push(resolved);
     }
     v.into_iter()
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs
index ac962c9e3e1..5dd8e2719a2 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs
@@ -497,7 +497,7 @@ pub(crate) fn associated_ty_data_query(
     let generic_params = generics(db.upcast(), type_alias.into());
     // let bound_vars = generic_params.bound_vars_subst(DebruijnIndex::INNERMOST);
     let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db.upcast());
-    let ctx = crate::TyLoweringContext::new(db, &resolver)
+    let ctx = crate::TyLoweringContext::new(db, &resolver, type_alias.into())
         .with_type_param_mode(crate::lower::ParamLoweringMode::Variable);
 
     let trait_subst = TyBuilder::subst_for_def(db, trait_, None)
@@ -592,6 +592,7 @@ fn well_known_trait_from_lang_item(item: LangItem) -> Option<WellKnownTrait> {
         LangItem::Unpin => WellKnownTrait::Unpin,
         LangItem::Unsize => WellKnownTrait::Unsize,
         LangItem::Tuple => WellKnownTrait::Tuple,
+        LangItem::PointeeTrait => WellKnownTrait::Pointee,
         _ => return None,
     })
 }
@@ -612,6 +613,7 @@ fn lang_item_from_well_known_trait(trait_: WellKnownTrait) -> LangItem {
         WellKnownTrait::Tuple => LangItem::Tuple,
         WellKnownTrait::Unpin => LangItem::Unpin,
         WellKnownTrait::Unsize => LangItem::Unsize,
+        WellKnownTrait::Pointee => LangItem::PointeeTrait,
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
index 40b63b17b5a..262341c6e9e 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
@@ -6,8 +6,8 @@ use hir_def::{
     hir::Expr,
     path::Path,
     resolver::{Resolver, ValueNs},
-    type_ref::ConstRef,
-    EnumVariantId, GeneralConstId, StaticId,
+    type_ref::LiteralConstRef,
+    ConstBlockLoc, EnumVariantId, GeneralConstId, StaticId,
 };
 use la_arena::{Idx, RawIdx};
 use stdx::never;
@@ -129,23 +129,28 @@ pub fn intern_const_scalar(value: ConstScalar, ty: Ty) -> Const {
 }
 
 /// Interns a constant scalar with the given type
-pub fn intern_const_ref(db: &dyn HirDatabase, value: &ConstRef, ty: Ty, krate: CrateId) -> Const {
+pub fn intern_const_ref(
+    db: &dyn HirDatabase,
+    value: &LiteralConstRef,
+    ty: Ty,
+    krate: CrateId,
+) -> Const {
     let layout = db.layout_of_ty(ty.clone(), krate);
     let bytes = match value {
-        ConstRef::Int(i) => {
+        LiteralConstRef::Int(i) => {
             // FIXME: We should handle failure of layout better.
             let size = layout.map(|x| x.size.bytes_usize()).unwrap_or(16);
             ConstScalar::Bytes(i.to_le_bytes()[0..size].to_vec(), MemoryMap::default())
         }
-        ConstRef::UInt(i) => {
+        LiteralConstRef::UInt(i) => {
             let size = layout.map(|x| x.size.bytes_usize()).unwrap_or(16);
             ConstScalar::Bytes(i.to_le_bytes()[0..size].to_vec(), MemoryMap::default())
         }
-        ConstRef::Bool(b) => ConstScalar::Bytes(vec![*b as u8], MemoryMap::default()),
-        ConstRef::Char(c) => {
+        LiteralConstRef::Bool(b) => ConstScalar::Bytes(vec![*b as u8], MemoryMap::default()),
+        LiteralConstRef::Char(c) => {
             ConstScalar::Bytes((*c as u32).to_le_bytes().to_vec(), MemoryMap::default())
         }
-        ConstRef::Unknown => ConstScalar::Unknown,
+        LiteralConstRef::Unknown => ConstScalar::Unknown,
     };
     intern_const_scalar(bytes, ty)
 }
@@ -154,7 +159,7 @@ pub fn intern_const_ref(db: &dyn HirDatabase, value: &ConstRef, ty: Ty, krate: C
 pub fn usize_const(db: &dyn HirDatabase, value: Option<u128>, krate: CrateId) -> Const {
     intern_const_ref(
         db,
-        &value.map_or(ConstRef::Unknown, ConstRef::UInt),
+        &value.map_or(LiteralConstRef::Unknown, LiteralConstRef::UInt),
         TyBuilder::usize(),
         krate,
     )
@@ -210,17 +215,18 @@ pub(crate) fn const_eval_query(
         GeneralConstId::ConstId(c) => {
             db.monomorphized_mir_body(c.into(), subst, db.trait_environment(c.into()))?
         }
-        GeneralConstId::AnonymousConstId(c) => {
-            let (def, root) = db.lookup_intern_anonymous_const(c);
-            let body = db.body(def);
-            let infer = db.infer(def);
+        GeneralConstId::ConstBlockId(c) => {
+            let ConstBlockLoc { parent, root } = db.lookup_intern_anonymous_const(c);
+            let body = db.body(parent);
+            let infer = db.infer(parent);
             Arc::new(monomorphize_mir_body_bad(
                 db,
-                lower_to_mir(db, def, &body, &infer, root)?,
+                lower_to_mir(db, parent, &body, &infer, root)?,
                 subst,
-                db.trait_environment_for_body(def),
+                db.trait_environment_for_body(parent),
             )?)
         }
+        GeneralConstId::InTypeConstId(c) => db.mir_body(c.into())?,
     };
     let c = interpret_mir(db, &body, false).0?;
     Ok(c)
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs
index 06fff08b7d3..0db1fefbfef 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs
@@ -2052,6 +2052,17 @@ fn extern_weak_statics() {
 }
 
 #[test]
+fn from_ne_bytes() {
+    check_number(
+        r#"
+//- minicore: int_impl
+const GOAL: u32 = u32::from_ne_bytes([44, 1, 0, 0]);
+        "#,
+        300,
+    );
+}
+
+#[test]
 fn enums() {
     check_number(
         r#"
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
index ca8a394e360..9dd810f844d 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
@@ -278,6 +278,7 @@ fn infer_wait(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult>
         DefWithBodyId::VariantId(it) => {
             db.enum_data(it.parent).variants[it.local_id].name.display(db.upcast()).to_string()
         }
+        DefWithBodyId::InTypeConstId(it) => format!("in type const {it:?}"),
     });
     db.infer_query(def)
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
index 7c38e6583a7..9f9a56ffab0 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
@@ -18,9 +18,10 @@ pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec<ExprId> {
 
     let is_unsafe = match def {
         DefWithBodyId::FunctionId(it) => db.function_data(it).has_unsafe_kw(),
-        DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) | DefWithBodyId::VariantId(_) => {
-            false
-        }
+        DefWithBodyId::StaticId(_)
+        | DefWithBodyId::ConstId(_)
+        | DefWithBodyId::VariantId(_)
+        | DefWithBodyId::InTypeConstId(_) => false,
     };
     if is_unsafe {
         return res;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
index f90e025c7cc..c1df24d1729 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
@@ -2,7 +2,10 @@
 //! HIR back into source code, and just displaying them for debugging/testing
 //! purposes.
 
-use std::fmt::{self, Debug};
+use std::{
+    fmt::{self, Debug},
+    mem::size_of,
+};
 
 use base_db::CrateId;
 use chalk_ir::{BoundVar, TyKind};
@@ -536,8 +539,44 @@ fn render_const_scalar(
                 }
                 f.write_str("]")
             }
+            TyKind::Dyn(_) => {
+                let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap());
+                let ty_id = usize::from_le_bytes(b[b.len() / 2..].try_into().unwrap());
+                let Ok(t) = memory_map.vtable.ty(ty_id) else {
+                    return f.write_str("<ty-missing-in-vtable-map>");
+                };
+                let Ok(layout) = f.db.layout_of_ty(t.clone(), krate) else {
+                    return f.write_str("<layout-error>");
+                };
+                let size = layout.size.bytes_usize();
+                let Some(bytes) = memory_map.get(addr, size) else {
+                    return f.write_str("<ref-data-not-available>");
+                };
+                f.write_str("&")?;
+                render_const_scalar(f, bytes, memory_map, t)
+            }
+            TyKind::Adt(adt, _) if b.len() == 2 * size_of::<usize>() => match adt.0 {
+                hir_def::AdtId::StructId(s) => {
+                    let data = f.db.struct_data(s);
+                    write!(f, "&{}", data.name.display(f.db.upcast()))?;
+                    Ok(())
+                }
+                _ => {
+                    return f.write_str("<unsized-enum-or-union>");
+                }
+            },
             _ => {
-                let addr = usize::from_le_bytes(b.try_into().unwrap());
+                let addr = usize::from_le_bytes(match b.try_into() {
+                    Ok(b) => b,
+                    Err(_) => {
+                        never!(
+                            "tried rendering ty {:?} in const ref with incorrect byte count {}",
+                            t,
+                            b.len()
+                        );
+                        return f.write_str("<layout-error>");
+                    }
+                });
                 let Ok(layout) = f.db.layout_of_ty(t.clone(), krate) else {
                     return f.write_str("<layout-error>");
                 };
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
index 80f32e96ee6..1ac0837b5b2 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
@@ -41,10 +41,15 @@ use stdx::{always, never};
 use triomphe::Arc;
 
 use crate::{
-    db::HirDatabase, fold_tys, infer::coerce::CoerceMany, lower::ImplTraitLoweringMode,
-    static_lifetime, to_assoc_type_id, traits::FnTrait, AliasEq, AliasTy, ClosureId, DomainGoal,
-    GenericArg, Goal, ImplTraitId, InEnvironment, Interner, ProjectionTy, RpitId, Substitution,
-    TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt,
+    db::HirDatabase,
+    fold_tys,
+    infer::coerce::CoerceMany,
+    lower::ImplTraitLoweringMode,
+    static_lifetime, to_assoc_type_id,
+    traits::FnTrait,
+    utils::{InTypeConstIdMetadata, UnevaluatedConstEvaluatorFolder},
+    AliasEq, AliasTy, ClosureId, DomainGoal, GenericArg, Goal, ImplTraitId, InEnvironment,
+    Interner, ProjectionTy, RpitId, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt,
 };
 
 // This lint has a false positive here. See the link below for details.
@@ -102,6 +107,16 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
                 },
             });
         }
+        DefWithBodyId::InTypeConstId(c) => {
+            // FIXME(const-generic-body): We should not get the return type in this way.
+            ctx.return_ty = c
+                .lookup(db.upcast())
+                .thing
+                .box_any()
+                .downcast::<InTypeConstIdMetadata>()
+                .unwrap()
+                .0;
+        }
     }
 
     ctx.infer_body();
@@ -684,7 +699,7 @@ impl<'a> InferenceContext<'a> {
 
     fn collect_fn(&mut self, func: FunctionId) {
         let data = self.db.function_data(func);
-        let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver)
+        let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver, func.into())
             .with_impl_trait_mode(ImplTraitLoweringMode::Param);
         let mut param_tys =
             data.params.iter().map(|type_ref| ctx.lower_ty(type_ref)).collect::<Vec<_>>();
@@ -708,7 +723,7 @@ impl<'a> InferenceContext<'a> {
         }
         let return_ty = &*data.ret_type;
 
-        let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver)
+        let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into())
             .with_impl_trait_mode(ImplTraitLoweringMode::Opaque);
         let return_ty = ctx.lower_ty(return_ty);
         let return_ty = self.insert_type_vars(return_ty);
@@ -823,7 +838,7 @@ impl<'a> InferenceContext<'a> {
     }
 
     fn make_ty(&mut self, type_ref: &TypeRef) -> Ty {
-        let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
+        let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into());
         let ty = ctx.lower_ty(type_ref);
         let ty = self.insert_type_vars(ty);
         self.normalize_associated_types_in(ty)
@@ -850,7 +865,21 @@ impl<'a> InferenceContext<'a> {
     }
 
     fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool {
-        self.table.unify(ty1, ty2)
+        let ty1 = ty1
+            .clone()
+            .try_fold_with(
+                &mut UnevaluatedConstEvaluatorFolder { db: self.db },
+                DebruijnIndex::INNERMOST,
+            )
+            .unwrap();
+        let ty2 = ty2
+            .clone()
+            .try_fold_with(
+                &mut UnevaluatedConstEvaluatorFolder { db: self.db },
+                DebruijnIndex::INNERMOST,
+            )
+            .unwrap();
+        self.table.unify(&ty1, &ty2)
     }
 
     /// Attempts to returns the deeply last field of nested structures, but
@@ -973,7 +1002,7 @@ impl<'a> InferenceContext<'a> {
             Some(path) => path,
             None => return (self.err_ty(), None),
         };
-        let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
+        let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into());
         let (resolution, unresolved) = if value_ns {
             match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path) {
                 Some(ResolveValueResult::ValueNs(value)) => match value {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs
index 23189f383e0..ff64ae252bc 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs
@@ -715,10 +715,9 @@ impl InferenceContext<'_> {
     }
 
     fn is_upvar(&self, place: &HirPlace) -> bool {
-        let b = &self.body[place.local];
         if let Some(c) = self.current_closure {
             let (_, root) = self.db.lookup_intern_closure(c.into());
-            return b.is_upvar(root);
+            return self.body.is_binding_upvar(place.local, root);
         }
         false
     }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
index 33e98ac86cf..194471f0048 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
@@ -173,8 +173,8 @@ impl<'a> InferenceContext<'a> {
             }
             Expr::Const(id) => {
                 self.with_breakable_ctx(BreakableKind::Border, None, None, |this| {
-                    let (_, expr) = this.db.lookup_intern_anonymous_const(*id);
-                    this.infer_expr(expr, expected)
+                    let loc = this.db.lookup_intern_anonymous_const(*id);
+                    this.infer_expr(loc.root, expected)
                 })
                 .1
             }
@@ -1715,6 +1715,7 @@ impl<'a> InferenceContext<'a> {
                         const_or_path_to_chalk(
                             this.db,
                             &this.resolver,
+                            this.owner.into(),
                             ty,
                             c,
                             ParamLoweringMode::Placeholder,
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs
index 44783424390..46f2e1d7d12 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs
@@ -43,8 +43,8 @@ impl<'a> InferenceContext<'a> {
                 }
             }
             Expr::Const(id) => {
-                let (_, expr) = self.db.lookup_intern_anonymous_const(*id);
-                self.infer_mut_expr(expr, Mutability::Not);
+                let loc = self.db.lookup_intern_anonymous_const(*id);
+                self.infer_mut_expr(loc.root, Mutability::Not);
             }
             Expr::Let { pat, expr } => self.infer_mut_expr(*expr, self.pat_bound_mutability(*pat)),
             Expr::Block { id: _, statements, tail, label: _ }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
index 95a20f983f1..79d9e21e797 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
@@ -44,7 +44,8 @@ impl InferenceContext<'_> {
             let last = path.segments().last()?;
 
             // Don't use `self.make_ty()` here as we need `orig_ns`.
-            let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
+            let ctx =
+                crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into());
             let (ty, orig_ns) = ctx.lower_ty_ext(type_ref);
             let ty = self.table.insert_type_vars(ty);
             let ty = self.table.normalize_associated_types_in(ty);
@@ -108,7 +109,7 @@ impl InferenceContext<'_> {
             }
         };
 
-        let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
+        let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into());
         let substs = ctx.substs_from_path(path, value_def, true);
         let substs = substs.as_slice(Interner);
         let parent_substs = self_subst.or_else(|| {
@@ -190,7 +191,11 @@ impl InferenceContext<'_> {
             (TypeNs::TraitId(trait_), true) => {
                 let segment =
                     remaining_segments.last().expect("there should be at least one segment here");
-                let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
+                let ctx = crate::lower::TyLoweringContext::new(
+                    self.db,
+                    &self.resolver,
+                    self.owner.into(),
+                );
                 let trait_ref =
                     ctx.lower_trait_ref_from_resolved_path(trait_, resolved_segment, None);
                 self.resolve_trait_assoc_item(trait_ref, segment, id)
@@ -202,7 +207,11 @@ impl InferenceContext<'_> {
                 // as Iterator>::Item::default`)
                 let remaining_segments_for_ty =
                     remaining_segments.take(remaining_segments.len() - 1);
-                let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
+                let ctx = crate::lower::TyLoweringContext::new(
+                    self.db,
+                    &self.resolver,
+                    self.owner.into(),
+                );
                 let (ty, _) = ctx.lower_partly_resolved_path(
                     def,
                     resolved_segment,
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs
index 89f7d9c4f4a..e4dd4b86cf9 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs
@@ -266,7 +266,7 @@ impl chalk_ir::interner::Interner for Interner {
         c1: &Self::InternedConcreteConst,
         c2: &Self::InternedConcreteConst,
     ) -> bool {
-        (c1 == &ConstScalar::Unknown) || (c2 == &ConstScalar::Unknown) || (c1 == c2)
+        !matches!(c1, ConstScalar::Bytes(..)) || !matches!(c2, ConstScalar::Bytes(..)) || (c1 == c2)
     }
 
     fn intern_generic_arg(
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs
index fca2e09ff0a..0ff8c532d47 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs
@@ -2,6 +2,7 @@ use std::collections::HashMap;
 
 use base_db::fixture::WithFixture;
 use chalk_ir::{AdtId, TyKind};
+use either::Either;
 use hir_def::db::DefDatabase;
 use triomphe::Arc;
 
@@ -25,27 +26,38 @@ fn eval_goal(ra_fixture: &str, minicore: &str) -> Result<Arc<Layout>, LayoutErro
     );
 
     let (db, file_ids) = TestDB::with_many_files(&ra_fixture);
-    let (adt_id, module_id) = file_ids
+    let (adt_or_type_alias_id, module_id) = file_ids
         .into_iter()
         .find_map(|file_id| {
             let module_id = db.module_for_file(file_id);
             let def_map = module_id.def_map(&db);
             let scope = &def_map[module_id.local_id].scope;
-            let adt_id = scope.declarations().find_map(|x| match x {
+            let adt_or_type_alias_id = scope.declarations().find_map(|x| match x {
                 hir_def::ModuleDefId::AdtId(x) => {
                     let name = match x {
                         hir_def::AdtId::StructId(x) => db.struct_data(x).name.to_smol_str(),
                         hir_def::AdtId::UnionId(x) => db.union_data(x).name.to_smol_str(),
                         hir_def::AdtId::EnumId(x) => db.enum_data(x).name.to_smol_str(),
                     };
-                    (name == "Goal").then_some(x)
+                    (name == "Goal").then_some(Either::Left(x))
+                }
+                hir_def::ModuleDefId::TypeAliasId(x) => {
+                    let name = db.type_alias_data(x).name.to_smol_str();
+                    (name == "Goal").then_some(Either::Right(x))
                 }
                 _ => None,
             })?;
-            Some((adt_id, module_id))
+            Some((adt_or_type_alias_id, module_id))
         })
         .unwrap();
-    let goal_ty = TyKind::Adt(AdtId(adt_id), Substitution::empty(Interner)).intern(Interner);
+    let goal_ty = match adt_or_type_alias_id {
+        Either::Left(adt_id) => {
+            TyKind::Adt(AdtId(adt_id), Substitution::empty(Interner)).intern(Interner)
+        }
+        Either::Right(ty_id) => {
+            db.ty(ty_id.into()).substitute(Interner, &Substitution::empty(Interner))
+        }
+    };
     db.layout_of_ty(goal_ty, module_id.krate())
 }
 
@@ -380,9 +392,22 @@ fn niche_optimization() {
 #[test]
 fn const_eval() {
     size_and_align! {
+        struct Goal([i32; 2 + 2]);
+    }
+    size_and_align! {
         const X: usize = 5;
         struct Goal([i32; X]);
     }
+    size_and_align! {
+        mod foo {
+            pub(super) const BAR: usize = 5;
+        }
+        struct Ar<T>([T; foo::BAR]);
+        struct Goal(Ar<Ar<i32>>);
+    }
+    size_and_align! {
+        type Goal = [u8; 2 + 2];
+    }
 }
 
 #[test]
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
index 0c68891fe49..9951a1c750b 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
@@ -27,10 +27,11 @@ use hir_def::{
     nameres::MacroSubNs,
     path::{GenericArg, GenericArgs, ModPath, Path, PathKind, PathSegment, PathSegments},
     resolver::{HasResolver, Resolver, TypeNs},
-    type_ref::{ConstRefOrPath, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, TypeRef},
+    type_ref::{ConstRef, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, TypeRef},
     AdtId, AssocItemId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, FunctionId,
-    GenericDefId, HasModule, ImplId, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, StaticId,
-    StructId, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, VariantId,
+    GenericDefId, HasModule, ImplId, InTypeConstLoc, ItemContainerId, LocalFieldId, Lookup,
+    ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, TypeOrConstParamId, TypeOwnerId,
+    TypeParamId, UnionId, VariantId,
 };
 use hir_expand::{name::Name, ExpandResult};
 use intern::Interned;
@@ -43,17 +44,24 @@ use triomphe::Arc;
 
 use crate::{
     all_super_traits,
-    consteval::{intern_const_ref, path_to_const, unknown_const, unknown_const_as_generic},
+    consteval::{
+        intern_const_ref, intern_const_scalar, path_to_const, unknown_const,
+        unknown_const_as_generic,
+    },
     db::HirDatabase,
     make_binders,
     mapping::{from_chalk_trait_id, ToChalk},
     static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx,
     utils::Generics,
-    utils::{all_super_trait_refs, associated_type_by_name_including_super_traits, generics},
-    AliasEq, AliasTy, Binders, BoundVar, CallableSig, Const, DebruijnIndex, DynTy, FnPointer,
-    FnSig, FnSubst, GenericArgData, ImplTraitId, Interner, ParamKind, PolyFnSig, ProjectionTy,
-    QuantifiedWhereClause, QuantifiedWhereClauses, ReturnTypeImplTrait, ReturnTypeImplTraits,
-    Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause,
+    utils::{
+        all_super_trait_refs, associated_type_by_name_including_super_traits, generics,
+        InTypeConstIdMetadata,
+    },
+    AliasEq, AliasTy, Binders, BoundVar, CallableSig, Const, ConstScalar, DebruijnIndex, DynTy,
+    FnPointer, FnSig, FnSubst, GenericArgData, ImplTraitId, Interner, ParamKind, PolyFnSig,
+    ProjectionTy, QuantifiedWhereClause, QuantifiedWhereClauses, ReturnTypeImplTrait,
+    ReturnTypeImplTraits, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder,
+    TyKind, WhereClause,
 };
 
 #[derive(Debug)]
@@ -106,6 +114,7 @@ pub struct TyLoweringContext<'a> {
     pub db: &'a dyn HirDatabase,
     resolver: &'a Resolver,
     in_binders: DebruijnIndex,
+    owner: TypeOwnerId,
     /// Note: Conceptually, it's thinkable that we could be in a location where
     /// some type params should be represented as placeholders, and others
     /// should be converted to variables. I think in practice, this isn't
@@ -118,13 +127,14 @@ pub struct TyLoweringContext<'a> {
 }
 
 impl<'a> TyLoweringContext<'a> {
-    pub fn new(db: &'a dyn HirDatabase, resolver: &'a Resolver) -> Self {
+    pub fn new(db: &'a dyn HirDatabase, resolver: &'a Resolver, owner: TypeOwnerId) -> Self {
         let impl_trait_mode = ImplTraitLoweringState::Disallowed;
         let type_param_mode = ParamLoweringMode::Placeholder;
         let in_binders = DebruijnIndex::INNERMOST;
         Self {
             db,
             resolver,
+            owner,
             in_binders,
             impl_trait_mode,
             type_param_mode,
@@ -235,6 +245,7 @@ impl<'a> TyLoweringContext<'a> {
                 let const_len = const_or_path_to_chalk(
                     self.db,
                     self.resolver,
+                    self.owner,
                     TyBuilder::usize(),
                     len,
                     self.type_param_mode,
@@ -840,6 +851,7 @@ impl<'a> TyLoweringContext<'a> {
                             const_or_path_to_chalk(
                                 self.db,
                                 self.resolver,
+                                self.owner,
                                 ty,
                                 c,
                                 self.type_param_mode,
@@ -1356,8 +1368,8 @@ pub(crate) fn field_types_query(
     };
     let generics = generics(db.upcast(), def);
     let mut res = ArenaMap::default();
-    let ctx =
-        TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
+    let ctx = TyLoweringContext::new(db, &resolver, GenericDefId::from(variant_id.adt_id()).into())
+        .with_type_param_mode(ParamLoweringMode::Variable);
     for (field_id, field_data) in var_data.fields().iter() {
         res.insert(field_id, make_binders(db, &generics, ctx.lower_ty(&field_data.type_ref)));
     }
@@ -1379,8 +1391,8 @@ pub(crate) fn generic_predicates_for_param_query(
     assoc_name: Option<Name>,
 ) -> Arc<[Binders<QuantifiedWhereClause>]> {
     let resolver = def.resolver(db.upcast());
-    let ctx =
-        TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
+    let ctx = TyLoweringContext::new(db, &resolver, def.into())
+        .with_type_param_mode(ParamLoweringMode::Variable);
     let generics = generics(db.upcast(), def);
     let mut predicates: Vec<_> = resolver
         .where_predicates_in_scope()
@@ -1468,8 +1480,8 @@ pub(crate) fn trait_environment_query(
     def: GenericDefId,
 ) -> Arc<TraitEnvironment> {
     let resolver = def.resolver(db.upcast());
-    let ctx =
-        TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Placeholder);
+    let ctx = TyLoweringContext::new(db, &resolver, def.into())
+        .with_type_param_mode(ParamLoweringMode::Placeholder);
     let mut traits_in_scope = Vec::new();
     let mut clauses = Vec::new();
     for pred in resolver.where_predicates_in_scope() {
@@ -1527,8 +1539,8 @@ pub(crate) fn generic_predicates_query(
     def: GenericDefId,
 ) -> Arc<[Binders<QuantifiedWhereClause>]> {
     let resolver = def.resolver(db.upcast());
-    let ctx =
-        TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
+    let ctx = TyLoweringContext::new(db, &resolver, def.into())
+        .with_type_param_mode(ParamLoweringMode::Variable);
     let generics = generics(db.upcast(), def);
 
     let mut predicates = resolver
@@ -1582,8 +1594,8 @@ pub(crate) fn generic_defaults_query(
     def: GenericDefId,
 ) -> Arc<[Binders<chalk_ir::GenericArg<Interner>>]> {
     let resolver = def.resolver(db.upcast());
-    let ctx =
-        TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
+    let ctx = TyLoweringContext::new(db, &resolver, def.into())
+        .with_type_param_mode(ParamLoweringMode::Variable);
     let generic_params = generics(db.upcast(), def);
     let parent_start_idx = generic_params.len_self();
 
@@ -1648,11 +1660,11 @@ pub(crate) fn generic_defaults_recover(
 fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
     let data = db.function_data(def);
     let resolver = def.resolver(db.upcast());
-    let ctx_params = TyLoweringContext::new(db, &resolver)
+    let ctx_params = TyLoweringContext::new(db, &resolver, def.into())
         .with_impl_trait_mode(ImplTraitLoweringMode::Variable)
         .with_type_param_mode(ParamLoweringMode::Variable);
     let params = data.params.iter().map(|tr| ctx_params.lower_ty(tr)).collect::<Vec<_>>();
-    let ctx_ret = TyLoweringContext::new(db, &resolver)
+    let ctx_ret = TyLoweringContext::new(db, &resolver, def.into())
         .with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
         .with_type_param_mode(ParamLoweringMode::Variable);
     let ret = ctx_ret.lower_ty(&data.ret_type);
@@ -1683,8 +1695,8 @@ fn type_for_const(db: &dyn HirDatabase, def: ConstId) -> Binders<Ty> {
     let data = db.const_data(def);
     let generics = generics(db.upcast(), def.into());
     let resolver = def.resolver(db.upcast());
-    let ctx =
-        TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
+    let ctx = TyLoweringContext::new(db, &resolver, def.into())
+        .with_type_param_mode(ParamLoweringMode::Variable);
 
     make_binders(db, &generics, ctx.lower_ty(&data.type_ref))
 }
@@ -1693,7 +1705,7 @@ fn type_for_const(db: &dyn HirDatabase, def: ConstId) -> Binders<Ty> {
 fn type_for_static(db: &dyn HirDatabase, def: StaticId) -> Binders<Ty> {
     let data = db.static_data(def);
     let resolver = def.resolver(db.upcast());
-    let ctx = TyLoweringContext::new(db, &resolver);
+    let ctx = TyLoweringContext::new(db, &resolver, def.into());
 
     Binders::empty(Interner, ctx.lower_ty(&data.type_ref))
 }
@@ -1702,8 +1714,8 @@ fn fn_sig_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> PolyFnS
     let struct_data = db.struct_data(def);
     let fields = struct_data.variant_data.fields();
     let resolver = def.resolver(db.upcast());
-    let ctx =
-        TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
+    let ctx = TyLoweringContext::new(db, &resolver, AdtId::from(def).into())
+        .with_type_param_mode(ParamLoweringMode::Variable);
     let params = fields.iter().map(|(_, field)| ctx.lower_ty(&field.type_ref)).collect::<Vec<_>>();
     let (ret, binders) = type_for_adt(db, def.into()).into_value_and_skipped_binders();
     Binders::new(binders, CallableSig::from_params_and_return(params, ret, false, Safety::Safe))
@@ -1715,7 +1727,7 @@ fn type_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> Binders<T
     if let StructKind::Unit = struct_data.variant_data.kind() {
         return type_for_adt(db, def.into());
     }
-    let generics = generics(db.upcast(), def.into());
+    let generics = generics(db.upcast(), AdtId::from(def).into());
     let substs = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST);
     make_binders(
         db,
@@ -1729,8 +1741,8 @@ fn fn_sig_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId)
     let var_data = &enum_data.variants[def.local_id];
     let fields = var_data.variant_data.fields();
     let resolver = def.parent.resolver(db.upcast());
-    let ctx =
-        TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
+    let ctx = TyLoweringContext::new(db, &resolver, DefWithBodyId::VariantId(def).into())
+        .with_type_param_mode(ParamLoweringMode::Variable);
     let params = fields.iter().map(|(_, field)| ctx.lower_ty(&field.type_ref)).collect::<Vec<_>>();
     let (ret, binders) = type_for_adt(db, def.parent.into()).into_value_and_skipped_binders();
     Binders::new(binders, CallableSig::from_params_and_return(params, ret, false, Safety::Safe))
@@ -1762,8 +1774,8 @@ fn type_for_adt(db: &dyn HirDatabase, adt: AdtId) -> Binders<Ty> {
 fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders<Ty> {
     let generics = generics(db.upcast(), t.into());
     let resolver = t.resolver(db.upcast());
-    let ctx =
-        TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
+    let ctx = TyLoweringContext::new(db, &resolver, t.into())
+        .with_type_param_mode(ParamLoweringMode::Variable);
     if db.type_alias_data(t).is_extern {
         Binders::empty(Interner, TyKind::Foreign(crate::to_foreign_def_id(t)).intern(Interner))
     } else {
@@ -1884,8 +1896,8 @@ pub(crate) fn impl_self_ty_query(db: &dyn HirDatabase, impl_id: ImplId) -> Binde
         "impl_self_ty_query({impl_id:?} -> {impl_loc:?} -> {impl_data:?})"
     ));
     let generics = generics(db.upcast(), impl_id.into());
-    let ctx =
-        TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
+    let ctx = TyLoweringContext::new(db, &resolver, impl_id.into())
+        .with_type_param_mode(ParamLoweringMode::Variable);
     make_binders(db, &generics, ctx.lower_ty(&impl_data.self_ty))
 }
 
@@ -1894,7 +1906,7 @@ pub(crate) fn const_param_ty_query(db: &dyn HirDatabase, def: ConstParamId) -> T
     let parent_data = db.generic_params(def.parent());
     let data = &parent_data.type_or_consts[def.local_id()];
     let resolver = def.parent().resolver(db.upcast());
-    let ctx = TyLoweringContext::new(db, &resolver);
+    let ctx = TyLoweringContext::new(db, &resolver, def.parent().into());
     match data {
         TypeOrConstParamData::TypeParamData(_) => {
             never!();
@@ -1920,8 +1932,8 @@ pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option<
     let _cx = stdx::panic_context::enter(format!(
         "impl_trait_query({impl_id:?} -> {impl_loc:?} -> {impl_data:?})"
     ));
-    let ctx =
-        TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
+    let ctx = TyLoweringContext::new(db, &resolver, impl_id.into())
+        .with_type_param_mode(ParamLoweringMode::Variable);
     let (self_ty, binders) = db.impl_self_ty(impl_id).into_value_and_skipped_binders();
     let target_trait = impl_data.target_trait.as_ref()?;
     Some(Binders::new(binders, ctx.lower_trait_ref(target_trait, Some(self_ty))?))
@@ -1934,7 +1946,7 @@ pub(crate) fn return_type_impl_traits(
     // FIXME unify with fn_sig_for_fn instead of doing lowering twice, maybe
     let data = db.function_data(def);
     let resolver = def.resolver(db.upcast());
-    let ctx_ret = TyLoweringContext::new(db, &resolver)
+    let ctx_ret = TyLoweringContext::new(db, &resolver, def.into())
         .with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
         .with_type_param_mode(ParamLoweringMode::Variable);
     let _ret = ctx_ret.lower_ty(&data.ret_type);
@@ -1969,7 +1981,7 @@ pub(crate) fn generic_arg_to_chalk<'a, T>(
     arg: &'a GenericArg,
     this: &mut T,
     for_type: impl FnOnce(&mut T, &TypeRef) -> Ty + 'a,
-    for_const: impl FnOnce(&mut T, &ConstRefOrPath, Ty) -> Const + 'a,
+    for_const: impl FnOnce(&mut T, &ConstRef, Ty) -> Const + 'a,
 ) -> Option<crate::GenericArg> {
     let kind = match kind_id {
         Either::Left(_) => ParamKind::Type,
@@ -1997,7 +2009,7 @@ pub(crate) fn generic_arg_to_chalk<'a, T>(
                 let p = p.mod_path()?;
                 if p.kind == PathKind::Plain {
                     if let [n] = p.segments() {
-                        let c = ConstRefOrPath::Path(n.clone());
+                        let c = ConstRef::Path(n.clone());
                         return Some(
                             GenericArgData::Const(for_const(this, &c, c_ty)).intern(Interner),
                         );
@@ -2013,15 +2025,16 @@ pub(crate) fn generic_arg_to_chalk<'a, T>(
 pub(crate) fn const_or_path_to_chalk(
     db: &dyn HirDatabase,
     resolver: &Resolver,
+    owner: TypeOwnerId,
     expected_ty: Ty,
-    value: &ConstRefOrPath,
+    value: &ConstRef,
     mode: ParamLoweringMode,
     args: impl FnOnce() -> Generics,
     debruijn: DebruijnIndex,
 ) -> Const {
     match value {
-        ConstRefOrPath::Scalar(s) => intern_const_ref(db, s, expected_ty, resolver.krate()),
-        ConstRefOrPath::Path(n) => {
+        ConstRef::Scalar(s) => intern_const_ref(db, s, expected_ty, resolver.krate()),
+        ConstRef::Path(n) => {
             let path = ModPath::from_segments(PathKind::Plain, Some(n.clone()));
             path_to_const(
                 db,
@@ -2034,6 +2047,26 @@ pub(crate) fn const_or_path_to_chalk(
             )
             .unwrap_or_else(|| unknown_const(expected_ty))
         }
+        &ConstRef::Complex(it) => {
+            let crate_data = &db.crate_graph()[owner.module(db.upcast()).krate()];
+            if crate_data.env.get("__ra_is_test_fixture").is_none() && crate_data.origin.is_local()
+            {
+                // FIXME: current `InTypeConstId` is very unstable, so we only use it in non local crate
+                // that are unlikely to be edited.
+                return unknown_const(expected_ty);
+            }
+            let c = db
+                .intern_in_type_const(InTypeConstLoc {
+                    id: it,
+                    owner,
+                    thing: Box::new(InTypeConstIdMetadata(expected_ty.clone())),
+                })
+                .into();
+            intern_const_scalar(
+                ConstScalar::UnevaluatedConst(c, Substitution::empty(Interner)),
+                expected_ty,
+            )
+        }
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
index 6fa3d1351a9..ab6430e8f19 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
@@ -570,7 +570,7 @@ impl ReceiverAdjustments {
                         .intern(Interner);
                     }
                 }
-                never!("unsize_array with non-reference-to-array {:?}", ty);
+                // FIXME: report diagnostic if array unsizing happens without indirection.
                 ty
             };
             adjust.push(Adjustment {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs
index a0ea1cc5ef7..a5dd0182eb6 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs
@@ -240,10 +240,14 @@ fn place_case(db: &dyn HirDatabase, body: &MirBody, lvalue: &Place) -> Projectio
 /// Returns a map from basic blocks to the set of locals that might be ever initialized before
 /// the start of the block. Only `StorageDead` can remove something from this map, and we ignore
 /// `Uninit` and `drop` and similar after initialization.
-fn ever_initialized_map(body: &MirBody) -> ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>> {
+fn ever_initialized_map(
+    db: &dyn HirDatabase,
+    body: &MirBody,
+) -> ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>> {
     let mut result: ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>> =
         body.basic_blocks.iter().map(|x| (x.0, ArenaMap::default())).collect();
     fn dfs(
+        db: &dyn HirDatabase,
         body: &MirBody,
         b: BasicBlockId,
         l: LocalId,
@@ -267,7 +271,7 @@ fn ever_initialized_map(body: &MirBody) -> ArenaMap<BasicBlockId, ArenaMap<Local
             }
         }
         let Some(terminator) = &block.terminator else {
-            never!("Terminator should be none only in construction");
+            never!("Terminator should be none only in construction.\nThe body:\n{}", body.pretty_print(db));
             return;
         };
         let targets = match &terminator.kind {
@@ -299,18 +303,18 @@ fn ever_initialized_map(body: &MirBody) -> ArenaMap<BasicBlockId, ArenaMap<Local
         for target in targets {
             if !result[target].contains_idx(l) || !result[target][l] && is_ever_initialized {
                 result[target].insert(l, is_ever_initialized);
-                dfs(body, target, l, result);
+                dfs(db, body, target, l, result);
             }
         }
     }
     for &l in &body.param_locals {
         result[body.start_block].insert(l, true);
-        dfs(body, body.start_block, l, &mut result);
+        dfs(db, body, body.start_block, l, &mut result);
     }
     for l in body.locals.iter().map(|x| x.0) {
         if !result[body.start_block].contains_idx(l) {
             result[body.start_block].insert(l, false);
-            dfs(body, body.start_block, l, &mut result);
+            dfs(db, body, body.start_block, l, &mut result);
         }
     }
     result
@@ -326,7 +330,7 @@ fn mutability_of_locals(
         MutabilityReason::Mut { spans } => spans.push(span),
         x @ MutabilityReason::Not => *x = MutabilityReason::Mut { spans: vec![span] },
     };
-    let ever_init_maps = ever_initialized_map(body);
+    let ever_init_maps = ever_initialized_map(db, body);
     for (block_id, mut ever_init_map) in ever_init_maps.into_iter() {
         let block = &body.basic_blocks[block_id];
         for statement in &block.statements {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
index ce14f6dbad5..9acf9d39e56 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
@@ -77,7 +77,7 @@ impl VTableMap {
         id
     }
 
-    fn ty(&self, id: usize) -> Result<&Ty> {
+    pub(crate) fn ty(&self, id: usize) -> Result<&Ty> {
         self.id_to_ty.get(id).ok_or(MirEvalError::InvalidVTableId(id))
     }
 
@@ -1571,16 +1571,24 @@ impl Evaluator<'_> {
                         }
                         None => {
                             let mut check_inner = None;
+                            let (addr, meta) = bytes.split_at(bytes.len() / 2);
                             let element_size = match t.kind(Interner) {
                                 TyKind::Str => 1,
                                 TyKind::Slice(t) => {
                                     check_inner = Some(t);
                                     this.size_of_sized(t, locals, "slice inner type")?
                                 }
-                                _ => return Ok(()), // FIXME: support other kind of unsized types
+                                TyKind::Dyn(_) => {
+                                    let t = this.vtable_map.ty_of_bytes(meta)?;
+                                    check_inner = Some(t);
+                                    this.size_of_sized(t, locals, "dyn concrete type")?
+                                }
+                                _ => return Ok(()),
+                            };
+                            let count = match t.kind(Interner) {
+                                TyKind::Dyn(_) => 1,
+                                _ => from_bytes!(usize, meta),
                             };
-                            let (addr, meta) = bytes.split_at(bytes.len() / 2);
-                            let count = from_bytes!(usize, meta);
                             let size = element_size * count;
                             let addr = Address::from_bytes(addr)?;
                             let b = this.read_memory(addr, size)?;
@@ -1588,7 +1596,7 @@ impl Evaluator<'_> {
                             if let Some(ty) = check_inner {
                                 for i in 0..count {
                                     let offset = element_size * i;
-                                    rec(this, &b[offset..offset + element_size], ty, locals, mm)?;
+                                    rec(this, &b[offset..offset + element_size], &ty, locals, mm)?;
                                 }
                             }
                         }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
index aad1a82f298..2cb29b4ab91 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
@@ -1853,7 +1853,7 @@ pub fn mir_body_for_closure_query(
         .result
         .binding_locals
         .into_iter()
-        .filter(|x| ctx.body[x.0].owner == Some(expr))
+        .filter(|it| ctx.body.binding_owners.get(&it.0).copied() == Some(expr))
         .collect();
     if let Some(err) = err {
         return Err(MirLowerError::UnresolvedUpvar(err));
@@ -1876,6 +1876,7 @@ pub fn mir_body_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Result<Arc<Mi
         DefWithBodyId::VariantId(it) => {
             db.enum_data(it.parent).variants[it.local_id].name.display(db.upcast()).to_string()
         }
+        DefWithBodyId::InTypeConstId(it) => format!("in type const {it:?}"),
     });
     let body = db.body(def);
     let infer = db.infer(def);
@@ -1908,10 +1909,11 @@ pub fn lower_to_mir(
     // 0 is return local
     ctx.result.locals.alloc(Local { ty: ctx.expr_ty_after_adjustments(root_expr) });
     let binding_picker = |b: BindingId| {
+        let owner = ctx.body.binding_owners.get(&b).copied();
         if root_expr == body.body_expr {
-            body[b].owner.is_none()
+            owner.is_none()
         } else {
-            body[b].owner == Some(root_expr)
+            owner == Some(root_expr)
         }
     };
     // 1 to param_len is for params
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs
index 58662b01b99..ac23e77bd2b 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs
@@ -60,6 +60,9 @@ impl MirBody {
                 let data = db.enum_data(id.parent);
                 w!(this, "enum {} = ", data.name.display(db.upcast()));
             }
+            hir_def::DefWithBodyId::InTypeConstId(id) => {
+                w!(this, "in type const {id:?} = ");
+            }
         });
         ctx.result
     }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
index 2db04024b7b..85714128006 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
@@ -146,6 +146,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
             let loc = db.lookup_intern_enum(it.parent);
             loc.source(&db).value.syntax().text_range().start()
         }
+        DefWithBodyId::InTypeConstId(it) => it.source(&db).syntax().text_range().start(),
     });
     let mut unexpected_type_mismatches = String::new();
     for def in defs {
@@ -391,6 +392,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
             let loc = db.lookup_intern_enum(it.parent);
             loc.source(&db).value.syntax().text_range().start()
         }
+        DefWithBodyId::InTypeConstId(it) => it.source(&db).syntax().text_range().start(),
     });
     for def in defs {
         let (body, source_map) = db.body_with_source_map(def);
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
index f18c953a7af..047900a324e 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
@@ -1955,3 +1955,26 @@ impl Inner<1> {
         "#,
     );
 }
+
+#[test]
+fn dont_crash_on_slice_unsizing() {
+    check_no_mismatches(
+        r#"
+//- minicore: slice, unsize, coerce_unsized
+trait Tr {
+    fn f(self);
+}
+
+impl Tr for [i32] {
+    fn f(self) {
+        let t;
+        x(t);
+    }
+}
+
+fn x(a: [i32; 4]) {
+    let b = a.f();
+}
+        "#,
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
index 3ece40486dd..a0ff628435f 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
@@ -1829,6 +1829,38 @@ impl Foo for u8 {
 }
 
 #[test]
+fn const_eval_in_function_signature() {
+    check_types(
+        r#"
+const fn foo() -> usize {
+    5
+}
+
+fn f() -> [u8; foo()] {
+    loop {}
+}
+
+fn main() {
+    let t = f();
+      //^ [u8; 5]
+}"#,
+    );
+    check_types(
+        r#"
+//- minicore: default, builtin_impls
+fn f() -> [u8; Default::default()] {
+    loop {}
+}
+
+fn main() {
+    let t = f();
+      //^ [u8; 0]
+}
+    "#,
+    );
+}
+
+#[test]
 fn shadowing_primitive_with_inner_items() {
     check_types(
         r#"
@@ -3465,6 +3497,22 @@ fn func() {
     );
 }
 
+#[test]
+fn pointee_trait() {
+    check_types(
+        r#"
+//- minicore: pointee
+use core::ptr::Pointee;
+fn func() {
+    let x: <u8 as Pointee>::Metadata;
+      //^ ()
+    let x: <[u8] as Pointee>::Metadata;
+      //^ usize
+}
+    "#,
+    );
+}
+
 // FIXME
 #[test]
 fn castable_to() {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
index 829a6ab189e..97ae732a904 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
@@ -4335,8 +4335,9 @@ fn derive_macro_bounds() {
         #[derive(Clone)]
         struct AssocGeneric<T: Tr>(T::Assoc);
 
-        #[derive(Clone)]
-        struct AssocGeneric2<T: Tr>(<T as Tr>::Assoc);
+        // Currently rustc does not accept this.
+        // #[derive(Clone)]
+        // struct AssocGeneric2<T: Tr>(<T as Tr>::Assoc);
 
         #[derive(Clone)]
         struct AssocGeneric3<T: Tr>(Generic<T::Assoc>);
@@ -4361,9 +4362,8 @@ fn derive_macro_bounds() {
             let x: &AssocGeneric<Copy> = &AssocGeneric(NotCopy);
             let x = x.clone();
               //^ &AssocGeneric<Copy>
-            let x: &AssocGeneric2<Copy> = &AssocGeneric2(NotCopy);
-            let x = x.clone();
-              //^ &AssocGeneric2<Copy>
+            // let x: &AssocGeneric2<Copy> = &AssocGeneric2(NotCopy);
+            // let x = x.clone();
             let x: &AssocGeneric3<Copy> = &AssocGeneric3(Generic(NotCopy));
             let x = x.clone();
               //^ &AssocGeneric3<Copy>
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs
index 681d087ede6..3636580630d 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs
@@ -1,7 +1,7 @@
 //! Helper functions for working with def, which don't need to be a separate
 //! query, but can't be computed directly from `*Data` (ie, which need a `db`).
 
-use std::iter;
+use std::{hash::Hash, iter};
 
 use base_db::CrateId;
 use chalk_ir::{
@@ -20,7 +20,8 @@ use hir_def::{
     resolver::{HasResolver, TypeNs},
     type_ref::{TraitBoundModifier, TypeRef},
     ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId, ItemContainerId,
-    LocalEnumVariantId, Lookup, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId,
+    LocalEnumVariantId, Lookup, OpaqueInternableThing, TraitId, TypeAliasId, TypeOrConstParamId,
+    TypeParamId,
 };
 use hir_expand::name::Name;
 use intern::Interned;
@@ -464,3 +465,28 @@ pub(crate) fn detect_variant_from_bytes<'a>(
     };
     Some((var_id, var_layout))
 }
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub(crate) struct InTypeConstIdMetadata(pub(crate) Ty);
+
+impl OpaqueInternableThing for InTypeConstIdMetadata {
+    fn dyn_hash(&self, mut state: &mut dyn std::hash::Hasher) {
+        self.hash(&mut state);
+    }
+
+    fn dyn_eq(&self, other: &dyn OpaqueInternableThing) -> bool {
+        other.as_any().downcast_ref::<Self>().map_or(false, |x| self == x)
+    }
+
+    fn dyn_clone(&self) -> Box<dyn OpaqueInternableThing> {
+        Box::new(self.clone())
+    }
+
+    fn as_any(&self) -> &dyn std::any::Any {
+        self
+    }
+
+    fn box_any(&self) -> Box<dyn std::any::Any> {
+        Box::new(self.clone())
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/hir/src/from_id.rs b/src/tools/rust-analyzer/crates/hir/src/from_id.rs
index 883e6a29b06..de23902199f 100644
--- a/src/tools/rust-analyzer/crates/hir/src/from_id.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/from_id.rs
@@ -40,6 +40,7 @@ from_id![
     (hir_def::TraitAliasId, crate::TraitAlias),
     (hir_def::StaticId, crate::Static),
     (hir_def::ConstId, crate::Const),
+    (hir_def::InTypeConstId, crate::InTypeConst),
     (hir_def::FunctionId, crate::Function),
     (hir_def::ImplId, crate::Impl),
     (hir_def::TypeOrConstParamId, crate::TypeOrConstParam),
@@ -144,6 +145,7 @@ impl From<DefWithBody> for DefWithBodyId {
             DefWithBody::Static(it) => DefWithBodyId::StaticId(it.id),
             DefWithBody::Const(it) => DefWithBodyId::ConstId(it.id),
             DefWithBody::Variant(it) => DefWithBodyId::VariantId(it.into()),
+            DefWithBody::InTypeConst(it) => DefWithBodyId::InTypeConstId(it.id),
         }
     }
 }
@@ -155,6 +157,7 @@ impl From<DefWithBodyId> for DefWithBody {
             DefWithBodyId::StaticId(it) => DefWithBody::Static(it.into()),
             DefWithBodyId::ConstId(it) => DefWithBody::Const(it.into()),
             DefWithBodyId::VariantId(it) => DefWithBody::Variant(it.into()),
+            DefWithBodyId::InTypeConstId(it) => DefWithBody::InTypeConst(it.into()),
         }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index 5926d865421..6df625380ff 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -52,9 +52,10 @@ use hir_def::{
     resolver::{HasResolver, Resolver},
     src::HasSource as _,
     AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, DefWithBodyId, EnumId,
-    EnumVariantId, FunctionId, GenericDefId, HasModule, ImplId, ItemContainerId, LifetimeParamId,
-    LocalEnumVariantId, LocalFieldId, Lookup, MacroExpander, MacroId, ModuleId, StaticId, StructId,
-    TraitAliasId, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId,
+    EnumVariantId, FunctionId, GenericDefId, HasModule, ImplId, InTypeConstId, ItemContainerId,
+    LifetimeParamId, LocalEnumVariantId, LocalFieldId, Lookup, MacroExpander, MacroId, ModuleId,
+    StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId,
+    UnionId,
 };
 use hir_expand::{name::name, MacroCallKind};
 use hir_ty::{
@@ -202,7 +203,7 @@ impl Crate {
 
     pub fn root_module(self, db: &dyn HirDatabase) -> Module {
         let def_map = db.crate_def_map(self.id);
-        Module { id: def_map.module_id(DefMap::ROOT) }
+        Module { id: def_map.crate_root().into() }
     }
 
     pub fn modules(self, db: &dyn HirDatabase) -> Vec<Module> {
@@ -475,7 +476,7 @@ impl Module {
     /// in the module tree of any target in `Cargo.toml`.
     pub fn crate_root(self, db: &dyn HirDatabase) -> Module {
         let def_map = db.crate_def_map(self.id.krate());
-        Module { id: def_map.module_id(DefMap::ROOT) }
+        Module { id: def_map.crate_root().into() }
     }
 
     pub fn is_crate_root(self) -> bool {
@@ -1375,8 +1376,9 @@ pub enum DefWithBody {
     Static(Static),
     Const(Const),
     Variant(Variant),
+    InTypeConst(InTypeConst),
 }
-impl_from!(Function, Const, Static, Variant for DefWithBody);
+impl_from!(Function, Const, Static, Variant, InTypeConst for DefWithBody);
 
 impl DefWithBody {
     pub fn module(self, db: &dyn HirDatabase) -> Module {
@@ -1385,6 +1387,7 @@ impl DefWithBody {
             DefWithBody::Function(f) => f.module(db),
             DefWithBody::Static(s) => s.module(db),
             DefWithBody::Variant(v) => v.module(db),
+            DefWithBody::InTypeConst(c) => c.module(db),
         }
     }
 
@@ -1394,6 +1397,7 @@ impl DefWithBody {
             DefWithBody::Static(s) => Some(s.name(db)),
             DefWithBody::Const(c) => c.name(db),
             DefWithBody::Variant(v) => Some(v.name(db)),
+            DefWithBody::InTypeConst(_) => None,
         }
     }
 
@@ -1404,6 +1408,11 @@ impl DefWithBody {
             DefWithBody::Static(it) => it.ty(db),
             DefWithBody::Const(it) => it.ty(db),
             DefWithBody::Variant(it) => it.parent.variant_body_ty(db),
+            DefWithBody::InTypeConst(it) => Type::new_with_resolver_inner(
+                db,
+                &DefWithBodyId::from(it.id).resolver(db.upcast()),
+                TyKind::Error.intern(Interner),
+            ),
         }
     }
 
@@ -1413,6 +1422,7 @@ impl DefWithBody {
             DefWithBody::Static(it) => it.id.into(),
             DefWithBody::Const(it) => it.id.into(),
             DefWithBody::Variant(it) => it.into(),
+            DefWithBody::InTypeConst(it) => it.id.into(),
         }
     }
 
@@ -1797,6 +1807,8 @@ impl DefWithBody {
             DefWithBody::Static(it) => it.into(),
             DefWithBody::Const(it) => it.into(),
             DefWithBody::Variant(it) => it.into(),
+            // FIXME: don't ignore diagnostics for in type const
+            DefWithBody::InTypeConst(_) => return,
         };
         for diag in hir_ty::diagnostics::incorrect_case(db, krate, def.into()) {
             acc.push(diag.into())
@@ -2086,6 +2098,17 @@ impl HasVisibility for Function {
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct InTypeConst {
+    pub(crate) id: InTypeConstId,
+}
+
+impl InTypeConst {
+    pub fn module(self, db: &dyn HirDatabase) -> Module {
+        Module { id: self.id.lookup(db.upcast()).owner.module(db.upcast()) }
+    }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct Const {
     pub(crate) id: ConstId,
 }
@@ -2515,7 +2538,7 @@ impl AsAssocItem for DefWithBody {
         match self {
             DefWithBody::Function(it) => it.as_assoc_item(db),
             DefWithBody::Const(it) => it.as_assoc_item(db),
-            DefWithBody::Static(_) | DefWithBody::Variant(_) => None,
+            DefWithBody::Static(_) | DefWithBody::Variant(_) | DefWithBody::InTypeConst(_) => None,
         }
     }
 }
@@ -2641,14 +2664,22 @@ impl GenericDef {
                 Either::Right(x) => GenericParam::TypeParam(x),
             }
         });
-        let lt_params = generics
+        self.lifetime_params(db)
+            .into_iter()
+            .map(GenericParam::LifetimeParam)
+            .chain(ty_params)
+            .collect()
+    }
+
+    pub fn lifetime_params(self, db: &dyn HirDatabase) -> Vec<LifetimeParam> {
+        let generics = db.generic_params(self.into());
+        generics
             .lifetimes
             .iter()
             .map(|(local_id, _)| LifetimeParam {
                 id: LifetimeParamId { parent: self.into(), local_id },
             })
-            .map(GenericParam::LifetimeParam);
-        lt_params.chain(ty_params).collect()
+            .collect()
     }
 
     pub fn type_params(self, db: &dyn HirDatabase) -> Vec<TypeOrConstParam> {
@@ -2769,6 +2800,22 @@ impl Local {
     /// All definitions for this local. Example: `let (a$0, _) | (_, a$0) = x;`
     pub fn sources(self, db: &dyn HirDatabase) -> Vec<LocalSource> {
         let (body, source_map) = db.body_with_source_map(self.parent);
+        self.sources_(db, &body, &source_map).collect()
+    }
+
+    /// The leftmost definition for this local. Example: `let (a$0, _) | (_, a) = x;`
+    pub fn primary_source(self, db: &dyn HirDatabase) -> LocalSource {
+        let (body, source_map) = db.body_with_source_map(self.parent);
+        let src = self.sources_(db, &body, &source_map).next().unwrap();
+        src
+    }
+
+    fn sources_<'a>(
+        self,
+        db: &'a dyn HirDatabase,
+        body: &'a hir_def::body::Body,
+        source_map: &'a hir_def::body::BodySourceMap,
+    ) -> impl Iterator<Item = LocalSource> + 'a {
         body[self.binding_id]
             .definitions
             .iter()
@@ -2781,14 +2828,7 @@ impl Local {
                     Either::Right(it) => Either::Right(it.to_node(&root)),
                 })
             })
-            .map(|source| LocalSource { local: self, source })
-            .collect()
-    }
-
-    /// The leftmost definition for this local. Example: `let (a$0, _) | (_, a) = x;`
-    pub fn primary_source(self, db: &dyn HirDatabase) -> LocalSource {
-        let all_sources = self.sources(db);
-        all_sources.into_iter().next().unwrap()
+            .map(move |source| LocalSource { local: self, source })
     }
 }
 
@@ -3494,6 +3534,14 @@ impl Type {
         }
     }
 
+    pub fn is_scalar(&self) -> bool {
+        matches!(self.ty.kind(Interner), TyKind::Scalar(_))
+    }
+
+    pub fn is_tuple(&self) -> bool {
+        matches!(self.ty.kind(Interner), TyKind::Tuple(..))
+    }
+
     pub fn remove_ref(&self) -> Option<Type> {
         match &self.ty.kind(Interner) {
             TyKind::Ref(.., ty) => Some(self.derived(ty.clone())),
@@ -3779,14 +3827,16 @@ impl Type {
         }
     }
 
-    pub fn autoderef<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Type> + 'a {
+    /// Returns types that this type dereferences to (including this type itself). The returned
+    /// iterator won't yield the same type more than once even if the deref chain contains a cycle.
+    pub fn autoderef(&self, db: &dyn HirDatabase) -> impl Iterator<Item = Type> + '_ {
         self.autoderef_(db).map(move |ty| self.derived(ty))
     }
 
-    fn autoderef_<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Ty> + 'a {
+    fn autoderef_(&self, db: &dyn HirDatabase) -> impl Iterator<Item = Ty> {
         // There should be no inference vars in types passed here
         let canonical = hir_ty::replace_errors_with_variables(&self.ty);
-        autoderef(db, self.env.clone(), canonical).map(|canonical| canonical.value)
+        autoderef(db, self.env.clone(), canonical)
     }
 
     // This would be nicer if it just returned an iterator, but that runs into
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
index 2d2b00b147e..5a76a9185a2 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
@@ -483,10 +483,6 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
         self.imp.scope_at_offset(node, offset)
     }
 
-    pub fn scope_for_def(&self, def: Trait) -> SemanticsScope<'db> {
-        self.imp.scope_for_def(def)
-    }
-
     pub fn assert_contains_node(&self, node: &SyntaxNode) {
         self.imp.assert_contains_node(node)
     }
@@ -1074,8 +1070,12 @@ impl<'db> SemanticsImpl<'db> {
     fn resolve_type(&self, ty: &ast::Type) -> Option<Type> {
         let analyze = self.analyze(ty.syntax())?;
         let ctx = LowerCtx::with_file_id(self.db.upcast(), analyze.file_id);
-        let ty = hir_ty::TyLoweringContext::new(self.db, &analyze.resolver)
-            .lower_ty(&crate::TypeRef::from_ast(&ctx, ty.clone()));
+        let ty = hir_ty::TyLoweringContext::new(
+            self.db,
+            &analyze.resolver,
+            analyze.resolver.module().into(),
+        )
+        .lower_ty(&crate::TypeRef::from_ast(&ctx, ty.clone()));
         Some(Type::new_with_resolver(self.db, &analyze.resolver, ty))
     }
 
@@ -1307,12 +1307,6 @@ impl<'db> SemanticsImpl<'db> {
         )
     }
 
-    fn scope_for_def(&self, def: Trait) -> SemanticsScope<'db> {
-        let file_id = self.db.lookup_intern_trait(def.id).id.file_id();
-        let resolver = def.id.resolver(self.db.upcast());
-        SemanticsScope { db: self.db, file_id, resolver }
-    }
-
     fn source<Def: HasSource>(&self, def: Def) -> Option<InFile<Def::Ast>>
     where
         Def::Ast: AstNode,
diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
index 1374fa332c6..ecb1b306a66 100644
--- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
@@ -38,8 +38,8 @@ use hir_ty::{
         UnsafeExpr,
     },
     lang_items::lang_items_for_bin_op,
-    method_resolution::{self},
-    Adjustment, InferenceResult, Interner, Substitution, Ty, TyExt, TyKind, TyLoweringContext,
+    method_resolution, Adjustment, InferenceResult, Interner, Substitution, Ty, TyExt, TyKind,
+    TyLoweringContext,
 };
 use itertools::Itertools;
 use smallvec::SmallVec;
@@ -978,7 +978,8 @@ fn resolve_hir_path_(
     let types = || {
         let (ty, unresolved) = match path.type_anchor() {
             Some(type_ref) => {
-                let (_, res) = TyLoweringContext::new(db, resolver).lower_ty_ext(type_ref);
+                let (_, res) = TyLoweringContext::new(db, resolver, resolver.module().into())
+                    .lower_ty_ext(type_ref);
                 res.map(|ty_ns| (ty_ns, path.segments().first()))
             }
             None => {
diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs
index 207e8206c92..43d957412bc 100644
--- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs
@@ -233,6 +233,7 @@ impl<'a> SymbolCollector<'a> {
             DefWithBodyId::VariantId(id) => {
                 Some(self.db.enum_data(id.parent).variants[id.local_id].name.to_smol_str())
             }
+            DefWithBodyId::InTypeConstId(_) => Some("in type const".into()),
         }
     }
 
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs
index 6340feda452..d07c6372628 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs
@@ -1,13 +1,9 @@
 use hir::HasSource;
-use ide_db::syntax_helpers::insert_whitespace_into_node::insert_ws_into;
 use syntax::ast::{self, make, AstNode};
 
 use crate::{
     assist_context::{AssistContext, Assists},
-    utils::{
-        add_trait_assoc_items_to_impl, filter_assoc_items, gen_trait_fn_body, render_snippet,
-        Cursor, DefaultMethods,
-    },
+    utils::{add_trait_assoc_items_to_impl, filter_assoc_items, gen_trait_fn_body, DefaultMethods},
     AssistId, AssistKind,
 };
 
@@ -130,50 +126,36 @@ fn add_missing_impl_members_inner(
     }
 
     let target = impl_def.syntax().text_range();
-    acc.add(AssistId(assist_id, AssistKind::QuickFix), label, target, |builder| {
-        let missing_items = missing_items
-            .into_iter()
-            .map(|it| {
-                if ctx.sema.hir_file_for(it.syntax()).is_macro() {
-                    if let Some(it) = ast::AssocItem::cast(insert_ws_into(it.syntax().clone())) {
-                        return it;
-                    }
-                }
-                it.clone_for_update()
-            })
-            .collect();
-        let (new_impl_def, first_new_item) = add_trait_assoc_items_to_impl(
+    acc.add(AssistId(assist_id, AssistKind::QuickFix), label, target, |edit| {
+        let new_impl_def = edit.make_mut(impl_def.clone());
+        let first_new_item = add_trait_assoc_items_to_impl(
             &ctx.sema,
-            missing_items,
+            &missing_items,
             trait_,
-            impl_def.clone(),
+            &new_impl_def,
             target_scope,
         );
-        match ctx.config.snippet_cap {
-            None => builder.replace(target, new_impl_def.to_string()),
-            Some(cap) => {
-                let mut cursor = Cursor::Before(first_new_item.syntax());
-                let placeholder;
-                if let DefaultMethods::No = mode {
-                    if let ast::AssocItem::Fn(func) = &first_new_item {
-                        if try_gen_trait_body(ctx, func, trait_ref, &impl_def).is_none() {
-                            if let Some(m) =
-                                func.syntax().descendants().find_map(ast::MacroCall::cast)
-                            {
-                                if m.syntax().text() == "todo!()" {
-                                    placeholder = m;
-                                    cursor = Cursor::Replace(placeholder.syntax());
-                                }
+
+        if let Some(cap) = ctx.config.snippet_cap {
+            let mut placeholder = None;
+            if let DefaultMethods::No = mode {
+                if let ast::AssocItem::Fn(func) = &first_new_item {
+                    if try_gen_trait_body(ctx, func, trait_ref, &impl_def).is_none() {
+                        if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast)
+                        {
+                            if m.syntax().text() == "todo!()" {
+                                placeholder = Some(m);
                             }
                         }
                     }
                 }
-                builder.replace_snippet(
-                    cap,
-                    target,
-                    render_snippet(cap, new_impl_def.syntax(), cursor),
-                )
             }
+
+            if let Some(macro_call) = placeholder {
+                edit.add_placeholder_snippet(cap, macro_call);
+            } else {
+                edit.add_tabstop_before(cap, first_new_item);
+            };
         };
     })
 }
@@ -366,6 +348,125 @@ impl<U> Foo<U> for S {
     }
 
     #[test]
+    fn test_lifetime_substitution() {
+        check_assist(
+            add_missing_impl_members,
+            r#"
+pub trait Trait<'a, 'b, A, B, C> {
+    fn foo(&self, one: &'a A, anoter: &'b B) -> &'a C;
+}
+
+impl<'x, 'y, T, V, U> Trait<'x, 'y, T, V, U> for () {$0}"#,
+            r#"
+pub trait Trait<'a, 'b, A, B, C> {
+    fn foo(&self, one: &'a A, anoter: &'b B) -> &'a C;
+}
+
+impl<'x, 'y, T, V, U> Trait<'x, 'y, T, V, U> for () {
+    fn foo(&self, one: &'x T, anoter: &'y V) -> &'x U {
+        ${0:todo!()}
+    }
+}"#,
+        );
+    }
+
+    #[test]
+    fn test_lifetime_substitution_with_body() {
+        check_assist(
+            add_missing_default_members,
+            r#"
+pub trait Trait<'a, 'b, A, B, C: Default> {
+    fn foo(&self, _one: &'a A, _anoter: &'b B) -> (C, &'a i32) {
+        let value: &'a i32 = &0;
+        (C::default(), value)
+    }
+}
+
+impl<'x, 'y, T, V, U: Default> Trait<'x, 'y, T, V, U> for () {$0}"#,
+            r#"
+pub trait Trait<'a, 'b, A, B, C: Default> {
+    fn foo(&self, _one: &'a A, _anoter: &'b B) -> (C, &'a i32) {
+        let value: &'a i32 = &0;
+        (C::default(), value)
+    }
+}
+
+impl<'x, 'y, T, V, U: Default> Trait<'x, 'y, T, V, U> for () {
+    $0fn foo(&self, _one: &'x T, _anoter: &'y V) -> (U, &'x i32) {
+        let value: &'x i32 = &0;
+        (<U>::default(), value)
+    }
+}"#,
+        );
+    }
+
+    #[test]
+    fn test_const_substitution() {
+        check_assist(
+            add_missing_default_members,
+            r#"
+struct Bar<const: N: bool> {
+    bar: [i32, N]
+}
+
+trait Foo<const N: usize, T> {
+    fn get_n_sq(&self, arg: &T) -> usize { N * N }
+    fn get_array(&self, arg: Bar<N>) -> [i32; N] { [1; N] }
+}
+
+struct S<T> {
+    wrapped: T
+}
+
+impl<const X: usize, Y, Z> Foo<X, Z> for S<Y> {
+    $0
+}"#,
+            r#"
+struct Bar<const: N: bool> {
+    bar: [i32, N]
+}
+
+trait Foo<const N: usize, T> {
+    fn get_n_sq(&self, arg: &T) -> usize { N * N }
+    fn get_array(&self, arg: Bar<N>) -> [i32; N] { [1; N] }
+}
+
+struct S<T> {
+    wrapped: T
+}
+
+impl<const X: usize, Y, Z> Foo<X, Z> for S<Y> {
+    $0fn get_n_sq(&self, arg: &Z) -> usize { X * X }
+
+    fn get_array(&self, arg: Bar<X>) -> [i32; X] { [1; X] }
+}"#,
+        )
+    }
+
+    #[test]
+    fn test_const_substitution_2() {
+        check_assist(
+            add_missing_default_members,
+            r#"
+trait Foo<const N: usize, const M: usize, T> {
+    fn get_sum(&self, arg: &T) -> usize { N + M }
+}
+
+impl<X> Foo<42, {20 + 22}, X> for () {
+    $0
+}"#,
+            r#"
+trait Foo<const N: usize, const M: usize, T> {
+    fn get_sum(&self, arg: &T) -> usize { N + M }
+}
+
+impl<X> Foo<42, {20 + 22}, X> for () {
+    $0fn get_sum(&self, arg: &X) -> usize { 42 + {20 + 22} }
+}"#,
+        )
+    }
+
+    #[test]
     fn test_cursor_after_empty_impl_def() {
         check_assist(
             add_missing_impl_members,
@@ -747,6 +848,115 @@ impl Foo<T> for S<T> {
     }
 
     #[test]
+    fn test_qualify_generic_default_parameter() {
+        check_assist(
+            add_missing_impl_members,
+            r#"
+mod m {
+    pub struct S;
+    pub trait Foo<T = S> {
+        fn bar(&self, other: &T);
+    }
+}
+
+struct S;
+impl m::Foo for S { $0 }"#,
+            r#"
+mod m {
+    pub struct S;
+    pub trait Foo<T = S> {
+        fn bar(&self, other: &T);
+    }
+}
+
+struct S;
+impl m::Foo for S {
+    fn bar(&self, other: &m::S) {
+        ${0:todo!()}
+    }
+}"#,
+        )
+    }
+
+    #[test]
+    fn test_qualify_generic_default_parameter_2() {
+        check_assist(
+            add_missing_impl_members,
+            r#"
+mod m {
+    pub struct Wrapper<T, V> {
+        one: T,
+        another: V
+    };
+    pub struct S;
+    pub trait Foo<T = Wrapper<S, bool>> {
+        fn bar(&self, other: &T);
+    }
+}
+
+struct S;
+impl m::Foo for S { $0 }"#,
+            r#"
+mod m {
+    pub struct Wrapper<T, V> {
+        one: T,
+        another: V
+    };
+    pub struct S;
+    pub trait Foo<T = Wrapper<S, bool>> {
+        fn bar(&self, other: &T);
+    }
+}
+
+struct S;
+impl m::Foo for S {
+    fn bar(&self, other: &m::Wrapper<m::S, bool>) {
+        ${0:todo!()}
+    }
+}"#,
+        );
+    }
+
+    #[test]
+    fn test_qualify_generic_default_parameter_3() {
+        check_assist(
+            add_missing_impl_members,
+            r#"
+mod m {
+    pub struct Wrapper<T, V> {
+        one: T,
+        another: V
+    };
+    pub struct S;
+    pub trait Foo<T = S, V = Wrapper<T, S>> {
+        fn bar(&self, other: &V);
+    }
+}
+
+struct S;
+impl m::Foo for S { $0 }"#,
+            r#"
+mod m {
+    pub struct Wrapper<T, V> {
+        one: T,
+        another: V
+    };
+    pub struct S;
+    pub trait Foo<T = S, V = Wrapper<T, S>> {
+        fn bar(&self, other: &V);
+    }
+}
+
+struct S;
+impl m::Foo for S {
+    fn bar(&self, other: &m::Wrapper<m::S, m::S>) {
+        ${0:todo!()}
+    }
+}"#,
+        );
+    }
+
+    #[test]
     fn test_assoc_type_bounds_are_removed() {
         check_assist(
             add_missing_impl_members,
@@ -1683,4 +1893,77 @@ impl m::Foo for S {
 }"#,
         )
     }
+
+    #[test]
+    fn nested_macro_should_not_cause_crash() {
+        check_assist(
+            add_missing_impl_members,
+            r#"
+macro_rules! ty { () => { i32 } }
+trait SomeTrait { type Output; }
+impl SomeTrait for i32 { type Output = i64; }
+macro_rules! define_method {
+    () => {
+        fn method(&mut self, params: <ty!() as SomeTrait>::Output);
+    };
+}
+trait AnotherTrait { define_method!(); }
+impl $0AnotherTrait for () {
+}
+"#,
+            r#"
+macro_rules! ty { () => { i32 } }
+trait SomeTrait { type Output; }
+impl SomeTrait for i32 { type Output = i64; }
+macro_rules! define_method {
+    () => {
+        fn method(&mut self, params: <ty!() as SomeTrait>::Output);
+    };
+}
+trait AnotherTrait { define_method!(); }
+impl AnotherTrait for () {
+    $0fn method(&mut self,params: <ty!()as SomeTrait>::Output) {
+        todo!()
+    }
+}
+"#,
+        );
+    }
+
+    // FIXME: `T` in `ty!(T)` should be replaced by `PathTransform`.
+    #[test]
+    fn paths_in_nested_macro_should_get_transformed() {
+        check_assist(
+            add_missing_impl_members,
+            r#"
+macro_rules! ty { ($me:ty) => { $me } }
+trait SomeTrait { type Output; }
+impl SomeTrait for i32 { type Output = i64; }
+macro_rules! define_method {
+    ($t:ty) => {
+        fn method(&mut self, params: <ty!($t) as SomeTrait>::Output);
+    };
+}
+trait AnotherTrait<T: SomeTrait> { define_method!(T); }
+impl $0AnotherTrait<i32> for () {
+}
+"#,
+            r#"
+macro_rules! ty { ($me:ty) => { $me } }
+trait SomeTrait { type Output; }
+impl SomeTrait for i32 { type Output = i64; }
+macro_rules! define_method {
+    ($t:ty) => {
+        fn method(&mut self, params: <ty!($t) as SomeTrait>::Output);
+    };
+}
+trait AnotherTrait<T: SomeTrait> { define_method!(T); }
+impl AnotherTrait<i32> for () {
+    $0fn method(&mut self,params: <ty!(T)as SomeTrait>::Output) {
+        todo!()
+    }
+}
+"#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs
index b310c2db9fa..b6e7d6209c9 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs
@@ -1,6 +1,9 @@
 use either::Either;
 use ide_db::syntax_helpers::node_ext::walk_ty;
-use syntax::ast::{self, edit::IndentLevel, make, AstNode, HasGenericParams, HasName};
+use syntax::{
+    ast::{self, edit::IndentLevel, make, AstNode, HasGenericParams, HasName},
+    ted,
+};
 
 use crate::{AssistContext, AssistId, AssistKind, Assists};
 
@@ -34,14 +37,16 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) ->
         || item.syntax(),
         |impl_| impl_.as_ref().either(AstNode::syntax, AstNode::syntax),
     );
-    let insert_pos = node.text_range().start();
     let target = ty.syntax().text_range();
 
     acc.add(
         AssistId("extract_type_alias", AssistKind::RefactorExtract),
         "Extract type as type alias",
         target,
-        |builder| {
+        |edit| {
+            let node = edit.make_syntax_mut(node.clone());
+            let target_ty = edit.make_mut(ty.clone());
+
             let mut known_generics = match item.generic_param_list() {
                 Some(it) => it.generic_params().collect(),
                 None => Vec::new(),
@@ -56,27 +61,29 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) ->
             let generic_params =
                 generics.map(|it| make::generic_param_list(it.into_iter().cloned()));
 
+            // Replace original type with the alias
             let ty_args = generic_params
                 .as_ref()
                 .map_or(String::new(), |it| it.to_generic_args().to_string());
-            let replacement = format!("Type{ty_args}");
-            builder.replace(target, replacement);
-
-            let indent = IndentLevel::from_node(node);
-            let generic_params = generic_params.map_or(String::new(), |it| it.to_string());
-            match ctx.config.snippet_cap {
-                Some(cap) => {
-                    builder.insert_snippet(
-                        cap,
-                        insert_pos,
-                        format!("type $0Type{generic_params} = {ty};\n\n{indent}"),
-                    );
-                }
-                None => {
-                    builder.insert(
-                        insert_pos,
-                        format!("type Type{generic_params} = {ty};\n\n{indent}"),
-                    );
+            // FIXME: replace with a `ast::make` constructor
+            let new_ty = make::ty(&format!("Type{ty_args}")).clone_for_update();
+            ted::replace(target_ty.syntax(), new_ty.syntax());
+
+            // Insert new alias
+            let indent = IndentLevel::from_node(&node);
+            let ty_alias = make::ty_alias("Type", generic_params, None, None, Some((ty, None)))
+                .clone_for_update();
+            ted::insert_all(
+                ted::Position::before(node),
+                vec![
+                    ty_alias.syntax().clone().into(),
+                    make::tokens::whitespace(&format!("\n\n{indent}")).into(),
+                ],
+            );
+
+            if let Some(cap) = ctx.config.snippet_cap {
+                if let Some(name) = ty_alias.name() {
+                    edit.add_tabstop_before(cap, name);
                 }
             }
         },
@@ -151,7 +158,7 @@ fn collect_used_generics<'gp>(
                     .and_then(|lt| known_generics.iter().find(find_lifetime(&lt.text()))),
             ),
             ast::Type::ArrayType(ar) => {
-                if let Some(ast::Expr::PathExpr(p)) = ar.expr() {
+                if let Some(ast::Expr::PathExpr(p)) = ar.const_arg().and_then(|x| x.expr()) {
                     if let Some(path) = p.path() {
                         if let Some(name_ref) = path.as_single_name_ref() {
                             if let Some(param) = known_generics.iter().find(|gp| {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs
index 3667fc375b4..b68c766e647 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs
@@ -72,29 +72,27 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
         let krate = ty.krate(ctx.db());
         ty.iterate_assoc_items(ctx.db(), krate, |item| {
             if let hir::AssocItem::Function(f) = item {
+                let name = f.name(ctx.db());
                 if f.self_param(ctx.db()).is_some()
                     && f.is_visible_from(ctx.db(), current_module)
-                    && seen_names.insert(f.name(ctx.db()))
+                    && seen_names.insert(name.clone())
                 {
-                    methods.push(f)
+                    methods.push((name, f))
                 }
             }
             Option::<()>::None
         });
     }
-
-    for method in methods {
+    methods.sort_by(|(a, _), (b, _)| a.cmp(b));
+    for (name, method) in methods {
         let adt = ast::Adt::Struct(strukt.clone());
-        let name = method.name(ctx.db()).display(ctx.db()).to_string();
+        let name = name.display(ctx.db()).to_string();
         // if `find_struct_impl` returns None, that means that a function named `name` already exists.
-        let Some(impl_def) = find_struct_impl(ctx, &adt, &[name]) else { continue; };
+        let Some(impl_def) = find_struct_impl(ctx, &adt, std::slice::from_ref(&name)) else { continue; };
         acc.add_group(
             &GroupLabel("Generate delegate methods…".to_owned()),
             AssistId("generate_delegate_methods", AssistKind::Generate),
-            format!(
-                "Generate delegate for `{field_name}.{}()`",
-                method.name(ctx.db()).display(ctx.db())
-            ),
+            format!("Generate delegate for `{field_name}.{name}()`",),
             target,
             |builder| {
                 // Create the function
@@ -102,9 +100,8 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
                     Some(source) => source.value,
                     None => return,
                 };
-                let method_name = method.name(ctx.db());
                 let vis = method_source.visibility();
-                let name = make::name(&method.name(ctx.db()).display(ctx.db()).to_string());
+                let fn_name = make::name(&name);
                 let params =
                     method_source.param_list().unwrap_or_else(|| make::param_list(None, []));
                 let type_params = method_source.generic_param_list();
@@ -114,7 +111,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
                 };
                 let tail_expr = make::expr_method_call(
                     make::ext::field_from_idents(["self", &field_name]).unwrap(), // This unwrap is ok because we have at least 1 arg in the list
-                    make::name_ref(&method_name.display(ctx.db()).to_string()),
+                    make::name_ref(&name),
                     arg_list,
                 );
                 let ret_type = method_source.ret_type();
@@ -126,7 +123,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
                 let body = make::block_expr([], Some(tail_expr_finished));
                 let f = make::fn_(
                     vis,
-                    name,
+                    fn_name,
                     type_params,
                     None,
                     params,
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs
index 850be21c300..c579f6780db 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs
@@ -291,12 +291,9 @@ impl FunctionBuilder {
         let await_expr = call.syntax().parent().and_then(ast::AwaitExpr::cast);
         let is_async = await_expr.is_some();
 
-        let (ret_type, should_focus_return_type) = make_return_type(
-            ctx,
-            &ast::Expr::CallExpr(call.clone()),
-            target_module,
-            &mut necessary_generic_params,
-        );
+        let expr_for_ret_ty = await_expr.map_or_else(|| call.clone().into(), |it| it.into());
+        let (ret_type, should_focus_return_type) =
+            make_return_type(ctx, &expr_for_ret_ty, target_module, &mut necessary_generic_params);
 
         let (generic_param_list, where_clause) =
             fn_generic_params(ctx, necessary_generic_params, &target)?;
@@ -338,12 +335,9 @@ impl FunctionBuilder {
         let await_expr = call.syntax().parent().and_then(ast::AwaitExpr::cast);
         let is_async = await_expr.is_some();
 
-        let (ret_type, should_focus_return_type) = make_return_type(
-            ctx,
-            &ast::Expr::MethodCallExpr(call.clone()),
-            target_module,
-            &mut necessary_generic_params,
-        );
+        let expr_for_ret_ty = await_expr.map_or_else(|| call.clone().into(), |it| it.into());
+        let (ret_type, should_focus_return_type) =
+            make_return_type(ctx, &expr_for_ret_ty, target_module, &mut necessary_generic_params);
 
         let (generic_param_list, where_clause) =
             fn_generic_params(ctx, necessary_generic_params, &target)?;
@@ -429,12 +423,12 @@ impl FunctionBuilder {
 /// user can change the `todo!` function body.
 fn make_return_type(
     ctx: &AssistContext<'_>,
-    call: &ast::Expr,
+    expr: &ast::Expr,
     target_module: Module,
     necessary_generic_params: &mut FxHashSet<hir::GenericParam>,
 ) -> (Option<ast::RetType>, bool) {
     let (ret_ty, should_focus_return_type) = {
-        match ctx.sema.type_of_expr(call).map(TypeInfo::original) {
+        match ctx.sema.type_of_expr(expr).map(TypeInfo::original) {
             Some(ty) if ty.is_unknown() => (Some(make::ty_placeholder()), true),
             None => (Some(make::ty_placeholder()), true),
             Some(ty) if ty.is_unit() => (None, false),
@@ -2268,13 +2262,13 @@ impl Foo {
         check_assist(
             generate_function,
             r"
-fn foo() {
-    $0bar(42).await();
+async fn foo() {
+    $0bar(42).await;
 }
 ",
             r"
-fn foo() {
-    bar(42).await();
+async fn foo() {
+    bar(42).await;
 }
 
 async fn bar(arg: i32) ${0:-> _} {
@@ -2285,6 +2279,28 @@ async fn bar(arg: i32) ${0:-> _} {
     }
 
     #[test]
+    fn return_type_for_async_fn() {
+        check_assist(
+            generate_function,
+            r"
+//- minicore: result
+async fn foo() {
+    if Err(()) = $0bar(42).await {}
+}
+",
+            r"
+async fn foo() {
+    if Err(()) = bar(42).await {}
+}
+
+async fn bar(arg: i32) -> Result<_, ()> {
+    ${0:todo!()}
+}
+",
+        );
+    }
+
+    #[test]
     fn create_method() {
         check_assist(
             generate_function,
@@ -2402,6 +2418,31 @@ fn foo() {S.bar();}
     }
 
     #[test]
+    fn create_async_method() {
+        check_assist(
+            generate_function,
+            r"
+//- minicore: result
+struct S;
+async fn foo() {
+    if let Err(()) = S.$0bar(42).await {}
+}
+",
+            r"
+struct S;
+impl S {
+    async fn bar(&self, arg: i32) -> Result<_, ()> {
+        ${0:todo!()}
+    }
+}
+async fn foo() {
+    if let Err(()) = S.bar(42).await {}
+}
+",
+        )
+    }
+
+    #[test]
     fn create_static_method() {
         check_assist(
             generate_function,
@@ -2422,6 +2463,31 @@ fn foo() {S::bar();}
     }
 
     #[test]
+    fn create_async_static_method() {
+        check_assist(
+            generate_function,
+            r"
+//- minicore: result
+struct S;
+async fn foo() {
+    if let Err(()) = S::$0bar(42).await {}
+}
+",
+            r"
+struct S;
+impl S {
+    async fn bar(arg: i32) -> Result<_, ()> {
+        ${0:todo!()}
+    }
+}
+async fn foo() {
+    if let Err(()) = S::bar(42).await {}
+}
+",
+        )
+    }
+
+    #[test]
     fn create_generic_static_method() {
         check_assist(
             generate_function,
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs
index 28d815e81b4..797180fa189 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs
@@ -958,7 +958,6 @@ fn main() {
         );
     }
 
-    // FIXME: const generics aren't being substituted, this is blocked on better support for them
     #[test]
     fn inline_substitutes_generics() {
         check_assist(
@@ -982,7 +981,7 @@ fn foo<T, const N: usize>() {
 fn bar<U, const M: usize>() {}
 
 fn main() {
-    bar::<usize, N>();
+    bar::<usize, {0}>();
 }
 "#,
         );
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs
new file mode 100644
index 00000000000..5b1540b50ca
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs
@@ -0,0 +1,722 @@
+use syntax::{ast, AstNode};
+
+use crate::{AssistContext, AssistId, AssistKind, Assists};
+
+// Assist: inline_const_as_literal
+//
+// Evaluate and inline const variable as literal.
+//
+// ```
+// const STRING: &str = "Hello, World!";
+//
+// fn something() -> &'static str {
+//     STRING$0
+// }
+// ```
+// ->
+// ```
+// const STRING: &str = "Hello, World!";
+//
+// fn something() -> &'static str {
+//     "Hello, World!"
+// }
+// ```
+pub(crate) fn inline_const_as_literal(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+    let variable = ctx.find_node_at_offset::<ast::PathExpr>()?;
+
+    if let hir::PathResolution::Def(hir::ModuleDef::Const(konst)) =
+        ctx.sema.resolve_path(&variable.path()?)?
+    {
+        let konst_ty = konst.ty(ctx.sema.db);
+
+        // Used as the upper limit for recursive calls if no TCO is available
+        let fuel = 20;
+
+        // There is no way to have a const static reference to a type that contains a interior
+        // mutability cell.
+
+        // FIXME: Add support to handle type aliases for builtin scalar types.
+        validate_type_recursively(ctx, Some(&konst_ty), false, fuel)?;
+
+        let expr = konst.value(ctx.sema.db)?;
+
+        let value = match expr {
+            ast::Expr::BlockExpr(_)
+            | ast::Expr::Literal(_)
+            | ast::Expr::RefExpr(_)
+            | ast::Expr::ArrayExpr(_)
+            | ast::Expr::TupleExpr(_)
+            | ast::Expr::IfExpr(_)
+            | ast::Expr::ParenExpr(_)
+            | ast::Expr::MatchExpr(_)
+            | ast::Expr::MacroExpr(_)
+            | ast::Expr::BinExpr(_)
+            | ast::Expr::CallExpr(_) => match konst.render_eval(ctx.sema.db) {
+                Ok(result) => result,
+                Err(_) => return None,
+            },
+            _ => return None,
+        };
+
+        let id = AssistId("inline_const_as_literal", AssistKind::RefactorInline);
+
+        let label = format!("Inline const as literal");
+        let target = variable.syntax().text_range();
+
+        return acc.add(id, label, target, |edit| {
+            edit.replace(variable.syntax().text_range(), value);
+        });
+    }
+    None
+}
+
+fn validate_type_recursively(
+    ctx: &AssistContext<'_>,
+    ty_hir: Option<&hir::Type>,
+    refed: bool,
+    fuel: i32,
+) -> Option<()> {
+    match (fuel > 0, ty_hir) {
+        (true, Some(ty)) if ty.is_reference() => validate_type_recursively(
+            ctx,
+            ty.as_reference().map(|(ty, _)| ty).as_ref(),
+            true,
+            // FIXME: Saving fuel when `&` repeating might not be a good idea if there's no TCO.
+            if refed { fuel } else { fuel - 1 },
+        ),
+        (true, Some(ty)) if ty.is_array() => validate_type_recursively(
+            ctx,
+            ty.as_array(ctx.db()).map(|(ty, _)| ty).as_ref(),
+            false,
+            fuel - 1,
+        ),
+        (true, Some(ty)) if ty.is_tuple() => ty
+            .tuple_fields(ctx.db())
+            .iter()
+            .all(|ty| validate_type_recursively(ctx, Some(ty), false, fuel - 1).is_some())
+            .then_some(()),
+        (true, Some(ty)) if refed && ty.is_slice() => {
+            validate_type_recursively(ctx, ty.as_slice().as_ref(), false, fuel - 1)
+        }
+        (_, Some(ty)) => match ty.as_builtin() {
+            // `const A: str` is not correct, but `const A: &builtin` is.
+            Some(builtin) if refed || (!refed && !builtin.is_str()) => Some(()),
+            _ => None,
+        },
+        _ => None,
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::tests::{check_assist, check_assist_not_applicable};
+
+    const NUMBER: u8 = 1;
+    const BOOL: u8 = 2;
+    const STR: u8 = 4;
+    const CHAR: u8 = 8;
+
+    const TEST_PAIRS: &[(&str, &str, u8)] = &[
+        ("u8", "0", NUMBER),
+        ("u16", "0", NUMBER),
+        ("u32", "0", NUMBER),
+        ("u64", "0", NUMBER),
+        ("u128", "0", NUMBER),
+        ("usize", "0", NUMBER),
+        ("i8", "0", NUMBER),
+        ("i16", "0", NUMBER),
+        ("i32", "0", NUMBER),
+        ("i64", "0", NUMBER),
+        ("i128", "0", NUMBER),
+        ("isize", "0", NUMBER),
+        ("bool", "false", BOOL),
+        ("&str", "\"str\"", STR),
+        ("char", "'c'", CHAR),
+    ];
+
+    // -----------Not supported-----------
+    #[test]
+    fn inline_const_as_literal_const_fn_call_slice() {
+        TEST_PAIRS.into_iter().for_each(|(ty, val, _)| {
+            check_assist_not_applicable(
+                inline_const_as_literal,
+                &format!(
+                    r#"
+                    const fn abc() -> &[{ty}] {{ &[{val}] }}
+                    const ABC: &[{ty}] = abc();
+                    fn a() {{ A$0BC }}
+                    "#
+                ),
+            );
+        });
+    }
+
+    #[test]
+    fn inline_const_as_literal_expr_as_str_lit_not_applicable_const() {
+        check_assist_not_applicable(
+            inline_const_as_literal,
+            r#"
+            const STR$0ING: &str = "Hello, World!";
+
+            fn something() -> &'static str {
+                STRING
+            }
+            "#,
+        );
+    }
+
+    #[test]
+    fn inline_const_as_struct_() {
+        check_assist_not_applicable(
+            inline_const_as_literal,
+            r#"
+            struct A;
+            const STRUKT: A = A;
+
+            fn something() -> A {
+                STRU$0KT
+            }
+            "#,
+        );
+    }
+
+    #[test]
+    fn inline_const_as_enum_() {
+        check_assist_not_applicable(
+            inline_const_as_literal,
+            r#"
+            enum A { A, B, C }
+            const ENUM: A = A::A;
+
+            fn something() -> A {
+                EN$0UM
+            }
+            "#,
+        );
+    }
+
+    #[test]
+    fn inline_const_as_tuple_closure() {
+        check_assist_not_applicable(
+            inline_const_as_literal,
+            r#"
+            const CLOSURE: (&dyn Fn(i32) -> i32) = (&|num| -> i32 { num });
+            fn something() -> (&dyn Fn(i32) -> i32) {
+                STRU$0KT
+            }
+            "#,
+        );
+    }
+
+    #[test]
+    fn inline_const_as_closure_() {
+        check_assist_not_applicable(
+            inline_const_as_literal,
+            r#"
+            const CLOSURE: &dyn Fn(i32) -> i32 = &|num| -> i32 { num };
+            fn something() -> &dyn Fn(i32) -> i32 {
+                STRU$0KT
+            }
+            "#,
+        );
+    }
+
+    #[test]
+    fn inline_const_as_fn_() {
+        check_assist_not_applicable(
+            inline_const_as_literal,
+            r#"
+            struct S(i32);
+            const CON: fn(i32) -> S = S;
+            fn something() {
+                let x = CO$0N;
+            }
+            "#,
+        );
+    }
+
+    // ----------------------------
+
+    #[test]
+    fn inline_const_as_literal_const_expr() {
+        TEST_PAIRS.into_iter().for_each(|(ty, val, _)| {
+            check_assist(
+                inline_const_as_literal,
+                &format!(
+                    r#"
+                    const ABC: {ty} = {val};
+                    fn a() {{ A$0BC }}
+                    "#
+                ),
+                &format!(
+                    r#"
+                    const ABC: {ty} = {val};
+                    fn a() {{ {val} }}
+                    "#
+                ),
+            );
+        });
+    }
+
+    #[test]
+    fn inline_const_as_literal_const_block_expr() {
+        TEST_PAIRS.into_iter().for_each(|(ty, val, _)| {
+            check_assist(
+                inline_const_as_literal,
+                &format!(
+                    r#"
+                    const ABC: {ty} = {{ {val} }};
+                    fn a() {{ A$0BC }}
+                    "#
+                ),
+                &format!(
+                    r#"
+                    const ABC: {ty} = {{ {val} }};
+                    fn a() {{ {val} }}
+                    "#
+                ),
+            );
+        });
+    }
+
+    #[test]
+    fn inline_const_as_literal_const_block_eval_expr() {
+        TEST_PAIRS.into_iter().for_each(|(ty, val, _)| {
+            check_assist(
+                inline_const_as_literal,
+                &format!(
+                    r#"
+                    const ABC: {ty} = {{ true; {val} }};
+                    fn a() {{ A$0BC }}
+                    "#
+                ),
+                &format!(
+                    r#"
+                    const ABC: {ty} = {{ true; {val} }};
+                    fn a() {{ {val} }}
+                    "#
+                ),
+            );
+        });
+    }
+
+    #[test]
+    fn inline_const_as_literal_const_block_eval_block_expr() {
+        TEST_PAIRS.into_iter().for_each(|(ty, val, _)| {
+            check_assist(
+                inline_const_as_literal,
+                &format!(
+                    r#"
+                    const ABC: {ty} = {{ true; {{ {val} }} }};
+                    fn a() {{ A$0BC }}
+                    "#
+                ),
+                &format!(
+                    r#"
+                    const ABC: {ty} = {{ true; {{ {val} }} }};
+                    fn a() {{ {val} }}
+                    "#
+                ),
+            );
+        });
+    }
+
+    #[test]
+    fn inline_const_as_literal_const_fn_call_block_nested_builtin() {
+        TEST_PAIRS.into_iter().for_each(|(ty, val, _)| {
+            check_assist(
+                inline_const_as_literal,
+                &format!(
+                    r#"
+                    const fn abc() -> {ty} {{ {{ {{ {{ {val} }} }} }} }}
+                    const ABC: {ty} = abc();
+                    fn a() {{ A$0BC }}
+                    "#
+                ),
+                &format!(
+                    r#"
+                    const fn abc() -> {ty} {{ {{ {{ {{ {val} }} }} }} }}
+                    const ABC: {ty} = abc();
+                    fn a() {{ {val} }}
+                    "#
+                ),
+            );
+        });
+    }
+
+    #[test]
+    fn inline_const_as_literal_const_fn_call_tuple() {
+        TEST_PAIRS.into_iter().for_each(|(ty, val, _)| {
+            check_assist(
+                inline_const_as_literal,
+                &format!(
+                    r#"
+                    const fn abc() -> ({ty}, {ty}) {{ ({val}, {val}) }}
+                    const ABC: ({ty}, {ty}) = abc();
+                    fn a() {{ A$0BC }}
+                    "#
+                ),
+                &format!(
+                    r#"
+                    const fn abc() -> ({ty}, {ty}) {{ ({val}, {val}) }}
+                    const ABC: ({ty}, {ty}) = abc();
+                    fn a() {{ ({val}, {val}) }}
+                    "#
+                ),
+            );
+        });
+    }
+
+    #[test]
+    fn inline_const_as_literal_const_fn_call_builtin() {
+        TEST_PAIRS.into_iter().for_each(|(ty, val, _)| {
+            check_assist(
+                inline_const_as_literal,
+                &format!(
+                    r#"
+					const fn abc() -> {ty} {{ {val} }}
+					const ABC: {ty} = abc();
+					fn a() {{ A$0BC }}
+					"#
+                ),
+                &format!(
+                    r#"
+					const fn abc() -> {ty} {{ {val} }}
+					const ABC: {ty} = abc();
+					fn a() {{ {val} }}
+					"#
+                ),
+            );
+        });
+    }
+
+    #[test]
+    fn inline_const_as_literal_scalar_operators() {
+        check_assist(
+            inline_const_as_literal,
+            r#"
+            const ABC: i32 = 1 + 2 + 3;
+            fn a() { A$0BC }
+            "#,
+            r#"
+            const ABC: i32 = 1 + 2 + 3;
+            fn a() { 6 }
+            "#,
+        );
+    }
+    #[test]
+    fn inline_const_as_literal_block_scalar_calculate_expr() {
+        check_assist(
+            inline_const_as_literal,
+            r#"
+            const ABC: i32 = { 1 + 2 + 3 };
+            fn a() { A$0BC }
+            "#,
+            r#"
+            const ABC: i32 = { 1 + 2 + 3 };
+            fn a() { 6 }
+            "#,
+        );
+    }
+
+    #[test]
+    fn inline_const_as_literal_block_scalar_calculate_param_expr() {
+        check_assist(
+            inline_const_as_literal,
+            r#"
+            const ABC: i32 = { (1 + 2 + 3) };
+            fn a() { A$0BC }
+            "#,
+            r#"
+            const ABC: i32 = { (1 + 2 + 3) };
+            fn a() { 6 }
+            "#,
+        );
+    }
+
+    #[test]
+    fn inline_const_as_literal_block_tuple_scalar_calculate_block_expr() {
+        check_assist(
+            inline_const_as_literal,
+            r#"
+            const ABC: (i32, i32) = { (1, { 2 + 3 }) };
+            fn a() { A$0BC }
+            "#,
+            r#"
+            const ABC: (i32, i32) = { (1, { 2 + 3 }) };
+            fn a() { (1, 5) }
+            "#,
+        );
+    }
+
+    // FIXME: Add support for nested ref slices when using `render_eval`
+    #[test]
+    fn inline_const_as_literal_block_slice() {
+        check_assist_not_applicable(
+            inline_const_as_literal,
+            r#"
+            const ABC: &[&[&[&[&[&[i32]]]]]] = { &[&[&[&[&[&[10, 20, 30]]]]]] };
+            fn a() { A$0BC }
+            "#,
+        );
+    }
+
+    // FIXME: Add support for unary tuple expressions when using `render_eval`.
+    // `const fn abc() -> (i32) { (1) }` will results in `1` instead of `(1)` because it's evaluated
+    // as a paren expr.
+    #[test]
+    fn inline_const_as_literal_block_tuple() {
+        check_assist(
+            inline_const_as_literal,
+            r#"
+            const ABC: (([i32; 3]), (i32), ((&str, i32), i32), i32) = { (([1, 2, 3]), (10), (("hello", 10), 20), 30) };
+            fn a() { A$0BC }
+            "#,
+            r#"
+            const ABC: (([i32; 3]), (i32), ((&str, i32), i32), i32) = { (([1, 2, 3]), (10), (("hello", 10), 20), 30) };
+            fn a() { ([1, 2, 3], 10, (("hello", 10), 20), 30) }
+            "#,
+        );
+    }
+
+    #[test]
+    fn inline_const_as_literal_block_slice_single() {
+        check_assist(
+            inline_const_as_literal,
+            r#"
+            const ABC: [i32; 1] = { [10] };
+            fn a() { A$0BC }
+            "#,
+            r#"
+            const ABC: [i32; 1] = { [10] };
+            fn a() { [10] }
+            "#,
+        );
+    }
+
+    #[test]
+    fn inline_const_as_literal_block_array() {
+        check_assist(
+            inline_const_as_literal,
+            r#"
+            const ABC: [[[i32; 1]; 1]; 1] = { [[[10]]] };
+            fn a() { A$0BC }
+            "#,
+            r#"
+            const ABC: [[[i32; 1]; 1]; 1] = { [[[10]]] };
+            fn a() { [[[10]]] }
+            "#,
+        );
+    }
+
+    #[test]
+    fn inline_const_as_literal_block_recursive() {
+        check_assist(
+            inline_const_as_literal,
+            r#"
+            const ABC: &str = { { { { "hello" } } } };
+            fn a() { A$0BC }
+            "#,
+            r#"
+            const ABC: &str = { { { { "hello" } } } };
+            fn a() { "hello" }
+            "#,
+        );
+    }
+
+    #[test]
+    fn inline_const_as_literal_expr_as_str_lit() {
+        check_assist(
+            inline_const_as_literal,
+            r#"
+            const STRING: &str = "Hello, World!";
+
+            fn something() -> &'static str {
+                STR$0ING
+            }
+            "#,
+            r#"
+            const STRING: &str = "Hello, World!";
+
+            fn something() -> &'static str {
+                "Hello, World!"
+            }
+            "#,
+        );
+    }
+
+    #[test]
+    fn inline_const_as_literal_eval_const_block_expr_to_str_lit() {
+        check_assist(
+            inline_const_as_literal,
+            r#"
+            const STRING: &str = {
+                let x = 9;
+                if x + 10 == 21 {
+                    "Hello, World!"
+                } else {
+                    "World, Hello!"
+                }
+            };
+
+            fn something() -> &'static str {
+                STR$0ING
+            }
+            "#,
+            r#"
+            const STRING: &str = {
+                let x = 9;
+                if x + 10 == 21 {
+                    "Hello, World!"
+                } else {
+                    "World, Hello!"
+                }
+            };
+
+            fn something() -> &'static str {
+                "World, Hello!"
+            }
+            "#,
+        );
+    }
+
+    #[test]
+    fn inline_const_as_literal_eval_const_block_macro_expr_to_str_lit() {
+        check_assist(
+            inline_const_as_literal,
+            r#"
+            macro_rules! co {() => {"World, Hello!"};}
+            const STRING: &str = { co!() };
+
+            fn something() -> &'static str {
+                STR$0ING
+            }
+            "#,
+            r#"
+            macro_rules! co {() => {"World, Hello!"};}
+            const STRING: &str = { co!() };
+
+            fn something() -> &'static str {
+                "World, Hello!"
+            }
+            "#,
+        );
+    }
+
+    #[test]
+    fn inline_const_as_literal_eval_const_match_expr_to_str_lit() {
+        check_assist(
+            inline_const_as_literal,
+            r#"
+            const STRING: &str = match 9 + 10 {
+                0..18 => "Hello, World!",
+                _ => "World, Hello!"
+            };
+
+            fn something() -> &'static str {
+                STR$0ING
+            }
+            "#,
+            r#"
+            const STRING: &str = match 9 + 10 {
+                0..18 => "Hello, World!",
+                _ => "World, Hello!"
+            };
+
+            fn something() -> &'static str {
+                "World, Hello!"
+            }
+            "#,
+        );
+    }
+
+    #[test]
+    fn inline_const_as_literal_eval_const_if_expr_to_str_lit() {
+        check_assist(
+            inline_const_as_literal,
+            r#"
+            const STRING: &str = if 1 + 2 == 4 {
+                "Hello, World!"
+            } else {
+                "World, Hello!"
+            }
+
+            fn something() -> &'static str {
+                STR$0ING
+            }
+            "#,
+            r#"
+            const STRING: &str = if 1 + 2 == 4 {
+                "Hello, World!"
+            } else {
+                "World, Hello!"
+            }
+
+            fn something() -> &'static str {
+                "World, Hello!"
+            }
+            "#,
+        );
+    }
+
+    #[test]
+    fn inline_const_as_literal_eval_const_macro_expr_to_str_lit() {
+        check_assist(
+            inline_const_as_literal,
+            r#"
+            macro_rules! co {() => {"World, Hello!"};}
+            const STRING: &str = co!();
+
+            fn something() -> &'static str {
+                STR$0ING
+            }
+            "#,
+            r#"
+            macro_rules! co {() => {"World, Hello!"};}
+            const STRING: &str = co!();
+
+            fn something() -> &'static str {
+                "World, Hello!"
+            }
+            "#,
+        );
+    }
+
+    #[test]
+    fn inline_const_as_literal_eval_const_call_expr_to_str_lit() {
+        check_assist(
+            inline_const_as_literal,
+            r#"
+            const fn const_call() -> &'static str {"World, Hello!"}
+            const STRING: &str = const_call();
+
+            fn something() -> &'static str {
+                STR$0ING
+            }
+            "#,
+            r#"
+            const fn const_call() -> &'static str {"World, Hello!"}
+            const STRING: &str = const_call();
+
+            fn something() -> &'static str {
+                "World, Hello!"
+            }
+            "#,
+        );
+    }
+
+    #[test]
+    fn inline_const_as_literal_expr_as_str_lit_not_applicable() {
+        check_assist_not_applicable(
+            inline_const_as_literal,
+            r#"
+            const STRING: &str = "Hello, World!";
+
+            fn something() -> &'static str {
+                STRING $0
+            }
+            "#,
+        );
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs
index 40ee4771d17..63db6063361 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs
@@ -158,9 +158,8 @@ pub(crate) fn remove_hash(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
 
 #[cfg(test)]
 mod tests {
-    use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
-
     use super::*;
+    use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
 
     #[test]
     fn make_raw_string_target() {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
index 36ac8c71d81..3bdd795bea8 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -1,8 +1,5 @@
 use hir::{InFile, ModuleDef};
-use ide_db::{
-    helpers::mod_path_to_ast, imports::import_assets::NameToImport, items_locator,
-    syntax_helpers::insert_whitespace_into_node::insert_ws_into,
-};
+use ide_db::{helpers::mod_path_to_ast, imports::import_assets::NameToImport, items_locator};
 use itertools::Itertools;
 use syntax::{
     ast::{self, AstNode, HasName},
@@ -182,7 +179,11 @@ fn impl_def_from_trait(
     let impl_def = {
         use syntax::ast::Impl;
         let text = generate_trait_impl_text(adt, trait_path.to_string().as_str(), "");
-        let parse = syntax::SourceFile::parse(&text);
+        // FIXME: `generate_trait_impl_text` currently generates two newlines
+        // at the front, but these leading newlines should really instead be
+        // inserted at the same time the impl is inserted
+        assert_eq!(&text[..2], "\n\n", "`generate_trait_impl_text` output changed");
+        let parse = syntax::SourceFile::parse(&text[2..]);
         let node = match parse.tree().syntax().descendants().find_map(Impl::cast) {
             Some(it) => it,
             None => {
@@ -193,24 +194,13 @@ fn impl_def_from_trait(
                 )
             }
         };
-        let node = node.clone_subtree();
+        let node = node.clone_for_update();
         assert_eq!(node.syntax().text_range().start(), 0.into());
         node
     };
 
-    let trait_items = trait_items
-        .into_iter()
-        .map(|it| {
-            if sema.hir_file_for(it.syntax()).is_macro() {
-                if let Some(it) = ast::AssocItem::cast(insert_ws_into(it.syntax().clone())) {
-                    return it;
-                }
-            }
-            it.clone_for_update()
-        })
-        .collect();
-    let (impl_def, first_assoc_item) =
-        add_trait_assoc_items_to_impl(sema, trait_items, trait_, impl_def, target_scope);
+    let first_assoc_item =
+        add_trait_assoc_items_to_impl(sema, &trait_items, trait_, &impl_def, target_scope);
 
     // Generate a default `impl` function body for the derived trait.
     if let ast::AssocItem::Fn(ref func) = first_assoc_item {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
index bd282e53434..111753bf309 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
@@ -161,6 +161,7 @@ mod handlers {
     mod generate_delegate_methods;
     mod add_return_type;
     mod inline_call;
+    mod inline_const_as_literal;
     mod inline_local_variable;
     mod inline_macro;
     mod inline_type_alias;
@@ -265,6 +266,7 @@ mod handlers {
             generate_new::generate_new,
             inline_call::inline_call,
             inline_call::inline_into_callers,
+            inline_const_as_literal::inline_const_as_literal,
             inline_local_variable::inline_local_variable,
             inline_type_alias::inline_type_alias,
             inline_type_alias::inline_type_alias_uses,
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
index 8a35fd290e6..c097e073980 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
@@ -1480,6 +1480,27 @@ fn foo(name: Option<&str>) {
 }
 
 #[test]
+fn doctest_inline_const_as_literal() {
+    check_doc_test(
+        "inline_const_as_literal",
+        r#####"
+const STRING: &str = "Hello, World!";
+
+fn something() -> &'static str {
+    STRING$0
+}
+"#####,
+        r#####"
+const STRING: &str = "Hello, World!";
+
+fn something() -> &'static str {
+    "Hello, World!"
+}
+"#####,
+    )
+}
+
+#[test]
 fn doctest_inline_into_callers() {
     check_doc_test(
         "inline_into_callers",
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
index 8f7ea26306c..03d8553506f 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
@@ -3,8 +3,11 @@
 use std::ops;
 
 pub(crate) use gen_trait_fn_body::gen_trait_fn_body;
-use hir::{db::HirDatabase, HirDisplay, Semantics};
-use ide_db::{famous_defs::FamousDefs, path_transform::PathTransform, RootDatabase, SnippetCap};
+use hir::{db::HirDatabase, HirDisplay, InFile, Semantics};
+use ide_db::{
+    famous_defs::FamousDefs, path_transform::PathTransform,
+    syntax_helpers::insert_whitespace_into_node::insert_ws_into, RootDatabase, SnippetCap,
+};
 use stdx::format_to;
 use syntax::{
     ast::{
@@ -91,30 +94,21 @@ pub fn filter_assoc_items(
     sema: &Semantics<'_, RootDatabase>,
     items: &[hir::AssocItem],
     default_methods: DefaultMethods,
-) -> Vec<ast::AssocItem> {
-    fn has_def_name(item: &ast::AssocItem) -> bool {
-        match item {
-            ast::AssocItem::Fn(def) => def.name(),
-            ast::AssocItem::TypeAlias(def) => def.name(),
-            ast::AssocItem::Const(def) => def.name(),
-            ast::AssocItem::MacroCall(_) => None,
-        }
-        .is_some()
-    }
-
-    items
+) -> Vec<InFile<ast::AssocItem>> {
+    return items
         .iter()
         // Note: This throws away items with no source.
-        .filter_map(|&i| {
-            let item = match i {
-                hir::AssocItem::Function(i) => ast::AssocItem::Fn(sema.source(i)?.value),
-                hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAlias(sema.source(i)?.value),
-                hir::AssocItem::Const(i) => ast::AssocItem::Const(sema.source(i)?.value),
+        .copied()
+        .filter_map(|assoc_item| {
+            let item = match assoc_item {
+                hir::AssocItem::Function(it) => sema.source(it)?.map(ast::AssocItem::Fn),
+                hir::AssocItem::TypeAlias(it) => sema.source(it)?.map(ast::AssocItem::TypeAlias),
+                hir::AssocItem::Const(it) => sema.source(it)?.map(ast::AssocItem::Const),
             };
             Some(item)
         })
         .filter(has_def_name)
-        .filter(|it| match it {
+        .filter(|it| match &it.value {
             ast::AssocItem::Fn(def) => matches!(
                 (default_methods, def.body()),
                 (DefaultMethods::Only, Some(_)) | (DefaultMethods::No, None)
@@ -125,31 +119,58 @@ pub fn filter_assoc_items(
             ),
             _ => default_methods == DefaultMethods::No,
         })
-        .collect::<Vec<_>>()
+        .collect();
+
+    fn has_def_name(item: &InFile<ast::AssocItem>) -> bool {
+        match &item.value {
+            ast::AssocItem::Fn(def) => def.name(),
+            ast::AssocItem::TypeAlias(def) => def.name(),
+            ast::AssocItem::Const(def) => def.name(),
+            ast::AssocItem::MacroCall(_) => None,
+        }
+        .is_some()
+    }
 }
 
+/// Given `original_items` retrieved from the trait definition (usually by
+/// [`filter_assoc_items()`]), clones each item for update and applies path transformation to it,
+/// then inserts into `impl_`. Returns the modified `impl_` and the first associated item that got
+/// inserted.
 pub fn add_trait_assoc_items_to_impl(
     sema: &Semantics<'_, RootDatabase>,
-    items: Vec<ast::AssocItem>,
+    original_items: &[InFile<ast::AssocItem>],
     trait_: hir::Trait,
-    impl_: ast::Impl,
+    impl_: &ast::Impl,
     target_scope: hir::SemanticsScope<'_>,
-) -> (ast::Impl, ast::AssocItem) {
-    let source_scope = sema.scope_for_def(trait_);
-
-    let transform = PathTransform::trait_impl(&target_scope, &source_scope, trait_, impl_.clone());
-
+) -> ast::AssocItem {
     let new_indent_level = IndentLevel::from_node(impl_.syntax()) + 1;
-    let items = items.into_iter().map(|assoc_item| {
-        transform.apply(assoc_item.syntax());
-        assoc_item.remove_attrs_and_docs();
-        assoc_item.reindent_to(new_indent_level);
-        assoc_item
-    });
+    let items = original_items.into_iter().map(|InFile { file_id, value: original_item }| {
+        let cloned_item = {
+            if file_id.is_macro() {
+                if let Some(formatted) =
+                    ast::AssocItem::cast(insert_ws_into(original_item.syntax().clone()))
+                {
+                    return formatted;
+                } else {
+                    stdx::never!("formatted `AssocItem` could not be cast back to `AssocItem`");
+                }
+            }
+            original_item.clone_for_update()
+        };
 
-    let res = impl_.clone_for_update();
+        if let Some(source_scope) = sema.scope(original_item.syntax()) {
+            // FIXME: Paths in nested macros are not handled well. See
+            // `add_missing_impl_members::paths_in_nested_macro_should_get_transformed` test.
+            let transform =
+                PathTransform::trait_impl(&target_scope, &source_scope, trait_, impl_.clone());
+            transform.apply(cloned_item.syntax());
+        }
+        cloned_item.remove_attrs_and_docs();
+        cloned_item.reindent_to(new_indent_level);
+        cloned_item
+    });
 
-    let assoc_item_list = res.get_or_create_assoc_item_list();
+    let assoc_item_list = impl_.get_or_create_assoc_item_list();
     let mut first_item = None;
     for item in items {
         first_item.get_or_insert_with(|| item.clone());
@@ -172,7 +193,7 @@ pub fn add_trait_assoc_items_to_impl(
         assoc_item_list.add_item(item)
     }
 
-    (res, first_item.unwrap())
+    first_item.unwrap()
 }
 
 #[derive(Clone, Copy, Debug)]
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs
index 57a784c45b6..c5bbb7f8d75 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs
@@ -105,13 +105,20 @@ fn complete_fields(
     mut named_field: impl FnMut(&mut Completions, hir::Field, hir::Type),
     mut tuple_index: impl FnMut(&mut Completions, usize, hir::Type),
 ) {
+    let mut seen_names = FxHashSet::default();
     for receiver in receiver.autoderef(ctx.db) {
         for (field, ty) in receiver.fields(ctx.db) {
-            named_field(acc, field, ty);
+            if seen_names.insert(field.name(ctx.db)) {
+                named_field(acc, field, ty);
+            }
         }
         for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() {
-            // Tuple fields are always public (tuple struct fields are handled above).
-            tuple_index(acc, i, ty);
+            // Tuples are always the last type in a deref chain, so just check if the name is
+            // already seen without inserting into the hashset.
+            if !seen_names.contains(&hir::Name::new_tuple_field(i)) {
+                // Tuple fields are always public (tuple struct fields are handled above).
+                tuple_index(acc, i, ty);
+            }
         }
     }
 }
@@ -672,6 +679,74 @@ impl T {
     }
 
     #[test]
+    fn test_field_no_same_name() {
+        check(
+            r#"
+//- minicore: deref
+struct A { field: u8 }
+struct B { field: u16, another: u32 }
+impl core::ops::Deref for A {
+    type Target = B;
+    fn deref(&self) -> &Self::Target { loop {} }
+}
+fn test(a: A) {
+    a.$0
+}
+"#,
+            expect![[r#"
+                fd another                u32
+                fd field                  u8
+                me deref() (use core::ops::Deref) fn(&self) -> &<Self as Deref>::Target
+            "#]],
+        );
+    }
+
+    #[test]
+    fn test_tuple_field_no_same_index() {
+        check(
+            r#"
+//- minicore: deref
+struct A(u8);
+struct B(u16, u32);
+impl core::ops::Deref for A {
+    type Target = B;
+    fn deref(&self) -> &Self::Target { loop {} }
+}
+fn test(a: A) {
+    a.$0
+}
+"#,
+            expect![[r#"
+                fd 0                      u8
+                fd 1                      u32
+                me deref() (use core::ops::Deref) fn(&self) -> &<Self as Deref>::Target
+            "#]],
+        );
+    }
+
+    #[test]
+    fn test_tuple_struct_deref_to_tuple_no_same_index() {
+        check(
+            r#"
+//- minicore: deref
+struct A(u8);
+impl core::ops::Deref for A {
+    type Target = (u16, u32);
+    fn deref(&self) -> &Self::Target { loop {} }
+}
+fn test(a: A) {
+    a.$0
+}
+"#,
+            expect![[r#"
+                fd 0                      u8
+                fd 1                      u32
+                me deref() (use core::ops::Deref) fn(&self) -> &<Self as Deref>::Target
+            "#]],
+        );
+    }
+
+    #[test]
     fn test_completion_works_in_consts() {
         check(
             r#"
@@ -979,4 +1054,45 @@ fn test(thing: impl Encrypt) {
             "#]],
         )
     }
+
+    #[test]
+    fn only_consider_same_type_once() {
+        check(
+            r#"
+//- minicore: deref
+struct A(u8);
+struct B(u16);
+impl core::ops::Deref for A {
+    type Target = B;
+    fn deref(&self) -> &Self::Target { loop {} }
+}
+impl core::ops::Deref for B {
+    type Target = A;
+    fn deref(&self) -> &Self::Target { loop {} }
+}
+fn test(a: A) {
+    a.$0
+}
+"#,
+            expect![[r#"
+                fd 0                      u8
+                me deref() (use core::ops::Deref) fn(&self) -> &<Self as Deref>::Target
+            "#]],
+        );
+    }
+
+    #[test]
+    fn no_inference_var_in_completion() {
+        check(
+            r#"
+struct S<T>(T);
+fn test(s: S<Unknown>) {
+    s.$0
+}
+"#,
+            expect![[r#"
+                fd 0 {unknown}
+            "#]],
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
index 7de1bf2dc13..269e40e6ef5 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
@@ -227,9 +227,8 @@ fn get_transformed_assoc_item(
     assoc_item: ast::AssocItem,
     impl_def: hir::Impl,
 ) -> Option<ast::AssocItem> {
-    let assoc_item = assoc_item.clone_for_update();
     let trait_ = impl_def.trait_(ctx.db)?;
-    let source_scope = &ctx.sema.scope_for_def(trait_);
+    let source_scope = &ctx.sema.scope(assoc_item.syntax())?;
     let target_scope = &ctx.sema.scope(ctx.sema.source(impl_def)?.syntax().value)?;
     let transform = PathTransform::trait_impl(
         target_scope,
@@ -238,6 +237,9 @@ fn get_transformed_assoc_item(
         ctx.sema.source(impl_def)?.value,
     );
 
+    let assoc_item = assoc_item.clone_for_update();
+    // FIXME: Paths in nested macros are not handled well. See
+    // `macro_generated_assoc_item2` test.
     transform.apply(assoc_item.syntax());
     assoc_item.remove_attrs_and_docs();
     Some(assoc_item)
@@ -834,6 +836,33 @@ impl Test for () {
     }
 
     #[test]
+    fn fn_with_lifetimes() {
+        check_edit(
+            "fn foo",
+            r#"
+trait Test<'a, 'b, T> {
+    fn foo(&self, a: &'a T, b: &'b T) -> &'a T;
+}
+
+impl<'x, 'y, A> Test<'x, 'y, A> for () {
+    t$0
+}
+"#,
+            r#"
+trait Test<'a, 'b, T> {
+    fn foo(&self, a: &'a T, b: &'b T) -> &'a T;
+}
+
+impl<'x, 'y, A> Test<'x, 'y, A> for () {
+    fn foo(&self, a: &'x A, b: &'y A) -> &'x A {
+    $0
+}
+}
+"#,
+        );
+    }
+
+    #[test]
     fn complete_without_name() {
         let test = |completion: &str, hint: &str, completed: &str, next_sibling: &str| {
             check_edit(
@@ -1194,6 +1223,81 @@ impl Foo for Test {
     }
 
     #[test]
+    fn macro_generated_assoc_item() {
+        check_edit(
+            "fn method",
+            r#"
+macro_rules! ty { () => { i32 } }
+trait SomeTrait { type Output; }
+impl SomeTrait for i32 { type Output = i64; }
+macro_rules! define_method {
+    () => {
+        fn method(&mut self, params: <ty!() as SomeTrait>::Output);
+    };
+}
+trait AnotherTrait { define_method!(); }
+impl AnotherTrait for () {
+    $0
+}
+"#,
+            r#"
+macro_rules! ty { () => { i32 } }
+trait SomeTrait { type Output; }
+impl SomeTrait for i32 { type Output = i64; }
+macro_rules! define_method {
+    () => {
+        fn method(&mut self, params: <ty!() as SomeTrait>::Output);
+    };
+}
+trait AnotherTrait { define_method!(); }
+impl AnotherTrait for () {
+    fn method(&mut self,params: <ty!()as SomeTrait>::Output) {
+    $0
+}
+}
+"#,
+        );
+    }
+
+    // FIXME: `T` in `ty!(T)` should be replaced by `PathTransform`.
+    #[test]
+    fn macro_generated_assoc_item2() {
+        check_edit(
+            "fn method",
+            r#"
+macro_rules! ty { ($me:ty) => { $me } }
+trait SomeTrait { type Output; }
+impl SomeTrait for i32 { type Output = i64; }
+macro_rules! define_method {
+    ($t:ty) => {
+        fn method(&mut self, params: <ty!($t) as SomeTrait>::Output);
+    };
+}
+trait AnotherTrait<T: SomeTrait> { define_method!(T); }
+impl AnotherTrait<i32> for () {
+    $0
+}
+"#,
+            r#"
+macro_rules! ty { ($me:ty) => { $me } }
+trait SomeTrait { type Output; }
+impl SomeTrait for i32 { type Output = i64; }
+macro_rules! define_method {
+    ($t:ty) => {
+        fn method(&mut self, params: <ty!($t) as SomeTrait>::Output);
+    };
+}
+trait AnotherTrait<T: SomeTrait> { define_method!(T); }
+impl AnotherTrait<i32> for () {
+    fn method(&mut self,params: <ty!(T)as SomeTrait>::Output) {
+    $0
+}
+}
+"#,
+        );
+    }
+
+    #[test]
     fn includes_gat_generics() {
         check_edit(
             "type Ty",
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs
index 8edda432ce3..0dd544d0ae2 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs
@@ -1,7 +1,10 @@
 //! Applies changes to the IDE state transactionally.
 
 use base_db::{
-    salsa::{Database, Durability},
+    salsa::{
+        debug::{DebugQueryTable, TableEntry},
+        Database, Durability, Query, QueryTable,
+    },
     Change, SourceRootId,
 };
 use profile::{memory_usage, Bytes};
@@ -47,16 +50,37 @@ impl RootDatabase {
     // | VS Code | **rust-analyzer: Memory Usage (Clears Database)**
     // |===
     // image::https://user-images.githubusercontent.com/48062697/113065592-08559f00-91b1-11eb-8c96-64b88068ec02.gif[]
-    pub fn per_query_memory_usage(&mut self) -> Vec<(String, Bytes)> {
-        let mut acc: Vec<(String, Bytes)> = vec![];
+    pub fn per_query_memory_usage(&mut self) -> Vec<(String, Bytes, usize)> {
+        let mut acc: Vec<(String, Bytes, usize)> = vec![];
+
+        fn collect_query_count<'q, Q>(table: &QueryTable<'q, Q>) -> usize
+        where
+            QueryTable<'q, Q>: DebugQueryTable,
+            Q: Query,
+            <Q as Query>::Storage: 'q,
+        {
+            struct EntryCounter(usize);
+            impl<K, V> FromIterator<TableEntry<K, V>> for EntryCounter {
+                fn from_iter<T>(iter: T) -> EntryCounter
+                where
+                    T: IntoIterator<Item = TableEntry<K, V>>,
+                {
+                    EntryCounter(iter.into_iter().count())
+                }
+            }
+            table.entries::<EntryCounter>().0
+        }
+
         macro_rules! purge_each_query {
             ($($q:path)*) => {$(
                 let before = memory_usage().allocated;
-                $q.in_db(self).purge();
+                let table = $q.in_db(self);
+                let count = collect_query_count(&table);
+                table.purge();
                 let after = memory_usage().allocated;
                 let q: $q = Default::default();
                 let name = format!("{:?}", q);
-                acc.push((name, before - after));
+                acc.push((name, before - after, count));
             )*}
         }
         purge_each_query![
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs
index 0ee627a44c6..73e6a920ee4 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs
@@ -9,6 +9,19 @@ use syntax::{
     ted, SyntaxNode,
 };
 
+#[derive(Default)]
+struct AstSubsts {
+    types_and_consts: Vec<TypeOrConst>,
+    lifetimes: Vec<ast::LifetimeArg>,
+}
+
+enum TypeOrConst {
+    Either(ast::TypeArg), // indistinguishable type or const param
+    Const(ast::ConstArg),
+}
+
+type LifetimeName = String;
+
 /// `PathTransform` substitutes path in SyntaxNodes in bulk.
 ///
 /// This is mostly useful for IDE code generation. If you paste some existing
@@ -34,7 +47,7 @@ use syntax::{
 /// ```
 pub struct PathTransform<'a> {
     generic_def: Option<hir::GenericDef>,
-    substs: Vec<ast::Type>,
+    substs: AstSubsts,
     target_scope: &'a SemanticsScope<'a>,
     source_scope: &'a SemanticsScope<'a>,
 }
@@ -72,7 +85,12 @@ impl<'a> PathTransform<'a> {
         target_scope: &'a SemanticsScope<'a>,
         source_scope: &'a SemanticsScope<'a>,
     ) -> PathTransform<'a> {
-        PathTransform { source_scope, target_scope, generic_def: None, substs: Vec::new() }
+        PathTransform {
+            source_scope,
+            target_scope,
+            generic_def: None,
+            substs: AstSubsts::default(),
+        }
     }
 
     pub fn apply(&self, syntax: &SyntaxNode) {
@@ -91,12 +109,14 @@ impl<'a> PathTransform<'a> {
         let target_module = self.target_scope.module();
         let source_module = self.source_scope.module();
         let skip = match self.generic_def {
-            // this is a trait impl, so we need to skip the first type parameter -- this is a bit hacky
+            // this is a trait impl, so we need to skip the first type parameter (i.e. Self) -- this is a bit hacky
             Some(hir::GenericDef::Trait(_)) => 1,
             _ => 0,
         };
-        let substs_by_param: FxHashMap<_, _> = self
-            .generic_def
+        let mut type_substs: FxHashMap<hir::TypeParam, ast::Type> = Default::default();
+        let mut const_substs: FxHashMap<hir::ConstParam, SyntaxNode> = Default::default();
+        let mut default_types: Vec<hir::TypeParam> = Default::default();
+        self.generic_def
             .into_iter()
             .flat_map(|it| it.type_params(db))
             .skip(skip)
@@ -106,53 +126,105 @@ impl<'a> PathTransform<'a> {
             // can still hit those trailing values and check if they actually have
             // a default type. If they do, go for that type from `hir` to `ast` so
             // the resulting change can be applied correctly.
-            .zip(self.substs.iter().map(Some).chain(std::iter::repeat(None)))
-            .filter_map(|(k, v)| match k.split(db) {
-                Either::Left(_) => None,
-                Either::Right(t) => match v {
-                    Some(v) => Some((k, v.clone())),
-                    None => {
-                        let default = t.default(db)?;
-                        Some((
-                            k,
-                            ast::make::ty(
-                                &default
-                                    .display_source_code(db, source_module.into(), false)
-                                    .ok()?,
-                            ),
-                        ))
+            .zip(self.substs.types_and_consts.iter().map(Some).chain(std::iter::repeat(None)))
+            .for_each(|(k, v)| match (k.split(db), v) {
+                (Either::Right(k), Some(TypeOrConst::Either(v))) => {
+                    if let Some(ty) = v.ty() {
+                        type_substs.insert(k, ty.clone());
+                    }
+                }
+                (Either::Right(k), None) => {
+                    if let Some(default) = k.default(db) {
+                        if let Some(default) =
+                            &default.display_source_code(db, source_module.into(), false).ok()
+                        {
+                            type_substs.insert(k, ast::make::ty(default).clone_for_update());
+                            default_types.push(k);
+                        }
+                    }
+                }
+                (Either::Left(k), Some(TypeOrConst::Either(v))) => {
+                    if let Some(ty) = v.ty() {
+                        const_substs.insert(k, ty.syntax().clone());
                     }
-                },
-            })
+                }
+                (Either::Left(k), Some(TypeOrConst::Const(v))) => {
+                    if let Some(expr) = v.expr() {
+                        // FIXME: expressions in curly brackets can cause ambiguity after insertion
+                        // (e.g. `N * 2` -> `{1 + 1} * 2`; it's unclear whether `{1 + 1}`
+                        // is a standalone statement or a part of another expresson)
+                        // and sometimes require slight modifications; see
+                        // https://doc.rust-lang.org/reference/statements.html#expression-statements
+                        const_substs.insert(k, expr.syntax().clone());
+                    }
+                }
+                (Either::Left(_), None) => (), // FIXME: get default const value
+                _ => (),                       // ignore mismatching params
+            });
+        let lifetime_substs: FxHashMap<_, _> = self
+            .generic_def
+            .into_iter()
+            .flat_map(|it| it.lifetime_params(db))
+            .zip(self.substs.lifetimes.clone())
+            .filter_map(|(k, v)| Some((k.name(db).display(db.upcast()).to_string(), v.lifetime()?)))
             .collect();
-        Ctx { substs: substs_by_param, target_module, source_scope: self.source_scope }
+        let ctx = Ctx {
+            type_substs,
+            const_substs,
+            lifetime_substs,
+            target_module,
+            source_scope: self.source_scope,
+        };
+        ctx.transform_default_type_substs(default_types);
+        ctx
     }
 }
 
 struct Ctx<'a> {
-    substs: FxHashMap<hir::TypeOrConstParam, ast::Type>,
+    type_substs: FxHashMap<hir::TypeParam, ast::Type>,
+    const_substs: FxHashMap<hir::ConstParam, SyntaxNode>,
+    lifetime_substs: FxHashMap<LifetimeName, ast::Lifetime>,
     target_module: hir::Module,
     source_scope: &'a SemanticsScope<'a>,
 }
 
+fn postorder(item: &SyntaxNode) -> impl Iterator<Item = SyntaxNode> {
+    item.preorder().filter_map(|event| match event {
+        syntax::WalkEvent::Enter(_) => None,
+        syntax::WalkEvent::Leave(node) => Some(node),
+    })
+}
+
 impl<'a> Ctx<'a> {
     fn apply(&self, item: &SyntaxNode) {
         // `transform_path` may update a node's parent and that would break the
         // tree traversal. Thus all paths in the tree are collected into a vec
         // so that such operation is safe.
-        let paths = item
-            .preorder()
-            .filter_map(|event| match event {
-                syntax::WalkEvent::Enter(_) => None,
-                syntax::WalkEvent::Leave(node) => Some(node),
-            })
-            .filter_map(ast::Path::cast)
-            .collect::<Vec<_>>();
-
+        let paths = postorder(item).filter_map(ast::Path::cast).collect::<Vec<_>>();
         for path in paths {
             self.transform_path(path);
         }
+
+        postorder(item).filter_map(ast::Lifetime::cast).for_each(|lifetime| {
+            if let Some(subst) = self.lifetime_substs.get(&lifetime.syntax().text().to_string()) {
+                ted::replace(lifetime.syntax(), subst.clone_subtree().clone_for_update().syntax());
+            }
+        });
     }
+
+    fn transform_default_type_substs(&self, default_types: Vec<hir::TypeParam>) {
+        for k in default_types {
+            let v = self.type_substs.get(&k).unwrap();
+            // `transform_path` may update a node's parent and that would break the
+            // tree traversal. Thus all paths in the tree are collected into a vec
+            // so that such operation is safe.
+            let paths = postorder(&v.syntax()).filter_map(ast::Path::cast).collect::<Vec<_>>();
+            for path in paths {
+                self.transform_path(path);
+            }
+        }
+    }
+
     fn transform_path(&self, path: ast::Path) -> Option<()> {
         if path.qualifier().is_some() {
             return None;
@@ -169,7 +241,7 @@ impl<'a> Ctx<'a> {
 
         match resolution {
             hir::PathResolution::TypeParam(tp) => {
-                if let Some(subst) = self.substs.get(&tp.merge()) {
+                if let Some(subst) = self.type_substs.get(&tp) {
                     let parent = path.syntax().parent()?;
                     if let Some(parent) = ast::Path::cast(parent.clone()) {
                         // Path inside path means that there is an associated
@@ -236,8 +308,12 @@ impl<'a> Ctx<'a> {
                 }
                 ted::replace(path.syntax(), res.syntax())
             }
+            hir::PathResolution::ConstParam(cp) => {
+                if let Some(subst) = self.const_substs.get(&cp) {
+                    ted::replace(path.syntax(), subst.clone_subtree().clone_for_update());
+                }
+            }
             hir::PathResolution::Local(_)
-            | hir::PathResolution::ConstParam(_)
             | hir::PathResolution::SelfType(_)
             | hir::PathResolution::Def(_)
             | hir::PathResolution::BuiltinAttr(_)
@@ -250,7 +326,7 @@ impl<'a> Ctx<'a> {
 
 // FIXME: It would probably be nicer if we could get this via HIR (i.e. get the
 // trait ref, and then go from the types in the substs back to the syntax).
-fn get_syntactic_substs(impl_def: ast::Impl) -> Option<Vec<ast::Type>> {
+fn get_syntactic_substs(impl_def: ast::Impl) -> Option<AstSubsts> {
     let target_trait = impl_def.trait_()?;
     let path_type = match target_trait {
         ast::Type::PathType(path) => path,
@@ -261,13 +337,22 @@ fn get_syntactic_substs(impl_def: ast::Impl) -> Option<Vec<ast::Type>> {
     get_type_args_from_arg_list(generic_arg_list)
 }
 
-fn get_type_args_from_arg_list(generic_arg_list: ast::GenericArgList) -> Option<Vec<ast::Type>> {
-    let mut result = Vec::new();
-    for generic_arg in generic_arg_list.generic_args() {
-        if let ast::GenericArg::TypeArg(type_arg) = generic_arg {
-            result.push(type_arg.ty()?)
+fn get_type_args_from_arg_list(generic_arg_list: ast::GenericArgList) -> Option<AstSubsts> {
+    let mut result = AstSubsts::default();
+    generic_arg_list.generic_args().for_each(|generic_arg| match generic_arg {
+        // Const params are marked as consts on definition only,
+        // being passed to the trait they are indistguishable from type params;
+        // anyway, we don't really need to distinguish them here.
+        ast::GenericArg::TypeArg(type_arg) => {
+            result.types_and_consts.push(TypeOrConst::Either(type_arg))
         }
-    }
+        // Some const values are recognized correctly.
+        ast::GenericArg::ConstArg(const_arg) => {
+            result.types_and_consts.push(TypeOrConst::Const(const_arg));
+        }
+        ast::GenericArg::LifetimeArg(l_arg) => result.lifetimes.push(l_arg),
+        _ => (),
+    });
 
     Some(result)
 }
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
index 73cd5dcaf23..e8ff107bd49 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
@@ -243,6 +243,8 @@ impl Definition {
                 DefWithBody::Const(c) => c.source(db).map(|src| src.syntax().cloned()),
                 DefWithBody::Static(s) => s.source(db).map(|src| src.syntax().cloned()),
                 DefWithBody::Variant(v) => v.source(db).map(|src| src.syntax().cloned()),
+                // FIXME: implement
+                DefWithBody::InTypeConst(_) => return SearchScope::empty(),
             };
             return match def {
                 Some(def) => SearchScope::file_range(def.as_ref().original_file_range_full(db)),
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt
index 77714efa350..7834c66033c 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt
+++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt
@@ -20,8 +20,10 @@
                     ),
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: STRUCT,
@@ -47,8 +49,10 @@
                     ),
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: STRUCT,
@@ -74,8 +78,10 @@
                     ),
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: STRUCT,
@@ -101,8 +107,10 @@
                     ),
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: STRUCT,
@@ -128,8 +136,10 @@
                     ),
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: STRUCT,
@@ -155,8 +165,10 @@
                     ),
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: STRUCT,
@@ -182,8 +194,10 @@
                     ),
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: STRUCT,
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt
index b5adfc13d96..1a00e29384e 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt
+++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt
@@ -18,8 +18,10 @@
                     },
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: TYPE_ALIAS,
@@ -43,8 +45,10 @@
                     },
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: CONST,
@@ -68,8 +72,10 @@
                     },
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: CONST,
@@ -95,8 +101,10 @@
                     ),
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: ENUM,
@@ -122,8 +130,10 @@
                     },
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: MACRO_DEF,
@@ -147,8 +157,10 @@
                     },
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: STATIC,
@@ -174,8 +186,10 @@
                     ),
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: STRUCT,
@@ -201,8 +215,12 @@
                     ),
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        2147483648,
+                    hir_file_id: MacroFile(
+                        MacroFile {
+                            macro_call_id: MacroCallId(
+                                0,
+                            ),
+                        },
                     ),
                     ptr: SyntaxNodePtr {
                         kind: STRUCT,
@@ -228,8 +246,10 @@
                     ),
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: STRUCT,
@@ -257,8 +277,10 @@
                     ),
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: STRUCT,
@@ -286,8 +308,10 @@
                     ),
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: STRUCT,
@@ -311,8 +335,10 @@
                     },
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: TRAIT,
@@ -338,8 +364,10 @@
                     ),
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: UNION,
@@ -365,8 +393,10 @@
                     },
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: MODULE,
@@ -392,8 +422,10 @@
                     },
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: MODULE,
@@ -419,8 +451,10 @@
                     },
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: MACRO_RULES,
@@ -444,8 +478,10 @@
                     },
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: FN,
@@ -471,8 +507,10 @@
                     },
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: MACRO_RULES,
@@ -496,8 +534,10 @@
                     },
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: FN,
@@ -521,8 +561,10 @@
                     },
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: FN,
@@ -561,8 +603,10 @@
                     ),
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: STRUCT,
@@ -599,8 +643,10 @@
                     ),
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        1,
+                    hir_file_id: FileId(
+                        FileId(
+                            1,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: STRUCT,
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
index a2f96977581..f75ebfa12eb 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
@@ -4423,6 +4423,29 @@ const FOO$0: Option<&i32> = Some(2).as_ref();
 }
 
 #[test]
+fn hover_const_eval_dyn_trait() {
+    check(
+        r#"
+//- minicore: fmt, coerce_unsized, builtin_impls
+use core::fmt::Debug;
+
+const FOO$0: &dyn Debug = &2i32;
+"#,
+        expect![[r#"
+            *FOO*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            const FOO: &dyn Debug = &2
+            ```
+        "#]],
+    );
+}
+
+#[test]
 fn hover_const_eval_slice() {
     check(
         r#"
@@ -4502,6 +4525,26 @@ const FOO$0: Tree = {
             ```
         "#]],
     );
+    // FIXME: Show the data of unsized structs
+    check(
+        r#"
+//- minicore: slice, index, coerce_unsized, transmute
+#[repr(transparent)]
+struct S<T: ?Sized>(T);
+const FOO$0: &S<[u8]> = core::mem::transmute::<&[u8], _>(&[1, 2, 3]);
+"#,
+        expect![[r#"
+            *FOO*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            const FOO: &S<[u8]> = &S
+            ```
+        "#]],
+    );
 }
 
 #[test]
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs
index ce1e03a069b..84eac16b9f9 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs
@@ -474,7 +474,7 @@ fn main() {
                                         file_id: FileId(
                                             1,
                                         ),
-                                        range: 9332..9340,
+                                        range: 9287..9295,
                                     },
                                 ),
                                 tooltip: "",
@@ -487,7 +487,7 @@ fn main() {
                                         file_id: FileId(
                                             1,
                                         ),
-                                        range: 9364..9368,
+                                        range: 9319..9323,
                                     },
                                 ),
                                 tooltip: "",
@@ -511,7 +511,7 @@ fn main() {
                                         file_id: FileId(
                                             1,
                                         ),
-                                        range: 9332..9340,
+                                        range: 9287..9295,
                                     },
                                 ),
                                 tooltip: "",
@@ -524,7 +524,7 @@ fn main() {
                                         file_id: FileId(
                                             1,
                                         ),
-                                        range: 9364..9368,
+                                        range: 9319..9323,
                                     },
                                 ),
                                 tooltip: "",
@@ -548,7 +548,7 @@ fn main() {
                                         file_id: FileId(
                                             1,
                                         ),
-                                        range: 9332..9340,
+                                        range: 9287..9295,
                                     },
                                 ),
                                 tooltip: "",
@@ -561,7 +561,7 @@ fn main() {
                                         file_id: FileId(
                                             1,
                                         ),
-                                        range: 9364..9368,
+                                        range: 9319..9323,
                                     },
                                 ),
                                 tooltip: "",
diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs
index 87e769e4230..f195f78b3ab 100644
--- a/src/tools/rust-analyzer/crates/ide/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs
@@ -181,7 +181,7 @@ impl AnalysisHost {
     }
 
     /// NB: this clears the database
-    pub fn per_query_memory_usage(&mut self) -> Vec<(String, profile::Bytes)> {
+    pub fn per_query_memory_usage(&mut self) -> Vec<(String, profile::Bytes, usize)> {
         self.db.per_query_memory_usage()
     }
     pub fn request_cancellation(&mut self) {
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs
index 8c02fe81648..dc06591ffea 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs
@@ -243,6 +243,9 @@ fn traverse(
     let mut attr_or_derive_item = None;
     let mut current_macro: Option<ast::Macro> = None;
     let mut macro_highlighter = MacroHighlighter::default();
+
+    // FIXME: these are not perfectly accurate, we determine them by the real file's syntax tree
+    // an an attribute nested in a macro call will not emit `inside_attribute`
     let mut inside_attribute = false;
     let mut inside_macro_call = false;
 
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
index 327e1502d19..fa374b04f19 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -86,6 +86,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">assert</span> <span class="brace">{</span><span class="brace">}</span>
 <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">rustc_builtin_macro</span><span class="attribute_bracket attribute">]</span>
 <span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">asm</span> <span class="brace">{</span><span class="brace">}</span>
+<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">rustc_builtin_macro</span><span class="attribute_bracket attribute">]</span>
+<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">concat</span> <span class="brace">{</span><span class="brace">}</span>
 
 <span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">toho</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panic<span class="punctuation">!</span><span class="parenthesis">(</span><span class="string_literal">"not yet implemented"</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
@@ -172,4 +174,5 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="macro">toho</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">fmt"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
     <span class="macro unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"mov eax, </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
     <span class="macro">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">concat</span><span class="punctuation macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">format_args</span><span class="operator macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 <span class="brace">}</span></code></pre>
\ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
index 887d18b989a..497992f684c 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
@@ -432,6 +432,8 @@ macro_rules! panic {}
 macro_rules! assert {}
 #[rustc_builtin_macro]
 macro_rules! asm {}
+#[rustc_builtin_macro]
+macro_rules! concat {}
 
 macro_rules! toho {
     () => ($crate::panic!("not yet implemented"));
@@ -518,6 +520,7 @@ fn main() {
     toho!("{}fmt", 0);
     asm!("mov eax, {0}");
     format_args!(concat!("{}"), "{}");
+    format_args!("{}", format_args!("{}", 0));
 }"#,
         expect_file!["./test_data/highlight_strings.html"],
         false,
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs
index 93ef4835027..96a6cdeaaff 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs
@@ -153,7 +153,9 @@ fn array_or_slice_type(p: &mut Parser<'_>) {
         // type T = [(); 92];
         T![;] => {
             p.bump(T![;]);
+            let m = p.start();
             expressions::expr(p);
+            m.complete(p, CONST_ARG);
             p.expect(T![']']);
             ARRAY_TYPE
         }
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0017_array_type.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0017_array_type.rast
index 2a5c644d467..0d50144b730 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0017_array_type.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0017_array_type.rast
@@ -14,8 +14,9 @@ SOURCE_FILE
         R_PAREN ")"
       SEMICOLON ";"
       WHITESPACE " "
-      LITERAL
-        INT_NUMBER "92"
+      CONST_ARG
+        LITERAL
+          INT_NUMBER "92"
       R_BRACK "]"
     SEMICOLON ";"
   WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0030_traits.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0030_traits.rast
index 44423581e6a..3965ae9596b 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0030_traits.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0030_traits.rast
@@ -51,8 +51,9 @@ SOURCE_FILE
                       IDENT "i32"
               SEMICOLON ";"
               WHITESPACE " "
-              LITERAL
-                INT_NUMBER "1"
+              CONST_ARG
+                LITERAL
+                  INT_NUMBER "1"
               R_BRACK "]"
           R_PAREN ")"
         SEMICOLON ";"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0043_complex_assignment.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0043_complex_assignment.rast
index 3b02c3f96ae..f3c85b45b6b 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0043_complex_assignment.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0043_complex_assignment.rast
@@ -24,8 +24,9 @@ SOURCE_FILE
                   IDENT "u8"
           SEMICOLON ";"
           WHITESPACE " "
-          LITERAL
-            INT_NUMBER "1"
+          CONST_ARG
+            LITERAL
+              INT_NUMBER "1"
           R_BRACK "]"
       WHITESPACE " "
       R_CURLY "}"
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server.rs
index 37a45bcbb83..6fd8de59342 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server.rs
@@ -97,7 +97,7 @@ impl server::TokenStream for RustAnalyzer {
         match tree {
             bridge::TokenTree::Group(group) => {
                 let group = Group {
-                    delimiter: delim_to_internal(group.delimiter),
+                    delimiter: delim_to_internal(group.delimiter, group.span),
                     token_trees: match group.stream {
                         Some(stream) => stream.into_iter().collect(),
                         None => Vec::new(),
@@ -221,14 +221,14 @@ impl server::TokenStream for RustAnalyzer {
     }
 }
 
-fn delim_to_internal(d: proc_macro::Delimiter) -> tt::Delimiter {
+fn delim_to_internal(d: proc_macro::Delimiter, span: bridge::DelimSpan<Span>) -> tt::Delimiter {
     let kind = match d {
         proc_macro::Delimiter::Parenthesis => tt::DelimiterKind::Parenthesis,
         proc_macro::Delimiter::Brace => tt::DelimiterKind::Brace,
         proc_macro::Delimiter::Bracket => tt::DelimiterKind::Bracket,
         proc_macro::Delimiter::None => tt::DelimiterKind::Invisible,
     };
-    tt::Delimiter { open: tt::TokenId::unspecified(), close: tt::TokenId::unspecified(), kind }
+    tt::Delimiter { open: span.open, close: span.close, kind }
 }
 
 fn delim_to_external(d: tt::Delimiter) -> proc_macro::Delimiter {
diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
index 0aca620a675..b5fe237fc4c 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
@@ -1403,6 +1403,12 @@ fn handle_hack_cargo_workspace(
             .unwrap();
         crate_graph.remove_and_replace(fake, original).unwrap();
     }
+    for (_, c) in crate_graph.iter_mut() {
+        if c.origin.is_local() {
+            // LangCrateOrigin::Other is good enough for a hack.
+            c.origin = CrateOrigin::Lang(LangCrateOrigin::Other);
+        }
+    }
     sysroot
         .crates()
         .filter_map(|krate| {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
index 77f02a83101..5b72d57560b 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
@@ -68,7 +68,6 @@ ide-db.workspace = true
 ide-ssr.workspace = true
 ide.workspace = true
 proc-macro-api.workspace = true
-proc-macro-srv-cli.workspace = true
 profile.workspace = true
 project-model.workspace = true
 stdx.workspace = true
@@ -95,5 +94,5 @@ mbe.workspace = true
 [features]
 jemalloc = ["jemallocator", "profile/jemalloc"]
 force-always-assert = ["always-assert/force"]
-sysroot-abi = ["proc-macro-srv-cli/sysroot-abi"]
+sysroot-abi = []
 in-rust-tree = ["sysroot-abi", "ide/in-rust-tree", "syntax/in-rust-tree"]
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs
index d5d877680a0..e3520192110 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs
@@ -50,21 +50,24 @@ fn report_metric(metric: &str, value: u64, unit: &str) {
 }
 
 fn print_memory_usage(mut host: AnalysisHost, vfs: Vfs) {
-    let mut mem = host.per_query_memory_usage();
+    let mem = host.per_query_memory_usage();
 
     let before = profile::memory_usage();
     drop(vfs);
     let vfs = before.allocated - profile::memory_usage().allocated;
-    mem.push(("VFS".into(), vfs));
 
     let before = profile::memory_usage();
     drop(host);
-    mem.push(("Unaccounted".into(), before.allocated - profile::memory_usage().allocated));
+    let unaccounted = before.allocated - profile::memory_usage().allocated;
+    let remaining = profile::memory_usage().allocated;
 
-    mem.push(("Remaining".into(), profile::memory_usage().allocated));
-
-    for (name, bytes) in mem {
+    for (name, bytes, entries) in mem {
         // NOTE: Not a debug print, so avoid going through the `eprintln` defined above.
-        eprintln!("{bytes:>8} {name}");
+        eprintln!("{bytes:>8} {entries:>6} {name}");
     }
+    eprintln!("{vfs:>8}        VFS");
+
+    eprintln!("{unaccounted:>8}        Unaccounted");
+
+    eprintln!("{remaining:>8}        Remaining");
 }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
index 2c2a9a18d2e..4cb917ce290 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -8,18 +8,20 @@ use std::{
 
 use hir::{
     db::{DefDatabase, ExpandDatabase, HirDatabase},
-    AssocItem, Crate, Function, HasCrate, HasSource, HirDisplay, ModuleDef,
+    Adt, AssocItem, Crate, DefWithBody, HasCrate, HasSource, HirDisplay, ModuleDef, Name,
 };
 use hir_def::{
     body::{BodySourceMap, SyntheticSyntax},
     hir::{ExprId, PatId},
-    FunctionId,
 };
 use hir_ty::{Interner, Substitution, TyExt, TypeFlags};
-use ide::{Analysis, AnalysisHost, LineCol, RootDatabase};
-use ide_db::base_db::{
-    salsa::{self, debug::DebugQueryTable, ParallelDatabase},
-    SourceDatabase, SourceDatabaseExt,
+use ide::{LineCol, RootDatabase};
+use ide_db::{
+    base_db::{
+        salsa::{self, debug::DebugQueryTable, ParallelDatabase},
+        SourceDatabase, SourceDatabaseExt,
+    },
+    LineIndexDatabase,
 };
 use itertools::Itertools;
 use oorandom::Rand32;
@@ -27,7 +29,6 @@ use profile::{Bytes, StopWatch};
 use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace, RustLibSource};
 use rayon::prelude::*;
 use rustc_hash::FxHashSet;
-use stdx::format_to;
 use syntax::{AstNode, SyntaxNode};
 use vfs::{AbsPathBuf, Vfs, VfsPath};
 
@@ -120,7 +121,7 @@ impl flags::AnalysisStats {
 
         eprint!("  crates: {num_crates}");
         let mut num_decls = 0;
-        let mut funcs = Vec::new();
+        let mut bodies = Vec::new();
         let mut adts = Vec::new();
         let mut consts = Vec::new();
         while let Some(module) = visit_queue.pop() {
@@ -130,40 +131,71 @@ impl flags::AnalysisStats {
                 for decl in module.declarations(db) {
                     num_decls += 1;
                     match decl {
-                        ModuleDef::Function(f) => funcs.push(f),
-                        ModuleDef::Adt(a) => adts.push(a),
-                        ModuleDef::Const(c) => consts.push(c),
+                        ModuleDef::Function(f) => bodies.push(DefWithBody::from(f)),
+                        ModuleDef::Adt(a) => {
+                            if let Adt::Enum(e) = a {
+                                for v in e.variants(db) {
+                                    bodies.push(DefWithBody::from(v));
+                                }
+                            }
+                            adts.push(a)
+                        }
+                        ModuleDef::Const(c) => {
+                            bodies.push(DefWithBody::from(c));
+                            consts.push(c)
+                        }
+                        ModuleDef::Static(s) => bodies.push(DefWithBody::from(s)),
                         _ => (),
-                    }
+                    };
                 }
 
                 for impl_def in module.impl_defs(db) {
                     for item in impl_def.items(db) {
                         num_decls += 1;
-                        if let AssocItem::Function(f) = item {
-                            funcs.push(f);
+                        match item {
+                            AssocItem::Function(f) => bodies.push(DefWithBody::from(f)),
+                            AssocItem::Const(c) => {
+                                bodies.push(DefWithBody::from(c));
+                                consts.push(c);
+                            }
+                            _ => (),
                         }
                     }
                 }
             }
         }
-        eprintln!(", mods: {}, decls: {num_decls}, fns: {}", visited_modules.len(), funcs.len());
+        eprintln!(
+            ", mods: {}, decls: {num_decls}, bodies: {}, adts: {}, consts: {}",
+            visited_modules.len(),
+            bodies.len(),
+            adts.len(),
+            consts.len(),
+        );
         eprintln!("{:<20} {}", "Item Collection:", analysis_sw.elapsed());
 
         if self.randomize {
-            shuffle(&mut rng, &mut funcs);
+            shuffle(&mut rng, &mut bodies);
+        }
+
+        if !self.skip_lowering {
+            self.run_body_lowering(db, &vfs, &bodies, verbosity);
         }
 
         if !self.skip_inference {
-            self.run_inference(&host, db, &vfs, &funcs, verbosity);
+            self.run_inference(db, &vfs, &bodies, verbosity);
         }
 
         if !self.skip_mir_stats {
-            self.run_mir_lowering(db, &funcs, verbosity);
+            self.run_mir_lowering(db, &bodies, verbosity);
         }
 
-        self.run_data_layout(db, &adts, verbosity);
-        self.run_const_eval(db, &consts, verbosity);
+        if !self.skip_data_layout {
+            self.run_data_layout(db, &adts, verbosity);
+        }
+
+        if !self.skip_const_eval {
+            self.run_const_eval(db, &consts, verbosity);
+        }
 
         let total_span = analysis_sw.elapsed();
         eprintln!("{:<20} {total_span}", "Total:");
@@ -210,8 +242,10 @@ impl flags::AnalysisStats {
                 continue;
             }
             all += 1;
-            let Err(e) = db.layout_of_adt(hir_def::AdtId::from(a).into(), Substitution::empty(Interner), a.krate(db).into()) else {
-                continue;
+            let Err(e)
+                = db.layout_of_adt(hir_def::AdtId::from(a).into(), Substitution::empty(Interner), a.krate(db).into())
+            else {
+                continue
             };
             if verbosity.is_spammy() {
                 let full_name = a
@@ -227,9 +261,11 @@ impl flags::AnalysisStats {
             }
             fail += 1;
         }
-        eprintln!("{:<20} {}", "Data layouts:", sw.elapsed());
+        let data_layout_time = sw.elapsed();
+        eprintln!("{:<20} {}", "Data layouts:", data_layout_time);
         eprintln!("Failed data layouts: {fail} ({}%)", percentage(fail, all));
         report_metric("failed data layouts", fail, "#");
+        report_metric("data layout time", data_layout_time.time.as_millis() as u64, "ms");
     }
 
     fn run_const_eval(&self, db: &RootDatabase, consts: &[hir::Const], verbosity: Verbosity) {
@@ -255,61 +291,63 @@ impl flags::AnalysisStats {
             }
             fail += 1;
         }
-        eprintln!("{:<20} {}", "Const evaluation:", sw.elapsed());
+        let const_eval_time = sw.elapsed();
+        eprintln!("{:<20} {}", "Const evaluation:", const_eval_time);
         eprintln!("Failed const evals: {fail} ({}%)", percentage(fail, all));
         report_metric("failed const evals", fail, "#");
+        report_metric("const eval time", const_eval_time.time.as_millis() as u64, "ms");
     }
 
-    fn run_mir_lowering(&self, db: &RootDatabase, funcs: &[Function], verbosity: Verbosity) {
+    fn run_mir_lowering(&self, db: &RootDatabase, bodies: &[DefWithBody], verbosity: Verbosity) {
         let mut sw = self.stop_watch();
-        let all = funcs.len() as u64;
+        let all = bodies.len() as u64;
         let mut fail = 0;
-        for f in funcs {
-            let Err(e) = db.mir_body(FunctionId::from(*f).into()) else {
+        for &body in bodies {
+            let Err(e) = db.mir_body(body.into()) else {
                 continue;
             };
             if verbosity.is_spammy() {
-                let full_name = f
+                let full_name = body
                     .module(db)
                     .path_to_root(db)
                     .into_iter()
                     .rev()
                     .filter_map(|it| it.name(db))
-                    .chain(Some(f.name(db)))
+                    .chain(Some(body.name(db).unwrap_or_else(Name::missing)))
                     .map(|it| it.display(db).to_string())
                     .join("::");
                 println!("Mir body for {full_name} failed due {e:?}");
             }
             fail += 1;
         }
-        eprintln!("{:<20} {}", "MIR lowering:", sw.elapsed());
+        let mir_lowering_time = sw.elapsed();
+        eprintln!("{:<20} {}", "MIR lowering:", mir_lowering_time);
         eprintln!("Mir failed bodies: {fail} ({}%)", percentage(fail, all));
         report_metric("mir failed bodies", fail, "#");
+        report_metric("mir lowering time", mir_lowering_time.time.as_millis() as u64, "ms");
     }
 
     fn run_inference(
         &self,
-        host: &AnalysisHost,
         db: &RootDatabase,
         vfs: &Vfs,
-        funcs: &[Function],
+        bodies: &[DefWithBody],
         verbosity: Verbosity,
     ) {
         let mut bar = match verbosity {
             Verbosity::Quiet | Verbosity::Spammy => ProgressReport::hidden(),
             _ if self.parallel || self.output.is_some() => ProgressReport::hidden(),
-            _ => ProgressReport::new(funcs.len() as u64),
+            _ => ProgressReport::new(bodies.len() as u64),
         };
 
         if self.parallel {
             let mut inference_sw = self.stop_watch();
             let snap = Snap(db.snapshot());
-            funcs
+            bodies
                 .par_iter()
-                .map_with(snap, |snap, &f| {
-                    let f_id = FunctionId::from(f);
-                    snap.0.body(f_id.into());
-                    snap.0.infer(f_id.into());
+                .map_with(snap, |snap, &body| {
+                    snap.0.body(body.into());
+                    snap.0.infer(body.into());
                 })
                 .count();
             eprintln!("{:<20} {}", "Parallel Inference:", inference_sw.elapsed());
@@ -325,39 +363,58 @@ impl flags::AnalysisStats {
         let mut num_pats_unknown = 0;
         let mut num_pats_partially_unknown = 0;
         let mut num_pat_type_mismatches = 0;
-        let analysis = host.analysis();
-        for f in funcs.iter().copied() {
-            let name = f.name(db);
-            let full_name = f
-                .module(db)
-                .path_to_root(db)
-                .into_iter()
-                .rev()
-                .filter_map(|it| it.name(db))
-                .chain(Some(f.name(db)))
-                .map(|it| it.display(db).to_string())
-                .join("::");
+        for &body_id in bodies {
+            let name = body_id.name(db).unwrap_or_else(Name::missing);
+            let module = body_id.module(db);
+            let full_name = move || {
+                module
+                    .krate()
+                    .display_name(db)
+                    .map(|it| it.canonical_name().to_string())
+                    .into_iter()
+                    .chain(
+                        module
+                            .path_to_root(db)
+                            .into_iter()
+                            .filter_map(|it| it.name(db))
+                            .rev()
+                            .chain(Some(body_id.name(db).unwrap_or_else(Name::missing)))
+                            .map(|it| it.display(db).to_string()),
+                    )
+                    .join("::")
+            };
             if let Some(only_name) = self.only.as_deref() {
-                if name.display(db).to_string() != only_name && full_name != only_name {
+                if name.display(db).to_string() != only_name && full_name() != only_name {
                     continue;
                 }
             }
-            let mut msg = format!("processing: {full_name}");
-            if verbosity.is_verbose() {
-                if let Some(src) = f.source(db) {
-                    let original_file = src.file_id.original_file(db);
-                    let path = vfs.file_path(original_file);
-                    let syntax_range = src.value.syntax().text_range();
-                    format_to!(msg, " ({} {:?})", path, syntax_range);
+            let msg = move || {
+                if verbosity.is_verbose() {
+                    let source = match body_id {
+                        DefWithBody::Function(it) => it.source(db).map(|it| it.syntax().cloned()),
+                        DefWithBody::Static(it) => it.source(db).map(|it| it.syntax().cloned()),
+                        DefWithBody::Const(it) => it.source(db).map(|it| it.syntax().cloned()),
+                        DefWithBody::Variant(it) => it.source(db).map(|it| it.syntax().cloned()),
+                        DefWithBody::InTypeConst(_) => unimplemented!(),
+                    };
+                    if let Some(src) = source {
+                        let original_file = src.file_id.original_file(db);
+                        let path = vfs.file_path(original_file);
+                        let syntax_range = src.value.text_range();
+                        format!("processing: {} ({} {:?})", full_name(), path, syntax_range)
+                    } else {
+                        format!("processing: {}", full_name())
+                    }
+                } else {
+                    format!("processing: {}", full_name())
                 }
-            }
+            };
             if verbosity.is_spammy() {
-                bar.println(msg.to_string());
+                bar.println(msg());
             }
-            bar.set_message(&msg);
-            let f_id = FunctionId::from(f);
-            let (body, sm) = db.body_with_source_map(f_id.into());
-            let inference_result = db.infer(f_id.into());
+            bar.set_message(msg);
+            let (body, sm) = db.body_with_source_map(body_id.into());
+            let inference_result = db.infer(body_id.into());
 
             // region:expressions
             let (previous_exprs, previous_unknown, previous_partially_unknown) =
@@ -368,9 +425,7 @@ impl flags::AnalysisStats {
                 let unknown_or_partial = if ty.is_unknown() {
                     num_exprs_unknown += 1;
                     if verbosity.is_spammy() {
-                        if let Some((path, start, end)) =
-                            expr_syntax_range(db, &analysis, vfs, &sm, expr_id)
-                        {
+                        if let Some((path, start, end)) = expr_syntax_range(db, vfs, &sm, expr_id) {
                             bar.println(format!(
                                 "{} {}:{}-{}:{}: Unknown type",
                                 path,
@@ -394,9 +449,7 @@ impl flags::AnalysisStats {
                 };
                 if self.only.is_some() && verbosity.is_spammy() {
                     // in super-verbose mode for just one function, we print every single expression
-                    if let Some((_, start, end)) =
-                        expr_syntax_range(db, &analysis, vfs, &sm, expr_id)
-                    {
+                    if let Some((_, start, end)) = expr_syntax_range(db, vfs, &sm, expr_id) {
                         bar.println(format!(
                             "{}:{}-{}:{}: {}",
                             start.line + 1,
@@ -412,16 +465,14 @@ impl flags::AnalysisStats {
                 if unknown_or_partial && self.output == Some(OutputFormat::Csv) {
                     println!(
                         r#"{},type,"{}""#,
-                        location_csv_expr(db, &analysis, vfs, &sm, expr_id),
+                        location_csv_expr(db, vfs, &sm, expr_id),
                         ty.display(db)
                     );
                 }
                 if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr_id) {
                     num_expr_type_mismatches += 1;
                     if verbosity.is_verbose() {
-                        if let Some((path, start, end)) =
-                            expr_syntax_range(db, &analysis, vfs, &sm, expr_id)
-                        {
+                        if let Some((path, start, end)) = expr_syntax_range(db, vfs, &sm, expr_id) {
                             bar.println(format!(
                                 "{} {}:{}-{}:{}: Expected {}, got {}",
                                 path,
@@ -444,7 +495,7 @@ impl flags::AnalysisStats {
                     if self.output == Some(OutputFormat::Csv) {
                         println!(
                             r#"{},mismatch,"{}","{}""#,
-                            location_csv_expr(db, &analysis, vfs, &sm, expr_id),
+                            location_csv_expr(db, vfs, &sm, expr_id),
                             mismatch.expected.display(db),
                             mismatch.actual.display(db)
                         );
@@ -454,7 +505,7 @@ impl flags::AnalysisStats {
             if verbosity.is_spammy() {
                 bar.println(format!(
                     "In {}: {} exprs, {} unknown, {} partial",
-                    full_name,
+                    full_name(),
                     num_exprs - previous_exprs,
                     num_exprs_unknown - previous_unknown,
                     num_exprs_partially_unknown - previous_partially_unknown
@@ -471,9 +522,7 @@ impl flags::AnalysisStats {
                 let unknown_or_partial = if ty.is_unknown() {
                     num_pats_unknown += 1;
                     if verbosity.is_spammy() {
-                        if let Some((path, start, end)) =
-                            pat_syntax_range(db, &analysis, vfs, &sm, pat_id)
-                        {
+                        if let Some((path, start, end)) = pat_syntax_range(db, vfs, &sm, pat_id) {
                             bar.println(format!(
                                 "{} {}:{}-{}:{}: Unknown type",
                                 path,
@@ -497,8 +546,7 @@ impl flags::AnalysisStats {
                 };
                 if self.only.is_some() && verbosity.is_spammy() {
                     // in super-verbose mode for just one function, we print every single pattern
-                    if let Some((_, start, end)) = pat_syntax_range(db, &analysis, vfs, &sm, pat_id)
-                    {
+                    if let Some((_, start, end)) = pat_syntax_range(db, vfs, &sm, pat_id) {
                         bar.println(format!(
                             "{}:{}-{}:{}: {}",
                             start.line + 1,
@@ -514,16 +562,14 @@ impl flags::AnalysisStats {
                 if unknown_or_partial && self.output == Some(OutputFormat::Csv) {
                     println!(
                         r#"{},type,"{}""#,
-                        location_csv_pat(db, &analysis, vfs, &sm, pat_id),
+                        location_csv_pat(db, vfs, &sm, pat_id),
                         ty.display(db)
                     );
                 }
                 if let Some(mismatch) = inference_result.type_mismatch_for_pat(pat_id) {
                     num_pat_type_mismatches += 1;
                     if verbosity.is_verbose() {
-                        if let Some((path, start, end)) =
-                            pat_syntax_range(db, &analysis, vfs, &sm, pat_id)
-                        {
+                        if let Some((path, start, end)) = pat_syntax_range(db, vfs, &sm, pat_id) {
                             bar.println(format!(
                                 "{} {}:{}-{}:{}: Expected {}, got {}",
                                 path,
@@ -546,7 +592,7 @@ impl flags::AnalysisStats {
                     if self.output == Some(OutputFormat::Csv) {
                         println!(
                             r#"{},mismatch,"{}","{}""#,
-                            location_csv_pat(db, &analysis, vfs, &sm, pat_id),
+                            location_csv_pat(db, vfs, &sm, pat_id),
                             mismatch.expected.display(db),
                             mismatch.actual.display(db)
                         );
@@ -556,7 +602,7 @@ impl flags::AnalysisStats {
             if verbosity.is_spammy() {
                 bar.println(format!(
                     "In {}: {} pats, {} unknown, {} partial",
-                    full_name,
+                    full_name(),
                     num_pats - previous_pats,
                     num_pats_unknown - previous_unknown,
                     num_pats_partially_unknown - previous_partially_unknown
@@ -567,6 +613,7 @@ impl flags::AnalysisStats {
         }
 
         bar.finish_and_clear();
+        let inference_time = inference_sw.elapsed();
         eprintln!(
             "  exprs: {}, ??ty: {} ({}%), ?ty: {} ({}%), !ty: {}",
             num_exprs,
@@ -585,12 +632,89 @@ impl flags::AnalysisStats {
             percentage(num_pats_partially_unknown, num_pats),
             num_pat_type_mismatches
         );
+        eprintln!("{:<20} {}", "Inference:", inference_time);
         report_metric("unknown type", num_exprs_unknown, "#");
         report_metric("type mismatches", num_expr_type_mismatches, "#");
         report_metric("pattern unknown type", num_pats_unknown, "#");
         report_metric("pattern type mismatches", num_pat_type_mismatches, "#");
+        report_metric("inference time", inference_time.time.as_millis() as u64, "ms");
+    }
+
+    fn run_body_lowering(
+        &self,
+        db: &RootDatabase,
+        vfs: &Vfs,
+        bodies: &[DefWithBody],
+        verbosity: Verbosity,
+    ) {
+        let mut bar = match verbosity {
+            Verbosity::Quiet | Verbosity::Spammy => ProgressReport::hidden(),
+            _ if self.output.is_some() => ProgressReport::hidden(),
+            _ => ProgressReport::new(bodies.len() as u64),
+        };
 
-        eprintln!("{:<20} {}", "Inference:", inference_sw.elapsed());
+        let mut sw = self.stop_watch();
+        bar.tick();
+        for &body_id in bodies {
+            let module = body_id.module(db);
+            let full_name = move || {
+                module
+                    .krate()
+                    .display_name(db)
+                    .map(|it| it.canonical_name().to_string())
+                    .into_iter()
+                    .chain(
+                        module
+                            .path_to_root(db)
+                            .into_iter()
+                            .filter_map(|it| it.name(db))
+                            .rev()
+                            .chain(Some(body_id.name(db).unwrap_or_else(Name::missing)))
+                            .map(|it| it.display(db).to_string()),
+                    )
+                    .join("::")
+            };
+            if let Some(only_name) = self.only.as_deref() {
+                if body_id.name(db).unwrap_or_else(Name::missing).display(db).to_string()
+                    != only_name
+                    && full_name() != only_name
+                {
+                    continue;
+                }
+            }
+            let msg = move || {
+                if verbosity.is_verbose() {
+                    let source = match body_id {
+                        DefWithBody::Function(it) => it.source(db).map(|it| it.syntax().cloned()),
+                        DefWithBody::Static(it) => it.source(db).map(|it| it.syntax().cloned()),
+                        DefWithBody::Const(it) => it.source(db).map(|it| it.syntax().cloned()),
+                        DefWithBody::Variant(it) => it.source(db).map(|it| it.syntax().cloned()),
+                        DefWithBody::InTypeConst(_) => unimplemented!(),
+                    };
+                    if let Some(src) = source {
+                        let original_file = src.file_id.original_file(db);
+                        let path = vfs.file_path(original_file);
+                        let syntax_range = src.value.text_range();
+                        format!("processing: {} ({} {:?})", full_name(), path, syntax_range)
+                    } else {
+                        format!("processing: {}", full_name())
+                    }
+                } else {
+                    format!("processing: {}", full_name())
+                }
+            };
+            if verbosity.is_spammy() {
+                bar.println(msg());
+            }
+            bar.set_message(msg);
+            db.body_with_source_map(body_id.into());
+            bar.inc(1);
+        }
+
+        bar.finish_and_clear();
+        let body_lowering_time = sw.elapsed();
+        eprintln!("{:<20} {}", "Body lowering:", body_lowering_time);
+        report_metric("body lowering time", body_lowering_time.time.as_millis() as u64, "ms");
     }
 
     fn stop_watch(&self) -> StopWatch {
@@ -598,13 +722,7 @@ impl flags::AnalysisStats {
     }
 }
 
-fn location_csv_expr(
-    db: &RootDatabase,
-    analysis: &Analysis,
-    vfs: &Vfs,
-    sm: &BodySourceMap,
-    expr_id: ExprId,
-) -> String {
+fn location_csv_expr(db: &RootDatabase, vfs: &Vfs, sm: &BodySourceMap, expr_id: ExprId) -> String {
     let src = match sm.expr_syntax(expr_id) {
         Ok(s) => s,
         Err(SyntheticSyntax) => return "synthetic,,".to_string(),
@@ -613,20 +731,14 @@ fn location_csv_expr(
     let node = src.map(|e| e.to_node(&root).syntax().clone());
     let original_range = node.as_ref().original_file_range(db);
     let path = vfs.file_path(original_range.file_id);
-    let line_index = analysis.file_line_index(original_range.file_id).unwrap();
+    let line_index = db.line_index(original_range.file_id);
     let text_range = original_range.range;
     let (start, end) =
         (line_index.line_col(text_range.start()), line_index.line_col(text_range.end()));
     format!("{path},{}:{},{}:{}", start.line + 1, start.col, end.line + 1, end.col)
 }
 
-fn location_csv_pat(
-    db: &RootDatabase,
-    analysis: &Analysis,
-    vfs: &Vfs,
-    sm: &BodySourceMap,
-    pat_id: PatId,
-) -> String {
+fn location_csv_pat(db: &RootDatabase, vfs: &Vfs, sm: &BodySourceMap, pat_id: PatId) -> String {
     let src = match sm.pat_syntax(pat_id) {
         Ok(s) => s,
         Err(SyntheticSyntax) => return "synthetic,,".to_string(),
@@ -637,7 +749,7 @@ fn location_csv_pat(
     });
     let original_range = node.as_ref().original_file_range(db);
     let path = vfs.file_path(original_range.file_id);
-    let line_index = analysis.file_line_index(original_range.file_id).unwrap();
+    let line_index = db.line_index(original_range.file_id);
     let text_range = original_range.range;
     let (start, end) =
         (line_index.line_col(text_range.start()), line_index.line_col(text_range.end()));
@@ -646,7 +758,6 @@ fn location_csv_pat(
 
 fn expr_syntax_range(
     db: &RootDatabase,
-    analysis: &Analysis,
     vfs: &Vfs,
     sm: &BodySourceMap,
     expr_id: ExprId,
@@ -657,7 +768,7 @@ fn expr_syntax_range(
         let node = src.map(|e| e.to_node(&root).syntax().clone());
         let original_range = node.as_ref().original_file_range(db);
         let path = vfs.file_path(original_range.file_id);
-        let line_index = analysis.file_line_index(original_range.file_id).unwrap();
+        let line_index = db.line_index(original_range.file_id);
         let text_range = original_range.range;
         let (start, end) =
             (line_index.line_col(text_range.start()), line_index.line_col(text_range.end()));
@@ -668,7 +779,6 @@ fn expr_syntax_range(
 }
 fn pat_syntax_range(
     db: &RootDatabase,
-    analysis: &Analysis,
     vfs: &Vfs,
     sm: &BodySourceMap,
     pat_id: PatId,
@@ -684,7 +794,7 @@ fn pat_syntax_range(
         });
         let original_range = node.as_ref().original_file_range(db);
         let path = vfs.file_path(original_range.file_id);
-        let line_index = analysis.file_line_index(original_range.file_id).unwrap();
+        let line_index = db.line_index(original_range.file_id);
         let text_range = original_range.range;
         let (start, end) =
             (line_index.line_col(text_range.start()), line_index.line_col(text_range.end()));
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs
index 4006d023def..4306d721298 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs
@@ -17,9 +17,15 @@ impl flags::Diagnostics {
     pub fn run(self) -> anyhow::Result<()> {
         let mut cargo_config = CargoConfig::default();
         cargo_config.sysroot = Some(RustLibSource::Discover);
+        let with_proc_macro_server = if let Some(p) = &self.proc_macro_srv {
+            let path = vfs::AbsPathBuf::assert(std::env::current_dir()?.join(&p));
+            ProcMacroServerChoice::Explicit(path)
+        } else {
+            ProcMacroServerChoice::Sysroot
+        };
         let load_cargo_config = LoadCargoConfig {
             load_out_dirs_from_check: !self.disable_build_scripts,
-            with_proc_macro_server: ProcMacroServerChoice::Sysroot,
+            with_proc_macro_server,
             prefill_caches: false,
         };
         let (host, _vfs, _proc_macro) =
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs
index 31012c01b95..208a4e6ecd9 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs
@@ -66,8 +66,6 @@ xflags::xflags! {
             optional --memory-usage
             /// Print the total length of all source and macro files (whitespace is not counted).
             optional --source-stats
-            /// Only type check, skip lowering to mir
-            optional --skip-mir-stats
 
             /// Only analyze items matching this path.
             optional -o, --only path: String
@@ -80,8 +78,16 @@ xflags::xflags! {
             optional --disable-build-scripts
             /// Don't use expand proc macros.
             optional --disable-proc-macros
-            /// Only resolve names, don't run type inference.
+            /// Skip body lowering.
+            optional --skip-lowering
+            /// Skip type inference.
             optional --skip-inference
+            /// Skip lowering to mir
+            optional --skip-mir-stats
+            /// Skip data layout calculation
+            optional --skip-data-layout
+            /// Skip const evaluation
+            optional --skip-const-eval
         }
 
         cmd diagnostics {
@@ -92,6 +98,8 @@ xflags::xflags! {
             optional --disable-build-scripts
             /// Don't use expand proc macros.
             optional --disable-proc-macros
+            /// Run a custom proc-macro-srv binary.
+            optional --proc-macro-srv path: PathBuf
         }
 
         cmd ssr {
@@ -174,13 +182,16 @@ pub struct AnalysisStats {
     pub parallel: bool,
     pub memory_usage: bool,
     pub source_stats: bool,
+    pub skip_lowering: bool,
+    pub skip_inference: bool,
     pub skip_mir_stats: bool,
+    pub skip_data_layout: bool,
+    pub skip_const_eval: bool,
     pub only: Option<String>,
     pub with_deps: bool,
     pub no_sysroot: bool,
     pub disable_build_scripts: bool,
     pub disable_proc_macros: bool,
-    pub skip_inference: bool,
 }
 
 #[derive(Debug)]
@@ -189,6 +200,7 @@ pub struct Diagnostics {
 
     pub disable_build_scripts: bool,
     pub disable_proc_macros: bool,
+    pub proc_macro_srv: Option<PathBuf>,
 }
 
 #[derive(Debug)]
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/progress_report.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/progress_report.rs
index d459dd115ce..c236f9c7fe1 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/progress_report.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/progress_report.rs
@@ -4,41 +4,29 @@
 use std::io::{self, Write};
 
 /// A Simple ASCII Progress Bar
-pub(crate) struct ProgressReport {
+pub(crate) struct ProgressReport<'a> {
     curr: f32,
     text: String,
     hidden: bool,
 
     len: u64,
     pos: u64,
-    msg: String,
+    msg: Option<Box<dyn Fn() -> String + 'a>>,
 }
 
-impl ProgressReport {
-    pub(crate) fn new(len: u64) -> ProgressReport {
-        ProgressReport {
-            curr: 0.0,
-            text: String::new(),
-            hidden: false,
-            len,
-            pos: 0,
-            msg: String::new(),
-        }
+impl<'a> ProgressReport<'a> {
+    pub(crate) fn new(len: u64) -> ProgressReport<'a> {
+        ProgressReport { curr: 0.0, text: String::new(), hidden: false, len, pos: 0, msg: None }
     }
 
-    pub(crate) fn hidden() -> ProgressReport {
-        ProgressReport {
-            curr: 0.0,
-            text: String::new(),
-            hidden: true,
-            len: 0,
-            pos: 0,
-            msg: String::new(),
-        }
+    pub(crate) fn hidden() -> ProgressReport<'a> {
+        ProgressReport { curr: 0.0, text: String::new(), hidden: true, len: 0, pos: 0, msg: None }
     }
 
-    pub(crate) fn set_message(&mut self, msg: &str) {
-        self.msg = msg.to_string();
+    pub(crate) fn set_message(&mut self, msg: impl Fn() -> String + 'a) {
+        if !self.hidden {
+            self.msg = Some(Box::new(msg));
+        }
         self.tick();
     }
 
@@ -67,7 +55,12 @@ impl ProgressReport {
             return;
         }
         let percent = (self.curr * 100.0) as u32;
-        let text = format!("{}/{} {percent:3>}% {}", self.pos, self.len, self.msg);
+        let text = format!(
+            "{}/{} {percent:3>}% {}",
+            self.pos,
+            self.len,
+            self.msg.as_ref().map_or_else(|| String::new(), |it| it())
+        );
         self.update_text(&text);
     }
 
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs
index ebe77b8dfe7..4e57c6eb65a 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs
@@ -135,7 +135,7 @@ impl<'a> RequestDispatcher<'a> {
         R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug,
         R::Result: Serialize,
     {
-        self.on_with_thread_intent::<R>(ThreadIntent::Worker, f)
+        self.on_with_thread_intent::<true, R>(ThreadIntent::Worker, f)
     }
 
     /// Dispatches a latency-sensitive request onto the thread pool.
@@ -148,7 +148,22 @@ impl<'a> RequestDispatcher<'a> {
         R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug,
         R::Result: Serialize,
     {
-        self.on_with_thread_intent::<R>(ThreadIntent::LatencySensitive, f)
+        self.on_with_thread_intent::<true, R>(ThreadIntent::LatencySensitive, f)
+    }
+
+    /// Formatting requests should never block on waiting a for task thread to open up, editors will wait
+    /// on the response and a late formatting update might mess with the document and user.
+    /// We can't run this on the main thread though as we invoke rustfmt which may take arbitrary time to complete!
+    pub(crate) fn on_fmt_thread<R>(
+        &mut self,
+        f: fn(GlobalStateSnapshot, R::Params) -> Result<R::Result>,
+    ) -> &mut Self
+    where
+        R: lsp_types::request::Request + 'static,
+        R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug,
+        R::Result: Serialize,
+    {
+        self.on_with_thread_intent::<false, R>(ThreadIntent::LatencySensitive, f)
     }
 
     pub(crate) fn finish(&mut self) {
@@ -163,7 +178,7 @@ impl<'a> RequestDispatcher<'a> {
         }
     }
 
-    fn on_with_thread_intent<R>(
+    fn on_with_thread_intent<const MAIN_POOL: bool, R>(
         &mut self,
         intent: ThreadIntent,
         f: fn(GlobalStateSnapshot, R::Params) -> Result<R::Result>,
@@ -178,17 +193,20 @@ impl<'a> RequestDispatcher<'a> {
             None => return self,
         };
 
-        self.global_state.task_pool.handle.spawn(intent, {
-            let world = self.global_state.snapshot();
-            move || {
-                let result = panic::catch_unwind(move || {
-                    let _pctx = stdx::panic_context::enter(panic_context);
-                    f(world, params)
-                });
-                match thread_result_to_response::<R>(req.id.clone(), result) {
-                    Ok(response) => Task::Response(response),
-                    Err(_) => Task::Retry(req),
-                }
+        let world = self.global_state.snapshot();
+        if MAIN_POOL {
+            &mut self.global_state.task_pool.handle
+        } else {
+            &mut self.global_state.fmt_pool.handle
+        }
+        .spawn(intent, move || {
+            let result = panic::catch_unwind(move || {
+                let _pctx = stdx::panic_context::enter(panic_context);
+                f(world, params)
+            });
+            match thread_result_to_response::<R>(req.id.clone(), result) {
+                Ok(response) => Task::Response(response),
+                Err(_) => Task::Retry(req),
             }
         });
 
@@ -289,7 +307,7 @@ pub(crate) struct NotificationDispatcher<'a> {
 }
 
 impl<'a> NotificationDispatcher<'a> {
-    pub(crate) fn on<N>(
+    pub(crate) fn on_sync_mut<N>(
         &mut self,
         f: fn(&mut GlobalState, N::Params) -> Result<()>,
     ) -> Result<&mut Self>
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
index 9f4dc444020..d5b0e3a5705 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
@@ -14,7 +14,7 @@ use nohash_hasher::IntMap;
 use parking_lot::{Mutex, RwLock};
 use proc_macro_api::ProcMacroServer;
 use project_model::{CargoWorkspace, ProjectWorkspace, Target, WorkspaceBuildScripts};
-use rustc_hash::FxHashMap;
+use rustc_hash::{FxHashMap, FxHashSet};
 use triomphe::Arc;
 use vfs::AnchoredPathBuf;
 
@@ -54,6 +54,7 @@ pub(crate) struct GlobalState {
     req_queue: ReqQueue,
 
     pub(crate) task_pool: Handle<TaskPool<Task>, Receiver<Task>>,
+    pub(crate) fmt_pool: Handle<TaskPool<Task>, Receiver<Task>>,
 
     pub(crate) config: Arc<Config>,
     pub(crate) config_errors: Option<ConfigError>,
@@ -111,9 +112,11 @@ pub(crate) struct GlobalState {
     /// the user just adds comments or whitespace to Cargo.toml, we do not want
     /// to invalidate any salsa caches.
     pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
+    pub(crate) crate_graph_file_dependencies: FxHashSet<vfs::VfsPath>,
 
     // op queues
-    pub(crate) fetch_workspaces_queue: OpQueue<(), Option<Vec<anyhow::Result<ProjectWorkspace>>>>,
+    pub(crate) fetch_workspaces_queue:
+        OpQueue<bool, Option<(Vec<anyhow::Result<ProjectWorkspace>>, bool)>>,
     pub(crate) fetch_build_data_queue:
         OpQueue<(), (Arc<Vec<ProjectWorkspace>>, Vec<anyhow::Result<WorkspaceBuildScripts>>)>,
     pub(crate) fetch_proc_macros_queue: OpQueue<Vec<ProcMacroPaths>, bool>,
@@ -151,6 +154,11 @@ impl GlobalState {
             let handle = TaskPool::new_with_threads(sender, config.main_loop_num_threads());
             Handle { handle, receiver }
         };
+        let fmt_pool = {
+            let (sender, receiver) = unbounded();
+            let handle = TaskPool::new_with_threads(sender, 1);
+            Handle { handle, receiver }
+        };
 
         let mut analysis_host = AnalysisHost::new(config.lru_parse_query_capacity());
         if let Some(capacities) = config.lru_query_capacities() {
@@ -161,6 +169,7 @@ impl GlobalState {
             sender,
             req_queue: ReqQueue::default(),
             task_pool,
+            fmt_pool,
             loader,
             config: Arc::new(config.clone()),
             analysis_host,
@@ -189,6 +198,7 @@ impl GlobalState {
             vfs_progress_n_done: 0,
 
             workspaces: Arc::new(Vec::new()),
+            crate_graph_file_dependencies: FxHashSet::default(),
             fetch_workspaces_queue: OpQueue::default(),
             fetch_build_data_queue: OpQueue::default(),
             fetch_proc_macros_queue: OpQueue::default(),
@@ -202,10 +212,9 @@ impl GlobalState {
 
     pub(crate) fn process_changes(&mut self) -> bool {
         let _p = profile::span("GlobalState::process_changes");
-        let mut workspace_structure_change = None;
 
         let mut file_changes = FxHashMap::default();
-        let (change, changed_files) = {
+        let (change, changed_files, workspace_structure_change) = {
             let mut change = Change::new();
             let (vfs, line_endings_map) = &mut *self.vfs.write();
             let changed_files = vfs.take_changes();
@@ -260,16 +269,20 @@ impl GlobalState {
                 .map(|(file_id, (change_kind, _))| vfs::ChangedFile { file_id, change_kind })
                 .collect();
 
+            let mut workspace_structure_change = None;
             // A file was added or deleted
             let mut has_structure_changes = false;
             for file in &changed_files {
-                if let Some(path) = vfs.file_path(file.file_id).as_path() {
+                let vfs_path = &vfs.file_path(file.file_id);
+                if let Some(path) = vfs_path.as_path() {
                     let path = path.to_path_buf();
                     if reload::should_refresh_for_change(&path, file.change_kind) {
-                        workspace_structure_change = Some(path);
+                        workspace_structure_change = Some((path.clone(), false));
                     }
                     if file.is_created_or_deleted() {
                         has_structure_changes = true;
+                        workspace_structure_change =
+                            Some((path, self.crate_graph_file_dependencies.contains(vfs_path)));
                     }
                 }
 
@@ -294,7 +307,7 @@ impl GlobalState {
                 let roots = self.source_root_config.partition(vfs);
                 change.set_roots(roots);
             }
-            (change, changed_files)
+            (change, changed_files, workspace_structure_change)
         };
 
         self.analysis_host.apply_change(change);
@@ -304,9 +317,11 @@ impl GlobalState {
             // FIXME: ideally we should only trigger a workspace fetch for non-library changes
             // but something's going wrong with the source root business when we add a new local
             // crate see https://github.com/rust-lang/rust-analyzer/issues/13029
-            if let Some(path) = workspace_structure_change {
-                self.fetch_workspaces_queue
-                    .request_op(format!("workspace vfs file change: {}", path.display()), ());
+            if let Some((path, force_crate_graph_reload)) = workspace_structure_change {
+                self.fetch_workspaces_queue.request_op(
+                    format!("workspace vfs file change: {}", path.display()),
+                    force_crate_graph_reload,
+                );
             }
             self.proc_macro_changed =
                 changed_files.iter().filter(|file| !file.is_created_or_deleted()).any(|file| {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
index 09de6900c8f..ae1dc23153c 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
@@ -127,7 +127,7 @@ pub(crate) fn handle_did_save_text_document(
             if reload::should_refresh_for_change(abs_path, ChangeKind::Modify) {
                 state
                     .fetch_workspaces_queue
-                    .request_op(format!("DidSaveTextDocument {}", abs_path.display()), ());
+                    .request_op(format!("DidSaveTextDocument {}", abs_path.display()), false);
             }
         }
 
@@ -205,7 +205,7 @@ pub(crate) fn handle_did_change_workspace_folders(
 
     if !config.has_linked_projects() && config.detached_files().is_empty() {
         config.rediscover_workspaces();
-        state.fetch_workspaces_queue.request_op("client workspaces changed".to_string(), ())
+        state.fetch_workspaces_queue.request_op("client workspaces changed".to_string(), false)
     }
 
     Ok(())
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
index 3f365c05942..a6a72552d57 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
@@ -18,12 +18,11 @@ use lsp_server::ErrorCode;
 use lsp_types::{
     CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem,
     CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams,
-    CodeLens, CompletionItem, DocumentFormattingParams, FoldingRange, FoldingRangeParams,
-    HoverContents, InlayHint, InlayHintParams, Location, LocationLink, Position,
-    PrepareRenameResponse, Range, RenameParams, SemanticTokensDeltaParams,
-    SemanticTokensFullDeltaResult, SemanticTokensParams, SemanticTokensRangeParams,
-    SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, SymbolTag,
-    TextDocumentIdentifier, Url, WorkspaceEdit,
+    CodeLens, CompletionItem, FoldingRange, FoldingRangeParams, HoverContents, InlayHint,
+    InlayHintParams, Location, LocationLink, Position, PrepareRenameResponse, Range, RenameParams,
+    SemanticTokensDeltaParams, SemanticTokensFullDeltaResult, SemanticTokensParams,
+    SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation,
+    SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit,
 };
 use project_model::{ManifestPath, ProjectWorkspace, TargetKind};
 use serde_json::json;
@@ -52,7 +51,7 @@ pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> Result<
     state.proc_macro_clients = Arc::from(Vec::new());
     state.proc_macro_changed = false;
 
-    state.fetch_workspaces_queue.request_op("reload workspace request".to_string(), ());
+    state.fetch_workspaces_queue.request_op("reload workspace request".to_string(), false);
     Ok(())
 }
 
@@ -115,13 +114,14 @@ pub(crate) fn handle_analyzer_status(
 
 pub(crate) fn handle_memory_usage(state: &mut GlobalState, _: ()) -> Result<String> {
     let _p = profile::span("handle_memory_usage");
-    let mut mem = state.analysis_host.per_query_memory_usage();
-    mem.push(("Remaining".into(), profile::memory_usage().allocated));
+    let mem = state.analysis_host.per_query_memory_usage();
 
     let mut out = String::new();
-    for (name, bytes) in mem {
-        format_to!(out, "{:>8} {}\n", bytes, name);
+    for (name, bytes, entries) in mem {
+        format_to!(out, "{:>8} {:>6} {}\n", bytes, entries, name);
     }
+    format_to!(out, "{:>8}        Remaining\n", profile::memory_usage().allocated);
+
     Ok(out)
 }
 
@@ -1076,7 +1076,7 @@ pub(crate) fn handle_references(
 
 pub(crate) fn handle_formatting(
     snap: GlobalStateSnapshot,
-    params: DocumentFormattingParams,
+    params: lsp_types::DocumentFormattingParams,
 ) -> Result<Option<Vec<lsp_types::TextEdit>>> {
     let _p = profile::span("handle_formatting");
 
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
index 12bc638929d..02dd94e5fa5 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
@@ -11,6 +11,7 @@ use flycheck::FlycheckHandle;
 use ide_db::base_db::{SourceDatabaseExt, VfsPath};
 use lsp_server::{Connection, Notification, Request};
 use lsp_types::notification::Notification as _;
+use stdx::thread::ThreadIntent;
 use triomphe::Arc;
 use vfs::FileId;
 
@@ -115,9 +116,11 @@ impl GlobalState {
             self.register_did_save_capability();
         }
 
-        self.fetch_workspaces_queue.request_op("startup".to_string(), ());
-        if let Some((cause, ())) = self.fetch_workspaces_queue.should_start_op() {
-            self.fetch_workspaces(cause);
+        self.fetch_workspaces_queue.request_op("startup".to_string(), false);
+        if let Some((cause, force_crate_graph_reload)) =
+            self.fetch_workspaces_queue.should_start_op()
+        {
+            self.fetch_workspaces(cause, force_crate_graph_reload);
         }
 
         while let Some(event) = self.next_event(&inbox) {
@@ -177,6 +180,9 @@ impl GlobalState {
             recv(self.task_pool.receiver) -> task =>
                 Some(Event::Task(task.unwrap())),
 
+            recv(self.fmt_pool.receiver) -> task =>
+                Some(Event::Task(task.unwrap())),
+
             recv(self.loader.receiver) -> task =>
                 Some(Event::Vfs(task.unwrap())),
 
@@ -277,6 +283,7 @@ impl GlobalState {
                 }
             }
         }
+        let event_handling_duration = loop_start.elapsed();
 
         let state_changed = self.process_changes();
         let memdocs_added_or_removed = self.mem_docs.take_changes();
@@ -364,8 +371,10 @@ impl GlobalState {
         }
 
         if self.config.cargo_autoreload() {
-            if let Some((cause, ())) = self.fetch_workspaces_queue.should_start_op() {
-                self.fetch_workspaces(cause);
+            if let Some((cause, force_crate_graph_reload)) =
+                self.fetch_workspaces_queue.should_start_op()
+            {
+                self.fetch_workspaces(cause, force_crate_graph_reload);
             }
         }
 
@@ -385,9 +394,9 @@ impl GlobalState {
 
         let loop_duration = loop_start.elapsed();
         if loop_duration > Duration::from_millis(100) && was_quiescent {
-            tracing::warn!("overly long loop turn took {loop_duration:?}: {event_dbg_msg}");
+            tracing::warn!("overly long loop turn took {loop_duration:?} (event handling took {event_handling_duration:?}): {event_dbg_msg}");
             self.poke_rust_analyzer_developer(format!(
-                "overly long loop turn took {loop_duration:?}: {event_dbg_msg}"
+                "overly long loop turn took {loop_duration:?} (event handling took {event_handling_duration:?}): {event_dbg_msg}"
             ));
         }
         Ok(())
@@ -397,7 +406,7 @@ impl GlobalState {
         tracing::debug!(%cause, "will prime caches");
         let num_worker_threads = self.config.prime_caches_num_threads();
 
-        self.task_pool.handle.spawn_with_sender(stdx::thread::ThreadIntent::Worker, {
+        self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, {
             let analysis = self.snapshot().analysis;
             move |sender| {
                 sender.send(Task::PrimeCaches(PrimeCachesProgress::Begin)).unwrap();
@@ -468,8 +477,9 @@ impl GlobalState {
                 let (state, msg) = match progress {
                     ProjectWorkspaceProgress::Begin => (Progress::Begin, None),
                     ProjectWorkspaceProgress::Report(msg) => (Progress::Report, Some(msg)),
-                    ProjectWorkspaceProgress::End(workspaces) => {
-                        self.fetch_workspaces_queue.op_completed(Some(workspaces));
+                    ProjectWorkspaceProgress::End(workspaces, force_reload_crate_graph) => {
+                        self.fetch_workspaces_queue
+                            .op_completed(Some((workspaces, force_reload_crate_graph)));
                         if let Err(e) = self.fetch_workspace_error() {
                             tracing::error!("FetchWorkspaceError:\n{e}");
                         }
@@ -546,7 +556,6 @@ impl GlobalState {
                 self.vfs_progress_n_total = n_total;
                 self.vfs_progress_n_done = n_done;
 
-                // if n_total != 0 {
                 let state = if n_done == 0 {
                     Progress::Begin
                 } else if n_done < n_total {
@@ -562,7 +571,6 @@ impl GlobalState {
                     Some(Progress::fraction(n_done, n_total)),
                     None,
                 );
-                // }
             }
         }
     }
@@ -678,6 +686,12 @@ impl GlobalState {
             .on_sync::<lsp_types::request::SelectionRangeRequest>(handlers::handle_selection_range)
             .on_sync::<lsp_ext::MatchingBrace>(handlers::handle_matching_brace)
             .on_sync::<lsp_ext::OnTypeFormatting>(handlers::handle_on_type_formatting)
+            // Formatting should be done immediately as the editor might wait on it, but we can't
+            // put it on the main thread as we do not want the main thread to block on rustfmt.
+            // So we have an extra thread just for formatting requests to make sure it gets handled
+            // as fast as possible.
+            .on_fmt_thread::<lsp_types::request::Formatting>(handlers::handle_formatting)
+            .on_fmt_thread::<lsp_types::request::RangeFormatting>(handlers::handle_range_formatting)
             // We can’t run latency-sensitive request handlers which do semantic
             // analysis on the main thread because that would block other
             // requests. Instead, we run these request handlers on higher priority
@@ -695,14 +709,6 @@ impl GlobalState {
             .on_latency_sensitive::<lsp_types::request::SemanticTokensRangeRequest>(
                 handlers::handle_semantic_tokens_range,
             )
-            // Formatting is not caused by the user typing,
-            // but it does qualify as latency-sensitive
-            // because a delay before formatting is applied
-            // can be confusing for the user.
-            .on_latency_sensitive::<lsp_types::request::Formatting>(handlers::handle_formatting)
-            .on_latency_sensitive::<lsp_types::request::RangeFormatting>(
-                handlers::handle_range_formatting,
-            )
             // All other request handlers
             .on::<lsp_ext::FetchDependencyList>(handlers::fetch_dependency_list)
             .on::<lsp_ext::AnalyzerStatus>(handlers::handle_analyzer_status)
@@ -757,18 +763,28 @@ impl GlobalState {
         use lsp_types::notification as notifs;
 
         NotificationDispatcher { not: Some(not), global_state: self }
-            .on::<notifs::Cancel>(handlers::handle_cancel)?
-            .on::<notifs::WorkDoneProgressCancel>(handlers::handle_work_done_progress_cancel)?
-            .on::<notifs::DidOpenTextDocument>(handlers::handle_did_open_text_document)?
-            .on::<notifs::DidChangeTextDocument>(handlers::handle_did_change_text_document)?
-            .on::<notifs::DidCloseTextDocument>(handlers::handle_did_close_text_document)?
-            .on::<notifs::DidSaveTextDocument>(handlers::handle_did_save_text_document)?
-            .on::<notifs::DidChangeConfiguration>(handlers::handle_did_change_configuration)?
-            .on::<notifs::DidChangeWorkspaceFolders>(handlers::handle_did_change_workspace_folders)?
-            .on::<notifs::DidChangeWatchedFiles>(handlers::handle_did_change_watched_files)?
-            .on::<lsp_ext::CancelFlycheck>(handlers::handle_cancel_flycheck)?
-            .on::<lsp_ext::ClearFlycheck>(handlers::handle_clear_flycheck)?
-            .on::<lsp_ext::RunFlycheck>(handlers::handle_run_flycheck)?
+            .on_sync_mut::<notifs::Cancel>(handlers::handle_cancel)?
+            .on_sync_mut::<notifs::WorkDoneProgressCancel>(
+                handlers::handle_work_done_progress_cancel,
+            )?
+            .on_sync_mut::<notifs::DidOpenTextDocument>(handlers::handle_did_open_text_document)?
+            .on_sync_mut::<notifs::DidChangeTextDocument>(
+                handlers::handle_did_change_text_document,
+            )?
+            .on_sync_mut::<notifs::DidCloseTextDocument>(handlers::handle_did_close_text_document)?
+            .on_sync_mut::<notifs::DidSaveTextDocument>(handlers::handle_did_save_text_document)?
+            .on_sync_mut::<notifs::DidChangeConfiguration>(
+                handlers::handle_did_change_configuration,
+            )?
+            .on_sync_mut::<notifs::DidChangeWorkspaceFolders>(
+                handlers::handle_did_change_workspace_folders,
+            )?
+            .on_sync_mut::<notifs::DidChangeWatchedFiles>(
+                handlers::handle_did_change_watched_files,
+            )?
+            .on_sync_mut::<lsp_ext::CancelFlycheck>(handlers::handle_cancel_flycheck)?
+            .on_sync_mut::<lsp_ext::ClearFlycheck>(handlers::handle_clear_flycheck)?
+            .on_sync_mut::<lsp_ext::RunFlycheck>(handlers::handle_run_flycheck)?
             .finish();
         Ok(())
     }
@@ -796,7 +812,7 @@ impl GlobalState {
 
         // Diagnostics are triggered by the user typing
         // so we run them on a latency sensitive thread.
-        self.task_pool.handle.spawn(stdx::thread::ThreadIntent::LatencySensitive, move || {
+        self.task_pool.handle.spawn(ThreadIntent::LatencySensitive, move || {
             let _p = profile::span("publish_diagnostics");
             let _ctx = stdx::panic_context::enter("publish_diagnostics".to_owned());
             let diagnostics = subscriptions
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
index 6e8c8ea91a1..310c6b076c0 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
@@ -27,6 +27,7 @@ use ide_db::{
 use itertools::Itertools;
 use proc_macro_api::{MacroDylib, ProcMacroServer};
 use project_model::{PackageRoot, ProjectWorkspace, WorkspaceBuildScripts};
+use rustc_hash::FxHashSet;
 use stdx::{format_to, thread::ThreadIntent};
 use syntax::SmolStr;
 use triomphe::Arc;
@@ -46,7 +47,7 @@ use ::tt::token_id as tt;
 pub(crate) enum ProjectWorkspaceProgress {
     Begin,
     Report(String),
-    End(Vec<anyhow::Result<ProjectWorkspace>>),
+    End(Vec<anyhow::Result<ProjectWorkspace>>, bool),
 }
 
 #[derive(Debug)]
@@ -85,7 +86,7 @@ impl GlobalState {
             );
         }
         if self.config.linked_projects() != old_config.linked_projects() {
-            self.fetch_workspaces_queue.request_op("linked projects changed".to_string(), ())
+            self.fetch_workspaces_queue.request_op("linked projects changed".to_string(), false)
         } else if self.config.flycheck() != old_config.flycheck() {
             self.reload_flycheck();
         }
@@ -110,7 +111,7 @@ impl GlobalState {
 
         if self.proc_macro_changed {
             status.health = lsp_ext::Health::Warning;
-            message.push_str("Proc-macros have changed and need to be rebuild.\n\n");
+            message.push_str("Proc-macros have changed and need to be rebuilt.\n\n");
         }
         if let Err(_) = self.fetch_build_data_error() {
             status.health = lsp_ext::Health::Warning;
@@ -182,7 +183,7 @@ impl GlobalState {
         status
     }
 
-    pub(crate) fn fetch_workspaces(&mut self, cause: Cause) {
+    pub(crate) fn fetch_workspaces(&mut self, cause: Cause, force_crate_graph_reload: bool) {
         tracing::info!(%cause, "will fetch workspaces");
 
         self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, {
@@ -250,7 +251,10 @@ impl GlobalState {
 
                 tracing::info!("did fetch workspaces {:?}", workspaces);
                 sender
-                    .send(Task::FetchWorkspace(ProjectWorkspaceProgress::End(workspaces)))
+                    .send(Task::FetchWorkspace(ProjectWorkspaceProgress::End(
+                        workspaces,
+                        force_crate_graph_reload,
+                    )))
                     .unwrap();
             }
         });
@@ -336,15 +340,19 @@ impl GlobalState {
         let _p = profile::span("GlobalState::switch_workspaces");
         tracing::info!(%cause, "will switch workspaces");
 
+        let Some((workspaces, force_reload_crate_graph)) = self.fetch_workspaces_queue.last_op_result() else { return; };
+
         if let Err(_) = self.fetch_workspace_error() {
             if !self.workspaces.is_empty() {
+                if *force_reload_crate_graph {
+                    self.recreate_crate_graph(cause);
+                }
                 // It only makes sense to switch to a partially broken workspace
                 // if we don't have any workspace at all yet.
                 return;
             }
         }
 
-        let Some(workspaces) = self.fetch_workspaces_queue.last_op_result() else { return; };
         let workspaces =
             workspaces.iter().filter_map(|res| res.as_ref().ok().cloned()).collect::<Vec<_>>();
 
@@ -373,6 +381,9 @@ impl GlobalState {
                 self.workspaces = Arc::new(workspaces);
             } else {
                 tracing::info!("build scripts do not match the version of the active workspace");
+                if *force_reload_crate_graph {
+                    self.recreate_crate_graph(cause);
+                }
                 // Current build scripts do not match the version of the active
                 // workspace, so there's nothing for us to update.
                 return;
@@ -467,13 +478,24 @@ impl GlobalState {
         });
         self.source_root_config = project_folders.source_root_config;
 
+        self.recreate_crate_graph(cause);
+
+        tracing::info!("did switch workspaces");
+    }
+
+    fn recreate_crate_graph(&mut self, cause: String) {
         // Create crate graph from all the workspaces
-        let (crate_graph, proc_macro_paths) = {
+        let (crate_graph, proc_macro_paths, crate_graph_file_dependencies) = {
             let vfs = &mut self.vfs.write().0;
             let loader = &mut self.loader;
+            // crate graph construction relies on these paths, record them so when one of them gets
+            // deleted or created we trigger a reconstruction of the crate graph
+            let mut crate_graph_file_dependencies = FxHashSet::default();
+
             let mut load = |path: &AbsPath| {
                 let _p = profile::span("switch_workspaces::load");
                 let vfs_path = vfs::VfsPath::from(path.to_path_buf());
+                crate_graph_file_dependencies.insert(vfs_path.clone());
                 match vfs.file_id(&vfs_path) {
                     Some(file_id) => Some(file_id),
                     None => {
@@ -494,26 +516,25 @@ impl GlobalState {
                 crate_graph.extend(other, &mut crate_proc_macros);
                 proc_macros.push(crate_proc_macros);
             }
-            (crate_graph, proc_macros)
+            (crate_graph, proc_macros, crate_graph_file_dependencies)
         };
-        let mut change = Change::new();
 
         if self.config.expand_proc_macros() {
             self.fetch_proc_macros_queue.request_op(cause, proc_macro_paths);
         }
+        let mut change = Change::new();
         change.set_crate_graph(crate_graph);
         self.analysis_host.apply_change(change);
+        self.crate_graph_file_dependencies = crate_graph_file_dependencies;
         self.process_changes();
 
         self.reload_flycheck();
-
-        tracing::info!("did switch workspaces");
     }
 
     pub(super) fn fetch_workspace_error(&self) -> Result<(), String> {
         let mut buf = String::new();
 
-        let Some(last_op_result) = self.fetch_workspaces_queue.last_op_result() else { return Ok(()) };
+        let Some((last_op_result, _)) = self.fetch_workspaces_queue.last_op_result() else { return Ok(()) };
         if last_op_result.is_empty() {
             stdx::format_to!(buf, "rust-analyzer failed to discover workspace");
         } else {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs
index 8a9e947ded0..648bc995ad5 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs
@@ -410,7 +410,7 @@ pub(crate) fn signature_help(
     let documentation = call_info.doc.filter(|_| config.docs).map(|doc| {
         lsp_types::Documentation::MarkupContent(lsp_types::MarkupContent {
             kind: lsp_types::MarkupKind::Markdown,
-            value: doc,
+            value: crate::markdown::format_docs(&doc),
         })
     });
 
@@ -1410,7 +1410,8 @@ pub(crate) fn rename_error(err: RenameError) -> crate::LspError {
 
 #[cfg(test)]
 mod tests {
-    use ide::Analysis;
+    use ide::{Analysis, FilePosition};
+    use test_utils::extract_offset;
     use triomphe::Arc;
 
     use super::*;
@@ -1451,6 +1452,34 @@ fn main() {
         }
     }
 
+    #[test]
+    fn calling_function_with_ignored_code_in_signature() {
+        let text = r#"
+fn foo() {
+    bar($0);
+}
+/// ```
+/// # use crate::bar;
+/// bar(5);
+/// ```
+fn bar(_: usize) {}
+"#;
+
+        let (offset, text) = extract_offset(text);
+        let (analysis, file_id) = Analysis::from_single_file(text);
+        let help = signature_help(
+            analysis.signature_help(FilePosition { file_id, offset }).unwrap().unwrap(),
+            CallInfoConfig { params_only: false, docs: true },
+            false,
+        );
+        let docs = match &help.signatures[help.active_signature.unwrap() as usize].documentation {
+            Some(lsp_types::Documentation::MarkupContent(content)) => &content.value,
+            _ => panic!("documentation contains markup"),
+        };
+        assert!(docs.contains("bar(5)"));
+        assert!(!docs.contains("use crate::bar"));
+    }
+
     // `Url` is not able to parse windows paths on unix machines.
     #[test]
     #[cfg(target_os = "windows")]
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
index e130c762fc4..0bb29e7080f 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
@@ -839,6 +839,17 @@ fn resolve_proc_macro() {
         return;
     }
 
+    // skip using the sysroot config as to prevent us from loading the sysroot sources
+    let mut rustc = std::process::Command::new(toolchain::rustc());
+    rustc.args(["--print", "sysroot"]);
+    let output = rustc.output().unwrap();
+    let sysroot =
+        vfs::AbsPathBuf::try_from(std::str::from_utf8(&output.stdout).unwrap().trim()).unwrap();
+
+    let standalone_server_name =
+        format!("rust-analyzer-proc-macro-srv{}", std::env::consts::EXE_SUFFIX);
+    let proc_macro_server_path = sysroot.join("libexec").join(&standalone_server_name);
+
     let server = Project::with_fixture(
         r###"
 //- /foo/Cargo.toml
@@ -916,7 +927,7 @@ pub fn foo(_input: TokenStream) -> TokenStream {
         },
         "procMacro": {
             "enable": true,
-            "server": PathBuf::from(env!("CARGO_BIN_EXE_rust-analyzer")),
+            "server": proc_macro_server_path.as_path().as_ref(),
         }
     }))
     .root("foo")
diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram
index 4c9027dec68..b096c997448 100644
--- a/src/tools/rust-analyzer/crates/syntax/rust.ungram
+++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram
@@ -565,7 +565,7 @@ RefType =
   '&' Lifetime? 'mut'? Type
 
 ArrayType =
-  '[' Type ';' Expr ']'
+  '[' Type ';' ConstArg ']'
 
 SliceType =
   '[' Type ']'
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
index 61f6a04c98d..e520801ea2e 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
@@ -1207,7 +1207,7 @@ impl ArrayType {
     pub fn l_brack_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['[']) }
     pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) }
     pub fn semicolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![;]) }
-    pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+    pub fn const_arg(&self) -> Option<ConstArg> { support::child(&self.syntax) }
     pub fn r_brack_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![']']) }
 }
 
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
index a07561e79a2..3c2b7e56b06 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
@@ -166,7 +166,7 @@ pub fn ty_alias(
     assignment: Option<(ast::Type, Option<ast::WhereClause>)>,
 ) -> ast::TypeAlias {
     let mut s = String::new();
-    s.push_str(&format!("type {} ", ident));
+    s.push_str(&format!("type {}", ident));
 
     if let Some(list) = generic_param_list {
         s.push_str(&list.to_string());
@@ -182,9 +182,9 @@ pub fn ty_alias(
 
     if let Some(exp) = assignment {
         if let Some(cl) = exp.1 {
-            s.push_str(&format!("= {} {}", &exp.0.to_string(), &cl.to_string()));
+            s.push_str(&format!(" = {} {}", &exp.0.to_string(), &cl.to_string()));
         } else {
-            s.push_str(&format!("= {}", &exp.0.to_string()));
+            s.push_str(&format!(" = {}", &exp.0.to_string()));
         }
     }
 
diff --git a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs
index 05f32f8e51e..602baed3707 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs
@@ -387,6 +387,10 @@ impl MiniCore {
             }
         }
 
+        if !active_regions.is_empty() {
+            panic!("unclosed regions: {:?} Add an `endregion` comment", active_regions);
+        }
+
         for flag in &self.valid_flags {
             if !seen_regions.iter().any(|it| it == flag) {
                 panic!("unused minicore flag: {flag:?}");
diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
index c79b4e966d6..266bc2391f1 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
@@ -32,6 +32,7 @@
 //!     include:
 //!     index: sized
 //!     infallible:
+//!     int_impl: size_of, transmute
 //!     iterator: option
 //!     iterators: iterator, fn
 //!     manually_drop: drop
@@ -41,9 +42,11 @@
 //!     panic: fmt
 //!     phantom_data:
 //!     pin:
+//!     pointee:
 //!     range:
 //!     result:
 //!     send: sized
+//!     size_of: sized
 //!     sized:
 //!     slice:
 //!     sync: sized
@@ -345,6 +348,12 @@ pub mod mem {
         pub fn transmute<Src, Dst>(src: Src) -> Dst;
     }
     // endregion:transmute
+
+    // region:size_of
+    extern "rust-intrinsic" {
+        pub fn size_of<T>() -> usize;
+    }
+    // endregion:size_of
 }
 
 pub mod ptr {
@@ -360,6 +369,14 @@ pub mod ptr {
         *dst = src;
     }
     // endregion:drop
+
+    // region:pointee
+    #[lang = "pointee_trait"]
+    pub trait Pointee {
+        #[lang = "metadata_type"]
+        type Metadata;
+    }
+    // endregion:pointee
 }
 
 pub mod ops {
@@ -859,29 +876,26 @@ pub mod fmt {
     }
 
     #[lang = "format_argument"]
-    pub struct ArgumentV1<'a> {
+    pub struct Argument<'a> {
         value: &'a Opaque,
         formatter: fn(&Opaque, &mut Formatter<'_>) -> Result,
     }
 
-    impl<'a> ArgumentV1<'a> {
-        pub fn new<'b, T>(x: &'b T, f: fn(&T, &mut Formatter<'_>) -> Result) -> ArgumentV1<'b> {
+    impl<'a> Argument<'a> {
+        pub fn new<'b, T>(x: &'b T, f: fn(&T, &mut Formatter<'_>) -> Result) -> Argument<'b> {
             use crate::mem::transmute;
-            unsafe { ArgumentV1 { formatter: transmute(f), value: transmute(x) } }
+            unsafe { Argument { formatter: transmute(f), value: transmute(x) } }
         }
     }
 
     #[lang = "format_arguments"]
     pub struct Arguments<'a> {
         pieces: &'a [&'static str],
-        args: &'a [ArgumentV1<'a>],
+        args: &'a [Argument<'a>],
     }
 
     impl<'a> Arguments<'a> {
-        pub const fn new_v1(
-            pieces: &'a [&'static str],
-            args: &'a [ArgumentV1<'a>],
-        ) -> Arguments<'a> {
+        pub const fn new_v1(pieces: &'a [&'static str], args: &'a [Argument<'a>]) -> Arguments<'a> {
             Arguments { pieces, args }
         }
     }
@@ -1307,6 +1321,25 @@ impl bool {
 }
 // endregion:bool_impl
 
+// region:int_impl
+macro_rules! impl_int {
+    ($($t:ty)*) => {
+        $(
+            impl $t {
+                pub const fn from_ne_bytes(bytes: [u8; mem::size_of::<Self>()]) -> Self {
+                    unsafe { mem::transmute(bytes) }
+                }
+            }
+        )*
+    }
+}
+
+impl_int! {
+    usize u8 u16 u32 u64 u128
+    isize i8 i16 i32 i64 i128
+}
+// endregion:int_impl
+
 // region:error
 pub mod error {
     #[rustc_has_incoherent_inherent_impls]
diff --git a/src/tools/rust-analyzer/docs/user/manual.adoc b/src/tools/rust-analyzer/docs/user/manual.adoc
index 5b9db10b093..b5c095fd9d8 100644
--- a/src/tools/rust-analyzer/docs/user/manual.adoc
+++ b/src/tools/rust-analyzer/docs/user/manual.adoc
@@ -244,7 +244,7 @@ Any other tools or libraries you will need to acquire  from Flatpak.
 
 Prerequisites: You have installed the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>.
 
-To use `rust-analyzer`, you need to install and enable one of the two popular two popular LSP client implementations for Emacs, https://github.com/joaotavora/eglot[Eglot] or https://github.com/emacs-lsp/lsp-mode[LSP Mode]. Both enable `rust-analyzer` by default in rust buffers if it is available.
+To use `rust-analyzer`, you need to install and enable one of the two popular LSP client implementations for Emacs, https://github.com/joaotavora/eglot[Eglot] or https://github.com/emacs-lsp/lsp-mode[LSP Mode]. Both enable `rust-analyzer` by default in rust buffers if it is available.
 
 ==== Eglot
 
@@ -653,9 +653,28 @@ However, if you use some other build system, you'll have to describe the structu
 [source,TypeScript]
 ----
 interface JsonProject {
+    /// Path to the sysroot directory.
+    ///
+    /// The sysroot is where rustc looks for the
+    /// crates that are built-in to rust, such as
+    /// std.
+    ///
+    /// https://doc.rust-lang.org/rustc/command-line-arguments.html#--sysroot-override-the-system-root
+    ///
+    /// To see the current value of sysroot, you
+    /// can query rustc:
+    ///
+    /// ```
+    /// $ rustc --print sysroot
+    /// /Users/yourname/.rustup/toolchains/stable-x86_64-apple-darwin
+    /// ```
+    sysroot?: string;
     /// Path to the directory with *source code* of
     /// sysroot crates.
     ///
+    /// By default, this is `lib/rustlib/src/rust/library`
+    /// relative to the sysroot.
+    ///
     /// It should point to the directory where std,
     /// core, and friends can be found:
     ///
diff --git a/src/tools/rust-analyzer/editors/code/.vscodeignore b/src/tools/rust-analyzer/editors/code/.vscodeignore
index 09dc27056b3..6e118f0b3ae 100644
--- a/src/tools/rust-analyzer/editors/code/.vscodeignore
+++ b/src/tools/rust-analyzer/editors/code/.vscodeignore
@@ -10,5 +10,6 @@
 !package-lock.json
 !package.json
 !ra_syntax_tree.tmGrammar.json
+!rustdoc.markdown.injection.tmGrammar.json
 !server
 !README.md
diff --git a/src/tools/rust-analyzer/lib/la-arena/src/lib.rs b/src/tools/rust-analyzer/lib/la-arena/src/lib.rs
index 5107f294394..f39c3a3e4ca 100644
--- a/src/tools/rust-analyzer/lib/la-arena/src/lib.rs
+++ b/src/tools/rust-analyzer/lib/la-arena/src/lib.rs
@@ -451,6 +451,12 @@ impl<T> Arena<T> {
     }
 }
 
+impl<T> AsMut<[T]> for Arena<T> {
+    fn as_mut(&mut self) -> &mut [T] {
+        self.data.as_mut()
+    }
+}
+
 impl<T> Default for Arena<T> {
     fn default() -> Arena<T> {
         Arena { data: Vec::new() }
diff --git a/tests/codegen/unchecked_shifts.rs b/tests/codegen/unchecked_shifts.rs
index 60d0cb09aca..0924dda08ee 100644
--- a/tests/codegen/unchecked_shifts.rs
+++ b/tests/codegen/unchecked_shifts.rs
@@ -8,6 +8,7 @@
 // CHECK-LABEL: @unchecked_shl_unsigned_same
 #[no_mangle]
 pub unsafe fn unchecked_shl_unsigned_same(a: u32, b: u32) -> u32 {
+    // CHECK-NOT: assume
     // CHECK-NOT: and i32
     // CHECK: shl i32 %a, %b
     // CHECK-NOT: and i32
@@ -30,6 +31,7 @@ pub unsafe fn unchecked_shl_unsigned_smaller(a: u16, b: u32) -> u16 {
 // CHECK-LABEL: @unchecked_shl_unsigned_bigger
 #[no_mangle]
 pub unsafe fn unchecked_shl_unsigned_bigger(a: u64, b: u32) -> u64 {
+    // CHECK-NOT: assume
     // CHECK: %[[EXT:.+]] = zext i32 %b to i64
     // CHECK: shl i64 %a, %[[EXT]]
     a.unchecked_shl(b)
@@ -38,6 +40,7 @@ pub unsafe fn unchecked_shl_unsigned_bigger(a: u64, b: u32) -> u64 {
 // CHECK-LABEL: @unchecked_shr_signed_same
 #[no_mangle]
 pub unsafe fn unchecked_shr_signed_same(a: i32, b: u32) -> i32 {
+    // CHECK-NOT: assume
     // CHECK-NOT: and i32
     // CHECK: ashr i32 %a, %b
     // CHECK-NOT: and i32
@@ -60,6 +63,7 @@ pub unsafe fn unchecked_shr_signed_smaller(a: i16, b: u32) -> i16 {
 // CHECK-LABEL: @unchecked_shr_signed_bigger
 #[no_mangle]
 pub unsafe fn unchecked_shr_signed_bigger(a: i64, b: u32) -> i64 {
+    // CHECK-NOT: assume
     // CHECK: %[[EXT:.+]] = zext i32 %b to i64
     // CHECK: ashr i64 %a, %[[EXT]]
     a.unchecked_shr(b)
diff --git a/tests/mir-opt/inline/unchecked_shifts.rs b/tests/mir-opt/inline/unchecked_shifts.rs
index 64ec26b587d..22f84e44a64 100644
--- a/tests/mir-opt/inline/unchecked_shifts.rs
+++ b/tests/mir-opt/inline/unchecked_shifts.rs
@@ -16,3 +16,15 @@ pub unsafe fn unchecked_shl_unsigned_smaller(a: u16, b: u32) -> u16 {
 pub unsafe fn unchecked_shr_signed_smaller(a: i16, b: u32) -> i16 {
     a.unchecked_shr(b)
 }
+
+// EMIT_MIR unchecked_shifts.unchecked_shl_unsigned_bigger.Inline.diff
+// EMIT_MIR unchecked_shifts.unchecked_shl_unsigned_bigger.PreCodegen.after.mir
+pub unsafe fn unchecked_shl_unsigned_bigger(a: u64, b: u32) -> u64 {
+    a.unchecked_shl(b)
+}
+
+// EMIT_MIR unchecked_shifts.unchecked_shr_signed_bigger.Inline.diff
+// EMIT_MIR unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.mir
+pub unsafe fn unchecked_shr_signed_bigger(a: i64, b: u32) -> i64 {
+    a.unchecked_shr(b)
+}
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_bigger.Inline.panic-abort.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_bigger.Inline.panic-abort.diff
new file mode 100644
index 00000000000..1ab1d01e5fa
--- /dev/null
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_bigger.Inline.panic-abort.diff
@@ -0,0 +1,36 @@
+- // MIR for `unchecked_shl_unsigned_bigger` before Inline
++ // MIR for `unchecked_shl_unsigned_bigger` after Inline
+  
+  fn unchecked_shl_unsigned_bigger(_1: u64, _2: u32) -> u64 {
+      debug a => _1;
+      debug b => _2;
+      let mut _0: u64;
+      let mut _3: u64;
+      let mut _4: u32;
++     scope 1 (inlined core::num::<impl u64>::unchecked_shl) {
++         debug self => _3;
++         debug rhs => _4;
++         let mut _5: u64;
++         scope 2 {
++         }
++     }
+  
+      bb0: {
+          StorageLive(_3);
+          _3 = _1;
+          StorageLive(_4);
+          _4 = _2;
+-         _0 = core::num::<impl u64>::unchecked_shl(move _3, move _4) -> [return: bb1, unwind unreachable];
+-     }
+- 
+-     bb1: {
++         StorageLive(_5);
++         _5 = _4 as u64 (IntToInt);
++         _0 = ShlUnchecked(_3, move _5);
++         StorageDead(_5);
+          StorageDead(_4);
+          StorageDead(_3);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_bigger.Inline.panic-unwind.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_bigger.Inline.panic-unwind.diff
new file mode 100644
index 00000000000..577fc8bee66
--- /dev/null
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_bigger.Inline.panic-unwind.diff
@@ -0,0 +1,36 @@
+- // MIR for `unchecked_shl_unsigned_bigger` before Inline
++ // MIR for `unchecked_shl_unsigned_bigger` after Inline
+  
+  fn unchecked_shl_unsigned_bigger(_1: u64, _2: u32) -> u64 {
+      debug a => _1;
+      debug b => _2;
+      let mut _0: u64;
+      let mut _3: u64;
+      let mut _4: u32;
++     scope 1 (inlined core::num::<impl u64>::unchecked_shl) {
++         debug self => _3;
++         debug rhs => _4;
++         let mut _5: u64;
++         scope 2 {
++         }
++     }
+  
+      bb0: {
+          StorageLive(_3);
+          _3 = _1;
+          StorageLive(_4);
+          _4 = _2;
+-         _0 = core::num::<impl u64>::unchecked_shl(move _3, move _4) -> bb1;
+-     }
+- 
+-     bb1: {
++         StorageLive(_5);
++         _5 = _4 as u64 (IntToInt);
++         _0 = ShlUnchecked(_3, move _5);
++         StorageDead(_5);
+          StorageDead(_4);
+          StorageDead(_3);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_bigger.PreCodegen.after.panic-abort.mir b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_bigger.PreCodegen.after.panic-abort.mir
new file mode 100644
index 00000000000..65b832497f9
--- /dev/null
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_bigger.PreCodegen.after.panic-abort.mir
@@ -0,0 +1,22 @@
+// MIR for `unchecked_shl_unsigned_bigger` after PreCodegen
+
+fn unchecked_shl_unsigned_bigger(_1: u64, _2: u32) -> u64 {
+    debug a => _1;
+    debug b => _2;
+    let mut _0: u64;
+    scope 1 (inlined core::num::<impl u64>::unchecked_shl) {
+        debug self => _1;
+        debug rhs => _2;
+        let mut _3: u64;
+        scope 2 {
+        }
+    }
+
+    bb0: {
+        StorageLive(_3);
+        _3 = _2 as u64 (IntToInt);
+        _0 = ShlUnchecked(_1, move _3);
+        StorageDead(_3);
+        return;
+    }
+}
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_bigger.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_bigger.PreCodegen.after.panic-unwind.mir
new file mode 100644
index 00000000000..65b832497f9
--- /dev/null
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_bigger.PreCodegen.after.panic-unwind.mir
@@ -0,0 +1,22 @@
+// MIR for `unchecked_shl_unsigned_bigger` after PreCodegen
+
+fn unchecked_shl_unsigned_bigger(_1: u64, _2: u32) -> u64 {
+    debug a => _1;
+    debug b => _2;
+    let mut _0: u64;
+    scope 1 (inlined core::num::<impl u64>::unchecked_shl) {
+        debug self => _1;
+        debug rhs => _2;
+        let mut _3: u64;
+        scope 2 {
+        }
+    }
+
+    bb0: {
+        StorageLive(_3);
+        _3 = _2 as u64 (IntToInt);
+        _0 = ShlUnchecked(_1, move _3);
+        StorageDead(_3);
+        return;
+    }
+}
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-abort.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-abort.diff
index 093925b8e4f..90b32247c95 100644
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-abort.diff
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-abort.diff
@@ -7,16 +7,36 @@
       let mut _0: u16;
       let mut _3: u16;
       let mut _4: u32;
++     scope 1 (inlined core::num::<impl u16>::unchecked_shl) {
++         debug self => _3;
++         debug rhs => _4;
++         let mut _5: u16;
++         let mut _6: bool;
++         let mut _7: u32;
++         scope 2 {
++         }
++     }
   
       bb0: {
           StorageLive(_3);
           _3 = _1;
           StorageLive(_4);
           _4 = _2;
-          _0 = core::num::<impl u16>::unchecked_shl(move _3, move _4) -> [return: bb1, unwind unreachable];
-      }
-  
-      bb1: {
+-         _0 = core::num::<impl u16>::unchecked_shl(move _3, move _4) -> [return: bb1, unwind unreachable];
+-     }
+- 
+-     bb1: {
++         StorageLive(_5);
++         StorageLive(_6);
++         StorageLive(_7);
++         _7 = const 65535_u32;
++         _6 = Le(_4, move _7);
++         StorageDead(_7);
++         assume(move _6);
++         StorageDead(_6);
++         _5 = _4 as u16 (IntToInt);
++         _0 = ShlUnchecked(_3, move _5);
++         StorageDead(_5);
           StorageDead(_4);
           StorageDead(_3);
           return;
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-unwind.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-unwind.diff
index 50934e0439a..ba159c063b3 100644
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-unwind.diff
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-unwind.diff
@@ -7,16 +7,36 @@
       let mut _0: u16;
       let mut _3: u16;
       let mut _4: u32;
++     scope 1 (inlined core::num::<impl u16>::unchecked_shl) {
++         debug self => _3;
++         debug rhs => _4;
++         let mut _5: u16;
++         let mut _6: bool;
++         let mut _7: u32;
++         scope 2 {
++         }
++     }
   
       bb0: {
           StorageLive(_3);
           _3 = _1;
           StorageLive(_4);
           _4 = _2;
-          _0 = core::num::<impl u16>::unchecked_shl(move _3, move _4) -> bb1;
-      }
-  
-      bb1: {
+-         _0 = core::num::<impl u16>::unchecked_shl(move _3, move _4) -> bb1;
+-     }
+- 
+-     bb1: {
++         StorageLive(_5);
++         StorageLive(_6);
++         StorageLive(_7);
++         _7 = const 65535_u32;
++         _6 = Le(_4, move _7);
++         StorageDead(_7);
++         assume(move _6);
++         StorageDead(_6);
++         _5 = _4 as u16 (IntToInt);
++         _0 = ShlUnchecked(_3, move _5);
++         StorageDead(_5);
           StorageDead(_4);
           StorageDead(_3);
           return;
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-abort.mir b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-abort.mir
index 46f3511b14c..3f388a69d7e 100644
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-abort.mir
@@ -4,12 +4,28 @@ fn unchecked_shl_unsigned_smaller(_1: u16, _2: u32) -> u16 {
     debug a => _1;
     debug b => _2;
     let mut _0: u16;
-
-    bb0: {
-        _0 = core::num::<impl u16>::unchecked_shl(_1, _2) -> [return: bb1, unwind unreachable];
+    scope 1 (inlined core::num::<impl u16>::unchecked_shl) {
+        debug self => _1;
+        debug rhs => _2;
+        let mut _3: u32;
+        let mut _4: bool;
+        let mut _5: u16;
+        scope 2 {
+        }
     }
 
-    bb1: {
+    bb0: {
+        StorageLive(_5);
+        StorageLive(_4);
+        StorageLive(_3);
+        _3 = const 65535_u32;
+        _4 = Le(_2, move _3);
+        StorageDead(_3);
+        assume(move _4);
+        StorageDead(_4);
+        _5 = _2 as u16 (IntToInt);
+        _0 = ShlUnchecked(_1, move _5);
+        StorageDead(_5);
         return;
     }
 }
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-unwind.mir
index 35fee449c35..3f388a69d7e 100644
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-unwind.mir
@@ -4,12 +4,28 @@ fn unchecked_shl_unsigned_smaller(_1: u16, _2: u32) -> u16 {
     debug a => _1;
     debug b => _2;
     let mut _0: u16;
-
-    bb0: {
-        _0 = core::num::<impl u16>::unchecked_shl(_1, _2) -> bb1;
+    scope 1 (inlined core::num::<impl u16>::unchecked_shl) {
+        debug self => _1;
+        debug rhs => _2;
+        let mut _3: u32;
+        let mut _4: bool;
+        let mut _5: u16;
+        scope 2 {
+        }
     }
 
-    bb1: {
+    bb0: {
+        StorageLive(_5);
+        StorageLive(_4);
+        StorageLive(_3);
+        _3 = const 65535_u32;
+        _4 = Le(_2, move _3);
+        StorageDead(_3);
+        assume(move _4);
+        StorageDead(_4);
+        _5 = _2 as u16 (IntToInt);
+        _0 = ShlUnchecked(_1, move _5);
+        StorageDead(_5);
         return;
     }
 }
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-abort.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-abort.diff
new file mode 100644
index 00000000000..1e83fec4f3d
--- /dev/null
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-abort.diff
@@ -0,0 +1,36 @@
+- // MIR for `unchecked_shr_signed_bigger` before Inline
++ // MIR for `unchecked_shr_signed_bigger` after Inline
+  
+  fn unchecked_shr_signed_bigger(_1: i64, _2: u32) -> i64 {
+      debug a => _1;
+      debug b => _2;
+      let mut _0: i64;
+      let mut _3: i64;
+      let mut _4: u32;
++     scope 1 (inlined core::num::<impl i64>::unchecked_shr) {
++         debug self => _3;
++         debug rhs => _4;
++         let mut _5: i64;
++         scope 2 {
++         }
++     }
+  
+      bb0: {
+          StorageLive(_3);
+          _3 = _1;
+          StorageLive(_4);
+          _4 = _2;
+-         _0 = core::num::<impl i64>::unchecked_shr(move _3, move _4) -> [return: bb1, unwind unreachable];
+-     }
+- 
+-     bb1: {
++         StorageLive(_5);
++         _5 = _4 as i64 (IntToInt);
++         _0 = ShrUnchecked(_3, move _5);
++         StorageDead(_5);
+          StorageDead(_4);
+          StorageDead(_3);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-unwind.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-unwind.diff
new file mode 100644
index 00000000000..d7ff104b92e
--- /dev/null
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-unwind.diff
@@ -0,0 +1,36 @@
+- // MIR for `unchecked_shr_signed_bigger` before Inline
++ // MIR for `unchecked_shr_signed_bigger` after Inline
+  
+  fn unchecked_shr_signed_bigger(_1: i64, _2: u32) -> i64 {
+      debug a => _1;
+      debug b => _2;
+      let mut _0: i64;
+      let mut _3: i64;
+      let mut _4: u32;
++     scope 1 (inlined core::num::<impl i64>::unchecked_shr) {
++         debug self => _3;
++         debug rhs => _4;
++         let mut _5: i64;
++         scope 2 {
++         }
++     }
+  
+      bb0: {
+          StorageLive(_3);
+          _3 = _1;
+          StorageLive(_4);
+          _4 = _2;
+-         _0 = core::num::<impl i64>::unchecked_shr(move _3, move _4) -> bb1;
+-     }
+- 
+-     bb1: {
++         StorageLive(_5);
++         _5 = _4 as i64 (IntToInt);
++         _0 = ShrUnchecked(_3, move _5);
++         StorageDead(_5);
+          StorageDead(_4);
+          StorageDead(_3);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-abort.mir b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-abort.mir
new file mode 100644
index 00000000000..7524ec4970e
--- /dev/null
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-abort.mir
@@ -0,0 +1,22 @@
+// MIR for `unchecked_shr_signed_bigger` after PreCodegen
+
+fn unchecked_shr_signed_bigger(_1: i64, _2: u32) -> i64 {
+    debug a => _1;
+    debug b => _2;
+    let mut _0: i64;
+    scope 1 (inlined core::num::<impl i64>::unchecked_shr) {
+        debug self => _1;
+        debug rhs => _2;
+        let mut _3: i64;
+        scope 2 {
+        }
+    }
+
+    bb0: {
+        StorageLive(_3);
+        _3 = _2 as i64 (IntToInt);
+        _0 = ShrUnchecked(_1, move _3);
+        StorageDead(_3);
+        return;
+    }
+}
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-unwind.mir
new file mode 100644
index 00000000000..7524ec4970e
--- /dev/null
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-unwind.mir
@@ -0,0 +1,22 @@
+// MIR for `unchecked_shr_signed_bigger` after PreCodegen
+
+fn unchecked_shr_signed_bigger(_1: i64, _2: u32) -> i64 {
+    debug a => _1;
+    debug b => _2;
+    let mut _0: i64;
+    scope 1 (inlined core::num::<impl i64>::unchecked_shr) {
+        debug self => _1;
+        debug rhs => _2;
+        let mut _3: i64;
+        scope 2 {
+        }
+    }
+
+    bb0: {
+        StorageLive(_3);
+        _3 = _2 as i64 (IntToInt);
+        _0 = ShrUnchecked(_1, move _3);
+        StorageDead(_3);
+        return;
+    }
+}
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.Inline.panic-abort.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.Inline.panic-abort.diff
index 1659a51b090..fa7e5d16e39 100644
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.Inline.panic-abort.diff
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.Inline.panic-abort.diff
@@ -7,16 +7,36 @@
       let mut _0: i16;
       let mut _3: i16;
       let mut _4: u32;
++     scope 1 (inlined core::num::<impl i16>::unchecked_shr) {
++         debug self => _3;
++         debug rhs => _4;
++         let mut _5: i16;
++         let mut _6: bool;
++         let mut _7: u32;
++         scope 2 {
++         }
++     }
   
       bb0: {
           StorageLive(_3);
           _3 = _1;
           StorageLive(_4);
           _4 = _2;
-          _0 = core::num::<impl i16>::unchecked_shr(move _3, move _4) -> [return: bb1, unwind unreachable];
-      }
-  
-      bb1: {
+-         _0 = core::num::<impl i16>::unchecked_shr(move _3, move _4) -> [return: bb1, unwind unreachable];
+-     }
+- 
+-     bb1: {
++         StorageLive(_5);
++         StorageLive(_6);
++         StorageLive(_7);
++         _7 = const 32767_u32;
++         _6 = Le(_4, move _7);
++         StorageDead(_7);
++         assume(move _6);
++         StorageDead(_6);
++         _5 = _4 as i16 (IntToInt);
++         _0 = ShrUnchecked(_3, move _5);
++         StorageDead(_5);
           StorageDead(_4);
           StorageDead(_3);
           return;
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.Inline.panic-unwind.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.Inline.panic-unwind.diff
index cb5ec37feb3..3d398e00fc8 100644
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.Inline.panic-unwind.diff
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.Inline.panic-unwind.diff
@@ -7,16 +7,36 @@
       let mut _0: i16;
       let mut _3: i16;
       let mut _4: u32;
++     scope 1 (inlined core::num::<impl i16>::unchecked_shr) {
++         debug self => _3;
++         debug rhs => _4;
++         let mut _5: i16;
++         let mut _6: bool;
++         let mut _7: u32;
++         scope 2 {
++         }
++     }
   
       bb0: {
           StorageLive(_3);
           _3 = _1;
           StorageLive(_4);
           _4 = _2;
-          _0 = core::num::<impl i16>::unchecked_shr(move _3, move _4) -> bb1;
-      }
-  
-      bb1: {
+-         _0 = core::num::<impl i16>::unchecked_shr(move _3, move _4) -> bb1;
+-     }
+- 
+-     bb1: {
++         StorageLive(_5);
++         StorageLive(_6);
++         StorageLive(_7);
++         _7 = const 32767_u32;
++         _6 = Le(_4, move _7);
++         StorageDead(_7);
++         assume(move _6);
++         StorageDead(_6);
++         _5 = _4 as i16 (IntToInt);
++         _0 = ShrUnchecked(_3, move _5);
++         StorageDead(_5);
           StorageDead(_4);
           StorageDead(_3);
           return;
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.panic-abort.mir b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.panic-abort.mir
index 9f2f40002a3..64ea25349ac 100644
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.panic-abort.mir
@@ -4,12 +4,28 @@ fn unchecked_shr_signed_smaller(_1: i16, _2: u32) -> i16 {
     debug a => _1;
     debug b => _2;
     let mut _0: i16;
-
-    bb0: {
-        _0 = core::num::<impl i16>::unchecked_shr(_1, _2) -> [return: bb1, unwind unreachable];
+    scope 1 (inlined core::num::<impl i16>::unchecked_shr) {
+        debug self => _1;
+        debug rhs => _2;
+        let mut _3: u32;
+        let mut _4: bool;
+        let mut _5: i16;
+        scope 2 {
+        }
     }
 
-    bb1: {
+    bb0: {
+        StorageLive(_5);
+        StorageLive(_4);
+        StorageLive(_3);
+        _3 = const 32767_u32;
+        _4 = Le(_2, move _3);
+        StorageDead(_3);
+        assume(move _4);
+        StorageDead(_4);
+        _5 = _2 as i16 (IntToInt);
+        _0 = ShrUnchecked(_1, move _5);
+        StorageDead(_5);
         return;
     }
 }
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.panic-unwind.mir
index aaf3bb62e8a..64ea25349ac 100644
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.panic-unwind.mir
@@ -4,12 +4,28 @@ fn unchecked_shr_signed_smaller(_1: i16, _2: u32) -> i16 {
     debug a => _1;
     debug b => _2;
     let mut _0: i16;
-
-    bb0: {
-        _0 = core::num::<impl i16>::unchecked_shr(_1, _2) -> bb1;
+    scope 1 (inlined core::num::<impl i16>::unchecked_shr) {
+        debug self => _1;
+        debug rhs => _2;
+        let mut _3: u32;
+        let mut _4: bool;
+        let mut _5: i16;
+        scope 2 {
+        }
     }
 
-    bb1: {
+    bb0: {
+        StorageLive(_5);
+        StorageLive(_4);
+        StorageLive(_3);
+        _3 = const 32767_u32;
+        _4 = Le(_2, move _3);
+        StorageDead(_3);
+        assume(move _4);
+        StorageDead(_4);
+        _5 = _2 as i16 (IntToInt);
+        _0 = ShrUnchecked(_1, move _5);
+        StorageDead(_5);
         return;
     }
 }
diff --git a/tests/mir-opt/lower_intrinsics.rs b/tests/mir-opt/lower_intrinsics.rs
index 876b4497034..0758e9b775b 100644
--- a/tests/mir-opt/lower_intrinsics.rs
+++ b/tests/mir-opt/lower_intrinsics.rs
@@ -13,8 +13,13 @@ pub fn wrapping(a: i32, b: i32) {
 
 // EMIT_MIR lower_intrinsics.unchecked.LowerIntrinsics.diff
 pub unsafe fn unchecked(a: i32, b: i32) {
+    let _a = core::intrinsics::unchecked_add(a, b);
+    let _b = core::intrinsics::unchecked_sub(a, b);
+    let _c = core::intrinsics::unchecked_mul(a, b);
     let _x = core::intrinsics::unchecked_div(a, b);
     let _y = core::intrinsics::unchecked_rem(a, b);
+    let _i = core::intrinsics::unchecked_shl(a, b);
+    let _j = core::intrinsics::unchecked_shr(a, b);
 }
 
 // EMIT_MIR lower_intrinsics.size_of.LowerIntrinsics.diff
diff --git a/tests/mir-opt/lower_intrinsics.unchecked.LowerIntrinsics.panic-abort.diff b/tests/mir-opt/lower_intrinsics.unchecked.LowerIntrinsics.panic-abort.diff
index c532d74ced0..dd92b8d6d2c 100644
--- a/tests/mir-opt/lower_intrinsics.unchecked.LowerIntrinsics.panic-abort.diff
+++ b/tests/mir-opt/lower_intrinsics.unchecked.LowerIntrinsics.panic-abort.diff
@@ -10,11 +10,41 @@
       let mut _5: i32;
       let mut _7: i32;
       let mut _8: i32;
+      let mut _10: i32;
+      let mut _11: i32;
+      let mut _13: i32;
+      let mut _14: i32;
+      let mut _16: i32;
+      let mut _17: i32;
+      let mut _19: i32;
+      let mut _20: i32;
+      let mut _22: i32;
+      let mut _23: i32;
       scope 1 {
-          debug _x => _3;
+          debug _a => _3;
           let _6: i32;
           scope 2 {
-              debug _y => _6;
+              debug _b => _6;
+              let _9: i32;
+              scope 3 {
+                  debug _c => _9;
+                  let _12: i32;
+                  scope 4 {
+                      debug _x => _12;
+                      let _15: i32;
+                      scope 5 {
+                          debug _y => _15;
+                          let _18: i32;
+                          scope 6 {
+                              debug _i => _18;
+                              let _21: i32;
+                              scope 7 {
+                                  debug _j => _21;
+                              }
+                          }
+                      }
+                  }
+              }
           }
       }
   
@@ -24,8 +54,8 @@
           _4 = _1;
           StorageLive(_5);
           _5 = _2;
--         _3 = unchecked_div::<i32>(move _4, move _5) -> [return: bb1, unwind unreachable];
-+         _3 = Div(move _4, move _5);
+-         _3 = unchecked_add::<i32>(move _4, move _5) -> [return: bb1, unwind unreachable];
++         _3 = AddUnchecked(move _4, move _5);
 +         goto -> bb1;
       }
   
@@ -37,15 +67,85 @@
           _7 = _1;
           StorageLive(_8);
           _8 = _2;
--         _6 = unchecked_rem::<i32>(move _7, move _8) -> [return: bb2, unwind unreachable];
-+         _6 = Rem(move _7, move _8);
+-         _6 = unchecked_sub::<i32>(move _7, move _8) -> [return: bb2, unwind unreachable];
++         _6 = SubUnchecked(move _7, move _8);
 +         goto -> bb2;
       }
   
       bb2: {
           StorageDead(_8);
           StorageDead(_7);
+          StorageLive(_9);
+          StorageLive(_10);
+          _10 = _1;
+          StorageLive(_11);
+          _11 = _2;
+-         _9 = unchecked_mul::<i32>(move _10, move _11) -> [return: bb3, unwind unreachable];
++         _9 = MulUnchecked(move _10, move _11);
++         goto -> bb3;
+      }
+  
+      bb3: {
+          StorageDead(_11);
+          StorageDead(_10);
+          StorageLive(_12);
+          StorageLive(_13);
+          _13 = _1;
+          StorageLive(_14);
+          _14 = _2;
+-         _12 = unchecked_div::<i32>(move _13, move _14) -> [return: bb4, unwind unreachable];
++         _12 = Div(move _13, move _14);
++         goto -> bb4;
+      }
+  
+      bb4: {
+          StorageDead(_14);
+          StorageDead(_13);
+          StorageLive(_15);
+          StorageLive(_16);
+          _16 = _1;
+          StorageLive(_17);
+          _17 = _2;
+-         _15 = unchecked_rem::<i32>(move _16, move _17) -> [return: bb5, unwind unreachable];
++         _15 = Rem(move _16, move _17);
++         goto -> bb5;
+      }
+  
+      bb5: {
+          StorageDead(_17);
+          StorageDead(_16);
+          StorageLive(_18);
+          StorageLive(_19);
+          _19 = _1;
+          StorageLive(_20);
+          _20 = _2;
+-         _18 = unchecked_shl::<i32>(move _19, move _20) -> [return: bb6, unwind unreachable];
++         _18 = ShlUnchecked(move _19, move _20);
++         goto -> bb6;
+      }
+  
+      bb6: {
+          StorageDead(_20);
+          StorageDead(_19);
+          StorageLive(_21);
+          StorageLive(_22);
+          _22 = _1;
+          StorageLive(_23);
+          _23 = _2;
+-         _21 = unchecked_shr::<i32>(move _22, move _23) -> [return: bb7, unwind unreachable];
++         _21 = ShrUnchecked(move _22, move _23);
++         goto -> bb7;
+      }
+  
+      bb7: {
+          StorageDead(_23);
+          StorageDead(_22);
           _0 = const ();
+          StorageDead(_21);
+          StorageDead(_18);
+          StorageDead(_15);
+          StorageDead(_12);
+          StorageDead(_9);
           StorageDead(_6);
           StorageDead(_3);
           return;
diff --git a/tests/mir-opt/lower_intrinsics.unchecked.LowerIntrinsics.panic-unwind.diff b/tests/mir-opt/lower_intrinsics.unchecked.LowerIntrinsics.panic-unwind.diff
index c532d74ced0..dd92b8d6d2c 100644
--- a/tests/mir-opt/lower_intrinsics.unchecked.LowerIntrinsics.panic-unwind.diff
+++ b/tests/mir-opt/lower_intrinsics.unchecked.LowerIntrinsics.panic-unwind.diff
@@ -10,11 +10,41 @@
       let mut _5: i32;
       let mut _7: i32;
       let mut _8: i32;
+      let mut _10: i32;
+      let mut _11: i32;
+      let mut _13: i32;
+      let mut _14: i32;
+      let mut _16: i32;
+      let mut _17: i32;
+      let mut _19: i32;
+      let mut _20: i32;
+      let mut _22: i32;
+      let mut _23: i32;
       scope 1 {
-          debug _x => _3;
+          debug _a => _3;
           let _6: i32;
           scope 2 {
-              debug _y => _6;
+              debug _b => _6;
+              let _9: i32;
+              scope 3 {
+                  debug _c => _9;
+                  let _12: i32;
+                  scope 4 {
+                      debug _x => _12;
+                      let _15: i32;
+                      scope 5 {
+                          debug _y => _15;
+                          let _18: i32;
+                          scope 6 {
+                              debug _i => _18;
+                              let _21: i32;
+                              scope 7 {
+                                  debug _j => _21;
+                              }
+                          }
+                      }
+                  }
+              }
           }
       }
   
@@ -24,8 +54,8 @@
           _4 = _1;
           StorageLive(_5);
           _5 = _2;
--         _3 = unchecked_div::<i32>(move _4, move _5) -> [return: bb1, unwind unreachable];
-+         _3 = Div(move _4, move _5);
+-         _3 = unchecked_add::<i32>(move _4, move _5) -> [return: bb1, unwind unreachable];
++         _3 = AddUnchecked(move _4, move _5);
 +         goto -> bb1;
       }
   
@@ -37,15 +67,85 @@
           _7 = _1;
           StorageLive(_8);
           _8 = _2;
--         _6 = unchecked_rem::<i32>(move _7, move _8) -> [return: bb2, unwind unreachable];
-+         _6 = Rem(move _7, move _8);
+-         _6 = unchecked_sub::<i32>(move _7, move _8) -> [return: bb2, unwind unreachable];
++         _6 = SubUnchecked(move _7, move _8);
 +         goto -> bb2;
       }
   
       bb2: {
           StorageDead(_8);
           StorageDead(_7);
+          StorageLive(_9);
+          StorageLive(_10);
+          _10 = _1;
+          StorageLive(_11);
+          _11 = _2;
+-         _9 = unchecked_mul::<i32>(move _10, move _11) -> [return: bb3, unwind unreachable];
++         _9 = MulUnchecked(move _10, move _11);
++         goto -> bb3;
+      }
+  
+      bb3: {
+          StorageDead(_11);
+          StorageDead(_10);
+          StorageLive(_12);
+          StorageLive(_13);
+          _13 = _1;
+          StorageLive(_14);
+          _14 = _2;
+-         _12 = unchecked_div::<i32>(move _13, move _14) -> [return: bb4, unwind unreachable];
++         _12 = Div(move _13, move _14);
++         goto -> bb4;
+      }
+  
+      bb4: {
+          StorageDead(_14);
+          StorageDead(_13);
+          StorageLive(_15);
+          StorageLive(_16);
+          _16 = _1;
+          StorageLive(_17);
+          _17 = _2;
+-         _15 = unchecked_rem::<i32>(move _16, move _17) -> [return: bb5, unwind unreachable];
++         _15 = Rem(move _16, move _17);
++         goto -> bb5;
+      }
+  
+      bb5: {
+          StorageDead(_17);
+          StorageDead(_16);
+          StorageLive(_18);
+          StorageLive(_19);
+          _19 = _1;
+          StorageLive(_20);
+          _20 = _2;
+-         _18 = unchecked_shl::<i32>(move _19, move _20) -> [return: bb6, unwind unreachable];
++         _18 = ShlUnchecked(move _19, move _20);
++         goto -> bb6;
+      }
+  
+      bb6: {
+          StorageDead(_20);
+          StorageDead(_19);
+          StorageLive(_21);
+          StorageLive(_22);
+          _22 = _1;
+          StorageLive(_23);
+          _23 = _2;
+-         _21 = unchecked_shr::<i32>(move _22, move _23) -> [return: bb7, unwind unreachable];
++         _21 = ShrUnchecked(move _22, move _23);
++         goto -> bb7;
+      }
+  
+      bb7: {
+          StorageDead(_23);
+          StorageDead(_22);
           _0 = const ();
+          StorageDead(_21);
+          StorageDead(_18);
+          StorageDead(_15);
+          StorageDead(_12);
+          StorageDead(_9);
           StorageDead(_6);
           StorageDead(_3);
           return;
diff --git a/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.mir
index 10b93a15799..800308c2e0b 100644
--- a/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.mir
+++ b/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.mir
@@ -7,17 +7,16 @@ fn checked_shl(_1: u32, _2: u32) -> Option<u32> {
     scope 1 (inlined core::num::<impl u32>::checked_shl) {
         debug self => _1;
         debug rhs => _2;
-        let mut _11: u32;
-        let mut _12: bool;
+        let mut _7: bool;
         scope 2 {
-            debug a => _11;
-            debug b => _10;
+            debug a => _5;
+            debug b => _6;
         }
         scope 3 (inlined core::num::<impl u32>::overflowing_shl) {
             debug self => _1;
             debug rhs => _2;
-            let mut _9: u32;
-            let mut _10: bool;
+            let mut _5: u32;
+            let mut _6: bool;
             scope 4 (inlined core::num::<impl u32>::wrapping_shl) {
                 debug self => _1;
                 debug rhs => _2;
@@ -27,52 +26,7 @@ fn checked_shl(_1: u32, _2: u32) -> Option<u32> {
                     scope 6 (inlined core::num::<impl u32>::unchecked_shl) {
                         debug self => _1;
                         debug rhs => _4;
-                        let mut _8: u32;
                         scope 7 {
-                            scope 8 (inlined core::num::<impl u32>::unchecked_shl::conv) {
-                                debug x => _4;
-                                let mut _5: std::result::Result<u32, std::convert::Infallible>;
-                                let mut _7: std::option::Option<u32>;
-                                scope 9 {
-                                    scope 10 (inlined <u32 as TryInto<u32>>::try_into) {
-                                        debug self => _4;
-                                        scope 11 (inlined <u32 as TryFrom<u32>>::try_from) {
-                                            debug value => _4;
-                                            scope 12 (inlined <u32 as Into<u32>>::into) {
-                                                debug self => _4;
-                                                scope 13 (inlined <u32 as From<u32>>::from) {
-                                                    debug t => _4;
-                                                }
-                                            }
-                                        }
-                                    }
-                                    scope 14 (inlined Result::<u32, Infallible>::ok) {
-                                        debug self => _5;
-                                        let _6: u32;
-                                        scope 15 {
-                                            debug x => _6;
-                                        }
-                                    }
-                                    scope 16 (inlined #[track_caller] Option::<u32>::unwrap_unchecked) {
-                                        debug self => _7;
-                                        let mut _13: &std::option::Option<u32>;
-                                        scope 17 {
-                                            debug val => _8;
-                                        }
-                                        scope 18 {
-                                            scope 20 (inlined unreachable_unchecked) {
-                                                scope 21 {
-                                                    scope 22 (inlined unreachable_unchecked::runtime) {
-                                                    }
-                                                }
-                                            }
-                                        }
-                                        scope 19 (inlined Option::<u32>::is_some) {
-                                            debug self => _13;
-                                        }
-                                    }
-                                }
-                            }
                         }
                     }
                 }
@@ -81,58 +35,38 @@ fn checked_shl(_1: u32, _2: u32) -> Option<u32> {
     }
 
     bb0: {
-        StorageLive(_10);
-        StorageLive(_11);
-        StorageLive(_9);
+        StorageLive(_5);
+        StorageLive(_6);
         StorageLive(_4);
         StorageLive(_3);
         _3 = const 31_u32;
         _4 = BitAnd(_2, move _3);
         StorageDead(_3);
-        StorageLive(_8);
+        _5 = ShlUnchecked(_1, _4);
+        StorageDead(_4);
+        _6 = Ge(_2, const _);
         StorageLive(_7);
-        StorageLive(_5);
-        _5 = Result::<u32, Infallible>::Ok(_4);
-        StorageLive(_6);
-        _6 = move ((_5 as Ok).0: u32);
-        _7 = Option::<u32>::Some(move _6);
-        StorageDead(_6);
-        StorageDead(_5);
-        StorageLive(_13);
-        _8 = move ((_7 as Some).0: u32);
-        StorageDead(_13);
-        StorageDead(_7);
-        _9 = unchecked_shl::<u32>(_1, move _8) -> [return: bb1, unwind unreachable];
+        _7 = unlikely(_6) -> [return: bb1, unwind unreachable];
     }
 
     bb1: {
-        StorageDead(_8);
-        StorageDead(_4);
-        _10 = Ge(_2, const _);
-        _11 = move _9;
-        StorageDead(_9);
-        StorageLive(_12);
-        _12 = unlikely(_10) -> [return: bb2, unwind unreachable];
+        switchInt(move _7) -> [0: bb2, otherwise: bb3];
     }
 
     bb2: {
-        switchInt(move _12) -> [0: bb3, otherwise: bb4];
+        _0 = Option::<u32>::Some(_5);
+        goto -> bb4;
     }
 
     bb3: {
-        _0 = Option::<u32>::Some(_11);
-        goto -> bb5;
-    }
-
-    bb4: {
         _0 = Option::<u32>::None;
-        goto -> bb5;
+        goto -> bb4;
     }
 
-    bb5: {
-        StorageDead(_12);
-        StorageDead(_11);
-        StorageDead(_10);
+    bb4: {
+        StorageDead(_7);
+        StorageDead(_6);
+        StorageDead(_5);
         return;
     }
 }
diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir
index 79282d88905..9d914e95344 100644
--- a/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir
@@ -10,18 +10,17 @@ fn slice_get_unchecked_mut_range(_1: &mut [u32], _2: std::ops::Range<usize>) ->
         debug self => _1;
         debug index => std::ops::Range<usize>{ .0 => _3, .1 => _4, };
         let mut _5: *mut [u32];
-        let mut _14: *mut [u32];
+        let mut _13: *mut [u32];
         scope 2 {
             scope 3 (inlined <std::ops::Range<usize> as SliceIndex<[u32]>>::get_unchecked_mut) {
                 debug self => std::ops::Range<usize>{ .0 => _3, .1 => _4, };
                 debug slice => _5;
                 let mut _7: *mut u32;
                 let mut _8: *mut u32;
-                let mut _9: usize;
+                let _15: usize;
                 let _16: usize;
-                let _17: usize;
                 scope 4 {
-                    debug this => std::ops::Range<usize>{ .0 => _16, .1 => _17, };
+                    debug this => std::ops::Range<usize>{ .0 => _15, .1 => _16, };
                     scope 5 {
                         let _6: usize;
                         scope 6 {
@@ -37,30 +36,30 @@ fn slice_get_unchecked_mut_range(_1: &mut [u32], _2: std::ops::Range<usize>) ->
                             }
                             scope 14 (inlined slice_from_raw_parts_mut::<u32>) {
                                 debug data => _8;
-                                debug len => _9;
-                                let mut _10: *mut ();
+                                debug len => _6;
+                                let mut _9: *mut ();
                                 scope 15 (inlined ptr::mut_ptr::<impl *mut u32>::cast::<()>) {
                                     debug self => _8;
                                 }
                                 scope 16 (inlined std::ptr::from_raw_parts_mut::<[u32]>) {
-                                    debug data_address => _10;
-                                    debug metadata => _9;
-                                    let mut _11: *const ();
-                                    let mut _12: std::ptr::metadata::PtrComponents<[u32]>;
-                                    let mut _13: std::ptr::metadata::PtrRepr<[u32]>;
+                                    debug data_address => _9;
+                                    debug metadata => _6;
+                                    let mut _10: *const ();
+                                    let mut _11: std::ptr::metadata::PtrComponents<[u32]>;
+                                    let mut _12: std::ptr::metadata::PtrRepr<[u32]>;
                                     scope 17 {
                                     }
                                 }
                             }
                         }
                         scope 7 (inlined <std::ops::Range<usize> as SliceIndex<[T]>>::get_unchecked_mut::runtime::<u32>) {
-                            debug this => std::ops::Range<usize>{ .0 => _16, .1 => _17, };
+                            debug this => std::ops::Range<usize>{ .0 => _15, .1 => _16, };
                             debug slice => _5;
                             scope 8 (inlined ptr::mut_ptr::<impl *mut [u32]>::len) {
                                 debug self => _5;
-                                let mut _15: *const [u32];
+                                let mut _14: *const [u32];
                                 scope 9 (inlined std::ptr::metadata::<[u32]>) {
-                                    debug ptr => _15;
+                                    debug ptr => _14;
                                     scope 10 {
                                     }
                                 }
@@ -75,46 +74,40 @@ fn slice_get_unchecked_mut_range(_1: &mut [u32], _2: std::ops::Range<usize>) ->
     bb0: {
         _3 = move (_2.0: usize);
         _4 = move (_2.1: usize);
-        StorageLive(_14);
+        StorageLive(_13);
         StorageLive(_5);
         _5 = &raw mut (*_1);
+        StorageLive(_6);
+        StorageLive(_14);
         StorageLive(_15);
         StorageLive(_16);
-        StorageLive(_17);
-        StorageLive(_6);
-        _6 = unchecked_sub::<usize>(_4, _3) -> [return: bb1, unwind unreachable];
-    }
-
-    bb1: {
+        _6 = SubUnchecked(_4, _3);
         StorageLive(_8);
         StorageLive(_7);
         _7 = _5 as *mut u32 (PtrToPtr);
         _8 = Offset(_7, _3);
         StorageDead(_7);
         StorageLive(_9);
-        _9 = _6;
-        StorageLive(_10);
-        _10 = _8 as *mut () (PtrToPtr);
-        StorageLive(_13);
+        _9 = _8 as *mut () (PtrToPtr);
         StorageLive(_12);
         StorageLive(_11);
-        _11 = _10 as *const () (Pointer(MutToConstPointer));
-        _12 = ptr::metadata::PtrComponents::<[u32]> { data_address: move _11, metadata: _9 };
+        StorageLive(_10);
+        _10 = _9 as *const () (Pointer(MutToConstPointer));
+        _11 = ptr::metadata::PtrComponents::<[u32]> { data_address: move _10, metadata: _6 };
+        StorageDead(_10);
+        _12 = ptr::metadata::PtrRepr::<[u32]> { const_ptr: move _11 };
         StorageDead(_11);
-        _13 = ptr::metadata::PtrRepr::<[u32]> { const_ptr: move _12 };
+        _13 = (_12.1: *mut [u32]);
         StorageDead(_12);
-        _14 = (_13.1: *mut [u32]);
-        StorageDead(_13);
-        StorageDead(_10);
         StorageDead(_9);
         StorageDead(_8);
-        StorageDead(_6);
-        StorageDead(_17);
         StorageDead(_16);
         StorageDead(_15);
-        StorageDead(_5);
-        _0 = &mut (*_14);
         StorageDead(_14);
+        StorageDead(_6);
+        StorageDead(_5);
+        _0 = &mut (*_13);
+        StorageDead(_13);
         return;
     }
 }
diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir
index 79282d88905..9d914e95344 100644
--- a/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir
@@ -10,18 +10,17 @@ fn slice_get_unchecked_mut_range(_1: &mut [u32], _2: std::ops::Range<usize>) ->
         debug self => _1;
         debug index => std::ops::Range<usize>{ .0 => _3, .1 => _4, };
         let mut _5: *mut [u32];
-        let mut _14: *mut [u32];
+        let mut _13: *mut [u32];
         scope 2 {
             scope 3 (inlined <std::ops::Range<usize> as SliceIndex<[u32]>>::get_unchecked_mut) {
                 debug self => std::ops::Range<usize>{ .0 => _3, .1 => _4, };
                 debug slice => _5;
                 let mut _7: *mut u32;
                 let mut _8: *mut u32;
-                let mut _9: usize;
+                let _15: usize;
                 let _16: usize;
-                let _17: usize;
                 scope 4 {
-                    debug this => std::ops::Range<usize>{ .0 => _16, .1 => _17, };
+                    debug this => std::ops::Range<usize>{ .0 => _15, .1 => _16, };
                     scope 5 {
                         let _6: usize;
                         scope 6 {
@@ -37,30 +36,30 @@ fn slice_get_unchecked_mut_range(_1: &mut [u32], _2: std::ops::Range<usize>) ->
                             }
                             scope 14 (inlined slice_from_raw_parts_mut::<u32>) {
                                 debug data => _8;
-                                debug len => _9;
-                                let mut _10: *mut ();
+                                debug len => _6;
+                                let mut _9: *mut ();
                                 scope 15 (inlined ptr::mut_ptr::<impl *mut u32>::cast::<()>) {
                                     debug self => _8;
                                 }
                                 scope 16 (inlined std::ptr::from_raw_parts_mut::<[u32]>) {
-                                    debug data_address => _10;
-                                    debug metadata => _9;
-                                    let mut _11: *const ();
-                                    let mut _12: std::ptr::metadata::PtrComponents<[u32]>;
-                                    let mut _13: std::ptr::metadata::PtrRepr<[u32]>;
+                                    debug data_address => _9;
+                                    debug metadata => _6;
+                                    let mut _10: *const ();
+                                    let mut _11: std::ptr::metadata::PtrComponents<[u32]>;
+                                    let mut _12: std::ptr::metadata::PtrRepr<[u32]>;
                                     scope 17 {
                                     }
                                 }
                             }
                         }
                         scope 7 (inlined <std::ops::Range<usize> as SliceIndex<[T]>>::get_unchecked_mut::runtime::<u32>) {
-                            debug this => std::ops::Range<usize>{ .0 => _16, .1 => _17, };
+                            debug this => std::ops::Range<usize>{ .0 => _15, .1 => _16, };
                             debug slice => _5;
                             scope 8 (inlined ptr::mut_ptr::<impl *mut [u32]>::len) {
                                 debug self => _5;
-                                let mut _15: *const [u32];
+                                let mut _14: *const [u32];
                                 scope 9 (inlined std::ptr::metadata::<[u32]>) {
-                                    debug ptr => _15;
+                                    debug ptr => _14;
                                     scope 10 {
                                     }
                                 }
@@ -75,46 +74,40 @@ fn slice_get_unchecked_mut_range(_1: &mut [u32], _2: std::ops::Range<usize>) ->
     bb0: {
         _3 = move (_2.0: usize);
         _4 = move (_2.1: usize);
-        StorageLive(_14);
+        StorageLive(_13);
         StorageLive(_5);
         _5 = &raw mut (*_1);
+        StorageLive(_6);
+        StorageLive(_14);
         StorageLive(_15);
         StorageLive(_16);
-        StorageLive(_17);
-        StorageLive(_6);
-        _6 = unchecked_sub::<usize>(_4, _3) -> [return: bb1, unwind unreachable];
-    }
-
-    bb1: {
+        _6 = SubUnchecked(_4, _3);
         StorageLive(_8);
         StorageLive(_7);
         _7 = _5 as *mut u32 (PtrToPtr);
         _8 = Offset(_7, _3);
         StorageDead(_7);
         StorageLive(_9);
-        _9 = _6;
-        StorageLive(_10);
-        _10 = _8 as *mut () (PtrToPtr);
-        StorageLive(_13);
+        _9 = _8 as *mut () (PtrToPtr);
         StorageLive(_12);
         StorageLive(_11);
-        _11 = _10 as *const () (Pointer(MutToConstPointer));
-        _12 = ptr::metadata::PtrComponents::<[u32]> { data_address: move _11, metadata: _9 };
+        StorageLive(_10);
+        _10 = _9 as *const () (Pointer(MutToConstPointer));
+        _11 = ptr::metadata::PtrComponents::<[u32]> { data_address: move _10, metadata: _6 };
+        StorageDead(_10);
+        _12 = ptr::metadata::PtrRepr::<[u32]> { const_ptr: move _11 };
         StorageDead(_11);
-        _13 = ptr::metadata::PtrRepr::<[u32]> { const_ptr: move _12 };
+        _13 = (_12.1: *mut [u32]);
         StorageDead(_12);
-        _14 = (_13.1: *mut [u32]);
-        StorageDead(_13);
-        StorageDead(_10);
         StorageDead(_9);
         StorageDead(_8);
-        StorageDead(_6);
-        StorageDead(_17);
         StorageDead(_16);
         StorageDead(_15);
-        StorageDead(_5);
-        _0 = &mut (*_14);
         StorageDead(_14);
+        StorageDead(_6);
+        StorageDead(_5);
+        _0 = &mut (*_13);
+        StorageDead(_13);
         return;
     }
 }
diff --git a/tests/run-make/coverage-reports/sort_subviews.py b/tests/run-make/coverage-reports/sort_subviews.py
index 10cfc51d447..10cfc51d447 100644..100755
--- a/tests/run-make/coverage-reports/sort_subviews.py
+++ b/tests/run-make/coverage-reports/sort_subviews.py
diff --git a/tests/run-make/libtest-junit/validate_junit.py b/tests/run-make/libtest-junit/validate_junit.py
index 47a8e70ccc3..0d9b34a3cf7 100755
--- a/tests/run-make/libtest-junit/validate_junit.py
+++ b/tests/run-make/libtest-junit/validate_junit.py
@@ -7,6 +7,6 @@ import xml.etree.ElementTree as ET
 for line in sys.stdin:
     try:
         ET.fromstring(line)
-    except ET.ParseError as pe:
+    except ET.ParseError:
         print("Invalid xml: %r" % line)
         raise
diff --git a/tests/run-make/rustdoc-map-file/validate_json.py b/tests/run-make/rustdoc-map-file/validate_json.py
index 5c14c90b70d..912dea3791b 100755
--- a/tests/run-make/rustdoc-map-file/validate_json.py
+++ b/tests/run-make/rustdoc-map-file/validate_json.py
@@ -6,7 +6,7 @@ import json
 
 
 def find_redirect_map_file(folder, errors):
-    for root, dirs, files in os.walk(folder):
+    for root, _dirs, files in os.walk(folder):
         for name in files:
             if not name.endswith("redirect-map.json"):
                 continue
diff --git a/tests/run-make/sysroot-crates-are-unstable/test.py b/tests/run-make/sysroot-crates-are-unstable/test.py
index cab4faa4e64..45cfdd195b4 100644
--- a/tests/run-make/sysroot-crates-are-unstable/test.py
+++ b/tests/run-make/sysroot-crates-are-unstable/test.py
@@ -46,7 +46,7 @@ def check_lib(lib):
                                    '--target', os.environ['TARGET'],
                                    '--extern', '{}={}'.format(lib['name'], lib['path'])],
                                   to_input=('extern crate {};'.format(lib['name'])).encode('utf-8'))
-    if not 'use of unstable library feature' in '{}{}'.format(stdout, stderr):
+    if 'use of unstable library feature' not in '{}{}'.format(stdout, stderr):
         print('crate {} "{}" is not unstable'.format(lib['name'], lib['path']))
         print('{}{}'.format(stdout, stderr))
         print('')
diff --git a/tests/ui/enum/suggest-default-attribute.stderr b/tests/ui/enum/suggest-default-attribute.stderr
index fb830d3f78b..b56d599a786 100644
--- a/tests/ui/enum/suggest-default-attribute.stderr
+++ b/tests/ui/enum/suggest-default-attribute.stderr
@@ -7,7 +7,7 @@ LL |     #[default]
 help: consider adding a derive
    |
 LL + #[derive(Default)]
-LL ~ pub enum Test {
+LL | pub enum Test {
    |
 
 error: aborting due to previous error
diff --git a/tests/ui/impl-trait/coherence-treats-tait-ambig.current.stderr b/tests/ui/impl-trait/coherence-treats-tait-ambig.current.stderr
new file mode 100644
index 00000000000..61fed16294b
--- /dev/null
+++ b/tests/ui/impl-trait/coherence-treats-tait-ambig.current.stderr
@@ -0,0 +1,13 @@
+error[E0119]: conflicting implementations of trait `Into<T>` for type `Foo`
+  --> $DIR/coherence-treats-tait-ambig.rs:10:1
+   |
+LL | impl Into<T> for Foo {
+   | ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: conflicting implementation in crate `core`:
+           - impl<T, U> Into<U> for T
+             where U: From<T>;
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0119`.
diff --git a/tests/ui/impl-trait/coherence-treats-tait-ambig.next.stderr b/tests/ui/impl-trait/coherence-treats-tait-ambig.next.stderr
new file mode 100644
index 00000000000..61fed16294b
--- /dev/null
+++ b/tests/ui/impl-trait/coherence-treats-tait-ambig.next.stderr
@@ -0,0 +1,13 @@
+error[E0119]: conflicting implementations of trait `Into<T>` for type `Foo`
+  --> $DIR/coherence-treats-tait-ambig.rs:10:1
+   |
+LL | impl Into<T> for Foo {
+   | ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: conflicting implementation in crate `core`:
+           - impl<T, U> Into<U> for T
+             where U: From<T>;
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0119`.
diff --git a/tests/ui/impl-trait/coherence-treats-tait-ambig.rs b/tests/ui/impl-trait/coherence-treats-tait-ambig.rs
new file mode 100644
index 00000000000..156a7eb0e23
--- /dev/null
+++ b/tests/ui/impl-trait/coherence-treats-tait-ambig.rs
@@ -0,0 +1,19 @@
+// revisions: current next
+//[next] compile-flags: -Ztrait-solver=next
+
+#![feature(type_alias_impl_trait)]
+
+type T = impl Sized;
+
+struct Foo;
+
+impl Into<T> for Foo {
+//~^ ERROR conflicting implementations of trait `Into<T>` for type `Foo`
+    fn into(self) -> T {
+        Foo
+    }
+}
+
+fn main() {
+    let _: T = Foo.into();
+}
diff --git a/tests/ui/impl-trait/in-trait/suggest-missing-item.fixed b/tests/ui/impl-trait/in-trait/suggest-missing-item.fixed
new file mode 100644
index 00000000000..16c4d15ddcd
--- /dev/null
+++ b/tests/ui/impl-trait/in-trait/suggest-missing-item.fixed
@@ -0,0 +1,22 @@
+// edition:2021
+// run-rustfix
+
+#![feature(async_fn_in_trait, return_position_impl_trait_in_trait)]
+
+trait Trait {
+    async fn foo();
+
+    async fn bar() -> i32;
+
+    fn test(&self) -> impl Sized + '_;
+}
+
+struct S;
+
+impl Trait for S {fn test(&self) -> impl Sized + '_ { todo!() }
+async fn bar() -> i32 { todo!() }
+async fn foo() { todo!() }
+}
+//~^ ERROR not all trait items implemented
+
+fn main() {}
diff --git a/tests/ui/impl-trait/in-trait/suggest-missing-item.rs b/tests/ui/impl-trait/in-trait/suggest-missing-item.rs
new file mode 100644
index 00000000000..f218e6cb581
--- /dev/null
+++ b/tests/ui/impl-trait/in-trait/suggest-missing-item.rs
@@ -0,0 +1,19 @@
+// edition:2021
+// run-rustfix
+
+#![feature(async_fn_in_trait, return_position_impl_trait_in_trait)]
+
+trait Trait {
+    async fn foo();
+
+    async fn bar() -> i32;
+
+    fn test(&self) -> impl Sized + '_;
+}
+
+struct S;
+
+impl Trait for S {}
+//~^ ERROR not all trait items implemented
+
+fn main() {}
diff --git a/tests/ui/impl-trait/in-trait/suggest-missing-item.stderr b/tests/ui/impl-trait/in-trait/suggest-missing-item.stderr
new file mode 100644
index 00000000000..d96641fe163
--- /dev/null
+++ b/tests/ui/impl-trait/in-trait/suggest-missing-item.stderr
@@ -0,0 +1,18 @@
+error[E0046]: not all trait items implemented, missing: `foo`, `bar`, `test`
+  --> $DIR/suggest-missing-item.rs:16:1
+   |
+LL |     async fn foo();
+   |     --------------- `foo` from trait
+LL |
+LL |     async fn bar() -> i32;
+   |     ---------------------- `bar` from trait
+LL |
+LL |     fn test(&self) -> impl Sized + '_;
+   |     ---------------------------------- `test` from trait
+...
+LL | impl Trait for S {}
+   | ^^^^^^^^^^^^^^^^ missing `foo`, `bar`, `test` in implementation
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0046`.
diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/match-non-const-eq.gated.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/match-non-const-eq.gated.stderr
new file mode 100644
index 00000000000..bd0dd126c5e
--- /dev/null
+++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/match-non-const-eq.gated.stderr
@@ -0,0 +1,26 @@
+error[E0277]: can't compare `str` with `str` in const contexts
+  --> $DIR/match-non-const-eq.rs:6:9
+   |
+LL |         "a" => (),
+   |         ^^^ no implementation for `str == str`
+   |
+   = help: the trait `~const PartialEq` is not implemented for `str`
+note: the trait `PartialEq` is implemented for `str`, but that implementation is not `const`
+  --> $DIR/match-non-const-eq.rs:6:9
+   |
+LL |         "a" => (),
+   |         ^^^
+
+error[E0015]: cannot match on `str` in constant functions
+  --> $DIR/match-non-const-eq.rs:6:9
+   |
+LL |         "a" => (),
+   |         ^^^
+   |
+   = note: `str` cannot be compared in compile-time, and therefore cannot be used in `match`es
+   = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0015, E0277.
+For more information about an error, try `rustc --explain E0015`.
diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/match-non-const-eq.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/match-non-const-eq.rs
new file mode 100644
index 00000000000..0d04101a383
--- /dev/null
+++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/match-non-const-eq.rs
@@ -0,0 +1,12 @@
+// revisions: stock gated
+#![cfg_attr(gated, feature(const_trait_impl))]
+
+const fn foo(input: &'static str) {
+    match input {
+        "a" => (), //[gated]~ ERROR can't compare `str` with `str` in const contexts
+        //~^ ERROR cannot match on `str` in constant functions
+        _ => (),
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/match-non-const-eq.stock.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/match-non-const-eq.stock.stderr
new file mode 100644
index 00000000000..dcb9b49ea04
--- /dev/null
+++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/match-non-const-eq.stock.stderr
@@ -0,0 +1,13 @@
+error[E0015]: cannot match on `str` in constant functions
+  --> $DIR/match-non-const-eq.rs:6:9
+   |
+LL |         "a" => (),
+   |         ^^^
+   |
+   = note: `str` cannot be compared in compile-time, and therefore cannot be used in `match`es
+   = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0015`.
diff --git a/tests/ui/test-attrs/test-function-signature.rs b/tests/ui/test-attrs/test-function-signature.rs
new file mode 100644
index 00000000000..9e86e9209e3
--- /dev/null
+++ b/tests/ui/test-attrs/test-function-signature.rs
@@ -0,0 +1,31 @@
+// compile-flags: --test
+
+#[test]
+fn foo() -> Result<(), ()> {
+    Ok(())
+}
+
+#[test]
+fn bar() -> i32 { //~ ERROR the trait bound `i32: Termination` is not satisfied
+    0
+}
+
+#[test]
+fn baz(val: i32) {} //~ ERROR functions used as tests can not have any arguments
+
+#[test]
+fn lifetime_generic<'a>() -> Result<(), &'a str> {
+    Err("coerce me to any lifetime")
+}
+
+#[test]
+fn type_generic<T>() {} //~ ERROR functions used as tests can not have any non-lifetime generic parameters
+
+#[test]
+fn const_generic<const N: usize>() {} //~ ERROR functions used as tests can not have any non-lifetime generic parameters
+
+// Regression test for <https://github.com/rust-lang/rust/issues/112360>. This used to ICE.
+fn nested() {
+    #[test]
+    fn foo(arg: ()) {} //~ ERROR functions used as tests can not have any arguments
+}
diff --git a/tests/ui/test-attrs/test-function-signature.stderr b/tests/ui/test-attrs/test-function-signature.stderr
new file mode 100644
index 00000000000..abdb30dc931
--- /dev/null
+++ b/tests/ui/test-attrs/test-function-signature.stderr
@@ -0,0 +1,39 @@
+error: functions used as tests can not have any arguments
+  --> $DIR/test-function-signature.rs:14:1
+   |
+LL | fn baz(val: i32) {}
+   | ^^^^^^^^^^^^^^^^^^^
+
+error: functions used as tests can not have any non-lifetime generic parameters
+  --> $DIR/test-function-signature.rs:22:1
+   |
+LL | fn type_generic<T>() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^
+
+error: functions used as tests can not have any non-lifetime generic parameters
+  --> $DIR/test-function-signature.rs:25:1
+   |
+LL | fn const_generic<const N: usize>() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: functions used as tests can not have any arguments
+  --> $DIR/test-function-signature.rs:30:5
+   |
+LL |     fn foo(arg: ()) {}
+   |     ^^^^^^^^^^^^^^^^^^
+
+error[E0277]: the trait bound `i32: Termination` is not satisfied
+  --> $DIR/test-function-signature.rs:9:13
+   |
+LL | #[test]
+   | ------- in this procedural macro expansion
+LL | fn bar() -> i32 {
+   |             ^^^ the trait `Termination` is not implemented for `i32`
+   |
+note: required by a bound in `assert_test_result`
+  --> $SRC_DIR/test/src/lib.rs:LL:COL
+   = note: this error originates in the attribute macro `test` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/traits/non_lifetime_binders/foreach-partial-eq.rs b/tests/ui/traits/non_lifetime_binders/foreach-partial-eq.rs
new file mode 100644
index 00000000000..96a7424f0dc
--- /dev/null
+++ b/tests/ui/traits/non_lifetime_binders/foreach-partial-eq.rs
@@ -0,0 +1,12 @@
+#![feature(non_lifetime_binders)]
+//~^ WARN the feature `non_lifetime_binders` is incomplete
+
+fn auto_trait()
+where
+    for<T> T: PartialEq + PartialOrd,
+{}
+
+fn main() {
+    auto_trait();
+    //~^ ERROR can't compare `T` with `T`
+}
diff --git a/tests/ui/traits/non_lifetime_binders/foreach-partial-eq.stderr b/tests/ui/traits/non_lifetime_binders/foreach-partial-eq.stderr
new file mode 100644
index 00000000000..da09343fb27
--- /dev/null
+++ b/tests/ui/traits/non_lifetime_binders/foreach-partial-eq.stderr
@@ -0,0 +1,28 @@
+warning: the feature `non_lifetime_binders` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/foreach-partial-eq.rs:1:12
+   |
+LL | #![feature(non_lifetime_binders)]
+   |            ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #108185 <https://github.com/rust-lang/rust/issues/108185> for more information
+   = note: `#[warn(incomplete_features)]` on by default
+
+error[E0277]: can't compare `T` with `T`
+  --> $DIR/foreach-partial-eq.rs:10:5
+   |
+LL |     auto_trait();
+   |     ^^^^^^^^^^ no implementation for `T < T` and `T > T`
+   |
+   = help: the trait `PartialOrd` is not implemented for `T`
+note: required by a bound in `auto_trait`
+  --> $DIR/foreach-partial-eq.rs:6:27
+   |
+LL | fn auto_trait()
+   |    ---------- required by a bound in this function
+LL | where
+LL |     for<T> T: PartialEq + PartialOrd,
+   |                           ^^^^^^^^^^ required by this bound in `auto_trait`
+
+error: aborting due to previous error; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/type-alias-impl-trait/debug-ty-with-weak.rs b/tests/ui/type-alias-impl-trait/debug-ty-with-weak.rs
new file mode 100644
index 00000000000..44158349fdd
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/debug-ty-with-weak.rs
@@ -0,0 +1,12 @@
+// compile-flags: --crate-type=lib -Cdebuginfo=2
+// build-pass
+
+#![feature(type_alias_impl_trait)]
+
+type Debuggable = impl core::fmt::Debug;
+
+static mut TEST: Option<Debuggable> = None;
+
+fn foo() -> Debuggable {
+    0u32
+}
diff --git a/tests/ui/type-alias-impl-trait/issue-76202-trait-impl-for-tait.rs b/tests/ui/type-alias-impl-trait/issue-76202-trait-impl-for-tait.rs
index b97e444c6d0..386b77d4d16 100644
--- a/tests/ui/type-alias-impl-trait/issue-76202-trait-impl-for-tait.rs
+++ b/tests/ui/type-alias-impl-trait/issue-76202-trait-impl-for-tait.rs
@@ -1,6 +1,8 @@
 // Regression test for issue #76202
 // Tests that we don't ICE when we have a trait impl on a TAIT.
 
+// revisions: current next
+//[next] compile-flags: -Ztrait-solver=next
 // check-pass
 
 #![feature(type_alias_impl_trait)]
diff --git a/tests/ui/typeck/dont-record-adjustments-when-pointing-at-arg.rs b/tests/ui/typeck/dont-record-adjustments-when-pointing-at-arg.rs
new file mode 100644
index 00000000000..0c2d71707c9
--- /dev/null
+++ b/tests/ui/typeck/dont-record-adjustments-when-pointing-at-arg.rs
@@ -0,0 +1,29 @@
+pub trait NSWindow: Sized {
+    fn frame(self) -> () {
+        unimplemented!()
+    }
+    fn setFrame_display_(self, display: ()) {}
+}
+impl NSWindow for () {}
+
+pub struct NSRect {}
+
+use std::ops::Deref;
+struct MainThreadSafe<T = ()>(T);
+impl<T> Deref for MainThreadSafe<T> {
+    type Target = T;
+
+    fn deref(&self) -> &T {
+        unimplemented!()
+    }
+}
+
+fn main() {
+    || {
+        let ns_window = MainThreadSafe(());
+        // Don't record adjustments twice for `*ns_window`
+        (*ns_window).frame();
+        ns_window.setFrame_display_(0);
+        //~^ ERROR mismatched types
+    };
+}
diff --git a/tests/ui/typeck/dont-record-adjustments-when-pointing-at-arg.stderr b/tests/ui/typeck/dont-record-adjustments-when-pointing-at-arg.stderr
new file mode 100644
index 00000000000..02e87d701b6
--- /dev/null
+++ b/tests/ui/typeck/dont-record-adjustments-when-pointing-at-arg.stderr
@@ -0,0 +1,17 @@
+error[E0308]: mismatched types
+  --> $DIR/dont-record-adjustments-when-pointing-at-arg.rs:26:37
+   |
+LL |         ns_window.setFrame_display_(0);
+   |                   ----------------- ^ expected `()`, found integer
+   |                   |
+   |                   arguments to this method are incorrect
+   |
+note: method defined here
+  --> $DIR/dont-record-adjustments-when-pointing-at-arg.rs:5:8
+   |
+LL |     fn setFrame_display_(self, display: ()) {}
+   |        ^^^^^^^^^^^^^^^^^       -----------
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.