about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorThe Miri Conjob Bot <miri@cron.bot>2023-07-07 06:34:22 +0000
committerThe Miri Conjob Bot <miri@cron.bot>2023-07-07 06:34:22 +0000
commitb13a9fa3dd085a488c5bf911e921b273d322663b (patch)
tree0b0748dd31e4b2956a38ea0c53e1f8cfcf3f528f /compiler
parenta5286fc6bf2892391b84ae9497da0aac04497185 (diff)
parent8765f9172750111e0b7af489561fba0e2ef22007 (diff)
downloadrust-b13a9fa3dd085a488c5bf911e921b273d322663b.tar.gz
rust-b13a9fa3dd085a488c5bf911e921b273d322663b.zip
Merge from rustc
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_ast/src/ast.rs9
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs9
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs117
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mod.rs2
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_errors.rs2
-rw-r--r--compiler/rustc_borrowck/src/region_infer/mod.rs2
-rw-r--r--compiler/rustc_borrowck/src/region_infer/opaque_types.rs10
-rw-r--r--compiler/rustc_borrowck/src/type_check/free_region_relations.rs2
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs13
-rw-r--r--compiler/rustc_borrowck/src/universal_regions.rs2
-rw-r--r--compiler/rustc_codegen_cranelift/src/abi/mod.rs3
-rw-r--r--compiler/rustc_codegen_cranelift/src/base.rs5
-rw-r--r--compiler/rustc_codegen_cranelift/src/codegen_i128.rs4
-rw-r--r--compiler/rustc_codegen_cranelift/src/common.rs2
-rw-r--r--compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs2
-rw-r--r--compiler/rustc_codegen_cranelift/src/num.rs2
-rw-r--r--compiler/rustc_codegen_gcc/src/coverageinfo.rs64
-rw-r--r--compiler/rustc_codegen_gcc/src/intrinsic/mod.rs10
-rw-r--r--compiler/rustc_codegen_gcc/src/type_of.rs2
-rw-r--r--compiler/rustc_codegen_llvm/Cargo.toml1
-rw-r--r--compiler/rustc_codegen_llvm/src/abi.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/allocator.rs4
-rw-r--r--compiler/rustc_codegen_llvm/src/back/lto.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/back/write.rs16
-rw-r--r--compiler/rustc_codegen_llvm/src/base.rs6
-rw-r--r--compiler/rustc_codegen_llvm/src/builder.rs16
-rw-r--r--compiler/rustc_codegen_llvm/src/consts.rs7
-rw-r--r--compiler/rustc_codegen_llvm/src/context.rs44
-rw-r--r--compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs (renamed from compiler/rustc_codegen_ssa/src/coverageinfo/ffi.rs)0
-rw-r--r--compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs (renamed from compiler/rustc_codegen_ssa/src/coverageinfo/map.rs)1
-rw-r--r--compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs4
-rw-r--r--compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs82
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs3
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs20
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/mod.rs9
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/utils.rs4
-rw-r--r--compiler/rustc_codegen_llvm/src/intrinsic.rs36
-rw-r--r--compiler/rustc_codegen_llvm/src/lib.rs1
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm/ffi.rs13
-rw-r--r--compiler/rustc_codegen_llvm/src/type_of.rs5
-rw-r--r--compiler/rustc_codegen_llvm/src/va_arg.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/base.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/coverageinfo/mod.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/lib.rs1
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/block.rs7
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs41
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/debuginfo.rs14
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/place.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/rvalue.rs9
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/statement.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs58
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/mod.rs4
-rw-r--r--compiler/rustc_const_eval/src/const_eval/mod.rs33
-rw-r--r--compiler/rustc_const_eval/src/interpret/intrinsics.rs2
-rw-r--r--compiler/rustc_const_eval/src/interpret/operand.rs4
-rw-r--r--compiler/rustc_const_eval/src/interpret/operator.rs2
-rw-r--r--compiler/rustc_const_eval/src/interpret/place.rs6
-rw-r--r--compiler/rustc_const_eval/src/interpret/projection.rs5
-rw-r--r--compiler/rustc_const_eval/src/interpret/terminator.rs4
-rw-r--r--compiler/rustc_const_eval/src/lib.rs6
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs2
-rw-r--r--compiler/rustc_const_eval/src/transform/promote_consts.rs5
-rw-r--r--compiler/rustc_const_eval/src/transform/validate.rs23
-rw-r--r--compiler/rustc_data_structures/Cargo.toml2
-rw-r--r--compiler/rustc_driver_impl/src/lib.rs69
-rw-r--r--compiler/rustc_errors/Cargo.toml2
-rw-r--r--compiler/rustc_errors/src/emitter.rs2
-rw-r--r--compiler/rustc_errors/src/lib.rs1
-rw-r--r--compiler/rustc_errors/src/markdown/mod.rs76
-rw-r--r--compiler/rustc_errors/src/markdown/parse.rs588
-rw-r--r--compiler/rustc_errors/src/markdown/term.rs189
-rw-r--r--compiler/rustc_errors/src/markdown/tests/input.md50
-rw-r--r--compiler/rustc_errors/src/markdown/tests/output.stdout35
-rw-r--r--compiler/rustc_errors/src/markdown/tests/parse.rs312
-rw-r--r--compiler/rustc_errors/src/markdown/tests/term.rs90
-rw-r--r--compiler/rustc_feature/src/active.rs2
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs6
-rw-r--r--compiler/rustc_hir_analysis/messages.ftl5
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/bounds.rs30
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/errors.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/mod.rs495
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/object_safety.rs408
-rw-r--r--compiler/rustc_hir_analysis/src/autoderef.rs7
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs23
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_impl_item.rs36
-rw-r--r--compiler/rustc_hir_analysis/src/check/intrinsic.rs172
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs9
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/builtin.rs8
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/orphan.rs6
-rw-r--r--compiler/rustc_hir_analysis/src/collect.rs19
-rw-r--r--compiler/rustc_hir_analysis/src/collect/generics_of.rs30
-rw-r--r--compiler/rustc_hir_analysis/src/collect/item_bounds.rs10
-rw-r--r--compiler/rustc_hir_analysis/src/collect/predicates_of.rs29
-rw-r--r--compiler/rustc_hir_analysis/src/collect/type_of.rs56
-rw-r--r--compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs11
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs10
-rw-r--r--compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs25
-rw-r--r--compiler/rustc_hir_analysis/src/lib.rs32
-rw-r--r--compiler/rustc_hir_analysis/src/outlives/explicit.rs3
-rw-r--r--compiler/rustc_hir_typeck/src/_match.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/callee.rs86
-rw-r--r--compiler/rustc_hir_typeck/src/cast.rs16
-rw-r--r--compiler/rustc_hir_typeck/src/check.rs12
-rw-r--r--compiler/rustc_hir_typeck/src/closure.rs13
-rw-r--r--compiler/rustc_hir_typeck/src/coercion.rs37
-rw-r--r--compiler/rustc_hir_typeck/src/demand.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs105
-rw-r--r--compiler/rustc_hir_typeck/src/fallback.rs6
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs53
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs50
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs10
-rw-r--r--compiler/rustc_hir_typeck/src/generator_interior/mod.rs6
-rw-r--r--compiler/rustc_hir_typeck/src/lib.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/mem_categorization.rs10
-rw-r--r--compiler/rustc_hir_typeck/src/method/confirm.rs20
-rw-r--r--compiler/rustc_hir_typeck/src/method/mod.rs10
-rw-r--r--compiler/rustc_hir_typeck/src/method/probe.rs13
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs17
-rw-r--r--compiler/rustc_hir_typeck/src/op.rs8
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs66
-rw-r--r--compiler/rustc_hir_typeck/src/place_op.rs17
-rw-r--r--compiler/rustc_hir_typeck/src/upvar.rs14
-rw-r--r--compiler/rustc_hir_typeck/src/writeback.rs7
-rw-r--r--compiler/rustc_infer/src/infer/at.rs1
-rw-r--r--compiler/rustc_infer/src/infer/canonical/canonicalizer.rs11
-rw-r--r--compiler/rustc_infer/src/infer/canonical/mod.rs6
-rw-r--r--compiler/rustc_infer/src/infer/canonical/query_response.rs2
-rw-r--r--compiler/rustc_infer/src/infer/combine.rs10
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs2
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs6
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs2
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs4
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/note.rs4
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs4
-rw-r--r--compiler/rustc_infer/src/infer/freshen.rs8
-rw-r--r--compiler/rustc_infer/src/infer/generalize.rs10
-rw-r--r--compiler/rustc_infer/src/infer/higher_ranked/mod.rs13
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs64
-rw-r--r--compiler/rustc_infer/src/infer/outlives/mod.rs3
-rw-r--r--compiler/rustc_infer/src/infer/projection.rs37
-rw-r--r--compiler/rustc_infer/src/infer/sub.rs2
-rw-r--r--compiler/rustc_infer/src/infer/type_variable.rs2
-rw-r--r--compiler/rustc_infer/src/traits/error_reporting/mod.rs8
-rw-r--r--compiler/rustc_infer/src/traits/util.rs7
-rw-r--r--compiler/rustc_interface/src/tests.rs2
-rw-r--r--compiler/rustc_lexer/src/lib.rs7
-rw-r--r--compiler/rustc_lint/src/builtin.rs9
-rw-r--r--compiler/rustc_lint/src/context.rs2
-rw-r--r--compiler/rustc_lint/src/nonstandard_style.rs4
-rw-r--r--compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs5
-rw-r--r--compiler/rustc_lint/src/types.rs162
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs27
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp26
-rw-r--r--compiler/rustc_metadata/messages.ftl3
-rw-r--r--compiler/rustc_metadata/src/errors.rs14
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs8
-rw-r--r--compiler/rustc_middle/Cargo.toml1
-rw-r--r--compiler/rustc_middle/src/infer/canonical.rs16
-rw-r--r--compiler/rustc_middle/src/infer/unify_key.rs6
-rw-r--r--compiler/rustc_middle/src/mir/mod.rs60
-rw-r--r--compiler/rustc_middle/src/mir/query.rs4
-rw-r--r--compiler/rustc_middle/src/mir/tcx.rs24
-rw-r--r--compiler/rustc_middle/src/query/erase.rs1
-rw-r--r--compiler/rustc_middle/src/query/keys.rs9
-rw-r--r--compiler/rustc_middle/src/query/mod.rs27
-rw-r--r--compiler/rustc_middle/src/traits/chalk.rs396
-rw-r--r--compiler/rustc_middle/src/traits/mod.rs9
-rw-r--r--compiler/rustc_middle/src/ty/_match.rs2
-rw-r--r--compiler/rustc_middle/src/ty/abstract_const.rs2
-rw-r--r--compiler/rustc_middle/src/ty/adjustment.rs2
-rw-r--r--compiler/rustc_middle/src/ty/codec.rs2
-rw-r--r--compiler/rustc_middle/src/ty/consts.rs283
-rw-r--r--compiler/rustc_middle/src/ty/consts/kind.rs204
-rw-r--r--compiler/rustc_middle/src/ty/context.rs462
-rw-r--r--compiler/rustc_middle/src/ty/diagnostics.rs2
-rw-r--r--compiler/rustc_middle/src/ty/flags.rs3
-rw-r--r--compiler/rustc_middle/src/ty/fold.rs16
-rw-r--r--compiler/rustc_middle/src/ty/generics.rs15
-rw-r--r--compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs2
-rw-r--r--compiler/rustc_middle/src/ty/inhabitedness/mod.rs2
-rw-r--r--compiler/rustc_middle/src/ty/instance.rs10
-rw-r--r--compiler/rustc_middle/src/ty/layout.rs49
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs25
-rw-r--r--compiler/rustc_middle/src/ty/opaque_types.rs10
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs51
-rw-r--r--compiler/rustc_middle/src/ty/relate.rs70
-rw-r--r--compiler/rustc_middle/src/ty/structural_impls.rs51
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs398
-rw-r--r--compiler/rustc_middle/src/ty/subst.rs5
-rw-r--r--compiler/rustc_middle/src/ty/util.rs24
-rw-r--r--compiler/rustc_middle/src/values.rs4
-rw-r--r--compiler/rustc_mir_build/src/build/expr/as_constant.rs4
-rw-r--r--compiler/rustc_mir_build/src/build/expr/as_place.rs2
-rw-r--r--compiler/rustc_mir_build/src/build/expr/as_rvalue.rs6
-rw-r--r--compiler/rustc_mir_build/src/build/matches/mod.rs5
-rw-r--r--compiler/rustc_mir_build/src/build/matches/test.rs10
-rw-r--r--compiler/rustc_mir_build/src/build/mod.rs4
-rw-r--r--compiler/rustc_mir_build/src/build/scope.rs3
-rw-r--r--compiler/rustc_mir_build/src/thir/constant.rs2
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/expr.rs18
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/mod.rs4
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs12
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/mod.rs10
-rw-r--r--compiler/rustc_mir_dataflow/src/elaborate_drops.rs13
-rw-r--r--compiler/rustc_mir_transform/src/check_alignment.rs2
-rw-r--r--compiler/rustc_mir_transform/src/elaborate_box_derefs.rs2
-rw-r--r--compiler/rustc_mir_transform/src/generator.rs24
-rw-r--r--compiler/rustc_mir_transform/src/large_enums.rs16
-rw-r--r--compiler/rustc_mir_transform/src/shim.rs23
-rw-r--r--compiler/rustc_monomorphize/src/partitioning.rs8
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs16
-rw-r--r--compiler/rustc_parse/src/parser/item.rs6
-rw-r--r--compiler/rustc_privacy/src/lib.rs1
-rw-r--r--compiler/rustc_query_system/src/query/plumbing.rs16
-rw-r--r--compiler/rustc_query_system/src/values.rs7
-rw-r--r--compiler/rustc_resolve/src/build_reduced_graph.rs33
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs59
-rw-r--r--compiler/rustc_resolve/src/effective_visibilities.rs18
-rw-r--r--compiler/rustc_resolve/src/ident.rs57
-rw-r--r--compiler/rustc_resolve/src/imports.rs110
-rw-r--r--compiler/rustc_resolve/src/late.rs15
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs60
-rw-r--r--compiler/rustc_resolve/src/lib.rs192
-rw-r--r--compiler/rustc_resolve/src/macros.rs4
-rw-r--r--compiler/rustc_serialize/Cargo.toml2
-rw-r--r--compiler/rustc_session/src/config.rs12
-rw-r--r--compiler/rustc_session/src/options.rs26
-rw-r--r--compiler/rustc_smir/Cargo.toml1
-rw-r--r--compiler/rustc_smir/src/lib.rs3
-rw-r--r--compiler/rustc_smir/src/stable_mir/mod.rs28
-rw-r--r--compiler/rustc_span/Cargo.toml2
-rw-r--r--compiler/rustc_span/src/symbol.rs3
-rw-r--r--compiler/rustc_symbol_mangling/src/legacy.rs2
-rw-r--r--compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs87
-rw-r--r--compiler/rustc_symbol_mangling/src/v0.rs5
-rw-r--r--compiler/rustc_target/src/abi/call/x86_64.rs10
-rw-r--r--compiler/rustc_target/src/spec/mod.rs1
-rw-r--r--compiler/rustc_target/src/spec/riscv64gc_unknown_netbsd.rs19
-rw-r--r--compiler/rustc_trait_selection/src/solve/alias_relate.rs68
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/mod.rs68
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs6
-rw-r--r--compiler/rustc_trait_selection/src/solve/canonicalize.rs8
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt.rs232
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs8
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs22
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs307
-rw-r--r--compiler/rustc_trait_selection/src/solve/fulfill.rs1
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect.rs96
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect/dump.rs5
-rw-r--r--compiler/rustc_trait_selection/src/solve/mod.rs43
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalize.rs222
-rw-r--r--compiler/rustc_trait_selection/src/solve/project_goals.rs190
-rw-r--r--compiler/rustc_trait_selection/src/solve/search_graph/mod.rs2
-rw-r--r--compiler/rustc_trait_selection/src/solve/trait_goals.rs304
-rw-r--r--compiler/rustc_trait_selection/src/traits/auto_trait.rs5
-rw-r--r--compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs149
-rw-r--r--compiler/rustc_trait_selection/src/traits/coherence.rs28
-rw-r--r--compiler/rustc_trait_selection/src/traits/engine.rs37
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs129
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs29
-rw-r--r--compiler/rustc_trait_selection/src/traits/fulfill.rs8
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs12
-rw-r--r--compiler/rustc_trait_selection/src/traits/object_safety.rs24
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs113
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs1
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/normalize.rs14
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs30
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs40
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs93
-rw-r--r--compiler/rustc_trait_selection/src/traits/util.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/wf.rs18
-rw-r--r--compiler/rustc_traits/Cargo.toml3
-rw-r--r--compiler/rustc_traits/src/chalk/db.rs796
-rw-r--r--compiler/rustc_traits/src/chalk/lowering.rs1234
-rw-r--r--compiler/rustc_traits/src/chalk/mod.rs169
-rw-r--r--compiler/rustc_traits/src/lib.rs5
-rw-r--r--compiler/rustc_traits/src/normalize_erasing_regions.rs3
-rw-r--r--compiler/rustc_ty_utils/Cargo.toml1
-rw-r--r--compiler/rustc_ty_utils/src/abi.rs17
-rw-r--r--compiler/rustc_ty_utils/src/assoc.rs7
-rw-r--r--compiler/rustc_ty_utils/src/consts.rs30
-rw-r--r--compiler/rustc_ty_utils/src/implied_bounds.rs76
-rw-r--r--compiler/rustc_ty_utils/src/layout.rs4
-rw-r--r--compiler/rustc_ty_utils/src/ty.rs141
-rw-r--r--compiler/rustc_type_ir/src/lib.rs10
-rw-r--r--compiler/rustc_type_ir/src/structural_impls.rs20
-rw-r--r--compiler/rustc_type_ir/src/sty.rs218
291 files changed, 6645 insertions, 6308 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index a398fd80119..a7198fbf887 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -2652,6 +2652,15 @@ pub struct NormalAttr {
     pub tokens: Option<LazyAttrTokenStream>,
 }
 
+impl NormalAttr {
+    pub fn from_ident(ident: Ident) -> Self {
+        Self {
+            item: AttrItem { path: Path::from_ident(ident), args: AttrArgs::Empty, tokens: None },
+            tokens: None,
+        }
+    }
+}
+
 #[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
 pub struct AttrItem {
     pub path: Path,
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 29972dd76eb..dcaaaafedbe 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -673,14 +673,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
             self.lower_attrs(
                 inner_hir_id,
                 &[Attribute {
-                    kind: AttrKind::Normal(ptr::P(NormalAttr {
-                        item: AttrItem {
-                            path: Path::from_ident(Ident::new(sym::track_caller, span)),
-                            args: AttrArgs::Empty,
-                            tokens: None,
-                        },
-                        tokens: None,
-                    })),
+                    kind: AttrKind::Normal(ptr::P(NormalAttr::from_ident(Ident::new(sym::track_caller, span)))),
                     id: self.tcx.sess.parse_sess.attr_id_generator.mk_attr_id(),
                     style: AttrStyle::Outer,
                     span: unstable_span,
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index 08ee3761bac..ab68436c093 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -3,6 +3,7 @@ use super::ResolverAstLoweringExt;
 use super::{AstOwner, ImplTraitContext, ImplTraitPosition};
 use super::{FnDeclKind, LoweringContext, ParamMode};
 
+use hir::definitions::DefPathData;
 use rustc_ast::ptr::P;
 use rustc_ast::visit::AssocCtxt;
 use rustc_ast::*;
@@ -257,10 +258,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     );
 
                     let itctx = ImplTraitContext::Universal;
-                    let (generics, decl) = this.lower_generics(generics, id, &itctx, |this| {
-                        let ret_id = asyncness.opt_return_id();
-                        this.lower_fn_decl(&decl, id, *fn_sig_span, FnDeclKind::Fn, ret_id)
-                    });
+                    let (generics, decl) =
+                        this.lower_generics(generics, header.constness, id, &itctx, |this| {
+                            let ret_id = asyncness.opt_return_id();
+                            this.lower_fn_decl(&decl, id, *fn_sig_span, FnDeclKind::Fn, ret_id)
+                        });
                     let sig = hir::FnSig {
                         decl,
                         header: this.lower_fn_header(*header),
@@ -295,6 +297,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 add_ty_alias_where_clause(&mut generics, *where_clauses, true);
                 let (generics, ty) = self.lower_generics(
                     &generics,
+                    Const::No,
                     id,
                     &ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
                     |this| match ty {
@@ -316,6 +319,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
             ItemKind::Enum(enum_definition, generics) => {
                 let (generics, variants) = self.lower_generics(
                     generics,
+                    Const::No,
                     id,
                     &ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
                     |this| {
@@ -329,6 +333,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
             ItemKind::Struct(struct_def, generics) => {
                 let (generics, struct_def) = self.lower_generics(
                     generics,
+                    Const::No,
                     id,
                     &ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
                     |this| this.lower_variant_data(hir_id, struct_def),
@@ -338,6 +343,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
             ItemKind::Union(vdata, generics) => {
                 let (generics, vdata) = self.lower_generics(
                     generics,
+                    Const::No,
                     id,
                     &ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
                     |this| this.lower_variant_data(hir_id, vdata),
@@ -369,7 +375,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 // parent lifetime.
                 let itctx = ImplTraitContext::Universal;
                 let (generics, (trait_ref, lowered_ty)) =
-                    self.lower_generics(ast_generics, id, &itctx, |this| {
+                    self.lower_generics(ast_generics, *constness, id, &itctx, |this| {
                         let trait_ref = trait_ref.as_ref().map(|trait_ref| {
                             this.lower_trait_ref(
                                 trait_ref,
@@ -410,8 +416,15 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 }))
             }
             ItemKind::Trait(box Trait { is_auto, unsafety, generics, bounds, items }) => {
+                // FIXME(const_trait_impl, effects, fee1-dead) this should be simplified if possible
+                let constness = attrs
+                    .unwrap_or(&[])
+                    .iter()
+                    .find(|x| x.has_name(sym::const_trait))
+                    .map_or(Const::No, |x| Const::Yes(x.span));
                 let (generics, (unsafety, items, bounds)) = self.lower_generics(
                     generics,
+                    constness,
                     id,
                     &ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
                     |this| {
@@ -431,6 +444,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
             ItemKind::TraitAlias(generics, bounds) => {
                 let (generics, bounds) = self.lower_generics(
                     generics,
+                    Const::No,
                     id,
                     &ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
                     |this| {
@@ -593,7 +607,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     let fdec = &sig.decl;
                     let itctx = ImplTraitContext::Universal;
                     let (generics, (fn_dec, fn_args)) =
-                        self.lower_generics(generics, i.id, &itctx, |this| {
+                        self.lower_generics(generics, Const::No, i.id, &itctx, |this| {
                             (
                                 // Disallow `impl Trait` in foreign items.
                                 this.lower_fn_decl(
@@ -745,6 +759,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 add_ty_alias_where_clause(&mut generics, *where_clauses, false);
                 let (generics, kind) = self.lower_generics(
                     &generics,
+                    Const::No,
                     i.id,
                     &ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
                     |this| {
@@ -843,6 +858,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 add_ty_alias_where_clause(&mut generics, *where_clauses, false);
                 self.lower_generics(
                     &generics,
+                    Const::No,
                     i.id,
                     &ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
                     |this| match ty {
@@ -1201,9 +1217,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
     ) -> (&'hir hir::Generics<'hir>, hir::FnSig<'hir>) {
         let header = self.lower_fn_header(sig.header);
         let itctx = ImplTraitContext::Universal;
-        let (generics, decl) = self.lower_generics(generics, id, &itctx, |this| {
-            this.lower_fn_decl(&sig.decl, id, sig.span, kind, is_async)
-        });
+        let (generics, decl) =
+            self.lower_generics(generics, sig.header.constness, id, &itctx, |this| {
+                this.lower_fn_decl(&sig.decl, id, sig.span, kind, is_async)
+            });
         (generics, hir::FnSig { header, decl, span: self.lower_span(sig.span) })
     }
 
@@ -1275,6 +1292,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
     fn lower_generics<T>(
         &mut self,
         generics: &Generics,
+        constness: Const,
         parent_node_id: NodeId,
         itctx: &ImplTraitContext,
         f: impl FnOnce(&mut Self) -> T,
@@ -1372,6 +1390,87 @@ impl<'hir> LoweringContext<'_, 'hir> {
         let impl_trait_bounds = std::mem::take(&mut self.impl_trait_bounds);
         predicates.extend(impl_trait_bounds.into_iter());
 
+        // Desugar `~const` bound in generics into an additional `const host: bool` param
+        // if the effects feature is enabled.
+        if let Const::Yes(span) = constness && self.tcx.features().effects
+            // Do not add host param if it already has it (manually specified)
+            && !params.iter().any(|x| {
+                self.attrs.get(&x.hir_id.local_id).map_or(false, |attrs| {
+                    attrs.iter().any(|x| x.has_name(sym::rustc_host))
+                })
+            })
+        {
+            let param_node_id = self.next_node_id();
+            let const_node_id = self.next_node_id();
+            let def_id = self.create_def(self.local_def_id(parent_node_id), param_node_id, DefPathData::TypeNs(sym::host), span);
+            let anon_const: LocalDefId = self.create_def(def_id, const_node_id, DefPathData::AnonConst, span);
+
+            let hir_id = self.next_id();
+            let const_id = self.next_id();
+            let const_expr_id = self.next_id();
+            let bool_id = self.next_id();
+
+            self.children.push((def_id, hir::MaybeOwner::NonOwner(hir_id)));
+            self.children.push((anon_const, hir::MaybeOwner::NonOwner(const_id)));
+
+            let attr_id = self.tcx.sess.parse_sess.attr_id_generator.mk_attr_id();
+
+            let attrs = self.arena.alloc_from_iter([
+                Attribute {
+                    kind: AttrKind::Normal(P(NormalAttr::from_ident(Ident::new(sym::rustc_host, span)))),
+                    span,
+                    id: attr_id,
+                    style: AttrStyle::Outer,
+                },
+            ]);
+            self.attrs.insert(hir_id.local_id, attrs);
+
+            let const_body = self.lower_body(|this| {
+                (
+                    &[],
+                    hir::Expr {
+                        hir_id: const_expr_id,
+                        kind: hir::ExprKind::Lit(
+                            this.arena.alloc(hir::Lit { node: LitKind::Bool(true), span }),
+                        ),
+                        span,
+                    },
+                )
+            });
+
+            let param = hir::GenericParam {
+                def_id,
+                hir_id,
+                name: hir::ParamName::Plain(Ident { name: sym::host, span }),
+                span,
+                kind: hir::GenericParamKind::Const {
+                    ty: self.arena.alloc(self.ty(
+                        span,
+                        hir::TyKind::Path(hir::QPath::Resolved(
+                            None,
+                            self.arena.alloc(hir::Path {
+                                res: Res::PrimTy(hir::PrimTy::Bool),
+                                span,
+                                segments: self.arena.alloc_from_iter([hir::PathSegment {
+                                    ident: Ident { name: sym::bool, span },
+                                    hir_id: bool_id,
+                                    res: Res::PrimTy(hir::PrimTy::Bool),
+                                    args: None,
+                                    infer_args: false,
+                                }]),
+                            }),
+                        )),
+                    )),
+                    default: Some(hir::AnonConst { def_id: anon_const, hir_id: const_id, body: const_body }),
+                },
+                colon_span: None,
+                pure_wrt_drop: false,
+                source: hir::GenericParamSource::Generics,
+            };
+
+            params.push(param);
+        }
+
         let lowered_generics = self.arena.alloc(hir::Generics {
             params: self.arena.alloc_from_iter(params),
             predicates: self.arena.alloc_from_iter(predicates),
diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs
index 42e50fd0fad..d292611e6a2 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mod.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs
@@ -1050,7 +1050,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                             Some(def_id) => type_known_to_meet_bound_modulo_regions(
                                 &self.infcx,
                                 self.param_env,
-                                tcx.mk_imm_ref(tcx.lifetimes.re_erased, ty),
+                                Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, ty),
                                 def_id,
                             ),
                             _ => false,
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
index 8ec872e2057..617c85174cb 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
@@ -508,7 +508,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                         let generic_arg = substs[param_index as usize];
                         let identity_substs =
                             InternalSubsts::identity_for_item(self.infcx.tcx, adt.did());
-                        let base_ty = self.infcx.tcx.mk_adt(*adt, identity_substs);
+                        let base_ty = Ty::new_adt(self.infcx.tcx, *adt, identity_substs);
                         let base_generic_arg = identity_substs[param_index as usize];
                         let adt_desc = adt.descr();
 
diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs
index 2a0cb49672b..e45d3a2c882 100644
--- a/compiler/rustc_borrowck/src/region_infer/mod.rs
+++ b/compiler/rustc_borrowck/src/region_infer/mod.rs
@@ -1139,7 +1139,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                             _ => arg.fold_with(self),
                         }
                     });
-                tcx.mk_opaque(def_id, tcx.mk_substs_from_iter(substs))
+                Ty::new_opaque(tcx, def_id, tcx.mk_substs_from_iter(substs))
             }
         }
 
diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
index 4a872eb251c..1a227f2d110 100644
--- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
+++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
@@ -158,7 +158,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                         )
                         .emit()
                     });
-                    prev.ty = infcx.tcx.ty_error(guar);
+                    prev.ty = Ty::new_error(infcx.tcx, guar);
                 }
                 // Pick a better span if there is one.
                 // FIXME(oli-obk): collect multiple spans for better diagnostics down the road.
@@ -248,13 +248,13 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
         instantiated_ty: OpaqueHiddenType<'tcx>,
     ) -> Ty<'tcx> {
         if let Some(e) = self.tainted_by_errors() {
-            return self.tcx.ty_error(e);
+            return Ty::new_error(self.tcx, e);
         }
 
         if let Err(guar) =
             check_opaque_type_parameter_valid(self.tcx, opaque_type_key, instantiated_ty.span)
         {
-            return self.tcx.ty_error(guar);
+            return Ty::new_error(self.tcx, guar);
         }
 
         let definition_ty = instantiated_ty
@@ -271,7 +271,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
             definition_ty,
         ) {
             Ok(hidden_ty) => hidden_ty,
-            Err(guar) => self.tcx.ty_error(guar),
+            Err(guar) => Ty::new_error(self.tcx, guar),
         }
     }
 }
@@ -313,7 +313,7 @@ fn check_opaque_type_well_formed<'tcx>(
 
     // Require that the hidden type actually fulfills all the bounds of the opaque type, even without
     // the bounds that the function supplies.
-    let opaque_ty = tcx.mk_opaque(def_id.to_def_id(), identity_substs);
+    let opaque_ty = Ty::new_opaque(tcx, def_id.to_def_id(), identity_substs);
     ocx.eq(&ObligationCause::misc(definition_span, def_id), param_env, opaque_ty, definition_ty)
         .map_err(|err| {
             infcx
diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
index c8ec1257d37..f22851d76b3 100644
--- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
+++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
@@ -245,7 +245,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
                 .and(type_op::normalize::Normalize::new(ty))
                 .fully_perform(self.infcx, span)
                 .unwrap_or_else(|guar| TypeOpOutput {
-                    output: self.infcx.tcx.ty_error(guar),
+                    output: Ty::new_error(self.infcx.tcx, guar),
                     constraints: None,
                     error_info: None,
                 });
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 23c3b7b7016..7ed7e125b3c 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -237,7 +237,7 @@ pub(crate) fn type_check<'mir, 'tcx>(
                     decl.hidden_type.span,
                     format!("could not resolve {:#?}", hidden_type.ty.kind()),
                 );
-                hidden_type.ty = infcx.tcx.ty_error(reported);
+                hidden_type.ty = Ty::new_error(infcx.tcx, reported);
             }
 
             (opaque_type_key, hidden_type)
@@ -520,7 +520,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
         for elem in place.projection.iter() {
             if place_ty.variant_index.is_none() {
                 if let Err(guar) = place_ty.ty.error_reported() {
-                    return PlaceTy::from_ty(self.tcx().ty_error(guar));
+                    return PlaceTy::from_ty(Ty::new_error(self.tcx(), guar));
                 }
             }
             place_ty = self.sanitize_projection(place_ty, elem, place, location, context);
@@ -656,7 +656,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
                 PlaceTy::from_ty(match base_ty.kind() {
                     ty::Array(inner, _) => {
                         assert!(!from_end, "array subslices should not use from_end");
-                        tcx.mk_array(*inner, to - from)
+                        Ty::new_array(tcx, *inner, to - from)
                     }
                     ty::Slice(..) => {
                         assert!(from_end, "slice subslices should use from_end");
@@ -749,7 +749,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
     }
 
     fn error(&mut self) -> Ty<'tcx> {
-        self.tcx().ty_error_misc()
+        Ty::new_misc_error(self.tcx())
     }
 
     fn get_ambient_variance(&self, context: PlaceContext) -> ty::Variance {
@@ -1918,7 +1918,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                         // and hence may contain unnormalized results.
                         let fn_sig = self.normalize(fn_sig, location);
 
-                        let ty_fn_ptr_from = tcx.mk_fn_ptr(fn_sig);
+                        let ty_fn_ptr_from = Ty::new_fn_ptr(tcx, fn_sig);
 
                         if let Err(terr) = self.eq_types(
                             *ty,
@@ -1942,7 +1942,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                             ty::Closure(_, substs) => substs.as_closure().sig(),
                             _ => bug!(),
                         };
-                        let ty_fn_ptr_from = tcx.mk_fn_ptr(tcx.signature_unclosure(sig, *unsafety));
+                        let ty_fn_ptr_from =
+                            Ty::new_fn_ptr(tcx, tcx.signature_unclosure(sig, *unsafety));
 
                         if let Err(terr) = self.eq_types(
                             *ty,
diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs
index c871703429a..7821b82bf2b 100644
--- a/compiler/rustc_borrowck/src/universal_regions.rs
+++ b/compiler/rustc_borrowck/src/universal_regions.rs
@@ -685,7 +685,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
                 assert_eq!(self.mir_def.to_def_id(), def_id);
                 let resume_ty = substs.as_generator().resume_ty();
                 let output = substs.as_generator().return_ty();
-                let generator_ty = tcx.mk_generator(def_id, substs, movability);
+                let generator_ty = Ty::new_generator(tcx, def_id, substs, movability);
                 let inputs_and_output =
                     self.infcx.tcx.mk_type_list(&[generator_ty, resume_ty, output]);
                 ty::Binder::dummy(inputs_and_output)
diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs
index 84e09cf0abe..199fa6861cf 100644
--- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs
+++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs
@@ -665,7 +665,8 @@ pub(crate) fn codegen_drop<'tcx>(
 
                 let arg_value = drop_place.place_ref(
                     fx,
-                    fx.layout_of(fx.tcx.mk_ref(
+                    fx.layout_of(Ty::new_ref(
+                        fx.tcx,
                         fx.tcx.lifetimes.re_erased,
                         TypeAndMut { ty, mutbl: crate::rustc_hir::Mutability::Mut },
                     )),
diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs
index ce10780f9de..826ce60ed13 100644
--- a/compiler/rustc_codegen_cranelift/src/base.rs
+++ b/compiler/rustc_codegen_cranelift/src/base.rs
@@ -706,7 +706,6 @@ fn codegen_stmt<'tcx>(
                     let times = fx
                         .monomorphize(times)
                         .eval(fx.tcx, ParamEnv::reveal_all())
-                        .kind()
                         .try_to_bits(fx.tcx.data_layout.pointer_size)
                         .unwrap();
                     if operand.layout().size.bytes() == 0 {
@@ -747,7 +746,7 @@ fn codegen_stmt<'tcx>(
                 }
                 Rvalue::ShallowInitBox(ref operand, content_ty) => {
                     let content_ty = fx.monomorphize(content_ty);
-                    let box_layout = fx.layout_of(fx.tcx.mk_box(content_ty));
+                    let box_layout = fx.layout_of(Ty::new_box(fx.tcx, content_ty));
                     let operand = codegen_operand(fx, operand);
                     let operand = operand.load_scalar(fx);
                     lval.write_cvalue(fx, CValue::by_val(operand, box_layout));
@@ -888,7 +887,7 @@ pub(crate) fn codegen_place<'tcx>(
                         let ptr = cplace.to_ptr();
                         cplace = CPlace::for_ptr(
                             ptr.offset_i64(fx, elem_layout.size.bytes() as i64 * (from as i64)),
-                            fx.layout_of(fx.tcx.mk_array(*elem_ty, to - from)),
+                            fx.layout_of(Ty::new_array(fx.tcx, *elem_ty, to - from)),
                         );
                     }
                     ty::Slice(elem_ty) => {
diff --git a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs
index 13568b198db..b2bc289a5b6 100644
--- a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs
+++ b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs
@@ -92,7 +92,7 @@ pub(crate) fn maybe_codegen_checked<'tcx>(
     match bin_op {
         BinOp::BitAnd | BinOp::BitOr | BinOp::BitXor => unreachable!(),
         BinOp::Mul if is_signed => {
-            let out_ty = fx.tcx.mk_tup(&[lhs.layout().ty, fx.tcx.types.bool]);
+            let out_ty = Ty::new_tup(fx.tcx, &[lhs.layout().ty, fx.tcx.types.bool]);
             let oflow = CPlace::new_stack_slot(fx, fx.layout_of(fx.tcx.types.i32));
             let lhs = lhs.load_scalar(fx);
             let rhs = rhs.load_scalar(fx);
@@ -112,7 +112,7 @@ pub(crate) fn maybe_codegen_checked<'tcx>(
             Some(CValue::by_val_pair(res, oflow, fx.layout_of(out_ty)))
         }
         BinOp::Add | BinOp::Sub | BinOp::Mul => {
-            let out_ty = fx.tcx.mk_tup(&[lhs.layout().ty, fx.tcx.types.bool]);
+            let out_ty = Ty::new_tup(fx.tcx, &[lhs.layout().ty, fx.tcx.types.bool]);
             let out_place = CPlace::new_stack_slot(fx, fx.layout_of(out_ty));
             let param_types = vec![
                 AbiParam::special(fx.pointer_type, ArgumentPurpose::StructReturn),
diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs
index a694bb26afb..67ea20112fe 100644
--- a/compiler/rustc_codegen_cranelift/src/common.rs
+++ b/compiler/rustc_codegen_cranelift/src/common.rs
@@ -99,7 +99,7 @@ fn clif_pair_type_from_ty<'tcx>(
 
 /// Is a pointer to this type a fat ptr?
 pub(crate) fn has_ptr_meta<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
-    let ptr_ty = tcx.mk_ptr(TypeAndMut { ty, mutbl: rustc_hir::Mutability::Not });
+    let ptr_ty = Ty::new_ptr(tcx, TypeAndMut { ty, mutbl: rustc_hir::Mutability::Not });
     match &tcx.layout_of(ParamEnv::reveal_all().and(ptr_ty)).unwrap().abi {
         Abi::Scalar(_) => false,
         Abi::ScalarPair(_, _) => true,
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs
index bbd5f4be783..24ad0083a22 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs
@@ -386,7 +386,7 @@ fn llvm_add_sub<'tcx>(
     // carry0 | carry1 -> carry or borrow respectively
     let cb_out = fx.bcx.ins().bor(cb0, cb1);
 
-    let layout = fx.layout_of(fx.tcx.mk_tup(&[fx.tcx.types.u8, fx.tcx.types.u64]));
+    let layout = fx.layout_of(Ty::new_tup(fx.tcx, &[fx.tcx.types.u8, fx.tcx.types.u64]));
     let val = CValue::by_val_pair(cb_out, c, layout);
     ret.write_cvalue(fx, val);
 }
diff --git a/compiler/rustc_codegen_cranelift/src/num.rs b/compiler/rustc_codegen_cranelift/src/num.rs
index ac1a6cce096..8992f40fb90 100644
--- a/compiler/rustc_codegen_cranelift/src/num.rs
+++ b/compiler/rustc_codegen_cranelift/src/num.rs
@@ -270,7 +270,7 @@ pub(crate) fn codegen_checked_int_binop<'tcx>(
         _ => bug!("binop {:?} on checked int/uint lhs: {:?} rhs: {:?}", bin_op, in_lhs, in_rhs),
     };
 
-    let out_layout = fx.layout_of(fx.tcx.mk_tup(&[in_lhs.layout().ty, fx.tcx.types.bool]));
+    let out_layout = fx.layout_of(Ty::new_tup(fx.tcx, &[in_lhs.layout().ty, fx.tcx.types.bool]));
     CValue::by_val_pair(res, has_overflow, out_layout)
 }
 
diff --git a/compiler/rustc_codegen_gcc/src/coverageinfo.rs b/compiler/rustc_codegen_gcc/src/coverageinfo.rs
index 872fc2472e2..849e9886ef3 100644
--- a/compiler/rustc_codegen_gcc/src/coverageinfo.rs
+++ b/compiler/rustc_codegen_gcc/src/coverageinfo.rs
@@ -1,69 +1,11 @@
-use gccjit::RValue;
-use rustc_codegen_ssa::traits::{CoverageInfoBuilderMethods, CoverageInfoMethods};
-use rustc_hir::def_id::DefId;
-use rustc_middle::mir::coverage::{
-    CodeRegion,
-    CounterValueReference,
-    ExpressionOperandId,
-    InjectedExpressionId,
-    Op,
-};
+use rustc_codegen_ssa::traits::CoverageInfoBuilderMethods;
+use rustc_middle::mir::Coverage;
 use rustc_middle::ty::Instance;
 
 use crate::builder::Builder;
-use crate::context::CodegenCx;
 
 impl<'a, 'gcc, 'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
-    fn set_function_source_hash(
-        &mut self,
-        _instance: Instance<'tcx>,
-        _function_source_hash: u64,
-    ) -> bool {
-        unimplemented!();
-    }
-
-    fn add_coverage_counter(&mut self, _instance: Instance<'tcx>, _id: CounterValueReference, _region: CodeRegion) -> bool {
-        // TODO(antoyo)
-        false
-    }
-
-    fn add_coverage_counter_expression(&mut self, _instance: Instance<'tcx>, _id: InjectedExpressionId, _lhs: ExpressionOperandId, _op: Op, _rhs: ExpressionOperandId, _region: Option<CodeRegion>) -> bool {
-        // TODO(antoyo)
-        false
-    }
-
-    fn add_coverage_unreachable(&mut self, _instance: Instance<'tcx>, _region: CodeRegion) -> bool {
+    fn add_coverage(&mut self, _instance: Instance<'tcx>, _coverage: &Coverage) {
         // TODO(antoyo)
-        false
-    }
-}
-
-impl<'gcc, 'tcx> CoverageInfoMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
-    fn coverageinfo_finalize(&self) {
-        // TODO(antoyo)
-    }
-
-    fn get_pgo_func_name_var(&self, _instance: Instance<'tcx>) -> RValue<'gcc> {
-        unimplemented!();
-    }
-
-    /// Functions with MIR-based coverage are normally codegenned _only_ if
-    /// called. LLVM coverage tools typically expect every function to be
-    /// defined (even if unused), with at least one call to LLVM intrinsic
-    /// `instrprof.increment`.
-    ///
-    /// Codegen a small function that will never be called, with one counter
-    /// that will never be incremented.
-    ///
-    /// For used/called functions, the coverageinfo was already added to the
-    /// `function_coverage_map` (keyed by function `Instance`) during codegen.
-    /// But in this case, since the unused function was _not_ previously
-    /// codegenned, collect the coverage `CodeRegion`s from the MIR and add
-    /// them. The first `CodeRegion` is used to add a single counter, with the
-    /// same counter ID used in the injected `instrprof.increment` intrinsic
-    /// call. Since the function is never called, all other `CodeRegion`s can be
-    /// added as `unreachable_region`s.
-    fn define_unused_fn(&self, _def_id: DefId) {
-        unimplemented!();
     }
 }
diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs
index a31fee39918..0b208be4e62 100644
--- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs
+++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs
@@ -1147,19 +1147,19 @@ fn get_rust_try_fn<'a, 'gcc, 'tcx>(cx: &'a CodegenCx<'gcc, 'tcx>, codegen: &mut
 
     // Define the type up front for the signature of the rust_try function.
     let tcx = cx.tcx;
-    let i8p = tcx.mk_mut_ptr(tcx.types.i8);
+    let i8p = Ty::new_mut_ptr(tcx,tcx.types.i8);
     // `unsafe fn(*mut i8) -> ()`
-    let try_fn_ty = tcx.mk_fn_ptr(ty::Binder::dummy(tcx.mk_fn_sig(
+    let try_fn_ty = Ty::new_fn_ptr(tcx,ty::Binder::dummy(tcx.mk_fn_sig(
         iter::once(i8p),
-        tcx.mk_unit(),
+        Ty::new_unit(tcx,),
         false,
         rustc_hir::Unsafety::Unsafe,
         Abi::Rust,
     )));
     // `unsafe fn(*mut i8, *mut i8) -> ()`
-    let catch_fn_ty = tcx.mk_fn_ptr(ty::Binder::dummy(tcx.mk_fn_sig(
+    let catch_fn_ty = Ty::new_fn_ptr(tcx,ty::Binder::dummy(tcx.mk_fn_sig(
         [i8p, i8p].iter().cloned(),
-        tcx.mk_unit(),
+        Ty::new_unit(tcx,),
         false,
         rustc_hir::Unsafety::Unsafe,
         Abi::Rust,
diff --git a/compiler/rustc_codegen_gcc/src/type_of.rs b/compiler/rustc_codegen_gcc/src/type_of.rs
index 74f016cf90a..e0823888f67 100644
--- a/compiler/rustc_codegen_gcc/src/type_of.rs
+++ b/compiler/rustc_codegen_gcc/src/type_of.rs
@@ -283,7 +283,7 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> {
             // only wide pointer boxes are handled as pointers
             // thin pointer boxes with scalar allocators are handled by the general logic below
             ty::Adt(def, substs) if def.is_box() && cx.layout_of(substs.type_at(1)).is_zst() => {
-                let ptr_ty = cx.tcx.mk_mut_ptr(self.ty.boxed_ty());
+                let ptr_ty = Ty::new_mut_ptr(cx.tcx,self.ty.boxed_ty());
                 return cx.layout_of(ptr_ty).scalar_pair_element_gcc_type(cx, index, immediate);
             }
             _ => {}
diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml
index 39ff3a0ba2d..ad51f2d0958 100644
--- a/compiler/rustc_codegen_llvm/Cargo.toml
+++ b/compiler/rustc_codegen_llvm/Cargo.toml
@@ -8,6 +8,7 @@ test = false
 
 [dependencies]
 bitflags = "1.0"
+cstr = "0.2"
 libc = "0.2"
 measureme = "10.0.0"
 object = { version = "0.31.1", default-features = false, features = [
diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs
index 28be6d033f8..d221bad28ef 100644
--- a/compiler/rustc_codegen_llvm/src/abi.rs
+++ b/compiler/rustc_codegen_llvm/src/abi.rs
@@ -351,7 +351,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
                     continue;
                 }
                 PassMode::Indirect { attrs: _, extra_attrs: Some(_), on_stack: _ } => {
-                    let ptr_ty = cx.tcx.mk_mut_ptr(arg.layout.ty);
+                    let ptr_ty = Ty::new_mut_ptr(cx.tcx, arg.layout.ty);
                     let ptr_layout = cx.layout_of(ptr_ty);
                     llargument_tys.push(ptr_layout.scalar_pair_element_llvm_type(cx, 0, true));
                     llargument_tys.push(ptr_layout.scalar_pair_element_llvm_type(cx, 1, true));
diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs
index ad0636894b7..a57508815d6 100644
--- a/compiler/rustc_codegen_llvm/src/allocator.rs
+++ b/compiler/rustc_codegen_llvm/src/allocator.rs
@@ -77,7 +77,7 @@ pub(crate) unsafe fn codegen(
                 llvm::LLVMRustGetOrInsertFunction(llmod, callee.as_ptr().cast(), callee.len(), ty);
             llvm::LLVMRustSetVisibility(callee, llvm::Visibility::Hidden);
 
-            let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, llfn, c"entry".as_ptr().cast());
+            let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, llfn, "entry\0".as_ptr().cast());
 
             let llbuilder = llvm::LLVMCreateBuilderInContext(llcx);
             llvm::LLVMPositionBuilderAtEnd(llbuilder, llbb);
@@ -129,7 +129,7 @@ pub(crate) unsafe fn codegen(
     attributes::apply_to_llfn(callee, llvm::AttributePlace::Function, &[no_return]);
     llvm::LLVMRustSetVisibility(callee, llvm::Visibility::Hidden);
 
-    let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, llfn, c"entry".as_ptr().cast());
+    let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, llfn, "entry\0".as_ptr().cast());
 
     let llbuilder = llvm::LLVMCreateBuilderInContext(llcx);
     llvm::LLVMPositionBuilderAtEnd(llbuilder, llbb);
diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs
index 94885b40cc1..d7dd98d7938 100644
--- a/compiler/rustc_codegen_llvm/src/back/lto.rs
+++ b/compiler/rustc_codegen_llvm/src/back/lto.rs
@@ -601,7 +601,7 @@ pub(crate) fn run_pass_manager(
             llvm::LLVMRustAddModuleFlag(
                 module.module_llvm.llmod(),
                 llvm::LLVMModFlagBehavior::Error,
-                c"LTOPostLink".as_ptr().cast(),
+                "LTOPostLink\0".as_ptr().cast(),
                 1,
             );
         }
diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs
index 998e3b300da..0f5e975445f 100644
--- a/compiler/rustc_codegen_llvm/src/back/write.rs
+++ b/compiler/rustc_codegen_llvm/src/back/write.rs
@@ -931,16 +931,16 @@ unsafe fn embed_bitcode(
         let llglobal = llvm::LLVMAddGlobal(
             llmod,
             common::val_ty(llconst),
-            c"rustc.embedded.module".as_ptr().cast(),
+            "rustc.embedded.module\0".as_ptr().cast(),
         );
         llvm::LLVMSetInitializer(llglobal, llconst);
 
         let section = if is_apple {
-            c"__LLVM,__bitcode"
+            "__LLVM,__bitcode\0"
         } else if is_aix {
-            c".ipa"
+            ".ipa\0"
         } else {
-            c".llvmbc"
+            ".llvmbc\0"
         };
         llvm::LLVMSetSection(llglobal, section.as_ptr().cast());
         llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage);
@@ -950,15 +950,15 @@ unsafe fn embed_bitcode(
         let llglobal = llvm::LLVMAddGlobal(
             llmod,
             common::val_ty(llconst),
-            c"rustc.embedded.cmdline".as_ptr().cast(),
+            "rustc.embedded.cmdline\0".as_ptr().cast(),
         );
         llvm::LLVMSetInitializer(llglobal, llconst);
         let section = if is_apple {
-            c"__LLVM,__cmdline"
+            "__LLVM,__cmdline\0"
         } else if is_aix {
-            c".info"
+            ".info\0"
         } else {
-            c".llvmcmd"
+            ".llvmcmd\0"
         };
         llvm::LLVMSetSection(llglobal, section.as_ptr().cast());
         llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage);
diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs
index 2f7eb08ad3d..5b2bbdb4bde 100644
--- a/compiler/rustc_codegen_llvm/src/base.rs
+++ b/compiler/rustc_codegen_llvm/src/base.rs
@@ -19,6 +19,8 @@ use crate::context::CodegenCx;
 use crate::llvm;
 use crate::value::Value;
 
+use cstr::cstr;
+
 use rustc_codegen_ssa::base::maybe_create_entry_wrapper;
 use rustc_codegen_ssa::mono_item::MonoItemExt;
 use rustc_codegen_ssa::traits::*;
@@ -108,11 +110,11 @@ pub fn compile_codegen_unit(tcx: TyCtxt<'_>, cgu_name: Symbol) -> (ModuleCodegen
 
             // Create the llvm.used and llvm.compiler.used variables.
             if !cx.used_statics.borrow().is_empty() {
-                cx.create_used_variable_impl(c"llvm.used", &*cx.used_statics.borrow());
+                cx.create_used_variable_impl(cstr!("llvm.used"), &*cx.used_statics.borrow());
             }
             if !cx.compiler_used_statics.borrow().is_empty() {
                 cx.create_used_variable_impl(
-                    c"llvm.compiler.used",
+                    cstr!("llvm.compiler.used"),
                     &*cx.compiler_used_statics.borrow(),
                 );
             }
diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs
index 9863ca35202..d55992bf092 100644
--- a/compiler/rustc_codegen_llvm/src/builder.rs
+++ b/compiler/rustc_codegen_llvm/src/builder.rs
@@ -6,6 +6,7 @@ use crate::llvm::{self, AtomicOrdering, AtomicRmwBinOp, BasicBlock, False, True}
 use crate::type_::Type;
 use crate::type_of::LayoutLlvmExt;
 use crate::value::Value;
+use cstr::cstr;
 use libc::{c_char, c_uint};
 use rustc_codegen_ssa::common::{IntPredicate, RealPredicate, SynchronizationScope, TypeKind};
 use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue};
@@ -25,6 +26,7 @@ use rustc_target::abi::{self, call::FnAbi, Align, Size, WrappingRange};
 use rustc_target::spec::{HasTargetSpec, SanitizerSet, Target};
 use smallvec::SmallVec;
 use std::borrow::Cow;
+use std::ffi::CStr;
 use std::iter;
 use std::ops::Deref;
 use std::ptr;
@@ -44,10 +46,13 @@ impl Drop for Builder<'_, '_, '_> {
     }
 }
 
+// FIXME(eddyb) use a checked constructor when they become `const fn`.
+const EMPTY_C_STR: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"\0") };
+
 /// Empty string, to be used where LLVM expects an instruction name, indicating
 /// that the instruction is to be left unnamed (i.e. numbered, in textual IR).
 // FIXME(eddyb) pass `&CStr` directly to FFI once it's a thin pointer.
-const UNNAMED: *const c_char = c"".as_ptr();
+const UNNAMED: *const c_char = EMPTY_C_STR.as_ptr();
 
 impl<'ll, 'tcx> BackendTypes for Builder<'_, 'll, 'tcx> {
     type Value = <CodegenCx<'ll, 'tcx> as BackendTypes>::Value;
@@ -1002,13 +1007,14 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
     }
 
     fn cleanup_pad(&mut self, parent: Option<&'ll Value>, args: &[&'ll Value]) -> Funclet<'ll> {
+        let name = cstr!("cleanuppad");
         let ret = unsafe {
             llvm::LLVMBuildCleanupPad(
                 self.llbuilder,
                 parent,
                 args.as_ptr(),
                 args.len() as c_uint,
-                c"cleanuppad".as_ptr(),
+                name.as_ptr(),
             )
         };
         Funclet::new(ret.expect("LLVM does not have support for cleanuppad"))
@@ -1022,13 +1028,14 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
     }
 
     fn catch_pad(&mut self, parent: &'ll Value, args: &[&'ll Value]) -> Funclet<'ll> {
+        let name = cstr!("catchpad");
         let ret = unsafe {
             llvm::LLVMBuildCatchPad(
                 self.llbuilder,
                 parent,
                 args.as_ptr(),
                 args.len() as c_uint,
-                c"catchpad".as_ptr(),
+                name.as_ptr(),
             )
         };
         Funclet::new(ret.expect("LLVM does not have support for catchpad"))
@@ -1040,13 +1047,14 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
         unwind: Option<&'ll BasicBlock>,
         handlers: &[&'ll BasicBlock],
     ) -> &'ll Value {
+        let name = cstr!("catchswitch");
         let ret = unsafe {
             llvm::LLVMBuildCatchSwitch(
                 self.llbuilder,
                 parent,
                 unwind,
                 handlers.len() as c_uint,
-                c"catchswitch".as_ptr(),
+                name.as_ptr(),
             )
         };
         let ret = ret.expect("LLVM does not have support for catchswitch");
diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs
index 2087754c66b..df52f50f86f 100644
--- a/compiler/rustc_codegen_llvm/src/consts.rs
+++ b/compiler/rustc_codegen_llvm/src/consts.rs
@@ -8,6 +8,7 @@ use crate::llvm::{self, True};
 use crate::type_::Type;
 use crate::type_of::LayoutLlvmExt;
 use crate::value::Value;
+use cstr::cstr;
 use rustc_codegen_ssa::traits::*;
 use rustc_hir::def_id::DefId;
 use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
@@ -481,9 +482,9 @@ impl<'ll> StaticMethods for CodegenCx<'ll, '_> {
                             .all(|&byte| byte == 0);
 
                     let sect_name = if all_bytes_are_zero {
-                        c"__DATA,__thread_bss"
+                        cstr!("__DATA,__thread_bss")
                     } else {
-                        c"__DATA,__thread_data"
+                        cstr!("__DATA,__thread_data")
                     };
                     llvm::LLVMSetSection(g, sect_name.as_ptr());
                 }
@@ -512,7 +513,7 @@ impl<'ll> StaticMethods for CodegenCx<'ll, '_> {
                     let val = llvm::LLVMMetadataAsValue(self.llcx, meta);
                     llvm::LLVMAddNamedMetadataOperand(
                         self.llmod,
-                        c"wasm.custom_sections".as_ptr().cast(),
+                        "wasm.custom_sections\0".as_ptr().cast(),
                         val,
                     );
                 }
diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs
index 287a22bc9a6..e1e0a442845 100644
--- a/compiler/rustc_codegen_llvm/src/context.rs
+++ b/compiler/rustc_codegen_llvm/src/context.rs
@@ -8,6 +8,7 @@ use crate::llvm_util;
 use crate::type_::Type;
 use crate::value::Value;
 
+use cstr::cstr;
 use rustc_codegen_ssa::base::{wants_msvc_seh, wants_wasm_eh};
 use rustc_codegen_ssa::traits::*;
 use rustc_data_structures::base_n;
@@ -223,42 +224,36 @@ pub unsafe fn create_module<'ll>(
     // If skipping the PLT is enabled, we need to add some module metadata
     // to ensure intrinsic calls don't use it.
     if !sess.needs_plt() {
-        llvm::LLVMRustAddModuleFlag(
-            llmod,
-            llvm::LLVMModFlagBehavior::Warning,
-            c"RtLibUseGOT".as_ptr().cast(),
-            1,
-        );
+        let avoid_plt = "RtLibUseGOT\0".as_ptr().cast();
+        llvm::LLVMRustAddModuleFlag(llmod, llvm::LLVMModFlagBehavior::Warning, avoid_plt, 1);
     }
 
     // Enable canonical jump tables if CFI is enabled. (See https://reviews.llvm.org/D65629.)
     if sess.is_sanitizer_cfi_canonical_jump_tables_enabled() && sess.is_sanitizer_cfi_enabled() {
+        let canonical_jump_tables = "CFI Canonical Jump Tables\0".as_ptr().cast();
         llvm::LLVMRustAddModuleFlag(
             llmod,
             llvm::LLVMModFlagBehavior::Override,
-            c"CFI Canonical Jump Tables".as_ptr().cast(),
+            canonical_jump_tables,
             1,
         );
     }
 
     // Enable LTO unit splitting if specified or if CFI is enabled. (See https://reviews.llvm.org/D53891.)
     if sess.is_split_lto_unit_enabled() || sess.is_sanitizer_cfi_enabled() {
+        let enable_split_lto_unit = "EnableSplitLTOUnit\0".as_ptr().cast();
         llvm::LLVMRustAddModuleFlag(
             llmod,
             llvm::LLVMModFlagBehavior::Override,
-            c"EnableSplitLTOUnit".as_ptr().cast(),
+            enable_split_lto_unit,
             1,
         );
     }
 
     // Add "kcfi" module flag if KCFI is enabled. (See https://reviews.llvm.org/D119296.)
     if sess.is_sanitizer_kcfi_enabled() {
-        llvm::LLVMRustAddModuleFlag(
-            llmod,
-            llvm::LLVMModFlagBehavior::Override,
-            c"kcfi".as_ptr().cast(),
-            1,
-        );
+        let kcfi = "kcfi\0".as_ptr().cast();
+        llvm::LLVMRustAddModuleFlag(llmod, llvm::LLVMModFlagBehavior::Override, kcfi, 1);
     }
 
     // Control Flow Guard is currently only supported by the MSVC linker on Windows.
@@ -270,7 +265,7 @@ pub unsafe fn create_module<'ll>(
                 llvm::LLVMRustAddModuleFlag(
                     llmod,
                     llvm::LLVMModFlagBehavior::Warning,
-                    c"cfguard".as_ptr() as *const _,
+                    "cfguard\0".as_ptr() as *const _,
                     1,
                 )
             }
@@ -279,7 +274,7 @@ pub unsafe fn create_module<'ll>(
                 llvm::LLVMRustAddModuleFlag(
                     llmod,
                     llvm::LLVMModFlagBehavior::Warning,
-                    c"cfguard".as_ptr() as *const _,
+                    "cfguard\0".as_ptr() as *const _,
                     2,
                 )
             }
@@ -297,26 +292,26 @@ pub unsafe fn create_module<'ll>(
             llvm::LLVMRustAddModuleFlag(
                 llmod,
                 behavior,
-                c"branch-target-enforcement".as_ptr().cast(),
+                "branch-target-enforcement\0".as_ptr().cast(),
                 bti.into(),
             );
             llvm::LLVMRustAddModuleFlag(
                 llmod,
                 behavior,
-                c"sign-return-address".as_ptr().cast(),
+                "sign-return-address\0".as_ptr().cast(),
                 pac_ret.is_some().into(),
             );
             let pac_opts = pac_ret.unwrap_or(PacRet { leaf: false, key: PAuthKey::A });
             llvm::LLVMRustAddModuleFlag(
                 llmod,
                 behavior,
-                c"sign-return-address-all".as_ptr().cast(),
+                "sign-return-address-all\0".as_ptr().cast(),
                 pac_opts.leaf.into(),
             );
             llvm::LLVMRustAddModuleFlag(
                 llmod,
                 behavior,
-                c"sign-return-address-with-bkey".as_ptr().cast(),
+                "sign-return-address-with-bkey\0".as_ptr().cast(),
                 u32::from(pac_opts.key == PAuthKey::B),
             );
         } else {
@@ -332,7 +327,7 @@ pub unsafe fn create_module<'ll>(
         llvm::LLVMRustAddModuleFlag(
             llmod,
             llvm::LLVMModFlagBehavior::Override,
-            c"cf-protection-branch".as_ptr().cast(),
+            "cf-protection-branch\0".as_ptr().cast(),
             1,
         )
     }
@@ -340,7 +335,7 @@ pub unsafe fn create_module<'ll>(
         llvm::LLVMRustAddModuleFlag(
             llmod,
             llvm::LLVMModFlagBehavior::Override,
-            c"cf-protection-return".as_ptr().cast(),
+            "cf-protection-return\0".as_ptr().cast(),
             1,
         )
     }
@@ -349,7 +344,7 @@ pub unsafe fn create_module<'ll>(
         llvm::LLVMRustAddModuleFlag(
             llmod,
             llvm::LLVMModFlagBehavior::Error,
-            c"Virtual Function Elim".as_ptr().cast(),
+            "Virtual Function Elim\0".as_ptr().cast(),
             1,
         );
     }
@@ -481,13 +476,14 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
     }
 
     pub(crate) fn create_used_variable_impl(&self, name: &'static CStr, values: &[&'ll Value]) {
+        let section = cstr!("llvm.metadata");
         let array = self.const_array(self.type_ptr_to(self.type_i8()), values);
 
         unsafe {
             let g = llvm::LLVMAddGlobal(self.llmod, self.val_ty(array), name.as_ptr());
             llvm::LLVMSetInitializer(g, array);
             llvm::LLVMRustSetLinkage(g, llvm::Linkage::AppendingLinkage);
-            llvm::LLVMSetSection(g, c"llvm.metadata".as_ptr());
+            llvm::LLVMSetSection(g, section.as_ptr());
         }
     }
 }
diff --git a/compiler/rustc_codegen_ssa/src/coverageinfo/ffi.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs
index 1791ce4b315..1791ce4b315 100644
--- a/compiler/rustc_codegen_ssa/src/coverageinfo/ffi.rs
+++ b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs
diff --git a/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs
index e4da3b8de05..06844afd6b8 100644
--- a/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs
+++ b/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs
@@ -1,6 +1,7 @@
 pub use super::ffi::*;
 
 use rustc_index::{IndexSlice, IndexVec};
+use rustc_middle::bug;
 use rustc_middle::mir::coverage::{
     CodeRegion, CounterValueReference, ExpressionOperandId, InjectedExpressionId,
     InjectedExpressionIndex, MappedExpressionIndex, Op,
diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
index 21a1ac34844..a1ff2aa6625 100644
--- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
+++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
@@ -1,10 +1,10 @@
 use crate::common::CodegenCx;
 use crate::coverageinfo;
+use crate::coverageinfo::map_data::{Counter, CounterExpression};
 use crate::llvm;
 
 use llvm::coverageinfo::CounterMappingRegion;
-use rustc_codegen_ssa::coverageinfo::map::{Counter, CounterExpression};
-use rustc_codegen_ssa::traits::{ConstMethods, CoverageInfoMethods};
+use rustc_codegen_ssa::traits::ConstMethods;
 use rustc_data_structures::fx::FxIndexSet;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::DefId;
diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
index cd261293e9b..42fdbd78618 100644
--- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
@@ -3,13 +3,13 @@ use crate::llvm;
 use crate::abi::Abi;
 use crate::builder::Builder;
 use crate::common::CodegenCx;
+use crate::coverageinfo::map_data::{CounterExpression, FunctionCoverage};
 
 use libc::c_uint;
 use llvm::coverageinfo::CounterMappingRegion;
-use rustc_codegen_ssa::coverageinfo::map::{CounterExpression, FunctionCoverage};
 use rustc_codegen_ssa::traits::{
-    BaseTypeMethods, BuilderMethods, ConstMethods, CoverageInfoBuilderMethods, CoverageInfoMethods,
-    MiscMethods, StaticMethods,
+    BaseTypeMethods, BuilderMethods, ConstMethods, CoverageInfoBuilderMethods, MiscMethods,
+    StaticMethods,
 };
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir as hir;
@@ -17,16 +17,20 @@ use rustc_hir::def_id::DefId;
 use rustc_llvm::RustString;
 use rustc_middle::bug;
 use rustc_middle::mir::coverage::{
-    CodeRegion, CounterValueReference, ExpressionOperandId, InjectedExpressionId, Op,
+    CodeRegion, CounterValueReference, CoverageKind, ExpressionOperandId, InjectedExpressionId, Op,
 };
+use rustc_middle::mir::Coverage;
 use rustc_middle::ty;
-use rustc_middle::ty::layout::FnAbiOf;
+use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt};
 use rustc_middle::ty::subst::InternalSubsts;
 use rustc_middle::ty::Instance;
+use rustc_middle::ty::Ty;
 
 use std::cell::RefCell;
 use std::ffi::CString;
 
+mod ffi;
+pub(crate) mod map_data;
 pub mod mapgen;
 
 const UNUSED_FUNCTION_COUNTER_ID: CounterValueReference = CounterValueReference::START;
@@ -53,11 +57,17 @@ impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> {
     }
 }
 
-impl<'ll, 'tcx> CoverageInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
-    fn coverageinfo_finalize(&self) {
+// These methods used to be part of trait `CoverageInfoMethods`, which no longer
+// exists after most coverage code was moved out of SSA.
+impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
+    pub(crate) fn coverageinfo_finalize(&self) {
         mapgen::finalize(self)
     }
 
+    /// For LLVM codegen, returns a function-specific `Value` for a global
+    /// string, to hold the function name passed to LLVM intrinsic
+    /// `instrprof.increment()`. The `Value` is only created once per instance.
+    /// Multiple invocations with the same instance return the same `Value`.
     fn get_pgo_func_name_var(&self, instance: Instance<'tcx>) -> &'ll llvm::Value {
         if let Some(coverage_context) = self.coverage_context() {
             debug!("getting pgo_func_name_var for instance={:?}", instance);
@@ -94,6 +104,54 @@ impl<'ll, 'tcx> CoverageInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
 }
 
 impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
+    fn add_coverage(&mut self, instance: Instance<'tcx>, coverage: &Coverage) {
+        let bx = self;
+
+        let Coverage { kind, code_region } = coverage.clone();
+        match kind {
+            CoverageKind::Counter { function_source_hash, id } => {
+                if bx.set_function_source_hash(instance, function_source_hash) {
+                    // If `set_function_source_hash()` returned true, the coverage map is enabled,
+                    // so continue adding the counter.
+                    if let Some(code_region) = code_region {
+                        // Note: Some counters do not have code regions, but may still be referenced
+                        // from expressions. In that case, don't add the counter to the coverage map,
+                        // but do inject the counter intrinsic.
+                        bx.add_coverage_counter(instance, id, code_region);
+                    }
+
+                    let coverageinfo = bx.tcx().coverageinfo(instance.def);
+
+                    let fn_name = bx.get_pgo_func_name_var(instance);
+                    let hash = bx.const_u64(function_source_hash);
+                    let num_counters = bx.const_u32(coverageinfo.num_counters);
+                    let index = bx.const_u32(id.zero_based_index());
+                    debug!(
+                        "codegen intrinsic instrprof.increment(fn_name={:?}, hash={:?}, num_counters={:?}, index={:?})",
+                        fn_name, hash, num_counters, index,
+                    );
+                    bx.instrprof_increment(fn_name, hash, num_counters, index);
+                }
+            }
+            CoverageKind::Expression { id, lhs, op, rhs } => {
+                bx.add_coverage_counter_expression(instance, id, lhs, op, rhs, code_region);
+            }
+            CoverageKind::Unreachable => {
+                bx.add_coverage_unreachable(
+                    instance,
+                    code_region.expect("unreachable regions always have code regions"),
+                );
+            }
+        }
+    }
+}
+
+// These methods used to be part of trait `CoverageInfoBuilderMethods`, but
+// after moving most coverage code out of SSA they are now just ordinary methods.
+impl<'tcx> Builder<'_, '_, 'tcx> {
+    /// Returns true if the function source hash was added to the coverage map (even if it had
+    /// already been added, for this instance). Returns false *only* if `-C instrument-coverage` is
+    /// not enabled (a coverage map is not being generated).
     fn set_function_source_hash(
         &mut self,
         instance: Instance<'tcx>,
@@ -115,6 +173,8 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
         }
     }
 
+    /// Returns true if the counter was added to the coverage map; false if `-C instrument-coverage`
+    /// is not enabled (a coverage map is not being generated).
     fn add_coverage_counter(
         &mut self,
         instance: Instance<'tcx>,
@@ -137,6 +197,8 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
         }
     }
 
+    /// Returns true if the expression was added to the coverage map; false if
+    /// `-C instrument-coverage` is not enabled (a coverage map is not being generated).
     fn add_coverage_counter_expression(
         &mut self,
         instance: Instance<'tcx>,
@@ -163,6 +225,8 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
         }
     }
 
+    /// Returns true if the region was added to the coverage map; false if `-C instrument-coverage`
+    /// is not enabled (a coverage map is not being generated).
     fn add_coverage_unreachable(&mut self, instance: Instance<'tcx>, region: CodeRegion) -> bool {
         if let Some(coverage_context) = self.coverage_context() {
             debug!(
@@ -199,8 +263,8 @@ fn declare_unused_fn<'tcx>(cx: &CodegenCx<'_, 'tcx>, def_id: DefId) -> Instance<
         tcx.symbol_name(instance).name,
         cx.fn_abi_of_fn_ptr(
             ty::Binder::dummy(tcx.mk_fn_sig(
-                [tcx.mk_unit()],
-                tcx.mk_unit(),
+                [Ty::new_unit(tcx)],
+                Ty::new_unit(tcx),
                 false,
                 hir::Unsafety::Unsafe,
                 Abi::Rust,
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs
index 8be54b7eb71..37f30917609 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs
@@ -38,6 +38,7 @@ pub fn get_or_insert_gdb_debug_scripts_section_global<'ll>(cx: &CodegenCx<'ll, '
         unsafe { llvm::LLVMGetNamedGlobal(cx.llmod, c_section_var_name.as_ptr().cast()) };
 
     section_var.unwrap_or_else(|| {
+        let section_name = b".debug_gdb_scripts\0";
         let mut section_contents = Vec::new();
 
         // Add the pretty printers for the standard library first.
@@ -70,7 +71,7 @@ pub fn get_or_insert_gdb_debug_scripts_section_global<'ll>(cx: &CodegenCx<'ll, '
             let section_var = cx
                 .define_global(section_var_name, llvm_type)
                 .unwrap_or_else(|| bug!("symbol `{}` is already defined", section_var_name));
-            llvm::LLVMSetSection(section_var, c".debug_gdb_scripts".as_ptr().cast());
+            llvm::LLVMSetSection(section_var, section_name.as_ptr().cast());
             llvm::LLVMSetInitializer(section_var, cx.const_bytes(section_contents));
             llvm::LLVMSetGlobalConstant(section_var, llvm::True);
             llvm::LLVMSetUnnamedAddress(section_var, llvm::UnnamedAddr::Global);
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
index 4b88ab8a97a..d61400d3fa3 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
@@ -20,6 +20,7 @@ use crate::llvm::debuginfo::{
 };
 use crate::value::Value;
 
+use cstr::cstr;
 use rustc_codegen_ssa::debuginfo::type_names::cpp_like_debuginfo;
 use rustc_codegen_ssa::debuginfo::type_names::VTableNameKind;
 use rustc_codegen_ssa::traits::*;
@@ -167,7 +168,7 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>(
     // a (fat) pointer. Make sure it is not called for e.g. `Box<T, NonZSTAllocator>`.
     debug_assert_eq!(
         cx.size_and_align_of(ptr_type),
-        cx.size_and_align_of(cx.tcx.mk_mut_ptr(pointee_type))
+        cx.size_and_align_of(Ty::new_mut_ptr(cx.tcx, pointee_type))
     );
 
     let pointee_type_di_node = type_di_node(cx, pointee_type);
@@ -222,8 +223,11 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>(
                     //        at all and instead emit regular struct debuginfo for it. We just
                     //        need to make sure that we don't break existing debuginfo consumers
                     //        by doing that (at least not without a warning period).
-                    let layout_type =
-                        if ptr_type.is_box() { cx.tcx.mk_mut_ptr(pointee_type) } else { ptr_type };
+                    let layout_type = if ptr_type.is_box() {
+                        Ty::new_mut_ptr(cx.tcx, pointee_type)
+                    } else {
+                        ptr_type
+                    };
 
                     let layout = cx.layout_of(layout_type);
                     let addr_field = layout.field(cx, abi::FAT_PTR_ADDR);
@@ -811,6 +815,7 @@ pub fn build_compile_unit_di_node<'ll, 'tcx>(
 
     let name_in_debuginfo = name_in_debuginfo.to_string_lossy();
     let work_dir = tcx.sess.opts.working_dir.to_string_lossy(FileNameDisplayPreference::Remapped);
+    let flags = "\0";
     let output_filenames = tcx.output_filenames(());
     let split_name = if tcx.sess.target_can_use_split_dwarf() {
         output_filenames
@@ -847,7 +852,7 @@ pub fn build_compile_unit_di_node<'ll, 'tcx>(
             producer.as_ptr().cast(),
             producer.len(),
             tcx.sess.opts.optimize != config::OptLevel::No,
-            c"".as_ptr().cast(),
+            flags.as_ptr().cast(),
             0,
             // NB: this doesn't actually have any perceptible effect, it seems. LLVM will instead
             // put the path supplied to `MCSplitDwarfFile` into the debug info of the final
@@ -876,7 +881,8 @@ pub fn build_compile_unit_di_node<'ll, 'tcx>(
             );
             let val = llvm::LLVMMetadataAsValue(debug_context.llcontext, gcov_metadata);
 
-            llvm::LLVMAddNamedMetadataOperand(debug_context.llmod, c"llvm.gcov".as_ptr(), val);
+            let llvm_gcov_ident = cstr!("llvm.gcov");
+            llvm::LLVMAddNamedMetadataOperand(debug_context.llmod, llvm_gcov_ident.as_ptr(), val);
         }
 
         // Insert `llvm.ident` metadata on the wasm targets since that will
@@ -889,7 +895,7 @@ pub fn build_compile_unit_di_node<'ll, 'tcx>(
             );
             llvm::LLVMAddNamedMetadataOperand(
                 debug_context.llmod,
-                c"llvm.ident".as_ptr(),
+                cstr!("llvm.ident").as_ptr(),
                 llvm::LLVMMDNodeInContext(debug_context.llcontext, &name_metadata, 1),
             );
         }
@@ -1295,7 +1301,7 @@ fn build_vtable_type_di_node<'ll, 'tcx>(
 
     // All function pointers are described as opaque pointers. This could be improved in the future
     // by describing them as actual function pointers.
-    let void_pointer_ty = tcx.mk_imm_ptr(tcx.types.unit);
+    let void_pointer_ty = Ty::new_imm_ptr(tcx, tcx.types.unit);
     let void_pointer_type_di_node = type_di_node(cx, void_pointer_ty);
     let usize_di_node = type_di_node(cx, tcx.types.usize);
     let (pointer_size, pointer_align) = cx.size_and_align_of(void_pointer_ty);
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
index c2f16cad3fc..b924c771af7 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
@@ -113,7 +113,7 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> {
                 llvm::LLVMRustAddModuleFlag(
                     self.llmod,
                     llvm::LLVMModFlagBehavior::Warning,
-                    c"Dwarf Version".as_ptr().cast(),
+                    "Dwarf Version\0".as_ptr().cast(),
                     dwarf_version,
                 );
             } else {
@@ -121,16 +121,17 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> {
                 llvm::LLVMRustAddModuleFlag(
                     self.llmod,
                     llvm::LLVMModFlagBehavior::Warning,
-                    c"CodeView".as_ptr().cast(),
+                    "CodeView\0".as_ptr().cast(),
                     1,
                 )
             }
 
             // Prevent bitcode readers from deleting the debug info.
+            let ptr = "Debug Info Version\0".as_ptr();
             llvm::LLVMRustAddModuleFlag(
                 self.llmod,
                 llvm::LLVMModFlagBehavior::Warning,
-                c"Debug Info Version".as_ptr().cast(),
+                ptr.cast(),
                 llvm::LLVMRustDebugMetadataVersion(),
             );
         }
@@ -453,7 +454,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
                         ty::Array(ct, _)
                             if (*ct == cx.tcx.types.u8) || cx.layout_of(*ct).is_zst() =>
                         {
-                            cx.tcx.mk_imm_ptr(*ct)
+                            Ty::new_imm_ptr(cx.tcx, *ct)
                         }
                         _ => t,
                     };
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs
index 6bcd3e5bf58..7be83638676 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs
@@ -82,8 +82,8 @@ pub(crate) fn fat_pointer_kind<'ll, 'tcx>(
         ty::Foreign(_) => {
             // Assert that pointers to foreign types really are thin:
             debug_assert_eq!(
-                cx.size_of(cx.tcx.mk_imm_ptr(pointee_tail_ty)),
-                cx.size_of(cx.tcx.mk_imm_ptr(cx.tcx.types.u8))
+                cx.size_of(Ty::new_imm_ptr(cx.tcx, pointee_tail_ty)),
+                cx.size_of(Ty::new_imm_ptr(cx.tcx, cx.tcx.types.u8))
             );
             None
         }
diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs
index 31bafa87814..a254c86c291 100644
--- a/compiler/rustc_codegen_llvm/src/intrinsic.rs
+++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs
@@ -873,23 +873,29 @@ fn get_rust_try_fn<'ll, 'tcx>(
 
     // Define the type up front for the signature of the rust_try function.
     let tcx = cx.tcx;
-    let i8p = tcx.mk_mut_ptr(tcx.types.i8);
+    let i8p = Ty::new_mut_ptr(tcx, tcx.types.i8);
     // `unsafe fn(*mut i8) -> ()`
-    let try_fn_ty = tcx.mk_fn_ptr(ty::Binder::dummy(tcx.mk_fn_sig(
-        [i8p],
-        tcx.mk_unit(),
-        false,
-        hir::Unsafety::Unsafe,
-        Abi::Rust,
-    )));
+    let try_fn_ty = Ty::new_fn_ptr(
+        tcx,
+        ty::Binder::dummy(tcx.mk_fn_sig(
+            [i8p],
+            Ty::new_unit(tcx),
+            false,
+            hir::Unsafety::Unsafe,
+            Abi::Rust,
+        )),
+    );
     // `unsafe fn(*mut i8, *mut i8) -> ()`
-    let catch_fn_ty = tcx.mk_fn_ptr(ty::Binder::dummy(tcx.mk_fn_sig(
-        [i8p, i8p],
-        tcx.mk_unit(),
-        false,
-        hir::Unsafety::Unsafe,
-        Abi::Rust,
-    )));
+    let catch_fn_ty = Ty::new_fn_ptr(
+        tcx,
+        ty::Binder::dummy(tcx.mk_fn_sig(
+            [i8p, i8p],
+            Ty::new_unit(tcx),
+            false,
+            hir::Unsafety::Unsafe,
+            Abi::Rust,
+        )),
+    );
     // `unsafe fn(unsafe fn(*mut i8) -> (), *mut i8, unsafe fn(*mut i8, *mut i8) -> ()) -> i32`
     let rust_fn_sig = ty::Binder::dummy(cx.tcx.mk_fn_sig(
         [try_fn_ty, i8p, catch_fn_ty],
diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs
index 24968e00cc8..24ba28bbc82 100644
--- a/compiler/rustc_codegen_llvm/src/lib.rs
+++ b/compiler/rustc_codegen_llvm/src/lib.rs
@@ -11,7 +11,6 @@
 #![feature(let_chains)]
 #![feature(never_type)]
 #![feature(impl_trait_in_assoc_type)]
-#![feature(c_str_literals)]
 #![recursion_limit = "256"]
 #![allow(rustc::potential_query_instability)]
 #![deny(rustc::untranslatable_diagnostic)]
diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
index b667bc4f6d4..3ad546b61e9 100644
--- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
@@ -1,7 +1,7 @@
 #![allow(non_camel_case_types)]
 #![allow(non_upper_case_globals)]
 
-use rustc_codegen_ssa::coverageinfo::map as coverage_map;
+use crate::coverageinfo::map_data as coverage_map;
 
 use super::debuginfo::{
     DIArray, DIBasicType, DIBuilder, DICompositeType, DIDerivedType, DIDescriptor, DIEnumerator,
@@ -585,6 +585,16 @@ pub enum ThreadLocalMode {
     LocalExec,
 }
 
+/// LLVMRustTailCallKind
+#[derive(Copy, Clone)]
+#[repr(C)]
+pub enum TailCallKind {
+    None,
+    Tail,
+    MustTail,
+    NoTail,
+}
+
 /// LLVMRustChecksumKind
 #[derive(Copy, Clone)]
 #[repr(C)]
@@ -1196,6 +1206,7 @@ extern "C" {
         NameLen: size_t,
     ) -> Option<&Value>;
     pub fn LLVMSetTailCall(CallInst: &Value, IsTailCall: Bool);
+    pub fn LLVMRustSetTailCallKind(CallInst: &Value, TKC: TailCallKind);
 
     // Operations on attributes
     pub fn LLVMRustCreateAttrNoValue(C: &Context, attr: AttributeKind) -> &Attribute;
diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs
index 3339e4e07ed..58e97be34f2 100644
--- a/compiler/rustc_codegen_llvm/src/type_of.rs
+++ b/compiler/rustc_codegen_llvm/src/type_of.rs
@@ -337,12 +337,13 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> {
             // only wide pointer boxes are handled as pointers
             // thin pointer boxes with scalar allocators are handled by the general logic below
             ty::Adt(def, substs) if def.is_box() && cx.layout_of(substs.type_at(1)).is_zst() => {
-                let ptr_ty = cx.tcx.mk_mut_ptr(self.ty.boxed_ty());
+                let ptr_ty = Ty::new_mut_ptr(cx.tcx, self.ty.boxed_ty());
                 return cx.layout_of(ptr_ty).scalar_pair_element_llvm_type(cx, index, immediate);
             }
             // `dyn* Trait` has the same ABI as `*mut dyn Trait`
             ty::Dynamic(bounds, region, ty::DynStar) => {
-                let ptr_ty = cx.tcx.mk_mut_ptr(cx.tcx.mk_dynamic(bounds, region, ty::Dyn));
+                let ptr_ty =
+                    Ty::new_mut_ptr(cx.tcx, Ty::new_dynamic(cx.tcx, bounds, region, ty::Dyn));
                 return cx.layout_of(ptr_ty).scalar_pair_element_llvm_type(cx, index, immediate);
             }
             _ => {}
diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs
index b19398e68c2..8800caa71d6 100644
--- a/compiler/rustc_codegen_llvm/src/va_arg.rs
+++ b/compiler/rustc_codegen_llvm/src/va_arg.rs
@@ -73,7 +73,7 @@ fn emit_ptr_va_arg<'ll, 'tcx>(
     let layout = bx.cx.layout_of(target_ty);
     let (llty, size, align) = if indirect {
         (
-            bx.cx.layout_of(bx.cx.tcx.mk_imm_ptr(target_ty)).llvm_type(bx.cx),
+            bx.cx.layout_of(Ty::new_imm_ptr(bx.cx.tcx, target_ty)).llvm_type(bx.cx),
             bx.cx.data_layout().pointer_size,
             bx.cx.data_layout().pointer_align,
         )
diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs
index f8ced6949d5..9133601ecd1 100644
--- a/compiler/rustc_codegen_ssa/src/base.rs
+++ b/compiler/rustc_codegen_ssa/src/base.rs
@@ -199,7 +199,7 @@ fn vtable_ptr_ty<'tcx, Cx: CodegenMethods<'tcx>>(
     cx.scalar_pair_element_backend_type(
         cx.layout_of(match kind {
             // vtable is the second field of `*mut dyn Trait`
-            ty::Dyn => cx.tcx().mk_mut_ptr(target),
+            ty::Dyn => Ty::new_mut_ptr(cx.tcx(), target),
             // vtable is the second field of `dyn* Trait`
             ty::DynStar => target,
         }),
diff --git a/compiler/rustc_codegen_ssa/src/coverageinfo/mod.rs b/compiler/rustc_codegen_ssa/src/coverageinfo/mod.rs
deleted file mode 100644
index 569fd3f1a51..00000000000
--- a/compiler/rustc_codegen_ssa/src/coverageinfo/mod.rs
+++ /dev/null
@@ -1,2 +0,0 @@
-pub mod ffi;
-pub mod map;
diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs
index c26a7422fdd..be4c81638d6 100644
--- a/compiler/rustc_codegen_ssa/src/lib.rs
+++ b/compiler/rustc_codegen_ssa/src/lib.rs
@@ -48,7 +48,6 @@ pub mod back;
 pub mod base;
 pub mod codegen_attrs;
 pub mod common;
-pub mod coverageinfo;
 pub mod debuginfo;
 pub mod errors;
 pub mod glue;
diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs
index 93bed3a4a4a..9d1b3ce8266 100644
--- a/compiler/rustc_codegen_ssa/src/mir/block.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/block.rs
@@ -1505,9 +1505,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         if let Some(slot) = self.personality_slot {
             slot
         } else {
-            let layout = cx.layout_of(
-                cx.tcx().mk_tup(&[cx.tcx().mk_mut_ptr(cx.tcx().types.u8), cx.tcx().types.i32]),
-            );
+            let layout = cx.layout_of(Ty::new_tup(
+                cx.tcx(),
+                &[Ty::new_mut_ptr(cx.tcx(), cx.tcx().types.u8), cx.tcx().types.i32],
+            ));
             let slot = PlaceRef::alloca(bx, layout);
             self.personality_slot = Some(slot);
             slot
diff --git a/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs b/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs
index f1fe495282a..ee70465966d 100644
--- a/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs
@@ -1,13 +1,12 @@
 use crate::traits::*;
 
-use rustc_middle::mir::coverage::*;
 use rustc_middle::mir::Coverage;
 use rustc_middle::mir::SourceScope;
 
 use super::FunctionCx;
 
 impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
-    pub fn codegen_coverage(&self, bx: &mut Bx, coverage: Coverage, scope: SourceScope) {
+    pub fn codegen_coverage(&self, bx: &mut Bx, coverage: &Coverage, scope: SourceScope) {
         // Determine the instance that coverage data was originally generated for.
         let instance = if let Some(inlined) = scope.inlined_instance(&self.mir.source_scopes) {
             self.monomorphize(inlined)
@@ -15,41 +14,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             self.instance
         };
 
-        let Coverage { kind, code_region } = coverage;
-        match kind {
-            CoverageKind::Counter { function_source_hash, id } => {
-                if bx.set_function_source_hash(instance, function_source_hash) {
-                    // If `set_function_source_hash()` returned true, the coverage map is enabled,
-                    // so continue adding the counter.
-                    if let Some(code_region) = code_region {
-                        // Note: Some counters do not have code regions, but may still be referenced
-                        // from expressions. In that case, don't add the counter to the coverage map,
-                        // but do inject the counter intrinsic.
-                        bx.add_coverage_counter(instance, id, code_region);
-                    }
-
-                    let coverageinfo = bx.tcx().coverageinfo(instance.def);
-
-                    let fn_name = bx.get_pgo_func_name_var(instance);
-                    let hash = bx.const_u64(function_source_hash);
-                    let num_counters = bx.const_u32(coverageinfo.num_counters);
-                    let index = bx.const_u32(id.zero_based_index());
-                    debug!(
-                        "codegen intrinsic instrprof.increment(fn_name={:?}, hash={:?}, num_counters={:?}, index={:?})",
-                        fn_name, hash, num_counters, index,
-                    );
-                    bx.instrprof_increment(fn_name, hash, num_counters, index);
-                }
-            }
-            CoverageKind::Expression { id, lhs, op, rhs } => {
-                bx.add_coverage_counter_expression(instance, id, lhs, op, rhs, code_region);
-            }
-            CoverageKind::Unreachable => {
-                bx.add_coverage_unreachable(
-                    instance,
-                    code_region.expect("unreachable regions always have code regions"),
-                );
-            }
-        }
+        // Handle the coverage info in a backend-specific way.
+        bx.add_coverage(instance, coverage);
     }
 }
diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
index c6589a40392..1ee89b3d5e8 100644
--- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
@@ -5,6 +5,7 @@ use rustc_middle::mir;
 use rustc_middle::ty;
 use rustc_middle::ty::layout::TyAndLayout;
 use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
+use rustc_middle::ty::Ty;
 use rustc_session::config::DebugInfo;
 use rustc_span::symbol::{kw, Symbol};
 use rustc_span::{BytePos, Span};
@@ -421,9 +422,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
 
         let create_alloca = |bx: &mut Bx, place: PlaceRef<'tcx, Bx::Value>, refcount| {
             // Create a variable which will be a pointer to the actual value
-            let ptr_ty = bx
-                .tcx()
-                .mk_ptr(ty::TypeAndMut { mutbl: mir::Mutability::Mut, ty: place.layout.ty });
+            let ptr_ty = Ty::new_ptr(
+                bx.tcx(),
+                ty::TypeAndMut { mutbl: mir::Mutability::Mut, ty: place.layout.ty },
+            );
             let ptr_layout = bx.layout_of(ptr_ty);
             let alloca = PlaceRef::alloca(bx, ptr_layout);
             bx.set_var_name(alloca.llval, &format!("{}.ref{}.dbg.spill", var.name, refcount));
@@ -525,8 +527,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 };
 
                 for _ in 0..var.references {
-                    var_ty =
-                        bx.tcx().mk_ptr(ty::TypeAndMut { mutbl: mir::Mutability::Mut, ty: var_ty });
+                    var_ty = Ty::new_ptr(
+                        bx.tcx(),
+                        ty::TypeAndMut { mutbl: mir::Mutability::Mut, ty: var_ty },
+                    );
                 }
 
                 self.cx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span)
diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs
index a58a61cd567..ab493ae5c1f 100644
--- a/compiler/rustc_codegen_ssa/src/mir/place.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/place.rs
@@ -61,7 +61,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
         layout: TyAndLayout<'tcx>,
     ) -> Self {
         assert!(layout.is_unsized(), "tried to allocate indirect place for sized values");
-        let ptr_ty = bx.cx().tcx().mk_mut_ptr(layout.ty);
+        let ptr_ty = Ty::new_mut_ptr(bx.cx().tcx(), layout.ty);
         let ptr_layout = bx.cx().layout_of(ptr_ty);
         Self::alloca(bx, ptr_layout)
     }
diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
index 2d3d0ec68b8..ab9eb421c5a 100644
--- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
@@ -581,7 +581,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
 
             mir::Rvalue::Ref(_, bk, place) => {
                 let mk_ref = move |tcx: TyCtxt<'tcx>, ty: Ty<'tcx>| {
-                    tcx.mk_ref(
+                    Ty::new_ref(
+                        tcx,
                         tcx.lifetimes.re_erased,
                         ty::TypeAndMut { ty, mutbl: bk.to_mutbl_lossy() },
                     )
@@ -592,7 +593,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             mir::Rvalue::CopyForDeref(place) => self.codegen_operand(bx, &Operand::Copy(place)),
             mir::Rvalue::AddressOf(mutability, place) => {
                 let mk_ptr = move |tcx: TyCtxt<'tcx>, ty: Ty<'tcx>| {
-                    tcx.mk_ptr(ty::TypeAndMut { ty, mutbl: mutability })
+                    Ty::new_ptr(tcx, ty::TypeAndMut { ty, mutbl: mutability })
                 };
                 self.codegen_place_to_pointer(bx, place, mk_ptr)
             }
@@ -644,7 +645,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     lhs.layout.ty,
                 );
                 let val_ty = op.ty(bx.tcx(), lhs.layout.ty, rhs.layout.ty);
-                let operand_ty = bx.tcx().mk_tup(&[val_ty, bx.tcx().types.bool]);
+                let operand_ty = Ty::new_tup(bx.tcx(), &[val_ty, bx.tcx().types.bool]);
                 OperandRef { val: result, layout: bx.cx().layout_of(operand_ty) }
             }
 
@@ -734,7 +735,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 let lloperand = operand.immediate();
 
                 let content_ty = self.monomorphize(content_ty);
-                let box_layout = bx.cx().layout_of(bx.tcx().mk_box(content_ty));
+                let box_layout = bx.cx().layout_of(Ty::new_box(bx.tcx(), content_ty));
                 let llty_ptr = bx.cx().backend_type(box_layout);
 
                 let val = bx.pointercast(lloperand, llty_ptr);
diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs
index 314d364c0c2..899e41265bb 100644
--- a/compiler/rustc_codegen_ssa/src/mir/statement.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs
@@ -65,7 +65,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 }
             }
             mir::StatementKind::Coverage(box ref coverage) => {
-                self.codegen_coverage(bx, coverage.clone(), statement.source_info.scope);
+                self.codegen_coverage(bx, coverage, statement.source_info.scope);
             }
             mir::StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(ref op)) => {
                 let op_val = self.codegen_operand(bx, op);
diff --git a/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs b/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs
index e77201cf0c8..7e8de0ddc5b 100644
--- a/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs
@@ -1,57 +1,11 @@
 use super::BackendTypes;
-use rustc_hir::def_id::DefId;
-use rustc_middle::mir::coverage::*;
+use rustc_middle::mir::Coverage;
 use rustc_middle::ty::Instance;
 
-pub trait CoverageInfoMethods<'tcx>: BackendTypes {
-    fn coverageinfo_finalize(&self);
-
-    /// Codegen a small function that will never be called, with one counter
-    /// that will never be incremented, that gives LLVM coverage tools a
-    /// function definition it needs in order to resolve coverage map references
-    /// to unused functions. This is necessary so unused functions will appear
-    /// as uncovered (coverage execution count `0`) in LLVM coverage reports.
-    fn define_unused_fn(&self, def_id: DefId);
-
-    /// For LLVM codegen, returns a function-specific `Value` for a global
-    /// string, to hold the function name passed to LLVM intrinsic
-    /// `instrprof.increment()`. The `Value` is only created once per instance.
-    /// Multiple invocations with the same instance return the same `Value`.
-    fn get_pgo_func_name_var(&self, instance: Instance<'tcx>) -> Self::Value;
-}
-
 pub trait CoverageInfoBuilderMethods<'tcx>: BackendTypes {
-    /// Returns true if the function source hash was added to the coverage map (even if it had
-    /// already been added, for this instance). Returns false *only* if `-C instrument-coverage` is
-    /// not enabled (a coverage map is not being generated).
-    fn set_function_source_hash(
-        &mut self,
-        instance: Instance<'tcx>,
-        function_source_hash: u64,
-    ) -> bool;
-
-    /// Returns true if the counter was added to the coverage map; false if `-C instrument-coverage`
-    /// is not enabled (a coverage map is not being generated).
-    fn add_coverage_counter(
-        &mut self,
-        instance: Instance<'tcx>,
-        index: CounterValueReference,
-        region: CodeRegion,
-    ) -> bool;
-
-    /// Returns true if the expression was added to the coverage map; false if
-    /// `-C instrument-coverage` is not enabled (a coverage map is not being generated).
-    fn add_coverage_counter_expression(
-        &mut self,
-        instance: Instance<'tcx>,
-        id: InjectedExpressionId,
-        lhs: ExpressionOperandId,
-        op: Op,
-        rhs: ExpressionOperandId,
-        region: Option<CodeRegion>,
-    ) -> bool;
-
-    /// Returns true if the region was added to the coverage map; false if `-C instrument-coverage`
-    /// is not enabled (a coverage map is not being generated).
-    fn add_coverage_unreachable(&mut self, instance: Instance<'tcx>, region: CodeRegion) -> bool;
+    /// Handle the MIR coverage info in a backend-specific way.
+    ///
+    /// This can potentially be a no-op in backends that don't support
+    /// coverage instrumentation.
+    fn add_coverage(&mut self, instance: Instance<'tcx>, coverage: &Coverage);
 }
diff --git a/compiler/rustc_codegen_ssa/src/traits/mod.rs b/compiler/rustc_codegen_ssa/src/traits/mod.rs
index 782fdadbfb8..8cb58bd4c70 100644
--- a/compiler/rustc_codegen_ssa/src/traits/mod.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/mod.rs
@@ -33,7 +33,7 @@ pub use self::asm::{AsmBuilderMethods, AsmMethods, GlobalAsmOperandRef, InlineAs
 pub use self::backend::{Backend, BackendTypes, CodegenBackend, ExtraBackendMethods};
 pub use self::builder::{BuilderMethods, OverflowOp};
 pub use self::consts::ConstMethods;
-pub use self::coverageinfo::{CoverageInfoBuilderMethods, CoverageInfoMethods};
+pub use self::coverageinfo::CoverageInfoBuilderMethods;
 pub use self::debuginfo::{DebugInfoBuilderMethods, DebugInfoMethods};
 pub use self::declare::PreDefineMethods;
 pub use self::intrinsic::IntrinsicCallMethods;
@@ -59,7 +59,6 @@ pub trait CodegenMethods<'tcx>:
     + MiscMethods<'tcx>
     + ConstMethods<'tcx>
     + StaticMethods
-    + CoverageInfoMethods<'tcx>
     + DebugInfoMethods<'tcx>
     + AsmMethods<'tcx>
     + PreDefineMethods<'tcx>
@@ -75,7 +74,6 @@ impl<'tcx, T> CodegenMethods<'tcx> for T where
         + MiscMethods<'tcx>
         + ConstMethods<'tcx>
         + StaticMethods
-        + CoverageInfoMethods<'tcx>
         + DebugInfoMethods<'tcx>
         + AsmMethods<'tcx>
         + PreDefineMethods<'tcx>
diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs
index 5cc1fa2a497..a3064b53db1 100644
--- a/compiler/rustc_const_eval/src/const_eval/mod.rs
+++ b/compiler/rustc_const_eval/src/const_eval/mod.rs
@@ -1,12 +1,10 @@
 // Not in interpret to make sure we do not use private implementation details
 
 use crate::errors::MaxNumNodesInConstErr;
-use crate::interpret::{
-    intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, InterpResult, Scalar,
-};
+use crate::interpret::{intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, Scalar};
 use rustc_middle::mir;
 use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId};
-use rustc_middle::ty::{self, TyCtxt};
+use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_span::{source_map::DUMMY_SP, symbol::Symbol};
 
 mod error;
@@ -87,23 +85,24 @@ pub(crate) fn eval_to_valtree<'tcx>(
 }
 
 #[instrument(skip(tcx), level = "debug")]
-pub(crate) fn try_destructure_mir_constant<'tcx>(
+pub(crate) fn try_destructure_mir_constant_for_diagnostics<'tcx>(
     tcx: TyCtxt<'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
-    val: mir::ConstantKind<'tcx>,
-) -> InterpResult<'tcx, mir::DestructuredConstant<'tcx>> {
+    val: ConstValue<'tcx>,
+    ty: Ty<'tcx>,
+) -> Option<mir::DestructuredConstant<'tcx>> {
+    let param_env = ty::ParamEnv::reveal_all();
     let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, CanAccessStatics::No);
-    let op = ecx.eval_mir_constant(&val, None, None)?;
+    let op = ecx.const_val_to_op(val, ty, None).ok()?;
 
     // We go to `usize` as we cannot allocate anything bigger anyway.
-    let (field_count, variant, down) = match val.ty().kind() {
+    let (field_count, variant, down) = match ty.kind() {
         ty::Array(_, len) => (len.eval_target_usize(tcx, param_env) as usize, None, op),
         ty::Adt(def, _) if def.variants().is_empty() => {
-            throw_ub!(Unreachable)
+            return None;
         }
         ty::Adt(def, _) => {
-            let variant = ecx.read_discriminant(&op)?.1;
-            let down = ecx.operand_downcast(&op, variant)?;
+            let variant = ecx.read_discriminant(&op).ok()?.1;
+            let down = ecx.operand_downcast(&op, variant).ok()?;
             (def.variants()[variant].fields.len(), Some(variant), down)
         }
         ty::Tuple(substs) => (substs.len(), None, op),
@@ -112,12 +111,12 @@ pub(crate) fn try_destructure_mir_constant<'tcx>(
 
     let fields_iter = (0..field_count)
         .map(|i| {
-            let field_op = ecx.operand_field(&down, i)?;
+            let field_op = ecx.operand_field(&down, i).ok()?;
             let val = op_to_const(&ecx, &field_op);
-            Ok(mir::ConstantKind::Val(val, field_op.layout.ty))
+            Some((val, field_op.layout.ty))
         })
-        .collect::<InterpResult<'tcx, Vec<_>>>()?;
+        .collect::<Option<Vec<_>>>()?;
     let fields = tcx.arena.alloc_from_iter(fields_iter);
 
-    Ok(mir::DestructuredConstant { variant, fields })
+    Some(mir::DestructuredConstant { variant, fields })
 }
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index 4f10e4837ce..ed64a7655b5 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -170,7 +170,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                     sym::pref_align_of | sym::variant_count => self.tcx.types.usize,
                     sym::needs_drop => self.tcx.types.bool,
                     sym::type_id => self.tcx.types.u128,
-                    sym::type_name => self.tcx.mk_static_str(),
+                    sym::type_name => Ty::new_static_str(self.tcx.tcx),
                     _ => bug!(),
                 };
                 let val = self.ctfe_query(None, |tcx| {
diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs
index e30af165501..5f89d652fab 100644
--- a/compiler/rustc_const_eval/src/interpret/operand.rs
+++ b/compiler/rustc_const_eval/src/interpret/operand.rs
@@ -106,7 +106,7 @@ impl<Prov: Provenance> std::fmt::Display for ImmTy<'_, Prov> {
                     // Just print the ptr value. `pretty_print_const_scalar_ptr` would also try to
                     // print what is points to, which would fail since it has no access to the local
                     // memory.
-                    cx.pretty_print_const_pointer(ptr, ty, true)
+                    cx.pretty_print_const_pointer(ptr, ty)
                 }
             }
         }
@@ -633,7 +633,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         }
     }
 
-    pub(super) fn const_val_to_op(
+    pub(crate) fn const_val_to_op(
         &self,
         val_val: ConstValue<'tcx>,
         ty: Ty<'tcx>,
diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs
index 7bca7efdf5a..e04764636cc 100644
--- a/compiler/rustc_const_eval/src/interpret/operator.rs
+++ b/compiler/rustc_const_eval/src/interpret/operator.rs
@@ -22,7 +22,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     ) -> InterpResult<'tcx> {
         let (val, overflowed, ty) = self.overflowing_binary_op(op, &left, &right)?;
         debug_assert_eq!(
-            self.tcx.mk_tup(&[ty, self.tcx.types.bool]),
+            Ty::new_tup(self.tcx.tcx, &[ty, self.tcx.types.bool]),
             dest.layout.ty,
             "type mismatch for result of {:?}",
             op,
diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs
index 2a31a59ad6c..24c1fe43d0c 100644
--- a/compiler/rustc_const_eval/src/interpret/place.rs
+++ b/compiler/rustc_const_eval/src/interpret/place.rs
@@ -9,6 +9,7 @@ use rustc_index::IndexSlice;
 use rustc_middle::mir;
 use rustc_middle::ty;
 use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
+use rustc_middle::ty::Ty;
 use rustc_target::abi::{self, Abi, Align, FieldIdx, HasDataLayout, Size, FIRST_VARIANT};
 
 use super::{
@@ -395,7 +396,7 @@ where
         // (Transmuting is okay since this is an in-memory place. We also double-check the size
         // stays the same.)
         let (len, e_ty) = mplace.layout.ty.simd_size_and_type(*self.tcx);
-        let array = self.tcx.mk_array(e_ty, len);
+        let array = Ty::new_array(self.tcx.tcx, e_ty, len);
         let layout = self.layout_of(array)?;
         assert_eq!(layout.size, mplace.layout.size);
         Ok((MPlaceTy { layout, ..*mplace }, len))
@@ -775,7 +776,8 @@ where
         let meta = Scalar::from_target_usize(u64::try_from(str.len()).unwrap(), self);
         let mplace = MemPlace { ptr: ptr.into(), meta: MemPlaceMeta::Meta(meta) };
 
-        let ty = self.tcx.mk_ref(
+        let ty = Ty::new_ref(
+            self.tcx.tcx,
             self.tcx.lifetimes.re_static,
             ty::TypeAndMut { ty: self.tcx.types.str_, mutbl },
         );
diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs
index 91da930db4f..d7d31fe1887 100644
--- a/compiler/rustc_const_eval/src/interpret/projection.rs
+++ b/compiler/rustc_const_eval/src/interpret/projection.rs
@@ -12,6 +12,7 @@ use either::{Left, Right};
 use rustc_middle::mir;
 use rustc_middle::ty;
 use rustc_middle::ty::layout::LayoutOf;
+use rustc_middle::ty::Ty;
 use rustc_target::abi::{self, Abi, VariantIdx};
 
 use super::{
@@ -317,7 +318,9 @@ where
         let (meta, ty) = match base.layout.ty.kind() {
             // It is not nice to match on the type, but that seems to be the only way to
             // implement this.
-            ty::Array(inner, _) => (MemPlaceMeta::None, self.tcx.mk_array(*inner, inner_len)),
+            ty::Array(inner, _) => {
+                (MemPlaceMeta::None, Ty::new_array(self.tcx.tcx, *inner, inner_len))
+            }
             ty::Slice(..) => {
                 let len = Scalar::from_target_usize(inner_len, self);
                 (MemPlaceMeta::Meta(len), base.layout.ty)
diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs
index 719d8a14b41..15823a5975e 100644
--- a/compiler/rustc_const_eval/src/interpret/terminator.rs
+++ b/compiler/rustc_const_eval/src/interpret/terminator.rs
@@ -650,7 +650,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 // Adjust receiver argument. Layout can be any (thin) ptr.
                 args[0] = ImmTy::from_immediate(
                     Scalar::from_maybe_pointer(adjusted_receiver, self).into(),
-                    self.layout_of(self.tcx.mk_mut_ptr(dyn_ty))?,
+                    self.layout_of(Ty::new_mut_ptr(self.tcx.tcx, dyn_ty))?,
                 )
                 .into();
                 trace!("Patched receiver operand to {:#?}", args[0]);
@@ -703,7 +703,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
         let arg = ImmTy::from_immediate(
             place.to_ref(self),
-            self.layout_of(self.tcx.mk_mut_ptr(place.layout.ty))?,
+            self.layout_of(Ty::new_mut_ptr(self.tcx.tcx, place.layout.ty))?,
         );
         let ret = MPlaceTy::fake_alloc_zst(self.layout_of(self.tcx.types.unit)?);
 
diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs
index 8314f53ba57..c126f749bf3 100644
--- a/compiler/rustc_const_eval/src/lib.rs
+++ b/compiler/rustc_const_eval/src/lib.rs
@@ -52,10 +52,8 @@ pub fn provide(providers: &mut Providers) {
         let (param_env, raw) = param_env_and_value.into_parts();
         const_eval::eval_to_valtree(tcx, param_env, raw)
     };
-    providers.try_destructure_mir_constant = |tcx, param_env_and_value| {
-        let (param_env, value) = param_env_and_value.into_parts();
-        const_eval::try_destructure_mir_constant(tcx, param_env, value).ok()
-    };
+    providers.try_destructure_mir_constant_for_diagnostics =
+        |tcx, (cv, ty)| const_eval::try_destructure_mir_constant_for_diagnostics(tcx, cv, ty);
     providers.valtree_to_const_val = |tcx, (ty, valtree)| {
         const_eval::valtree_to_const_value(tcx, ty::ParamEnv::empty().and(ty), valtree)
     };
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
index 36c76e53231..015a4aa94cd 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
@@ -223,7 +223,7 @@ impl Qualif for CustomEq {
         def: AdtDef<'tcx>,
         substs: SubstsRef<'tcx>,
     ) -> bool {
-        let ty = cx.tcx.mk_adt(def, substs);
+        let ty = Ty::new_adt(cx.tcx, def, substs);
         !ty.is_structural_eq_shallow(cx.tcx)
     }
 }
diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs
index dd80f745c2f..1b39a76e460 100644
--- a/compiler/rustc_const_eval/src/transform/promote_consts.rs
+++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs
@@ -17,7 +17,7 @@ use rustc_middle::mir;
 use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor};
 use rustc_middle::mir::*;
 use rustc_middle::ty::subst::InternalSubsts;
-use rustc_middle::ty::{self, List, TyCtxt, TypeVisitableExt};
+use rustc_middle::ty::{self, List, Ty, TyCtxt, TypeVisitableExt};
 use rustc_span::Span;
 
 use rustc_index::{Idx, IndexSlice, IndexVec};
@@ -867,7 +867,8 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
             let ty = local_decls[place.local].ty;
             let span = statement.source_info.span;
 
-            let ref_ty = tcx.mk_ref(
+            let ref_ty = Ty::new_ref(
+                tcx,
                 tcx.lifetimes.re_erased,
                 ty::TypeAndMut { ty, mutbl: borrow_kind.to_mutbl_lossy() },
             );
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index 7ed73a3f6fe..2a6c341b662 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -67,8 +67,8 @@ impl<'tcx> MirPass<'tcx> for Validator {
             unwind_edge_count: 0,
             reachable_blocks: traversal::reachable_as_bitset(body),
             storage_liveness,
-            place_cache: Vec::new(),
-            value_cache: Vec::new(),
+            place_cache: FxHashSet::default(),
+            value_cache: FxHashSet::default(),
         };
         checker.visit_body(body);
         checker.check_cleanup_control_flow();
@@ -95,8 +95,8 @@ struct TypeChecker<'a, 'tcx> {
     unwind_edge_count: usize,
     reachable_blocks: BitSet<BasicBlock>,
     storage_liveness: ResultsCursor<'a, 'tcx, MaybeStorageLive<'static>>,
-    place_cache: Vec<PlaceRef<'tcx>>,
-    value_cache: Vec<u128>,
+    place_cache: FxHashSet<PlaceRef<'tcx>>,
+    value_cache: FxHashSet<u128>,
 }
 
 impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
@@ -951,10 +951,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
 
                 self.value_cache.clear();
                 self.value_cache.extend(targets.iter().map(|(value, _)| value));
-                let all_len = self.value_cache.len();
-                self.value_cache.sort_unstable();
-                self.value_cache.dedup();
-                let has_duplicates = all_len != self.value_cache.len();
+                let has_duplicates = targets.iter().len() != self.value_cache.len();
                 if has_duplicates {
                     self.fail(
                         location,
@@ -987,16 +984,14 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                 // passed by a reference to the callee. Consequently they must be non-overlapping.
                 // Currently this simply checks for duplicate places.
                 self.place_cache.clear();
-                self.place_cache.push(destination.as_ref());
+                self.place_cache.insert(destination.as_ref());
+                let mut has_duplicates = false;
                 for arg in args {
                     if let Operand::Move(place) = arg {
-                        self.place_cache.push(place.as_ref());
+                        has_duplicates |= !self.place_cache.insert(place.as_ref());
                     }
                 }
-                let all_len = self.place_cache.len();
-                let mut dedup = FxHashSet::default();
-                self.place_cache.retain(|p| dedup.insert(*p));
-                let has_duplicates = all_len != self.place_cache.len();
+
                 if has_duplicates {
                     self.fail(
                         location,
diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml
index 78f73d193e3..a5c3cb3f857 100644
--- a/compiler/rustc_data_structures/Cargo.toml
+++ b/compiler/rustc_data_structures/Cargo.toml
@@ -10,7 +10,7 @@ arrayvec = { version = "0.7", default-features = false }
 bitflags = "1.2.1"
 cfg-if = "1.0"
 ena = "0.14.2"
-indexmap = { version = "1.9.3" }
+indexmap = { version = "2.0.0" }
 jobserver_crate = { version = "0.1.13", package = "jobserver" }
 libc = "0.2"
 measureme = "10.0.0"
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index 4b4573ec2eb..9352fe3147e 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -24,6 +24,7 @@ use rustc_data_structures::profiling::{
 };
 use rustc_data_structures::sync::SeqCst;
 use rustc_errors::registry::{InvalidErrorCode, Registry};
+use rustc_errors::{markdown, ColorConfig};
 use rustc_errors::{
     DiagnosticMessage, ErrorGuaranteed, Handler, PResult, SubdiagnosticMessage, TerminalUrl,
 };
@@ -282,7 +283,7 @@ fn run_compiler(
     interface::set_thread_safe_mode(&sopts.unstable_opts);
 
     if let Some(ref code) = matches.opt_str("explain") {
-        handle_explain(&early_error_handler, diagnostics_registry(), code);
+        handle_explain(&early_error_handler, diagnostics_registry(), code, sopts.color);
         return Ok(());
     }
 
@@ -540,7 +541,7 @@ impl Compilation {
     }
 }
 
-fn handle_explain(handler: &EarlyErrorHandler, registry: Registry, code: &str) {
+fn handle_explain(handler: &EarlyErrorHandler, registry: Registry, code: &str, color: ColorConfig) {
     let upper_cased_code = code.to_ascii_uppercase();
     let normalised =
         if upper_cased_code.starts_with('E') { upper_cased_code } else { format!("E{code:0>4}") };
@@ -564,7 +565,7 @@ fn handle_explain(handler: &EarlyErrorHandler, registry: Registry, code: &str) {
                 text.push('\n');
             }
             if io::stdout().is_terminal() {
-                show_content_with_pager(&text);
+                show_md_content_with_pager(&text, color);
             } else {
                 safe_print!("{text}");
             }
@@ -575,34 +576,72 @@ fn handle_explain(handler: &EarlyErrorHandler, registry: Registry, code: &str) {
     }
 }
 
-fn show_content_with_pager(content: &str) {
+/// If color is always or auto, print formatted & colorized markdown. If color is never or
+/// if formatted printing fails, print the raw text.
+///
+/// Prefers a pager, falls back standard print
+fn show_md_content_with_pager(content: &str, color: ColorConfig) {
+    let mut fallback_to_println = false;
     let pager_name = env::var_os("PAGER").unwrap_or_else(|| {
         if cfg!(windows) { OsString::from("more.com") } else { OsString::from("less") }
     });
 
-    let mut fallback_to_println = false;
+    let mut cmd = Command::new(&pager_name);
+    // FIXME: find if other pagers accept color options
+    let mut print_formatted = if pager_name == "less" {
+        cmd.arg("-r");
+        true
+    } else if ["bat", "catbat", "delta"].iter().any(|v| *v == pager_name) {
+        true
+    } else {
+        false
+    };
 
-    match Command::new(pager_name).stdin(Stdio::piped()).spawn() {
-        Ok(mut pager) => {
-            if let Some(pipe) = pager.stdin.as_mut() {
-                if pipe.write_all(content.as_bytes()).is_err() {
-                    fallback_to_println = true;
-                }
-            }
+    if color == ColorConfig::Never {
+        print_formatted = false;
+    } else if color == ColorConfig::Always {
+        print_formatted = true;
+    }
+
+    let mdstream = markdown::MdStream::parse_str(content);
+    let bufwtr = markdown::create_stdout_bufwtr();
+    let mut mdbuf = bufwtr.buffer();
+    if mdstream.write_termcolor_buf(&mut mdbuf).is_err() {
+        print_formatted = false;
+    }
 
-            if pager.wait().is_err() {
+    if let Ok(mut pager) = cmd.stdin(Stdio::piped()).spawn() {
+        if let Some(pipe) = pager.stdin.as_mut() {
+            let res = if print_formatted {
+                pipe.write_all(mdbuf.as_slice())
+            } else {
+                pipe.write_all(content.as_bytes())
+            };
+
+            if res.is_err() {
                 fallback_to_println = true;
             }
         }
-        Err(_) => {
+
+        if pager.wait().is_err() {
             fallback_to_println = true;
         }
+    } else {
+        fallback_to_println = true;
     }
 
     // If pager fails for whatever reason, we should still print the content
     // to standard output
     if fallback_to_println {
-        safe_print!("{content}");
+        let fmt_success = match color {
+            ColorConfig::Auto => io::stdout().is_terminal() && bufwtr.print(&mdbuf).is_ok(),
+            ColorConfig::Always => bufwtr.print(&mdbuf).is_ok(),
+            ColorConfig::Never => false,
+        };
+
+        if !fmt_success {
+            safe_print!("{content}");
+        }
     }
 }
 
diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml
index bd3033fcb3e..e8bcd7c1184 100644
--- a/compiler/rustc_errors/Cargo.toml
+++ b/compiler/rustc_errors/Cargo.toml
@@ -20,7 +20,7 @@ rustc_hir = { path = "../rustc_hir" }
 rustc_lint_defs = { path = "../rustc_lint_defs" }
 rustc_type_ir = { path = "../rustc_type_ir" }
 unicode-width = "0.1.4"
-termcolor = "1.0"
+termcolor = "1.2.0"
 annotate-snippets = "0.9"
 termize = "0.1.1"
 serde = { version = "1.0.125", features = [ "derive" ] }
diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs
index d8c997b49a1..9d4d159fd96 100644
--- a/compiler/rustc_errors/src/emitter.rs
+++ b/compiler/rustc_errors/src/emitter.rs
@@ -616,7 +616,7 @@ pub enum ColorConfig {
 }
 
 impl ColorConfig {
-    fn to_color_choice(self) -> ColorChoice {
+    pub fn to_color_choice(self) -> ColorChoice {
         match self {
             ColorConfig::Always => {
                 if io::stderr().is_terminal() {
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 24d1cc8af82..b9db25103a3 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -61,6 +61,7 @@ pub mod emitter;
 pub mod error;
 pub mod json;
 mod lock;
+pub mod markdown;
 pub mod registry;
 mod snippet;
 mod styled_buffer;
diff --git a/compiler/rustc_errors/src/markdown/mod.rs b/compiler/rustc_errors/src/markdown/mod.rs
new file mode 100644
index 00000000000..53b766dfcce
--- /dev/null
+++ b/compiler/rustc_errors/src/markdown/mod.rs
@@ -0,0 +1,76 @@
+//! A simple markdown parser that can write formatted text to the terminal
+//!
+//! Entrypoint is `MdStream::parse_str(...)`
+use std::io;
+
+use termcolor::{Buffer, BufferWriter, ColorChoice};
+mod parse;
+mod term;
+
+/// An AST representation of a Markdown document
+#[derive(Clone, Debug, Default, PartialEq)]
+pub struct MdStream<'a>(Vec<MdTree<'a>>);
+
+impl<'a> MdStream<'a> {
+    /// Parse a markdown string to a tokenstream
+    #[must_use]
+    pub fn parse_str(s: &str) -> MdStream<'_> {
+        parse::entrypoint(s)
+    }
+
+    /// Write formatted output to a termcolor buffer
+    pub fn write_termcolor_buf(&self, buf: &mut Buffer) -> io::Result<()> {
+        term::entrypoint(self, buf)
+    }
+}
+
+/// Create a termcolor buffer with the `Always` color choice
+pub fn create_stdout_bufwtr() -> BufferWriter {
+    BufferWriter::stdout(ColorChoice::Always)
+}
+
+/// A single tokentree within a Markdown document
+#[derive(Clone, Debug, PartialEq)]
+pub enum MdTree<'a> {
+    /// Leaf types
+    Comment(&'a str),
+    CodeBlock {
+        txt: &'a str,
+        lang: Option<&'a str>,
+    },
+    CodeInline(&'a str),
+    Strong(&'a str),
+    Emphasis(&'a str),
+    Strikethrough(&'a str),
+    PlainText(&'a str),
+    /// [Foo](www.foo.com) or simple anchor <www.foo.com>
+    Link {
+        disp: &'a str,
+        link: &'a str,
+    },
+    /// `[Foo link][ref]`
+    RefLink {
+        disp: &'a str,
+        id: Option<&'a str>,
+    },
+    /// [ref]: www.foo.com
+    LinkDef {
+        id: &'a str,
+        link: &'a str,
+    },
+    /// Break bewtween two paragraphs (double `\n`), not directly parsed but
+    /// added later
+    ParagraphBreak,
+    /// Break bewtween two lines (single `\n`)
+    LineBreak,
+    HorizontalRule,
+    Heading(u8, MdStream<'a>),
+    OrderedListItem(u16, MdStream<'a>),
+    UnorderedListItem(MdStream<'a>),
+}
+
+impl<'a> From<Vec<MdTree<'a>>> for MdStream<'a> {
+    fn from(value: Vec<MdTree<'a>>) -> Self {
+        Self(value)
+    }
+}
diff --git a/compiler/rustc_errors/src/markdown/parse.rs b/compiler/rustc_errors/src/markdown/parse.rs
new file mode 100644
index 00000000000..362a451fde6
--- /dev/null
+++ b/compiler/rustc_errors/src/markdown/parse.rs
@@ -0,0 +1,588 @@
+use crate::markdown::{MdStream, MdTree};
+use std::{iter, mem, str};
+
+/// Short aliases that we can use in match patterns. If an end pattern is not
+/// included, this type may be variable
+const ANC_E: &[u8] = b">";
+const ANC_S: &[u8] = b"<";
+const BRK: &[u8] = b"---";
+const CBK: &[u8] = b"```";
+const CIL: &[u8] = b"`";
+const CMT_E: &[u8] = b"-->";
+const CMT_S: &[u8] = b"<!--";
+const EMP: &[u8] = b"_";
+const HDG: &[u8] = b"#";
+const LNK_CHARS: &str = "$-_.+!*'()/&?=:%";
+const LNK_E: &[u8] = b"]";
+const LNK_S: &[u8] = b"[";
+const STG: &[u8] = b"**";
+const STK: &[u8] = b"~~";
+const UL1: &[u8] = b"* ";
+const UL2: &[u8] = b"- ";
+
+/// Pattern replacements
+const REPLACEMENTS: &[(&str, &str)] = &[
+    ("(c)", "©"),
+    ("(C)", "©"),
+    ("(r)", "®"),
+    ("(R)", "®"),
+    ("(tm)", "â„¢"),
+    ("(TM)", "â„¢"),
+    (":crab:", "🦀"),
+    ("\n", " "),
+];
+
+/// `(extracted, remaining)`
+type Parsed<'a> = (MdTree<'a>, &'a [u8]);
+/// Output of a parse function
+type ParseResult<'a> = Option<Parsed<'a>>;
+
+/// Parsing context
+#[derive(Clone, Copy, Debug, PartialEq)]
+struct Context {
+    /// If true, we are at a the topmost level (not recursing a nested tt)
+    top_block: bool,
+    /// Previous character
+    prev: Prev,
+}
+
+/// Character class preceding this one
+#[derive(Clone, Copy, Debug, PartialEq)]
+enum Prev {
+    Newline,
+    /// Whitespace that is not a newline
+    Whitespace,
+    Escape,
+    Any,
+}
+
+impl Default for Context {
+    /// Most common setting for non top-level parsing: not top block, not at
+    /// line start (yes leading whitespace, not escaped)
+    fn default() -> Self {
+        Self { top_block: false, prev: Prev::Whitespace }
+    }
+}
+
+/// Flags to simple parser function
+#[derive(Clone, Copy, Debug, PartialEq)]
+enum ParseOpt {
+    /// Ignore escapes before closing pattern, trim content
+    TrimNoEsc,
+    None,
+}
+
+/// Parse a buffer
+pub fn entrypoint(txt: &str) -> MdStream<'_> {
+    let ctx = Context { top_block: true, prev: Prev::Newline };
+    normalize(parse_recursive(txt.trim().as_bytes(), ctx), &mut Vec::new())
+}
+
+/// Parse a buffer with specified context
+fn parse_recursive<'a>(buf: &'a [u8], ctx: Context) -> MdStream<'_> {
+    use ParseOpt as Po;
+    use Prev::{Escape, Newline, Whitespace};
+
+    let mut stream: Vec<MdTree<'a>> = Vec::new();
+    let Context { top_block: top_blk, mut prev } = ctx;
+
+    // wip_buf is our entire unprocessed (unpushed) buffer, loop_buf is our to
+    // check buffer that shrinks with each loop
+    let mut wip_buf = buf;
+    let mut loop_buf = wip_buf;
+
+    while !loop_buf.is_empty() {
+        let next_prev = match loop_buf[0] {
+            b'\n' => Newline,
+            b'\\' => Escape,
+            x if x.is_ascii_whitespace() => Whitespace,
+            _ => Prev::Any,
+        };
+
+        let res: ParseResult<'_> = match (top_blk, prev) {
+            (_, Newline | Whitespace) if loop_buf.starts_with(CMT_S) => {
+                parse_simple_pat(loop_buf, CMT_S, CMT_E, Po::TrimNoEsc, MdTree::Comment)
+            }
+            (true, Newline) if loop_buf.starts_with(CBK) => Some(parse_codeblock(loop_buf)),
+            (_, Newline | Whitespace) if loop_buf.starts_with(CIL) => parse_codeinline(loop_buf),
+            (true, Newline | Whitespace) if loop_buf.starts_with(HDG) => parse_heading(loop_buf),
+            (true, Newline) if loop_buf.starts_with(BRK) => {
+                Some((MdTree::HorizontalRule, parse_to_newline(loop_buf).1))
+            }
+            (_, Newline | Whitespace) if loop_buf.starts_with(EMP) => {
+                parse_simple_pat(loop_buf, EMP, EMP, Po::None, MdTree::Emphasis)
+            }
+            (_, Newline | Whitespace) if loop_buf.starts_with(STG) => {
+                parse_simple_pat(loop_buf, STG, STG, Po::None, MdTree::Strong)
+            }
+            (_, Newline | Whitespace) if loop_buf.starts_with(STK) => {
+                parse_simple_pat(loop_buf, STK, STK, Po::None, MdTree::Strikethrough)
+            }
+            (_, Newline | Whitespace) if loop_buf.starts_with(ANC_S) => {
+                let tt_fn = |link| MdTree::Link { disp: link, link };
+                let ret = parse_simple_pat(loop_buf, ANC_S, ANC_E, Po::None, tt_fn);
+                match ret {
+                    Some((MdTree::Link { disp, .. }, _))
+                        if disp.chars().all(|ch| LNK_CHARS.contains(ch)) =>
+                    {
+                        ret
+                    }
+                    _ => None,
+                }
+            }
+            (_, Newline) if (loop_buf.starts_with(UL1) || loop_buf.starts_with(UL2)) => {
+                Some(parse_unordered_li(loop_buf))
+            }
+            (_, Newline) if ord_list_start(loop_buf).is_some() => Some(parse_ordered_li(loop_buf)),
+            (_, Newline | Whitespace) if loop_buf.starts_with(LNK_S) => {
+                parse_any_link(loop_buf, top_blk && prev == Prev::Newline)
+            }
+            (_, Escape | _) => None,
+        };
+
+        if let Some((tree, rest)) = res {
+            // We found something: push our WIP and then push the found tree
+            let prev_buf = &wip_buf[..(wip_buf.len() - loop_buf.len())];
+            if !prev_buf.is_empty() {
+                let prev_str = str::from_utf8(prev_buf).unwrap();
+                stream.push(MdTree::PlainText(prev_str));
+            }
+            stream.push(tree);
+
+            wip_buf = rest;
+            loop_buf = rest;
+        } else {
+            // Just move on to the next character
+            loop_buf = &loop_buf[1..];
+            // If we are at the end and haven't found anything, just push plain text
+            if loop_buf.is_empty() && !wip_buf.is_empty() {
+                let final_str = str::from_utf8(wip_buf).unwrap();
+                stream.push(MdTree::PlainText(final_str));
+            }
+        };
+
+        prev = next_prev;
+    }
+
+    MdStream(stream)
+}
+
+/// The simplest kind of patterns: data within start and end patterns
+fn parse_simple_pat<'a, F>(
+    buf: &'a [u8],
+    start_pat: &[u8],
+    end_pat: &[u8],
+    opts: ParseOpt,
+    create_tt: F,
+) -> ParseResult<'a>
+where
+    F: FnOnce(&'a str) -> MdTree<'a>,
+{
+    let ignore_esc = matches!(opts, ParseOpt::TrimNoEsc);
+    let trim = matches!(opts, ParseOpt::TrimNoEsc);
+    let (txt, rest) = parse_with_end_pat(&buf[start_pat.len()..], end_pat, ignore_esc)?;
+    let mut txt = str::from_utf8(txt).unwrap();
+    if trim {
+        txt = txt.trim();
+    }
+    Some((create_tt(txt), rest))
+}
+
+/// Parse backtick-wrapped inline code. Accounts for >1 backtick sets
+fn parse_codeinline(buf: &[u8]) -> ParseResult<'_> {
+    let seps = buf.iter().take_while(|ch| **ch == b'`').count();
+    let (txt, rest) = parse_with_end_pat(&buf[seps..], &buf[..seps], true)?;
+    Some((MdTree::CodeInline(str::from_utf8(txt).unwrap()), rest))
+}
+
+/// Parse a codeblock. Accounts for >3 backticks and language specification
+fn parse_codeblock(buf: &[u8]) -> Parsed<'_> {
+    // account for ````code```` style
+    let seps = buf.iter().take_while(|ch| **ch == b'`').count();
+    let end_sep = &buf[..seps];
+    let mut working = &buf[seps..];
+
+    // Handle "````rust" style language specifications
+    let next_ws_idx = working.iter().take_while(|ch| !ch.is_ascii_whitespace()).count();
+
+    let lang = if next_ws_idx > 0 {
+        // Munch the lang
+        let tmp = str::from_utf8(&working[..next_ws_idx]).unwrap();
+        working = &working[next_ws_idx..];
+        Some(tmp)
+    } else {
+        None
+    };
+
+    let mut end_pat = vec![b'\n'];
+    end_pat.extend(end_sep);
+
+    // Find first end pattern with nothing else on its line
+    let mut found = None;
+    for idx in (0..working.len()).filter(|idx| working[*idx..].starts_with(&end_pat)) {
+        let (eol_txt, rest) = parse_to_newline(&working[(idx + end_pat.len())..]);
+        if !eol_txt.iter().any(u8::is_ascii_whitespace) {
+            found = Some((&working[..idx], rest));
+            break;
+        }
+    }
+
+    let (txt, rest) = found.unwrap_or((working, &[]));
+    let txt = str::from_utf8(txt).unwrap().trim_matches('\n');
+
+    (MdTree::CodeBlock { txt, lang }, rest)
+}
+
+fn parse_heading(buf: &[u8]) -> ParseResult<'_> {
+    let level = buf.iter().take_while(|ch| **ch == b'#').count();
+    let buf = &buf[level..];
+
+    if level > 6 || (buf.len() > 1 && !buf[0].is_ascii_whitespace()) {
+        // Enforce max 6 levels and whitespace following the `##` pattern
+        return None;
+    }
+
+    let (txt, rest) = parse_to_newline(&buf[1..]);
+    let ctx = Context { top_block: false, prev: Prev::Whitespace };
+    let stream = parse_recursive(txt, ctx);
+
+    Some((MdTree::Heading(level.try_into().unwrap(), stream), rest))
+}
+
+/// Bulleted list
+fn parse_unordered_li(buf: &[u8]) -> Parsed<'_> {
+    debug_assert!(buf.starts_with(b"* ") || buf.starts_with(b"- "));
+    let (txt, rest) = get_indented_section(&buf[2..]);
+    let ctx = Context { top_block: false, prev: Prev::Whitespace };
+    let stream = parse_recursive(trim_ascii_start(txt), ctx);
+    (MdTree::UnorderedListItem(stream), rest)
+}
+
+/// Numbered list
+fn parse_ordered_li(buf: &[u8]) -> Parsed<'_> {
+    let (num, pos) = ord_list_start(buf).unwrap(); // success tested in caller
+    let (txt, rest) = get_indented_section(&buf[pos..]);
+    let ctx = Context { top_block: false, prev: Prev::Whitespace };
+    let stream = parse_recursive(trim_ascii_start(txt), ctx);
+    (MdTree::OrderedListItem(num, stream), rest)
+}
+
+/// Find first line that isn't empty or doesn't start with whitespace, that will
+/// be our contents
+fn get_indented_section(buf: &[u8]) -> (&[u8], &[u8]) {
+    let mut end = buf.len();
+    for (idx, window) in buf.windows(2).enumerate() {
+        let &[ch, next_ch] = window else {unreachable!("always 2 elements")};
+        if idx >= buf.len().saturating_sub(2) && next_ch == b'\n' {
+            // End of stream
+            end = buf.len().saturating_sub(1);
+            break;
+        } else if ch == b'\n' && (!next_ch.is_ascii_whitespace() || next_ch == b'\n') {
+            end = idx;
+            break;
+        }
+    }
+
+    (&buf[..end], &buf[end..])
+}
+
+/// Verify a valid ordered list start (e.g. `1.`) and parse it. Returns the
+/// parsed number and offset of character after the dot.
+fn ord_list_start(buf: &[u8]) -> Option<(u16, usize)> {
+    let pos = buf.iter().take(10).position(|ch| *ch == b'.')?;
+    let n = str::from_utf8(&buf[..pos]).ok()?;
+    if !buf.get(pos + 1)?.is_ascii_whitespace() {
+        return None;
+    }
+    n.parse::<u16>().ok().map(|v| (v, pos + 2))
+}
+
+/// Parse links. `can_be_def` indicates that a link definition is possible (top
+/// level, located at the start of a line)
+fn parse_any_link(buf: &[u8], can_be_def: bool) -> ParseResult<'_> {
+    let (bracketed, rest) = parse_with_end_pat(&buf[1..], LNK_E, true)?;
+    if rest.is_empty() {
+        return None;
+    }
+
+    let disp = str::from_utf8(bracketed).unwrap();
+    match (can_be_def, rest[0]) {
+        (true, b':') => {
+            let (link, tmp) = parse_to_newline(&rest[1..]);
+            let link = str::from_utf8(link).unwrap().trim();
+            Some((MdTree::LinkDef { id: disp, link }, tmp))
+        }
+        (_, b'(') => parse_simple_pat(rest, b"(", b")", ParseOpt::TrimNoEsc, |link| MdTree::Link {
+            disp,
+            link,
+        }),
+        (_, b'[') => parse_simple_pat(rest, b"[", b"]", ParseOpt::TrimNoEsc, |id| {
+            MdTree::RefLink { disp, id: Some(id) }
+        }),
+        _ => Some((MdTree::RefLink { disp, id: None }, rest)),
+    }
+}
+
+/// Find and consume an end pattern, return `(match, residual)`
+fn parse_with_end_pat<'a>(
+    buf: &'a [u8],
+    end_sep: &[u8],
+    ignore_esc: bool,
+) -> Option<(&'a [u8], &'a [u8])> {
+    // Find positions that start with the end seperator
+    for idx in (0..buf.len()).filter(|idx| buf[*idx..].starts_with(end_sep)) {
+        if !ignore_esc && idx > 0 && buf[idx - 1] == b'\\' {
+            continue;
+        }
+        return Some((&buf[..idx], &buf[idx + end_sep.len()..]));
+    }
+    None
+}
+
+/// Resturn `(match, residual)` to end of line. The EOL is returned with the
+/// residual.
+fn parse_to_newline(buf: &[u8]) -> (&[u8], &[u8]) {
+    buf.iter().position(|ch| *ch == b'\n').map_or((buf, &[]), |pos| buf.split_at(pos))
+}
+
+/// Take a parsed stream and fix the little things
+fn normalize<'a>(MdStream(stream): MdStream<'a>, linkdefs: &mut Vec<MdTree<'a>>) -> MdStream<'a> {
+    let mut new_stream = Vec::with_capacity(stream.len());
+    let new_defs = stream.iter().filter(|tt| matches!(tt, MdTree::LinkDef { .. }));
+    linkdefs.extend(new_defs.cloned());
+
+    // Run plaintest expansions on types that need it, call this function on nested types
+    for item in stream {
+        match item {
+            MdTree::PlainText(txt) => expand_plaintext(txt, &mut new_stream, MdTree::PlainText),
+            MdTree::Strong(txt) => expand_plaintext(txt, &mut new_stream, MdTree::Strong),
+            MdTree::Emphasis(txt) => expand_plaintext(txt, &mut new_stream, MdTree::Emphasis),
+            MdTree::Strikethrough(txt) => {
+                expand_plaintext(txt, &mut new_stream, MdTree::Strikethrough);
+            }
+            MdTree::RefLink { disp, id } => new_stream.push(match_reflink(linkdefs, disp, id)),
+            MdTree::OrderedListItem(n, st) => {
+                new_stream.push(MdTree::OrderedListItem(n, normalize(st, linkdefs)));
+            }
+            MdTree::UnorderedListItem(st) => {
+                new_stream.push(MdTree::UnorderedListItem(normalize(st, linkdefs)));
+            }
+            MdTree::Heading(n, st) => new_stream.push(MdTree::Heading(n, normalize(st, linkdefs))),
+            _ => new_stream.push(item),
+        }
+    }
+
+    // Remove non printing types, duplicate paragraph breaks, and breaks at start/end
+    new_stream.retain(|x| !matches!(x, MdTree::Comment(_) | MdTree::LinkDef { .. }));
+    new_stream.dedup_by(|r, l| matches!((r, l), (MdTree::ParagraphBreak, MdTree::ParagraphBreak)));
+
+    if new_stream.first().is_some_and(is_break_ty) {
+        new_stream.remove(0);
+    }
+    if new_stream.last().is_some_and(is_break_ty) {
+        new_stream.pop();
+    }
+
+    // Remove paragraph breaks that shouldn't be there. w[1] is what will be
+    // removed in these cases. Note that these are the items to keep, not delete
+    // (for `retain`)
+    let to_keep: Vec<bool> = new_stream
+        .windows(3)
+        .map(|w| {
+            !((matches!(&w[1], MdTree::ParagraphBreak)
+                && matches!(should_break(&w[0], &w[2]), BreakRule::Always(1) | BreakRule::Never))
+                || (matches!(&w[1], MdTree::PlainText(txt) if txt.trim().is_empty())
+                    && matches!(
+                        should_break(&w[0], &w[2]),
+                        BreakRule::Always(_) | BreakRule::Never
+                    )))
+        })
+        .collect();
+    let mut iter = iter::once(true).chain(to_keep).chain(iter::once(true));
+    new_stream.retain(|_| iter.next().unwrap());
+
+    // Insert line or paragraph breaks where there should be some
+    let mut insertions = 0;
+    let to_insert: Vec<(usize, MdTree<'_>)> = new_stream
+        .windows(2)
+        .enumerate()
+        .filter_map(|(idx, w)| match should_break(&w[0], &w[1]) {
+            BreakRule::Always(1) => Some((idx, MdTree::LineBreak)),
+            BreakRule::Always(2) => Some((idx, MdTree::ParagraphBreak)),
+            _ => None,
+        })
+        .map(|(idx, tt)| {
+            insertions += 1;
+            (idx + insertions, tt)
+        })
+        .collect();
+    to_insert.into_iter().for_each(|(idx, tt)| new_stream.insert(idx, tt));
+
+    MdStream(new_stream)
+}
+
+/// Whether two types should or shouldn't have a paragraph break between them
+#[derive(Clone, Copy, Debug, PartialEq)]
+enum BreakRule {
+    Always(u8),
+    Never,
+    Optional,
+}
+
+/// Blocks that automatically handle their own text wrapping
+fn should_break(left: &MdTree<'_>, right: &MdTree<'_>) -> BreakRule {
+    use MdTree::*;
+
+    match (left, right) {
+        // Separate these types with a single line
+        (HorizontalRule, _)
+        | (_, HorizontalRule)
+        | (OrderedListItem(_, _), OrderedListItem(_, _))
+        | (UnorderedListItem(_), UnorderedListItem(_)) => BreakRule::Always(1),
+        // Condensed types shouldn't have an extra break on either side
+        (Comment(_) | ParagraphBreak | Heading(_, _), _) | (_, Comment(_) | ParagraphBreak) => {
+            BreakRule::Never
+        }
+        // Block types should always be separated by full breaks
+        (CodeBlock { .. } | OrderedListItem(_, _) | UnorderedListItem(_), _)
+        | (_, CodeBlock { .. } | Heading(_, _) | OrderedListItem(_, _) | UnorderedListItem(_)) => {
+            BreakRule::Always(2)
+        }
+        // Text types may or may not be separated by a break
+        (
+            CodeInline(_)
+            | Strong(_)
+            | Emphasis(_)
+            | Strikethrough(_)
+            | PlainText(_)
+            | Link { .. }
+            | RefLink { .. }
+            | LinkDef { .. },
+            CodeInline(_)
+            | Strong(_)
+            | Emphasis(_)
+            | Strikethrough(_)
+            | PlainText(_)
+            | Link { .. }
+            | RefLink { .. }
+            | LinkDef { .. },
+        ) => BreakRule::Optional,
+        (LineBreak, _) | (_, LineBreak) => {
+            unreachable!("should have been removed during deduplication")
+        }
+    }
+}
+
+/// Types that indicate some form of break
+fn is_break_ty(val: &MdTree<'_>) -> bool {
+    matches!(val, MdTree::ParagraphBreak | MdTree::LineBreak)
+        // >1 break between paragraphs acts as a break
+        || matches!(val, MdTree::PlainText(txt) if txt.trim().is_empty())
+}
+
+/// Perform tranformations to text. This splits paragraphs, replaces patterns,
+/// and corrects newlines.
+///
+/// To avoid allocating strings (and using a different heavier tt type), our
+/// replace method means split into three and append each. For this reason, any
+/// viewer should treat consecutive `PlainText` types as belonging to the same
+/// paragraph.
+fn expand_plaintext<'a>(
+    txt: &'a str,
+    stream: &mut Vec<MdTree<'a>>,
+    mut f: fn(&'a str) -> MdTree<'a>,
+) {
+    if txt.is_empty() {
+        return;
+    } else if txt == "\n" {
+        if let Some(tt) = stream.last() {
+            let tmp = MdTree::PlainText(" ");
+            if should_break(tt, &tmp) == BreakRule::Optional {
+                stream.push(tmp);
+            }
+        }
+        return;
+    }
+    let mut queue1 = Vec::new();
+    let mut queue2 = Vec::new();
+    let stream_start_len = stream.len();
+    for paragraph in txt.split("\n\n") {
+        if paragraph.is_empty() {
+            stream.push(MdTree::ParagraphBreak);
+            continue;
+        }
+        let paragraph = trim_extra_ws(paragraph);
+
+        queue1.clear();
+        queue1.push(paragraph);
+
+        for (from, to) in REPLACEMENTS {
+            queue2.clear();
+            for item in &queue1 {
+                for s in item.split(from) {
+                    queue2.extend(&[s, to]);
+                }
+                if queue2.len() > 1 {
+                    let _ = queue2.pop(); // remove last unnecessary intersperse
+                }
+            }
+            mem::swap(&mut queue1, &mut queue2);
+        }
+
+        // Make sure we don't double whitespace
+        queue1.retain(|s| !s.is_empty());
+        for idx in 0..queue1.len() {
+            queue1[idx] = trim_extra_ws(queue1[idx]);
+            if idx < queue1.len() - 1
+                && queue1[idx].ends_with(char::is_whitespace)
+                && queue1[idx + 1].starts_with(char::is_whitespace)
+            {
+                queue1[idx] = queue1[idx].trim_end();
+            }
+        }
+        stream.extend(queue1.iter().copied().filter(|txt| !txt.is_empty()).map(&mut f));
+        stream.push(MdTree::ParagraphBreak);
+    }
+
+    if stream.len() - stream_start_len > 1 {
+        let _ = stream.pop(); // remove last unnecessary intersperse
+    }
+}
+
+/// Turn reflinks (links with reference IDs) into normal standalone links using
+/// listed link definitions
+fn match_reflink<'a>(linkdefs: &[MdTree<'a>], disp: &'a str, match_id: Option<&str>) -> MdTree<'a> {
+    let to_match = match_id.unwrap_or(disp); // Match with the display name if there isn't an id
+    for def in linkdefs {
+        if let MdTree::LinkDef { id, link } = def {
+            if *id == to_match {
+                return MdTree::Link { disp, link };
+            }
+        }
+    }
+    MdTree::Link { disp, link: "" } // link not found
+}
+
+/// If there is more than one whitespace char at start or end, trim the extras
+fn trim_extra_ws(mut txt: &str) -> &str {
+    let start_ws =
+        txt.bytes().position(|ch| !ch.is_ascii_whitespace()).unwrap_or(txt.len()).saturating_sub(1);
+    txt = &txt[start_ws..];
+    let end_ws = txt
+        .bytes()
+        .rev()
+        .position(|ch| !ch.is_ascii_whitespace())
+        .unwrap_or(txt.len())
+        .saturating_sub(1);
+    &txt[..txt.len() - end_ws]
+}
+
+/// If there is more than one whitespace char at start, trim the extras
+fn trim_ascii_start(buf: &[u8]) -> &[u8] {
+    let count = buf.iter().take_while(|ch| ch.is_ascii_whitespace()).count();
+    &buf[count..]
+}
+
+#[cfg(test)]
+#[path = "tests/parse.rs"]
+mod tests;
diff --git a/compiler/rustc_errors/src/markdown/term.rs b/compiler/rustc_errors/src/markdown/term.rs
new file mode 100644
index 00000000000..e45ba6d2cda
--- /dev/null
+++ b/compiler/rustc_errors/src/markdown/term.rs
@@ -0,0 +1,189 @@
+use std::cell::Cell;
+use std::io::{self, Write};
+
+use termcolor::{Buffer, Color, ColorSpec, WriteColor};
+
+use crate::markdown::{MdStream, MdTree};
+
+const DEFAULT_COLUMN_WIDTH: usize = 140;
+
+thread_local! {
+    /// Track the position of viewable characters in our buffer
+    static CURSOR: Cell<usize> = Cell::new(0);
+    /// Width of the terminal
+    static WIDTH: Cell<usize> = Cell::new(DEFAULT_COLUMN_WIDTH);
+}
+
+/// Print to terminal output to a buffer
+pub fn entrypoint(stream: &MdStream<'_>, buf: &mut Buffer) -> io::Result<()> {
+    #[cfg(not(test))]
+    if let Some((w, _)) = termize::dimensions() {
+        WIDTH.with(|c| c.set(std::cmp::min(w, DEFAULT_COLUMN_WIDTH)));
+    }
+    write_stream(stream, buf, None, 0)?;
+    buf.write_all(b"\n")
+}
+
+/// Write the buffer, reset to the default style after each
+fn write_stream(
+    MdStream(stream): &MdStream<'_>,
+    buf: &mut Buffer,
+    default: Option<&ColorSpec>,
+    indent: usize,
+) -> io::Result<()> {
+    match default {
+        Some(c) => buf.set_color(c)?,
+        None => buf.reset()?,
+    }
+
+    for tt in stream {
+        write_tt(tt, buf, indent)?;
+        if let Some(c) = default {
+            buf.set_color(c)?;
+        }
+    }
+
+    buf.reset()?;
+    Ok(())
+}
+
+pub fn write_tt(tt: &MdTree<'_>, buf: &mut Buffer, indent: usize) -> io::Result<()> {
+    match tt {
+        MdTree::CodeBlock { txt, lang: _ } => {
+            buf.set_color(ColorSpec::new().set_dimmed(true))?;
+            buf.write_all(txt.as_bytes())?;
+        }
+        MdTree::CodeInline(txt) => {
+            buf.set_color(ColorSpec::new().set_dimmed(true))?;
+            write_wrapping(buf, txt, indent, None)?;
+        }
+        MdTree::Strong(txt) => {
+            buf.set_color(ColorSpec::new().set_bold(true))?;
+            write_wrapping(buf, txt, indent, None)?;
+        }
+        MdTree::Emphasis(txt) => {
+            buf.set_color(ColorSpec::new().set_italic(true))?;
+            write_wrapping(buf, txt, indent, None)?;
+        }
+        MdTree::Strikethrough(txt) => {
+            buf.set_color(ColorSpec::new().set_strikethrough(true))?;
+            write_wrapping(buf, txt, indent, None)?;
+        }
+        MdTree::PlainText(txt) => {
+            write_wrapping(buf, txt, indent, None)?;
+        }
+        MdTree::Link { disp, link } => {
+            write_wrapping(buf, disp, indent, Some(link))?;
+        }
+        MdTree::ParagraphBreak => {
+            buf.write_all(b"\n\n")?;
+            reset_cursor();
+        }
+        MdTree::LineBreak => {
+            buf.write_all(b"\n")?;
+            reset_cursor();
+        }
+        MdTree::HorizontalRule => {
+            (0..WIDTH.with(Cell::get)).for_each(|_| buf.write_all(b"-").unwrap());
+            reset_cursor();
+        }
+        MdTree::Heading(n, stream) => {
+            let mut cs = ColorSpec::new();
+            cs.set_fg(Some(Color::Cyan));
+            match n {
+                1 => cs.set_intense(true).set_bold(true).set_underline(true),
+                2 => cs.set_intense(true).set_underline(true),
+                3 => cs.set_intense(true).set_italic(true),
+                4.. => cs.set_underline(true).set_italic(true),
+                0 => unreachable!(),
+            };
+            write_stream(stream, buf, Some(&cs), 0)?;
+            buf.write_all(b"\n")?;
+        }
+        MdTree::OrderedListItem(n, stream) => {
+            let base = format!("{n}. ");
+            write_wrapping(buf, &format!("{base:<4}"), indent, None)?;
+            write_stream(stream, buf, None, indent + 4)?;
+        }
+        MdTree::UnorderedListItem(stream) => {
+            let base = "* ";
+            write_wrapping(buf, &format!("{base:<4}"), indent, None)?;
+            write_stream(stream, buf, None, indent + 4)?;
+        }
+        // Patterns popped in previous step
+        MdTree::Comment(_) | MdTree::LinkDef { .. } | MdTree::RefLink { .. } => unreachable!(),
+    }
+
+    buf.reset()?;
+
+    Ok(())
+}
+
+/// End of that block, just wrap the line
+fn reset_cursor() {
+    CURSOR.with(|cur| cur.set(0));
+}
+
+/// Change to be generic on Write for testing. If we have a link URL, we don't
+/// count the extra tokens to make it clickable.
+fn write_wrapping<B: io::Write>(
+    buf: &mut B,
+    text: &str,
+    indent: usize,
+    link_url: Option<&str>,
+) -> io::Result<()> {
+    let ind_ws = &b"          "[..indent];
+    let mut to_write = text;
+    if let Some(url) = link_url {
+        // This is a nonprinting prefix so we don't increment our cursor
+        write!(buf, "\x1b]8;;{url}\x1b\\")?;
+    }
+    CURSOR.with(|cur| {
+        loop {
+            if cur.get() == 0 {
+                buf.write_all(ind_ws)?;
+                cur.set(indent);
+            }
+            let ch_count = WIDTH.with(Cell::get) - cur.get();
+            let mut iter = to_write.char_indices();
+            let Some((end_idx, _ch)) = iter.nth(ch_count) else {
+                // Write entire line
+                buf.write_all(to_write.as_bytes())?;
+                cur.set(cur.get()+to_write.chars().count());
+                break;
+            };
+
+            if let Some((break_idx, ch)) = to_write[..end_idx]
+                .char_indices()
+                .rev()
+                .find(|(_idx, ch)| ch.is_whitespace() || ['_', '-'].contains(ch))
+            {
+                // Found whitespace to break at
+                if ch.is_whitespace() {
+                    writeln!(buf, "{}", &to_write[..break_idx])?;
+                    to_write = to_write[break_idx..].trim_start();
+                } else {
+                    // Break at a `-` or `_` separator
+                    writeln!(buf, "{}", &to_write.get(..break_idx + 1).unwrap_or(to_write))?;
+                    to_write = to_write.get(break_idx + 1..).unwrap_or_default().trim_start();
+                }
+            } else {
+                // No whitespace, we need to just split
+                let ws_idx =
+                    iter.find(|(_, ch)| ch.is_whitespace()).map_or(to_write.len(), |(idx, _)| idx);
+                writeln!(buf, "{}", &to_write[..ws_idx])?;
+                to_write = to_write.get(ws_idx + 1..).map_or("", str::trim_start);
+            }
+            cur.set(0);
+        }
+        if link_url.is_some() {
+            buf.write_all(b"\x1b]8;;\x1b\\")?;
+        }
+
+        Ok(())
+    })
+}
+
+#[cfg(test)]
+#[path = "tests/term.rs"]
+mod tests;
diff --git a/compiler/rustc_errors/src/markdown/tests/input.md b/compiler/rustc_errors/src/markdown/tests/input.md
new file mode 100644
index 00000000000..7d207fc4220
--- /dev/null
+++ b/compiler/rustc_errors/src/markdown/tests/input.md
@@ -0,0 +1,50 @@
+# H1 Heading [with a link][remote-link]
+
+H1 content: **some words in bold** and `so does inline code`
+
+## H2 Heading
+
+H2 content: _some words in italic_
+
+### H3 Heading
+
+H3 content: ~~strikethrough~~ text
+
+#### H4 Heading
+
+H4 content: A [simple link](https://docs.rs) and a [remote-link].
+
+---
+
+A section break was above. We can also do paragraph breaks:
+
+(new paragraph) and unordered lists:
+
+- Item 1 in `code`
+- Item 2 in _italics_
+
+Or ordered:
+
+1. Item 1 in **bold**
+2. Item 2 with some long lines that should wrap: Lorem ipsum dolor sit amet,
+   consectetur adipiscing elit. Aenean ac mattis nunc. Phasellus elit quam,
+   pulvinar ac risus in, dictum vehicula turpis. Vestibulum neque est, accumsan
+   in cursus sit amet, dictum a nunc. Suspendisse aliquet, lorem eu eleifend
+   accumsan, magna neque sodales nisi, a aliquet lectus leo eu sem.
+
+---
+
+## Code
+
+Both `inline code` and code blocks are supported:
+
+```rust
+/// A rust enum
+#[derive(Debug, PartialEq, Clone)]
+enum Foo {
+    /// Start of line
+    Bar
+}
+```
+
+[remote-link]: http://docs.rs
diff --git a/compiler/rustc_errors/src/markdown/tests/output.stdout b/compiler/rustc_errors/src/markdown/tests/output.stdout
new file mode 100644
index 00000000000..23c60d5c319
--- /dev/null
+++ b/compiler/rustc_errors/src/markdown/tests/output.stdout
@@ -0,0 +1,35 @@
+H1 Heading ]8;;http://docs.rs\with a link]8;;\
+H1 content: some words in bold and so does inline code
+
+H2 Heading
+H2 content: some words in italic
+
+H3 Heading
+H3 content: strikethrough text
+
+H4 Heading
+H4 content: A ]8;;https://docs.rs\simple link]8;;\ and a ]8;;http://docs.rs\remote-link]8;;\.
+--------------------------------------------------------------------------------------------------------------------------------------------
+A section break was above. We can also do paragraph breaks:
+
+(new paragraph) and unordered lists:
+
+*   Item 1 in code
+*   Item 2 in italics
+
+Or ordered:
+
+1.  Item 1 in bold
+2.  Item 2 with some long lines that should wrap: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ac mattis nunc. Phasellus
+    elit quam, pulvinar ac risus in, dictum vehicula turpis. Vestibulum neque est, accumsan in cursus sit amet, dictum a nunc. Suspendisse
+    aliquet, lorem eu eleifend accumsan, magna neque sodales nisi, a aliquet lectus leo eu sem.
+--------------------------------------------------------------------------------------------------------------------------------------------
+Code
+Both inline code and code blocks are supported:
+
+/// A rust enum
+#[derive(Debug, PartialEq, Clone)]
+enum Foo {
+    /// Start of line
+    Bar
+}
diff --git a/compiler/rustc_errors/src/markdown/tests/parse.rs b/compiler/rustc_errors/src/markdown/tests/parse.rs
new file mode 100644
index 00000000000..e39e8c89b35
--- /dev/null
+++ b/compiler/rustc_errors/src/markdown/tests/parse.rs
@@ -0,0 +1,312 @@
+use super::*;
+use ParseOpt as PO;
+
+#[test]
+fn test_parse_simple() {
+    let buf = "**abcd** rest";
+    let (t, r) = parse_simple_pat(buf.as_bytes(), STG, STG, PO::None, MdTree::Strong).unwrap();
+    assert_eq!(t, MdTree::Strong("abcd"));
+    assert_eq!(r, b" rest");
+
+    // Escaping should fail
+    let buf = r"**abcd\** rest";
+    let res = parse_simple_pat(buf.as_bytes(), STG, STG, PO::None, MdTree::Strong);
+    assert!(res.is_none());
+}
+
+#[test]
+fn test_parse_comment() {
+    let opt = PO::TrimNoEsc;
+    let buf = "<!-- foobar! -->rest";
+    let (t, r) = parse_simple_pat(buf.as_bytes(), CMT_S, CMT_E, opt, MdTree::Comment).unwrap();
+    assert_eq!(t, MdTree::Comment("foobar!"));
+    assert_eq!(r, b"rest");
+
+    let buf = r"<!-- foobar! \-->rest";
+    let (t, r) = parse_simple_pat(buf.as_bytes(), CMT_S, CMT_E, opt, MdTree::Comment).unwrap();
+    assert_eq!(t, MdTree::Comment(r"foobar! \"));
+    assert_eq!(r, b"rest");
+}
+
+#[test]
+fn test_parse_heading() {
+    let buf1 = "# Top level\nrest";
+    let (t, r) = parse_heading(buf1.as_bytes()).unwrap();
+    assert_eq!(t, MdTree::Heading(1, vec![MdTree::PlainText("Top level")].into()));
+    assert_eq!(r, b"\nrest");
+
+    let buf1 = "# Empty";
+    let (t, r) = parse_heading(buf1.as_bytes()).unwrap();
+    assert_eq!(t, MdTree::Heading(1, vec![MdTree::PlainText("Empty")].into()));
+    assert_eq!(r, b"");
+
+    // Combo
+    let buf2 = "### Top `level` _woo_\nrest";
+    let (t, r) = parse_heading(buf2.as_bytes()).unwrap();
+    assert_eq!(
+        t,
+        MdTree::Heading(
+            3,
+            vec![
+                MdTree::PlainText("Top "),
+                MdTree::CodeInline("level"),
+                MdTree::PlainText(" "),
+                MdTree::Emphasis("woo"),
+            ]
+            .into()
+        )
+    );
+    assert_eq!(r, b"\nrest");
+}
+
+#[test]
+fn test_parse_code_inline() {
+    let buf1 = "`abcd` rest";
+    let (t, r) = parse_codeinline(buf1.as_bytes()).unwrap();
+    assert_eq!(t, MdTree::CodeInline("abcd"));
+    assert_eq!(r, b" rest");
+
+    // extra backticks, newline
+    let buf2 = "```ab\ncd``` rest";
+    let (t, r) = parse_codeinline(buf2.as_bytes()).unwrap();
+    assert_eq!(t, MdTree::CodeInline("ab\ncd"));
+    assert_eq!(r, b" rest");
+
+    // test no escaping
+    let buf3 = r"`abcd\` rest";
+    let (t, r) = parse_codeinline(buf3.as_bytes()).unwrap();
+    assert_eq!(t, MdTree::CodeInline(r"abcd\"));
+    assert_eq!(r, b" rest");
+}
+
+#[test]
+fn test_parse_code_block() {
+    let buf1 = "```rust\ncode\ncode\n```\nleftovers";
+    let (t, r) = parse_codeblock(buf1.as_bytes());
+    assert_eq!(t, MdTree::CodeBlock { txt: "code\ncode", lang: Some("rust") });
+    assert_eq!(r, b"\nleftovers");
+
+    let buf2 = "`````\ncode\ncode````\n`````\nleftovers";
+    let (t, r) = parse_codeblock(buf2.as_bytes());
+    assert_eq!(t, MdTree::CodeBlock { txt: "code\ncode````", lang: None });
+    assert_eq!(r, b"\nleftovers");
+}
+
+#[test]
+fn test_parse_link() {
+    let simple = "[see here](docs.rs) other";
+    let (t, r) = parse_any_link(simple.as_bytes(), false).unwrap();
+    assert_eq!(t, MdTree::Link { disp: "see here", link: "docs.rs" });
+    assert_eq!(r, b" other");
+
+    let simple_toplevel = "[see here](docs.rs) other";
+    let (t, r) = parse_any_link(simple_toplevel.as_bytes(), true).unwrap();
+    assert_eq!(t, MdTree::Link { disp: "see here", link: "docs.rs" });
+    assert_eq!(r, b" other");
+
+    let reference = "[see here] other";
+    let (t, r) = parse_any_link(reference.as_bytes(), true).unwrap();
+    assert_eq!(t, MdTree::RefLink { disp: "see here", id: None });
+    assert_eq!(r, b" other");
+
+    let reference_full = "[see here][docs-rs] other";
+    let (t, r) = parse_any_link(reference_full.as_bytes(), false).unwrap();
+    assert_eq!(t, MdTree::RefLink { disp: "see here", id: Some("docs-rs") });
+    assert_eq!(r, b" other");
+
+    let reference_def = "[see here]: docs.rs\nother";
+    let (t, r) = parse_any_link(reference_def.as_bytes(), true).unwrap();
+    assert_eq!(t, MdTree::LinkDef { id: "see here", link: "docs.rs" });
+    assert_eq!(r, b"\nother");
+}
+
+const IND1: &str = r"test standard
+    ind
+    ind2
+not ind";
+const IND2: &str = r"test end of stream
+  1
+  2
+";
+const IND3: &str = r"test empty lines
+  1
+  2
+
+not ind";
+
+#[test]
+fn test_indented_section() {
+    let (t, r) = get_indented_section(IND1.as_bytes());
+    assert_eq!(str::from_utf8(t).unwrap(), "test standard\n    ind\n    ind2");
+    assert_eq!(str::from_utf8(r).unwrap(), "\nnot ind");
+
+    let (txt, rest) = get_indented_section(IND2.as_bytes());
+    assert_eq!(str::from_utf8(txt).unwrap(), "test end of stream\n  1\n  2");
+    assert_eq!(str::from_utf8(rest).unwrap(), "\n");
+
+    let (txt, rest) = get_indented_section(IND3.as_bytes());
+    assert_eq!(str::from_utf8(txt).unwrap(), "test empty lines\n  1\n  2");
+    assert_eq!(str::from_utf8(rest).unwrap(), "\n\nnot ind");
+}
+
+const HBT: &str = r"# Heading
+
+content";
+
+#[test]
+fn test_heading_breaks() {
+    let expected = vec![
+        MdTree::Heading(1, vec![MdTree::PlainText("Heading")].into()),
+        MdTree::PlainText("content"),
+    ]
+    .into();
+    let res = entrypoint(HBT);
+    assert_eq!(res, expected);
+}
+
+const NL1: &str = r"start
+
+end";
+const NL2: &str = r"start
+
+
+end";
+const NL3: &str = r"start
+
+
+
+end";
+
+#[test]
+fn test_newline_breaks() {
+    let expected =
+        vec![MdTree::PlainText("start"), MdTree::ParagraphBreak, MdTree::PlainText("end")].into();
+    for (idx, check) in [NL1, NL2, NL3].iter().enumerate() {
+        let res = entrypoint(check);
+        assert_eq!(res, expected, "failed {idx}");
+    }
+}
+
+const WRAP: &str = "plain _italics
+italics_";
+
+#[test]
+fn test_wrap_pattern() {
+    let expected = vec![
+        MdTree::PlainText("plain "),
+        MdTree::Emphasis("italics"),
+        MdTree::Emphasis(" "),
+        MdTree::Emphasis("italics"),
+    ]
+    .into();
+    let res = entrypoint(WRAP);
+    assert_eq!(res, expected);
+}
+
+const WRAP_NOTXT: &str = r"_italics_
+**bold**";
+
+#[test]
+fn test_wrap_notxt() {
+    let expected =
+        vec![MdTree::Emphasis("italics"), MdTree::PlainText(" "), MdTree::Strong("bold")].into();
+    let res = entrypoint(WRAP_NOTXT);
+    assert_eq!(res, expected);
+}
+
+const MIXED_LIST: &str = r"start
+- _italics item_
+<!-- comment -->
+- **bold item**
+  second line [link1](foobar1)
+  third line [link2][link-foo]
+-   :crab:
+    extra indent
+end
+[link-foo]: foobar2
+";
+
+#[test]
+fn test_list() {
+    let expected = vec![
+        MdTree::PlainText("start"),
+        MdTree::ParagraphBreak,
+        MdTree::UnorderedListItem(vec![MdTree::Emphasis("italics item")].into()),
+        MdTree::LineBreak,
+        MdTree::UnorderedListItem(
+            vec![
+                MdTree::Strong("bold item"),
+                MdTree::PlainText(" second line "),
+                MdTree::Link { disp: "link1", link: "foobar1" },
+                MdTree::PlainText(" third line "),
+                MdTree::Link { disp: "link2", link: "foobar2" },
+            ]
+            .into(),
+        ),
+        MdTree::LineBreak,
+        MdTree::UnorderedListItem(
+            vec![MdTree::PlainText("🦀"), MdTree::PlainText(" extra indent")].into(),
+        ),
+        MdTree::ParagraphBreak,
+        MdTree::PlainText("end"),
+    ]
+    .into();
+    let res = entrypoint(MIXED_LIST);
+    assert_eq!(res, expected);
+}
+
+const SMOOSHED: &str = r#"
+start
+### heading
+1. ordered item
+```rust
+println!("Hello, world!");
+```
+`inline`
+``end``
+"#;
+
+#[test]
+fn test_without_breaks() {
+    let expected = vec![
+        MdTree::PlainText("start"),
+        MdTree::ParagraphBreak,
+        MdTree::Heading(3, vec![MdTree::PlainText("heading")].into()),
+        MdTree::OrderedListItem(1, vec![MdTree::PlainText("ordered item")].into()),
+        MdTree::ParagraphBreak,
+        MdTree::CodeBlock { txt: r#"println!("Hello, world!");"#, lang: Some("rust") },
+        MdTree::ParagraphBreak,
+        MdTree::CodeInline("inline"),
+        MdTree::PlainText(" "),
+        MdTree::CodeInline("end"),
+    ]
+    .into();
+    let res = entrypoint(SMOOSHED);
+    assert_eq!(res, expected);
+}
+
+const CODE_STARTLINE: &str = r#"
+start
+`code`
+middle
+`more code`
+end
+"#;
+
+#[test]
+fn test_code_at_start() {
+    let expected = vec![
+        MdTree::PlainText("start"),
+        MdTree::PlainText(" "),
+        MdTree::CodeInline("code"),
+        MdTree::PlainText(" "),
+        MdTree::PlainText("middle"),
+        MdTree::PlainText(" "),
+        MdTree::CodeInline("more code"),
+        MdTree::PlainText(" "),
+        MdTree::PlainText("end"),
+    ]
+    .into();
+    let res = entrypoint(CODE_STARTLINE);
+    assert_eq!(res, expected);
+}
diff --git a/compiler/rustc_errors/src/markdown/tests/term.rs b/compiler/rustc_errors/src/markdown/tests/term.rs
new file mode 100644
index 00000000000..3b31c6d6295
--- /dev/null
+++ b/compiler/rustc_errors/src/markdown/tests/term.rs
@@ -0,0 +1,90 @@
+use std::io::BufWriter;
+use std::path::PathBuf;
+use termcolor::{BufferWriter, ColorChoice};
+
+use super::*;
+use crate::markdown::MdStream;
+
+const INPUT: &str = include_str!("input.md");
+const OUTPUT_PATH: &[&str] = &[env!("CARGO_MANIFEST_DIR"), "src","markdown","tests","output.stdout"];
+
+const TEST_WIDTH: usize = 80;
+
+// We try to make some words long to create corner cases
+const TXT: &str = r"Lorem ipsum dolor sit amet, consecteturadipiscingelit.
+Fusce-id-urna-sollicitudin, pharetra nisl nec, lobortis tellus. In at
+metus hendrerit, tincidunteratvel, ultrices turpis. Curabitur_risus_sapien,
+porta-sed-nunc-sed, ultricesposuerelacus. Sed porttitor quis
+dolor non venenatis. Aliquam ut. ";
+
+const WRAPPED: &str = r"Lorem ipsum dolor sit amet, consecteturadipiscingelit. Fusce-id-urna-
+sollicitudin, pharetra nisl nec, lobortis tellus. In at metus hendrerit,
+tincidunteratvel, ultrices turpis. Curabitur_risus_sapien, porta-sed-nunc-sed,
+ultricesposuerelacus. Sed porttitor quis dolor non venenatis. Aliquam ut. Lorem
+    ipsum dolor sit amet, consecteturadipiscingelit. Fusce-id-urna-
+    sollicitudin, pharetra nisl nec, lobortis tellus. In at metus hendrerit,
+    tincidunteratvel, ultrices turpis. Curabitur_risus_sapien, porta-sed-nunc-
+    sed, ultricesposuerelacus. Sed porttitor quis dolor non venenatis. Aliquam
+    ut. Sample link lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet,
+consecteturadipiscingelit. Fusce-id-urna-sollicitudin, pharetra nisl nec,
+lobortis tellus. In at metus hendrerit, tincidunteratvel, ultrices turpis.
+Curabitur_risus_sapien, porta-sed-nunc-sed, ultricesposuerelacus. Sed porttitor
+quis dolor non venenatis. Aliquam ut. ";
+
+#[test]
+fn test_wrapping_write() {
+    WIDTH.with(|w| w.set(TEST_WIDTH));
+    let mut buf = BufWriter::new(Vec::new());
+    let txt = TXT.replace("-\n","-").replace("_\n","_").replace('\n', " ").replace("    ", "");
+    write_wrapping(&mut buf, &txt, 0, None).unwrap();
+    write_wrapping(&mut buf, &txt, 4, None).unwrap();
+    write_wrapping(
+        &mut buf,
+        "Sample link lorem ipsum dolor sit amet. ",
+        4,
+        Some("link-address-placeholder"),
+    )
+    .unwrap();
+    write_wrapping(&mut buf, &txt, 0, None).unwrap();
+    let out = String::from_utf8(buf.into_inner().unwrap()).unwrap();
+    let out = out
+        .replace("\x1b\\", "")
+        .replace('\x1b', "")
+        .replace("]8;;", "")
+        .replace("link-address-placeholder", "");
+
+    for line in out.lines() {
+        assert!(line.len() <= TEST_WIDTH, "line length\n'{line}'")
+    }
+
+    assert_eq!(out, WRAPPED);
+}
+
+#[test]
+fn test_output() {
+    // Capture `--bless` when run via ./x
+    let bless = std::env::var("RUSTC_BLESS").unwrap_or_default() == "1";
+    let ast = MdStream::parse_str(INPUT);
+    let bufwtr = BufferWriter::stderr(ColorChoice::Always);
+    let mut buffer = bufwtr.buffer();
+    ast.write_termcolor_buf(&mut buffer).unwrap();
+
+    let mut blessed = PathBuf::new();
+    blessed.extend(OUTPUT_PATH);
+
+    if bless {
+        std::fs::write(&blessed, buffer.into_inner()).unwrap();
+        eprintln!("blessed output at {}", blessed.display());
+    } else {
+        let output = buffer.into_inner();
+        if std::fs::read(blessed).unwrap() != output {
+            // hack: I don't know any way to write bytes to the captured stdout
+            // that cargo test uses
+            let mut out = std::io::stdout();
+            out.write_all(b"\n\nMarkdown output did not match. Expected:\n").unwrap();
+            out.write_all(&output).unwrap();
+            out.write_all(b"\n\n").unwrap();
+            panic!("markdown output mismatch");
+        }
+    }
+}
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index 906c31c9a3d..5185820a727 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -391,6 +391,8 @@ declare_features! (
     (active, doc_masked, "1.21.0", Some(44027), None),
     /// Allows `dyn* Trait` objects.
     (incomplete, dyn_star, "1.65.0", Some(102425), None),
+    // Uses generic effect parameters for ~const bounds
+    (active, effects, "CURRENT_RUSTC_VERSION", Some(102090), None),
     /// Allows `X..Y` patterns.
     (active, exclusive_range_pattern, "1.11.0", Some(37854), None),
     /// Allows exhaustive pattern matching on types that contain uninhabited types.
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index 3c5bff3812a..36600004404 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -723,6 +723,12 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         and it is only intended to be used in `alloc`."
     ),
 
+    rustc_attr!(
+        rustc_host, AttributeType::Normal, template!(Word), ErrorFollowing,
+        "#[rustc_host] annotates const generic parameters as the `host` effect param, \
+        and it is only intended for internal use and as a desugaring."
+    ),
+
     BuiltinAttribute {
         name: sym::rustc_diagnostic_item,
         // FIXME: This can be `true` once we always use `tcx.is_diagnostic_item`.
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index ad26c495c02..0738961d6ce 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -288,6 +288,11 @@ hir_analysis_unrecognized_intrinsic_function =
     unrecognized intrinsic function: `{$name}`
     .label = unrecognized intrinsic
 
+hir_analysis_unused_associated_type_bounds =
+    unnecessary associated type bound for not object safe associated type
+    .note = this associated type has a `where Self: Sized` bound. Thus, while the associated type can be specified, it cannot be used in any way, because trait objects are not `Sized`.
+    .suggestion = remove this bound
+
 hir_analysis_value_of_associated_struct_already_specified =
     the value of the associated type `{$item_name}` (from trait `{$def_path}`) is already specified
     .label = re-bound here
diff --git a/compiler/rustc_hir_analysis/src/astconv/bounds.rs b/compiler/rustc_hir_analysis/src/astconv/bounds.rs
index 89677141f38..b13de770137 100644
--- a/compiler/rustc_hir_analysis/src/astconv/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/bounds.rs
@@ -363,7 +363,8 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
                                 );
                                 emitted_bad_param_err = true;
                             }
-                            tcx.mk_bound(
+                            Ty::new_bound(
+                                tcx,
                                 ty::INNERMOST,
                                 ty::BoundTy {
                                     var: ty::BoundVar::from_usize(num_bound_vars),
@@ -386,11 +387,10 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
                                 .type_of(param.def_id)
                                 .no_bound_vars()
                                 .expect("ct params cannot have early bound vars");
-                            tcx.mk_const(
-                                ty::ConstKind::Bound(
-                                    ty::INNERMOST,
-                                    ty::BoundVar::from_usize(num_bound_vars),
-                                ),
+                            ty::Const::new_bound(
+                                tcx,
+                                ty::INNERMOST,
+                                ty::BoundVar::from_usize(num_bound_vars),
                                 ty,
                             )
                             .into()
@@ -528,14 +528,14 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
                         }
                         let reported = err.emit();
                         term = match def_kind {
-                            hir::def::DefKind::AssocTy => tcx.ty_error(reported).into(),
-                            hir::def::DefKind::AssocConst => tcx
-                                .const_error(
-                                    tcx.type_of(assoc_item_def_id)
-                                        .subst(tcx, projection_ty.skip_binder().substs),
-                                    reported,
-                                )
-                                .into(),
+                            hir::def::DefKind::AssocTy => Ty::new_error(tcx, reported).into(),
+                            hir::def::DefKind::AssocConst => ty::Const::new_error(
+                                tcx,
+                                reported,
+                                tcx.type_of(assoc_item_def_id)
+                                    .subst(tcx, projection_ty.skip_binder().substs),
+                            )
+                            .into(),
                             _ => unreachable!(),
                         };
                     }
@@ -559,7 +559,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
                 // type bound into a trait predicate, since we only want to add predicates
                 // for the `Self` type.
                 if !only_self_bounds.0 {
-                    let param_ty = tcx.mk_alias(ty::Projection, projection_ty.skip_binder());
+                    let param_ty = Ty::new_alias(tcx, ty::Projection, projection_ty.skip_binder());
                     self.add_bounds(
                         param_ty,
                         ast_bounds.iter(),
diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs
index dc17ef7048d..ddf99853b2e 100644
--- a/compiler/rustc_hir_analysis/src/astconv/errors.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs
@@ -357,7 +357,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     let projection_ty = pred.skip_binder().projection_ty;
 
                     let substs_with_infer_self = tcx.mk_substs_from_iter(
-                        std::iter::once(tcx.mk_ty_var(ty::TyVid::from_u32(0)).into())
+                        std::iter::once(Ty::new_var(tcx, ty::TyVid::from_u32(0)).into())
                             .chain(projection_ty.substs.iter().skip(1)),
                     );
 
diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs
index 2f5bcf8d647..3d698462840 100644
--- a/compiler/rustc_hir_analysis/src/astconv/mod.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs
@@ -6,14 +6,13 @@ mod bounds;
 mod errors;
 pub mod generics;
 mod lint;
+mod object_safety;
 
 use crate::astconv::errors::prohibit_assoc_ty_binding;
 use crate::astconv::generics::{check_generic_arg_count, create_substs_for_generic_args};
 use crate::bounds::Bounds;
 use crate::collect::HirPlaceholderCollector;
-use crate::errors::{
-    AmbiguousLifetimeBound, TraitObjectDeclaredWithNoTraits, TypeofReservedKeywordUsed,
-};
+use crate::errors::{AmbiguousLifetimeBound, TypeofReservedKeywordUsed};
 use crate::middle::resolve_bound_vars as rbv;
 use crate::require_c_abi_if_c_variadic;
 use rustc_ast::TraitObjectSyntax;
@@ -31,24 +30,17 @@ use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt};
 use rustc_infer::traits::ObligationCause;
 use rustc_middle::middle::stability::AllowUnstable;
 use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, SubstsRef};
-use rustc_middle::ty::DynKind;
 use rustc_middle::ty::GenericParamDefKind;
-use rustc_middle::ty::ToPredicate;
 use rustc_middle::ty::{self, Const, IsSuggestable, Ty, TyCtxt, TypeVisitableExt};
 use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS;
 use rustc_span::edit_distance::find_best_match_for_name;
 use rustc_span::symbol::{kw, Ident, Symbol};
 use rustc_span::{sym, Span, DUMMY_SP};
 use rustc_target::spec::abi;
-use rustc_trait_selection::traits::error_reporting::report_object_safety_error;
 use rustc_trait_selection::traits::wf::object_region_bounds;
-use rustc_trait_selection::traits::{
-    self, astconv_object_safety_violations, NormalizeExt, ObligationCtxt,
-};
+use rustc_trait_selection::traits::{self, NormalizeExt, ObligationCtxt};
 use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
 
-use smallvec::{smallvec, SmallVec};
-use std::collections::BTreeSet;
 use std::fmt::Display;
 use std::slice;
 
@@ -451,7 +443,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     }
                     if let (hir::TyKind::Infer, false) = (&ty.kind, self.astconv.allow_ty_infer()) {
                         self.inferred_params.push(ty.span);
-                        tcx.ty_error_misc().into()
+                        Ty::new_misc_error(tcx).into()
                     } else {
                         self.astconv.ast_ty_to_ty(ty).into()
                     }
@@ -482,7 +474,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                             self.astconv.ct_infer(ty, Some(param), inf.span).into()
                         } else {
                             self.inferred_params.push(inf.span);
-                            tcx.const_error_misc(ty).into()
+                            ty::Const::new_misc_error(tcx, ty).into()
                         }
                     }
                     _ => unreachable!(),
@@ -520,14 +512,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                                 _ => false,
                             }) {
                                 // Avoid ICE #86756 when type error recovery goes awry.
-                                return tcx.ty_error_misc().into();
+                                return Ty::new_misc_error(tcx).into();
                             }
                             tcx.at(self.span).type_of(param.def_id).subst(tcx, substs).into()
                         } else if infer_args {
                             self.astconv.ty_infer(Some(param), self.span).into()
                         } else {
                             // We've already errored above about the mismatch.
-                            tcx.ty_error_misc().into()
+                            Ty::new_misc_error(tcx).into()
                         }
                     }
                     GenericParamDefKind::Const { has_default } => {
@@ -537,7 +529,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                             .no_bound_vars()
                             .expect("const parameter types cannot be generic");
                         if let Err(guar) = ty.error_reported() {
-                            return tcx.const_error(ty, guar).into();
+                            return ty::Const::new_error(tcx, guar, ty).into();
                         }
                         if !infer_args && has_default {
                             tcx.const_param_default(param.def_id).subst(tcx, substs.unwrap()).into()
@@ -546,7 +538,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                                 self.astconv.ct_infer(ty, Some(param), self.span).into()
                             } else {
                                 // We've already errored above about the mismatch.
-                                tcx.const_error_misc(ty).into()
+                                ty::Const::new_misc_error(tcx, ty).into()
                             }
                         }
                     }
@@ -920,387 +912,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             // referencing a single opaque type) get encoded as a type alias that normalization will
             // then actually instantiate the where bounds of.
             let alias_ty = self.tcx().mk_alias_ty(did, substs);
-            self.tcx().mk_alias(ty::Weak, alias_ty)
+            Ty::new_alias(self.tcx(), ty::Weak, alias_ty)
         } else {
             ty.subst(self.tcx(), substs)
         }
     }
 
-    fn conv_object_ty_poly_trait_ref(
-        &self,
-        span: Span,
-        hir_trait_bounds: &[hir::PolyTraitRef<'_>],
-        lifetime: &hir::Lifetime,
-        borrowed: bool,
-        representation: DynKind,
-    ) -> Ty<'tcx> {
-        let tcx = self.tcx();
-
-        let mut bounds = Bounds::default();
-        let mut potential_assoc_types = Vec::new();
-        let dummy_self = self.tcx().types.trait_object_dummy_self;
-        for trait_bound in hir_trait_bounds.iter().rev() {
-            if let GenericArgCountResult {
-                correct:
-                    Err(GenericArgCountMismatch { invalid_args: cur_potential_assoc_types, .. }),
-                ..
-            } = self.instantiate_poly_trait_ref(
-                &trait_bound.trait_ref,
-                trait_bound.span,
-                ty::BoundConstness::NotConst,
-                ty::ImplPolarity::Positive,
-                dummy_self,
-                &mut bounds,
-                false,
-                // FIXME: This should be `true`, but we don't really handle
-                // associated type bounds or type aliases in objects in a way
-                // that makes this meaningful, I think.
-                OnlySelfBounds(false),
-            ) {
-                potential_assoc_types.extend(cur_potential_assoc_types);
-            }
-        }
-
-        let mut trait_bounds = vec![];
-        let mut projection_bounds = vec![];
-        for (pred, span) in bounds.clauses() {
-            let bound_pred = pred.kind();
-            match bound_pred.skip_binder() {
-                ty::ClauseKind::Trait(trait_pred) => {
-                    assert_eq!(trait_pred.polarity, ty::ImplPolarity::Positive);
-                    trait_bounds.push((
-                        bound_pred.rebind(trait_pred.trait_ref),
-                        span,
-                        trait_pred.constness,
-                    ));
-                }
-                ty::ClauseKind::Projection(proj) => {
-                    projection_bounds.push((bound_pred.rebind(proj), span));
-                }
-                ty::ClauseKind::TypeOutlives(_) => {
-                    // Do nothing, we deal with regions separately
-                }
-                ty::ClauseKind::RegionOutlives(_)
-                | ty::ClauseKind::ConstArgHasType(..)
-                | ty::ClauseKind::WellFormed(_)
-                | ty::ClauseKind::ConstEvaluatable(_)
-                | ty::ClauseKind::TypeWellFormedFromEnv(_) => {
-                    bug!()
-                }
-            }
-        }
-
-        // Expand trait aliases recursively and check that only one regular (non-auto) trait
-        // is used and no 'maybe' bounds are used.
-        let expanded_traits =
-            traits::expand_trait_aliases(tcx, trait_bounds.iter().map(|&(a, b, _)| (a, b)));
-
-        let (mut auto_traits, regular_traits): (Vec<_>, Vec<_>) = expanded_traits
-            .filter(|i| i.trait_ref().self_ty().skip_binder() == dummy_self)
-            .partition(|i| tcx.trait_is_auto(i.trait_ref().def_id()));
-        if regular_traits.len() > 1 {
-            let first_trait = &regular_traits[0];
-            let additional_trait = &regular_traits[1];
-            let mut err = struct_span_err!(
-                tcx.sess,
-                additional_trait.bottom().1,
-                E0225,
-                "only auto traits can be used as additional traits in a trait object"
-            );
-            additional_trait.label_with_exp_info(
-                &mut err,
-                "additional non-auto trait",
-                "additional use",
-            );
-            first_trait.label_with_exp_info(&mut err, "first non-auto trait", "first use");
-            err.help(format!(
-                "consider creating a new trait with all of these as supertraits and using that \
-                 trait here instead: `trait NewTrait: {} {{}}`",
-                regular_traits
-                    .iter()
-                    .map(|t| t.trait_ref().print_only_trait_path().to_string())
-                    .collect::<Vec<_>>()
-                    .join(" + "),
-            ));
-            err.note(
-                "auto-traits like `Send` and `Sync` are traits that have special properties; \
-                 for more information on them, visit \
-                 <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>",
-            );
-            err.emit();
-        }
-
-        if regular_traits.is_empty() && auto_traits.is_empty() {
-            let trait_alias_span = trait_bounds
-                .iter()
-                .map(|&(trait_ref, _, _)| trait_ref.def_id())
-                .find(|&trait_ref| tcx.is_trait_alias(trait_ref))
-                .map(|trait_ref| tcx.def_span(trait_ref));
-            let reported =
-                tcx.sess.emit_err(TraitObjectDeclaredWithNoTraits { span, trait_alias_span });
-            return tcx.ty_error(reported);
-        }
-
-        // Check that there are no gross object safety violations;
-        // most importantly, that the supertraits don't contain `Self`,
-        // to avoid ICEs.
-        for item in &regular_traits {
-            let object_safety_violations =
-                astconv_object_safety_violations(tcx, item.trait_ref().def_id());
-            if !object_safety_violations.is_empty() {
-                let reported = report_object_safety_error(
-                    tcx,
-                    span,
-                    item.trait_ref().def_id(),
-                    &object_safety_violations,
-                )
-                .emit();
-                return tcx.ty_error(reported);
-            }
-        }
-
-        // Use a `BTreeSet` to keep output in a more consistent order.
-        let mut associated_types: FxHashMap<Span, BTreeSet<DefId>> = FxHashMap::default();
-
-        let regular_traits_refs_spans = trait_bounds
-            .into_iter()
-            .filter(|(trait_ref, _, _)| !tcx.trait_is_auto(trait_ref.def_id()));
-
-        for (base_trait_ref, span, constness) in regular_traits_refs_spans {
-            assert_eq!(constness, ty::BoundConstness::NotConst);
-            let base_pred: ty::Predicate<'tcx> = base_trait_ref.to_predicate(tcx);
-            for pred in traits::elaborate(tcx, [base_pred]) {
-                debug!("conv_object_ty_poly_trait_ref: observing object predicate `{:?}`", pred);
-
-                let bound_predicate = pred.kind();
-                match bound_predicate.skip_binder() {
-                    ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
-                        let pred = bound_predicate.rebind(pred);
-                        associated_types.entry(span).or_default().extend(
-                            tcx.associated_items(pred.def_id())
-                                .in_definition_order()
-                                .filter(|item| item.kind == ty::AssocKind::Type)
-                                .filter(|item| item.opt_rpitit_info.is_none())
-                                .map(|item| item.def_id),
-                        );
-                    }
-                    ty::PredicateKind::Clause(ty::ClauseKind::Projection(pred)) => {
-                        let pred = bound_predicate.rebind(pred);
-                        // A `Self` within the original bound will be substituted with a
-                        // `trait_object_dummy_self`, so check for that.
-                        let references_self = match pred.skip_binder().term.unpack() {
-                            ty::TermKind::Ty(ty) => ty.walk().any(|arg| arg == dummy_self.into()),
-                            ty::TermKind::Const(c) => {
-                                c.ty().walk().any(|arg| arg == dummy_self.into())
-                            }
-                        };
-
-                        // If the projection output contains `Self`, force the user to
-                        // elaborate it explicitly to avoid a lot of complexity.
-                        //
-                        // The "classically useful" case is the following:
-                        // ```
-                        //     trait MyTrait: FnMut() -> <Self as MyTrait>::MyOutput {
-                        //         type MyOutput;
-                        //     }
-                        // ```
-                        //
-                        // Here, the user could theoretically write `dyn MyTrait<Output = X>`,
-                        // but actually supporting that would "expand" to an infinitely-long type
-                        // `fix $ τ → dyn MyTrait<MyOutput = X, Output = <τ as MyTrait>::MyOutput`.
-                        //
-                        // Instead, we force the user to write
-                        // `dyn MyTrait<MyOutput = X, Output = X>`, which is uglier but works. See
-                        // the discussion in #56288 for alternatives.
-                        if !references_self {
-                            // Include projections defined on supertraits.
-                            projection_bounds.push((pred, span));
-                        }
-                    }
-                    _ => (),
-                }
-            }
-        }
-
-        // `dyn Trait<Assoc = Foo>` desugars to (not Rust syntax) `dyn Trait where <Self as Trait>::Assoc = Foo`.
-        // So every `Projection` clause is an `Assoc = Foo` bound. `associated_types` contains all associated
-        // types's `DefId`, so the following loop removes all the `DefIds` of the associated types that have a
-        // corresponding `Projection` clause
-        for (projection_bound, _) in &projection_bounds {
-            for def_ids in associated_types.values_mut() {
-                def_ids.remove(&projection_bound.projection_def_id());
-            }
-        }
-
-        self.complain_about_missing_associated_types(
-            associated_types,
-            potential_assoc_types,
-            hir_trait_bounds,
-        );
-
-        // De-duplicate auto traits so that, e.g., `dyn Trait + Send + Send` is the same as
-        // `dyn Trait + Send`.
-        // We remove duplicates by inserting into a `FxHashSet` to avoid re-ordering
-        // the bounds
-        let mut duplicates = FxHashSet::default();
-        auto_traits.retain(|i| duplicates.insert(i.trait_ref().def_id()));
-        debug!("regular_traits: {:?}", regular_traits);
-        debug!("auto_traits: {:?}", auto_traits);
-
-        // Erase the `dummy_self` (`trait_object_dummy_self`) used above.
-        let existential_trait_refs = regular_traits.iter().map(|i| {
-            i.trait_ref().map_bound(|trait_ref: ty::TraitRef<'tcx>| {
-                assert_eq!(trait_ref.self_ty(), dummy_self);
-
-                // Verify that `dummy_self` did not leak inside default type parameters. This
-                // could not be done at path creation, since we need to see through trait aliases.
-                let mut missing_type_params = vec![];
-                let mut references_self = false;
-                let generics = tcx.generics_of(trait_ref.def_id);
-                let substs: Vec<_> = trait_ref
-                    .substs
-                    .iter()
-                    .enumerate()
-                    .skip(1) // Remove `Self` for `ExistentialPredicate`.
-                    .map(|(index, arg)| {
-                        if arg == dummy_self.into() {
-                            let param = &generics.params[index];
-                            missing_type_params.push(param.name);
-                            return tcx.ty_error_misc().into();
-                        } else if arg.walk().any(|arg| arg == dummy_self.into()) {
-                            references_self = true;
-                            return tcx.ty_error_misc().into();
-                        }
-                        arg
-                    })
-                    .collect();
-                let substs = tcx.mk_substs(&substs);
-
-                let span = i.bottom().1;
-                let empty_generic_args = hir_trait_bounds.iter().any(|hir_bound| {
-                    hir_bound.trait_ref.path.res == Res::Def(DefKind::Trait, trait_ref.def_id)
-                        && hir_bound.span.contains(span)
-                });
-                self.complain_about_missing_type_params(
-                    missing_type_params,
-                    trait_ref.def_id,
-                    span,
-                    empty_generic_args,
-                );
-
-                if references_self {
-                    let def_id = i.bottom().0.def_id();
-                    let mut err = struct_span_err!(
-                        tcx.sess,
-                        i.bottom().1,
-                        E0038,
-                        "the {} `{}` cannot be made into an object",
-                        tcx.def_descr(def_id),
-                        tcx.item_name(def_id),
-                    );
-                    err.note(
-                        rustc_middle::traits::ObjectSafetyViolation::SupertraitSelf(smallvec![])
-                            .error_msg(),
-                    );
-                    err.emit();
-                }
-
-                ty::ExistentialTraitRef { def_id: trait_ref.def_id, substs }
-            })
-        });
-
-        let existential_projections = projection_bounds
-            .iter()
-            // We filter out traits that don't have `Self` as their self type above,
-            // we need to do the same for projections.
-            .filter(|(bound, _)| bound.skip_binder().self_ty() == dummy_self)
-            .map(|(bound, _)| {
-                bound.map_bound(|mut b| {
-                    assert_eq!(b.projection_ty.self_ty(), dummy_self);
-
-                    // Like for trait refs, verify that `dummy_self` did not leak inside default type
-                    // parameters.
-                    let references_self = b.projection_ty.substs.iter().skip(1).any(|arg| {
-                        if arg.walk().any(|arg| arg == dummy_self.into()) {
-                            return true;
-                        }
-                        false
-                    });
-                    if references_self {
-                        let guar = tcx.sess.delay_span_bug(
-                            span,
-                            "trait object projection bounds reference `Self`",
-                        );
-                        let substs: Vec<_> = b
-                            .projection_ty
-                            .substs
-                            .iter()
-                            .map(|arg| {
-                                if arg.walk().any(|arg| arg == dummy_self.into()) {
-                                    return tcx.ty_error(guar).into();
-                                }
-                                arg
-                            })
-                            .collect();
-                        b.projection_ty.substs = tcx.mk_substs(&substs);
-                    }
-
-                    ty::ExistentialProjection::erase_self_ty(tcx, b)
-                })
-            });
-
-        let regular_trait_predicates = existential_trait_refs
-            .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait));
-        let auto_trait_predicates = auto_traits.into_iter().map(|trait_ref| {
-            ty::Binder::dummy(ty::ExistentialPredicate::AutoTrait(trait_ref.trait_ref().def_id()))
-        });
-        // N.b. principal, projections, auto traits
-        // FIXME: This is actually wrong with multiple principals in regards to symbol mangling
-        let mut v = regular_trait_predicates
-            .chain(
-                existential_projections.map(|x| x.map_bound(ty::ExistentialPredicate::Projection)),
-            )
-            .chain(auto_trait_predicates)
-            .collect::<SmallVec<[_; 8]>>();
-        v.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder()));
-        v.dedup();
-        let existential_predicates = tcx.mk_poly_existential_predicates(&v);
-
-        // Use explicitly-specified region bound.
-        let region_bound = if !lifetime.is_elided() {
-            self.ast_region_to_region(lifetime, None)
-        } else {
-            self.compute_object_lifetime_bound(span, existential_predicates).unwrap_or_else(|| {
-                if tcx.named_bound_var(lifetime.hir_id).is_some() {
-                    self.ast_region_to_region(lifetime, None)
-                } else {
-                    self.re_infer(None, span).unwrap_or_else(|| {
-                        let mut err = struct_span_err!(
-                            tcx.sess,
-                            span,
-                            E0228,
-                            "the lifetime bound for this object type cannot be deduced \
-                             from context; please supply an explicit bound"
-                        );
-                        let e = if borrowed {
-                            // We will have already emitted an error E0106 complaining about a
-                            // missing named lifetime in `&dyn Trait`, so we elide this one.
-                            err.delay_as_bug()
-                        } else {
-                            err.emit()
-                        };
-                        ty::Region::new_error(tcx, e)
-                    })
-                }
-            })
-        };
-        debug!("region_bound: {:?}", region_bound);
-
-        let ty = tcx.mk_dynamic(existential_predicates, region_bound, representation);
-        debug!("trait_object_type: {:?}", ty);
-        ty
-    }
-
     fn report_ambiguous_associated_type(
         &self,
         span: Span,
@@ -1893,6 +1510,15 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
     ) -> Result<Option<(Ty<'tcx>, DefId)>, ErrorGuaranteed> {
         let tcx = self.tcx();
 
+        // Don't attempt to look up inherent associated types when the feature is not enabled.
+        // Theoretically it'd be fine to do so since we feature-gate their definition site.
+        // However, due to current limitations of the implementation (caused by us performing
+        // selection in AstConv), IATs can lead to cycle errors (#108491, #110106) which mask the
+        // feature-gate error, needlessly confusing users that use IATs by accident (#113265).
+        if !tcx.features().inherent_associated_types {
+            return Ok(None);
+        }
+
         let candidates: Vec<_> = tcx
             .inherent_impls(adt_did)
             .iter()
@@ -1903,11 +1529,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             return Ok(None);
         }
 
-        if !tcx.features().inherent_associated_types {
-            tcx.sess
-                .delay_span_bug(span, "found inherent assoc type without the feature being gated");
-        }
-
         //
         // Select applicable inherent associated type candidates modulo regions.
         //
@@ -1952,10 +1573,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
 
                 fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
                     match *ty.kind() {
-                        ty::Bound(_, bv) => self.tcx.mk_placeholder(ty::PlaceholderType {
-                            universe: self.universe,
-                            bound: bv,
-                        }),
+                        ty::Bound(_, bv) => Ty::new_placeholder(
+                            self.tcx,
+                            ty::PlaceholderType { universe: self.universe, bound: bv },
+                        ),
                         _ => ty.super_fold_with(self),
                     }
                 }
@@ -1967,7 +1588,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     assert!(!ct.ty().has_escaping_bound_vars());
 
                     match ct.kind() {
-                        ty::ConstKind::Bound(_, bv) => self.tcx.mk_const(
+                        ty::ConstKind::Bound(_, bv) => ty::Const::new_placeholder(
+                            self.tcx,
                             ty::PlaceholderConst { universe: self.universe, bound: bv },
                             ct.ty(),
                         ),
@@ -2043,7 +1665,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     .chain(substs.into_iter().skip(parent_substs.len())),
             );
 
-            let ty = tcx.mk_alias(ty::Inherent, tcx.mk_alias_ty(assoc_item, substs));
+            let ty = Ty::new_alias(tcx, ty::Inherent, tcx.mk_alias_ty(assoc_item, substs));
 
             return Ok(Some((ty, assoc_item)));
         }
@@ -2228,7 +1850,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 &[path_str],
                 item_segment.ident.name,
             );
-            return tcx.ty_error(reported)
+            return Ty::new_error(tcx,reported)
         };
 
         debug!("qpath_to_ty: self_type={:?}", self_ty);
@@ -2251,7 +1873,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
 
         debug!("qpath_to_ty: trait_ref={:?}", trait_ref);
 
-        tcx.mk_projection(item_def_id, item_substs)
+        Ty::new_projection(tcx, item_def_id, item_substs)
     }
 
     pub fn prohibit_generics<'a>(
@@ -2514,7 +2136,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     err.note("`impl Trait` types can't have type parameters");
                 });
                 let substs = self.ast_path_substs_for_ty(span, did, item_segment.0);
-                tcx.mk_opaque(did, substs)
+                Ty::new_opaque(tcx, did, substs)
             }
             Res::Def(
                 DefKind::Enum
@@ -2566,16 +2188,16 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                             var: ty::BoundVar::from_u32(index),
                             kind: ty::BoundTyKind::Param(def_id, name),
                         };
-                        tcx.mk_bound(debruijn, br)
+                        Ty::new_bound(tcx, debruijn, br)
                     }
                     Some(rbv::ResolvedArg::EarlyBound(_)) => {
                         let def_id = def_id.expect_local();
                         let item_def_id = tcx.hir().ty_param_owner(def_id);
                         let generics = tcx.generics_of(item_def_id);
                         let index = generics.param_def_id_to_index[&def_id.to_def_id()];
-                        tcx.mk_ty_param(index, tcx.hir().ty_param_name(def_id))
+                        Ty::new_param(tcx, index, tcx.hir().ty_param_name(def_id))
                     }
-                    Some(rbv::ResolvedArg::Error(guar)) => tcx.ty_error(guar),
+                    Some(rbv::ResolvedArg::Error(guar)) => Ty::new_error(tcx, guar),
                     arg => bug!("unexpected bound var resolution for {hir_id:?}: {arg:?}"),
                 }
             }
@@ -2687,7 +2309,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     {
                         err.span_note(impl_.self_ty.span, "not a concrete type");
                     }
-                    tcx.ty_error(err.emit())
+                    Ty::new_error(tcx, err.emit())
                 } else {
                     ty
                 }
@@ -2728,9 +2350,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 match prim_ty {
                     hir::PrimTy::Bool => tcx.types.bool,
                     hir::PrimTy::Char => tcx.types.char,
-                    hir::PrimTy::Int(it) => tcx.mk_mach_int(ty::int_ty(it)),
-                    hir::PrimTy::Uint(uit) => tcx.mk_mach_uint(ty::uint_ty(uit)),
-                    hir::PrimTy::Float(ft) => tcx.mk_mach_float(ty::float_ty(ft)),
+                    hir::PrimTy::Int(it) => Ty::new_int(tcx, ty::int_ty(it)),
+                    hir::PrimTy::Uint(uit) => Ty::new_uint(tcx, ty::uint_ty(uit)),
+                    hir::PrimTy::Float(ft) => Ty::new_float(tcx, ty::float_ty(ft)),
                     hir::PrimTy::Str => tcx.types.str_,
                 }
             }
@@ -2740,7 +2362,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     .sess
                     .delay_span_bug(path.span, "path with `Res::Err` but no error emitted");
                 self.set_tainted_by_errors(e);
-                self.tcx().ty_error(e)
+                Ty::new_error(self.tcx(), e)
             }
             _ => span_bug!(span, "unexpected resolution: {:?}", path.res),
         }
@@ -2765,31 +2387,27 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         let tcx = self.tcx();
 
         let result_ty = match &ast_ty.kind {
-            hir::TyKind::Slice(ty) => tcx.mk_slice(self.ast_ty_to_ty(ty)),
+            hir::TyKind::Slice(ty) => Ty::new_slice(tcx, self.ast_ty_to_ty(ty)),
             hir::TyKind::Ptr(mt) => {
-                tcx.mk_ptr(ty::TypeAndMut { ty: self.ast_ty_to_ty(mt.ty), mutbl: mt.mutbl })
+                Ty::new_ptr(tcx, ty::TypeAndMut { ty: self.ast_ty_to_ty(mt.ty), mutbl: mt.mutbl })
             }
             hir::TyKind::Ref(region, mt) => {
                 let r = self.ast_region_to_region(region, None);
                 debug!(?r);
                 let t = self.ast_ty_to_ty_inner(mt.ty, true, false);
-                tcx.mk_ref(r, ty::TypeAndMut { ty: t, mutbl: mt.mutbl })
+                Ty::new_ref(tcx, r, ty::TypeAndMut { ty: t, mutbl: mt.mutbl })
             }
             hir::TyKind::Never => tcx.types.never,
             hir::TyKind::Tup(fields) => {
-                tcx.mk_tup_from_iter(fields.iter().map(|t| self.ast_ty_to_ty(t)))
+                Ty::new_tup_from_iter(tcx, fields.iter().map(|t| self.ast_ty_to_ty(t)))
             }
             hir::TyKind::BareFn(bf) => {
                 require_c_abi_if_c_variadic(tcx, bf.decl, bf.abi, ast_ty.span);
 
-                tcx.mk_fn_ptr(self.ty_of_fn(
-                    ast_ty.hir_id,
-                    bf.unsafety,
-                    bf.abi,
-                    bf.decl,
-                    None,
-                    Some(ast_ty),
-                ))
+                Ty::new_fn_ptr(
+                    tcx,
+                    self.ty_of_fn(ast_ty.hir_id, bf.unsafety, bf.abi, bf.decl, None, Some(ast_ty)),
+                )
             }
             hir::TyKind::TraitObject(bounds, lifetime, repr) => {
                 self.maybe_lint_bare_trait(ast_ty, in_path);
@@ -2798,7 +2416,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     TraitObjectSyntax::DynStar => ty::DynStar,
                 };
 
-                self.conv_object_ty_poly_trait_ref(ast_ty.span, bounds, lifetime, borrowed, repr)
+                self.conv_object_ty_poly_trait_ref(
+                    ast_ty.span,
+                    ast_ty.hir_id,
+                    bounds,
+                    lifetime,
+                    borrowed,
+                    repr,
+                )
             }
             hir::TyKind::Path(hir::QPath::Resolved(maybe_qself, path)) => {
                 debug!(?maybe_qself, ?path);
@@ -2829,7 +2454,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 let ty = self.ast_ty_to_ty_inner(qself, false, true);
                 self.associated_path_to_ty(ast_ty.hir_id, ast_ty.span, ty, qself, segment, false)
                     .map(|(ty, _, _)| ty)
-                    .unwrap_or_else(|guar| tcx.ty_error(guar))
+                    .unwrap_or_else(|guar| Ty::new_error(tcx, guar))
             }
             &hir::TyKind::Path(hir::QPath::LangItem(lang_item, span, _)) => {
                 let def_id = tcx.require_lang_item(lang_item, Some(span));
@@ -2853,7 +2478,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     }
                 };
 
-                tcx.mk_array_with_const_len(self.ast_ty_to_ty(ty), length)
+                Ty::new_array_with_const_len(tcx, self.ast_ty_to_ty(ty), length)
             }
             hir::TyKind::Typeof(e) => {
                 let ty_erased = tcx.type_of(e.def_id).subst_identity();
@@ -2877,7 +2502,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 // handled specially and will not descend into this routine.
                 self.ty_infer(None, ast_ty.span)
             }
-            hir::TyKind::Err(guar) => tcx.ty_error(*guar),
+            hir::TyKind::Err(guar) => Ty::new_error(tcx, *guar),
         };
 
         self.record_ty(ast_ty.hir_id, result_ty, ast_ty.span);
@@ -2914,7 +2539,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         });
         debug!("impl_trait_ty_to_ty: substs={:?}", substs);
 
-        if in_trait { tcx.mk_projection(def_id, substs) } else { tcx.mk_opaque(def_id, substs) }
+        if in_trait {
+            Ty::new_projection(tcx, def_id, substs)
+        } else {
+            Ty::new_opaque(tcx, def_id, substs)
+        }
     }
 
     pub fn ty_of_arg(&self, ty: &hir::Ty<'_>, expected_ty: Option<Ty<'tcx>>) -> Ty<'tcx> {
@@ -2983,7 +2612,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     self.ast_ty_to_ty(output)
                 }
             }
-            hir::FnRetTy::DefaultReturn(..) => tcx.mk_unit(),
+            hir::FnRetTy::DefaultReturn(..) => Ty::new_unit(tcx,),
         };
 
         debug!(?output_ty);
diff --git a/compiler/rustc_hir_analysis/src/astconv/object_safety.rs b/compiler/rustc_hir_analysis/src/astconv/object_safety.rs
new file mode 100644
index 00000000000..9227ee93451
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/astconv/object_safety.rs
@@ -0,0 +1,408 @@
+use crate::astconv::{GenericArgCountMismatch, GenericArgCountResult, OnlySelfBounds};
+use crate::bounds::Bounds;
+use crate::errors::TraitObjectDeclaredWithNoTraits;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::struct_span_err;
+use rustc_hir as hir;
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::def_id::DefId;
+use rustc_lint_defs::builtin::UNUSED_ASSOCIATED_TYPE_BOUNDS;
+use rustc_middle::ty::{self, Ty};
+use rustc_middle::ty::{DynKind, ToPredicate};
+use rustc_span::Span;
+use rustc_trait_selection::traits::error_reporting::report_object_safety_error;
+use rustc_trait_selection::traits::{self, astconv_object_safety_violations};
+
+use smallvec::{smallvec, SmallVec};
+use std::collections::BTreeSet;
+
+use super::AstConv;
+
+impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
+    pub(super) fn conv_object_ty_poly_trait_ref(
+        &self,
+        span: Span,
+        hir_id: hir::HirId,
+        hir_trait_bounds: &[hir::PolyTraitRef<'_>],
+        lifetime: &hir::Lifetime,
+        borrowed: bool,
+        representation: DynKind,
+    ) -> Ty<'tcx> {
+        let tcx = self.tcx();
+
+        let mut bounds = Bounds::default();
+        let mut potential_assoc_types = Vec::new();
+        let dummy_self = self.tcx().types.trait_object_dummy_self;
+        for trait_bound in hir_trait_bounds.iter().rev() {
+            if let GenericArgCountResult {
+                correct:
+                    Err(GenericArgCountMismatch { invalid_args: cur_potential_assoc_types, .. }),
+                ..
+            } = self.instantiate_poly_trait_ref(
+                &trait_bound.trait_ref,
+                trait_bound.span,
+                ty::BoundConstness::NotConst,
+                ty::ImplPolarity::Positive,
+                dummy_self,
+                &mut bounds,
+                false,
+                // FIXME: This should be `true`, but we don't really handle
+                // associated type bounds or type aliases in objects in a way
+                // that makes this meaningful, I think.
+                OnlySelfBounds(false),
+            ) {
+                potential_assoc_types.extend(cur_potential_assoc_types);
+            }
+        }
+
+        let mut trait_bounds = vec![];
+        let mut projection_bounds = vec![];
+        for (pred, span) in bounds.clauses() {
+            let bound_pred = pred.kind();
+            match bound_pred.skip_binder() {
+                ty::ClauseKind::Trait(trait_pred) => {
+                    assert_eq!(trait_pred.polarity, ty::ImplPolarity::Positive);
+                    trait_bounds.push((
+                        bound_pred.rebind(trait_pred.trait_ref),
+                        span,
+                        trait_pred.constness,
+                    ));
+                }
+                ty::ClauseKind::Projection(proj) => {
+                    projection_bounds.push((bound_pred.rebind(proj), span));
+                }
+                ty::ClauseKind::TypeOutlives(_) => {
+                    // Do nothing, we deal with regions separately
+                }
+                ty::ClauseKind::RegionOutlives(_)
+                | ty::ClauseKind::ConstArgHasType(..)
+                | ty::ClauseKind::WellFormed(_)
+                | ty::ClauseKind::ConstEvaluatable(_) => {
+                    bug!()
+                }
+            }
+        }
+
+        // Expand trait aliases recursively and check that only one regular (non-auto) trait
+        // is used and no 'maybe' bounds are used.
+        let expanded_traits =
+            traits::expand_trait_aliases(tcx, trait_bounds.iter().map(|&(a, b, _)| (a, b)));
+
+        let (mut auto_traits, regular_traits): (Vec<_>, Vec<_>) = expanded_traits
+            .filter(|i| i.trait_ref().self_ty().skip_binder() == dummy_self)
+            .partition(|i| tcx.trait_is_auto(i.trait_ref().def_id()));
+        if regular_traits.len() > 1 {
+            let first_trait = &regular_traits[0];
+            let additional_trait = &regular_traits[1];
+            let mut err = struct_span_err!(
+                tcx.sess,
+                additional_trait.bottom().1,
+                E0225,
+                "only auto traits can be used as additional traits in a trait object"
+            );
+            additional_trait.label_with_exp_info(
+                &mut err,
+                "additional non-auto trait",
+                "additional use",
+            );
+            first_trait.label_with_exp_info(&mut err, "first non-auto trait", "first use");
+            err.help(format!(
+                "consider creating a new trait with all of these as supertraits and using that \
+             trait here instead: `trait NewTrait: {} {{}}`",
+                regular_traits
+                    .iter()
+                    .map(|t| t.trait_ref().print_only_trait_path().to_string())
+                    .collect::<Vec<_>>()
+                    .join(" + "),
+            ));
+            err.note(
+                "auto-traits like `Send` and `Sync` are traits that have special properties; \
+             for more information on them, visit \
+             <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>",
+            );
+            err.emit();
+        }
+
+        if regular_traits.is_empty() && auto_traits.is_empty() {
+            let trait_alias_span = trait_bounds
+                .iter()
+                .map(|&(trait_ref, _, _)| trait_ref.def_id())
+                .find(|&trait_ref| tcx.is_trait_alias(trait_ref))
+                .map(|trait_ref| tcx.def_span(trait_ref));
+            let reported =
+                tcx.sess.emit_err(TraitObjectDeclaredWithNoTraits { span, trait_alias_span });
+            return Ty::new_error(tcx, reported);
+        }
+
+        // Check that there are no gross object safety violations;
+        // most importantly, that the supertraits don't contain `Self`,
+        // to avoid ICEs.
+        for item in &regular_traits {
+            let object_safety_violations =
+                astconv_object_safety_violations(tcx, item.trait_ref().def_id());
+            if !object_safety_violations.is_empty() {
+                let reported = report_object_safety_error(
+                    tcx,
+                    span,
+                    item.trait_ref().def_id(),
+                    &object_safety_violations,
+                )
+                .emit();
+                return Ty::new_error(tcx, reported);
+            }
+        }
+
+        // Use a `BTreeSet` to keep output in a more consistent order.
+        let mut associated_types: FxHashMap<Span, BTreeSet<DefId>> = FxHashMap::default();
+
+        let regular_traits_refs_spans = trait_bounds
+            .into_iter()
+            .filter(|(trait_ref, _, _)| !tcx.trait_is_auto(trait_ref.def_id()));
+
+        for (base_trait_ref, span, constness) in regular_traits_refs_spans {
+            assert_eq!(constness, ty::BoundConstness::NotConst);
+            let base_pred: ty::Predicate<'tcx> = base_trait_ref.to_predicate(tcx);
+            for pred in traits::elaborate(tcx, [base_pred]) {
+                debug!("conv_object_ty_poly_trait_ref: observing object predicate `{:?}`", pred);
+
+                let bound_predicate = pred.kind();
+                match bound_predicate.skip_binder() {
+                    ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
+                        let pred = bound_predicate.rebind(pred);
+                        associated_types.entry(span).or_default().extend(
+                            tcx.associated_items(pred.def_id())
+                                .in_definition_order()
+                                .filter(|item| item.kind == ty::AssocKind::Type)
+                                .filter(|item| item.opt_rpitit_info.is_none())
+                                .map(|item| item.def_id),
+                        );
+                    }
+                    ty::PredicateKind::Clause(ty::ClauseKind::Projection(pred)) => {
+                        let pred = bound_predicate.rebind(pred);
+                        // A `Self` within the original bound will be substituted with a
+                        // `trait_object_dummy_self`, so check for that.
+                        let references_self = match pred.skip_binder().term.unpack() {
+                            ty::TermKind::Ty(ty) => ty.walk().any(|arg| arg == dummy_self.into()),
+                            ty::TermKind::Const(c) => {
+                                c.ty().walk().any(|arg| arg == dummy_self.into())
+                            }
+                        };
+
+                        // If the projection output contains `Self`, force the user to
+                        // elaborate it explicitly to avoid a lot of complexity.
+                        //
+                        // The "classically useful" case is the following:
+                        // ```
+                        //     trait MyTrait: FnMut() -> <Self as MyTrait>::MyOutput {
+                        //         type MyOutput;
+                        //     }
+                        // ```
+                        //
+                        // Here, the user could theoretically write `dyn MyTrait<Output = X>`,
+                        // but actually supporting that would "expand" to an infinitely-long type
+                        // `fix $ τ → dyn MyTrait<MyOutput = X, Output = <τ as MyTrait>::MyOutput`.
+                        //
+                        // Instead, we force the user to write
+                        // `dyn MyTrait<MyOutput = X, Output = X>`, which is uglier but works. See
+                        // the discussion in #56288 for alternatives.
+                        if !references_self {
+                            // Include projections defined on supertraits.
+                            projection_bounds.push((pred, span));
+                        }
+                    }
+                    _ => (),
+                }
+            }
+        }
+
+        // `dyn Trait<Assoc = Foo>` desugars to (not Rust syntax) `dyn Trait where <Self as Trait>::Assoc = Foo`.
+        // So every `Projection` clause is an `Assoc = Foo` bound. `associated_types` contains all associated
+        // types's `DefId`, so the following loop removes all the `DefIds` of the associated types that have a
+        // corresponding `Projection` clause
+        for def_ids in associated_types.values_mut() {
+            for (projection_bound, span) in &projection_bounds {
+                let def_id = projection_bound.projection_def_id();
+                def_ids.remove(&def_id);
+                if tcx.generics_require_sized_self(def_id) {
+                    tcx.emit_spanned_lint(
+                        UNUSED_ASSOCIATED_TYPE_BOUNDS,
+                        hir_id,
+                        *span,
+                        crate::errors::UnusedAssociatedTypeBounds { span: *span },
+                    );
+                }
+            }
+            // If the associated type has a `where Self: Sized` bound, we do not need to constrain the associated
+            // type in the `dyn Trait`.
+            def_ids.retain(|def_id| !tcx.generics_require_sized_self(def_id));
+        }
+
+        self.complain_about_missing_associated_types(
+            associated_types,
+            potential_assoc_types,
+            hir_trait_bounds,
+        );
+
+        // De-duplicate auto traits so that, e.g., `dyn Trait + Send + Send` is the same as
+        // `dyn Trait + Send`.
+        // We remove duplicates by inserting into a `FxHashSet` to avoid re-ordering
+        // the bounds
+        let mut duplicates = FxHashSet::default();
+        auto_traits.retain(|i| duplicates.insert(i.trait_ref().def_id()));
+        debug!("regular_traits: {:?}", regular_traits);
+        debug!("auto_traits: {:?}", auto_traits);
+
+        // Erase the `dummy_self` (`trait_object_dummy_self`) used above.
+        let existential_trait_refs = regular_traits.iter().map(|i| {
+            i.trait_ref().map_bound(|trait_ref: ty::TraitRef<'tcx>| {
+                assert_eq!(trait_ref.self_ty(), dummy_self);
+
+                // Verify that `dummy_self` did not leak inside default type parameters. This
+                // could not be done at path creation, since we need to see through trait aliases.
+                let mut missing_type_params = vec![];
+                let mut references_self = false;
+                let generics = tcx.generics_of(trait_ref.def_id);
+                let substs: Vec<_> = trait_ref
+                    .substs
+                    .iter()
+                    .enumerate()
+                    .skip(1) // Remove `Self` for `ExistentialPredicate`.
+                    .map(|(index, arg)| {
+                        if arg == dummy_self.into() {
+                            let param = &generics.params[index];
+                            missing_type_params.push(param.name);
+                            return Ty::new_misc_error(tcx).into();
+                        } else if arg.walk().any(|arg| arg == dummy_self.into()) {
+                            references_self = true;
+                            return Ty::new_misc_error(tcx).into();
+                        }
+                        arg
+                    })
+                    .collect();
+                let substs = tcx.mk_substs(&substs);
+
+                let span = i.bottom().1;
+                let empty_generic_args = hir_trait_bounds.iter().any(|hir_bound| {
+                    hir_bound.trait_ref.path.res == Res::Def(DefKind::Trait, trait_ref.def_id)
+                        && hir_bound.span.contains(span)
+                });
+                self.complain_about_missing_type_params(
+                    missing_type_params,
+                    trait_ref.def_id,
+                    span,
+                    empty_generic_args,
+                );
+
+                if references_self {
+                    let def_id = i.bottom().0.def_id();
+                    let mut err = struct_span_err!(
+                        tcx.sess,
+                        i.bottom().1,
+                        E0038,
+                        "the {} `{}` cannot be made into an object",
+                        tcx.def_descr(def_id),
+                        tcx.item_name(def_id),
+                    );
+                    err.note(
+                        rustc_middle::traits::ObjectSafetyViolation::SupertraitSelf(smallvec![])
+                            .error_msg(),
+                    );
+                    err.emit();
+                }
+
+                ty::ExistentialTraitRef { def_id: trait_ref.def_id, substs }
+            })
+        });
+
+        let existential_projections = projection_bounds
+            .iter()
+            // We filter out traits that don't have `Self` as their self type above,
+            // we need to do the same for projections.
+            .filter(|(bound, _)| bound.skip_binder().self_ty() == dummy_self)
+            .map(|(bound, _)| {
+                bound.map_bound(|mut b| {
+                    assert_eq!(b.projection_ty.self_ty(), dummy_self);
+
+                    // Like for trait refs, verify that `dummy_self` did not leak inside default type
+                    // parameters.
+                    let references_self = b.projection_ty.substs.iter().skip(1).any(|arg| {
+                        if arg.walk().any(|arg| arg == dummy_self.into()) {
+                            return true;
+                        }
+                        false
+                    });
+                    if references_self {
+                        let guar = tcx.sess.delay_span_bug(
+                            span,
+                            "trait object projection bounds reference `Self`",
+                        );
+                        let substs: Vec<_> = b
+                            .projection_ty
+                            .substs
+                            .iter()
+                            .map(|arg| {
+                                if arg.walk().any(|arg| arg == dummy_self.into()) {
+                                    return Ty::new_error(tcx, guar).into();
+                                }
+                                arg
+                            })
+                            .collect();
+                        b.projection_ty.substs = tcx.mk_substs(&substs);
+                    }
+
+                    ty::ExistentialProjection::erase_self_ty(tcx, b)
+                })
+            });
+
+        let regular_trait_predicates = existential_trait_refs
+            .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait));
+        let auto_trait_predicates = auto_traits.into_iter().map(|trait_ref| {
+            ty::Binder::dummy(ty::ExistentialPredicate::AutoTrait(trait_ref.trait_ref().def_id()))
+        });
+        // N.b. principal, projections, auto traits
+        // FIXME: This is actually wrong with multiple principals in regards to symbol mangling
+        let mut v = regular_trait_predicates
+            .chain(
+                existential_projections.map(|x| x.map_bound(ty::ExistentialPredicate::Projection)),
+            )
+            .chain(auto_trait_predicates)
+            .collect::<SmallVec<[_; 8]>>();
+        v.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder()));
+        v.dedup();
+        let existential_predicates = tcx.mk_poly_existential_predicates(&v);
+
+        // Use explicitly-specified region bound.
+        let region_bound = if !lifetime.is_elided() {
+            self.ast_region_to_region(lifetime, None)
+        } else {
+            self.compute_object_lifetime_bound(span, existential_predicates).unwrap_or_else(|| {
+                if tcx.named_bound_var(lifetime.hir_id).is_some() {
+                    self.ast_region_to_region(lifetime, None)
+                } else {
+                    self.re_infer(None, span).unwrap_or_else(|| {
+                        let mut err = struct_span_err!(
+                            tcx.sess,
+                            span,
+                            E0228,
+                            "the lifetime bound for this object type cannot be deduced \
+                         from context; please supply an explicit bound"
+                        );
+                        let e = if borrowed {
+                            // We will have already emitted an error E0106 complaining about a
+                            // missing named lifetime in `&dyn Trait`, so we elide this one.
+                            err.delay_as_bug()
+                        } else {
+                            err.emit()
+                        };
+                        ty::Region::new_error(tcx, e)
+                    })
+                }
+            })
+        };
+        debug!("region_bound: {:?}", region_bound);
+
+        let ty = Ty::new_dynamic(tcx, existential_predicates, region_bound, representation);
+        debug!("trait_object_type: {:?}", ty);
+        ty
+    }
+}
diff --git a/compiler/rustc_hir_analysis/src/autoderef.rs b/compiler/rustc_hir_analysis/src/autoderef.rs
index 8aa9a2c2734..c07ac35cba3 100644
--- a/compiler/rustc_hir_analysis/src/autoderef.rs
+++ b/compiler/rustc_hir_analysis/src/autoderef.rs
@@ -148,8 +148,11 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
             return None;
         }
 
-        let (normalized_ty, obligations) =
-            self.structurally_normalize(tcx.mk_projection(tcx.lang_items().deref_target()?, [ty]))?;
+        let (normalized_ty, obligations) = self.structurally_normalize(Ty::new_projection(
+            tcx,
+            tcx.lang_items().deref_target()?,
+            [ty],
+        ))?;
         debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations);
         self.state.obligations.extend(obligations);
 
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 346e153a69d..270b90fa6b2 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -224,7 +224,8 @@ fn check_opaque(tcx: TyCtxt<'_>, id: hir::ItemId) {
     if check_opaque_for_cycles(tcx, item.owner_id.def_id, substs, span, &origin).is_err() {
         return;
     }
-    check_opaque_meets_bounds(tcx, item.owner_id.def_id, span, &origin);
+
+    let _ = check_opaque_meets_bounds(tcx, item.owner_id.def_id, span, &origin);
 }
 
 /// Checks that an opaque type does not use `Self` or `T::Foo` projections that would result
@@ -307,9 +308,9 @@ pub(super) fn check_opaque_for_inheriting_lifetimes(
     {
         let substs = InternalSubsts::identity_for_item(tcx, def_id);
         let opaque_identity_ty = if in_trait && !tcx.lower_impl_trait_in_trait_to_assoc_ty() {
-            tcx.mk_projection(def_id.to_def_id(), substs)
+            Ty::new_projection(tcx, def_id.to_def_id(), substs)
         } else {
-            tcx.mk_opaque(def_id.to_def_id(), substs)
+            Ty::new_opaque(tcx, def_id.to_def_id(), substs)
         };
         let mut visitor = ProhibitOpaqueVisitor {
             opaque_identity_ty,
@@ -395,7 +396,7 @@ fn check_opaque_meets_bounds<'tcx>(
     def_id: LocalDefId,
     span: Span,
     origin: &hir::OpaqueTyOrigin,
-) {
+) -> Result<(), ErrorGuaranteed> {
     let defining_use_anchor = match *origin {
         hir::OpaqueTyOrigin::FnReturn(did) | hir::OpaqueTyOrigin::AsyncFn(did) => did,
         hir::OpaqueTyOrigin::TyAlias { .. } => tcx.impl_trait_parent(def_id),
@@ -409,7 +410,7 @@ fn check_opaque_meets_bounds<'tcx>(
     let ocx = ObligationCtxt::new(&infcx);
 
     let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id());
-    let opaque_ty = tcx.mk_opaque(def_id.to_def_id(), substs);
+    let opaque_ty = Ty::new_opaque(tcx, def_id.to_def_id(), substs);
 
     // `ReErased` regions appear in the "parent_substs" of closures/generators.
     // We're ignoring them here and replacing them with fresh region variables.
@@ -429,10 +430,10 @@ fn check_opaque_meets_bounds<'tcx>(
         Ok(()) => {}
         Err(ty_err) => {
             let ty_err = ty_err.to_string(tcx);
-            tcx.sess.delay_span_bug(
+            return Err(tcx.sess.delay_span_bug(
                 span,
                 format!("could not unify `{hidden_ty}` with revealed type:\n{ty_err}"),
-            );
+            ));
         }
     }
 
@@ -447,7 +448,8 @@ fn check_opaque_meets_bounds<'tcx>(
     // version.
     let errors = ocx.select_all_or_error();
     if !errors.is_empty() {
-        infcx.err_ctxt().report_fulfillment_errors(&errors);
+        let guar = infcx.err_ctxt().report_fulfillment_errors(&errors);
+        return Err(guar);
     }
     match origin {
         // Checked when type checking the function containing them.
@@ -461,14 +463,15 @@ fn check_opaque_meets_bounds<'tcx>(
             if tcx.def_kind(tcx.parent(def_id.to_def_id())) == DefKind::OpaqueTy => {}
         // Can have different predicates to their defining use
         hir::OpaqueTyOrigin::TyAlias { .. } => {
-            let wf_tys = ocx.assumed_wf_types(param_env, span, def_id);
+            let wf_tys = ocx.assumed_wf_types_and_report_errors(param_env, def_id)?;
             let implied_bounds = infcx.implied_bounds_tys(param_env, def_id, wf_tys);
             let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
-            let _ = ocx.resolve_regions_and_report_errors(defining_use_anchor, &outlives_env);
+            ocx.resolve_regions_and_report_errors(defining_use_anchor, &outlives_env)?;
         }
     }
     // Clean up after ourselves
     let _ = infcx.take_opaque_types();
+    Ok(())
 }
 
 fn is_enum_of_nonnullable_ptr<'tcx>(
diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
index 76edeccaf29..22e576e345e 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
@@ -271,7 +271,7 @@ fn compare_method_predicate_entailment<'tcx>(
         infer::HigherRankedType,
         tcx.fn_sig(impl_m.def_id).subst_identity(),
     );
-    let unnormalized_impl_fty = tcx.mk_fn_ptr(ty::Binder::dummy(unnormalized_impl_sig));
+    let unnormalized_impl_fty = Ty::new_fn_ptr(tcx, ty::Binder::dummy(unnormalized_impl_sig));
 
     let norm_cause = ObligationCause::misc(impl_m_span, impl_m_def_id);
     let impl_sig = ocx.normalize(&norm_cause, param_env, unnormalized_impl_sig);
@@ -288,7 +288,7 @@ fn compare_method_predicate_entailment<'tcx>(
     // We also have to add the normalized trait signature
     // as we don't normalize during implied bounds computation.
     wf_tys.extend(trait_sig.inputs_and_output.iter());
-    let trait_fty = tcx.mk_fn_ptr(ty::Binder::dummy(trait_sig));
+    let trait_fty = Ty::new_fn_ptr(tcx, ty::Binder::dummy(trait_sig));
 
     debug!("compare_impl_method: trait_fty={:?}", trait_fty);
 
@@ -669,11 +669,13 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
         )
         .fold_with(&mut collector);
 
-    debug_assert_ne!(
-        collector.types.len(),
-        0,
-        "expect >1 RPITITs in call to `collect_return_position_impl_trait_in_trait_tys`"
-    );
+    if !unnormalized_trait_sig.output().references_error() {
+        debug_assert_ne!(
+            collector.types.len(),
+            0,
+            "expect >1 RPITITs in call to `collect_return_position_impl_trait_in_trait_tys`"
+        );
+    }
 
     let trait_sig = ocx.normalize(&norm_cause, param_env, unnormalized_trait_sig);
     trait_sig.error_reported()?;
@@ -803,7 +805,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
                     return_span,
                 }) {
                     Ok(ty) => ty,
-                    Err(guar) => tcx.ty_error(guar),
+                    Err(guar) => Ty::new_error(tcx, guar),
                 };
                 collected_tys.insert(def_id, ty::EarlyBinder::bind(ty));
             }
@@ -812,7 +814,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
                     return_span,
                     format!("could not fully resolve: {ty} => {err:?}"),
                 );
-                collected_tys.insert(def_id, ty::EarlyBinder::bind(tcx.ty_error(reported)));
+                collected_tys.insert(def_id, ty::EarlyBinder::bind(Ty::new_error(tcx, reported)));
             }
         }
     }
@@ -916,7 +918,7 @@ impl<'tcx> ty::FallibleTypeFolder<TyCtxt<'tcx>> for RemapHiddenTyRegions<'tcx> {
                     _ => arg.try_fold_with(self)?,
                 });
             }
-            Ok(self.tcx.mk_opaque(def_id, self.tcx.mk_substs(&mapped_substs)))
+            Ok(Ty::new_opaque(self.tcx, def_id, self.tcx.mk_substs(&mapped_substs)))
         } else {
             t.try_super_fold_with(self)
         }
@@ -2027,7 +2029,8 @@ pub(super) fn check_type_bounds<'tcx>(
                 let kind = ty::BoundTyKind::Param(param.def_id, param.name);
                 let bound_var = ty::BoundVariableKind::Ty(kind);
                 bound_vars.push(bound_var);
-                tcx.mk_bound(
+                Ty::new_bound(
+                    tcx,
                     ty::INNERMOST,
                     ty::BoundTy { var: ty::BoundVar::from_usize(bound_vars.len() - 1), kind },
                 )
@@ -2047,11 +2050,10 @@ pub(super) fn check_type_bounds<'tcx>(
             GenericParamDefKind::Const { .. } => {
                 let bound_var = ty::BoundVariableKind::Const;
                 bound_vars.push(bound_var);
-                tcx.mk_const(
-                    ty::ConstKind::Bound(
-                        ty::INNERMOST,
-                        ty::BoundVar::from_usize(bound_vars.len() - 1),
-                    ),
+                ty::Const::new_bound(
+                    tcx,
+                    ty::INNERMOST,
+                    ty::BoundVar::from_usize(bound_vars.len() - 1),
                     tcx.type_of(param.def_id)
                         .no_bound_vars()
                         .expect("const parameter types cannot be generic"),
@@ -2121,7 +2123,7 @@ pub(super) fn check_type_bounds<'tcx>(
             _ => bug!(),
         }
     };
-    let assumed_wf_types = ocx.assumed_wf_types(param_env, impl_ty_span, impl_ty_def_id);
+    let assumed_wf_types = ocx.assumed_wf_types_and_report_errors(param_env, impl_ty_def_id)?;
 
     let normalize_cause = ObligationCause::new(
         impl_ty_span,
diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
index 36c468e7789..1248f991cc4 100644
--- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs
+++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
@@ -11,7 +11,7 @@ use hir::def_id::DefId;
 use rustc_errors::{struct_span_err, DiagnosticMessage};
 use rustc_hir as hir;
 use rustc_middle::traits::{ObligationCause, ObligationCauseCode};
-use rustc_middle::ty::{self, TyCtxt};
+use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_span::symbol::{kw, sym, Symbol};
 use rustc_target::spec::abi::Abi;
 
@@ -53,14 +53,14 @@ fn equate_intrinsic_type<'tcx>(
         && gen_count_ok(own_counts.types, n_tps, "type")
         && gen_count_ok(own_counts.consts, 0, "const")
     {
-        let fty = tcx.mk_fn_ptr(sig);
+        let fty = Ty::new_fn_ptr(tcx, sig);
         let it_def_id = it.owner_id.def_id;
         let cause = ObligationCause::new(it.span, it_def_id, ObligationCauseCode::IntrinsicType);
         require_same_types(
             tcx,
             &cause,
             ty::ParamEnv::empty(), // FIXME: do all intrinsics have an empty param env?
-            tcx.mk_fn_ptr(tcx.fn_sig(it.owner_id).subst_identity()),
+            Ty::new_fn_ptr(tcx, tcx.fn_sig(it.owner_id).subst_identity()),
             fty,
         );
     }
@@ -134,7 +134,7 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: DefId) -> hir
 /// Remember to add all intrinsics here, in `compiler/rustc_codegen_llvm/src/intrinsic.rs`,
 /// and in `library/core/src/intrinsics.rs`.
 pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
-    let param = |n| tcx.mk_ty_param(n, Symbol::intern(&format!("P{}", n)));
+    let param = |n| Ty::new_param(tcx, n, Symbol::intern(&format!("P{}", n)));
     let intrinsic_id = it.owner_id.to_def_id();
     let intrinsic_name = tcx.item_name(intrinsic_id);
     let name_str = intrinsic_name.as_str();
@@ -156,7 +156,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
                 ty::BoundRegion { var: ty::BoundVar::from_u32(1), kind: ty::BrEnv },
             );
             let va_list_ty = tcx.type_of(did).subst(tcx, &[region.into()]);
-            (tcx.mk_ref(env_region, ty::TypeAndMut { ty: va_list_ty, mutbl }), va_list_ty)
+            (Ty::new_ref(tcx, env_region, ty::TypeAndMut { ty: va_list_ty, mutbl }), va_list_ty)
         })
     };
 
@@ -168,15 +168,15 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
         let (n_tps, inputs, output) = match split[1] {
             "cxchg" | "cxchgweak" => (
                 1,
-                vec![tcx.mk_mut_ptr(param(0)), param(0), param(0)],
-                tcx.mk_tup(&[param(0), tcx.types.bool]),
+                vec![Ty::new_mut_ptr(tcx, param(0)), param(0), param(0)],
+                Ty::new_tup(tcx, &[param(0), tcx.types.bool]),
             ),
-            "load" => (1, vec![tcx.mk_imm_ptr(param(0))], param(0)),
-            "store" => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit()),
+            "load" => (1, vec![Ty::new_imm_ptr(tcx, param(0))], param(0)),
+            "store" => (1, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], Ty::new_unit(tcx)),
 
             "xchg" | "xadd" | "xsub" | "and" | "nand" | "or" | "xor" | "max" | "min" | "umax"
-            | "umin" => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], param(0)),
-            "fence" | "singlethreadfence" => (0, Vec::new(), tcx.mk_unit()),
+            | "umin" => (1, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], param(0)),
+            "fence" | "singlethreadfence" => (0, Vec::new(), Ty::new_unit(tcx)),
             op => {
                 tcx.sess.emit_err(UnrecognizedAtomicOperation { span: it.span, op });
                 return;
@@ -188,19 +188,19 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
         let (n_tps, inputs, output) = match intrinsic_name {
             sym::abort => (0, Vec::new(), tcx.types.never),
             sym::unreachable => (0, Vec::new(), tcx.types.never),
-            sym::breakpoint => (0, Vec::new(), tcx.mk_unit()),
+            sym::breakpoint => (0, Vec::new(), Ty::new_unit(tcx)),
             sym::size_of | sym::pref_align_of | sym::min_align_of | sym::variant_count => {
                 (1, Vec::new(), tcx.types.usize)
             }
             sym::size_of_val | sym::min_align_of_val => {
-                (1, vec![tcx.mk_imm_ptr(param(0))], tcx.types.usize)
+                (1, vec![Ty::new_imm_ptr(tcx, param(0))], tcx.types.usize)
             }
             sym::rustc_peek => (1, vec![param(0)], param(0)),
             sym::caller_location => (0, vec![], tcx.caller_location_ty()),
             sym::assert_inhabited
             | sym::assert_zero_valid
-            | sym::assert_mem_uninitialized_valid => (1, Vec::new(), tcx.mk_unit()),
-            sym::forget => (1, vec![param(0)], tcx.mk_unit()),
+            | sym::assert_mem_uninitialized_valid => (1, Vec::new(), Ty::new_unit(tcx)),
+            sym::forget => (1, vec![param(0)], Ty::new_unit(tcx)),
             sym::transmute | sym::transmute_unchecked => (2, vec![param(0)], param(1)),
             sym::prefetch_read_data
             | sym::prefetch_write_data
@@ -208,75 +208,79 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
             | sym::prefetch_write_instruction => (
                 1,
                 vec![
-                    tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
+                    Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
                     tcx.types.i32,
                 ],
-                tcx.mk_unit(),
+                Ty::new_unit(tcx),
             ),
-            sym::drop_in_place => (1, vec![tcx.mk_mut_ptr(param(0))], tcx.mk_unit()),
+            sym::drop_in_place => (1, vec![Ty::new_mut_ptr(tcx, param(0))], Ty::new_unit(tcx)),
             sym::needs_drop => (1, Vec::new(), tcx.types.bool),
 
-            sym::type_name => (1, Vec::new(), tcx.mk_static_str()),
+            sym::type_name => (1, Vec::new(), Ty::new_static_str(tcx)),
             sym::type_id => (1, Vec::new(), tcx.types.u128),
             sym::offset => (2, vec![param(0), param(1)], param(0)),
             sym::arith_offset => (
                 1,
                 vec![
-                    tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
+                    Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
                     tcx.types.isize,
                 ],
-                tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
+                Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
             ),
             sym::option_payload_ptr => {
                 let option_def_id = tcx.require_lang_item(hir::LangItem::Option, None);
                 let p0 = param(0);
                 (
                     1,
-                    vec![tcx.mk_ptr(ty::TypeAndMut {
-                        ty: tcx.mk_adt(
-                            tcx.adt_def(option_def_id),
-                            tcx.mk_substs_from_iter([ty::GenericArg::from(p0)].into_iter()),
-                        ),
-                        mutbl: hir::Mutability::Not,
-                    })],
-                    tcx.mk_ptr(ty::TypeAndMut { ty: p0, mutbl: hir::Mutability::Not }),
+                    vec![Ty::new_ptr(
+                        tcx,
+                        ty::TypeAndMut {
+                            ty: Ty::new_adt(
+                                tcx,
+                                tcx.adt_def(option_def_id),
+                                tcx.mk_substs_from_iter([ty::GenericArg::from(p0)].into_iter()),
+                            ),
+                            mutbl: hir::Mutability::Not,
+                        },
+                    )],
+                    Ty::new_ptr(tcx, ty::TypeAndMut { ty: p0, mutbl: hir::Mutability::Not }),
                 )
             }
             sym::ptr_mask => (
                 1,
                 vec![
-                    tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
+                    Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
                     tcx.types.usize,
                 ],
-                tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
+                Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
             ),
 
             sym::copy | sym::copy_nonoverlapping => (
                 1,
                 vec![
-                    tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
-                    tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Mut }),
+                    Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
+                    Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Mut }),
                     tcx.types.usize,
                 ],
-                tcx.mk_unit(),
+                Ty::new_unit(tcx),
             ),
             sym::volatile_copy_memory | sym::volatile_copy_nonoverlapping_memory => (
                 1,
                 vec![
-                    tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Mut }),
-                    tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
+                    Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Mut }),
+                    Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
                     tcx.types.usize,
                 ],
-                tcx.mk_unit(),
+                Ty::new_unit(tcx),
             ),
             sym::write_bytes | sym::volatile_set_memory => (
                 1,
                 vec![
-                    tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Mut }),
+                    Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Mut }),
                     tcx.types.u8,
                     tcx.types.usize,
                 ],
-                tcx.mk_unit(),
+                Ty::new_unit(tcx),
             ),
             sym::sqrtf32 => (0, vec![tcx.types.f32], tcx.types.f32),
             sym::sqrtf64 => (0, vec![tcx.types.f64], tcx.types.f64),
@@ -324,10 +328,10 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
             sym::roundevenf64 => (0, vec![tcx.types.f64], tcx.types.f64),
 
             sym::volatile_load | sym::unaligned_volatile_load => {
-                (1, vec![tcx.mk_imm_ptr(param(0))], param(0))
+                (1, vec![Ty::new_imm_ptr(tcx, param(0))], param(0))
             }
             sym::volatile_store | sym::unaligned_volatile_store => {
-                (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit())
+                (1, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], Ty::new_unit(tcx))
             }
 
             sym::ctpop
@@ -339,28 +343,34 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
             | sym::bitreverse => (1, vec![param(0)], param(0)),
 
             sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow => {
-                (1, vec![param(0), param(0)], tcx.mk_tup(&[param(0), tcx.types.bool]))
+                (1, vec![param(0), param(0)], Ty::new_tup(tcx, &[param(0), tcx.types.bool]))
             }
 
-            sym::ptr_guaranteed_cmp => {
-                (1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.u8)
-            }
+            sym::ptr_guaranteed_cmp => (
+                1,
+                vec![Ty::new_imm_ptr(tcx, param(0)), Ty::new_imm_ptr(tcx, param(0))],
+                tcx.types.u8,
+            ),
 
             sym::const_allocate => {
-                (0, vec![tcx.types.usize, tcx.types.usize], tcx.mk_mut_ptr(tcx.types.u8))
+                (0, vec![tcx.types.usize, tcx.types.usize], Ty::new_mut_ptr(tcx, tcx.types.u8))
             }
             sym::const_deallocate => (
                 0,
-                vec![tcx.mk_mut_ptr(tcx.types.u8), tcx.types.usize, tcx.types.usize],
-                tcx.mk_unit(),
+                vec![Ty::new_mut_ptr(tcx, tcx.types.u8), tcx.types.usize, tcx.types.usize],
+                Ty::new_unit(tcx),
             ),
 
-            sym::ptr_offset_from => {
-                (1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.isize)
-            }
-            sym::ptr_offset_from_unsigned => {
-                (1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.usize)
-            }
+            sym::ptr_offset_from => (
+                1,
+                vec![Ty::new_imm_ptr(tcx, param(0)), Ty::new_imm_ptr(tcx, param(0))],
+                tcx.types.isize,
+            ),
+            sym::ptr_offset_from_unsigned => (
+                1,
+                vec![Ty::new_imm_ptr(tcx, param(0)), Ty::new_imm_ptr(tcx, param(0))],
+                tcx.types.usize,
+            ),
             sym::unchecked_div | sym::unchecked_rem | sym::exact_div => {
                 (1, vec![param(0), param(0)], param(0))
             }
@@ -379,12 +389,14 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
             }
             sym::float_to_int_unchecked => (2, vec![param(0)], param(1)),
 
-            sym::assume => (0, vec![tcx.types.bool], tcx.mk_unit()),
+            sym::assume => (0, vec![tcx.types.bool], Ty::new_unit(tcx)),
             sym::likely => (0, vec![tcx.types.bool], tcx.types.bool),
             sym::unlikely => (0, vec![tcx.types.bool], tcx.types.bool),
 
-            sym::read_via_copy => (1, vec![tcx.mk_imm_ptr(param(0))], param(0)),
-            sym::write_via_move => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit()),
+            sym::read_via_copy => (1, vec![Ty::new_imm_ptr(tcx, param(0))], param(0)),
+            sym::write_via_move => {
+                (1, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], Ty::new_unit(tcx))
+            }
 
             sym::discriminant_value => {
                 let assoc_items = tcx.associated_item_def_ids(
@@ -395,48 +407,47 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
                 let br = ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon(None) };
                 (
                     1,
-                    vec![
-                        tcx.mk_imm_ref(
-                            ty::Region::new_late_bound(tcx, ty::INNERMOST, br),
-                            param(0),
-                        ),
-                    ],
-                    tcx.mk_projection(discriminant_def_id, tcx.mk_substs(&[param(0).into()])),
+                    vec![Ty::new_imm_ref(
+                        tcx,
+                        ty::Region::new_late_bound(tcx, ty::INNERMOST, br),
+                        param(0),
+                    )],
+                    Ty::new_projection(tcx, discriminant_def_id, tcx.mk_substs(&[param(0).into()])),
                 )
             }
 
             kw::Try => {
-                let mut_u8 = tcx.mk_mut_ptr(tcx.types.u8);
+                let mut_u8 = Ty::new_mut_ptr(tcx, tcx.types.u8);
                 let try_fn_ty = ty::Binder::dummy(tcx.mk_fn_sig(
                     [mut_u8],
-                    tcx.mk_unit(),
+                    Ty::new_unit(tcx),
                     false,
                     hir::Unsafety::Normal,
                     Abi::Rust,
                 ));
                 let catch_fn_ty = ty::Binder::dummy(tcx.mk_fn_sig(
                     [mut_u8, mut_u8],
-                    tcx.mk_unit(),
+                    Ty::new_unit(tcx),
                     false,
                     hir::Unsafety::Normal,
                     Abi::Rust,
                 ));
                 (
                     0,
-                    vec![tcx.mk_fn_ptr(try_fn_ty), mut_u8, tcx.mk_fn_ptr(catch_fn_ty)],
+                    vec![Ty::new_fn_ptr(tcx, try_fn_ty), mut_u8, Ty::new_fn_ptr(tcx, catch_fn_ty)],
                     tcx.types.i32,
                 )
             }
 
             sym::va_start | sym::va_end => match mk_va_list_ty(hir::Mutability::Mut) {
-                Some((va_list_ref_ty, _)) => (0, vec![va_list_ref_ty], tcx.mk_unit()),
+                Some((va_list_ref_ty, _)) => (0, vec![va_list_ref_ty], Ty::new_unit(tcx)),
                 None => bug!("`va_list` language item needed for C-variadic intrinsics"),
             },
 
             sym::va_copy => match mk_va_list_ty(hir::Mutability::Not) {
                 Some((va_list_ref_ty, va_list_ty)) => {
-                    let va_list_ptr_ty = tcx.mk_mut_ptr(va_list_ty);
-                    (0, vec![va_list_ptr_ty, va_list_ref_ty], tcx.mk_unit())
+                    let va_list_ptr_ty = Ty::new_mut_ptr(tcx, va_list_ty);
+                    (0, vec![va_list_ptr_ty, va_list_ref_ty], Ty::new_unit(tcx))
                 }
                 None => bug!("`va_list` language item needed for C-variadic intrinsics"),
             },
@@ -446,12 +457,17 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
                 None => bug!("`va_list` language item needed for C-variadic intrinsics"),
             },
 
-            sym::nontemporal_store => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit()),
+            sym::nontemporal_store => {
+                (1, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], Ty::new_unit(tcx))
+            }
 
             sym::raw_eq => {
                 let br = ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon(None) };
-                let param_ty =
-                    tcx.mk_imm_ref(ty::Region::new_late_bound(tcx, ty::INNERMOST, br), param(0));
+                let param_ty = Ty::new_imm_ref(
+                    tcx,
+                    ty::Region::new_late_bound(tcx, ty::INNERMOST, br),
+                    param(0),
+                );
                 (1, vec![param_ty; 2], tcx.types.bool)
             }
 
@@ -460,7 +476,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
             sym::const_eval_select => (4, vec![param(0), param(1), param(2)], param(3)),
 
             sym::vtable_size | sym::vtable_align => {
-                (0, vec![tcx.mk_imm_ptr(tcx.mk_unit())], tcx.types.usize)
+                (0, vec![Ty::new_imm_ptr(tcx, Ty::new_unit(tcx))], tcx.types.usize)
             }
 
             other => {
@@ -479,7 +495,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
 pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
     let param = |n| {
         let name = Symbol::intern(&format!("P{}", n));
-        tcx.mk_ty_param(n, name)
+        Ty::new_param(tcx, n, name)
     };
 
     let name = it.ident.name;
@@ -521,7 +537,7 @@ pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>)
         sym::simd_fpowi => (1, vec![param(0), tcx.types.i32], param(0)),
         sym::simd_fma => (1, vec![param(0), param(0), param(0)], param(0)),
         sym::simd_gather => (3, vec![param(0), param(1), param(2)], param(0)),
-        sym::simd_scatter => (3, vec![param(0), param(1), param(2)], tcx.mk_unit()),
+        sym::simd_scatter => (3, vec![param(0), param(1), param(2)], Ty::new_unit(tcx)),
         sym::simd_insert => (2, vec![param(0), tcx.types.u32, param(1)], param(0)),
         sym::simd_extract => (2, vec![param(0), tcx.types.u32], param(1)),
         sym::simd_cast
@@ -550,7 +566,7 @@ pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>)
         name if name.as_str().starts_with("simd_shuffle") => {
             match name.as_str()["simd_shuffle".len()..].parse() {
                 Ok(n) => {
-                    let params = vec![param(0), param(0), tcx.mk_array(tcx.types.u32, n)];
+                    let params = vec![param(0), param(0), Ty::new_array(tcx, tcx.types.u32, n)];
                     (2, params, param(1))
                 }
                 Err(_) => {
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index d34d6f644a7..7a625dd5932 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -105,7 +105,12 @@ pub(super) fn enter_wf_checking_ctxt<'tcx, F>(
     }
     f(&mut wfcx);
 
-    let assumed_wf_types = wfcx.ocx.assumed_wf_types(param_env, span, body_def_id);
+    let assumed_wf_types = match wfcx.ocx.assumed_wf_types_and_report_errors(param_env, body_def_id)
+    {
+        Ok(wf_types) => wf_types,
+        Err(_guar) => return,
+    };
+
     let implied_bounds = infcx.implied_bounds_tys(param_env, body_def_id, assumed_wf_types);
 
     let errors = wfcx.select_all_or_error();
@@ -556,7 +561,7 @@ fn gather_gat_bounds<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
                 // our example, the type was `Self`, which will also be
                 // `Self` in the GAT.
                 let ty_param = gat_generics.param_at(*ty_idx, tcx);
-                let ty_param = tcx.mk_ty_param(ty_param.index, ty_param.name);
+                let ty_param = Ty::new_param(tcx, ty_param.index, ty_param.name);
                 // Same for the region. In our example, 'a corresponds
                 // to the 'me parameter.
                 let region_param = gat_generics.param_at(*region_a_idx, tcx);
diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
index 2441c8667d4..79cc43edff1 100644
--- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
@@ -336,15 +336,17 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) -> Coe
             infcx.sub_regions(infer::RelateObjectBound(span), r_b, r_a);
             let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a };
             let mt_b = ty::TypeAndMut { ty: ty_b, mutbl: mutbl_b };
-            check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ref(r_b, ty))
+            check_mutbl(mt_a, mt_b, &|ty| Ty::new_imm_ref(tcx, r_b, ty))
         }
 
         (&ty::Ref(_, ty_a, mutbl_a), &ty::RawPtr(mt_b)) => {
             let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a };
-            check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty))
+            check_mutbl(mt_a, mt_b, &|ty| Ty::new_imm_ptr(tcx, ty))
         }
 
-        (&ty::RawPtr(mt_a), &ty::RawPtr(mt_b)) => check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty)),
+        (&ty::RawPtr(mt_a), &ty::RawPtr(mt_b)) => {
+            check_mutbl(mt_a, mt_b, &|ty| Ty::new_imm_ptr(tcx, ty))
+        }
 
         (&ty::Adt(def_a, substs_a), &ty::Adt(def_b, substs_b))
             if def_a.is_struct() && def_b.is_struct() =>
diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs
index eb299a1ea79..025bab14021 100644
--- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs
@@ -343,7 +343,7 @@ fn emit_orphan_check_error<'tcx>(
                     // That way if we had `Vec<MyType>`, we will properly attribute the
                     // problem to `Vec<T>` and avoid confusing the user if they were to see
                     // `MyType` in the error.
-                    ty::Adt(def, _) => tcx.mk_adt(*def, ty::List::empty()),
+                    ty::Adt(def, _) => Ty::new_adt(tcx, *def, ty::List::empty()),
                     _ => ty,
                 };
                 let msg = |ty: &str, postfix: &str| {
@@ -605,7 +605,9 @@ fn fast_reject_auto_impl<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId, self_ty:
     }
 
     let self_ty_root = match self_ty.kind() {
-        ty::Adt(def, _) => tcx.mk_adt(*def, InternalSubsts::identity_for_item(tcx, def.did())),
+        ty::Adt(def, _) => {
+            Ty::new_adt(tcx, *def, InternalSubsts::identity_for_item(tcx, def.did()))
+        }
         _ => unimplemented!("unexpected self ty {:?}", self_ty),
     };
 
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index d7ac9e7ce73..f47df4f215b 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -380,7 +380,7 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> {
     }
 
     fn ty_infer(&self, _: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> {
-        self.tcx().ty_error_with_message(span, "bad placeholder type")
+        Ty::new_error_with_message(self.tcx(), span, "bad placeholder type")
     }
 
     fn ct_infer(&self, ty: Ty<'tcx>, _: Option<&ty::GenericParamDef>, span: Span) -> Const<'tcx> {
@@ -390,7 +390,7 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> {
             // left alone.
             r => bug!("unexpected region: {r:?}"),
         });
-        self.tcx().const_error_with_message(ty, span, "bad placeholder constant")
+        ty::Const::new_error_with_message(self.tcx(), ty, span, "bad placeholder constant")
     }
 
     fn projected_ty_from_poly_trait_ref(
@@ -407,7 +407,7 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> {
                 item_segment,
                 trait_ref.substs,
             );
-            self.tcx().mk_projection(item_def_id, item_substs)
+            Ty::new_projection(self.tcx(), item_def_id, item_substs)
         } else {
             // There are no late-bound regions; we can just ignore the binder.
             let (mut mpart_sugg, mut inferred_sugg) = (None, None);
@@ -471,14 +471,15 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> {
                 }
                 _ => {}
             }
-            self.tcx().ty_error(self.tcx().sess.emit_err(
-                errors::AssociatedTypeTraitUninferredGenericParams {
+            Ty::new_error(
+                self.tcx(),
+                self.tcx().sess.emit_err(errors::AssociatedTypeTraitUninferredGenericParams {
                     span,
                     inferred_sugg,
                     bound,
                     mpart_sugg,
-                },
-            ))
+                }),
+            )
         }
     }
 
@@ -1239,7 +1240,7 @@ fn infer_return_ty_for_fn_sig<'tcx>(
             } else {
                 ty::Binder::dummy(tcx.mk_fn_sig(
                     fn_sig.inputs().iter().copied(),
-                    tcx.ty_error(guar),
+                    Ty::new_error(tcx, guar),
                     fn_sig.c_variadic,
                     fn_sig.unsafety,
                     fn_sig.abi,
@@ -1332,7 +1333,7 @@ fn suggest_impl_trait<'tcx>(
         let item_ty = ocx.normalize(
             &ObligationCause::misc(span, def_id),
             param_env,
-            tcx.mk_projection(assoc_item_def_id, substs),
+            Ty::new_projection(tcx, assoc_item_def_id, substs),
         );
         // FIXME(compiler-errors): We may benefit from resolving regions here.
         if ocx.select_where_possible().is_empty()
diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
index 2206f640529..ccc9f808411 100644
--- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
@@ -9,7 +9,7 @@ use rustc_hir::def_id::LocalDefId;
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_session::lint;
 use rustc_span::symbol::{kw, Symbol};
-use rustc_span::Span;
+use rustc_span::{sym, Span};
 
 pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
     use rustc_hir::*;
@@ -101,6 +101,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
                         param_def_id_to_index,
                         has_self: generics.has_self,
                         has_late_bound_regions: generics.has_late_bound_regions,
+                        host_effect_index: None,
                     };
                 } else {
                     // HACK(eddyb) this provides the correct generics when
@@ -226,10 +227,12 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
     let has_self = opt_self.is_some();
     let mut parent_has_self = false;
     let mut own_start = has_self as u32;
+    let mut host_effect_index = None;
     let parent_count = parent_def_id.map_or(0, |def_id| {
         let generics = tcx.generics_of(def_id);
         assert!(!has_self);
         parent_has_self = generics.has_self;
+        host_effect_index = generics.host_effect_index;
         own_start = generics.count() as u32;
         generics.parent_count + generics.params.len()
     });
@@ -251,11 +254,11 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
 
     // Now create the real type and const parameters.
     let type_start = own_start - has_self as u32 + params.len() as u32;
-    let mut i = 0;
+    let mut i: u32 = 0;
     let mut next_index = || {
         let prev = i;
         i += 1;
-        prev as u32 + type_start
+        prev + type_start
     };
 
     const TYPE_DEFAULT_NOT_ALLOWED: &'static str = "defaults for type parameters are only allowed in \
@@ -295,7 +298,13 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
             })
         }
         GenericParamKind::Const { default, .. } => {
-            if !matches!(allow_defaults, Defaults::Allowed) && default.is_some() {
+            let is_host_param = tcx.has_attr(param.def_id, sym::rustc_host);
+
+            if !matches!(allow_defaults, Defaults::Allowed)
+                && default.is_some()
+                // `rustc_host` effect params are allowed to have defaults.
+                && !is_host_param
+            {
                 tcx.sess.span_err(
                     param.span,
                     "defaults for const parameters are only allowed in \
@@ -303,8 +312,18 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
                 );
             }
 
+            let index = next_index();
+
+            if is_host_param {
+                if let Some(idx) = host_effect_index {
+                    bug!("parent also has host effect param? index: {idx}, def: {def_id:?}");
+                }
+
+                host_effect_index = Some(parent_count + index as usize);
+            }
+
             Some(ty::GenericParamDef {
-                index: next_index(),
+                index,
                 name: param.name.ident().name,
                 def_id: param.def_id.to_def_id(),
                 pure_wrt_drop: param.pure_wrt_drop,
@@ -356,6 +375,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
         param_def_id_to_index,
         has_self: has_self || parent_has_self,
         has_late_bound_regions: has_late_bound_regions(tcx, node),
+        host_effect_index,
     }
 }
 
diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
index 8b3f98493c1..57f74172ed0 100644
--- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
+++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
@@ -20,7 +20,8 @@ fn associated_type_bounds<'tcx>(
     ast_bounds: &'tcx [hir::GenericBound<'tcx>],
     span: Span,
 ) -> &'tcx [(ty::Clause<'tcx>, Span)] {
-    let item_ty = tcx.mk_projection(
+    let item_ty = Ty::new_projection(
+        tcx,
         assoc_item_def_id.to_def_id(),
         InternalSubsts::identity_for_item(tcx, assoc_item_def_id),
     );
@@ -91,7 +92,8 @@ pub(super) fn explicit_item_bounds(
                 tcx,
                 opaque_def_id.expect_local(),
                 opaque_ty.bounds,
-                tcx.mk_projection(
+                Ty::new_projection(
+                    tcx,
                     def_id.to_def_id(),
                     ty::InternalSubsts::identity_for_item(tcx, def_id),
                 ),
@@ -117,9 +119,9 @@ pub(super) fn explicit_item_bounds(
         }) => {
             let substs = InternalSubsts::identity_for_item(tcx, def_id);
             let item_ty = if *in_trait && !tcx.lower_impl_trait_in_trait_to_assoc_ty() {
-                tcx.mk_projection(def_id.to_def_id(), substs)
+                Ty::new_projection(tcx, def_id.to_def_id(), substs)
             } else {
-                tcx.mk_opaque(def_id.to_def_id(), substs)
+                Ty::new_opaque(tcx, def_id.to_def_id(), substs)
             };
             opaque_type_bounds(tcx, def_id, bounds, item_ty, *span)
         }
diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
index b9e71aaa004..12936664130 100644
--- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
@@ -63,7 +63,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
     use rustc_hir::*;
 
     match tcx.opt_rpitit_info(def_id.to_def_id()) {
-        Some(ImplTraitInTraitData::Trait { opaque_def_id, .. }) => {
+        Some(ImplTraitInTraitData::Trait { opaque_def_id, fn_def_id }) => {
             let opaque_ty_id = tcx.hir().local_def_id_to_hir_id(opaque_def_id.expect_local());
             let opaque_ty_node = tcx.hir().get(opaque_ty_id);
             let Node::Item(&Item { kind: ItemKind::OpaqueTy(OpaqueTy { lifetime_mapping, .. }), .. }) = opaque_ty_node else {
@@ -71,6 +71,18 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
             };
 
             let mut predicates = Vec::new();
+
+            // RPITITs should inherit the predicates of their parent. This is
+            // both to ensure that the RPITITs are only instantiated when the
+            // parent predicates would hold, and also so that the param-env
+            // inherits these predicates as assumptions.
+            let identity_substs = InternalSubsts::identity_for_item(tcx, def_id);
+            predicates.extend(
+                tcx.explicit_predicates_of(fn_def_id).instantiate_own(tcx, identity_substs),
+            );
+
+            // We also install bidirectional outlives predicates for the RPITIT
+            // to keep the duplicates lifetimes from opaque lowering in sync.
             compute_bidirectional_outlives_predicates(
                 tcx,
                 def_id,
@@ -89,12 +101,13 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
 
         Some(ImplTraitInTraitData::Impl { fn_def_id }) => {
             let assoc_item = tcx.associated_item(def_id);
-            let trait_assoc_predicates = tcx.predicates_of(assoc_item.trait_item_def_id.unwrap());
+            let trait_assoc_predicates =
+                tcx.explicit_predicates_of(assoc_item.trait_item_def_id.unwrap());
 
             let impl_assoc_identity_substs = InternalSubsts::identity_for_item(tcx, def_id);
             let impl_def_id = tcx.parent(fn_def_id);
             let impl_trait_ref_substs =
-                tcx.impl_trait_ref(impl_def_id).unwrap().skip_binder().substs;
+                tcx.impl_trait_ref(impl_def_id).unwrap().subst_identity().substs;
 
             let impl_assoc_substs =
                 impl_assoc_identity_substs.rebase_onto(tcx, impl_def_id, impl_trait_ref_substs);
@@ -230,9 +243,12 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
                 let name = param.name.ident().name;
                 let param_const = ty::ParamConst::new(index, name);
 
-                let ct_ty = tcx.type_of(param.def_id.to_def_id()).subst_identity();
+                let ct_ty = tcx
+                    .type_of(param.def_id.to_def_id())
+                    .no_bound_vars()
+                    .expect("const parameters cannot be generic");
 
-                let ct = tcx.mk_const(param_const, ct_ty);
+                let ct = ty::Const::new_param(tcx, param_const, ct_ty);
 
                 predicates.insert((
                     ty::ClauseKind::ConstArgHasType(ct, ct_ty).to_predicate(tcx),
@@ -703,6 +719,7 @@ pub(super) fn type_param_predicates(
     (item_def_id, def_id, assoc_name): (LocalDefId, LocalDefId, Ident),
 ) -> ty::GenericPredicates<'_> {
     use rustc_hir::*;
+    use rustc_middle::ty::Ty;
 
     // In the AST, bounds can derive from two places. Either
     // written inline like `<T: Foo>` or in a where-clause like
@@ -712,7 +729,7 @@ pub(super) fn type_param_predicates(
     let param_owner = tcx.hir().ty_param_owner(def_id);
     let generics = tcx.generics_of(param_owner);
     let index = generics.param_def_id_to_index[&def_id.to_def_id()];
-    let ty = tcx.mk_ty_param(index, tcx.hir().ty_param_name(def_id));
+    let ty = Ty::new_param(tcx, index, tcx.hir().ty_param_name(def_id));
 
     // Don't look for bounds where the type parameter isn't in scope.
     let parent = if item_def_id == param_owner {
diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs
index bd92ee4b550..3755342aef5 100644
--- a/compiler/rustc_hir_analysis/src/collect/type_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs
@@ -16,6 +16,7 @@ mod opaque;
 
 fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
     use hir::*;
+    use rustc_middle::ty::Ty;
     let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
 
     let Node::AnonConst(_) = tcx.hir().get(hir_id) else { panic!() };
@@ -25,13 +26,13 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
 
     let (generics, arg_idx) = match parent_node {
         // Easy case: arrays repeat expressions.
-        Node::Ty(&Ty { kind: TyKind::Array(_, ref constant), .. })
+        Node::Ty(&hir::Ty { kind: TyKind::Array(_, ref constant), .. })
         | Node::Expr(&Expr { kind: ExprKind::Repeat(_, ref constant), .. })
             if constant.hir_id() == hir_id =>
         {
             return tcx.types.usize
         }
-        Node::Ty(&Ty { kind: TyKind::Typeof(ref e), .. }) if e.hir_id == hir_id => {
+        Node::Ty(&hir::Ty { kind: TyKind::Typeof(ref e), .. }) if e.hir_id == hir_id => {
             return tcx.typeck(def_id).node_type(e.hir_id)
         }
         Node::Expr(&Expr { kind: ExprKind::InlineAsm(asm), .. })
@@ -67,7 +68,7 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
             ) =>
         {
             let Some(trait_def_id) = trait_ref.trait_def_id() else {
-                return tcx.ty_error_with_message(tcx.def_span(def_id), "Could not find trait");
+                return Ty::new_error_with_message(tcx,tcx.def_span(def_id), "Could not find trait");
             };
             let assoc_items = tcx.associated_items(trait_def_id);
             let assoc_item = assoc_items.find_by_name_and_kind(
@@ -79,7 +80,7 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
                     .expect("const parameter types cannot be generic")
             } else {
                 // FIXME(associated_const_equality): add a useful error message here.
-                tcx.ty_error_with_message(tcx.def_span(def_id), "Could not find associated const on trait")
+                Ty::new_error_with_message(tcx,tcx.def_span(def_id), "Could not find associated const on trait")
             }
         }
 
@@ -99,7 +100,7 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
         // arm would handle this.
         //
         // I believe this match arm is only needed for GAT but I am not 100% sure - BoxyUwU
-        Node::Ty(hir_ty @ Ty { kind: TyKind::Path(QPath::TypeRelative(_, segment)), .. }) => {
+        Node::Ty(hir_ty @ hir::Ty { kind: TyKind::Path(QPath::TypeRelative(_, segment)), .. }) => {
             // Find the Item containing the associated type so we can create an ItemCtxt.
             // Using the ItemCtxt convert the HIR for the unresolved assoc type into a
             // ty which is a fully resolved projection.
@@ -137,7 +138,7 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
                 (generics, arg_index)
             } else {
                 // I dont think it's possible to reach this but I'm not 100% sure - BoxyUwU
-                return tcx.ty_error_with_message(
+                return Ty::new_error_with_message(tcx,
                     tcx.def_span(def_id),
                     "unexpected non-GAT usage of an anon const",
                 );
@@ -154,7 +155,7 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
             // As there is no relevant param for `def_id`, we simply return
             // `None` here.
             let Some(type_dependent_def) = tables.type_dependent_def_id(parent_node_id) else {
-                return tcx.ty_error_with_message(
+                return Ty::new_error_with_message(tcx,
                     tcx.def_span(def_id),
                     format!("unable to find type-dependent def for {:?}", parent_node_id),
                 );
@@ -174,12 +175,12 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
             (tcx.generics_of(type_dependent_def), idx)
         }
 
-        Node::Ty(&Ty { kind: TyKind::Path(_), .. })
+        Node::Ty(&hir::Ty { kind: TyKind::Path(_), .. })
         | Node::Expr(&Expr { kind: ExprKind::Path(_) | ExprKind::Struct(..), .. })
         | Node::TraitRef(..)
         | Node::Pat(_) => {
             let path = match parent_node {
-                Node::Ty(&Ty { kind: TyKind::Path(QPath::Resolved(_, path)), .. })
+                Node::Ty(&hir::Ty { kind: TyKind::Path(QPath::Resolved(_, path)), .. })
                 | Node::TraitRef(&TraitRef { path, .. }) => &*path,
                 Node::Expr(&Expr {
                     kind:
@@ -195,14 +196,14 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
                     if let Some(path) = get_path_containing_arg_in_pat(pat, hir_id) {
                         path
                     } else {
-                        return tcx.ty_error_with_message(
+                        return Ty::new_error_with_message(tcx,
                             tcx.def_span(def_id),
                             format!("unable to find const parent for {} in pat {:?}", hir_id, pat),
                         );
                     }
                 }
                 _ => {
-                    return tcx.ty_error_with_message(
+                    return Ty::new_error_with_message(tcx,
                         tcx.def_span(def_id),
                         format!("unexpected const parent path {:?}", parent_node),
                     );
@@ -224,7 +225,7 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
                     .position(|ct| ct.hir_id == hir_id)
                     .map(|idx| (idx, seg)))
             }) else {
-                return tcx.ty_error_with_message(
+                return Ty::new_error_with_message(tcx,
                     tcx.def_span(def_id),
                     "no arg matching AnonConst in path",
                 );
@@ -233,7 +234,7 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
             let generics = match tcx.res_generics_def_id(segment.res) {
                 Some(def_id) => tcx.generics_of(def_id),
                 None => {
-                    return tcx.ty_error_with_message(
+                    return Ty::new_error_with_message(tcx,
                         tcx.def_span(def_id),
                         format!("unexpected anon const res {:?} in path: {:?}", segment.res, path),
                     );
@@ -243,7 +244,7 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
             (generics, arg_index)
         }
 
-        _ => return tcx.ty_error_with_message(
+        _ => return Ty::new_error_with_message(tcx,
             tcx.def_span(def_id),
             format!("unexpected const parent in type_of(): {parent_node:?}"),
         ),
@@ -269,7 +270,8 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
     {
         tcx.type_of(param_def_id).no_bound_vars().expect("const parameter types cannot be generic")
     } else {
-        return tcx.ty_error_with_message(
+        return Ty::new_error_with_message(
+            tcx,
             tcx.def_span(def_id),
             format!("const generic parameter not found in {generics:?} at position {arg_idx:?}"),
         );
@@ -305,6 +307,9 @@ fn get_path_containing_arg_in_pat<'hir>(
 }
 
 pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty<'_>> {
+    use rustc_hir::*;
+    use rustc_middle::ty::Ty;
+
     // If we are computing `type_of` the synthesized associated type for an RPITIT in the impl
     // side, use `collect_return_position_impl_trait_in_trait_tys` to infer the value of the
     // associated type in the impl.
@@ -317,7 +322,8 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty
                 return map[&assoc_item.trait_item_def_id.unwrap()];
             }
             Err(_) => {
-                return ty::EarlyBinder::bind(tcx.ty_error_with_message(
+                return ty::EarlyBinder::bind(Ty::new_error_with_message(
+                    tcx,
                     DUMMY_SP,
                     "Could not collect return position impl trait in trait tys",
                 ));
@@ -325,8 +331,6 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty
         }
     }
 
-    use rustc_hir::*;
-
     let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
 
     let icx = ItemCtxt::new(tcx, def_id);
@@ -335,7 +339,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty
         Node::TraitItem(item) => match item.kind {
             TraitItemKind::Fn(..) => {
                 let substs = InternalSubsts::identity_for_item(tcx, def_id);
-                tcx.mk_fn_def(def_id.to_def_id(), substs)
+                Ty::new_fn_def(tcx, def_id.to_def_id(), substs)
             }
             TraitItemKind::Const(ty, body_id) => body_id
                 .and_then(|body_id| {
@@ -360,7 +364,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty
         Node::ImplItem(item) => match item.kind {
             ImplItemKind::Fn(..) => {
                 let substs = InternalSubsts::identity_for_item(tcx, def_id);
-                tcx.mk_fn_def(def_id.to_def_id(), substs)
+                Ty::new_fn_def(tcx, def_id.to_def_id(), substs)
             }
             ImplItemKind::Const(ty, body_id) => {
                 if is_suggestable_infer_ty(ty) {
@@ -417,18 +421,18 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty
                             span: spans.into(),
                             note: (),
                         });
-                        tcx.ty_error(guar)
+                        Ty::new_error(tcx, guar)
                     }
                     _ => icx.to_ty(*self_ty),
                 },
                 ItemKind::Fn(..) => {
                     let substs = InternalSubsts::identity_for_item(tcx, def_id);
-                    tcx.mk_fn_def(def_id.to_def_id(), substs)
+                    Ty::new_fn_def(tcx, def_id.to_def_id(), substs)
                 }
                 ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Union(..) => {
                     let def = tcx.adt_def(def_id);
                     let substs = InternalSubsts::identity_for_item(tcx, def_id);
-                    tcx.mk_adt(def, substs)
+                    Ty::new_adt(tcx, def, substs)
                 }
                 ItemKind::OpaqueTy(OpaqueTy {
                     origin: hir::OpaqueTyOrigin::TyAlias { .. },
@@ -469,10 +473,10 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty
         Node::ForeignItem(foreign_item) => match foreign_item.kind {
             ForeignItemKind::Fn(..) => {
                 let substs = InternalSubsts::identity_for_item(tcx, def_id);
-                tcx.mk_fn_def(def_id.to_def_id(), substs)
+                Ty::new_fn_def(tcx, def_id.to_def_id(), substs)
             }
             ForeignItemKind::Static(t, _) => icx.to_ty(t),
-            ForeignItemKind::Type => tcx.mk_foreign(def_id.to_def_id()),
+            ForeignItemKind::Type => Ty::new_foreign(tcx, def_id.to_def_id()),
         },
 
         Node::Ctor(def) | Node::Variant(Variant { data: def, .. }) => match def {
@@ -481,7 +485,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty
             }
             VariantData::Tuple(..) => {
                 let substs = InternalSubsts::identity_for_item(tcx, def_id);
-                tcx.mk_fn_def(def_id.to_def_id(), substs)
+                Ty::new_fn_def(tcx, def_id.to_def_id(), substs)
             }
         },
 
diff --git a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs
index 4d96a7ff4c3..f4779a3f265 100644
--- a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs
+++ b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs
@@ -84,7 +84,7 @@ pub(super) fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: Local
                 _ => "item",
             },
         });
-        tcx.ty_error(reported)
+        Ty::new_error(tcx, reported)
     }
 }
 
@@ -128,7 +128,8 @@ impl TaitConstraintLocator<'_> {
         // ```
         let tables = self.tcx.typeck(item_def_id);
         if let Some(guar) = tables.tainted_by_errors {
-            self.found = Some(ty::OpaqueHiddenType { span: DUMMY_SP, ty: self.tcx.ty_error(guar) });
+            self.found =
+                Some(ty::OpaqueHiddenType { span: DUMMY_SP, ty: Ty::new_error(self.tcx, guar) });
             return;
         }
 
@@ -162,7 +163,7 @@ impl TaitConstraintLocator<'_> {
             if let Some(prev) = &mut self.found {
                 if concrete_type.ty != prev.ty && !(concrete_type, prev.ty).references_error() {
                     let guar = prev.report_mismatch(&concrete_type, self.def_id, self.tcx).emit();
-                    prev.ty = self.tcx.ty_error(guar);
+                    prev.ty = Ty::new_error(self.tcx, guar);
                 }
             } else {
                 self.found = Some(concrete_type);
@@ -258,7 +259,7 @@ pub(super) fn find_opaque_ty_constraints_for_rpit<'tcx>(
         if let Some(guar) = tables.tainted_by_errors {
             // Some error in the owner fn prevented us from populating
             // the `concrete_opaque_types` table.
-            tcx.ty_error(guar)
+            Ty::new_error(tcx, guar)
         } else {
             // Fall back to the RPIT we inferred during HIR typeck
             if let Some(hir_opaque_ty) = hir_opaque_ty {
@@ -270,7 +271,7 @@ pub(super) fn find_opaque_ty_constraints_for_rpit<'tcx>(
                 // so we can just make the hidden type be `!`.
                 // For backwards compatibility reasons, we fall back to
                 // `()` until we the diverging default is changed.
-                tcx.mk_diverging_default()
+                Ty::new_diverging_default(tcx)
             }
         }
     }
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index cb840592edd..205e26d0eda 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -5,7 +5,7 @@ use rustc_errors::{
     error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed, Handler, IntoDiagnostic,
     MultiSpan,
 };
-use rustc_macros::{Diagnostic, Subdiagnostic};
+use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
 use rustc_middle::ty::{self, print::TraitRefPrintOnlyTraitPath, Ty};
 use rustc_span::{symbol::Ident, Span, Symbol};
 
@@ -900,3 +900,11 @@ pub(crate) enum LateBoundInApit {
         param_span: Span,
     },
 }
+
+#[derive(LintDiagnostic)]
+#[diag(hir_analysis_unused_associated_type_bounds)]
+#[note]
+pub struct UnusedAssociatedTypeBounds {
+    #[suggestion(code = "")]
+    pub span: Span,
+}
diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
index 383144ce139..c64fb469bb2 100644
--- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
+++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
@@ -77,7 +77,7 @@ use rustc_infer::traits::specialization_graph::Node;
 use rustc_middle::ty::subst::{GenericArg, InternalSubsts, SubstsRef};
 use rustc_middle::ty::trait_def::TraitSpecializationKind;
 use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
-use rustc_span::Span;
+use rustc_span::{ErrorGuaranteed, Span};
 use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
 use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
 use rustc_trait_selection::traits::{self, translate_substs_with_cause, wf, ObligationCtxt};
@@ -113,7 +113,7 @@ fn check_always_applicable(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node
     let span = tcx.def_span(impl1_def_id);
     check_has_items(tcx, impl1_def_id, impl2_node, span);
 
-    if let Some((impl1_substs, impl2_substs)) = get_impl_substs(tcx, impl1_def_id, impl2_node) {
+    if let Ok((impl1_substs, impl2_substs)) = get_impl_substs(tcx, impl1_def_id, impl2_node) {
         let impl2_def_id = impl2_node.def_id();
         debug!(?impl2_def_id, ?impl2_substs);
 
@@ -171,16 +171,14 @@ fn get_impl_substs(
     tcx: TyCtxt<'_>,
     impl1_def_id: LocalDefId,
     impl2_node: Node,
-) -> Option<(SubstsRef<'_>, SubstsRef<'_>)> {
+) -> Result<(SubstsRef<'_>, SubstsRef<'_>), ErrorGuaranteed> {
     let infcx = &tcx.infer_ctxt().build();
     let ocx = ObligationCtxt::new(infcx);
     let param_env = tcx.param_env(impl1_def_id);
-
-    let assumed_wf_types =
-        ocx.assumed_wf_types(param_env, tcx.def_span(impl1_def_id), impl1_def_id);
+    let impl1_span = tcx.def_span(impl1_def_id);
+    let assumed_wf_types = ocx.assumed_wf_types_and_report_errors(param_env, impl1_def_id)?;
 
     let impl1_substs = InternalSubsts::identity_for_item(tcx, impl1_def_id);
-    let impl1_span = tcx.def_span(impl1_def_id);
     let impl2_substs = translate_substs_with_cause(
         infcx,
         param_env,
@@ -198,8 +196,8 @@ fn get_impl_substs(
 
     let errors = ocx.select_all_or_error();
     if !errors.is_empty() {
-        ocx.infcx.err_ctxt().report_fulfillment_errors(&errors);
-        return None;
+        let guar = ocx.infcx.err_ctxt().report_fulfillment_errors(&errors);
+        return Err(guar);
     }
 
     let implied_bounds = infcx.implied_bounds_tys(param_env, impl1_def_id, assumed_wf_types);
@@ -207,10 +205,10 @@ fn get_impl_substs(
     let _ = ocx.resolve_regions_and_report_errors(impl1_def_id, &outlives_env);
     let Ok(impl2_substs) = infcx.fully_resolve(impl2_substs) else {
         let span = tcx.def_span(impl1_def_id);
-        tcx.sess.emit_err(SubstsOnOverriddenImpl { span });
-        return None;
+        let guar = tcx.sess.emit_err(SubstsOnOverriddenImpl { span });
+        return Err(guar);
     };
-    Some((impl1_substs, impl2_substs))
+    Ok((impl1_substs, impl2_substs))
 }
 
 /// Returns a list of all of the unconstrained subst of the given impl.
@@ -553,7 +551,6 @@ fn trait_predicate_kind<'tcx>(
         | ty::PredicateKind::ClosureKind(..)
         | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
         | ty::PredicateKind::ConstEquate(..)
-        | ty::PredicateKind::Ambiguous
-        | ty::PredicateKind::Clause(ty::ClauseKind::TypeWellFormedFromEnv(..)) => None,
+        | ty::PredicateKind::Ambiguous => None,
     }
 }
diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs
index d2a1b1c1a42..a68832d96a6 100644
--- a/compiler/rustc_hir_analysis/src/lib.rs
+++ b/compiler/rustc_hir_analysis/src/lib.rs
@@ -319,16 +319,19 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
         expected_return_type = main_fnsig.output();
     } else {
         // standard () main return type
-        expected_return_type = ty::Binder::dummy(tcx.mk_unit());
+        expected_return_type = ty::Binder::dummy(Ty::new_unit(tcx));
     }
 
     if error {
         return;
     }
 
-    let se_ty = tcx.mk_fn_ptr(expected_return_type.map_bound(|expected_return_type| {
-        tcx.mk_fn_sig([], expected_return_type, false, hir::Unsafety::Normal, Abi::Rust)
-    }));
+    let se_ty = Ty::new_fn_ptr(
+        tcx,
+        expected_return_type.map_bound(|expected_return_type| {
+            tcx.mk_fn_sig([], expected_return_type, false, hir::Unsafety::Normal, Abi::Rust)
+        }),
+    );
 
     require_same_types(
         tcx,
@@ -339,7 +342,7 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
         ),
         param_env,
         se_ty,
-        tcx.mk_fn_ptr(main_fnsig),
+        Ty::new_fn_ptr(tcx, main_fnsig),
     );
 }
 fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: DefId) {
@@ -397,13 +400,16 @@ fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: DefId) {
                 }
             }
 
-            let se_ty = tcx.mk_fn_ptr(ty::Binder::dummy(tcx.mk_fn_sig(
-                [tcx.types.isize, tcx.mk_imm_ptr(tcx.mk_imm_ptr(tcx.types.u8))],
-                tcx.types.isize,
-                false,
-                hir::Unsafety::Normal,
-                Abi::Rust,
-            )));
+            let se_ty = Ty::new_fn_ptr(
+                tcx,
+                ty::Binder::dummy(tcx.mk_fn_sig(
+                    [tcx.types.isize, Ty::new_imm_ptr(tcx, Ty::new_imm_ptr(tcx, tcx.types.u8))],
+                    tcx.types.isize,
+                    false,
+                    hir::Unsafety::Normal,
+                    Abi::Rust,
+                )),
+            );
 
             require_same_types(
                 tcx,
@@ -414,7 +420,7 @@ fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: DefId) {
                 ),
                 ty::ParamEnv::empty(), // start should not have any where bounds.
                 se_ty,
-                tcx.mk_fn_ptr(tcx.fn_sig(start_def_id).subst_identity()),
+                Ty::new_fn_ptr(tcx, tcx.fn_sig(start_def_id).subst_identity()),
             );
         }
         _ => {
diff --git a/compiler/rustc_hir_analysis/src/outlives/explicit.rs b/compiler/rustc_hir_analysis/src/outlives/explicit.rs
index 3a04c3336ca..a7fca41f86a 100644
--- a/compiler/rustc_hir_analysis/src/outlives/explicit.rs
+++ b/compiler/rustc_hir_analysis/src/outlives/explicit.rs
@@ -53,8 +53,7 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> {
                     | ty::ClauseKind::Projection(_)
                     | ty::ClauseKind::ConstArgHasType(_, _)
                     | ty::ClauseKind::WellFormed(_)
-                    | ty::ClauseKind::ConstEvaluatable(_)
-                    | ty::ClauseKind::TypeWellFormedFromEnv(_) => {}
+                    | ty::ClauseKind::ConstEvaluatable(_) => {}
                 }
             }
 
diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs
index c7fa27da1ac..e8720a5da02 100644
--- a/compiler/rustc_hir_typeck/src/_match.rs
+++ b/compiler/rustc_hir_typeck/src/_match.rs
@@ -65,7 +65,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // us to give better error messages (pointing to a usually better
                 // arm for inconsistent arms or to the whole match when a `()` type
                 // is required).
-                Expectation::ExpectHasType(ety) if ety != self.tcx.mk_unit() => ety,
+                Expectation::ExpectHasType(ety) if ety != Ty::new_unit(self.tcx) => ety,
                 _ => self.next_ty_var(TypeVariableOrigin {
                     kind: TypeVariableOriginKind::MiscVariable,
                     span: expr.span,
diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs
index 9da72aae776..f306653c1ab 100644
--- a/compiler/rustc_hir_typeck/src/callee.rs
+++ b/compiler/rustc_hir_typeck/src/callee.rs
@@ -6,8 +6,9 @@ use crate::type_error_struct;
 use rustc_ast::util::parser::PREC_POSTFIX;
 use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed, StashKey};
 use rustc_hir as hir;
-use rustc_hir::def::{self, CtorKind, Namespace, Res};
+use rustc_hir::def::{self, CtorKind, DefKind, Namespace, Res};
 use rustc_hir::def_id::DefId;
+use rustc_hir::HirId;
 use rustc_hir_analysis::autoderef::Autoderef;
 use rustc_infer::{
     infer,
@@ -89,7 +90,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             _ => self.check_expr(callee_expr),
         };
 
-        let expr_ty = self.structurally_resolved_type(call_expr.span, original_callee_ty);
+        let expr_ty = self.structurally_resolve_type(call_expr.span, original_callee_ty);
 
         let mut autoderef = self.autoderef(callee_expr.span, expr_ty);
         let mut result = None;
@@ -138,7 +139,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         autoderef: &Autoderef<'a, 'tcx>,
     ) -> Option<CallStep<'tcx>> {
         let adjusted_ty =
-            self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false));
+            self.structurally_resolve_type(autoderef.span(), autoderef.final_ty(false));
 
         // If the callee is a bare function or a closure, then we're all set.
         match *adjusted_ty.kind() {
@@ -232,12 +233,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             let Some(trait_def_id) = opt_trait_def_id else { continue };
 
             let opt_input_type = opt_arg_exprs.map(|arg_exprs| {
-                self.tcx.mk_tup_from_iter(arg_exprs.iter().map(|e| {
-                    self.next_ty_var(TypeVariableOrigin {
-                        kind: TypeVariableOriginKind::TypeInference,
-                        span: e.span,
-                    })
-                }))
+                Ty::new_tup_from_iter(
+                    self.tcx,
+                    arg_exprs.iter().map(|e| {
+                        self.next_ty_var(TypeVariableOrigin {
+                            kind: TypeVariableOriginKind::TypeInference,
+                            span: e.span,
+                        })
+                    }),
+                )
             });
 
             if let Some(ok) = self.lookup_method_in_trait(
@@ -376,15 +380,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expected: Expectation<'tcx>,
     ) -> Ty<'tcx> {
         let (fn_sig, def_id) = match *callee_ty.kind() {
-            ty::FnDef(def_id, subst) => {
-                let fn_sig = self.tcx.fn_sig(def_id).subst(self.tcx, subst);
+            ty::FnDef(def_id, substs) => {
+                self.enforce_context_effects(call_expr.hir_id, call_expr.span, def_id, substs);
+                let fn_sig = self.tcx.fn_sig(def_id).subst(self.tcx, substs);
 
                 // Unit testing: function items annotated with
                 // `#[rustc_evaluate_where_clauses]` trigger special output
                 // to let us test the trait evaluation system.
                 if self.tcx.has_attr(def_id, sym::rustc_evaluate_where_clauses) {
                     let predicates = self.tcx.predicates_of(def_id);
-                    let predicates = predicates.instantiate(self.tcx, subst);
+                    let predicates = predicates.instantiate(self.tcx, substs);
                     for (predicate, predicate_span) in predicates {
                         let obligation = Obligation::new(
                             self.tcx,
@@ -405,6 +410,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 }
                 (fn_sig, Some(def_id))
             }
+            // FIXME(effects): these arms should error because we can't enforce them
             ty::FnPtr(sig) => (sig, None),
             _ => {
                 for arg in arg_exprs {
@@ -432,7 +438,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
                 let err = self.report_invalid_callee(call_expr, callee_expr, callee_ty, arg_exprs);
 
-                return self.tcx.ty_error(err);
+                return Ty::new_error(self.tcx, err);
             }
         };
 
@@ -739,6 +745,60 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         fn_sig.output()
     }
 
+    #[tracing::instrument(level = "debug", skip(self, span))]
+    pub(super) fn enforce_context_effects(
+        &self,
+        call_expr_hir: HirId,
+        span: Span,
+        callee_did: DefId,
+        callee_substs: SubstsRef<'tcx>,
+    ) {
+        let tcx = self.tcx;
+
+        if !tcx.features().effects || tcx.sess.opts.unstable_opts.unleash_the_miri_inside_of_you {
+            return;
+        }
+
+        // Compute the constness required by the context.
+        let context = tcx.hir().enclosing_body_owner(call_expr_hir);
+        let const_context = tcx.hir().body_const_context(context);
+
+        let kind = tcx.def_kind(context.to_def_id());
+        debug_assert_ne!(kind, DefKind::ConstParam);
+
+        if tcx.has_attr(context.to_def_id(), sym::rustc_do_not_const_check) {
+            trace!("do not const check this context");
+            return;
+        }
+
+        let effect = match const_context {
+            Some(hir::ConstContext::Static(_) | hir::ConstContext::Const) => tcx.consts.false_,
+            Some(hir::ConstContext::ConstFn) => {
+                let substs = ty::InternalSubsts::identity_for_item(tcx, context);
+                substs.host_effect_param().expect("ConstContext::Maybe must have host effect param")
+            }
+            None => tcx.consts.true_,
+        };
+
+        let generics = tcx.generics_of(callee_did);
+
+        trace!(?effect, ?generics, ?callee_substs);
+
+        if let Some(idx) = generics.host_effect_index {
+            let param = callee_substs.const_at(idx);
+            let cause = self.misc(span);
+            match self.at(&cause, self.param_env).eq(infer::DefineOpaqueTypes::No, effect, param) {
+                Ok(infer::InferOk { obligations, value: () }) => {
+                    self.register_predicates(obligations);
+                }
+                Err(e) => {
+                    // FIXME(effects): better diagnostic
+                    self.err_ctxt().report_mismatched_consts(&cause, effect, param, e).emit();
+                }
+            }
+        }
+    }
+
     fn confirm_overloaded_call(
         &self,
         call_expr: &'tcx hir::Expr<'tcx>,
diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs
index 7a1e830073a..71799387458 100644
--- a/compiler/rustc_hir_typeck/src/cast.rs
+++ b/compiler/rustc_hir_typeck/src/cast.rs
@@ -393,7 +393,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
                         && fcx
                             .try_coerce(
                                 self.expr,
-                                fcx.tcx.mk_ref(
+                                Ty::new_ref(fcx.tcx,
                                     fcx.tcx.lifetimes.re_erased,
                                     TypeAndMut { ty: expr_ty, mutbl },
                                 ),
@@ -410,7 +410,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
                         && fcx
                             .try_coerce(
                                 self.expr,
-                                fcx.tcx.mk_ref(
+                                Ty::new_ref(fcx.tcx,
                                     expr_reg,
                                     TypeAndMut { ty: expr_ty, mutbl: Mutability::Mut },
                                 ),
@@ -428,7 +428,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
                         && fcx
                             .try_coerce(
                                 self.expr,
-                                fcx.tcx.mk_ref(reg, TypeAndMut { ty: self.expr_ty, mutbl }),
+                                Ty::new_ref(fcx.tcx,reg, TypeAndMut { ty: self.expr_ty, mutbl }),
                                 self.cast_ty,
                                 AllowTwoPhase::No,
                                 None,
@@ -441,7 +441,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
                     && fcx
                         .try_coerce(
                             self.expr,
-                            fcx.tcx.mk_ref(
+                            Ty::new_ref(fcx.tcx,
                                 fcx.tcx.lifetimes.re_erased,
                                 TypeAndMut { ty: self.expr_ty, mutbl },
                             ),
@@ -717,8 +717,8 @@ impl<'a, 'tcx> CastCheck<'tcx> {
 
     #[instrument(skip(fcx), level = "debug")]
     pub fn check(mut self, fcx: &FnCtxt<'a, 'tcx>) {
-        self.expr_ty = fcx.structurally_resolved_type(self.expr_span, self.expr_ty);
-        self.cast_ty = fcx.structurally_resolved_type(self.cast_span, self.cast_ty);
+        self.expr_ty = fcx.structurally_resolve_type(self.expr_span, self.expr_ty);
+        self.cast_ty = fcx.structurally_resolve_type(self.cast_span, self.cast_ty);
 
         debug!("check_cast({}, {:?} as {:?})", self.expr.hir_id, self.expr_ty, self.cast_ty);
 
@@ -765,7 +765,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
                         let res = fcx.try_coerce(
                             self.expr,
                             self.expr_ty,
-                            fcx.tcx.mk_fn_ptr(f),
+                            Ty::new_fn_ptr(fcx.tcx, f),
                             AllowTwoPhase::No,
                             None,
                         );
@@ -957,7 +957,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
                 // from a region pointer to a vector.
 
                 // Coerce to a raw pointer so that we generate AddressOf in MIR.
-                let array_ptr_type = fcx.tcx.mk_ptr(m_expr);
+                let array_ptr_type = Ty::new_ptr(fcx.tcx, m_expr);
                 fcx.try_coerce(self.expr, self.expr_ty, array_ptr_type, AllowTwoPhase::No, None)
                     .unwrap_or_else(|_| {
                         bug!(
diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs
index 0a1b639af51..8b57e311fc0 100644
--- a/compiler/rustc_hir_typeck/src/check.rs
+++ b/compiler/rustc_hir_typeck/src/check.rs
@@ -62,11 +62,11 @@ pub(super) fn check_fn<'a, 'tcx>(
             fcx.require_type_is_sized(yield_ty, span, traits::SizedYieldType);
             yield_ty
         } else {
-            tcx.mk_unit()
+            Ty::new_unit(tcx,)
         };
 
         // Resume type defaults to `()` if the generator has no argument.
-        let resume_ty = fn_sig.inputs().get(0).copied().unwrap_or_else(|| tcx.mk_unit());
+        let resume_ty = fn_sig.inputs().get(0).copied().unwrap_or_else(|| Ty::new_unit(tcx,));
 
         fcx.resume_yield_tys = Some((resume_ty, yield_ty));
     }
@@ -256,10 +256,10 @@ fn check_lang_start_fn<'tcx>(
         // for example `start`'s generic should be a type parameter
         let generics = tcx.generics_of(def_id);
         let fn_generic = generics.param_at(0, tcx);
-        let generic_ty = tcx.mk_ty_param(fn_generic.index, fn_generic.name);
+        let generic_ty = Ty::new_param(tcx, fn_generic.index, fn_generic.name);
         let expected_fn_sig =
             tcx.mk_fn_sig([], generic_ty, false, hir::Unsafety::Normal, Abi::Rust);
-        let expected_ty = tcx.mk_fn_ptr(Binder::dummy(expected_fn_sig));
+        let expected_ty = Ty::new_fn_ptr(tcx, Binder::dummy(expected_fn_sig));
 
         // we emit the same error to suggest changing the arg no matter what's wrong with the arg
         let emit_main_fn_arg_err = || {
@@ -316,9 +316,9 @@ fn check_lang_start_fn<'tcx>(
 
         if !argv_is_okay {
             let inner_ptr_ty =
-                tcx.mk_ptr(ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: tcx.types.u8 });
+                Ty::new_ptr(tcx, ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: tcx.types.u8 });
             let expected_ty =
-                tcx.mk_ptr(ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: inner_ptr_ty });
+                Ty::new_ptr(tcx, ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: inner_ptr_ty });
             tcx.sess.emit_err(LangStartIncorrectParam {
                 param_span: decl.inputs[2].span,
                 param_num: 3,
diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs
index c64b64e925a..78a9ac49de2 100644
--- a/compiler/rustc_hir_typeck/src/closure.rs
+++ b/compiler/rustc_hir_typeck/src/closure.rs
@@ -117,7 +117,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 },
             );
 
-            return self.tcx.mk_generator(
+            return Ty::new_generator(
+                self.tcx,
                 expr_def_id.to_def_id(),
                 generator_substs.substs,
                 movability,
@@ -128,7 +129,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // the `closures` table.
         let sig = bound_sig.map_bound(|sig| {
             self.tcx.mk_fn_sig(
-                [self.tcx.mk_tup(sig.inputs())],
+                [Ty::new_tup(self.tcx, sig.inputs())],
                 sig.output(),
                 sig.c_variadic,
                 sig.unsafety,
@@ -155,12 +156,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             ty::ClosureSubstsParts {
                 parent_substs,
                 closure_kind_ty,
-                closure_sig_as_fn_ptr_ty: self.tcx.mk_fn_ptr(sig),
+                closure_sig_as_fn_ptr_ty: Ty::new_fn_ptr(self.tcx, sig),
                 tupled_upvars_ty,
             },
         );
 
-        self.tcx.mk_closure(expr_def_id.to_def_id(), closure_substs.substs)
+        Ty::new_closure(self.tcx, expr_def_id.to_def_id(), closure_substs.substs)
     }
 
     /// Given the expected type, figures out what it can about this closure we
@@ -190,7 +191,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 (sig, kind)
             }
             ty::Infer(ty::TyVar(vid)) => self.deduce_closure_signature_from_predicates(
-                self.tcx.mk_ty_var(self.root_var(vid)),
+                Ty::new_var(self.tcx, self.root_var(vid)),
                 self.obligations_for_self_ty(vid).map(|obl| (obl.predicate, obl.cause.span)),
             ),
             ty::FnPtr(sig) => {
@@ -806,7 +807,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         guar: ErrorGuaranteed,
     ) -> ty::PolyFnSig<'tcx> {
         let astconv: &dyn AstConv<'_> = self;
-        let err_ty = self.tcx.ty_error(guar);
+        let err_ty = Ty::new_error(self.tcx, guar);
 
         let supplied_arguments = decl.inputs.iter().map(|a| {
             // Convert the types that the user supplied (if any), but ignore them.
diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index 5f98bacaf2a..ea6ae094f09 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -192,7 +192,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
             let _ = self.commit_if_ok(|_| {
                 self.at(&self.cause, self.param_env).eq(DefineOpaqueTypes::Yes, a, b)
             });
-            return success(vec![], self.fcx.tcx.ty_error(guar), vec![]);
+            return success(vec![], Ty::new_error(self.fcx.tcx, guar), vec![]);
         }
 
         // Coercing from `!` to any type is allowed:
@@ -440,7 +440,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                 }
                 r_borrow_var.unwrap()
             };
-            let derefd_ty_a = self.tcx.mk_ref(
+            let derefd_ty_a = Ty::new_ref(
+                self.tcx,
                 r,
                 TypeAndMut {
                     ty: referent_ty,
@@ -558,9 +559,11 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                     Adjustment { kind: Adjust::Deref(None), target: ty_a },
                     Adjustment {
                         kind: Adjust::Borrow(AutoBorrow::Ref(r_borrow, mutbl)),
-                        target: self
-                            .tcx
-                            .mk_ref(r_borrow, ty::TypeAndMut { mutbl: mutbl_b, ty: ty_a }),
+                        target: Ty::new_ref(
+                            self.tcx,
+                            r_borrow,
+                            ty::TypeAndMut { mutbl: mutbl_b, ty: ty_a },
+                        ),
                     },
                 ))
             }
@@ -571,7 +574,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                     Adjustment { kind: Adjust::Deref(None), target: ty_a },
                     Adjustment {
                         kind: Adjust::Borrow(AutoBorrow::RawPtr(mt_b)),
-                        target: self.tcx.mk_ptr(ty::TypeAndMut { mutbl: mt_b, ty: ty_a }),
+                        target: Ty::new_ptr(self.tcx, ty::TypeAndMut { mutbl: mt_b, ty: ty_a }),
                     },
                 ))
             }
@@ -883,7 +886,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                     self.at(&self.cause, self.param_env).normalize(a_sig);
                 obligations.extend(o1);
 
-                let a_fn_pointer = self.tcx.mk_fn_ptr(a_sig);
+                let a_fn_pointer = Ty::new_fn_ptr(self.tcx, a_sig);
                 let InferOk { value, obligations: o2 } = self.coerce_from_safe_fn(
                     a_fn_pointer,
                     a_sig,
@@ -945,7 +948,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                 let closure_sig = substs_a.as_closure().sig();
                 let unsafety = fn_ty.unsafety();
                 let pointer_ty =
-                    self.tcx.mk_fn_ptr(self.tcx.signature_unclosure(closure_sig, unsafety));
+                    Ty::new_fn_ptr(self.tcx, self.tcx.signature_unclosure(closure_sig, unsafety));
                 debug!("coerce_closure_to_fn(a={:?}, b={:?}, pty={:?})", a, b, pointer_ty);
                 self.unify_and(
                     pointer_ty,
@@ -973,7 +976,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
         coerce_mutbls(mt_a.mutbl, mutbl_b)?;
 
         // Check that the types which they point at are compatible.
-        let a_unsafe = self.tcx.mk_ptr(ty::TypeAndMut { mutbl: mutbl_b, ty: mt_a.ty });
+        let a_unsafe = Ty::new_ptr(self.tcx, ty::TypeAndMut { mutbl: mutbl_b, ty: mt_a.ty });
         // Although references and unsafe ptrs have the same
         // representation, we still register an Adjust::DerefRef so that
         // regionck knows that the region for `a` must be valid here.
@@ -1005,7 +1008,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         allow_two_phase: AllowTwoPhase,
         cause: Option<ObligationCause<'tcx>>,
     ) -> RelateResult<'tcx, Ty<'tcx>> {
-        let source = self.resolve_vars_with_obligations(expr_ty);
+        let source = self.try_structurally_resolve_type(expr.span, expr_ty);
         debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target);
 
         let cause =
@@ -1015,7 +1018,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         let (adjustments, _) = self.register_infer_ok_obligations(ok);
         self.apply_adjustments(expr, adjustments);
-        Ok(if let Err(guar) = expr_ty.error_reported() { self.tcx.ty_error(guar) } else { target })
+        Ok(if let Err(guar) = expr_ty.error_reported() {
+            Ty::new_error(self.tcx, guar)
+        } else {
+            target
+        })
     }
 
     /// Same as `try_coerce()`, but without side-effects.
@@ -1179,7 +1186,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 .map(|ok| self.register_infer_ok_obligations(ok))?;
 
             // Reify both sides and return the reified fn pointer type.
-            let fn_ptr = self.tcx.mk_fn_ptr(sig);
+            let fn_ptr = Ty::new_fn_ptr(self.tcx, sig);
             let prev_adjustment = match prev_ty.kind() {
                 ty::Closure(..) => Adjust::Pointer(PointerCast::ClosureFnPointer(a_sig.unsafety())),
                 ty::FnDef(..) => Adjust::Pointer(PointerCast::ReifyFnPointer),
@@ -1430,7 +1437,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
             fcx,
             cause,
             None,
-            fcx.tcx.mk_unit(),
+            Ty::new_unit(fcx.tcx),
             Some(augment_error),
             label_unit_as_expected,
         )
@@ -1461,7 +1468,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
         // If we see any error types, just propagate that error
         // upwards.
         if let Err(guar) = (expression_ty, self.merged_ty()).error_reported() {
-            self.final_ty = Some(fcx.tcx.ty_error(guar));
+            self.final_ty = Some(Ty::new_error(fcx.tcx, guar));
             return;
         }
 
@@ -1649,7 +1656,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
 
                 let reported = err.emit_unless(unsized_return);
 
-                self.final_ty = Some(fcx.tcx.ty_error(reported));
+                self.final_ty = Some(Ty::new_error(fcx.tcx, reported));
             }
         }
     }
diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs
index 47f3c6b8407..cc8198aab25 100644
--- a/compiler/rustc_hir_typeck/src/demand.rs
+++ b/compiler/rustc_hir_typeck/src/demand.rs
@@ -1366,10 +1366,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // ```
                 let ref_ty = match mutability {
                     hir::Mutability::Mut => {
-                        self.tcx.mk_mut_ref(self.tcx.lifetimes.re_static, checked_ty)
+                        Ty::new_mut_ref(self.tcx,self.tcx.lifetimes.re_static, checked_ty)
                     }
                     hir::Mutability::Not => {
-                        self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, checked_ty)
+                        Ty::new_imm_ref(self.tcx,self.tcx.lifetimes.re_static, checked_ty)
                     }
                 };
                 if self.can_coerce(ref_ty, expected) {
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index f5be030c1a5..72b29f7b6e9 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -94,7 +94,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 return if let [Adjustment { kind: Adjust::NeverToAny, target }] = &adjustments[..] {
                     target.to_owned()
                 } else {
-                    self.tcx().ty_error(reported)
+                    Ty::new_error(self.tcx(), reported)
                 };
             }
 
@@ -321,7 +321,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     tcx.types.never
                 } else {
                     // There was an error; make type-check fail.
-                    tcx.ty_error_misc()
+                    Ty::new_misc_error(tcx)
                 }
             }
             ExprKind::Ret(ref expr_opt) => self.check_expr_return(expr_opt.as_deref(), expr),
@@ -361,7 +361,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             ExprKind::Field(base, field) => self.check_field(expr, &base, field, expected),
             ExprKind::Index(base, idx) => self.check_expr_index(base, idx, expr),
             ExprKind::Yield(value, ref src) => self.check_expr_yield(value, expr, src),
-            hir::ExprKind::Err(guar) => tcx.ty_error(guar),
+            hir::ExprKind::Err(guar) => Ty::new_error(tcx, guar),
         }
     }
 
@@ -380,7 +380,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let mut oprnd_t = self.check_expr_with_expectation(&oprnd, expected_inner);
 
         if !oprnd_t.references_error() {
-            oprnd_t = self.structurally_resolved_type(expr.span, oprnd_t);
+            oprnd_t = self.structurally_resolve_type(expr.span, oprnd_t);
             match unop {
                 hir::UnOp::Deref => {
                     if let Some(ty) = self.lookup_derefing(expr, oprnd, oprnd_t) {
@@ -399,7 +399,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         {
                             err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
                         }
-                        oprnd_t = tcx.ty_error(err.emit());
+                        oprnd_t = Ty::new_error(tcx, err.emit());
                     }
                 }
                 hir::UnOp::Not => {
@@ -449,10 +449,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         let tm = ty::TypeAndMut { ty, mutbl };
         match kind {
-            _ if tm.ty.references_error() => self.tcx.ty_error_misc(),
+            _ if tm.ty.references_error() => Ty::new_misc_error(self.tcx),
             hir::BorrowKind::Raw => {
                 self.check_named_place_expr(oprnd);
-                self.tcx.mk_ptr(tm)
+                Ty::new_ptr(self.tcx, tm)
             }
             hir::BorrowKind::Ref => {
                 // Note: at this point, we cannot say what the best lifetime
@@ -470,7 +470,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // whose address was taken can actually be made to live as long
                 // as it needs to live.
                 let region = self.next_region_var(infer::AddrOfRegion(expr.span));
-                self.tcx.mk_ref(region, tm)
+                Ty::new_ref(self.tcx, region, tm)
             }
         }
     }
@@ -528,11 +528,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let e =
                     self.tcx.sess.delay_span_bug(qpath.span(), "`Res::Err` but no error emitted");
                 self.set_tainted_by_errors(e);
-                tcx.ty_error(e)
+                Ty::new_error(tcx, e)
             }
             Res::Def(DefKind::Variant, _) => {
                 let e = report_unexpected_variant_res(tcx, res, qpath, expr.span, "E0533", "value");
-                tcx.ty_error(e)
+                Ty::new_error(tcx, e)
             }
             _ => self.instantiate_value_path(segs, opt_ty, res, expr.span, expr.hir_id).0,
         };
@@ -620,7 +620,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         Some(ctxt) => ctxt.coerce.as_ref().map(|coerce| coerce.expected_ty()),
                         None => {
                             // Avoid ICE when `break` is inside a closure (#65383).
-                            return tcx.ty_error_with_message(
+                            return Ty::new_error_with_message(
+                                tcx,
                                 expr.span,
                                 "break was outside loop, but no error was emitted",
                             );
@@ -631,7 +632,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // If the loop context is not a `loop { }`, then break with
                 // a value is illegal, and `opt_coerce_to` will be `None`.
                 // Just set expectation to error in that case.
-                let coerce_to = opt_coerce_to.unwrap_or_else(|| tcx.ty_error_misc());
+                let coerce_to = opt_coerce_to.unwrap_or_else(|| Ty::new_misc_error(tcx));
 
                 // Recurse without `enclosing_breakables` borrowed.
                 e_ty = self.check_expr_with_hint(e, coerce_to);
@@ -639,7 +640,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             } else {
                 // Otherwise, this is a break *without* a value. That's
                 // always legal, and is equivalent to `break ()`.
-                e_ty = tcx.mk_unit();
+                e_ty = Ty::new_unit(tcx);
                 cause = self.misc(expr.span);
             }
 
@@ -649,7 +650,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
             let Some(ctxt) = enclosing_breakables.opt_find_breakable(target_id) else {
                 // Avoid ICE when `break` is inside a closure (#65383).
-                return tcx.ty_error_with_message(
+                return Ty::new_error_with_message(tcx,
                     expr.span,
                     "break was outside loop, but no error was emitted",
                 );
@@ -707,7 +708,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // this can only happen if the `break` was not
             // inside a loop at all, which is caught by the
             // loop-checking pass.
-            let err = self.tcx.ty_error_with_message(
+            let err = Ty::new_error_with_message(
+                self.tcx,
                 expr.span,
                 "break was outside loop, but no error was emitted",
             );
@@ -1072,7 +1074,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
 
         let result_ty = coerce.complete(self);
-        if let Err(guar) = cond_ty.error_reported() { self.tcx.ty_error(guar) } else { result_ty }
+        if let Err(guar) = cond_ty.error_reported() {
+            Ty::new_error(self.tcx, guar)
+        } else {
+            result_ty
+        }
     }
 
     /// Type check assignment expression `expr` of form `lhs = rhs`.
@@ -1090,7 +1096,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // The expected type is `bool` but this will result in `()` so we can reasonably
             // say that the user intended to write `lhs == rhs` instead of `lhs = rhs`.
             // The likely cause of this is `if foo = bar { .. }`.
-            let actual_ty = self.tcx.mk_unit();
+            let actual_ty = Ty::new_unit(self.tcx);
             let mut err = self.demand_suptype_diag(expr.span, expected_ty, actual_ty).unwrap();
             let lhs_ty = self.check_expr(&lhs);
             let rhs_ty = self.check_expr(&rhs);
@@ -1148,7 +1154,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // If the assignment expression itself is ill-formed, don't
             // bother emitting another error
             let reported = err.emit_unless(lhs_ty.references_error() || rhs_ty.references_error());
-            return self.tcx.ty_error(reported);
+            return Ty::new_error(self.tcx, reported);
         }
 
         let lhs_ty = self.check_expr_with_needs(&lhs, Needs::MutPlace);
@@ -1195,9 +1201,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         self.require_type_is_sized(lhs_ty, lhs.span, traits::AssignmentLhsSized);
 
         if let Err(guar) = (lhs_ty, rhs_ty).error_reported() {
-            self.tcx.ty_error(guar)
+            Ty::new_error(self.tcx, guar)
         } else {
-            self.tcx.mk_unit()
+            Ty::new_unit(self.tcx)
         }
     }
 
@@ -1252,7 +1258,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // [1]
             self.tcx.sess.delay_span_bug(body.span, "no coercion, but loop may not break");
         }
-        ctxt.coerce.map(|c| c.complete(self)).unwrap_or_else(|| self.tcx.mk_unit())
+        ctxt.coerce.map(|c| c.complete(self)).unwrap_or_else(|| Ty::new_unit(self.tcx))
     }
 
     /// Checks a method call.
@@ -1266,14 +1272,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ) -> Ty<'tcx> {
         let rcvr_t = self.check_expr(&rcvr);
         // no need to check for bot/err -- callee does that
-        let rcvr_t = self.structurally_resolved_type(rcvr.span, rcvr_t);
+        let rcvr_t = self.structurally_resolve_type(rcvr.span, rcvr_t);
         let span = segment.ident.span;
 
         let method = match self.lookup_method(rcvr_t, segment, span, expr, rcvr, args) {
             Ok(method) => {
                 // We could add a "consider `foo::<params>`" suggestion here, but I wasn't able to
-                // trigger this codepath causing `structurally_resolved_type` to emit an error.
+                // trigger this codepath causing `structurally_resolve_type` to emit an error.
 
+                self.enforce_context_effects(expr.hir_id, expr.span, method.def_id, method.substs);
                 self.write_method_call(expr.hir_id, method);
                 Ok(method)
             }
@@ -1315,7 +1322,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         // Eagerly check for some obvious errors.
         if let Err(guar) = (t_expr, t_cast).error_reported() {
-            self.tcx.ty_error(guar)
+            Ty::new_error(self.tcx, guar)
         } else {
             // Defer other checks until we're done type checking.
             let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut();
@@ -1336,7 +1343,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     deferred_cast_checks.push(cast_check);
                     t_cast
                 }
-                Err(guar) => self.tcx.ty_error(guar),
+                Err(guar) => Ty::new_error(self.tcx, guar),
             }
         }
     }
@@ -1376,7 +1383,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         };
         let array_len = args.len() as u64;
         self.suggest_array_len(expr, array_len);
-        self.tcx.mk_array(element_ty, array_len)
+        Ty::new_array(self.tcx, element_ty, array_len)
     }
 
     fn suggest_array_len(&self, expr: &'tcx hir::Expr<'tcx>, array_len: u64) {
@@ -1464,18 +1471,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         };
 
         if let Err(guar) = element_ty.error_reported() {
-            return tcx.ty_error(guar);
+            return Ty::new_error(tcx, guar);
         }
 
         self.check_repeat_element_needs_copy_bound(element, count, element_ty);
 
         self.register_wf_obligation(
-            tcx.mk_array_with_const_len(t, count).into(),
+            Ty::new_array_with_const_len(tcx, t, count).into(),
             expr.span,
             traits::WellFormed(None),
         );
 
-        tcx.mk_array_with_const_len(t, count)
+        Ty::new_array_with_const_len(tcx, t, count)
     }
 
     fn check_repeat_element_needs_copy_bound(
@@ -1538,9 +1545,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
             _ => self.check_expr_with_expectation(&e, NoExpectation),
         });
-        let tuple = self.tcx.mk_tup_from_iter(elt_ts_iter);
+        let tuple = Ty::new_tup_from_iter(self.tcx, elt_ts_iter);
         if let Err(guar) = tuple.error_reported() {
-            self.tcx.ty_error(guar)
+            Ty::new_error(self.tcx, guar)
         } else {
             self.require_type_is_sized(tuple, expr.span, traits::TupleInitializerSized);
             tuple
@@ -1560,7 +1567,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             Ok(data) => data,
             Err(guar) => {
                 self.check_struct_fields_on_error(fields, base_expr);
-                return self.tcx.ty_error(guar);
+                return Ty::new_error(self.tcx, guar);
             }
         };
 
@@ -1659,7 +1666,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     )
                 };
 
-                tcx.ty_error(guar)
+                Ty::new_error(tcx, guar)
             };
 
             // Make sure to give a type to the field even if there's
@@ -1771,7 +1778,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     // `MyStruct<'a, _, F2, C>`, as opposed to just `_`...
                     // This is important to allow coercions to happen in
                     // `other_struct` itself. See `coerce-in-base-expr.rs`.
-                    let fresh_base_ty = self.tcx.mk_adt(*adt, fresh_substs);
+                    let fresh_base_ty = Ty::new_adt(self.tcx, *adt, fresh_substs);
                     self.check_expr_has_type_or_error(
                         base_expr,
                         self.resolve_vars_if_possible(fresh_base_ty),
@@ -2252,7 +2259,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ) -> Ty<'tcx> {
         debug!("check_field(expr: {:?}, base: {:?}, field: {:?})", expr, base, field);
         let base_ty = self.check_expr(base);
-        let base_ty = self.structurally_resolved_type(base.span, base_ty);
+        let base_ty = self.structurally_resolve_type(base.span, base_ty);
         let mut private_candidate = None;
         let mut autoderef = self.autoderef(expr.span, base_ty);
         while let Some((deref_base_ty, _)) = autoderef.next() {
@@ -2300,7 +2307,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 _ => {}
             }
         }
-        self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false));
+        self.structurally_resolve_type(autoderef.span(), autoderef.final_ty(false));
 
         if let Some((adjustments, did)) = private_candidate {
             // (#90483) apply adjustments to avoid ExprUseVisitor from
@@ -2313,7 +2320,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 did,
                 expected.only_has_type(self),
             );
-            return self.tcx().ty_error(guar);
+            return Ty::new_error(self.tcx(), guar);
         }
 
         let guar = if field.name == kw::Empty {
@@ -2399,7 +2406,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             err.emit()
         };
 
-        self.tcx().ty_error(guar)
+        Ty::new_error(self.tcx(), guar)
     }
 
     fn suggest_await_on_field_access(
@@ -2857,7 +2864,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         } else if idx_t.references_error() {
             idx_t
         } else {
-            let base_t = self.structurally_resolved_type(base.span, base_t);
+            let base_t = self.structurally_resolve_type(base.span, base_t);
             match self.lookup_indexing(expr, base, base_t, idx, idx_t) {
                 Some((index_ty, element_ty)) => {
                     // two-phase not needed because index_ty is never mutable
@@ -2931,7 +2938,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     }
 
                     let reported = err.emit();
-                    self.tcx.ty_error(reported)
+                    Ty::new_error(self.tcx, reported)
                 }
             }
         }
@@ -3005,7 +3012,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             let element_ty = ocx.normalize(
                 &cause,
                 self.param_env,
-                self.tcx.mk_projection(index_trait_output_def_id, impl_trait_ref.substs),
+                Ty::new_projection(self.tcx, index_trait_output_def_id, impl_trait_ref.substs),
             );
 
             let errors = ocx.select_where_possible();
@@ -3053,14 +3060,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // information. Hence, we check the source of the yield expression here and check its
             // value's type against `()` (this check should always hold).
             None if src.is_await() => {
-                self.check_expr_coercible_to_type(&value, self.tcx.mk_unit(), None);
-                self.tcx.mk_unit()
+                self.check_expr_coercible_to_type(&value, Ty::new_unit(self.tcx), None);
+                Ty::new_unit(self.tcx)
             }
             _ => {
                 self.tcx.sess.emit_err(YieldExprOutsideOfGenerator { span: expr.span });
                 // Avoid expressions without types during writeback (#78653).
                 self.check_expr(value);
-                self.tcx.mk_unit()
+                Ty::new_unit(self.tcx)
             }
         }
     }
@@ -3084,14 +3091,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // allows them to be inferred based on how they are used later in the
         // function.
         if is_input {
-            let ty = self.structurally_resolved_type(expr.span, ty);
+            let ty = self.structurally_resolve_type(expr.span, ty);
             match *ty.kind() {
                 ty::FnDef(..) => {
-                    let fnptr_ty = self.tcx.mk_fn_ptr(ty.fn_sig(self.tcx));
+                    let fnptr_ty = Ty::new_fn_ptr(self.tcx, ty.fn_sig(self.tcx));
                     self.demand_coerce(expr, ty, fnptr_ty, None, AllowTwoPhase::No);
                 }
                 ty::Ref(_, base_ty, mutbl) => {
-                    let ptr_ty = self.tcx.mk_ptr(ty::TypeAndMut { ty: base_ty, mutbl });
+                    let ptr_ty = Ty::new_ptr(self.tcx, ty::TypeAndMut { ty: base_ty, mutbl });
                     self.demand_coerce(expr, ty, ptr_ty, None, AllowTwoPhase::No);
                 }
                 _ => {}
@@ -3126,7 +3133,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         if asm.options.contains(ast::InlineAsmOptions::NORETURN) {
             self.tcx.types.never
         } else {
-            self.tcx.mk_unit()
+            Ty::new_unit(self.tcx)
         }
     }
 
@@ -3142,7 +3149,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let mut current_container = container;
 
         for &field in fields {
-            let container = self.structurally_resolved_type(expr.span, current_container);
+            let container = self.structurally_resolve_type(expr.span, current_container);
 
             match container.kind() {
                 ty::Adt(container_def, substs) if !container_def.is_enum() => {
diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs
index b7ae621c685..a76db6e73a1 100644
--- a/compiler/rustc_hir_typeck/src/fallback.rs
+++ b/compiler/rustc_hir_typeck/src/fallback.rs
@@ -104,7 +104,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
         // type, `?T` is not considered unsolved, but `?I` is. The
         // same is true for float variables.)
         let fallback = match ty.kind() {
-            _ if let Some(e) = self.tainted_by_errors() => self.tcx.ty_error(e),
+            _ if let Some(e) = self.tainted_by_errors() => Ty::new_error(self.tcx,e),
             ty::Infer(ty::IntVar(_)) => self.tcx.types.i32,
             ty::Infer(ty::FloatVar(_)) => self.tcx.types.f64,
             _ => match diverging_fallback.get(&ty) {
@@ -287,7 +287,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
         let mut diverging_fallback = FxHashMap::default();
         diverging_fallback.reserve(diverging_vids.len());
         for &diverging_vid in &diverging_vids {
-            let diverging_ty = self.tcx.mk_ty_var(diverging_vid);
+            let diverging_ty = Ty::new_var(self.tcx, diverging_vid);
             let root_vid = self.root_var(diverging_vid);
             let can_reach_non_diverging = coercion_graph
                 .depth_first_search(root_vid)
@@ -334,7 +334,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
                 diverging_fallback.insert(diverging_ty, self.tcx.types.unit);
             } else {
                 debug!("fallback to ! - all diverging: {:?}", diverging_vid);
-                diverging_fallback.insert(diverging_ty, self.tcx.mk_diverging_default());
+                diverging_fallback.insert(diverging_ty, Ty::new_diverging_default(self.tcx));
             }
         }
 
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index 0ee87173a36..9a80a9c9303 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -83,6 +83,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// version (resolve_vars_if_possible), this version will
     /// also select obligations if it seems useful, in an effort
     /// to get more type information.
+    // FIXME(-Ztrait-solver=next): A lot of the calls to this method should
+    // probably be `try_structurally_resolve_type` or `structurally_resolve_type` instead.
     pub(in super::super) fn resolve_vars_with_obligations(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
         self.resolve_vars_with_obligations_and_mutate_fulfillment(ty, |_| {})
     }
@@ -451,7 +453,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     pub fn node_ty(&self, id: hir::HirId) -> Ty<'tcx> {
         match self.typeck_results.borrow().node_types().get(id) {
             Some(&t) => t,
-            None if let Some(e) = self.tainted_by_errors() => self.tcx.ty_error(e),
+            None if let Some(e) = self.tainted_by_errors() => Ty::new_error(self.tcx,e),
             None => {
                 bug!(
                     "no type for node {} in fcx {}",
@@ -465,7 +467,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     pub fn node_ty_opt(&self, id: hir::HirId) -> Option<Ty<'tcx>> {
         match self.typeck_results.borrow().node_types().get(id) {
             Some(&t) => Some(t),
-            None if let Some(e) = self.tainted_by_errors() => Some(self.tcx.ty_error(e)),
+            None if let Some(e) = self.tainted_by_errors() => Some(Ty::new_error(self.tcx,e)),
             None => None,
         }
     }
@@ -556,7 +558,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 self.tcx,
                 self.tcx.typeck_root_def_id(expr_def_id.to_def_id()),
             );
-            let witness = self.tcx.mk_generator_witness_mir(expr_def_id.to_def_id(), substs);
+            let witness = Ty::new_generator_witness_mir(self.tcx, expr_def_id.to_def_id(), substs);
 
             // Unify `interior` with `witness` and collect all the resulting obligations.
             let span = self.tcx.hir().body(body_id).value.span;
@@ -683,7 +685,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // inference variable.
                 | ty::PredicateKind::ClosureKind(..)
                 | ty::PredicateKind::Ambiguous
-                | ty::PredicateKind::Clause(ty::ClauseKind::TypeWellFormedFromEnv(..)) => None,
+                 => None,
             },
         )
     }
@@ -701,7 +703,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     }
 
     pub(in super::super) fn err_args(&self, len: usize) -> Vec<Ty<'tcx>> {
-        let ty_error = self.tcx.ty_error_misc();
+        let ty_error = Ty::new_misc_error(self.tcx);
         vec![ty_error; len]
     }
 
@@ -1240,7 +1242,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         }
                     }
                     let reported = err.emit();
-                    return (tcx.ty_error(reported), res);
+                    return (Ty::new_error(tcx, reported), res);
                 }
             }
         } else {
@@ -1465,16 +1467,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
-    /// Resolves `typ` by a single level if `typ` is a type variable.
+    /// Try to resolve `ty` to a structural type, normalizing aliases.
     ///
-    /// When the new solver is enabled, this will also attempt to normalize
-    /// the type if it's a projection (note that it will not deeply normalize
-    /// projections within the type, just the outermost layer of the type).
-    ///
-    /// If no resolution is possible, then an error is reported.
-    /// Numeric inference variables may be left unresolved.
-    pub fn structurally_resolved_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
-        let mut ty = self.resolve_vars_with_obligations(ty);
+    /// In case there is still ambiguity, the returned type may be an inference
+    /// variable. This is different from `structurally_resolve_type` which errors
+    /// in this case.
+    pub fn try_structurally_resolve_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
+        let ty = self.resolve_vars_with_obligations(ty);
 
         if self.next_trait_solver()
             && let ty::Alias(ty::Projection, _) = ty.kind()
@@ -1483,15 +1482,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 .at(&self.misc(sp), self.param_env)
                 .structurally_normalize(ty, &mut **self.fulfillment_cx.borrow_mut())
             {
-                Ok(normalized_ty) => {
-                    ty = normalized_ty;
-                },
+                Ok(normalized_ty) => normalized_ty,
                 Err(errors) => {
                     let guar = self.err_ctxt().report_fulfillment_errors(&errors);
-                    return self.tcx.ty_error(guar);
+                    return Ty::new_error(self.tcx,guar);
                 }
             }
-        }
+        } else {
+            ty
+       }
+    }
+
+    /// Resolves `ty` by a single level if `ty` is a type variable.
+    ///
+    /// When the new solver is enabled, this will also attempt to normalize
+    /// the type if it's a projection (note that it will not deeply normalize
+    /// projections within the type, just the outermost layer of the type).
+    ///
+    /// If no resolution is possible, then an error is reported.
+    /// Numeric inference variables may be left unresolved.
+    pub fn structurally_resolve_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
+        let ty = self.try_structurally_resolve_type(sp, ty);
 
         if !ty.is_ty_var() {
             ty
@@ -1501,7 +1512,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     .emit_inference_failure_err(self.body_id, sp, ty.into(), E0282, true)
                     .emit()
             });
-            let err = self.tcx.ty_error(e);
+            let err = Ty::new_error(self.tcx, e);
             self.demand_suptype(sp, err, ty);
             err
         }
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs
index 223aedefea3..ed9bb4945af 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs
@@ -254,7 +254,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             type BreakTy = ty::GenericArg<'tcx>;
             fn visit_ty(&mut self, ty: Ty<'tcx>) -> std::ops::ControlFlow<Self::BreakTy> {
                 if let Some(origin) = self.0.type_var_origin(ty)
-                    && let rustc_infer::infer::type_variable::TypeVariableOriginKind::TypeParameterDefinition(_, Some(def_id)) =
+                    && let rustc_infer::infer::type_variable::TypeVariableOriginKind::TypeParameterDefinition(_, def_id) =
                         origin.kind
                     && let generics = self.0.tcx.generics_of(self.1)
                     && let Some(index) = generics.param_def_id_to_index(self.0.tcx, def_id)
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index dacd5559c71..4bd4f2d7e9c 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -73,7 +73,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let ty = self.typeck_results.borrow().expr_ty_adjusted(expr);
                 let ty = self.resolve_vars_if_possible(ty);
                 if ty.has_non_region_infer() {
-                    self.tcx.ty_error_misc()
+                    Ty::new_misc_error(self.tcx)
                 } else {
                     self.tcx.erase_regions(ty)
                 }
@@ -101,7 +101,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
             let err_inputs = match tuple_arguments {
                 DontTupleArguments => err_inputs,
-                TupleArguments => vec![self.tcx.mk_tup(&err_inputs)],
+                TupleArguments => vec![Ty::new_tup(self.tcx, &err_inputs)],
             };
 
             self.check_argument_types(
@@ -114,7 +114,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 tuple_arguments,
                 method.ok().map(|method| method.def_id),
             );
-            return self.tcx.ty_error_misc();
+            return Ty::new_misc_error(self.tcx);
         }
 
         let method = method.unwrap();
@@ -184,7 +184,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         // If the arguments should be wrapped in a tuple (ex: closures), unwrap them here
         let (formal_input_tys, expected_input_tys) = if tuple_arguments == TupleArguments {
-            let tuple_type = self.structurally_resolved_type(call_span, formal_input_tys[0]);
+            let tuple_type = self.structurally_resolve_type(call_span, formal_input_tys[0]);
             match tuple_type.kind() {
                 // We expected a tuple and got a tuple
                 ty::Tuple(arg_types) => {
@@ -412,7 +412,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
                 // There are a few types which get autopromoted when passed via varargs
                 // in C but we just error out instead and require explicit casts.
-                let arg_ty = self.structurally_resolved_type(arg.span, arg_ty);
+                let arg_ty = self.structurally_resolve_type(arg.span, arg_ty);
                 match arg_ty.kind() {
                     ty::Float(ty::FloatTy::F32) => {
                         variadic_error(tcx.sess, arg.span, arg_ty, "c_double");
@@ -424,7 +424,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         variadic_error(tcx.sess, arg.span, arg_ty, "c_uint");
                     }
                     ty::FnDef(..) => {
-                        let ptr_ty = self.tcx.mk_fn_ptr(arg_ty.fn_sig(self.tcx));
+                        let ptr_ty = Ty::new_fn_ptr(self.tcx, arg_ty.fn_sig(self.tcx));
                         let ptr_ty = self.resolve_vars_if_possible(ptr_ty);
                         variadic_error(tcx.sess, arg.span, arg_ty, &ptr_ty.to_string());
                     }
@@ -539,7 +539,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     .typeck_results
                     .borrow()
                     .expr_ty_adjusted_opt(*expr)
-                    .unwrap_or_else(|| tcx.ty_error_misc());
+                    .unwrap_or_else(|| Ty::new_misc_error(tcx));
                 (self.resolve_vars_if_possible(ty), normalize_span(expr.span))
             })
             .collect();
@@ -648,7 +648,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 && provided_arg_tys.len() == formal_and_expected_inputs.len() - 1 + tys.len()
             {
                 // Wrap up the N provided arguments starting at this position in a tuple.
-                let provided_as_tuple = tcx.mk_tup_from_iter(
+                let provided_as_tuple = Ty::new_tup_from_iter(tcx,
                     provided_arg_tys.iter().map(|(ty, _)| *ty).skip(mismatch_idx).take(tys.len()),
                 );
 
@@ -1309,14 +1309,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let tcx = self.tcx;
 
         match lit.node {
-            ast::LitKind::Str(..) => tcx.mk_static_str(),
-            ast::LitKind::ByteStr(ref v, _) => {
-                tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_array(tcx.types.u8, v.len() as u64))
-            }
+            ast::LitKind::Str(..) => Ty::new_static_str(tcx),
+            ast::LitKind::ByteStr(ref v, _) => Ty::new_imm_ref(
+                tcx,
+                tcx.lifetimes.re_static,
+                Ty::new_array(tcx, tcx.types.u8, v.len() as u64),
+            ),
             ast::LitKind::Byte(_) => tcx.types.u8,
             ast::LitKind::Char(_) => tcx.types.char,
-            ast::LitKind::Int(_, ast::LitIntType::Signed(t)) => tcx.mk_mach_int(ty::int_ty(t)),
-            ast::LitKind::Int(_, ast::LitIntType::Unsigned(t)) => tcx.mk_mach_uint(ty::uint_ty(t)),
+            ast::LitKind::Int(_, ast::LitIntType::Signed(t)) => Ty::new_int(tcx, ty::int_ty(t)),
+            ast::LitKind::Int(_, ast::LitIntType::Unsigned(t)) => Ty::new_uint(tcx, ty::uint_ty(t)),
             ast::LitKind::Int(_, ast::LitIntType::Unsuffixed) => {
                 let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() {
                     ty::Int(_) | ty::Uint(_) => Some(ty),
@@ -1328,7 +1330,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 opt_ty.unwrap_or_else(|| self.next_int_var())
             }
             ast::LitKind::Float(_, ast::LitFloatType::Suffixed(t)) => {
-                tcx.mk_mach_float(ty::float_ty(t))
+                Ty::new_float(tcx, ty::float_ty(t))
             }
             ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) => {
                 let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() {
@@ -1338,12 +1340,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 opt_ty.unwrap_or_else(|| self.next_float_var())
             }
             ast::LitKind::Bool(_) => tcx.types.bool,
-            ast::LitKind::CStr(_, _) => tcx.mk_imm_ref(
+            ast::LitKind::CStr(_, _) => Ty::new_imm_ref(
+                tcx,
                 tcx.lifetimes.re_static,
                 tcx.type_of(tcx.require_lang_item(hir::LangItem::CStr, Some(lit.span)))
                     .skip_binder(),
             ),
-            ast::LitKind::Err => tcx.ty_error_misc(),
+            ast::LitKind::Err => Ty::new_misc_error(tcx),
         }
     }
 
@@ -1513,7 +1516,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             hir::StmtKind::Item(_) => {}
             hir::StmtKind::Expr(ref expr) => {
                 // Check with expected type of `()`.
-                self.check_expr_has_type_or_error(&expr, self.tcx.mk_unit(), |err| {
+                self.check_expr_has_type_or_error(&expr, Ty::new_unit(self.tcx), |err| {
                     if expr.can_have_side_effects() {
                         self.suggest_semicolon_at_end(expr.span, err);
                     }
@@ -1536,7 +1539,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     }
 
     pub fn check_block_no_value(&self, blk: &'tcx hir::Block<'tcx>) {
-        let unit = self.tcx.mk_unit();
+        let unit = Ty::new_unit(self.tcx);
         let ty = self.check_block_with_expected(blk, ExpectHasType(unit));
 
         // if the block produces a `!` value, that can always be
@@ -1649,7 +1652,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                         blk.span,
                                         blk.hir_id,
                                         expected_ty,
-                                        self.tcx.mk_unit(),
+                                        Ty::new_unit(self.tcx),
                                     );
                                 }
                                 if !self.consider_removing_semicolon(blk, expected_ty, err) {
@@ -1795,7 +1798,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ) {
         if let Err(guar) = ty.error_reported() {
             // Override the types everywhere with `err()` to avoid knock on errors.
-            let err = self.tcx.ty_error(guar);
+            let err = Ty::new_error(self.tcx, guar);
             self.write_ty(hir_id, err);
             self.write_ty(pat.hir_id, err);
             self.locals.borrow_mut().insert(hir_id, err);
@@ -1823,8 +1826,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let result = self
                     .astconv()
                     .associated_path_to_ty(hir_id, path_span, ty.raw, qself, segment, true);
-                let ty =
-                    result.map(|(ty, _, _)| ty).unwrap_or_else(|guar| self.tcx().ty_error(guar));
+                let ty = result
+                    .map(|(ty, _, _)| ty)
+                    .unwrap_or_else(|guar| Ty::new_error(self.tcx(), guar));
                 let ty = self.handle_raw_ty(path_span, ty);
                 let result = result.map(|(_, kind, def_id)| (kind, def_id));
 
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
index e035d233bf7..20b34df99b2 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
@@ -191,7 +191,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     }
 
     pub fn next_root_ty_var(&self, origin: TypeVariableOrigin) -> Ty<'tcx> {
-        self.tcx.mk_ty_var(self.next_ty_var_id_in_universe(origin, ty::UniverseIndex::ROOT))
+        Ty::new_var(self.tcx, self.next_ty_var_id_in_universe(origin, ty::UniverseIndex::ROOT))
     }
 }
 
@@ -295,7 +295,7 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
             trait_ref.substs,
         );
 
-        self.tcx().mk_projection(item_def_id, item_substs)
+        Ty::new_projection(self.tcx(), item_def_id, item_substs)
     }
 
     fn probe_adt(&self, span: Span, ty: Ty<'tcx>) -> Option<ty::AdtDef<'tcx>> {
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index 3a4fe334f88..79a7c016185 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -426,7 +426,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // Given `Result<_, E>`, check our expected ty is `Result<_, &E>` for
             // `as_ref` and `as_deref` compatibility.
             let error_tys_equate_as_ref = error_tys.map_or(true, |(found, expected)| {
-                self.can_eq(self.param_env, self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, found), expected)
+                self.can_eq(self.param_env, Ty::new_imm_ref(self.tcx,self.tcx.lifetimes.re_erased, found), expected)
             });
             // FIXME: This could/should be extended to suggest `as_mut` and `as_deref_mut`,
             // but those checks need to be a bit more delicate and the benefit is diminishing.
@@ -515,7 +515,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         if self.tcx.hir().is_inside_const_context(hir_id) || !expected.is_box() || found.is_box() {
             return false;
         }
-        if self.can_coerce(self.tcx.mk_box(found), expected) {
+        if self.can_coerce(Ty::new_box(self.tcx, found), expected) {
             let suggest_boxing = match found.kind() {
                 ty::Tuple(tuple) if tuple.is_empty() => {
                     SuggestBoxing::Unit { start: span.shrink_to_lo(), end: span }
@@ -595,9 +595,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         if pin_did.is_none() || self.tcx.lang_items().owned_box().is_none() {
             return false;
         }
-        let box_found = self.tcx.mk_box(found);
-        let pin_box_found = self.tcx.mk_lang_item(box_found, LangItem::Pin).unwrap();
-        let pin_found = self.tcx.mk_lang_item(found, LangItem::Pin).unwrap();
+        let box_found = Ty::new_box(self.tcx, found);
+        let pin_box_found = Ty::new_lang_item(self.tcx, box_found, LangItem::Pin).unwrap();
+        let pin_found = Ty::new_lang_item(self.tcx, found, LangItem::Pin).unwrap();
         match expected.kind() {
             ty::Adt(def, _) if Some(def.did()) == pin_did => {
                 if self.can_coerce(pin_box_found, expected) {
diff --git a/compiler/rustc_hir_typeck/src/generator_interior/mod.rs b/compiler/rustc_hir_typeck/src/generator_interior/mod.rs
index fdbb153ec7d..86ea092bc40 100644
--- a/compiler/rustc_hir_typeck/src/generator_interior/mod.rs
+++ b/compiler/rustc_hir_typeck/src/generator_interior/mod.rs
@@ -312,7 +312,8 @@ pub fn resolve_interior<'a, 'tcx>(
     // Extract type components to build the witness type.
     let type_list = fcx.tcx.mk_type_list_from_iter(type_causes.iter().map(|cause| cause.ty));
     let bound_vars = fcx.tcx.mk_bound_variable_kinds(&bound_vars);
-    let witness = fcx.tcx.mk_generator_witness(ty::Binder::bind_with_vars(type_list, bound_vars));
+    let witness =
+        Ty::new_generator_witness(fcx.tcx, ty::Binder::bind_with_vars(type_list, bound_vars));
 
     drop(typeck_results);
     // Store the generator types and spans into the typeck results for this generator.
@@ -361,7 +362,8 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
                             let ty =
                                 self.interior_visitor.fcx.typeck_results.borrow().node_type(id);
                             let tcx = self.interior_visitor.fcx.tcx;
-                            let ty = tcx.mk_ref(
+                            let ty = Ty::new_ref(
+                                tcx,
                                 // Use `ReErased` as `resolve_interior` is going to replace all the
                                 // regions anyway.
                                 tcx.lifetimes.re_erased,
diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs
index 6f7288ed7e5..6f82ffcfe4a 100644
--- a/compiler/rustc_hir_typeck/src/lib.rs
+++ b/compiler/rustc_hir_typeck/src/lib.rs
@@ -154,7 +154,7 @@ fn typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckResults<'tc
 fn diagnostic_only_typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckResults<'tcx> {
     let fallback = move || {
         let span = tcx.hir().span(tcx.hir().local_def_id_to_hir_id(def_id));
-        tcx.ty_error_with_message(span, "diagnostic only typeck table used")
+        Ty::new_error_with_message(tcx, span, "diagnostic only typeck table used")
     };
     typeck_with_fallback(tcx, def_id, fallback)
 }
diff --git a/compiler/rustc_hir_typeck/src/mem_categorization.rs b/compiler/rustc_hir_typeck/src/mem_categorization.rs
index 6c8589493cb..a1aa090841a 100644
--- a/compiler/rustc_hir_typeck/src/mem_categorization.rs
+++ b/compiler/rustc_hir_typeck/src/mem_categorization.rs
@@ -277,9 +277,11 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
             adjustment::Adjust::Deref(overloaded) => {
                 // Equivalent to *expr or something similar.
                 let base = if let Some(deref) = overloaded {
-                    let ref_ty = self
-                        .tcx()
-                        .mk_ref(deref.region, ty::TypeAndMut { ty: target, mutbl: deref.mutbl });
+                    let ref_ty = Ty::new_ref(
+                        self.tcx(),
+                        deref.region,
+                        ty::TypeAndMut { ty: target, mutbl: deref.mutbl },
+                    );
                     self.cat_rvalue(expr.hir_id, expr.span, ref_ty)
                 } else {
                     previous()?
@@ -489,7 +491,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
         let ty::Ref(region, _, mutbl) = *base_ty.kind() else {
             span_bug!(expr.span, "cat_overloaded_place: base is not a reference");
         };
-        let ref_ty = self.tcx().mk_ref(region, ty::TypeAndMut { ty: place_ty, mutbl });
+        let ref_ty = Ty::new_ref(self.tcx(), region, ty::TypeAndMut { ty: place_ty, mutbl });
 
         let base = self.cat_rvalue(expr.hir_id, expr.span, ref_ty);
         self.cat_deref(expr, base)
diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs
index 31894d25b60..55ad99e55af 100644
--- a/compiler/rustc_hir_typeck/src/method/confirm.rs
+++ b/compiler/rustc_hir_typeck/src/method/confirm.rs
@@ -143,7 +143,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
         // a custom error in that case.
         if illegal_sized_bound.is_none() {
             self.add_obligations(
-                self.tcx.mk_fn_ptr(method_sig),
+                Ty::new_fn_ptr(self.tcx, method_sig),
                 all_substs,
                 method_predicates,
                 pick.item.def_id,
@@ -171,7 +171,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
         // time writing the results into the various typeck results.
         let mut autoderef = self.autoderef(self.call_expr.span, unadjusted_self_ty);
         let Some((ty, n)) = autoderef.nth(pick.autoderefs) else {
-            return self.tcx.ty_error_with_message(
+            return Ty::new_error_with_message(self.tcx,
                 rustc_span::DUMMY_SP,
                 format!("failed autoderef {}", pick.autoderefs),
             );
@@ -179,7 +179,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
         assert_eq!(n, pick.autoderefs);
 
         let mut adjustments = self.adjust_steps(&autoderef);
-        let mut target = self.structurally_resolved_type(autoderef.span(), ty);
+        let mut target = self.structurally_resolve_type(autoderef.span(), ty);
 
         match pick.autoref_or_ptr_adjustment {
             Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, unsize }) => {
@@ -187,7 +187,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
                 // Type we're wrapping in a reference, used later for unsizing
                 let base_ty = target;
 
-                target = self.tcx.mk_ref(region, ty::TypeAndMut { mutbl, ty: target });
+                target = Ty::new_ref(self.tcx, region, ty::TypeAndMut { mutbl, ty: target });
 
                 // Method call receivers are the primary use case
                 // for two-phase borrows.
@@ -200,16 +200,18 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
 
                 if unsize {
                     let unsized_ty = if let ty::Array(elem_ty, _) = base_ty.kind() {
-                        self.tcx.mk_slice(*elem_ty)
+                        Ty::new_slice(self.tcx, *elem_ty)
                     } else {
                         bug!(
                             "AutorefOrPtrAdjustment's unsize flag should only be set for array ty, found {}",
                             base_ty
                         )
                     };
-                    target = self
-                        .tcx
-                        .mk_ref(region, ty::TypeAndMut { mutbl: mutbl.into(), ty: unsized_ty });
+                    target = Ty::new_ref(
+                        self.tcx,
+                        region,
+                        ty::TypeAndMut { mutbl: mutbl.into(), ty: unsized_ty },
+                    );
                     adjustments
                         .push(Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target });
                 }
@@ -218,7 +220,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
                 target = match target.kind() {
                     &ty::RawPtr(ty::TypeAndMut { ty, mutbl }) => {
                         assert!(mutbl.is_mut());
-                        self.tcx.mk_ptr(ty::TypeAndMut { mutbl: hir::Mutability::Not, ty })
+                        Ty::new_ptr(self.tcx, ty::TypeAndMut { mutbl: hir::Mutability::Not, ty })
                     }
                     other => panic!("Cannot adjust receiver type {:?} to const ptr", other),
                 };
diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs
index ebd7b3123eb..e52cea1889f 100644
--- a/compiler/rustc_hir_typeck/src/method/mod.rs
+++ b/compiler/rustc_hir_typeck/src/method/mod.rs
@@ -205,9 +205,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         if let Some(span) = result.illegal_sized_bound {
             let mut needs_mut = false;
             if let ty::Ref(region, t_type, mutability) = self_ty.kind() {
-                let trait_type = self
-                    .tcx
-                    .mk_ref(*region, ty::TypeAndMut { ty: *t_type, mutbl: mutability.invert() });
+                let trait_type = Ty::new_ref(
+                    self.tcx,
+                    *region,
+                    ty::TypeAndMut { ty: *t_type, mutbl: mutability.invert() },
+                );
                 // We probe again to see if there might be a borrow mutability discrepancy.
                 match self.lookup_probe(
                     segment.ident,
@@ -464,7 +466,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         ));
 
         // Also add an obligation for the method type being well-formed.
-        let method_ty = tcx.mk_fn_ptr(ty::Binder::dummy(fn_sig));
+        let method_ty = Ty::new_fn_ptr(tcx, ty::Binder::dummy(fn_sig));
         debug!(
             "lookup_in_trait_adjusted: matched method method_ty={:?} obligation={:?}",
             method_ty, obligation
diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
index 35d70968ce7..52e62837f35 100644
--- a/compiler/rustc_hir_typeck/src/method/probe.rs
+++ b/compiler/rustc_hir_typeck/src/method/probe.rs
@@ -451,7 +451,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             } else {
                 // Ended up encountering a type variable when doing autoderef,
                 // but it may not be a type variable after processing obligations
-                // in our local `FnCtxt`, so don't call `structurally_resolved_type`.
+                // in our local `FnCtxt`, so don't call `structurally_resolve_type`.
                 let ty = &bad_ty.ty;
                 let ty = self
                     .probe_instantiate_query_response(span, &orig_values, ty)
@@ -465,7 +465,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     ty::Error(guar) => guar,
                     _ => bug!("unexpected bad final type in method autoderef"),
                 };
-                self.demand_eqtype(span, ty, self.tcx.ty_error(guar));
+                self.demand_eqtype(span, ty, Ty::new_error(self.tcx, guar));
                 return Err(MethodError::NoMatch(NoMatchData {
                     static_candidates: Vec::new(),
                     unsatisfied_predicates: Vec::new(),
@@ -551,7 +551,7 @@ fn method_autoderef_steps<'tcx>(
             steps.push(CandidateStep {
                 self_ty: infcx.make_query_response_ignoring_pending_obligations(
                     inference_vars,
-                    infcx.tcx.mk_slice(*elem_ty),
+                    Ty::new_slice(infcx.tcx, *elem_ty),
                 ),
                 autoderefs: dereferences,
                 // this could be from an unsafe deref if we had
@@ -847,8 +847,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
                 | ty::ClauseKind::Projection(_)
                 | ty::ClauseKind::ConstArgHasType(_, _)
                 | ty::ClauseKind::WellFormed(_)
-                | ty::ClauseKind::ConstEvaluatable(_)
-                | ty::ClauseKind::TypeWellFormedFromEnv(_) => None,
+                | ty::ClauseKind::ConstEvaluatable(_) => None,
             }
         });
 
@@ -1216,7 +1215,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
         // In general, during probing we erase regions.
         let region = tcx.lifetimes.re_erased;
 
-        let autoref_ty = tcx.mk_ref(region, ty::TypeAndMut { ty: self_ty, mutbl });
+        let autoref_ty = Ty::new_ref(tcx, region, ty::TypeAndMut { ty: self_ty, mutbl });
         self.pick_method(autoref_ty, unstable_candidates).map(|r| {
             r.map(|mut pick| {
                 pick.autoderefs = step.autoderefs;
@@ -1246,7 +1245,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
         };
 
         let const_self_ty = ty::TypeAndMut { ty, mutbl: hir::Mutability::Not };
-        let const_ptr_ty = self.tcx.mk_ptr(const_self_ty);
+        let const_ptr_ty = Ty::new_ptr(self.tcx, const_self_ty);
         self.pick_method(const_ptr_ty, unstable_candidates).map(|r| {
             r.map(|mut pick| {
                 pick.autoderefs = step.autoderefs;
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 67fb7c1d48c..5f924f30936 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -211,7 +211,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 }
                 if let ty::Ref(region, t_type, mutability) = rcvr_ty.kind() {
                     if needs_mut {
-                        let trait_type = self.tcx.mk_ref(
+                        let trait_type = Ty::new_ref(
+                            self.tcx,
                             *region,
                             ty::TypeAndMut { ty: *t_type, mutbl: mutability.invert() },
                         );
@@ -647,7 +648,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         let projection_ty = pred.skip_binder().projection_ty;
 
                         let substs_with_infer_self = tcx.mk_substs_from_iter(
-                            iter::once(tcx.mk_ty_var(ty::TyVid::from_u32(0)).into())
+                            iter::once(Ty::new_var(tcx, ty::TyVid::from_u32(0)).into())
                                 .chain(projection_ty.substs.iter().skip(1)),
                         );
 
@@ -2408,8 +2409,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // just this list.
             for (rcvr_ty, post) in &[
                 (rcvr_ty, ""),
-                (self.tcx.mk_mut_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&mut "),
-                (self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&"),
+                (Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_erased, rcvr_ty), "&mut "),
+                (Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, rcvr_ty), "&"),
             ] {
                 match self.lookup_probe_for_diagnostic(
                     item_name,
@@ -2444,10 +2445,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 }
 
                 for (rcvr_ty, pre) in &[
-                    (self.tcx.mk_lang_item(*rcvr_ty, LangItem::OwnedBox), "Box::new"),
-                    (self.tcx.mk_lang_item(*rcvr_ty, LangItem::Pin), "Pin::new"),
-                    (self.tcx.mk_diagnostic_item(*rcvr_ty, sym::Arc), "Arc::new"),
-                    (self.tcx.mk_diagnostic_item(*rcvr_ty, sym::Rc), "Rc::new"),
+                    (Ty::new_lang_item(self.tcx, *rcvr_ty, LangItem::OwnedBox), "Box::new"),
+                    (Ty::new_lang_item(self.tcx, *rcvr_ty, LangItem::Pin), "Pin::new"),
+                    (Ty::new_diagnostic_item(self.tcx, *rcvr_ty, sym::Arc), "Arc::new"),
+                    (Ty::new_diagnostic_item(self.tcx, *rcvr_ty, sym::Rc), "Rc::new"),
                 ] {
                     if let Some(new_rcvr_t) = *rcvr_ty
                         && let Ok(pick) = self.lookup_probe_for_diagnostic(
diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs
index 4f3d1d45679..1eae258c1b2 100644
--- a/compiler/rustc_hir_typeck/src/op.rs
+++ b/compiler/rustc_hir_typeck/src/op.rs
@@ -38,7 +38,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let ty =
             if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) {
                 self.enforce_builtin_binop_types(lhs.span, lhs_ty, rhs.span, rhs_ty, op);
-                self.tcx.mk_unit()
+                Ty::new_unit(self.tcx)
             } else {
                 return_ty
             };
@@ -297,7 +297,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
             // error types are considered "builtin"
             Err(_) if lhs_ty.references_error() || rhs_ty.references_error() => {
-                self.tcx.ty_error_misc()
+                Ty::new_misc_error(self.tcx)
             }
             Err(errors) => {
                 let (_, trait_def_id) =
@@ -568,7 +568,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 }
 
                 let reported = err.emit();
-                self.tcx.ty_error(reported)
+                Ty::new_error(self.tcx, reported)
             }
         };
 
@@ -752,7 +752,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     }
                     err.emit()
                 });
-                self.tcx.ty_error(guar)
+                Ty::new_error(self.tcx, guar)
             }
         }
     }
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index 9c989d3b4b2..19f82fbd3bf 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -393,7 +393,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // They can denote both statically and dynamically-sized byte arrays.
         let mut pat_ty = ty;
         if let hir::ExprKind::Lit(Spanned { node: ast::LitKind::ByteStr(..), .. }) = lt.kind {
-            let expected = self.structurally_resolved_type(span, expected);
+            let expected = self.structurally_resolve_type(span, expected);
             if let ty::Ref(_, inner_ty, _) = expected.kind()
                 && matches!(inner_ty.kind(), ty::Slice(_))
             {
@@ -403,7 +403,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     .borrow_mut()
                     .treat_byte_string_as_slice
                     .insert(lt.hir_id.local_id);
-                pat_ty = tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_slice(tcx.types.u8));
+                pat_ty = Ty::new_imm_ref(tcx,tcx.lifetimes.re_static, Ty::new_slice(tcx,tcx.types.u8));
             }
         }
 
@@ -412,7 +412,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             let expected = self.resolve_vars_if_possible(expected);
             pat_ty = match expected.kind() {
                 ty::Adt(def, _) if Some(def.did()) == tcx.lang_items().string() => expected,
-                ty::Str => tcx.mk_static_str(),
+                ty::Str => Ty::new_static_str(tcx,),
                 _ => pat_ty,
             };
         }
@@ -474,7 +474,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // There exists a side that didn't meet our criteria that the end-point
             // be of a numeric or char type, as checked in `calc_side` above.
             let guar = self.emit_err_pat_range(span, lhs, rhs);
-            return self.tcx.ty_error(guar);
+            return Ty::new_error(self.tcx, guar);
         }
 
         // Unify each side with `expected`.
@@ -494,14 +494,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         demand_eqtype(&mut rhs, lhs);
 
         if let (Some((true, ..)), _) | (_, Some((true, ..))) = (lhs, rhs) {
-            return self.tcx.ty_error_misc();
+            return Ty::new_misc_error(self.tcx);
         }
 
         // Find the unified type and check if it's of numeric or char type again.
         // This check is needed if both sides are inference variables.
         // We require types to be resolved here so that we emit inference failure
         // rather than "_ is not a char or numeric".
-        let ty = self.structurally_resolved_type(span, expected);
+        let ty = self.structurally_resolve_type(span, expected);
         if !(ty.is_numeric() || ty.is_char() || ty.references_error()) {
             if let Some((ref mut fail, _, _)) = lhs {
                 *fail = true;
@@ -510,7 +510,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 *fail = true;
             }
             let guar = self.emit_err_pat_range(span, lhs, rhs);
-            return self.tcx.ty_error(guar);
+            return Ty::new_error(self.tcx, guar);
         }
         ty
     }
@@ -848,7 +848,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let (variant, pat_ty) = match self.check_struct_path(qpath, pat.hir_id) {
             Ok(data) => data,
             Err(guar) => {
-                let err = self.tcx.ty_error(guar);
+                let err = Ty::new_error(self.tcx, guar);
                 for field in fields {
                     let ti = ti;
                     self.check_pat(field.pat, err, def_bm, ti);
@@ -864,7 +864,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         if self.check_struct_pat_fields(pat_ty, &pat, variant, fields, has_rest_pat, def_bm, ti) {
             pat_ty
         } else {
-            self.tcx.ty_error_misc()
+            Ty::new_misc_error(self.tcx)
         }
     }
 
@@ -884,12 +884,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             Res::Err => {
                 let e = tcx.sess.delay_span_bug(qpath.span(), "`Res::Err` but no error emitted");
                 self.set_tainted_by_errors(e);
-                return tcx.ty_error(e);
+                return Ty::new_error(tcx, e);
             }
             Res::Def(DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::Variant, _) => {
                 let expected = "unit struct, unit variant or constant";
                 let e = report_unexpected_variant_res(tcx, res, qpath, pat.span, "E0533", expected);
-                return tcx.ty_error(e);
+                return Ty::new_error(tcx, e);
             }
             Res::SelfCtor(..)
             | Res::Def(
@@ -1032,7 +1032,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let tcx = self.tcx;
         let on_error = |e| {
             for pat in subpats {
-                self.check_pat(pat, tcx.ty_error(e), def_bm, ti);
+                self.check_pat(pat, Ty::new_error(tcx, e), def_bm, ti);
             }
         };
         let report_unexpected_res = |res: Res| {
@@ -1049,7 +1049,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             let e = tcx.sess.delay_span_bug(pat.span, "`Res::Err` but no error emitted");
             self.set_tainted_by_errors(e);
             on_error(e);
-            return tcx.ty_error(e);
+            return Ty::new_error(tcx, e);
         }
 
         // Type-check the path.
@@ -1057,7 +1057,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             self.instantiate_value_path(segments, opt_ty, res, pat.span, pat.hir_id);
         if !pat_ty.is_fn() {
             let e = report_unexpected_res(res);
-            return tcx.ty_error(e);
+            return Ty::new_error(tcx, e);
         }
 
         let variant = match res {
@@ -1065,11 +1065,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let e = tcx.sess.delay_span_bug(pat.span, "`Res::Err` but no error emitted");
                 self.set_tainted_by_errors(e);
                 on_error(e);
-                return tcx.ty_error(e);
+                return Ty::new_error(tcx, e);
             }
             Res::Def(DefKind::AssocConst | DefKind::AssocFn, _) => {
                 let e = report_unexpected_res(res);
-                return tcx.ty_error(e);
+                return Ty::new_error(tcx, e);
             }
             Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) => tcx.expect_variant_res(res),
             _ => bug!("unexpected pattern resolution: {:?}", res),
@@ -1112,7 +1112,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             let e =
                 self.e0023(pat.span, res, qpath, subpats, &variant.fields.raw, expected, had_err);
             on_error(e);
-            return tcx.ty_error(e);
+            return Ty::new_error(tcx, e);
         }
         pat_ty
     }
@@ -1289,7 +1289,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let mut expected_len = elements.len();
         if ddpos.as_opt_usize().is_some() {
             // Require known type only when `..` is present.
-            if let ty::Tuple(tys) = self.structurally_resolved_type(span, expected).kind() {
+            if let ty::Tuple(tys) = self.structurally_resolve_type(span, expected).kind() {
                 expected_len = tys.len();
             }
         }
@@ -1303,16 +1303,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             )
         });
         let element_tys = tcx.mk_type_list_from_iter(element_tys_iter);
-        let pat_ty = tcx.mk_tup(element_tys);
+        let pat_ty = Ty::new_tup(tcx, element_tys);
         if let Some(mut err) = self.demand_eqtype_pat_diag(span, expected, pat_ty, ti) {
             let reported = err.emit();
             // Walk subpatterns with an expected type of `err` in this case to silence
             // further errors being emitted when using the bindings. #50333
-            let element_tys_iter = (0..max_len).map(|_| tcx.ty_error(reported));
+            let element_tys_iter = (0..max_len).map(|_| Ty::new_error(tcx, reported));
             for (_, elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) {
-                self.check_pat(elem, tcx.ty_error(reported), def_bm, ti);
+                self.check_pat(elem, Ty::new_error(tcx, reported), def_bm, ti);
             }
-            tcx.mk_tup_from_iter(element_tys_iter)
+            Ty::new_tup_from_iter(tcx, element_tys_iter)
         } else {
             for (i, elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) {
                 self.check_pat(elem, element_tys[i], def_bm, ti);
@@ -1357,7 +1357,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 Occupied(occupied) => {
                     no_field_errors = false;
                     let guar = self.error_field_already_bound(span, field.ident, *occupied.get());
-                    tcx.ty_error(guar)
+                    Ty::new_error(tcx, guar)
                 }
                 Vacant(vacant) => {
                     vacant.insert(span);
@@ -1371,7 +1371,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         .unwrap_or_else(|| {
                             inexistent_fields.push(field);
                             no_field_errors = false;
-                            tcx.ty_error_misc()
+                            Ty::new_misc_error(tcx)
                         })
                 }
             };
@@ -1951,12 +1951,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     kind: TypeVariableOriginKind::TypeInference,
                     span: inner.span,
                 });
-                let box_ty = tcx.mk_box(inner_ty);
+                let box_ty = Ty::new_box(tcx, inner_ty);
                 self.demand_eqtype_pat(span, expected, box_ty, ti);
                 (box_ty, inner_ty)
             }
             Err(guar) => {
-                let err = tcx.ty_error(guar);
+                let err = Ty::new_error(tcx, guar);
                 (err, err)
             }
         };
@@ -2007,7 +2007,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 }
             }
             Err(guar) => {
-                let err = tcx.ty_error(guar);
+                let err = Ty::new_error(tcx, guar);
                 (err, err)
             }
         };
@@ -2019,7 +2019,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     fn new_ref_ty(&self, span: Span, mutbl: hir::Mutability, ty: Ty<'tcx>) -> Ty<'tcx> {
         let region = self.next_region_var(infer::PatternRegion(span));
         let mt = ty::TypeAndMut { ty, mutbl };
-        self.tcx.mk_ref(region, mt)
+        Ty::new_ref(self.tcx, region, mt)
     }
 
     /// Type check a slice pattern.
@@ -2042,7 +2042,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         def_bm: BindingMode,
         ti: TopInfo<'tcx>,
     ) -> Ty<'tcx> {
-        let expected = self.structurally_resolved_type(span, expected);
+        let expected = self.structurally_resolve_type(span, expected);
         let (element_ty, opt_slice_ty, inferred) = match *expected.kind() {
             // An array, so we might have something like `let [a, b, c] = [0, 1, 2];`.
             ty::Array(element_ty, len) => {
@@ -2061,7 +2061,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     .error_reported()
                     .err()
                     .unwrap_or_else(|| self.error_expected_array_or_slice(span, expected, ti));
-                let err = self.tcx.ty_error(guar);
+                let err = Ty::new_error(self.tcx, guar);
                 (err, Some(err), err)
             }
         };
@@ -2108,7 +2108,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             } else if let Some(pat_len) = len.checked_sub(min_len) {
                 // The variable-length pattern was there,
                 // so it has an array type with the remaining elements left as its size...
-                return (Some(self.tcx.mk_array(element_ty, pat_len)), arr_ty);
+                return (Some(Ty::new_array(self.tcx, element_ty, pat_len)), arr_ty);
             } else {
                 // ...however, in this case, there were no remaining elements.
                 // That is, the slice pattern requires more than the array type offers.
@@ -2117,7 +2117,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         } else if slice.is_none() {
             // We have a pattern with a fixed length,
             // which we can use to infer the length of the array.
-            let updated_arr_ty = self.tcx.mk_array(element_ty, min_len);
+            let updated_arr_ty = Ty::new_array(self.tcx, element_ty, min_len);
             self.demand_eqtype(span, updated_arr_ty, arr_ty);
             return (None, updated_arr_ty);
         } else {
@@ -2128,7 +2128,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         };
 
         // If we get here, we must have emitted an error.
-        (Some(self.tcx.ty_error(guar)), arr_ty)
+        (Some(Ty::new_error(self.tcx, guar)), arr_ty)
     }
 
     fn error_scrutinee_inconsistent_length(
diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs
index e2b1dc007ba..826cee3f982 100644
--- a/compiler/rustc_hir_typeck/src/place_op.rs
+++ b/compiler/rustc_hir_typeck/src/place_op.rs
@@ -90,7 +90,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             );
         }
         let reported = err.emit();
-        Some((self.tcx.ty_error(reported), self.tcx.ty_error(reported)))
+        Some((Ty::new_error(self.tcx, reported), Ty::new_error(self.tcx, reported)))
     }
 
     /// To type-check `base_expr[index_expr]`, we progressively autoderef
@@ -107,7 +107,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         index_expr: &hir::Expr<'_>,
     ) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> {
         let adjusted_ty =
-            self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false));
+            self.structurally_resolve_type(autoderef.span(), autoderef.final_ty(false));
         debug!(
             "try_index_step(expr={:?}, base_expr={:?}, adjusted_ty={:?}, \
              index_ty={:?})",
@@ -138,7 +138,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             if unsize {
                 // We only unsize arrays here.
                 if let ty::Array(element_ty, _) = adjusted_ty.kind() {
-                    self_ty = self.tcx.mk_slice(*element_ty);
+                    self_ty = Ty::new_slice(self.tcx, *element_ty);
                 } else {
                     continue;
                 }
@@ -162,7 +162,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind() {
                     adjustments.push(Adjustment {
                         kind: Adjust::Borrow(AutoBorrow::Ref(*region, AutoBorrowMutability::Not)),
-                        target: self.tcx.mk_ref(
+                        target: Ty::new_ref(
+                            self.tcx,
                             *region,
                             ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: adjusted_ty },
                         ),
@@ -427,9 +428,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         allow_two_phase_borrow: AllowTwoPhase::No,
                     };
                     adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(*region, mutbl));
-                    adjustment.target = self
-                        .tcx
-                        .mk_ref(*region, ty::TypeAndMut { ty: source, mutbl: mutbl.into() });
+                    adjustment.target = Ty::new_ref(
+                        self.tcx,
+                        *region,
+                        ty::TypeAndMut { ty: source, mutbl: mutbl.into() },
+                    );
                 }
                 source = adjustment.target;
             }
diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs
index 9458099f56f..208c40a3932 100644
--- a/compiler/rustc_hir_typeck/src/upvar.rs
+++ b/compiler/rustc_hir_typeck/src/upvar.rs
@@ -300,7 +300,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         // Build a tuple (U0..Un) of the final upvar types U0..Un
         // and unify the upvar tuple type in the closure with it:
-        let final_tupled_upvars_type = self.tcx.mk_tup(&final_upvar_tys);
+        let final_tupled_upvars_type = Ty::new_tup(self.tcx, &final_upvar_tys);
         self.demand_suptype(span, substs.tupled_upvars_ty(), final_tupled_upvars_type);
 
         let fake_reads = delegate
@@ -314,8 +314,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             self.typeck_results.borrow_mut().closure_size_eval.insert(
                 closure_def_id,
                 ClosureSizeProfileData {
-                    before_feature_tys: self.tcx.mk_tup(&before_feature_tys),
-                    after_feature_tys: self.tcx.mk_tup(&after_feature_tys),
+                    before_feature_tys: Ty::new_tup(self.tcx, &before_feature_tys),
+                    after_feature_tys: Ty::new_tup(self.tcx, &after_feature_tys),
                 },
             );
         }
@@ -1665,9 +1665,11 @@ fn apply_capture_kind_on_capture_ty<'tcx>(
 ) -> Ty<'tcx> {
     match capture_kind {
         ty::UpvarCapture::ByValue => ty,
-        ty::UpvarCapture::ByRef(kind) => {
-            tcx.mk_ref(region.unwrap(), ty::TypeAndMut { ty: ty, mutbl: kind.to_mutbl_lossy() })
-        }
+        ty::UpvarCapture::ByRef(kind) => Ty::new_ref(
+            tcx,
+            region.unwrap(),
+            ty::TypeAndMut { ty: ty, mutbl: kind.to_mutbl_lossy() },
+        ),
     }
 }
 
diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs
index 0cf3a4e877a..01332483a06 100644
--- a/compiler/rustc_hir_typeck/src/writeback.rs
+++ b/compiler/rustc_hir_typeck/src/writeback.rs
@@ -232,7 +232,8 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
                         // to access an nonexistent index. We assume that more relevant errors will
                         // already have been emitted, so we only gate on this with an ICE if no
                         // error has been emitted. (#64638)
-                        self.fcx.tcx.ty_error_with_message(
+                        Ty::new_error_with_message(
+                            self.fcx.tcx,
                             e.span,
                             format!("bad index {:?} for base: `{:?}`", index, base),
                         )
@@ -823,7 +824,7 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Resolver<'cx, 'tcx> {
                 debug!("Resolver::fold_ty: input type `{:?}` not fully resolvable", t);
                 let e = self.report_error(t);
                 self.replaced_with_error = Some(e);
-                self.fcx.tcx.ty_error(e)
+                Ty::new_error(self.fcx.tcx, e)
             }
         }
     }
@@ -840,7 +841,7 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Resolver<'cx, 'tcx> {
                 debug!("Resolver::fold_const: input const `{:?}` not fully resolvable", ct);
                 let e = self.report_error(ct);
                 self.replaced_with_error = Some(e);
-                self.fcx.tcx.const_error(ct.ty(), e)
+                ty::Const::new_error(self.fcx.tcx, e, ct.ty())
             }
         }
     }
diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs
index f1a187d2677..433735e827b 100644
--- a/compiler/rustc_infer/src/infer/at.rs
+++ b/compiler/rustc_infer/src/infer/at.rs
@@ -40,6 +40,7 @@ pub enum DefineOpaqueTypes {
     No,
 }
 
+#[derive(Clone, Copy)]
 pub struct At<'a, 'tcx> {
     pub infcx: &'a InferCtxt<'tcx>,
     pub cause: &'a ObligationCause<'tcx>,
diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
index bf53a73f398..e57532e2de2 100644
--- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
+++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
@@ -382,7 +382,7 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
                 // any equated inference vars correctly!
                 let root_vid = self.infcx.root_var(vid);
                 if root_vid != vid {
-                    t = self.infcx.tcx.mk_ty_var(root_vid);
+                    t = Ty::new_var(self.infcx.tcx, root_vid);
                     vid = root_vid;
                 }
 
@@ -497,7 +497,7 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
                 // any equated inference vars correctly!
                 let root_vid = self.infcx.root_const_var(vid);
                 if root_vid != vid {
-                    ct = self.infcx.tcx.mk_const(ty::InferConst::Var(root_vid), ct.ty());
+                    ct = ty::Const::new_var(self.infcx.tcx, root_vid, ct.ty());
                     vid = root_vid;
                 }
 
@@ -785,7 +785,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
             self.fold_ty(bound_to)
         } else {
             let var = self.canonical_var(info, ty_var.into());
-            self.interner().mk_bound(self.binder_index, var.into())
+            Ty::new_bound(self.tcx, self.binder_index, var.into())
         }
     }
 
@@ -804,10 +804,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
             self.fold_const(bound_to)
         } else {
             let var = self.canonical_var(info, const_var.into());
-            self.interner().mk_const(
-                ty::ConstKind::Bound(self.binder_index, var),
-                self.fold_ty(const_var.ty()),
-            )
+            ty::Const::new_bound(self.tcx, self.binder_index, var, self.fold_ty(const_var.ty()))
         }
     }
 }
diff --git a/compiler/rustc_infer/src/infer/canonical/mod.rs b/compiler/rustc_infer/src/infer/canonical/mod.rs
index c8c318c3f02..f765c41a367 100644
--- a/compiler/rustc_infer/src/infer/canonical/mod.rs
+++ b/compiler/rustc_infer/src/infer/canonical/mod.rs
@@ -26,7 +26,7 @@ use crate::infer::{InferCtxt, RegionVariableOrigin, TypeVariableOrigin, TypeVari
 use rustc_index::IndexVec;
 use rustc_middle::ty::fold::TypeFoldable;
 use rustc_middle::ty::subst::GenericArg;
-use rustc_middle::ty::{self, List, TyCtxt};
+use rustc_middle::ty::{self, List, Ty, TyCtxt};
 use rustc_span::source_map::Span;
 
 pub use rustc_middle::infer::canonical::*;
@@ -128,7 +128,7 @@ impl<'tcx> InferCtxt<'tcx> {
             CanonicalVarKind::PlaceholderTy(ty::PlaceholderType { universe, bound }) => {
                 let universe_mapped = universe_map(universe);
                 let placeholder_mapped = ty::PlaceholderType { universe: universe_mapped, bound };
-                self.tcx.mk_placeholder(placeholder_mapped).into()
+                Ty::new_placeholder(self.tcx, placeholder_mapped).into()
             }
 
             CanonicalVarKind::Region(ui) => self
@@ -155,7 +155,7 @@ impl<'tcx> InferCtxt<'tcx> {
             CanonicalVarKind::PlaceholderConst(ty::PlaceholderConst { universe, bound }, ty) => {
                 let universe_mapped = universe_map(universe);
                 let placeholder_mapped = ty::PlaceholderConst { universe: universe_mapped, bound };
-                self.tcx.mk_const(placeholder_mapped, ty).into()
+                ty::Const::new_placeholder(self.tcx, placeholder_mapped, ty).into()
             }
         }
     }
diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs
index 32054e6d125..9c3ab04deae 100644
--- a/compiler/rustc_infer/src/infer/canonical/query_response.rs
+++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs
@@ -520,7 +520,7 @@ impl<'tcx> InferCtxt<'tcx> {
                 self.at(cause, param_env)
                     .eq(
                         DefineOpaqueTypes::Yes,
-                        self.tcx.mk_opaque(a.def_id.to_def_id(), a.substs),
+                        Ty::new_opaque(self.tcx, a.def_id.to_def_id(), a.substs),
                         b,
                     )?
                     .obligations,
diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs
index fc6ff01c00c..a9cdb8c51cf 100644
--- a/compiler/rustc_infer/src/infer/combine.rs
+++ b/compiler/rustc_infer/src/infer/combine.rs
@@ -189,11 +189,11 @@ impl<'tcx> InferCtxt<'tcx> {
             // HACK: equating both sides with `[const error]` eagerly prevents us
             // from leaving unconstrained inference vars during things like impl
             // matching in the solver.
-            let a_error = self.tcx.const_error(a.ty(), guar);
+            let a_error = ty::Const::new_error(self.tcx, guar, a.ty());
             if let ty::ConstKind::Infer(InferConst::Var(vid)) = a.kind() {
                 return self.unify_const_variable(vid, a_error, relation.param_env());
             }
-            let b_error = self.tcx.const_error(b.ty(), guar);
+            let b_error = ty::Const::new_error(self.tcx, guar, b.ty());
             if let ty::ConstKind::Infer(InferConst::Var(vid)) = b.kind() {
                 return self.unify_const_variable(vid, b_error, relation.param_env());
             }
@@ -322,8 +322,8 @@ impl<'tcx> InferCtxt<'tcx> {
             .unify_var_value(vid, Some(val))
             .map_err(|e| int_unification_error(vid_is_expected, e))?;
         match val {
-            IntType(v) => Ok(self.tcx.mk_mach_int(v)),
-            UintType(v) => Ok(self.tcx.mk_mach_uint(v)),
+            IntType(v) => Ok(Ty::new_int(self.tcx, v)),
+            UintType(v) => Ok(Ty::new_uint(self.tcx, v)),
         }
     }
 
@@ -338,7 +338,7 @@ impl<'tcx> InferCtxt<'tcx> {
             .float_unification_table()
             .unify_var_value(vid, Some(ty::FloatVarValue(val)))
             .map_err(|e| float_unification_error(vid_is_expected, e))?;
-        Ok(self.tcx.mk_mach_float(val))
+        Ok(Ty::new_float(self.tcx, val))
     }
 }
 
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index 15213c4b023..b826ced0453 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -315,7 +315,7 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>(
 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
     let mut err = tcx.sess.create_err(errors::OpaqueCapturesLifetime {
         span,
-        opaque_ty: tcx.mk_opaque(opaque_ty_key.def_id.to_def_id(), opaque_ty_key.substs),
+        opaque_ty: Ty::new_opaque(tcx, opaque_ty_key.def_id.to_def_id(), opaque_ty_key.substs),
         opaque_ty_span: tcx.def_span(opaque_ty_key.def_id),
     });
 
diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
index f3b2ec4c5e3..bb75ecc6adb 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
@@ -265,9 +265,9 @@ impl<'tcx> InferCtxt<'tcx> {
                                 kind: UnderspecifiedArgKind::Type {
                                     prefix: "type parameter".into(),
                                 },
-                                parent: def_id.and_then(|def_id| {
-                                    InferenceDiagnosticsParentData::for_def_id(self.tcx, def_id)
-                                }),
+                                parent: InferenceDiagnosticsParentData::for_def_id(
+                                    self.tcx, def_id,
+                                ),
                             };
                         }
                     }
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
index aad9885827d..a9b485a6f7e 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
@@ -288,7 +288,7 @@ pub fn suggest_new_region_bound(
 
                 // Get the identity type for this RPIT
                 let did = item_id.owner_id.to_def_id();
-                let ty = tcx.mk_opaque(did, ty::InternalSubsts::identity_for_item(tcx, did));
+                let ty = Ty::new_opaque(tcx, did, ty::InternalSubsts::identity_for_item(tcx, did));
 
                 if let Some(span) = opaque.bounds.iter().find_map(|arg| match arg {
                     GenericBound::Outlives(Lifetime {
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs
index ce70bcc5c85..12d38ced030 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs
@@ -41,8 +41,8 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
             // all of the region highlighting machinery only deals with those.
             let guar = self.emit_err(
                 var_origin.span(),
-                self.cx.tcx.mk_fn_ptr(ty::Binder::dummy(expected)),
-                self.cx.tcx.mk_fn_ptr(ty::Binder::dummy(found)),
+                Ty::new_fn_ptr(self.cx.tcx,ty::Binder::dummy(expected)),
+                Ty::new_fn_ptr(self.cx.tcx,ty::Binder::dummy(found)),
                 *trait_item_def_id,
             );
             return Some(guar);
diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs
index 07a9eff2dbe..e55e9e75fb6 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/note.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs
@@ -11,7 +11,7 @@ use rustc_errors::{
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_middle::traits::ObligationCauseCode;
 use rustc_middle::ty::error::TypeError;
-use rustc_middle::ty::{self, IsSuggestable, Region};
+use rustc_middle::ty::{self, IsSuggestable, Region, Ty};
 use rustc_span::symbol::kw;
 
 use super::ObligationCauseAsDiagArg;
@@ -304,7 +304,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         let trait_substs = trait_ref
             .subst_identity()
             // Replace the explicit self type with `Self` for better suggestion rendering
-            .with_self_ty(self.tcx, self.tcx.mk_ty_param(0, kw::SelfUpper))
+            .with_self_ty(self.tcx, Ty::new_param(self.tcx, 0, kw::SelfUpper))
             .substs;
         let trait_item_substs = ty::InternalSubsts::identity_for_item(self.tcx, impl_item_def_id)
             .rebase_onto(self.tcx, impl_def_id, trait_substs);
diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs
index 7293de4c6c5..c9a8f8131df 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs
@@ -574,7 +574,9 @@ fn foo(&self) -> Self::T { String::new() }
         let Some(hir_id) = body_owner_def_id.as_local() else {
             return false;
         };
-        let hir_id = tcx.hir().local_def_id_to_hir_id(hir_id);
+        let Some(hir_id) = tcx.opt_local_def_id_to_hir_id(hir_id) else {
+            return false;
+        };
         // When `body_owner` is an `impl` or `trait` item, look in its associated types for
         // `expected` and point at it.
         let parent_id = tcx.hir().get_parent_item(hir_id);
diff --git a/compiler/rustc_infer/src/infer/freshen.rs b/compiler/rustc_infer/src/infer/freshen.rs
index 0219167f6e5..05769b7907a 100644
--- a/compiler/rustc_infer/src/infer/freshen.rs
+++ b/compiler/rustc_infer/src/infer/freshen.rs
@@ -95,7 +95,7 @@ impl<'a, 'tcx> TypeFreshener<'a, 'tcx> {
             Entry::Vacant(entry) => {
                 let index = self.const_freshen_count;
                 self.const_freshen_count += 1;
-                let ct = self.infcx.tcx.mk_const(freshener(index), ty);
+                let ct = ty::Const::new_infer(self.infcx.tcx, freshener(index), ty);
                 entry.insert(ct);
                 ct
             }
@@ -188,7 +188,7 @@ impl<'a, 'tcx> TypeFreshener<'a, 'tcx> {
         match v {
             ty::TyVar(v) => {
                 let opt_ty = self.infcx.inner.borrow_mut().type_variables().probe(v).known();
-                Some(self.freshen_ty(opt_ty, ty::TyVar(v), |n| self.infcx.tcx.mk_fresh_ty(n)))
+                Some(self.freshen_ty(opt_ty, ty::TyVar(v), |n| Ty::new_fresh(self.infcx.tcx, n)))
             }
 
             ty::IntVar(v) => Some(
@@ -200,7 +200,7 @@ impl<'a, 'tcx> TypeFreshener<'a, 'tcx> {
                         .probe_value(v)
                         .map(|v| v.to_type(self.infcx.tcx)),
                     ty::IntVar(v),
-                    |n| self.infcx.tcx.mk_fresh_int_ty(n),
+                    |n| Ty::new_fresh_int(self.infcx.tcx, n),
                 ),
             ),
 
@@ -213,7 +213,7 @@ impl<'a, 'tcx> TypeFreshener<'a, 'tcx> {
                         .probe_value(v)
                         .map(|v| v.to_type(self.infcx.tcx)),
                     ty::FloatVar(v),
-                    |n| self.infcx.tcx.mk_fresh_float_ty(n),
+                    |n| Ty::new_fresh_float(self.infcx.tcx, n),
                 ),
             ),
 
diff --git a/compiler/rustc_infer/src/infer/generalize.rs b/compiler/rustc_infer/src/infer/generalize.rs
index d4a1dacde10..78025016757 100644
--- a/compiler/rustc_infer/src/infer/generalize.rs
+++ b/compiler/rustc_infer/src/infer/generalize.rs
@@ -277,7 +277,7 @@ where
                             let origin = *inner.type_variables().var_origin(vid);
                             let new_var_id =
                                 inner.type_variables().new_var(self.for_universe, origin);
-                            let u = self.tcx().mk_ty_var(new_var_id);
+                            let u = Ty::new_var(self.tcx(), new_var_id);
 
                             // Record that we replaced `vid` with `new_var_id` as part of a generalization
                             // operation. This is needed to detect cyclic types. To see why, see the
@@ -398,7 +398,7 @@ where
                                 origin: var_value.origin,
                                 val: ConstVariableValue::Unknown { universe: self.for_universe },
                             });
-                            Ok(self.tcx().mk_const(new_var_id, c.ty()))
+                            Ok(ty::Const::new_var(self.tcx(), new_var_id, c.ty()))
                         }
                     }
                 }
@@ -412,7 +412,11 @@ where
                     substs,
                     substs,
                 )?;
-                Ok(self.tcx().mk_const(ty::UnevaluatedConst { def, substs }, c.ty()))
+                Ok(ty::Const::new_unevaluated(
+                    self.tcx(),
+                    ty::UnevaluatedConst { def, substs },
+                    c.ty(),
+                ))
             }
             ty::ConstKind::Placeholder(placeholder) => {
                 if self.for_universe.can_name(placeholder.universe) {
diff --git a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs
index 974bc2f1153..510b1797d3c 100644
--- a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs
+++ b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs
@@ -6,7 +6,7 @@ use super::{HigherRankedType, InferCtxt};
 use crate::infer::CombinedSnapshot;
 use rustc_middle::ty::fold::FnMutDelegate;
 use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
-use rustc_middle::ty::{self, Binder, TyCtxt, TypeFoldable};
+use rustc_middle::ty::{self, Binder, Ty, TyCtxt, TypeFoldable};
 
 impl<'a, 'tcx> CombineFields<'a, 'tcx> {
     /// Checks whether `for<..> sub <: for<..> sup` holds.
@@ -88,13 +88,14 @@ impl<'tcx> InferCtxt<'tcx> {
                 )
             },
             types: &mut |bound_ty: ty::BoundTy| {
-                self.tcx.mk_placeholder(ty::PlaceholderType {
-                    universe: next_universe,
-                    bound: bound_ty,
-                })
+                Ty::new_placeholder(
+                    self.tcx,
+                    ty::PlaceholderType { universe: next_universe, bound: bound_ty },
+                )
             },
             consts: &mut |bound_var: ty::BoundVar, ty| {
-                self.tcx.mk_const(
+                ty::Const::new_placeholder(
+                    self.tcx,
                     ty::PlaceholderConst { universe: next_universe, bound: bound_var },
                     ty,
                 )
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index f1f5ac81fb7..fca32b73d1d 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -725,19 +725,19 @@ impl<'tcx> InferCtxt<'tcx> {
             .type_variables()
             .unsolved_variables()
             .into_iter()
-            .map(|t| self.tcx.mk_ty_var(t))
+            .map(|t| Ty::new_var(self.tcx, t))
             .collect();
         vars.extend(
             (0..inner.int_unification_table().len())
                 .map(|i| ty::IntVid { index: i as u32 })
                 .filter(|&vid| inner.int_unification_table().probe_value(vid).is_none())
-                .map(|v| self.tcx.mk_int_var(v)),
+                .map(|v| Ty::new_int_var(self.tcx, v)),
         );
         vars.extend(
             (0..inner.float_unification_table().len())
                 .map(|i| ty::FloatVid { index: i as u32 })
                 .filter(|&vid| inner.float_unification_table().probe_value(vid).is_none())
-                .map(|v| self.tcx.mk_float_var(v)),
+                .map(|v| Ty::new_float_var(self.tcx, v)),
         );
         vars
     }
@@ -978,7 +978,7 @@ impl<'tcx> InferCtxt<'tcx> {
     }
 
     pub fn next_ty_var(&self, origin: TypeVariableOrigin) -> Ty<'tcx> {
-        self.tcx.mk_ty_var(self.next_ty_var_id(origin))
+        Ty::new_var(self.tcx, self.next_ty_var_id(origin))
     }
 
     pub fn next_ty_var_id_in_universe(
@@ -995,11 +995,11 @@ impl<'tcx> InferCtxt<'tcx> {
         universe: ty::UniverseIndex,
     ) -> Ty<'tcx> {
         let vid = self.next_ty_var_id_in_universe(origin, universe);
-        self.tcx.mk_ty_var(vid)
+        Ty::new_var(self.tcx, vid)
     }
 
     pub fn next_const_var(&self, ty: Ty<'tcx>, origin: ConstVariableOrigin) -> ty::Const<'tcx> {
-        self.tcx.mk_const(self.next_const_var_id(origin), ty)
+        ty::Const::new_var(self.tcx, self.next_const_var_id(origin), ty)
     }
 
     pub fn next_const_var_in_universe(
@@ -1013,7 +1013,7 @@ impl<'tcx> InferCtxt<'tcx> {
             .borrow_mut()
             .const_unification_table()
             .new_key(ConstVarValue { origin, val: ConstVariableValue::Unknown { universe } });
-        self.tcx.mk_const(vid, ty)
+        ty::Const::new_var(self.tcx, vid, ty)
     }
 
     pub fn next_const_var_id(&self, origin: ConstVariableOrigin) -> ConstVid<'tcx> {
@@ -1028,7 +1028,7 @@ impl<'tcx> InferCtxt<'tcx> {
     }
 
     pub fn next_int_var(&self) -> Ty<'tcx> {
-        self.tcx.mk_int_var(self.next_int_var_id())
+        Ty::new_int_var(self.tcx, self.next_int_var_id())
     }
 
     fn next_float_var_id(&self) -> FloatVid {
@@ -1036,7 +1036,7 @@ impl<'tcx> InferCtxt<'tcx> {
     }
 
     pub fn next_float_var(&self) -> Ty<'tcx> {
-        self.tcx.mk_float_var(self.next_float_var_id())
+        Ty::new_float_var(self.tcx, self.next_float_var_id())
     }
 
     /// Creates a fresh region variable with the next available index.
@@ -1110,13 +1110,13 @@ impl<'tcx> InferCtxt<'tcx> {
                     TypeVariableOrigin {
                         kind: TypeVariableOriginKind::TypeParameterDefinition(
                             param.name,
-                            Some(param.def_id),
+                            param.def_id,
                         ),
                         span,
                     },
                 );
 
-                self.tcx.mk_ty_var(ty_var_id).into()
+                Ty::new_var(self.tcx, ty_var_id).into()
             }
             GenericParamDefKind::Const { .. } => {
                 let origin = ConstVariableOrigin {
@@ -1131,15 +1131,15 @@ impl<'tcx> InferCtxt<'tcx> {
                         origin,
                         val: ConstVariableValue::Unknown { universe: self.universe() },
                     });
-                self.tcx
-                    .mk_const(
-                        const_var_id,
-                        self.tcx
-                            .type_of(param.def_id)
-                            .no_bound_vars()
-                            .expect("const parameter types cannot be generic"),
-                    )
-                    .into()
+                ty::Const::new_var(
+                    self.tcx,
+                    const_var_id,
+                    self.tcx
+                        .type_of(param.def_id)
+                        .no_bound_vars()
+                        .expect("const parameter types cannot be generic"),
+                )
+                .into()
             }
         }
     }
@@ -1265,7 +1265,7 @@ impl<'tcx> InferCtxt<'tcx> {
         if let Some(value) = inner.int_unification_table().probe_value(vid) {
             value.to_type(self.tcx)
         } else {
-            self.tcx.mk_int_var(inner.int_unification_table().find(vid))
+            Ty::new_int_var(self.tcx, inner.int_unification_table().find(vid))
         }
     }
 
@@ -1276,7 +1276,7 @@ impl<'tcx> InferCtxt<'tcx> {
         if let Some(value) = inner.float_unification_table().probe_value(vid) {
             value.to_type(self.tcx)
         } else {
-            self.tcx.mk_float_var(inner.float_unification_table().find(vid))
+            Ty::new_float_var(self.tcx, inner.float_unification_table().find(vid))
         }
     }
 
@@ -1472,7 +1472,7 @@ impl<'tcx> InferCtxt<'tcx> {
         span: Option<Span>,
     ) -> Result<ty::Const<'tcx>, ErrorHandled> {
         match self.const_eval_resolve(param_env, unevaluated, span) {
-            Ok(Some(val)) => Ok(self.tcx.mk_const(val, ty)),
+            Ok(Some(val)) => Ok(ty::Const::new_value(self.tcx, val, ty)),
             Ok(None) => {
                 let tcx = self.tcx;
                 let def_id = unevaluated.def;
@@ -1945,13 +1945,16 @@ fn replace_param_and_infer_substs_with_placeholder<'tcx>(
                     self.idx += 1;
                     idx
                 };
-                self.tcx.mk_placeholder(ty::PlaceholderType {
-                    universe: ty::UniverseIndex::ROOT,
-                    bound: ty::BoundTy {
-                        var: ty::BoundVar::from_u32(idx),
-                        kind: ty::BoundTyKind::Anon,
+                Ty::new_placeholder(
+                    self.tcx,
+                    ty::PlaceholderType {
+                        universe: ty::UniverseIndex::ROOT,
+                        bound: ty::BoundTy {
+                            var: ty::BoundVar::from_u32(idx),
+                            kind: ty::BoundTyKind::Anon,
+                        },
                     },
-                })
+                )
             } else {
                 t.super_fold_with(self)
             }
@@ -1964,7 +1967,8 @@ fn replace_param_and_infer_substs_with_placeholder<'tcx>(
                 if ty.has_non_region_param() || ty.has_non_region_infer() {
                     bug!("const `{c}`'s type should not reference params or types");
                 }
-                self.tcx.mk_const(
+                ty::Const::new_placeholder(
+                    self.tcx,
                     ty::PlaceholderConst {
                         universe: ty::UniverseIndex::ROOT,
                         bound: ty::BoundVar::from_u32({
diff --git a/compiler/rustc_infer/src/infer/outlives/mod.rs b/compiler/rustc_infer/src/infer/outlives/mod.rs
index c998c923760..cb92fc6ddb6 100644
--- a/compiler/rustc_infer/src/infer/outlives/mod.rs
+++ b/compiler/rustc_infer/src/infer/outlives/mod.rs
@@ -31,8 +31,7 @@ pub fn explicit_outlives_bounds<'tcx>(
             | ty::ClauseKind::Projection(_)
             | ty::ClauseKind::ConstArgHasType(_, _)
             | ty::ClauseKind::WellFormed(_)
-            | ty::ClauseKind::ConstEvaluatable(_)
-            | ty::ClauseKind::TypeWellFormedFromEnv(_) => None,
+            | ty::ClauseKind::ConstEvaluatable(_) => None,
         })
 }
 
diff --git a/compiler/rustc_infer/src/infer/projection.rs b/compiler/rustc_infer/src/infer/projection.rs
index 75455533181..38e74e53868 100644
--- a/compiler/rustc_infer/src/infer/projection.rs
+++ b/compiler/rustc_infer/src/infer/projection.rs
@@ -21,29 +21,18 @@ impl<'tcx> InferCtxt<'tcx> {
         recursion_depth: usize,
         obligations: &mut Vec<PredicateObligation<'tcx>>,
     ) -> Ty<'tcx> {
-        if self.next_trait_solver() {
-            // FIXME(-Ztrait-solver=next): Instead of branching here,
-            // completely change the normalization routine with the new solver.
-            //
-            // The new solver correctly handles projection equality so this hack
-            // is not necessary. if re-enabled it should emit `PredicateKind::AliasRelate`
-            // not `PredicateKind::Clause(ClauseKind::Projection(..))` as in the new solver
-            // `Projection` is used as `normalizes-to` which will fail for `<T as Trait>::Assoc eq ?0`.
-            return projection_ty.to_ty(self.tcx);
-        } else {
-            let def_id = projection_ty.def_id;
-            let ty_var = self.next_ty_var(TypeVariableOrigin {
-                kind: TypeVariableOriginKind::NormalizeProjectionType,
-                span: self.tcx.def_span(def_id),
-            });
-            let projection =
-                ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::Projection(
-                    ty::ProjectionPredicate { projection_ty, term: ty_var.into() },
-                )));
-            let obligation =
-                Obligation::with_depth(self.tcx, cause, recursion_depth, param_env, projection);
-            obligations.push(obligation);
-            ty_var
-        }
+        debug_assert!(!self.next_trait_solver());
+        let def_id = projection_ty.def_id;
+        let ty_var = self.next_ty_var(TypeVariableOrigin {
+            kind: TypeVariableOriginKind::NormalizeProjectionType,
+            span: self.tcx.def_span(def_id),
+        });
+        let projection = ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::Projection(
+            ty::ProjectionPredicate { projection_ty, term: ty_var.into() },
+        )));
+        let obligation =
+            Obligation::with_depth(self.tcx, cause, recursion_depth, param_env, projection);
+        obligations.push(obligation);
+        ty_var
     }
 }
diff --git a/compiler/rustc_infer/src/infer/sub.rs b/compiler/rustc_infer/src/infer/sub.rs
index d9f9d2aabdb..27e1ed56f31 100644
--- a/compiler/rustc_infer/src/infer/sub.rs
+++ b/compiler/rustc_infer/src/infer/sub.rs
@@ -118,7 +118,7 @@ impl<'tcx> TypeRelation<'tcx> for Sub<'_, '_, 'tcx> {
 
             (&ty::Error(e), _) | (_, &ty::Error(e)) => {
                 infcx.set_tainted_by_errors(e);
-                Ok(self.tcx().ty_error(e))
+                Ok(Ty::new_error(self.tcx(), e))
             }
 
             (
diff --git a/compiler/rustc_infer/src/infer/type_variable.rs b/compiler/rustc_infer/src/infer/type_variable.rs
index 9f85f9207e8..01c11d16345 100644
--- a/compiler/rustc_infer/src/infer/type_variable.rs
+++ b/compiler/rustc_infer/src/infer/type_variable.rs
@@ -123,7 +123,7 @@ pub enum TypeVariableOriginKind {
     NormalizeProjectionType,
     TypeInference,
     OpaqueTypeInference(DefId),
-    TypeParameterDefinition(Symbol, Option<DefId>),
+    TypeParameterDefinition(Symbol, DefId),
 
     /// One of the upvars or closure kind parameters in a `ClosureSubsts`
     /// (before it has been determined).
diff --git a/compiler/rustc_infer/src/traits/error_reporting/mod.rs b/compiler/rustc_infer/src/traits/error_reporting/mod.rs
index b5a7d0326a8..9f440f39849 100644
--- a/compiler/rustc_infer/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/traits/error_reporting/mod.rs
@@ -25,9 +25,11 @@ impl<'tcx> InferCtxt<'tcx> {
             "impl has stricter requirements than trait"
         );
 
-        if let Some(span) = self.tcx.hir().span_if_local(trait_item_def_id) {
-            let item_name = self.tcx.item_name(impl_item_def_id.to_def_id());
-            err.span_label(span, format!("definition of `{}` from trait", item_name));
+        if !self.tcx.is_impl_trait_in_trait(trait_item_def_id) {
+            if let Some(span) = self.tcx.hir().span_if_local(trait_item_def_id) {
+                let item_name = self.tcx.item_name(impl_item_def_id.to_def_id());
+                err.span_label(span, format!("definition of `{}` from trait", item_name));
+            }
         }
 
         err.span_label(error_span, format!("impl has extra requirement {}", requirement));
diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs
index 66389cf5995..074ff7ec97f 100644
--- a/compiler/rustc_infer/src/traits/util.rs
+++ b/compiler/rustc_infer/src/traits/util.rs
@@ -3,7 +3,7 @@ use smallvec::smallvec;
 use crate::infer::outlives::components::{push_outlives_components, Component};
 use crate::traits::{self, Obligation, PredicateObligation};
 use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
-use rustc_middle::ty::{self, ToPredicate, TyCtxt};
+use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt};
 use rustc_span::symbol::Ident;
 use rustc_span::Span;
 
@@ -344,7 +344,7 @@ impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> {
                             }
 
                             Component::Param(p) => {
-                                let ty = tcx.mk_ty_param(p.index, p.name);
+                                let ty = Ty::new_param(tcx, p.index, p.name);
                                 Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, r_min)))
                             }
 
@@ -370,9 +370,6 @@ impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> {
                         }),
                 );
             }
-            ty::PredicateKind::Clause(ty::ClauseKind::TypeWellFormedFromEnv(..)) => {
-                // Nothing to elaborate
-            }
             ty::PredicateKind::Ambiguous => {}
             ty::PredicateKind::AliasRelate(..) => {
                 // No
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index 0f5dba1df14..09141afd137 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -841,7 +841,7 @@ fn test_unstable_options_tracking_hash() {
     tracked!(thir_unsafeck, true);
     tracked!(tiny_const_eval_limit, true);
     tracked!(tls_model, Some(TlsModel::GeneralDynamic));
-    tracked!(trait_solver, TraitSolver::Chalk);
+    tracked!(trait_solver, TraitSolver::NextCoherence);
     tracked!(translate_remapped_path_to_local_path, false);
     tracked!(trap_unreachable, Some(false));
     tracked!(treat_err_as_bug, NonZeroUsize::new(1));
diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs
index d511d2b1280..29335a8c0f4 100644
--- a/compiler/rustc_lexer/src/lib.rs
+++ b/compiler/rustc_lexer/src/lib.rs
@@ -367,13 +367,6 @@ impl Cursor<'_> {
                 Some(|terminated| Byte { terminated }),
             ),
 
-            // c-string literal, raw c-string literal or identifier.
-            'c' => self.c_or_byte_string(
-                |terminated| CStr { terminated },
-                |n_hashes| RawCStr { n_hashes },
-                None,
-            ),
-
             // Identifier (this should be checked after other variant that can
             // start as identifier).
             c if is_id_start(c) => self.ident_or_unknown_prefix(),
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 3a177038ca8..b821933e908 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -674,21 +674,21 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations {
                     return;
                 }
                 let def = cx.tcx.adt_def(item.owner_id);
-                (def, cx.tcx.mk_adt(def, ty::List::empty()))
+                (def, Ty::new_adt(cx.tcx, def, ty::List::empty()))
             }
             hir::ItemKind::Union(_, ref ast_generics) => {
                 if !ast_generics.params.is_empty() {
                     return;
                 }
                 let def = cx.tcx.adt_def(item.owner_id);
-                (def, cx.tcx.mk_adt(def, ty::List::empty()))
+                (def, Ty::new_adt(cx.tcx, def, ty::List::empty()))
             }
             hir::ItemKind::Enum(_, ref ast_generics) => {
                 if !ast_generics.params.is_empty() {
                     return;
                 }
                 let def = cx.tcx.adt_def(item.owner_id);
-                (def, cx.tcx.mk_adt(def, ty::List::empty()))
+                (def, Ty::new_adt(cx.tcx, def, ty::List::empty()))
             }
             _ => return,
         };
@@ -1606,8 +1606,7 @@ impl<'tcx> LateLintPass<'tcx> for TrivialConstraints {
                     // Ignore bounds that a user can't type
                     | ClauseKind::WellFormed(..)
                     // FIXME(generic_const_exprs): `ConstEvaluatable` can be written
-                    | ClauseKind::ConstEvaluatable(..)
-                    | ClauseKind::TypeWellFormedFromEnv(_)  => continue,
+                    | ClauseKind::ConstEvaluatable(..)  => continue,
                 };
                 if predicate.is_global() {
                     cx.emit_spanned_lint(
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index 13164b0b339..3761754f3ae 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -1345,7 +1345,7 @@ impl<'tcx> LateContext<'tcx> {
         tcx.associated_items(trait_id)
             .find_by_name_and_kind(tcx, Ident::from_str(name), ty::AssocKind::Type, trait_id)
             .and_then(|assoc| {
-                let proj = tcx.mk_projection(assoc.def_id, [self_ty]);
+                let proj = Ty::new_projection(tcx, assoc.def_id, [self_ty]);
                 tcx.try_normalize_erasing_regions(self.param_env, proj).ok()
             })
     }
diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs
index 79253cbc8b4..145de494835 100644
--- a/compiler/rustc_lint/src/nonstandard_style.rs
+++ b/compiler/rustc_lint/src/nonstandard_style.rs
@@ -533,6 +533,10 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals {
 
     fn check_generic_param(&mut self, cx: &LateContext<'_>, param: &hir::GenericParam<'_>) {
         if let GenericParamKind::Const { .. } = param.kind {
+            // `rustc_host` params are explicitly allowed to be lowercase.
+            if cx.tcx.has_attr(param.def_id, sym::rustc_host) {
+                return;
+            }
             NonUpperCaseGlobals::check_upper_case(cx, "const parameter", &param.name.ident());
         }
     }
diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
index 63a56806a45..09a1651c2f5 100644
--- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
+++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
@@ -97,7 +97,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound {
             }
 
             let proj_ty =
-                cx.tcx.mk_projection(proj.projection_ty.def_id, proj.projection_ty.substs);
+                Ty::new_projection(cx.tcx, proj.projection_ty.def_id, proj.projection_ty.substs);
             // For every instance of the projection type in the bounds,
             // replace them with the term we're assigning to the associated
             // type in our opaque type.
@@ -144,7 +144,8 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound {
                         OPAQUE_HIDDEN_INFERRED_BOUND,
                         pred_span,
                         OpaqueHiddenInferredBoundLint {
-                            ty: cx.tcx.mk_opaque(
+                            ty: Ty::new_opaque(
+                                cx.tcx,
                                 def_id,
                                 ty::InternalSubsts::identity_for_item(cx.tcx, def_id),
                             ),
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index 9a2d45ccd66..2509d493a4c 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -862,12 +862,12 @@ fn get_nullable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'t
             };
             return get_nullable_type(cx, inner_field_ty);
         }
-        ty::Int(ty) => tcx.mk_mach_int(ty),
-        ty::Uint(ty) => tcx.mk_mach_uint(ty),
-        ty::RawPtr(ty_mut) => tcx.mk_ptr(ty_mut),
+        ty::Int(ty) => Ty::new_int(tcx, ty),
+        ty::Uint(ty) => Ty::new_uint(tcx, ty),
+        ty::RawPtr(ty_mut) => Ty::new_ptr(tcx, ty_mut),
         // As these types are always non-null, the nullable equivalent of
         // Option<T> of these types are their raw pointer counterparts.
-        ty::Ref(_region, ty, mutbl) => tcx.mk_ptr(ty::TypeAndMut { ty, mutbl }),
+        ty::Ref(_region, ty, mutbl) => Ty::new_ptr(tcx, ty::TypeAndMut { ty, mutbl }),
         ty::FnPtr(..) => {
             // There is no nullable equivalent for Rust's function pointers -- you
             // must use an Option<fn(..) -> _> to represent it.
@@ -1379,7 +1379,29 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
         }
     }
 
-    fn check_foreign_fn(&mut self, def_id: LocalDefId, decl: &hir::FnDecl<'_>) {
+    /// Check if a function's argument types and result type are "ffi-safe".
+    ///
+    /// For a external ABI function, argument types and the result type are walked to find fn-ptr
+    /// types that have external ABIs, as these still need checked.
+    fn check_fn(&mut self, def_id: LocalDefId, decl: &'tcx hir::FnDecl<'_>) {
+        let sig = self.cx.tcx.fn_sig(def_id).subst_identity();
+        let sig = self.cx.tcx.erase_late_bound_regions(sig);
+
+        for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) {
+            for (fn_ptr_ty, span) in self.find_fn_ptr_ty_with_external_abi(input_hir, *input_ty) {
+                self.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, false, false);
+            }
+        }
+
+        if let hir::FnRetTy::Return(ref ret_hir) = decl.output {
+            for (fn_ptr_ty, span) in self.find_fn_ptr_ty_with_external_abi(ret_hir, sig.output()) {
+                self.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, false, true);
+            }
+        }
+    }
+
+    /// Check if a function's argument types and result type are "ffi-safe".
+    fn check_foreign_fn(&mut self, def_id: LocalDefId, decl: &'tcx hir::FnDecl<'_>) {
         let sig = self.cx.tcx.fn_sig(def_id).subst_identity();
         let sig = self.cx.tcx.erase_late_bound_regions(sig);
 
@@ -1388,8 +1410,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
         }
 
         if let hir::FnRetTy::Return(ref ret_hir) = decl.output {
-            let ret_ty = sig.output();
-            self.check_type_for_ffi_and_report_errors(ret_hir.span, ret_ty, false, true);
+            self.check_type_for_ffi_and_report_errors(ret_hir.span, sig.output(), false, true);
         }
     }
 
@@ -1404,28 +1425,131 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
             SpecAbi::Rust | SpecAbi::RustCall | SpecAbi::RustIntrinsic | SpecAbi::PlatformIntrinsic
         )
     }
+
+    /// Find any fn-ptr types with external ABIs in `ty`.
+    ///
+    /// For example, `Option<extern "C" fn()>` returns `extern "C" fn()`
+    fn find_fn_ptr_ty_with_external_abi(
+        &self,
+        hir_ty: &hir::Ty<'tcx>,
+        ty: Ty<'tcx>,
+    ) -> Vec<(Ty<'tcx>, Span)> {
+        struct FnPtrFinder<'parent, 'a, 'tcx> {
+            visitor: &'parent ImproperCTypesVisitor<'a, 'tcx>,
+            spans: Vec<Span>,
+            tys: Vec<Ty<'tcx>>,
+        }
+
+        impl<'parent, 'a, 'tcx> hir::intravisit::Visitor<'_> for FnPtrFinder<'parent, 'a, 'tcx> {
+            fn visit_ty(&mut self, ty: &'_ hir::Ty<'_>) {
+                debug!(?ty);
+                if let hir::TyKind::BareFn(hir::BareFnTy { abi, .. }) = ty.kind
+                    && !self.visitor.is_internal_abi(*abi)
+                {
+                    self.spans.push(ty.span);
+                }
+
+                hir::intravisit::walk_ty(self, ty)
+            }
+        }
+
+        impl<'vis, 'a, 'tcx> ty::visit::TypeVisitor<TyCtxt<'tcx>> for FnPtrFinder<'vis, 'a, 'tcx> {
+            type BreakTy = Ty<'tcx>;
+
+            fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+                if let ty::FnPtr(sig) = ty.kind() && !self.visitor.is_internal_abi(sig.abi()) {
+                    self.tys.push(ty);
+                }
+
+                ty.super_visit_with(self)
+            }
+        }
+
+        let mut visitor = FnPtrFinder { visitor: &*self, spans: Vec::new(), tys: Vec::new() };
+        ty.visit_with(&mut visitor);
+        hir::intravisit::Visitor::visit_ty(&mut visitor, hir_ty);
+
+        iter::zip(visitor.tys.drain(..), visitor.spans.drain(..)).collect()
+    }
 }
 
 impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDeclarations {
-    fn check_foreign_item(&mut self, cx: &LateContext<'_>, it: &hir::ForeignItem<'_>) {
+    fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, it: &hir::ForeignItem<'tcx>) {
         let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Declaration };
         let abi = cx.tcx.hir().get_foreign_abi(it.hir_id());
 
-        if !vis.is_internal_abi(abi) {
-            match it.kind {
-                hir::ForeignItemKind::Fn(ref decl, _, _) => {
-                    vis.check_foreign_fn(it.owner_id.def_id, decl);
-                }
-                hir::ForeignItemKind::Static(ref ty, _) => {
-                    vis.check_foreign_static(it.owner_id, ty.span);
-                }
-                hir::ForeignItemKind::Type => (),
+        match it.kind {
+            hir::ForeignItemKind::Fn(ref decl, _, _) if !vis.is_internal_abi(abi) => {
+                vis.check_foreign_fn(it.owner_id.def_id, decl);
+            }
+            hir::ForeignItemKind::Static(ref ty, _) if !vis.is_internal_abi(abi) => {
+                vis.check_foreign_static(it.owner_id, ty.span);
             }
+            hir::ForeignItemKind::Fn(ref decl, _, _) => vis.check_fn(it.owner_id.def_id, decl),
+            hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => (),
+        }
+    }
+}
+
+impl ImproperCTypesDefinitions {
+    fn check_ty_maybe_containing_foreign_fnptr<'tcx>(
+        &mut self,
+        cx: &LateContext<'tcx>,
+        hir_ty: &'tcx hir::Ty<'_>,
+        ty: Ty<'tcx>,
+    ) {
+        let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Definition };
+        for (fn_ptr_ty, span) in vis.find_fn_ptr_ty_with_external_abi(hir_ty, ty) {
+            vis.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, true, false);
         }
     }
 }
 
+/// `ImproperCTypesDefinitions` checks items outside of foreign items (e.g. stuff that isn't in
+/// `extern "C" { }` blocks):
+///
+/// - `extern "<abi>" fn` definitions are checked in the same way as the
+///   `ImproperCtypesDeclarations` visitor checks functions if `<abi>` is external (e.g. "C").
+/// - All other items which contain types (e.g. other functions, struct definitions, etc) are
+///   checked for extern fn-ptrs with external ABIs.
 impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions {
+    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
+        match item.kind {
+            hir::ItemKind::Static(ty, ..)
+            | hir::ItemKind::Const(ty, ..)
+            | hir::ItemKind::TyAlias(ty, ..) => {
+                self.check_ty_maybe_containing_foreign_fnptr(
+                    cx,
+                    ty,
+                    cx.tcx.type_of(item.owner_id).subst_identity(),
+                );
+            }
+            // See `check_fn`..
+            hir::ItemKind::Fn(..) => {}
+            // See `check_field_def`..
+            hir::ItemKind::Union(..) | hir::ItemKind::Struct(..) | hir::ItemKind::Enum(..) => {}
+            // Doesn't define something that can contain a external type to be checked.
+            hir::ItemKind::Impl(..)
+            | hir::ItemKind::TraitAlias(..)
+            | hir::ItemKind::Trait(..)
+            | hir::ItemKind::OpaqueTy(..)
+            | hir::ItemKind::GlobalAsm(..)
+            | hir::ItemKind::ForeignMod { .. }
+            | hir::ItemKind::Mod(..)
+            | hir::ItemKind::Macro(..)
+            | hir::ItemKind::Use(..)
+            | hir::ItemKind::ExternCrate(..) => {}
+        }
+    }
+
+    fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::FieldDef<'tcx>) {
+        self.check_ty_maybe_containing_foreign_fnptr(
+            cx,
+            field.ty,
+            cx.tcx.type_of(field.def_id).subst_identity(),
+        );
+    }
+
     fn check_fn(
         &mut self,
         cx: &LateContext<'tcx>,
@@ -1444,7 +1568,9 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions {
         };
 
         let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Definition };
-        if !vis.is_internal_abi(abi) {
+        if vis.is_internal_abi(abi) {
+            vis.check_fn(id, decl);
+        } else {
             vis.check_foreign_fn(id, decl);
         }
     }
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index ef82a6c17ee..87c542dc2e2 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -3409,6 +3409,7 @@ declare_lint_pass! {
         UNSTABLE_SYNTAX_PRE_EXPANSION,
         UNSUPPORTED_CALLING_CONVENTIONS,
         UNUSED_ASSIGNMENTS,
+        UNUSED_ASSOCIATED_TYPE_BOUNDS,
         UNUSED_ATTRIBUTES,
         UNUSED_CRATE_DEPENDENCIES,
         UNUSED_EXTERN_CRATES,
@@ -3469,6 +3470,32 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `unused_associated_type_bounds` lint is emitted when an
+    /// associated type bound is added to a trait object, but the associated
+    /// type has a `where Self: Sized` bound, and is thus unavailable on the
+    /// trait object anyway.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// trait Foo {
+    ///     type Bar where Self: Sized;
+    /// }
+    /// type Mop = dyn Foo<Bar = ()>;
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Just like methods with `Self: Sized` bounds are unavailable on trait
+    /// objects, associated types can be removed from the trait object.
+    pub UNUSED_ASSOCIATED_TYPE_BOUNDS,
+    Warn,
+    "detects unused `Foo = Bar` bounds in `dyn Trait<Foo = Bar>`"
+}
+
+declare_lint! {
     /// The `unused_doc_comments` lint detects doc comments that aren't used
     /// by `rustdoc`.
     ///
diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
index 553fe6cf087..bb7510b3a53 100644
--- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
@@ -121,6 +121,32 @@ extern "C" LLVMValueRef LLVMRustGetNamedValue(LLVMModuleRef M, const char *Name,
   return wrap(unwrap(M)->getNamedValue(StringRef(Name, NameLen)));
 }
 
+enum class LLVMRustTailCallKind {
+  None,
+  Tail,
+  MustTail,
+  NoTail,
+};
+
+static CallInst::TailCallKind fromRust(LLVMRustTailCallKind Kind) {
+  switch (Kind) {
+  case LLVMRustTailCallKind::None:
+    return CallInst::TailCallKind::TCK_None;
+  case LLVMRustTailCallKind::Tail:
+    return CallInst::TailCallKind::TCK_Tail;
+  case LLVMRustTailCallKind::MustTail:
+    return CallInst::TailCallKind::TCK_MustTail;
+  case LLVMRustTailCallKind::NoTail:
+    return CallInst::TailCallKind::TCK_NoTail;
+  default:
+    report_fatal_error("bad CallInst::TailCallKind.");
+  }
+}
+
+extern "C" void LLVMRustSetTailCallKind(LLVMValueRef Call, LLVMRustTailCallKind TCK) {
+  unwrap<CallInst>(Call)->setTailCallKind(fromRust(TCK));
+}
+
 extern "C" LLVMValueRef LLVMRustGetOrInsertFunction(LLVMModuleRef M,
                                                     const char *Name,
                                                     size_t NameLen,
diff --git a/compiler/rustc_metadata/messages.ftl b/compiler/rustc_metadata/messages.ftl
index cc58d51befd..13b3dac85d1 100644
--- a/compiler/rustc_metadata/messages.ftl
+++ b/compiler/rustc_metadata/messages.ftl
@@ -25,9 +25,6 @@ metadata_conflicting_alloc_error_handler =
 metadata_conflicting_global_alloc =
     the `#[global_allocator]` in {$other_crate_name} conflicts with global allocator in: {$crate_name}
 
-metadata_consider_adding_std =
-    consider adding the standard library to the sysroot with `x build library --target {$locator_triple}`
-
 metadata_consider_building_std =
     consider building the standard library from source with `cargo build -Zbuild-std`
 
diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs
index b6c82376f4e..fca06c0f47c 100644
--- a/compiler/rustc_metadata/src/errors.rs
+++ b/compiler/rustc_metadata/src/errors.rs
@@ -646,18 +646,12 @@ impl IntoDiagnostic<'_> for CannotFindCrate {
             } else {
                 diag.note(fluent::metadata_target_no_std_support);
             }
-
+            // NOTE: this suggests using rustup, even though the user may not have it installed.
+            // That's because they could choose to install it; or this may give them a hint which
+            // target they need to install from their distro.
             if self.missing_core {
-                if env!("CFG_RELEASE_CHANNEL") == "dev" {
-                    diag.help(fluent::metadata_consider_adding_std);
-                } else {
-                    // NOTE: this suggests using rustup, even though the user may not have it installed.
-                    // That's because they could choose to install it; or this may give them a hint which
-                    // target they need to install from their distro.
-                    diag.help(fluent::metadata_consider_downloading_target);
-                }
+                diag.help(fluent::metadata_consider_downloading_target);
             }
-
             // Suggest using #![no_std]. #[no_core] is unstable and not really supported anyway.
             // NOTE: this is a dummy span if `extern crate std` was injected by the compiler.
             // If it's not a dummy, that means someone added `extern crate std` explicitly and
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index efe49d687c9..541c19c3561 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -1151,10 +1151,10 @@ fn should_encode_type(tcx: TyCtxt<'_>, def_id: LocalDefId, def_kind: DefKind) ->
             let assoc_item = tcx.associated_item(def_id);
             match assoc_item.container {
                 ty::AssocItemContainer::ImplContainer => true,
-                // FIXME(-Zlower-impl-trait-in-trait-to-assoc-ty) always encode RPITITs,
-                // since we need to be able to "project" from an RPITIT associated item
-                // to an opaque when installing the default projection predicates in
-                // default trait methods with RPITITs.
+                // Always encode RPITITs, since we need to be able to project
+                // from an RPITIT associated item to an opaque when installing
+                // the default projection predicates in default trait methods
+                // with RPITITs.
                 ty::AssocItemContainer::TraitContainer => {
                     assoc_item.defaultness(tcx).has_value() || assoc_item.opt_rpitit_info.is_some()
                 }
diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml
index 07ab3ba4e8a..4c238308fe8 100644
--- a/compiler/rustc_middle/Cargo.toml
+++ b/compiler/rustc_middle/Cargo.toml
@@ -7,7 +7,6 @@ edition = "2021"
 
 [dependencies]
 bitflags = "1.2.1"
-chalk-ir = "0.92.0"
 derive_more = "0.99.17"
 either = "1.5.0"
 gsgdt = "0.1.2"
diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs
index 1b19ed9ad14..d5e8330b3f6 100644
--- a/compiler/rustc_middle/src/infer/canonical.rs
+++ b/compiler/rustc_middle/src/infer/canonical.rs
@@ -433,7 +433,8 @@ impl<'tcx> CanonicalVarValues<'tcx> {
                 |(i, info)| -> ty::GenericArg<'tcx> {
                     match info.kind {
                         CanonicalVarKind::Ty(_) | CanonicalVarKind::PlaceholderTy(_) => {
-                            tcx.mk_bound(ty::INNERMOST, ty::BoundVar::from_usize(i).into()).into()
+                            Ty::new_bound(tcx, ty::INNERMOST, ty::BoundVar::from_usize(i).into())
+                                .into()
                         }
                         CanonicalVarKind::Region(_) | CanonicalVarKind::PlaceholderRegion(_) => {
                             let br = ty::BoundRegion {
@@ -443,12 +444,13 @@ impl<'tcx> CanonicalVarValues<'tcx> {
                             ty::Region::new_late_bound(tcx, ty::INNERMOST, br).into()
                         }
                         CanonicalVarKind::Const(_, ty)
-                        | CanonicalVarKind::PlaceholderConst(_, ty) => tcx
-                            .mk_const(
-                                ty::ConstKind::Bound(ty::INNERMOST, ty::BoundVar::from_usize(i)),
-                                ty,
-                            )
-                            .into(),
+                        | CanonicalVarKind::PlaceholderConst(_, ty) => ty::Const::new_bound(
+                            tcx,
+                            ty::INNERMOST,
+                            ty::BoundVar::from_usize(i),
+                            ty,
+                        )
+                        .into(),
                     }
                 },
             )),
diff --git a/compiler/rustc_middle/src/infer/unify_key.rs b/compiler/rustc_middle/src/infer/unify_key.rs
index 991b9f01985..85fb9214d9d 100644
--- a/compiler/rustc_middle/src/infer/unify_key.rs
+++ b/compiler/rustc_middle/src/infer/unify_key.rs
@@ -90,15 +90,15 @@ impl<'tcx> UnifyValue for UnifiedRegion<'tcx> {
 impl ToType for ty::IntVarValue {
     fn to_type<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
         match *self {
-            ty::IntType(i) => tcx.mk_mach_int(i),
-            ty::UintType(i) => tcx.mk_mach_uint(i),
+            ty::IntType(i) => Ty::new_int(tcx, i),
+            ty::UintType(i) => Ty::new_uint(tcx, i),
         }
     }
 }
 
 impl ToType for ty::FloatVarValue {
     fn to_type<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
-        tcx.mk_mach_float(self.0)
+        Ty::new_float(tcx, self.0)
     }
 }
 
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index ad1c93c31e9..efb01b0faba 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -1913,7 +1913,7 @@ impl<'tcx> Operand<'tcx> {
         substs: impl IntoIterator<Item = GenericArg<'tcx>>,
         span: Span,
     ) -> Self {
-        let ty = tcx.mk_fn_def(def_id, substs);
+        let ty = Ty::new_fn_def(tcx, def_id, substs);
         Operand::Constant(Box::new(Constant {
             span,
             user_ty: None,
@@ -2329,10 +2329,10 @@ impl<'tcx> ConstantKind<'tcx> {
     pub fn eval(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self {
         match self {
             Self::Ty(c) => {
-                if let Some(val) = c.kind().try_eval_for_mir(tcx, param_env) {
+                if let Some(val) = c.try_eval_for_mir(tcx, param_env) {
                     match val {
                         Ok(val) => Self::Val(val, c.ty()),
-                        Err(guar) => Self::Ty(tcx.const_error(self.ty(), guar)),
+                        Err(guar) => Self::Ty(ty::Const::new_error(tcx, guar, self.ty())),
                     }
                 } else {
                     self
@@ -2344,7 +2344,9 @@ impl<'tcx> ConstantKind<'tcx> {
                 match tcx.const_eval_resolve(param_env, uneval, None) {
                     Ok(val) => Self::Val(val, ty),
                     Err(ErrorHandled::TooGeneric) => self,
-                    Err(ErrorHandled::Reported(guar)) => Self::Ty(tcx.const_error(ty, guar.into())),
+                    Err(ErrorHandled::Reported(guar)) => {
+                        Self::Ty(ty::Const::new_error(tcx, guar.into(), ty))
+                    }
                 }
             }
         }
@@ -2510,7 +2512,7 @@ impl<'tcx> ConstantKind<'tcx> {
                 let generics = tcx.generics_of(item_def_id);
                 let index = generics.param_def_id_to_index[&def_id];
                 let name = tcx.item_name(def_id);
-                let ty_const = tcx.mk_const(ty::ParamConst::new(index, name), ty);
+                let ty_const = ty::Const::new_param(tcx, ty::ParamConst::new(index, name), ty);
                 debug!(?ty_const);
 
                 return Self::Ty(ty_const);
@@ -2774,7 +2776,7 @@ impl<'tcx> Display for ConstantKind<'tcx> {
     fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
         match *self {
             ConstantKind::Ty(c) => pretty_print_const(c, fmt, true),
-            ConstantKind::Val(val, ty) => pretty_print_const_value(val, ty, fmt, true),
+            ConstantKind::Val(val, ty) => pretty_print_const_value(val, ty, fmt),
             // FIXME(valtrees): Correctly print mir constants.
             ConstantKind::Unevaluated(..) => {
                 fmt.write_str("_")?;
@@ -2804,13 +2806,16 @@ fn pretty_print_byte_str(fmt: &mut Formatter<'_>, byte_str: &[u8]) -> fmt::Resul
     write!(fmt, "b\"{}\"", byte_str.escape_ascii())
 }
 
-fn comma_sep<'tcx>(fmt: &mut Formatter<'_>, elems: Vec<ConstantKind<'tcx>>) -> fmt::Result {
+fn comma_sep<'tcx>(
+    fmt: &mut Formatter<'_>,
+    elems: Vec<(ConstValue<'tcx>, Ty<'tcx>)>,
+) -> fmt::Result {
     let mut first = true;
-    for elem in elems {
+    for (ct, ty) in elems {
         if !first {
             fmt.write_str(", ")?;
         }
-        fmt.write_str(&format!("{}", elem))?;
+        pretty_print_const_value(ct, ty, fmt)?;
         first = false;
     }
     Ok(())
@@ -2821,7 +2826,6 @@ fn pretty_print_const_value<'tcx>(
     ct: ConstValue<'tcx>,
     ty: Ty<'tcx>,
     fmt: &mut Formatter<'_>,
-    print_ty: bool,
 ) -> fmt::Result {
     use crate::ty::print::PrettyPrinter;
 
@@ -2865,7 +2869,7 @@ fn pretty_print_const_value<'tcx>(
                 }
             }
             (ConstValue::ByRef { alloc, offset }, ty::Array(t, n)) if *t == u8_type => {
-                let n = n.kind().try_to_bits(tcx.data_layout.pointer_size).unwrap();
+                let n = n.try_to_bits(tcx.data_layout.pointer_size).unwrap();
                 // cast is ok because we already checked for pointer size (32 or 64 bit) above
                 let range = AllocRange { start: offset, size: Size::from_bytes(n) };
                 let byte_str = alloc.inner().get_bytes_strip_provenance(&tcx, range).unwrap();
@@ -2880,16 +2884,11 @@ fn pretty_print_const_value<'tcx>(
             // introducing ICEs (e.g. via `layout_of`) from missing bounds.
             // E.g. `transmute([0usize; 2]): (u8, *mut T)` needs to know `T: Sized`
             // to be able to destructure the tuple into `(0u8, *mut T)`
-            //
-            // FIXME(eddyb) for `--emit=mir`/`-Z dump-mir`, we should provide the
-            // correct `ty::ParamEnv` to allow printing *all* constant values.
             (_, ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) if !ty.has_non_region_param() => {
                 let ct = tcx.lift(ct).unwrap();
                 let ty = tcx.lift(ty).unwrap();
-                if let Some(contents) = tcx.try_destructure_mir_constant(
-                    ty::ParamEnv::reveal_all().and(ConstantKind::Val(ct, ty)),
-                ) {
-                    let fields = contents.fields.to_vec();
+                if let Some(contents) = tcx.try_destructure_mir_constant_for_diagnostics((ct, ty)) {
+                    let fields: Vec<(ConstValue<'_>, Ty<'_>)> = contents.fields.to_vec();
                     match *ty.kind() {
                         ty::Array(..) => {
                             fmt.write_str("[")?;
@@ -2928,12 +2927,14 @@ fn pretty_print_const_value<'tcx>(
                                 None => {
                                     fmt.write_str(" {{ ")?;
                                     let mut first = true;
-                                    for (field_def, field) in iter::zip(&variant_def.fields, fields)
+                                    for (field_def, (ct, ty)) in
+                                        iter::zip(&variant_def.fields, fields)
                                     {
                                         if !first {
                                             fmt.write_str(", ")?;
                                         }
-                                        fmt.write_str(&format!("{}: {}", field_def.name, field))?;
+                                        write!(fmt, "{}: ", field_def.name)?;
+                                        pretty_print_const_value(ct, ty, fmt)?;
                                         first = false;
                                     }
                                     fmt.write_str(" }}")?;
@@ -2943,20 +2944,13 @@ fn pretty_print_const_value<'tcx>(
                         _ => unreachable!(),
                     }
                     return Ok(());
-                } else {
-                    // Fall back to debug pretty printing for invalid constants.
-                    fmt.write_str(&format!("{:?}", ct))?;
-                    if print_ty {
-                        fmt.write_str(&format!(": {}", ty))?;
-                    }
-                    return Ok(());
-                };
+                }
             }
             (ConstValue::Scalar(scalar), _) => {
                 let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
                 cx.print_alloc_ids = true;
                 let ty = tcx.lift(ty).unwrap();
-                cx = cx.pretty_print_const_scalar(scalar, ty, print_ty)?;
+                cx = cx.pretty_print_const_scalar(scalar, ty)?;
                 fmt.write_str(&cx.into_buffer())?;
                 return Ok(());
             }
@@ -2971,12 +2965,8 @@ fn pretty_print_const_value<'tcx>(
             // their fields instead of just dumping the memory.
             _ => {}
         }
-        // fallback
-        fmt.write_str(&format!("{:?}", ct))?;
-        if print_ty {
-            fmt.write_str(&format!(": {}", ty))?;
-        }
-        Ok(())
+        // Fall back to debug pretty printing for invalid constants.
+        write!(fmt, "{ct:?}: {ty}")
     })
 }
 
diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs
index 13a1011e328..613b132ff2d 100644
--- a/compiler/rustc_middle/src/mir/query.rs
+++ b/compiler/rustc_middle/src/mir/query.rs
@@ -1,6 +1,6 @@
 //! Values computed by queries that use MIR.
 
-use crate::mir::ConstantKind;
+use crate::mir::interpret::ConstValue;
 use crate::ty::{self, OpaqueHiddenType, Ty, TyCtxt};
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_data_structures::unord::UnordSet;
@@ -444,7 +444,7 @@ impl<'tcx> ClosureOutlivesSubjectTy<'tcx> {
 #[derive(Copy, Clone, Debug, HashStable)]
 pub struct DestructuredConstant<'tcx> {
     pub variant: Option<VariantIdx>,
-    pub fields: &'tcx [ConstantKind<'tcx>],
+    pub fields: &'tcx [(ConstValue<'tcx>, Ty<'tcx>)],
 }
 
 /// Coverage information summarized from a MIR if instrumented for source code coverage (see
diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs
index 65dff193c80..8618a531527 100644
--- a/compiler/rustc_middle/src/mir/tcx.rs
+++ b/compiler/rustc_middle/src/mir/tcx.rs
@@ -95,11 +95,13 @@ impl<'tcx> PlaceTy<'tcx> {
             ProjectionElem::Subslice { from, to, from_end } => {
                 PlaceTy::from_ty(match self.ty.kind() {
                     ty::Slice(..) => self.ty,
-                    ty::Array(inner, _) if !from_end => tcx.mk_array(*inner, (to - from) as u64),
+                    ty::Array(inner, _) if !from_end => {
+                        Ty::new_array(tcx, *inner, (to - from) as u64)
+                    }
                     ty::Array(inner, size) if from_end => {
                         let size = size.eval_target_usize(tcx, param_env);
                         let len = size - from - to;
-                        tcx.mk_array(*inner, len)
+                        Ty::new_array(tcx, *inner, len)
                     }
                     _ => bug!("cannot subslice non-array type: `{:?}`", self),
                 })
@@ -162,16 +164,16 @@ impl<'tcx> Rvalue<'tcx> {
         match *self {
             Rvalue::Use(ref operand) => operand.ty(local_decls, tcx),
             Rvalue::Repeat(ref operand, count) => {
-                tcx.mk_array_with_const_len(operand.ty(local_decls, tcx), count)
+                Ty::new_array_with_const_len(tcx, operand.ty(local_decls, tcx), count)
             }
             Rvalue::ThreadLocalRef(did) => tcx.thread_local_ptr_ty(did),
             Rvalue::Ref(reg, bk, ref place) => {
                 let place_ty = place.ty(local_decls, tcx).ty;
-                tcx.mk_ref(reg, ty::TypeAndMut { ty: place_ty, mutbl: bk.to_mutbl_lossy() })
+                Ty::new_ref(tcx, reg, ty::TypeAndMut { ty: place_ty, mutbl: bk.to_mutbl_lossy() })
             }
             Rvalue::AddressOf(mutability, ref place) => {
                 let place_ty = place.ty(local_decls, tcx).ty;
-                tcx.mk_ptr(ty::TypeAndMut { ty: place_ty, mutbl: mutability })
+                Ty::new_ptr(tcx, ty::TypeAndMut { ty: place_ty, mutbl: mutability })
             }
             Rvalue::Len(..) => tcx.types.usize,
             Rvalue::Cast(.., ty) => ty,
@@ -184,7 +186,7 @@ impl<'tcx> Rvalue<'tcx> {
                 let lhs_ty = lhs.ty(local_decls, tcx);
                 let rhs_ty = rhs.ty(local_decls, tcx);
                 let ty = op.ty(tcx, lhs_ty, rhs_ty);
-                tcx.mk_tup(&[ty, tcx.types.bool])
+                Ty::new_tup(tcx, &[ty, tcx.types.bool])
             }
             Rvalue::UnaryOp(UnOp::Not | UnOp::Neg, ref operand) => operand.ty(local_decls, tcx),
             Rvalue::Discriminant(ref place) => place.ty(local_decls, tcx).ty.discriminant_ty(tcx),
@@ -192,17 +194,17 @@ impl<'tcx> Rvalue<'tcx> {
                 tcx.types.usize
             }
             Rvalue::Aggregate(ref ak, ref ops) => match **ak {
-                AggregateKind::Array(ty) => tcx.mk_array(ty, ops.len() as u64),
+                AggregateKind::Array(ty) => Ty::new_array(tcx, ty, ops.len() as u64),
                 AggregateKind::Tuple => {
-                    tcx.mk_tup_from_iter(ops.iter().map(|op| op.ty(local_decls, tcx)))
+                    Ty::new_tup_from_iter(tcx, ops.iter().map(|op| op.ty(local_decls, tcx)))
                 }
                 AggregateKind::Adt(did, _, substs, _, _) => tcx.type_of(did).subst(tcx, substs),
-                AggregateKind::Closure(did, substs) => tcx.mk_closure(did, substs),
+                AggregateKind::Closure(did, substs) => Ty::new_closure(tcx, did, substs),
                 AggregateKind::Generator(did, substs, movability) => {
-                    tcx.mk_generator(did, substs, movability)
+                    Ty::new_generator(tcx, did, substs, movability)
                 }
             },
-            Rvalue::ShallowInitBox(_, ty) => tcx.mk_box(ty),
+            Rvalue::ShallowInitBox(_, ty) => Ty::new_box(tcx, ty),
             Rvalue::CopyForDeref(ref place) => place.ty(local_decls, tcx).ty,
         }
     }
diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs
index e8f4fca147b..2c481745d98 100644
--- a/compiler/rustc_middle/src/query/erase.rs
+++ b/compiler/rustc_middle/src/query/erase.rs
@@ -315,7 +315,6 @@ tcx_lifetime! {
     rustc_middle::mir::interpret::ConstValue,
     rustc_middle::mir::interpret::GlobalId,
     rustc_middle::mir::interpret::LitToConstInput,
-    rustc_middle::traits::ChalkEnvironmentAndGoal,
     rustc_middle::traits::query::MethodAutoderefStepsResult,
     rustc_middle::traits::query::type_op::AscribeUserType,
     rustc_middle::traits::query::type_op::Eq,
diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs
index 4006a6cd1cc..0119e07a44e 100644
--- a/compiler/rustc_middle/src/query/keys.rs
+++ b/compiler/rustc_middle/src/query/keys.rs
@@ -2,6 +2,7 @@
 
 use crate::infer::canonical::Canonical;
 use crate::mir;
+use crate::mir::interpret::ConstValue;
 use crate::traits;
 use crate::ty::fast_reject::SimplifiedType;
 use crate::ty::layout::{TyAndLayout, ValidityRequirement};
@@ -333,6 +334,14 @@ impl<'tcx> Key for (ty::Const<'tcx>, FieldIdx) {
     }
 }
 
+impl<'tcx> Key for (ConstValue<'tcx>, Ty<'tcx>) {
+    type CacheSelector = DefaultCacheSelector<Self>;
+
+    fn default_span(&self, _: TyCtxt<'_>) -> Span {
+        DUMMY_SP
+    }
+}
+
 impl<'tcx> Key for mir::interpret::ConstAlloc<'tcx> {
     type CacheSelector = DefaultCacheSelector<Self>;
 
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index c12587845e5..d58a1a65533 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -39,8 +39,8 @@ use crate::traits::query::{
 };
 use crate::traits::specialization_graph;
 use crate::traits::{
-    CanonicalChalkEnvironmentAndGoal, CodegenObligationError, EvaluationResult, ImplSource,
-    ObjectSafetyViolation, ObligationCause, OverflowError, WellFormedLoc,
+    CodegenObligationError, EvaluationResult, ImplSource, ObjectSafetyViolation, ObligationCause,
+    OverflowError, WellFormedLoc,
 };
 use crate::ty::fast_reject::SimplifiedType;
 use crate::ty::layout::ValidityRequirement;
@@ -881,7 +881,7 @@ rustc_queries! {
     ///
     /// Note that we've liberated the late bound regions of function signatures, so
     /// this can not be used to check whether these types are well formed.
-    query assumed_wf_types(key: DefId) -> &'tcx ty::List<Ty<'tcx>> {
+    query assumed_wf_types(key: LocalDefId) -> &'tcx [(Ty<'tcx>, Span)] {
         desc { |tcx| "computing the implied bounds of `{}`", tcx.def_path_str(key) }
     }
 
@@ -1087,11 +1087,13 @@ rustc_queries! {
     }
 
     /// Tries to destructure an `mir::ConstantKind` ADT or array into its variant index
-    /// and its field values.
-    query try_destructure_mir_constant(
-        key: ty::ParamEnvAnd<'tcx, mir::ConstantKind<'tcx>>
+    /// and its field values. This should only be used for pretty printing.
+    query try_destructure_mir_constant_for_diagnostics(
+        key: (ConstValue<'tcx>, Ty<'tcx>)
     ) -> Option<mir::DestructuredConstant<'tcx>> {
         desc { "destructuring MIR constant"}
+        no_hash
+        eval_always
     }
 
     query const_caller_location(key: (rustc_span::Symbol, u32, u32)) -> ConstValue<'tcx> {
@@ -1971,15 +1973,6 @@ rustc_queries! {
         desc { "evaluating trait selection obligation `{}`", goal.value.value }
     }
 
-    query evaluate_goal(
-        goal: CanonicalChalkEnvironmentAndGoal<'tcx>
-    ) -> Result<
-        &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>,
-        NoSolution
-    > {
-        desc { "evaluating trait selection obligation `{}`", goal.value }
-    }
-
     /// Do not call this query directly: part of the `Eq` type-op
     query type_op_ascribe_user_type(
         goal: CanonicalTypeOpAscribeUserTypeGoal<'tcx>
@@ -2206,6 +2199,10 @@ rustc_queries! {
         desc { "getting cfg-ed out item names" }
         separate_provide_extern
     }
+
+    query generics_require_sized_self(def_id: DefId) -> bool {
+        desc { "check whether the item has a `where Self: Sized` bound" }
+    }
 }
 
 rustc_query_append! { define_callbacks! }
diff --git a/compiler/rustc_middle/src/traits/chalk.rs b/compiler/rustc_middle/src/traits/chalk.rs
deleted file mode 100644
index 6e3d2d91ae9..00000000000
--- a/compiler/rustc_middle/src/traits/chalk.rs
+++ /dev/null
@@ -1,396 +0,0 @@
-//! Types required for Chalk-related queries
-//!
-//! The primary purpose of this file is defining an implementation for the
-//! `chalk_ir::interner::Interner` trait. The primary purpose of this trait, as
-//! its name suggest, is to provide an abstraction boundary for creating
-//! interned Chalk types.
-
-use rustc_middle::ty::{self, AdtDef, TyCtxt};
-
-use rustc_hir::def_id::DefId;
-use rustc_target::spec::abi::Abi;
-
-use std::cmp::Ordering;
-use std::fmt;
-use std::hash::{Hash, Hasher};
-
-#[derive(Copy, Clone)]
-pub struct RustInterner<'tcx> {
-    pub tcx: TyCtxt<'tcx>,
-}
-
-/// We don't ever actually need this. It's only required for derives.
-impl<'tcx> Hash for RustInterner<'tcx> {
-    fn hash<H: Hasher>(&self, _state: &mut H) {}
-}
-
-/// We don't ever actually need this. It's only required for derives.
-impl<'tcx> Ord for RustInterner<'tcx> {
-    fn cmp(&self, _other: &Self) -> Ordering {
-        Ordering::Equal
-    }
-}
-
-/// We don't ever actually need this. It's only required for derives.
-impl<'tcx> PartialOrd for RustInterner<'tcx> {
-    fn partial_cmp(&self, _other: &Self) -> Option<Ordering> {
-        None
-    }
-}
-
-/// We don't ever actually need this. It's only required for derives.
-impl<'tcx> PartialEq for RustInterner<'tcx> {
-    fn eq(&self, _other: &Self) -> bool {
-        false
-    }
-}
-
-/// We don't ever actually need this. It's only required for derives.
-impl<'tcx> Eq for RustInterner<'tcx> {}
-
-impl fmt::Debug for RustInterner<'_> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "RustInterner")
-    }
-}
-
-// Right now, there is no interning at all. I was running into problems with
-// adding interning in `ty/context.rs` for Chalk types with
-// `parallel-compiler = true`. -jackh726
-impl<'tcx> chalk_ir::interner::Interner for RustInterner<'tcx> {
-    type InternedType = Box<chalk_ir::TyData<Self>>;
-    type InternedLifetime = Box<chalk_ir::LifetimeData<Self>>;
-    type InternedConst = Box<chalk_ir::ConstData<Self>>;
-    type InternedConcreteConst = ty::ValTree<'tcx>;
-    type InternedGenericArg = Box<chalk_ir::GenericArgData<Self>>;
-    type InternedGoal = Box<chalk_ir::GoalData<Self>>;
-    type InternedGoals = Vec<chalk_ir::Goal<Self>>;
-    type InternedSubstitution = Vec<chalk_ir::GenericArg<Self>>;
-    type InternedProgramClause = Box<chalk_ir::ProgramClauseData<Self>>;
-    type InternedProgramClauses = Vec<chalk_ir::ProgramClause<Self>>;
-    type InternedQuantifiedWhereClauses = Vec<chalk_ir::QuantifiedWhereClause<Self>>;
-    type InternedVariableKinds = Vec<chalk_ir::VariableKind<Self>>;
-    type InternedCanonicalVarKinds = Vec<chalk_ir::CanonicalVarKind<Self>>;
-    type InternedVariances = Vec<chalk_ir::Variance>;
-    type InternedConstraints = Vec<chalk_ir::InEnvironment<chalk_ir::Constraint<Self>>>;
-    type DefId = DefId;
-    type InternedAdtId = AdtDef<'tcx>;
-    type Identifier = ();
-    type FnAbi = Abi;
-
-    fn debug_program_clause_implication(
-        pci: &chalk_ir::ProgramClauseImplication<Self>,
-        fmt: &mut fmt::Formatter<'_>,
-    ) -> Option<fmt::Result> {
-        let mut write = || {
-            write!(fmt, "{:?}", pci.consequence)?;
-
-            let conditions = pci.conditions.interned();
-            let constraints = pci.constraints.interned();
-
-            let conds = conditions.len();
-            let consts = constraints.len();
-            if conds == 0 && consts == 0 {
-                return Ok(());
-            }
-
-            write!(fmt, " :- ")?;
-
-            if conds != 0 {
-                for cond in &conditions[..conds - 1] {
-                    write!(fmt, "{:?}, ", cond)?;
-                }
-                write!(fmt, "{:?}", conditions[conds - 1])?;
-            }
-
-            if conds != 0 && consts != 0 {
-                write!(fmt, " ; ")?;
-            }
-
-            if consts != 0 {
-                for constraint in &constraints[..consts - 1] {
-                    write!(fmt, "{:?}, ", constraint)?;
-                }
-                write!(fmt, "{:?}", constraints[consts - 1])?;
-            }
-
-            Ok(())
-        };
-        Some(write())
-    }
-
-    fn debug_substitution(
-        substitution: &chalk_ir::Substitution<Self>,
-        fmt: &mut fmt::Formatter<'_>,
-    ) -> Option<fmt::Result> {
-        Some(write!(fmt, "{:?}", substitution.interned()))
-    }
-
-    fn debug_separator_trait_ref(
-        separator_trait_ref: &chalk_ir::SeparatorTraitRef<'_, Self>,
-        fmt: &mut fmt::Formatter<'_>,
-    ) -> Option<fmt::Result> {
-        let substitution = &separator_trait_ref.trait_ref.substitution;
-        let parameters = substitution.interned();
-        Some(write!(
-            fmt,
-            "{:?}{}{:?}{:?}",
-            parameters[0],
-            separator_trait_ref.separator,
-            separator_trait_ref.trait_ref.trait_id,
-            chalk_ir::debug::Angle(&parameters[1..])
-        ))
-    }
-
-    fn debug_quantified_where_clauses(
-        clauses: &chalk_ir::QuantifiedWhereClauses<Self>,
-        fmt: &mut fmt::Formatter<'_>,
-    ) -> Option<fmt::Result> {
-        Some(write!(fmt, "{:?}", clauses.interned()))
-    }
-
-    fn debug_ty(ty: &chalk_ir::Ty<Self>, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
-        match &ty.interned().kind {
-            chalk_ir::TyKind::Ref(chalk_ir::Mutability::Not, lifetime, ty) => {
-                Some(write!(fmt, "(&{:?} {:?})", lifetime, ty))
-            }
-            chalk_ir::TyKind::Ref(chalk_ir::Mutability::Mut, lifetime, ty) => {
-                Some(write!(fmt, "(&{:?} mut {:?})", lifetime, ty))
-            }
-            chalk_ir::TyKind::Array(ty, len) => Some(write!(fmt, "[{:?}; {:?}]", ty, len)),
-            chalk_ir::TyKind::Slice(ty) => Some(write!(fmt, "[{:?}]", ty)),
-            chalk_ir::TyKind::Tuple(len, substs) => Some(
-                try {
-                    write!(fmt, "(")?;
-                    for (idx, substitution) in substs.interned().iter().enumerate() {
-                        if idx == *len && *len != 1 {
-                            // Don't add a trailing comma if the tuple has more than one element
-                            write!(fmt, "{:?}", substitution)?;
-                        } else {
-                            write!(fmt, "{:?},", substitution)?;
-                        }
-                    }
-                    write!(fmt, ")")?;
-                },
-            ),
-            _ => None,
-        }
-    }
-
-    fn debug_alias(
-        alias_ty: &chalk_ir::AliasTy<Self>,
-        fmt: &mut fmt::Formatter<'_>,
-    ) -> Option<fmt::Result> {
-        match alias_ty {
-            chalk_ir::AliasTy::Projection(projection_ty) => {
-                Self::debug_projection_ty(projection_ty, fmt)
-            }
-            chalk_ir::AliasTy::Opaque(opaque_ty) => Self::debug_opaque_ty(opaque_ty, fmt),
-        }
-    }
-
-    fn debug_projection_ty(
-        projection_ty: &chalk_ir::ProjectionTy<Self>,
-        fmt: &mut fmt::Formatter<'_>,
-    ) -> Option<fmt::Result> {
-        Some(write!(
-            fmt,
-            "projection: {:?} {:?}",
-            projection_ty.associated_ty_id, projection_ty.substitution,
-        ))
-    }
-
-    fn debug_opaque_ty(
-        opaque_ty: &chalk_ir::OpaqueTy<Self>,
-        fmt: &mut fmt::Formatter<'_>,
-    ) -> Option<fmt::Result> {
-        Some(write!(fmt, "{:?}", opaque_ty.opaque_ty_id))
-    }
-
-    fn intern_ty(self, ty: chalk_ir::TyKind<Self>) -> Self::InternedType {
-        let flags = ty.compute_flags(self);
-        Box::new(chalk_ir::TyData { kind: ty, flags: flags })
-    }
-
-    fn ty_data(self, ty: &Self::InternedType) -> &chalk_ir::TyData<Self> {
-        ty
-    }
-
-    fn intern_lifetime(self, lifetime: chalk_ir::LifetimeData<Self>) -> Self::InternedLifetime {
-        Box::new(lifetime)
-    }
-
-    fn lifetime_data(self, lifetime: &Self::InternedLifetime) -> &chalk_ir::LifetimeData<Self> {
-        &lifetime
-    }
-
-    fn intern_const(self, constant: chalk_ir::ConstData<Self>) -> Self::InternedConst {
-        Box::new(constant)
-    }
-
-    fn const_data(self, constant: &Self::InternedConst) -> &chalk_ir::ConstData<Self> {
-        &constant
-    }
-
-    fn const_eq(
-        self,
-        _ty: &Self::InternedType,
-        c1: &Self::InternedConcreteConst,
-        c2: &Self::InternedConcreteConst,
-    ) -> bool {
-        c1 == c2
-    }
-
-    fn intern_generic_arg(self, data: chalk_ir::GenericArgData<Self>) -> Self::InternedGenericArg {
-        Box::new(data)
-    }
-
-    fn generic_arg_data(self, data: &Self::InternedGenericArg) -> &chalk_ir::GenericArgData<Self> {
-        &data
-    }
-
-    fn intern_goal(self, goal: chalk_ir::GoalData<Self>) -> Self::InternedGoal {
-        Box::new(goal)
-    }
-
-    fn goal_data(self, goal: &Self::InternedGoal) -> &chalk_ir::GoalData<Self> {
-        &goal
-    }
-
-    fn intern_goals<E>(
-        self,
-        data: impl IntoIterator<Item = Result<chalk_ir::Goal<Self>, E>>,
-    ) -> Result<Self::InternedGoals, E> {
-        data.into_iter().collect::<Result<Vec<_>, _>>()
-    }
-
-    fn goals_data(self, goals: &Self::InternedGoals) -> &[chalk_ir::Goal<Self>] {
-        goals
-    }
-
-    fn intern_substitution<E>(
-        self,
-        data: impl IntoIterator<Item = Result<chalk_ir::GenericArg<Self>, E>>,
-    ) -> Result<Self::InternedSubstitution, E> {
-        data.into_iter().collect::<Result<Vec<_>, _>>()
-    }
-
-    fn substitution_data(
-        self,
-        substitution: &Self::InternedSubstitution,
-    ) -> &[chalk_ir::GenericArg<Self>] {
-        substitution
-    }
-
-    fn intern_program_clause(
-        self,
-        data: chalk_ir::ProgramClauseData<Self>,
-    ) -> Self::InternedProgramClause {
-        Box::new(data)
-    }
-
-    fn program_clause_data(
-        self,
-        clause: &Self::InternedProgramClause,
-    ) -> &chalk_ir::ProgramClauseData<Self> {
-        &clause
-    }
-
-    fn intern_program_clauses<E>(
-        self,
-        data: impl IntoIterator<Item = Result<chalk_ir::ProgramClause<Self>, E>>,
-    ) -> Result<Self::InternedProgramClauses, E> {
-        data.into_iter().collect::<Result<Vec<_>, _>>()
-    }
-
-    fn program_clauses_data(
-        self,
-        clauses: &Self::InternedProgramClauses,
-    ) -> &[chalk_ir::ProgramClause<Self>] {
-        clauses
-    }
-
-    fn intern_quantified_where_clauses<E>(
-        self,
-        data: impl IntoIterator<Item = Result<chalk_ir::QuantifiedWhereClause<Self>, E>>,
-    ) -> Result<Self::InternedQuantifiedWhereClauses, E> {
-        data.into_iter().collect::<Result<Vec<_>, _>>()
-    }
-
-    fn quantified_where_clauses_data(
-        self,
-        clauses: &Self::InternedQuantifiedWhereClauses,
-    ) -> &[chalk_ir::QuantifiedWhereClause<Self>] {
-        clauses
-    }
-
-    fn intern_generic_arg_kinds<E>(
-        self,
-        data: impl IntoIterator<Item = Result<chalk_ir::VariableKind<Self>, E>>,
-    ) -> Result<Self::InternedVariableKinds, E> {
-        data.into_iter().collect::<Result<Vec<_>, _>>()
-    }
-
-    fn variable_kinds_data(
-        self,
-        parameter_kinds: &Self::InternedVariableKinds,
-    ) -> &[chalk_ir::VariableKind<Self>] {
-        parameter_kinds
-    }
-
-    fn intern_canonical_var_kinds<E>(
-        self,
-        data: impl IntoIterator<Item = Result<chalk_ir::CanonicalVarKind<Self>, E>>,
-    ) -> Result<Self::InternedCanonicalVarKinds, E> {
-        data.into_iter().collect::<Result<Vec<_>, _>>()
-    }
-
-    fn canonical_var_kinds_data(
-        self,
-        canonical_var_kinds: &Self::InternedCanonicalVarKinds,
-    ) -> &[chalk_ir::CanonicalVarKind<Self>] {
-        canonical_var_kinds
-    }
-
-    fn intern_constraints<E>(
-        self,
-        data: impl IntoIterator<Item = Result<chalk_ir::InEnvironment<chalk_ir::Constraint<Self>>, E>>,
-    ) -> Result<Self::InternedConstraints, E> {
-        data.into_iter().collect::<Result<Vec<_>, _>>()
-    }
-
-    fn constraints_data(
-        self,
-        constraints: &Self::InternedConstraints,
-    ) -> &[chalk_ir::InEnvironment<chalk_ir::Constraint<Self>>] {
-        constraints
-    }
-
-    fn intern_variances<E>(
-        self,
-        data: impl IntoIterator<Item = Result<chalk_ir::Variance, E>>,
-    ) -> Result<Self::InternedVariances, E> {
-        data.into_iter().collect::<Result<Vec<_>, _>>()
-    }
-
-    fn variances_data(self, variances: &Self::InternedVariances) -> &[chalk_ir::Variance] {
-        variances
-    }
-}
-
-impl<'tcx> chalk_ir::interner::HasInterner for RustInterner<'tcx> {
-    type Interner = Self;
-}
-
-/// A chalk environment and goal.
-#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable, TypeFoldable, TypeVisitable)]
-pub struct ChalkEnvironmentAndGoal<'tcx> {
-    pub environment: &'tcx ty::List<ty::Clause<'tcx>>,
-    pub goal: ty::Predicate<'tcx>,
-}
-
-impl<'tcx> fmt::Display for ChalkEnvironmentAndGoal<'tcx> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "environment: {:?}, goal: {}", self.environment, self.goal)
-    }
-}
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index a333fbbb506..c7d2e4c22d2 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -2,7 +2,6 @@
 //!
 //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html
 
-mod chalk;
 pub mod query;
 pub mod select;
 pub mod solve;
@@ -30,12 +29,8 @@ use std::hash::{Hash, Hasher};
 
 pub use self::select::{EvaluationCache, EvaluationResult, OverflowError, SelectionCache};
 
-pub type CanonicalChalkEnvironmentAndGoal<'tcx> = Canonical<'tcx, ChalkEnvironmentAndGoal<'tcx>>;
-
 pub use self::ObligationCauseCode::*;
 
-pub use self::chalk::{ChalkEnvironmentAndGoal, RustInterner as ChalkRustInterner};
-
 /// Depending on the stage of compilation, we want projection to be
 /// more or less conservative.
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, HashStable, Encodable, Decodable)]
@@ -593,6 +588,10 @@ pub enum SelectionError<'tcx> {
     /// Signaling that an error has already been emitted, to avoid
     /// multiple errors being shown.
     ErrorReporting,
+    /// Computing an opaque type's hidden type caused an error (e.g. a cycle error).
+    /// We can thus not know whether the hidden type implements an auto trait, so
+    /// we should not presume anything about it.
+    OpaqueTypeAutoTraitLeakageUnknown(DefId),
 }
 
 #[derive(Clone, Debug, TypeVisitable, Lift)]
diff --git a/compiler/rustc_middle/src/ty/_match.rs b/compiler/rustc_middle/src/ty/_match.rs
index cbc68fde9d9..09517200b0d 100644
--- a/compiler/rustc_middle/src/ty/_match.rs
+++ b/compiler/rustc_middle/src/ty/_match.rs
@@ -81,7 +81,7 @@ impl<'tcx> TypeRelation<'tcx> for Match<'tcx> {
                 Err(TypeError::Sorts(relate::expected_found(self, a, b)))
             }
 
-            (&ty::Error(guar), _) | (_, &ty::Error(guar)) => Ok(self.tcx().ty_error(guar)),
+            (&ty::Error(guar), _) | (_, &ty::Error(guar)) => Ok(Ty::new_error(self.tcx(), guar)),
 
             _ => relate::structurally_relate_tys(self, a, b),
         }
diff --git a/compiler/rustc_middle/src/ty/abstract_const.rs b/compiler/rustc_middle/src/ty/abstract_const.rs
index a39631da936..ffee7ba28c3 100644
--- a/compiler/rustc_middle/src/ty/abstract_const.rs
+++ b/compiler/rustc_middle/src/ty/abstract_const.rs
@@ -53,7 +53,7 @@ impl<'tcx> TyCtxt<'tcx> {
             fn fold_const(&mut self, c: Const<'tcx>) -> Const<'tcx> {
                 let ct = match c.kind() {
                     ty::ConstKind::Unevaluated(uv) => match self.tcx.thir_abstract_const(uv.def) {
-                        Err(e) => self.tcx.const_error(c.ty(), e),
+                        Err(e) => ty::Const::new_error(self.tcx, e, c.ty()),
                         Ok(Some(bac)) => {
                             let substs = self.tcx.erase_regions(uv.substs);
                             let bac = bac.subst(self.tcx, substs);
diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs
index cd0f7e8daf1..a4893684bc8 100644
--- a/compiler/rustc_middle/src/ty/adjustment.rs
+++ b/compiler/rustc_middle/src/ty/adjustment.rs
@@ -132,7 +132,7 @@ impl<'tcx> OverloadedDeref<'tcx> {
             .find(|m| m.kind == ty::AssocKind::Fn)
             .unwrap()
             .def_id;
-        tcx.mk_fn_def(method_def_id, [source])
+        Ty::new_fn_def(tcx, method_def_id, [source])
     }
 }
 
diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs
index 22bed6ad1c5..6adbb44a153 100644
--- a/compiler/rustc_middle/src/ty/codec.rs
+++ b/compiler/rustc_middle/src/ty/codec.rs
@@ -344,7 +344,7 @@ impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D>
 impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> Decodable<D> for ty::Const<'tcx> {
     fn decode(decoder: &mut D) -> Self {
         let consts: ty::ConstData<'tcx> = Decodable::decode(decoder);
-        decoder.interner().mk_const(consts.kind, consts.ty)
+        decoder.interner().mk_ct_from_kind(consts.kind, consts.ty)
     }
 }
 
diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs
index aecb46556b0..1cbfe99f87f 100644
--- a/compiler/rustc_middle/src/ty/consts.rs
+++ b/compiler/rustc_middle/src/ty/consts.rs
@@ -1,7 +1,8 @@
 use crate::middle::resolve_bound_vars as rbv;
-use crate::mir::interpret::LitToConstInput;
-use crate::ty::{self, InternalSubsts, ParamEnv, ParamEnvAnd, Ty, TyCtxt};
+use crate::mir::interpret::{AllocId, ConstValue, LitToConstInput, Scalar};
+use crate::ty::{self, InternalSubsts, ParamEnv, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt};
 use rustc_data_structures::intern::Interned;
+use rustc_error_messages::MultiSpan;
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::LocalDefId;
@@ -13,8 +14,13 @@ mod valtree;
 
 pub use int::*;
 pub use kind::*;
+use rustc_span::ErrorGuaranteed;
+use rustc_span::DUMMY_SP;
+use rustc_target::abi::Size;
 pub use valtree::*;
 
+use super::sty::ConstKind;
+
 /// Use this rather than `ConstData`, whenever possible.
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, HashStable)]
 #[rustc_pass_by_value]
@@ -30,6 +36,16 @@ pub struct ConstData<'tcx> {
 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
 static_assert_size!(ConstData<'_>, 40);
 
+enum EvalMode {
+    Typeck,
+    Mir,
+}
+
+enum EvalResult<'tcx> {
+    ValTree(ty::ValTree<'tcx>),
+    ConstVal(ConstValue<'tcx>),
+}
+
 impl<'tcx> Const<'tcx> {
     #[inline]
     pub fn ty(self) -> Ty<'tcx> {
@@ -38,7 +54,98 @@ impl<'tcx> Const<'tcx> {
 
     #[inline]
     pub fn kind(self) -> ConstKind<'tcx> {
-        self.0.kind
+        self.0.kind.clone()
+    }
+
+    #[inline]
+    pub fn new(tcx: TyCtxt<'tcx>, kind: ty::ConstKind<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> {
+        tcx.mk_ct_from_kind(kind, ty)
+    }
+
+    #[inline]
+    pub fn new_param(tcx: TyCtxt<'tcx>, param: ty::ParamConst, ty: Ty<'tcx>) -> Const<'tcx> {
+        Const::new(tcx, ty::ConstKind::Param(param), ty)
+    }
+
+    #[inline]
+    pub fn new_var(tcx: TyCtxt<'tcx>, infer: ty::ConstVid<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> {
+        Const::new(tcx, ty::ConstKind::Infer(ty::InferConst::Var(infer)), ty)
+    }
+
+    #[inline]
+    pub fn new_fresh(tcx: TyCtxt<'tcx>, fresh: u32, ty: Ty<'tcx>) -> Const<'tcx> {
+        Const::new(tcx, ty::ConstKind::Infer(ty::InferConst::Fresh(fresh)), ty)
+    }
+
+    #[inline]
+    pub fn new_infer(tcx: TyCtxt<'tcx>, infer: ty::InferConst<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> {
+        Const::new(tcx, ty::ConstKind::Infer(infer), ty)
+    }
+
+    #[inline]
+    pub fn new_bound(
+        tcx: TyCtxt<'tcx>,
+        debruijn: ty::DebruijnIndex,
+        var: ty::BoundVar,
+        ty: Ty<'tcx>,
+    ) -> Const<'tcx> {
+        Const::new(tcx, ty::ConstKind::Bound(debruijn, var), ty)
+    }
+
+    #[inline]
+    pub fn new_placeholder(
+        tcx: TyCtxt<'tcx>,
+        placeholder: ty::PlaceholderConst<'tcx>,
+        ty: Ty<'tcx>,
+    ) -> Const<'tcx> {
+        Const::new(tcx, ty::ConstKind::Placeholder(placeholder), ty)
+    }
+
+    #[inline]
+    pub fn new_unevaluated(
+        tcx: TyCtxt<'tcx>,
+        uv: ty::UnevaluatedConst<'tcx>,
+        ty: Ty<'tcx>,
+    ) -> Const<'tcx> {
+        Const::new(tcx, ty::ConstKind::Unevaluated(uv), ty)
+    }
+
+    #[inline]
+    pub fn new_value(tcx: TyCtxt<'tcx>, val: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> {
+        Const::new(tcx, ty::ConstKind::Value(val), ty)
+    }
+
+    #[inline]
+    pub fn new_expr(tcx: TyCtxt<'tcx>, expr: ty::Expr<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> {
+        Const::new(tcx, ty::ConstKind::Expr(expr), ty)
+    }
+
+    #[inline]
+    pub fn new_error(tcx: TyCtxt<'tcx>, e: ty::ErrorGuaranteed, ty: Ty<'tcx>) -> Const<'tcx> {
+        Const::new(tcx, ty::ConstKind::Error(e), ty)
+    }
+
+    /// Like [Ty::new_error] but for constants.
+    #[track_caller]
+    pub fn new_misc_error(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> {
+        Const::new_error_with_message(
+            tcx,
+            ty,
+            DUMMY_SP,
+            "ty::ConstKind::Error constructed but no error reported",
+        )
+    }
+
+    /// Like [Ty::new_error_with_message] but for constants.
+    #[track_caller]
+    pub fn new_error_with_message<S: Into<MultiSpan>>(
+        tcx: TyCtxt<'tcx>,
+        ty: Ty<'tcx>,
+        span: S,
+        msg: &'static str,
+    ) -> Const<'tcx> {
+        let reported = tcx.sess.delay_span_bug(span, msg);
+        Const::new_error(tcx, reported, ty)
     }
 
     /// Literals and const generic parameters are eagerly converted to a constant, everything else
@@ -60,7 +167,8 @@ impl<'tcx> Const<'tcx> {
 
         match Self::try_eval_lit_or_param(tcx, ty, expr) {
             Some(v) => v,
-            None => tcx.mk_const(
+            None => ty::Const::new_unevaluated(
+                tcx,
                 ty::UnevaluatedConst {
                     def: def.to_def_id(),
                     substs: InternalSubsts::identity_for_item(tcx, def.to_def_id()),
@@ -126,13 +234,19 @@ impl<'tcx> Const<'tcx> {
                         let generics = tcx.generics_of(item_def_id);
                         let index = generics.param_def_id_to_index[&def_id];
                         let name = tcx.item_name(def_id);
-                        Some(tcx.mk_const(ty::ParamConst::new(index, name), param_ty))
+                        Some(ty::Const::new_param(tcx, ty::ParamConst::new(index, name), param_ty))
+                    }
+                    Some(rbv::ResolvedArg::LateBound(debruijn, index, _)) => {
+                        Some(ty::Const::new_bound(
+                            tcx,
+                            debruijn,
+                            ty::BoundVar::from_u32(index),
+                            param_ty,
+                        ))
+                    }
+                    Some(rbv::ResolvedArg::Error(guar)) => {
+                        Some(ty::Const::new_error(tcx, guar, param_ty))
                     }
-                    Some(rbv::ResolvedArg::LateBound(debruijn, index, _)) => Some(tcx.mk_const(
-                        ty::ConstKind::Bound(debruijn, ty::BoundVar::from_u32(index)),
-                        param_ty,
-                    )),
-                    Some(rbv::ResolvedArg::Error(guar)) => Some(tcx.const_error(param_ty, guar)),
                     arg => bug!("unexpected bound var resolution for {:?}: {arg:?}", expr.hir_id),
                 }
             }
@@ -155,7 +269,8 @@ impl<'tcx> Const<'tcx> {
             .layout_of(ty)
             .unwrap_or_else(|e| panic!("could not compute layout for {:?}: {:?}", ty, e))
             .size;
-        tcx.mk_const(
+        ty::Const::new_value(
+            tcx,
             ty::ValTree::from_scalar_int(ScalarInt::try_from_uint(bits, size).unwrap()),
             ty.value,
         )
@@ -164,7 +279,7 @@ impl<'tcx> Const<'tcx> {
     #[inline]
     /// Creates an interned zst constant.
     pub fn zero_sized(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Self {
-        tcx.mk_const(ty::ValTree::zst(), ty)
+        ty::Const::new_value(tcx, ty::ValTree::zst(), ty)
     }
 
     #[inline]
@@ -192,12 +307,12 @@ impl<'tcx> Const<'tcx> {
         assert_eq!(self.ty(), ty);
         let size = tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(ty)).ok()?.size;
         // if `ty` does not depend on generic parameters, use an empty param_env
-        self.kind().eval(tcx, param_env).try_to_bits(size)
+        self.eval(tcx, param_env).try_to_bits(size)
     }
 
     #[inline]
     pub fn try_eval_bool(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option<bool> {
-        self.kind().eval(tcx, param_env).try_to_bool()
+        self.eval(tcx, param_env).try_to_bool()
     }
 
     #[inline]
@@ -206,17 +321,17 @@ impl<'tcx> Const<'tcx> {
         tcx: TyCtxt<'tcx>,
         param_env: ParamEnv<'tcx>,
     ) -> Option<u64> {
-        self.kind().eval(tcx, param_env).try_to_target_usize(tcx)
+        self.eval(tcx, param_env).try_to_target_usize(tcx)
     }
 
     #[inline]
     /// Tries to evaluate the constant if it is `Unevaluated`. If that doesn't succeed, return the
     /// unevaluated constant.
     pub fn eval(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Const<'tcx> {
-        if let Some(val) = self.kind().try_eval_for_typeck(tcx, param_env) {
+        if let Some(val) = self.try_eval_for_typeck(tcx, param_env) {
             match val {
-                Ok(val) => tcx.mk_const(val, self.ty()),
-                Err(guar) => tcx.const_error(self.ty(), guar),
+                Ok(val) => ty::Const::new_value(tcx, val, self.ty()),
+                Err(guar) => ty::Const::new_error(tcx, guar, self.ty()),
             }
         } else {
             // Either the constant isn't evaluatable or ValTree creation failed.
@@ -238,6 +353,138 @@ impl<'tcx> Const<'tcx> {
             .unwrap_or_else(|| bug!("expected usize, got {:#?}", self))
     }
 
+    #[inline]
+    /// Tries to evaluate the constant if it is `Unevaluated`. If that isn't possible or necessary
+    /// return `None`.
+    // FIXME(@lcnr): Completely rework the evaluation/normalization system for `ty::Const` once valtrees are merged.
+    pub fn try_eval_for_mir(
+        self,
+        tcx: TyCtxt<'tcx>,
+        param_env: ParamEnv<'tcx>,
+    ) -> Option<Result<ConstValue<'tcx>, ErrorGuaranteed>> {
+        match self.try_eval_inner(tcx, param_env, EvalMode::Mir) {
+            Some(Ok(EvalResult::ValTree(_))) => unreachable!(),
+            Some(Ok(EvalResult::ConstVal(v))) => Some(Ok(v)),
+            Some(Err(e)) => Some(Err(e)),
+            None => None,
+        }
+    }
+
+    #[inline]
+    /// Tries to evaluate the constant if it is `Unevaluated`. If that isn't possible or necessary
+    /// return `None`.
+    // FIXME(@lcnr): Completely rework the evaluation/normalization system for `ty::Const` once valtrees are merged.
+    pub fn try_eval_for_typeck(
+        self,
+        tcx: TyCtxt<'tcx>,
+        param_env: ParamEnv<'tcx>,
+    ) -> Option<Result<ty::ValTree<'tcx>, ErrorGuaranteed>> {
+        match self.try_eval_inner(tcx, param_env, EvalMode::Typeck) {
+            Some(Ok(EvalResult::ValTree(v))) => Some(Ok(v)),
+            Some(Ok(EvalResult::ConstVal(_))) => unreachable!(),
+            Some(Err(e)) => Some(Err(e)),
+            None => None,
+        }
+    }
+
+    #[inline]
+    fn try_eval_inner(
+        self,
+        tcx: TyCtxt<'tcx>,
+        param_env: ParamEnv<'tcx>,
+        eval_mode: EvalMode,
+    ) -> Option<Result<EvalResult<'tcx>, ErrorGuaranteed>> {
+        assert!(!self.has_escaping_bound_vars(), "escaping vars in {self:?}");
+        if let ConstKind::Unevaluated(unevaluated) = self.kind() {
+            use crate::mir::interpret::ErrorHandled;
+
+            // HACK(eddyb) this erases lifetimes even though `const_eval_resolve`
+            // also does later, but we want to do it before checking for
+            // inference variables.
+            // Note that we erase regions *before* calling `with_reveal_all_normalized`,
+            // so that we don't try to invoke this query with
+            // any region variables.
+
+            // HACK(eddyb) when the query key would contain inference variables,
+            // attempt using identity substs and `ParamEnv` instead, that will succeed
+            // when the expression doesn't depend on any parameters.
+            // FIXME(eddyb, skinny121) pass `InferCtxt` into here when it's available, so that
+            // we can call `infcx.const_eval_resolve` which handles inference variables.
+            let param_env_and = if (param_env, unevaluated).has_non_region_infer() {
+                tcx.param_env(unevaluated.def).and(ty::UnevaluatedConst {
+                    def: unevaluated.def,
+                    substs: InternalSubsts::identity_for_item(tcx, unevaluated.def),
+                })
+            } else {
+                tcx.erase_regions(param_env)
+                    .with_reveal_all_normalized(tcx)
+                    .and(tcx.erase_regions(unevaluated))
+            };
+
+            // FIXME(eddyb) maybe the `const_eval_*` methods should take
+            // `ty::ParamEnvAnd` instead of having them separate.
+            let (param_env, unevaluated) = param_env_and.into_parts();
+            // try to resolve e.g. associated constants to their definition on an impl, and then
+            // evaluate the const.
+            match eval_mode {
+                EvalMode::Typeck => {
+                    match tcx.const_eval_resolve_for_typeck(param_env, unevaluated, None) {
+                        // NOTE(eddyb) `val` contains no lifetimes/types/consts,
+                        // and we use the original type, so nothing from `substs`
+                        // (which may be identity substs, see above),
+                        // can leak through `val` into the const we return.
+                        Ok(val) => Some(Ok(EvalResult::ValTree(val?))),
+                        Err(ErrorHandled::TooGeneric) => None,
+                        Err(ErrorHandled::Reported(e)) => Some(Err(e.into())),
+                    }
+                }
+                EvalMode::Mir => {
+                    match tcx.const_eval_resolve(param_env, unevaluated.expand(), None) {
+                        // NOTE(eddyb) `val` contains no lifetimes/types/consts,
+                        // and we use the original type, so nothing from `substs`
+                        // (which may be identity substs, see above),
+                        // can leak through `val` into the const we return.
+                        Ok(val) => Some(Ok(EvalResult::ConstVal(val))),
+                        Err(ErrorHandled::TooGeneric) => None,
+                        Err(ErrorHandled::Reported(e)) => Some(Err(e.into())),
+                    }
+                }
+            }
+        } else {
+            None
+        }
+    }
+
+    #[inline]
+    pub fn try_to_value(self) -> Option<ty::ValTree<'tcx>> {
+        if let ConstKind::Value(val) = self.kind() { Some(val) } else { None }
+    }
+
+    #[inline]
+    pub fn try_to_scalar(self) -> Option<Scalar<AllocId>> {
+        self.try_to_value()?.try_to_scalar()
+    }
+
+    #[inline]
+    pub fn try_to_scalar_int(self) -> Option<ScalarInt> {
+        self.try_to_value()?.try_to_scalar_int()
+    }
+
+    #[inline]
+    pub fn try_to_bits(self, size: Size) -> Option<u128> {
+        self.try_to_scalar_int()?.to_bits(size).ok()
+    }
+
+    #[inline]
+    pub fn try_to_bool(self) -> Option<bool> {
+        self.try_to_scalar_int()?.try_into().ok()
+    }
+
+    #[inline]
+    pub fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option<u64> {
+        self.try_to_value()?.try_to_target_usize(tcx)
+    }
+
     pub fn is_ct_infer(self) -> bool {
         matches!(self.kind(), ty::ConstKind::Infer(_))
     }
diff --git a/compiler/rustc_middle/src/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs
index 1dd4f8a2437..a6bf7491118 100644
--- a/compiler/rustc_middle/src/ty/consts/kind.rs
+++ b/compiler/rustc_middle/src/ty/consts/kind.rs
@@ -1,17 +1,11 @@
 use super::Const;
 use crate::mir;
-use crate::mir::interpret::{AllocId, ConstValue, Scalar};
 use crate::ty::abstract_const::CastKind;
-use crate::ty::subst::{InternalSubsts, SubstsRef};
-use crate::ty::ParamEnv;
-use crate::ty::{self, List, Ty, TyCtxt, TypeVisitableExt};
+use crate::ty::subst::SubstsRef;
+use crate::ty::{self, List, Ty};
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
-use rustc_errors::ErrorGuaranteed;
 use rustc_hir::def_id::DefId;
 use rustc_macros::HashStable;
-use rustc_target::abi::Size;
-
-use super::ScalarInt;
 
 /// An unevaluated (potentially generic) constant used in the type-system.
 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Lift)]
@@ -41,45 +35,6 @@ impl<'tcx> UnevaluatedConst<'tcx> {
     }
 }
 
-/// Represents a constant in Rust.
-#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable)]
-#[derive(Hash, HashStable, TypeFoldable, TypeVisitable)]
-#[derive(derive_more::From)]
-pub enum ConstKind<'tcx> {
-    /// A const generic parameter.
-    Param(ty::ParamConst),
-
-    /// Infer the value of the const.
-    Infer(InferConst<'tcx>),
-
-    /// Bound const variable, used only when preparing a trait query.
-    Bound(ty::DebruijnIndex, ty::BoundVar),
-
-    /// A placeholder const - universally quantified higher-ranked const.
-    Placeholder(ty::PlaceholderConst<'tcx>),
-
-    /// Used in the HIR by using `Unevaluated` everywhere and later normalizing to one of the other
-    /// variants when the code is monomorphic enough for that.
-    Unevaluated(UnevaluatedConst<'tcx>),
-
-    /// Used to hold computed value.
-    Value(ty::ValTree<'tcx>),
-
-    /// A placeholder for a const which could not be computed; this is
-    /// propagated to avoid useless error messages.
-    #[from(ignore)]
-    Error(ErrorGuaranteed),
-
-    /// Expr which contains an expression which has partially evaluated items.
-    Expr(Expr<'tcx>),
-}
-
-impl<'tcx> From<ty::ConstVid<'tcx>> for ConstKind<'tcx> {
-    fn from(const_vid: ty::ConstVid<'tcx>) -> Self {
-        InferConst::Var(const_vid).into()
-    }
-}
-
 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
 #[derive(HashStable, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable)]
 pub enum Expr<'tcx> {
@@ -93,39 +48,7 @@ pub enum Expr<'tcx> {
 static_assert_size!(Expr<'_>, 24);
 
 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
-static_assert_size!(ConstKind<'_>, 32);
-
-impl<'tcx> ConstKind<'tcx> {
-    #[inline]
-    pub fn try_to_value(self) -> Option<ty::ValTree<'tcx>> {
-        if let ConstKind::Value(val) = self { Some(val) } else { None }
-    }
-
-    #[inline]
-    pub fn try_to_scalar(self) -> Option<Scalar<AllocId>> {
-        self.try_to_value()?.try_to_scalar()
-    }
-
-    #[inline]
-    pub fn try_to_scalar_int(self) -> Option<ScalarInt> {
-        self.try_to_value()?.try_to_scalar_int()
-    }
-
-    #[inline]
-    pub fn try_to_bits(self, size: Size) -> Option<u128> {
-        self.try_to_scalar_int()?.to_bits(size).ok()
-    }
-
-    #[inline]
-    pub fn try_to_bool(self) -> Option<bool> {
-        self.try_to_scalar_int()?.try_into().ok()
-    }
-
-    #[inline]
-    pub fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option<u64> {
-        self.try_to_value()?.try_to_target_usize(tcx)
-    }
-}
+static_assert_size!(super::ConstKind<'_>, 32);
 
 /// An inference variable for a const, for use in const generics.
 #[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Hash)]
@@ -144,124 +67,3 @@ impl<CTX> HashStable<CTX> for InferConst<'_> {
         }
     }
 }
-
-enum EvalMode {
-    Typeck,
-    Mir,
-}
-
-enum EvalResult<'tcx> {
-    ValTree(ty::ValTree<'tcx>),
-    ConstVal(ConstValue<'tcx>),
-}
-
-impl<'tcx> ConstKind<'tcx> {
-    #[inline]
-    /// Tries to evaluate the constant if it is `Unevaluated`. If that doesn't succeed, return the
-    /// unevaluated constant.
-    pub fn eval(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Self {
-        self.try_eval_for_typeck(tcx, param_env).and_then(Result::ok).map_or(self, ConstKind::Value)
-    }
-
-    #[inline]
-    /// Tries to evaluate the constant if it is `Unevaluated`. If that isn't possible or necessary
-    /// return `None`.
-    // FIXME(@lcnr): Completely rework the evaluation/normalization system for `ty::Const` once valtrees are merged.
-    pub fn try_eval_for_mir(
-        self,
-        tcx: TyCtxt<'tcx>,
-        param_env: ParamEnv<'tcx>,
-    ) -> Option<Result<ConstValue<'tcx>, ErrorGuaranteed>> {
-        match self.try_eval_inner(tcx, param_env, EvalMode::Mir) {
-            Some(Ok(EvalResult::ValTree(_))) => unreachable!(),
-            Some(Ok(EvalResult::ConstVal(v))) => Some(Ok(v)),
-            Some(Err(e)) => Some(Err(e)),
-            None => None,
-        }
-    }
-
-    #[inline]
-    /// Tries to evaluate the constant if it is `Unevaluated`. If that isn't possible or necessary
-    /// return `None`.
-    // FIXME(@lcnr): Completely rework the evaluation/normalization system for `ty::Const` once valtrees are merged.
-    pub fn try_eval_for_typeck(
-        self,
-        tcx: TyCtxt<'tcx>,
-        param_env: ParamEnv<'tcx>,
-    ) -> Option<Result<ty::ValTree<'tcx>, ErrorGuaranteed>> {
-        match self.try_eval_inner(tcx, param_env, EvalMode::Typeck) {
-            Some(Ok(EvalResult::ValTree(v))) => Some(Ok(v)),
-            Some(Ok(EvalResult::ConstVal(_))) => unreachable!(),
-            Some(Err(e)) => Some(Err(e)),
-            None => None,
-        }
-    }
-
-    #[inline]
-    fn try_eval_inner(
-        self,
-        tcx: TyCtxt<'tcx>,
-        param_env: ParamEnv<'tcx>,
-        eval_mode: EvalMode,
-    ) -> Option<Result<EvalResult<'tcx>, ErrorGuaranteed>> {
-        assert!(!self.has_escaping_bound_vars(), "escaping vars in {self:?}");
-        if let ConstKind::Unevaluated(unevaluated) = self {
-            use crate::mir::interpret::ErrorHandled;
-
-            // HACK(eddyb) this erases lifetimes even though `const_eval_resolve`
-            // also does later, but we want to do it before checking for
-            // inference variables.
-            // Note that we erase regions *before* calling `with_reveal_all_normalized`,
-            // so that we don't try to invoke this query with
-            // any region variables.
-
-            // HACK(eddyb) when the query key would contain inference variables,
-            // attempt using identity substs and `ParamEnv` instead, that will succeed
-            // when the expression doesn't depend on any parameters.
-            // FIXME(eddyb, skinny121) pass `InferCtxt` into here when it's available, so that
-            // we can call `infcx.const_eval_resolve` which handles inference variables.
-            let param_env_and = if (param_env, unevaluated).has_non_region_infer() {
-                tcx.param_env(unevaluated.def).and(ty::UnevaluatedConst {
-                    def: unevaluated.def,
-                    substs: InternalSubsts::identity_for_item(tcx, unevaluated.def),
-                })
-            } else {
-                tcx.erase_regions(param_env)
-                    .with_reveal_all_normalized(tcx)
-                    .and(tcx.erase_regions(unevaluated))
-            };
-
-            // FIXME(eddyb) maybe the `const_eval_*` methods should take
-            // `ty::ParamEnvAnd` instead of having them separate.
-            let (param_env, unevaluated) = param_env_and.into_parts();
-            // try to resolve e.g. associated constants to their definition on an impl, and then
-            // evaluate the const.
-            match eval_mode {
-                EvalMode::Typeck => {
-                    match tcx.const_eval_resolve_for_typeck(param_env, unevaluated, None) {
-                        // NOTE(eddyb) `val` contains no lifetimes/types/consts,
-                        // and we use the original type, so nothing from `substs`
-                        // (which may be identity substs, see above),
-                        // can leak through `val` into the const we return.
-                        Ok(val) => Some(Ok(EvalResult::ValTree(val?))),
-                        Err(ErrorHandled::TooGeneric) => None,
-                        Err(ErrorHandled::Reported(e)) => Some(Err(e.into())),
-                    }
-                }
-                EvalMode::Mir => {
-                    match tcx.const_eval_resolve(param_env, unevaluated.expand(), None) {
-                        // NOTE(eddyb) `val` contains no lifetimes/types/consts,
-                        // and we use the original type, so nothing from `substs`
-                        // (which may be identity substs, see above),
-                        // can leak through `val` into the const we return.
-                        Ok(val) => Some(Ok(EvalResult::ConstVal(val))),
-                        Err(ErrorHandled::TooGeneric) => None,
-                        Err(ErrorHandled::Reported(e)) => Some(Err(e.into())),
-                    }
-                }
-            }
-        } else {
-            None
-        }
-    }
-}
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 1c610d6891b..035e978f64c 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -25,10 +25,10 @@ use crate::traits::solve::{
     ExternalConstraints, ExternalConstraintsData, PredefinedOpaques, PredefinedOpaquesData,
 };
 use crate::ty::{
-    self, AdtDef, AdtDefData, AdtKind, Binder, Clause, Const, ConstData, FloatTy, FloatVar,
-    FloatVid, GenericParamDefKind, ImplPolarity, InferTy, IntTy, IntVar, IntVid, List, ParamConst,
-    ParamTy, PolyExistentialPredicate, PolyFnSig, Predicate, PredicateKind, Region, RegionKind,
-    ReprOptions, TraitObjectVisitor, Ty, TyKind, TyVar, TyVid, TypeAndMut, UintTy, Visibility,
+    self, AdtDef, AdtDefData, AdtKind, Binder, Clause, Const, ConstData, GenericParamDefKind,
+    ImplPolarity, InferTy, List, ParamConst, ParamTy, PolyExistentialPredicate, PolyFnSig,
+    Predicate, PredicateKind, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind,
+    TyVid, TypeAndMut, Visibility,
 };
 use crate::ty::{GenericArg, InternalSubsts, SubstsRef};
 use rustc_ast::{self as ast, attr};
@@ -70,10 +70,9 @@ use rustc_target::abi::{FieldIdx, Layout, LayoutS, TargetDataLayout, VariantIdx}
 use rustc_target::spec::abi;
 use rustc_type_ir::sty::TyKind::*;
 use rustc_type_ir::WithCachedTypeInfo;
-use rustc_type_ir::{CollectAndApply, DynKind, Interner, TypeFlags};
+use rustc_type_ir::{CollectAndApply, Interner, TypeFlags};
 
 use std::any::Any;
-use std::assert_matches::debug_assert_matches;
 use std::borrow::Borrow;
 use std::cmp::Ordering;
 use std::fmt;
@@ -108,6 +107,14 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
     type PredicateKind = ty::PredicateKind<'tcx>;
     type AllocId = crate::mir::interpret::AllocId;
 
+    type InferConst = ty::InferConst<'tcx>;
+    type AliasConst = ty::UnevaluatedConst<'tcx>;
+    type ParamConst = ty::ParamConst;
+    type BoundConst = ty::BoundVar;
+    type PlaceholderConst = ty::PlaceholderConst<'tcx>;
+    type ValueConst = ty::ValTree<'tcx>;
+    type ExprConst = ty::Expr<'tcx>;
+
     type EarlyBoundRegion = ty::EarlyBoundRegion;
     type BoundRegion = ty::BoundRegion;
     type FreeRegion = ty::FreeRegion;
@@ -320,6 +327,8 @@ pub struct CommonLifetimes<'tcx> {
 
 pub struct CommonConsts<'tcx> {
     pub unit: Const<'tcx>,
+    pub true_: Const<'tcx>,
+    pub false_: Const<'tcx>,
 }
 
 impl<'tcx> CommonTypes<'tcx> {
@@ -417,6 +426,14 @@ impl<'tcx> CommonConsts<'tcx> {
                 kind: ty::ConstKind::Value(ty::ValTree::zst()),
                 ty: types.unit,
             }),
+            true_: mk_const(ty::ConstData {
+                kind: ty::ConstKind::Value(ty::ValTree::Leaf(ty::ScalarInt::TRUE)),
+                ty: types.bool,
+            }),
+            false_: mk_const(ty::ConstData {
+                kind: ty::ConstKind::Value(ty::ValTree::Leaf(ty::ScalarInt::FALSE)),
+                ty: types.bool,
+            }),
         }
     }
 }
@@ -709,58 +726,6 @@ impl<'tcx> TyCtxt<'tcx> {
         }
     }
 
-    /// Constructs a `TyKind::Error` type with current `ErrorGuaranteed`
-    #[track_caller]
-    pub fn ty_error(self, reported: ErrorGuaranteed) -> Ty<'tcx> {
-        self.mk_ty_from_kind(Error(reported))
-    }
-
-    /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` to ensure it gets used.
-    #[track_caller]
-    pub fn ty_error_misc(self) -> Ty<'tcx> {
-        self.ty_error_with_message(DUMMY_SP, "TyKind::Error constructed but no error reported")
-    }
-
-    /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` with the given `msg` to
-    /// ensure it gets used.
-    #[track_caller]
-    pub fn ty_error_with_message<S: Into<MultiSpan>>(
-        self,
-        span: S,
-        msg: impl Into<DiagnosticMessage>,
-    ) -> Ty<'tcx> {
-        let reported = self.sess.delay_span_bug(span, msg);
-        self.mk_ty_from_kind(Error(reported))
-    }
-
-    /// Like [TyCtxt::ty_error] but for constants, with current `ErrorGuaranteed`
-    #[track_caller]
-    pub fn const_error(self, ty: Ty<'tcx>, reported: ErrorGuaranteed) -> Const<'tcx> {
-        self.mk_const(ty::ConstKind::Error(reported), ty)
-    }
-
-    /// Like [TyCtxt::ty_error] but for constants.
-    #[track_caller]
-    pub fn const_error_misc(self, ty: Ty<'tcx>) -> Const<'tcx> {
-        self.const_error_with_message(
-            ty,
-            DUMMY_SP,
-            "ty::ConstKind::Error constructed but no error reported",
-        )
-    }
-
-    /// Like [TyCtxt::ty_error_with_message] but for constants.
-    #[track_caller]
-    pub fn const_error_with_message<S: Into<MultiSpan>>(
-        self,
-        ty: Ty<'tcx>,
-        span: S,
-        msg: &'static str,
-    ) -> Const<'tcx> {
-        let reported = self.sess.delay_span_bug(span, msg);
-        self.mk_const(ty::ConstKind::Error(reported), ty)
-    }
-
     pub fn consider_optimizing<T: Fn() -> String>(self, msg: T) -> bool {
         self.sess.consider_optimizing(|| self.crate_name(LOCAL_CRATE), msg)
     }
@@ -1154,7 +1119,8 @@ impl<'tcx> TyCtxt<'tcx> {
 
     /// Returns `&'static core::panic::Location<'static>`.
     pub fn caller_location_ty(self) -> Ty<'tcx> {
-        self.mk_imm_ref(
+        Ty::new_imm_ref(
+            self,
             self.lifetimes.re_static,
             self.type_of(self.require_lang_item(LangItem::PanicLocation, None))
                 .subst(self, self.mk_substs(&[self.lifetimes.re_static.into()])),
@@ -1552,7 +1518,10 @@ impl<'tcx> TyCtxt<'tcx> {
     /// unsafe.
     pub fn safe_to_unsafe_fn_ty(self, sig: PolyFnSig<'tcx>) -> Ty<'tcx> {
         assert_eq!(sig.unsafety(), hir::Unsafety::Normal);
-        self.mk_fn_ptr(sig.map_bound(|sig| ty::FnSig { unsafety: hir::Unsafety::Unsafe, ..sig }))
+        Ty::new_fn_ptr(
+            self,
+            sig.map_bound(|sig| ty::FnSig { unsafety: hir::Unsafety::Unsafe, ..sig }),
+        )
     }
 
     /// Given the def_id of a Trait `trait_def_id` and the name of an associated item `assoc_name`
@@ -1626,18 +1595,6 @@ impl<'tcx> TyCtxt<'tcx> {
         })
     }
 
-    // Avoid this in favour of more specific `mk_*` methods, where possible.
-    #[allow(rustc::usage_of_ty_tykind)]
-    #[inline]
-    pub fn mk_ty_from_kind(self, st: TyKind<'tcx>) -> Ty<'tcx> {
-        self.interners.intern_ty(
-            st,
-            self.sess,
-            // This is only used to create a stable hashing context.
-            &self.untracked,
-        )
-    }
-
     #[inline]
     pub fn mk_predicate(self, binder: Binder<'tcx, PredicateKind<'tcx>>) -> Predicate<'tcx> {
         self.interners.intern_predicate(
@@ -1657,174 +1614,6 @@ impl<'tcx> TyCtxt<'tcx> {
         if pred.kind() != binder { self.mk_predicate(binder) } else { pred }
     }
 
-    pub fn mk_mach_int(self, tm: IntTy) -> Ty<'tcx> {
-        match tm {
-            IntTy::Isize => self.types.isize,
-            IntTy::I8 => self.types.i8,
-            IntTy::I16 => self.types.i16,
-            IntTy::I32 => self.types.i32,
-            IntTy::I64 => self.types.i64,
-            IntTy::I128 => self.types.i128,
-        }
-    }
-
-    pub fn mk_mach_uint(self, tm: UintTy) -> Ty<'tcx> {
-        match tm {
-            UintTy::Usize => self.types.usize,
-            UintTy::U8 => self.types.u8,
-            UintTy::U16 => self.types.u16,
-            UintTy::U32 => self.types.u32,
-            UintTy::U64 => self.types.u64,
-            UintTy::U128 => self.types.u128,
-        }
-    }
-
-    pub fn mk_mach_float(self, tm: FloatTy) -> Ty<'tcx> {
-        match tm {
-            FloatTy::F32 => self.types.f32,
-            FloatTy::F64 => self.types.f64,
-        }
-    }
-
-    #[inline]
-    pub fn mk_static_str(self) -> Ty<'tcx> {
-        self.mk_imm_ref(self.lifetimes.re_static, self.types.str_)
-    }
-
-    #[inline]
-    pub fn mk_adt(self, def: AdtDef<'tcx>, substs: SubstsRef<'tcx>) -> Ty<'tcx> {
-        // Take a copy of substs so that we own the vectors inside.
-        self.mk_ty_from_kind(Adt(def, substs))
-    }
-
-    #[inline]
-    pub fn mk_foreign(self, def_id: DefId) -> Ty<'tcx> {
-        self.mk_ty_from_kind(Foreign(def_id))
-    }
-
-    fn mk_generic_adt(self, wrapper_def_id: DefId, ty_param: Ty<'tcx>) -> Ty<'tcx> {
-        let adt_def = self.adt_def(wrapper_def_id);
-        let substs =
-            InternalSubsts::for_item(self, wrapper_def_id, |param, substs| match param.kind {
-                GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => bug!(),
-                GenericParamDefKind::Type { has_default, .. } => {
-                    if param.index == 0 {
-                        ty_param.into()
-                    } else {
-                        assert!(has_default);
-                        self.type_of(param.def_id).subst(self, substs).into()
-                    }
-                }
-            });
-        self.mk_ty_from_kind(Adt(adt_def, substs))
-    }
-
-    #[inline]
-    pub fn mk_box(self, ty: Ty<'tcx>) -> Ty<'tcx> {
-        let def_id = self.require_lang_item(LangItem::OwnedBox, None);
-        self.mk_generic_adt(def_id, ty)
-    }
-
-    #[inline]
-    pub fn mk_lang_item(self, ty: Ty<'tcx>, item: LangItem) -> Option<Ty<'tcx>> {
-        let def_id = self.lang_items().get(item)?;
-        Some(self.mk_generic_adt(def_id, ty))
-    }
-
-    #[inline]
-    pub fn mk_diagnostic_item(self, ty: Ty<'tcx>, name: Symbol) -> Option<Ty<'tcx>> {
-        let def_id = self.get_diagnostic_item(name)?;
-        Some(self.mk_generic_adt(def_id, ty))
-    }
-
-    #[inline]
-    pub fn mk_maybe_uninit(self, ty: Ty<'tcx>) -> Ty<'tcx> {
-        let def_id = self.require_lang_item(LangItem::MaybeUninit, None);
-        self.mk_generic_adt(def_id, ty)
-    }
-
-    #[inline]
-    pub fn mk_ptr(self, tm: TypeAndMut<'tcx>) -> Ty<'tcx> {
-        self.mk_ty_from_kind(RawPtr(tm))
-    }
-
-    #[inline]
-    pub fn mk_ref(self, r: Region<'tcx>, tm: TypeAndMut<'tcx>) -> Ty<'tcx> {
-        self.mk_ty_from_kind(Ref(r, tm.ty, tm.mutbl))
-    }
-
-    #[inline]
-    pub fn mk_mut_ref(self, r: Region<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
-        self.mk_ref(r, TypeAndMut { ty, mutbl: hir::Mutability::Mut })
-    }
-
-    #[inline]
-    pub fn mk_imm_ref(self, r: Region<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
-        self.mk_ref(r, TypeAndMut { ty, mutbl: hir::Mutability::Not })
-    }
-
-    #[inline]
-    pub fn mk_mut_ptr(self, ty: Ty<'tcx>) -> Ty<'tcx> {
-        self.mk_ptr(TypeAndMut { ty, mutbl: hir::Mutability::Mut })
-    }
-
-    #[inline]
-    pub fn mk_imm_ptr(self, ty: Ty<'tcx>) -> Ty<'tcx> {
-        self.mk_ptr(TypeAndMut { ty, mutbl: hir::Mutability::Not })
-    }
-
-    #[inline]
-    pub fn mk_array(self, ty: Ty<'tcx>, n: u64) -> Ty<'tcx> {
-        self.mk_ty_from_kind(Array(ty, ty::Const::from_target_usize(self, n)))
-    }
-
-    #[inline]
-    pub fn mk_array_with_const_len(self, ty: Ty<'tcx>, ct: Const<'tcx>) -> Ty<'tcx> {
-        self.mk_ty_from_kind(Array(ty, ct))
-    }
-
-    #[inline]
-    pub fn mk_slice(self, ty: Ty<'tcx>) -> Ty<'tcx> {
-        self.mk_ty_from_kind(Slice(ty))
-    }
-
-    #[inline]
-    pub fn mk_tup(self, ts: &[Ty<'tcx>]) -> Ty<'tcx> {
-        if ts.is_empty() {
-            self.types.unit
-        } else {
-            self.mk_ty_from_kind(Tuple(self.mk_type_list(&ts)))
-        }
-    }
-
-    pub fn mk_tup_from_iter<I, T>(self, iter: I) -> T::Output
-    where
-        I: Iterator<Item = T>,
-        T: CollectAndApply<Ty<'tcx>, Ty<'tcx>>,
-    {
-        T::collect_and_apply(iter, |ts| self.mk_tup(ts))
-    }
-
-    #[inline]
-    pub fn mk_unit(self) -> Ty<'tcx> {
-        self.types.unit
-    }
-
-    #[inline]
-    pub fn mk_diverging_default(self) -> Ty<'tcx> {
-        if self.features().never_type_fallback { self.types.never } else { self.types.unit }
-    }
-
-    #[inline]
-    pub fn mk_fn_def(
-        self,
-        def_id: DefId,
-        substs: impl IntoIterator<Item: Into<GenericArg<'tcx>>>,
-    ) -> Ty<'tcx> {
-        let substs = self.check_and_mk_substs(def_id, substs);
-        self.mk_ty_from_kind(FnDef(def_id, substs))
-    }
-
     #[inline(always)]
     pub(crate) fn check_and_mk_substs(
         self,
@@ -1856,131 +1645,20 @@ impl<'tcx> TyCtxt<'tcx> {
     }
 
     #[inline]
-    pub fn mk_fn_ptr(self, fty: PolyFnSig<'tcx>) -> Ty<'tcx> {
-        self.mk_ty_from_kind(FnPtr(fty))
-    }
-
-    #[inline]
-    pub fn mk_dynamic(
-        self,
-        obj: &'tcx List<PolyExistentialPredicate<'tcx>>,
-        reg: ty::Region<'tcx>,
-        repr: DynKind,
-    ) -> Ty<'tcx> {
-        self.mk_ty_from_kind(Dynamic(obj, reg, repr))
-    }
-
-    #[inline]
-    pub fn mk_projection(
-        self,
-        item_def_id: DefId,
-        substs: impl IntoIterator<Item: Into<GenericArg<'tcx>>>,
-    ) -> Ty<'tcx> {
-        self.mk_alias(ty::Projection, self.mk_alias_ty(item_def_id, substs))
-    }
-
-    #[inline]
-    pub fn mk_closure(self, def_id: DefId, closure_substs: SubstsRef<'tcx>) -> Ty<'tcx> {
-        debug_assert_eq!(
-            closure_substs.len(),
-            self.generics_of(self.typeck_root_def_id(def_id)).count() + 3,
-            "closure constructed with incorrect substitutions"
-        );
-        self.mk_ty_from_kind(Closure(def_id, closure_substs))
-    }
-
-    #[inline]
-    pub fn mk_generator(
-        self,
-        def_id: DefId,
-        generator_substs: SubstsRef<'tcx>,
-        movability: hir::Movability,
-    ) -> Ty<'tcx> {
-        debug_assert_eq!(
-            generator_substs.len(),
-            self.generics_of(self.typeck_root_def_id(def_id)).count() + 5,
-            "generator constructed with incorrect number of substitutions"
-        );
-        self.mk_ty_from_kind(Generator(def_id, generator_substs, movability))
-    }
-
-    #[inline]
-    pub fn mk_generator_witness(self, types: ty::Binder<'tcx, &'tcx List<Ty<'tcx>>>) -> Ty<'tcx> {
-        self.mk_ty_from_kind(GeneratorWitness(types))
-    }
-
-    /// Creates a `&mut Context<'_>` [`Ty`] with erased lifetimes.
-    pub fn mk_task_context(self) -> Ty<'tcx> {
-        let context_did = self.require_lang_item(LangItem::Context, None);
-        let context_adt_ref = self.adt_def(context_did);
-        let context_substs = self.mk_substs(&[self.lifetimes.re_erased.into()]);
-        let context_ty = self.mk_adt(context_adt_ref, context_substs);
-        self.mk_mut_ref(self.lifetimes.re_erased, context_ty)
-    }
-
-    #[inline]
-    pub fn mk_generator_witness_mir(self, id: DefId, substs: SubstsRef<'tcx>) -> Ty<'tcx> {
-        self.mk_ty_from_kind(GeneratorWitnessMIR(id, substs))
-    }
-
-    #[inline]
-    pub fn mk_const(self, kind: impl Into<ty::ConstKind<'tcx>>, ty: Ty<'tcx>) -> Const<'tcx> {
-        self.intern_const(ty::ConstData { kind: kind.into(), ty })
-    }
-
-    #[inline]
-    pub fn mk_ty_var(self, v: TyVid) -> Ty<'tcx> {
-        // Use a pre-interned one when possible.
-        self.types
-            .ty_vars
-            .get(v.as_usize())
-            .copied()
-            .unwrap_or_else(|| self.mk_ty_from_kind(Infer(TyVar(v))))
-    }
-
-    #[inline]
-    pub fn mk_int_var(self, v: IntVid) -> Ty<'tcx> {
-        self.mk_ty_from_kind(Infer(IntVar(v)))
-    }
-
-    #[inline]
-    pub fn mk_float_var(self, v: FloatVid) -> Ty<'tcx> {
-        self.mk_ty_from_kind(Infer(FloatVar(v)))
-    }
-
-    #[inline]
-    pub fn mk_fresh_ty(self, n: u32) -> Ty<'tcx> {
-        // Use a pre-interned one when possible.
-        self.types
-            .fresh_tys
-            .get(n as usize)
-            .copied()
-            .unwrap_or_else(|| self.mk_ty_from_kind(Infer(ty::FreshTy(n))))
-    }
-
-    #[inline]
-    pub fn mk_fresh_int_ty(self, n: u32) -> Ty<'tcx> {
-        // Use a pre-interned one when possible.
-        self.types
-            .fresh_int_tys
-            .get(n as usize)
-            .copied()
-            .unwrap_or_else(|| self.mk_ty_from_kind(Infer(ty::FreshIntTy(n))))
-    }
-
-    #[inline]
-    pub fn mk_fresh_float_ty(self, n: u32) -> Ty<'tcx> {
-        // Use a pre-interned one when possible.
-        self.types
-            .fresh_float_tys
-            .get(n as usize)
-            .copied()
-            .unwrap_or_else(|| self.mk_ty_from_kind(Infer(ty::FreshFloatTy(n))))
+    pub fn mk_ct_from_kind(self, kind: ty::ConstKind<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> {
+        self.intern_const(ty::ConstData { kind, ty })
     }
 
+    // Avoid this in favour of more specific `Ty::new_*` methods, where possible.
+    #[allow(rustc::usage_of_ty_tykind)]
     #[inline]
-    pub fn mk_ty_param(self, index: u32, name: Symbol) -> Ty<'tcx> {
-        self.mk_ty_from_kind(Param(ParamTy { index, name }))
+    pub fn mk_ty_from_kind(self, st: TyKind<'tcx>) -> Ty<'tcx> {
+        self.interners.intern_ty(
+            st,
+            self.sess,
+            // This is only used to create a stable hashing context.
+            &self.untracked,
+        )
     }
 
     pub fn mk_param_from_def(self, param: &ty::GenericParamDef) -> GenericArg<'tcx> {
@@ -1988,45 +1666,18 @@ impl<'tcx> TyCtxt<'tcx> {
             GenericParamDefKind::Lifetime => {
                 ty::Region::new_early_bound(self, param.to_early_bound_region_data()).into()
             }
-            GenericParamDefKind::Type { .. } => self.mk_ty_param(param.index, param.name).into(),
-            GenericParamDefKind::Const { .. } => self
-                .mk_const(
-                    ParamConst { index: param.index, name: param.name },
-                    self.type_of(param.def_id)
-                        .no_bound_vars()
-                        .expect("const parameter types cannot be generic"),
-                )
-                .into(),
+            GenericParamDefKind::Type { .. } => Ty::new_param(self, param.index, param.name).into(),
+            GenericParamDefKind::Const { .. } => ty::Const::new_param(
+                self,
+                ParamConst { index: param.index, name: param.name },
+                self.type_of(param.def_id)
+                    .no_bound_vars()
+                    .expect("const parameter types cannot be generic"),
+            )
+            .into(),
         }
     }
 
-    #[inline]
-    pub fn mk_bound(self, index: ty::DebruijnIndex, bound_ty: ty::BoundTy) -> Ty<'tcx> {
-        self.mk_ty_from_kind(Bound(index, bound_ty))
-    }
-
-    #[inline]
-    pub fn mk_placeholder(self, placeholder: ty::PlaceholderType) -> Ty<'tcx> {
-        self.mk_ty_from_kind(Placeholder(placeholder))
-    }
-
-    #[inline]
-    pub fn mk_alias(self, kind: ty::AliasKind, alias_ty: ty::AliasTy<'tcx>) -> Ty<'tcx> {
-        debug_assert_matches!(
-            (kind, self.def_kind(alias_ty.def_id)),
-            (ty::Opaque, DefKind::OpaqueTy)
-                | (ty::Projection | ty::Inherent, DefKind::AssocTy)
-                | (ty::Opaque | ty::Projection, DefKind::ImplTraitPlaceholder)
-                | (ty::Weak, DefKind::TyAlias)
-        );
-        self.mk_ty_from_kind(Alias(kind, alias_ty))
-    }
-
-    #[inline]
-    pub fn mk_opaque(self, def_id: DefId, substs: SubstsRef<'tcx>) -> Ty<'tcx> {
-        self.mk_alias(ty::Opaque, self.mk_alias_ty(def_id, substs))
-    }
-
     pub fn mk_place_field(self, place: Place<'tcx>, f: FieldIdx, ty: Ty<'tcx>) -> Place<'tcx> {
         self.mk_place_elem(place, PlaceElem::Field(f, ty))
     }
@@ -2377,21 +2028,6 @@ impl<'tcx> TyCtxt<'tcx> {
     }
 }
 
-impl<'tcx> TyCtxtAt<'tcx> {
-    /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` to ensure it gets used.
-    #[track_caller]
-    pub fn ty_error_misc(self) -> Ty<'tcx> {
-        self.tcx.ty_error_with_message(self.span, "TyKind::Error constructed but no error reported")
-    }
-
-    /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` with the given `msg` to
-    /// ensure it gets used.
-    #[track_caller]
-    pub fn ty_error_with_message(self, msg: impl Into<DiagnosticMessage>) -> Ty<'tcx> {
-        self.tcx.ty_error_with_message(self.span, msg)
-    }
-}
-
 /// Parameter attributes that can only be determined by examining the body of a function instead
 /// of just its signature.
 ///
diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs
index d89baa9c88d..a0b17c374e4 100644
--- a/compiler/rustc_middle/src/ty/diagnostics.rs
+++ b/compiler/rustc_middle/src/ty/diagnostics.rs
@@ -559,7 +559,7 @@ impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for MakeSuggestableFolder<'tcx> {
             Infer(InferTy::TyVar(_)) if self.infer_suggestable => t,
 
             FnDef(def_id, substs) => {
-                self.tcx.mk_fn_ptr(self.tcx.fn_sig(def_id).subst(self.tcx, substs))
+                Ty::new_fn_ptr(self.tcx, self.tcx.fn_sig(def_id).subst(self.tcx, substs))
             }
 
             // FIXME(compiler-errors): We could replace these with infer, I guess.
diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs
index 06a8e34cbab..ff391794703 100644
--- a/compiler/rustc_middle/src/ty/flags.rs
+++ b/compiler/rustc_middle/src/ty/flags.rs
@@ -287,9 +287,6 @@ impl FlagComputation {
                 self.add_const(expected);
                 self.add_const(found);
             }
-            ty::PredicateKind::Clause(ty::ClauseKind::TypeWellFormedFromEnv(ty)) => {
-                self.add_ty(ty);
-            }
             ty::PredicateKind::Ambiguous => {}
             ty::PredicateKind::AliasRelate(t1, t2, _) => {
                 self.add_term(t1);
diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs
index 149ce29b8d9..77cf6bee79d 100644
--- a/compiler/rustc_middle/src/ty/fold.rs
+++ b/compiler/rustc_middle/src/ty/fold.rs
@@ -348,10 +348,14 @@ impl<'tcx> TyCtxt<'tcx> {
                     )
                 },
                 types: &mut |t: ty::BoundTy| {
-                    self.mk_bound(ty::INNERMOST, ty::BoundTy { var: shift_bv(t.var), kind: t.kind })
+                    Ty::new_bound(
+                        self,
+                        ty::INNERMOST,
+                        ty::BoundTy { var: shift_bv(t.var), kind: t.kind },
+                    )
                 },
                 consts: &mut |c, ty: Ty<'tcx>| {
-                    self.mk_const(ty::ConstKind::Bound(ty::INNERMOST, shift_bv(c)), ty)
+                    ty::Const::new_bound(self, ty::INNERMOST, shift_bv(c), ty)
                 },
             },
         )
@@ -393,14 +397,14 @@ impl<'tcx> TyCtxt<'tcx> {
                 let kind = entry
                     .or_insert_with(|| ty::BoundVariableKind::Ty(ty::BoundTyKind::Anon))
                     .expect_ty();
-                self.tcx.mk_bound(ty::INNERMOST, BoundTy { var, kind })
+                Ty::new_bound(self.tcx, ty::INNERMOST, BoundTy { var, kind })
             }
             fn replace_const(&mut self, bv: ty::BoundVar, ty: Ty<'tcx>) -> ty::Const<'tcx> {
                 let entry = self.map.entry(bv);
                 let index = entry.index();
                 let var = ty::BoundVar::from_usize(index);
                 let () = entry.or_insert_with(|| ty::BoundVariableKind::Const).expect_const();
-                self.tcx.mk_const(ty::ConstKind::Bound(ty::INNERMOST, var), ty)
+                ty::Const::new_bound(self.tcx, ty::INNERMOST, var, ty)
             }
         }
 
@@ -462,7 +466,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Shifter<'tcx> {
         match *ty.kind() {
             ty::Bound(debruijn, bound_ty) if debruijn >= self.current_index => {
                 let debruijn = debruijn.shifted_in(self.amount);
-                self.tcx.mk_bound(debruijn, bound_ty)
+                Ty::new_bound(self.tcx, debruijn, bound_ty)
             }
 
             _ if ty.has_vars_bound_at_or_above(self.current_index) => ty.super_fold_with(self),
@@ -475,7 +479,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Shifter<'tcx> {
             && debruijn >= self.current_index
         {
             let debruijn = debruijn.shifted_in(self.amount);
-            self.tcx.mk_const(ty::ConstKind::Bound(debruijn, bound_ct), ct.ty())
+            ty::Const::new_bound(self.tcx, debruijn, bound_ct, ct.ty())
         } else {
             ct.super_fold_with(self)
         }
diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs
index 58fd6e1aa27..6c7125c4cb7 100644
--- a/compiler/rustc_middle/src/ty/generics.rs
+++ b/compiler/rustc_middle/src/ty/generics.rs
@@ -6,7 +6,7 @@ use rustc_hir::def_id::DefId;
 use rustc_span::symbol::{kw, Symbol};
 use rustc_span::Span;
 
-use super::{Clause, EarlyBoundRegion, InstantiatedPredicates, ParamConst, ParamTy, TyCtxt};
+use super::{Clause, EarlyBoundRegion, InstantiatedPredicates, ParamConst, ParamTy, Ty, TyCtxt};
 
 #[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)]
 pub enum GenericParamDefKind {
@@ -101,10 +101,12 @@ impl GenericParamDef {
     ) -> ty::GenericArg<'tcx> {
         match &self.kind {
             ty::GenericParamDefKind::Lifetime => ty::Region::new_error_misc(tcx).into(),
-            ty::GenericParamDefKind::Type { .. } => tcx.ty_error_misc().into(),
-            ty::GenericParamDefKind::Const { .. } => {
-                tcx.const_error_misc(tcx.type_of(self.def_id).subst(tcx, preceding_substs)).into()
-            }
+            ty::GenericParamDefKind::Type { .. } => Ty::new_misc_error(tcx).into(),
+            ty::GenericParamDefKind::Const { .. } => ty::Const::new_misc_error(
+                tcx,
+                tcx.type_of(self.def_id).subst(tcx, preceding_substs),
+            )
+            .into(),
         }
     }
 }
@@ -133,6 +135,9 @@ pub struct Generics {
 
     pub has_self: bool,
     pub has_late_bound_regions: Option<Span>,
+
+    // The index of the host effect when substituted. (i.e. might be index to parent substs)
+    pub host_effect_index: Option<usize>,
 }
 
 impl<'tcx> Generics {
diff --git a/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs b/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs
index 018fa227154..295cb146461 100644
--- a/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs
+++ b/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs
@@ -170,7 +170,7 @@ impl<'tcx> InhabitedPredicate<'tcx> {
         match self {
             Self::ConstIsZero(c) => {
                 let c = ty::EarlyBinder::bind(c).subst(tcx, substs);
-                let pred = match c.kind().try_to_target_usize(tcx) {
+                let pred = match c.try_to_target_usize(tcx) {
                     Some(0) => Self::True,
                     Some(1..) => Self::False,
                     None => Self::ConstIsZero(c),
diff --git a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs
index 4223502848e..b92d84152b4 100644
--- a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs
+++ b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs
@@ -197,7 +197,7 @@ fn inhabited_predicate_type<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> InhabitedP
 
         // If we can evaluate the array length before having a `ParamEnv`, then
         // we can simplify the predicate. This is an optimization.
-        Array(ty, len) => match len.kind().try_to_target_usize(tcx) {
+        Array(ty, len) => match len.try_to_target_usize(tcx) {
             Some(0) => InhabitedPredicate::True,
             Some(1..) => ty.inhabited_predicate(tcx),
             None => ty.inhabited_predicate(tcx).or(tcx, InhabitedPredicate::ConstIsZero(len)),
diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs
index c0d591430f7..ae57e954ff4 100644
--- a/compiler/rustc_middle/src/ty/instance.rs
+++ b/compiler/rustc_middle/src/ty/instance.rs
@@ -410,8 +410,8 @@ impl<'tcx> Instance<'tcx> {
     ) -> Instance<'tcx> {
         match ty::Instance::resolve(tcx, param_env, def_id, substs) {
             Ok(Some(instance)) => instance,
-            _ => bug!(
-                "failed to resolve instance for {}",
+            instance => bug!(
+                "failed to resolve instance for {}: {instance:#?}",
                 tcx.def_path_str_with_substs(def_id, substs)
             ),
         }
@@ -552,7 +552,7 @@ impl<'tcx> Instance<'tcx> {
             tcx.codegen_fn_attrs(closure_did).flags.contains(CodegenFnAttrFlags::TRACK_CALLER);
         let def = ty::InstanceDef::ClosureOnceShim { call_once, track_caller };
 
-        let self_ty = tcx.mk_closure(closure_did, substs);
+        let self_ty = Ty::new_closure(tcx, closure_did, substs);
 
         let sig = substs.as_closure().sig();
         let sig =
@@ -680,7 +680,7 @@ fn polymorphize<'tcx>(
                     if substs == polymorphized_substs {
                         ty
                     } else {
-                        self.tcx.mk_closure(def_id, polymorphized_substs)
+                        Ty::new_closure(self.tcx, def_id, polymorphized_substs)
                     }
                 }
                 ty::Generator(def_id, substs, movability) => {
@@ -689,7 +689,7 @@ fn polymorphize<'tcx>(
                     if substs == polymorphized_substs {
                         ty
                     } else {
-                        self.tcx.mk_generator(def_id, polymorphized_substs, movability)
+                        Ty::new_generator(self.tcx, def_id, polymorphized_substs, movability)
                     }
                 }
                 _ => ty.super_fold_with(self),
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index 9485106e95e..0fe801ad2ed 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -2,7 +2,7 @@ use crate::error::UnsupportedFnAbi;
 use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use crate::query::TyCtxtAt;
 use crate::ty::normalize_erasing_regions::NormalizationError;
-use crate::ty::{self, ReprOptions, Ty, TyCtxt, TypeVisitableExt};
+use crate::ty::{self, ConstKind, ReprOptions, Ty, TyCtxt, TypeVisitableExt};
 use rustc_error_messages::DiagnosticMessage;
 use rustc_errors::{DiagnosticBuilder, Handler, IntoDiagnostic};
 use rustc_hir as hir;
@@ -133,7 +133,7 @@ impl PrimitiveExt for Primitive {
             F32 => tcx.types.f32,
             F64 => tcx.types.f64,
             // FIXME(erikdesjardins): handle non-default addrspace ptr sizes
-            Pointer(_) => tcx.mk_mut_ptr(tcx.mk_unit()),
+            Pointer(_) => Ty::new_mut_ptr(tcx, Ty::new_unit(tcx)),
         }
     }
 
@@ -480,13 +480,11 @@ fn mul_sorted_consts<'tcx>(
     b: ty::Const<'tcx>,
 ) -> Option<ty::Const<'tcx>> {
     use crate::mir::BinOp::Mul;
-    use ty::ConstKind::Expr;
-    use ty::Expr::Binop;
 
     let mut work = vec![a, b];
     let mut done = vec![];
     while let Some(n) = work.pop() {
-        if let Expr(Binop(Mul, l, r)) = n.kind() {
+        if let ConstKind::Expr(ty::Expr::Binop(Mul, l, r)) = n.kind() {
             work.push(l);
             work.push(r)
         } else {
@@ -517,7 +515,7 @@ fn mul_sorted_consts<'tcx>(
     done.sort_unstable();
 
     // create a single tree from the buffer
-    done.into_iter().reduce(|acc, n| tcx.mk_const(Expr(Binop(Mul, n, acc)), n.ty()))
+    done.into_iter().reduce(|acc, n| ty::Const::new_expr(tcx, ty::Expr::Binop(Mul, n, acc), n.ty()))
 }
 
 pub trait HasTyCtxt<'tcx>: HasDataLayout {
@@ -812,11 +810,11 @@ where
                     // (which may have no non-DST form), and will work as long
                     // as the `Abi` or `FieldsShape` is checked by users.
                     if i == 0 {
-                        let nil = tcx.mk_unit();
+                        let nil = Ty::new_unit(tcx);
                         let unit_ptr_ty = if this.ty.is_unsafe_ptr() {
-                            tcx.mk_mut_ptr(nil)
+                            Ty::new_mut_ptr(tcx, nil)
                         } else {
-                            tcx.mk_mut_ref(tcx.lifetimes.re_static, nil)
+                            Ty::new_mut_ref(tcx, tcx.lifetimes.re_static, nil)
                         };
 
                         // NOTE(eddyb) using an empty `ParamEnv`, and `unwrap`-ing
@@ -829,7 +827,11 @@ where
                     }
 
                     let mk_dyn_vtable = || {
-                        tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_array(tcx.types.usize, 3))
+                        Ty::new_imm_ref(
+                            tcx,
+                            tcx.lifetimes.re_static,
+                            Ty::new_array(tcx, tcx.types.usize, 3),
+                        )
                         /* FIXME: use actual fn pointers
                         Warning: naively computing the number of entries in the
                         vtable by counting the methods on the trait + methods on
@@ -838,9 +840,9 @@ where
                         Increase this counter if you tried to implement this but
                         failed to do it without duplicating a lot of code from
                         other places in the compiler: 2
-                        tcx.mk_tup(&[
-                            tcx.mk_array(tcx.types.usize, 3),
-                            tcx.mk_array(Option<fn()>),
+                        Ty::new_tup(tcx,&[
+                            Ty::new_array(tcx,tcx.types.usize, 3),
+                            Ty::new_array(tcx,Option<fn()>),
                         ])
                         */
                     };
@@ -852,7 +854,7 @@ where
                     {
                         let metadata = tcx.normalize_erasing_regions(
                             cx.param_env(),
-                            tcx.mk_projection(metadata_def_id, [pointee]),
+                            Ty::new_projection(tcx,metadata_def_id, [pointee]),
                         );
 
                         // Map `Metadata = DynMetadata<dyn Trait>` back to a vtable, since it
@@ -927,15 +929,14 @@ where
 
                 ty::Dynamic(_, _, ty::DynStar) => {
                     if i == 0 {
-                        TyMaybeWithLayout::Ty(tcx.mk_mut_ptr(tcx.types.unit))
+                        TyMaybeWithLayout::Ty(Ty::new_mut_ptr(tcx, tcx.types.unit))
                     } else if i == 1 {
                         // FIXME(dyn-star) same FIXME as above applies here too
-                        TyMaybeWithLayout::Ty(
-                            tcx.mk_imm_ref(
-                                tcx.lifetimes.re_static,
-                                tcx.mk_array(tcx.types.usize, 3),
-                            ),
-                        )
+                        TyMaybeWithLayout::Ty(Ty::new_imm_ref(
+                            tcx,
+                            tcx.lifetimes.re_static,
+                            Ty::new_array(tcx, tcx.types.usize, 3),
+                        ))
                     } else {
                         bug!("no field {i} on dyn*")
                     }
@@ -980,10 +981,8 @@ where
                 })
             }
             ty::FnPtr(fn_sig) if offset.bytes() == 0 => {
-                tcx.layout_of(param_env.and(tcx.mk_fn_ptr(fn_sig))).ok().map(|layout| PointeeInfo {
-                    size: layout.size,
-                    align: layout.align.abi,
-                    safe: None,
+                tcx.layout_of(param_env.and(Ty::new_fn_ptr(tcx, fn_sig))).ok().map(|layout| {
+                    PointeeInfo { size: layout.size, align: layout.align.abi, safe: None }
                 })
             }
             ty::Ref(_, ty, mt) if offset.bytes() == 0 => {
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 7bd49ad07ea..c100c45b61a 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -66,6 +66,10 @@ use std::{fmt, str};
 
 pub use crate::ty::diagnostics::*;
 pub use rustc_type_ir::AliasKind::*;
+pub use rustc_type_ir::ConstKind::{
+    Bound as BoundCt, Error as ErrorCt, Expr as ExprCt, Infer as InferCt, Param as ParamCt,
+    Placeholder as PlaceholderCt, Unevaluated, Value,
+};
 pub use rustc_type_ir::DynKind::*;
 pub use rustc_type_ir::InferTy::*;
 pub use rustc_type_ir::RegionKind::*;
@@ -81,7 +85,7 @@ pub use self::closure::{
     CAPTURE_STRUCT_LOCAL,
 };
 pub use self::consts::{
-    Const, ConstData, ConstInt, ConstKind, Expr, InferConst, ScalarInt, UnevaluatedConst, ValTree,
+    Const, ConstData, ConstInt, Expr, InferConst, ScalarInt, UnevaluatedConst, ValTree,
 };
 pub use self::context::{
     tls, CtxtInterners, DeducedParamAttrs, FreeRegionInfo, GlobalCtxt, Lift, TyCtxt, TyCtxtFeed,
@@ -93,7 +97,7 @@ pub use self::rvalue_scopes::RvalueScopes;
 pub use self::sty::BoundRegionKind::*;
 pub use self::sty::{
     AliasTy, Article, Binder, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, BoundVar,
-    BoundVariableKind, CanonicalPolyFnSig, ClosureSubsts, ClosureSubstsParts, ConstVid,
+    BoundVariableKind, CanonicalPolyFnSig, ClosureSubsts, ClosureSubstsParts, ConstKind, ConstVid,
     EarlyBoundRegion, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FnSig,
     FreeRegion, GenSig, GeneratorSubsts, GeneratorSubstsParts, InlineConstSubsts,
     InlineConstSubstsParts, ParamConst, ParamTy, PolyExistentialPredicate,
@@ -554,8 +558,7 @@ impl<'tcx> Predicate<'tcx> {
             | PredicateKind::Coerce(_)
             | PredicateKind::Clause(ClauseKind::ConstEvaluatable(_))
             | PredicateKind::ConstEquate(_, _)
-            | PredicateKind::Ambiguous
-            | PredicateKind::Clause(ClauseKind::TypeWellFormedFromEnv(_)) => true,
+            | PredicateKind::Ambiguous => true,
         }
     }
 }
@@ -661,11 +664,6 @@ pub enum ClauseKind<'tcx> {
 
     /// Constant initializer must evaluate successfully.
     ConstEvaluatable(ty::Const<'tcx>),
-
-    /// Represents a type found in the environment that we can use for implied bounds.
-    ///
-    /// Only used for Chalk.
-    TypeWellFormedFromEnv(Ty<'tcx>),
 }
 
 #[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable)]
@@ -1424,8 +1422,7 @@ impl<'tcx> Predicate<'tcx> {
             | PredicateKind::Clause(ClauseKind::TypeOutlives(..))
             | PredicateKind::Clause(ClauseKind::ConstEvaluatable(..))
             | PredicateKind::ConstEquate(..)
-            | PredicateKind::Ambiguous
-            | PredicateKind::Clause(ClauseKind::TypeWellFormedFromEnv(..)) => None,
+            | PredicateKind::Ambiguous => None,
         }
     }
 
@@ -1445,8 +1442,7 @@ impl<'tcx> Predicate<'tcx> {
             | PredicateKind::Clause(ClauseKind::TypeOutlives(..))
             | PredicateKind::Clause(ClauseKind::ConstEvaluatable(..))
             | PredicateKind::ConstEquate(..)
-            | PredicateKind::Ambiguous
-            | PredicateKind::Clause(ClauseKind::TypeWellFormedFromEnv(..)) => None,
+            | PredicateKind::Ambiguous => None,
         }
     }
 
@@ -1466,8 +1462,7 @@ impl<'tcx> Predicate<'tcx> {
             | PredicateKind::ClosureKind(..)
             | PredicateKind::Clause(ClauseKind::ConstEvaluatable(..))
             | PredicateKind::ConstEquate(..)
-            | PredicateKind::Ambiguous
-            | PredicateKind::Clause(ClauseKind::TypeWellFormedFromEnv(..)) => None,
+            | PredicateKind::Ambiguous => None,
         }
     }
 
diff --git a/compiler/rustc_middle/src/ty/opaque_types.rs b/compiler/rustc_middle/src/ty/opaque_types.rs
index d1ed7be3d2e..b10921eff08 100644
--- a/compiler/rustc_middle/src/ty/opaque_types.rs
+++ b/compiler/rustc_middle/src/ty/opaque_types.rs
@@ -150,17 +150,17 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReverseMapper<'tcx> {
         match *ty.kind() {
             ty::Closure(def_id, substs) => {
                 let substs = self.fold_closure_substs(def_id, substs);
-                self.tcx.mk_closure(def_id, substs)
+                Ty::new_closure(self.tcx, def_id, substs)
             }
 
             ty::Generator(def_id, substs, movability) => {
                 let substs = self.fold_closure_substs(def_id, substs);
-                self.tcx.mk_generator(def_id, substs, movability)
+                Ty::new_generator(self.tcx, def_id, substs, movability)
             }
 
             ty::GeneratorWitnessMIR(def_id, substs) => {
                 let substs = self.fold_closure_substs(def_id, substs);
-                self.tcx.mk_generator_witness_mir(def_id, substs)
+                Ty::new_generator_witness_mir(self.tcx, def_id, substs)
             }
 
             ty::Param(param) => {
@@ -186,7 +186,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReverseMapper<'tcx> {
                                 .emit();
                         }
 
-                        self.interner().ty_error_misc()
+                        Ty::new_misc_error(self.tcx)
                     }
                 }
             }
@@ -216,7 +216,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReverseMapper<'tcx> {
                             })
                             .emit_unless(self.ignore_errors);
 
-                        self.interner().const_error(ct.ty(), guar)
+                        ty::Const::new_error(self.tcx, guar, ct.ty())
                     }
                 }
             }
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index dcc8247937d..96cf36eb996 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -1228,7 +1228,7 @@ pub trait PrettyPrinter<'tcx>:
                 // in order to place the projections inside the `<...>`.
                 if !resugared {
                     // Use a type that can't appear in defaults of type parameters.
-                    let dummy_cx = cx.tcx().mk_fresh_ty(0);
+                    let dummy_cx = Ty::new_fresh(cx.tcx(), 0);
                     let principal = principal.with_self_ty(cx.tcx(), dummy_cx);
 
                     let args = cx
@@ -1393,11 +1393,12 @@ pub trait PrettyPrinter<'tcx>:
         self,
         scalar: Scalar,
         ty: Ty<'tcx>,
-        print_ty: bool,
     ) -> Result<Self::Const, Self::Error> {
         match scalar {
-            Scalar::Ptr(ptr, _size) => self.pretty_print_const_scalar_ptr(ptr, ty, print_ty),
-            Scalar::Int(int) => self.pretty_print_const_scalar_int(int, ty, print_ty),
+            Scalar::Ptr(ptr, _size) => self.pretty_print_const_scalar_ptr(ptr, ty),
+            Scalar::Int(int) => {
+                self.pretty_print_const_scalar_int(int, ty, /* print_ty */ true)
+            }
         }
     }
 
@@ -1405,7 +1406,6 @@ pub trait PrettyPrinter<'tcx>:
         mut self,
         ptr: Pointer,
         ty: Ty<'tcx>,
-        print_ty: bool,
     ) -> Result<Self::Const, Self::Error> {
         define_scoped_cx!(self);
 
@@ -1459,7 +1459,7 @@ pub trait PrettyPrinter<'tcx>:
             _ => {}
         }
         // Any pointer values not covered by a branch above
-        self = self.pretty_print_const_pointer(ptr, ty, print_ty)?;
+        self = self.pretty_print_const_pointer(ptr, ty)?;
         Ok(self)
     }
 
@@ -1527,24 +1527,18 @@ pub trait PrettyPrinter<'tcx>:
     /// This is overridden for MIR printing because we only want to hide alloc ids from users, not
     /// from MIR where it is actually useful.
     fn pretty_print_const_pointer<Prov: Provenance>(
-        mut self,
+        self,
         _: Pointer<Prov>,
         ty: Ty<'tcx>,
-        print_ty: bool,
     ) -> Result<Self::Const, Self::Error> {
-        if print_ty {
-            self.typed_value(
-                |mut this| {
-                    this.write_str("&_")?;
-                    Ok(this)
-                },
-                |this| this.print_type(ty),
-                ": ",
-            )
-        } else {
-            self.write_str("&_")?;
-            Ok(self)
-        }
+        self.typed_value(
+            |mut this| {
+                this.write_str("&_")?;
+                Ok(this)
+            },
+            |this| this.print_type(ty),
+            ": ",
+        )
     }
 
     fn pretty_print_byte_str(mut self, byte_str: &'tcx [u8]) -> Result<Self::Const, Self::Error> {
@@ -1601,7 +1595,8 @@ pub trait PrettyPrinter<'tcx>:
             }
             // Aggregates, printed as array/tuple/struct/variant construction syntax.
             (ty::ValTree::Branch(_), ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) => {
-                let contents = self.tcx().destructure_const(self.tcx().mk_const(valtree, ty));
+                let contents =
+                    self.tcx().destructure_const(ty::Const::new_value(self.tcx(), valtree, ty));
                 let fields = contents.fields.iter().copied();
                 match *ty.kind() {
                     ty::Array(..) => {
@@ -2155,7 +2150,6 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> {
         self,
         p: Pointer<Prov>,
         ty: Ty<'tcx>,
-        print_ty: bool,
     ) -> Result<Self::Const, Self::Error> {
         let print = |mut this: Self| {
             define_scoped_cx!(this);
@@ -2166,11 +2160,7 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> {
             }
             Ok(this)
         };
-        if print_ty {
-            self.typed_value(print, |this| this.print_type(ty), ": ")
-        } else {
-            print(self)
-        }
+        self.typed_value(print, |this| this.print_type(ty), ": ")
     }
 }
 
@@ -2746,7 +2736,7 @@ define_print_and_forward_display! {
 
     ty::ExistentialTraitRef<'tcx> {
         // Use a type that can't appear in defaults of type parameters.
-        let dummy_self = cx.tcx().mk_fresh_ty(0);
+        let dummy_self = Ty::new_fresh(cx.tcx(),0);
         let trait_ref = self.with_self_ty(cx.tcx(), dummy_self);
         p!(print(trait_ref.print_only_trait_path()))
     }
@@ -2888,9 +2878,6 @@ define_print_and_forward_display! {
             ty::ClauseKind::ConstEvaluatable(ct) => {
                 p!("the constant `", print(ct), "` can be evaluated")
             }
-            ty::ClauseKind::TypeWellFormedFromEnv(ty) => {
-                p!("the type `", print(ty), "` is found in the environment")
-            }
         }
     }
 
diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs
index 74a3bddf2fa..5741832c980 100644
--- a/compiler/rustc_middle/src/ty/relate.rs
+++ b/compiler/rustc_middle/src/ty/relate.rs
@@ -408,7 +408,7 @@ pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>(
             bug!("bound types encountered in structurally_relate_tys")
         }
 
-        (&ty::Error(guar), _) | (_, &ty::Error(guar)) => Ok(tcx.ty_error(guar)),
+        (&ty::Error(guar), _) | (_, &ty::Error(guar)) => Ok(Ty::new_error(tcx, guar)),
 
         (&ty::Never, _)
         | (&ty::Char, _)
@@ -428,10 +428,10 @@ pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>(
 
         (&ty::Adt(a_def, a_substs), &ty::Adt(b_def, b_substs)) if a_def == b_def => {
             let substs = relation.relate_item_substs(a_def.did(), a_substs, b_substs)?;
-            Ok(tcx.mk_adt(a_def, substs))
+            Ok(Ty::new_adt(tcx, a_def, substs))
         }
 
-        (&ty::Foreign(a_id), &ty::Foreign(b_id)) if a_id == b_id => Ok(tcx.mk_foreign(a_id)),
+        (&ty::Foreign(a_id), &ty::Foreign(b_id)) if a_id == b_id => Ok(Ty::new_foreign(tcx, a_id)),
 
         (&ty::Dynamic(a_obj, a_region, a_repr), &ty::Dynamic(b_obj, b_region, b_repr))
             if a_repr == b_repr =>
@@ -439,7 +439,7 @@ pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>(
             let region_bound = relation.with_cause(Cause::ExistentialRegionBound, |relation| {
                 relation.relate(a_region, b_region)
             })?;
-            Ok(tcx.mk_dynamic(relation.relate(a_obj, b_obj)?, region_bound, a_repr))
+            Ok(Ty::new_dynamic(tcx, relation.relate(a_obj, b_obj)?, region_bound, a_repr))
         }
 
         (&ty::Generator(a_id, a_substs, movability), &ty::Generator(b_id, b_substs, _))
@@ -449,7 +449,7 @@ pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>(
             // the (anonymous) type of the same generator expression. So
             // all of their regions should be equated.
             let substs = relation.relate(a_substs, b_substs)?;
-            Ok(tcx.mk_generator(a_id, substs, movability))
+            Ok(Ty::new_generator(tcx, a_id, substs, movability))
         }
 
         (&ty::GeneratorWitness(a_types), &ty::GeneratorWitness(b_types)) => {
@@ -459,7 +459,7 @@ pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>(
             let b_types = b_types.map_bound(GeneratorWitness);
             // Then remove the GeneratorWitness for the result
             let types = relation.relate(a_types, b_types)?.map_bound(|witness| witness.0);
-            Ok(tcx.mk_generator_witness(types))
+            Ok(Ty::new_generator_witness(tcx, types))
         }
 
         (&ty::GeneratorWitnessMIR(a_id, a_substs), &ty::GeneratorWitnessMIR(b_id, b_substs))
@@ -469,7 +469,7 @@ pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>(
             // the (anonymous) type of the same generator expression. So
             // all of their regions should be equated.
             let substs = relation.relate(a_substs, b_substs)?;
-            Ok(tcx.mk_generator_witness_mir(a_id, substs))
+            Ok(Ty::new_generator_witness_mir(tcx, a_id, substs))
         }
 
         (&ty::Closure(a_id, a_substs), &ty::Closure(b_id, b_substs)) if a_id == b_id => {
@@ -477,12 +477,12 @@ pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>(
             // the (anonymous) type of the same closure expression. So
             // all of their regions should be equated.
             let substs = relation.relate(a_substs, b_substs)?;
-            Ok(tcx.mk_closure(a_id, &substs))
+            Ok(Ty::new_closure(tcx, a_id, &substs))
         }
 
         (&ty::RawPtr(a_mt), &ty::RawPtr(b_mt)) => {
             let mt = relate_type_and_mut(relation, a_mt, b_mt, a)?;
-            Ok(tcx.mk_ptr(mt))
+            Ok(Ty::new_ptr(tcx, mt))
         }
 
         (&ty::Ref(a_r, a_ty, a_mutbl), &ty::Ref(b_r, b_ty, b_mutbl)) => {
@@ -490,13 +490,13 @@ pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>(
             let a_mt = ty::TypeAndMut { ty: a_ty, mutbl: a_mutbl };
             let b_mt = ty::TypeAndMut { ty: b_ty, mutbl: b_mutbl };
             let mt = relate_type_and_mut(relation, a_mt, b_mt, a)?;
-            Ok(tcx.mk_ref(r, mt))
+            Ok(Ty::new_ref(tcx, r, mt))
         }
 
         (&ty::Array(a_t, sz_a), &ty::Array(b_t, sz_b)) => {
             let t = relation.relate(a_t, b_t)?;
             match relation.relate(sz_a, sz_b) {
-                Ok(sz) => Ok(tcx.mk_array_with_const_len(t, sz)),
+                Ok(sz) => Ok(Ty::new_array_with_const_len(tcx, t, sz)),
                 Err(err) => {
                     // Check whether the lengths are both concrete/known values,
                     // but are unequal, for better diagnostics.
@@ -519,12 +519,15 @@ pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>(
 
         (&ty::Slice(a_t), &ty::Slice(b_t)) => {
             let t = relation.relate(a_t, b_t)?;
-            Ok(tcx.mk_slice(t))
+            Ok(Ty::new_slice(tcx, t))
         }
 
         (&ty::Tuple(as_), &ty::Tuple(bs)) => {
             if as_.len() == bs.len() {
-                Ok(tcx.mk_tup_from_iter(iter::zip(as_, bs).map(|(a, b)| relation.relate(a, b)))?)
+                Ok(Ty::new_tup_from_iter(
+                    tcx,
+                    iter::zip(as_, bs).map(|(a, b)| relation.relate(a, b)),
+                )?)
             } else if !(as_.is_empty() || bs.is_empty()) {
                 Err(TypeError::TupleSize(expected_found(relation, as_.len(), bs.len())))
             } else {
@@ -536,25 +539,16 @@ pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>(
             if a_def_id == b_def_id =>
         {
             let substs = relation.relate_item_substs(a_def_id, a_substs, b_substs)?;
-            Ok(tcx.mk_fn_def(a_def_id, substs))
+            Ok(Ty::new_fn_def(tcx, a_def_id, substs))
         }
 
         (&ty::FnPtr(a_fty), &ty::FnPtr(b_fty)) => {
             let fty = relation.relate(a_fty, b_fty)?;
-            Ok(tcx.mk_fn_ptr(fty))
-        }
-
-        // these two are already handled downstream in case of lazy normalization
-        (&ty::Alias(ty::Projection, a_data), &ty::Alias(ty::Projection, b_data)) => {
-            let projection_ty = relation.relate(a_data, b_data)?;
-            Ok(tcx.mk_projection(projection_ty.def_id, projection_ty.substs))
-        }
-
-        (&ty::Alias(ty::Inherent, a_data), &ty::Alias(ty::Inherent, b_data)) => {
-            let alias_ty = relation.relate(a_data, b_data)?;
-            Ok(tcx.mk_alias(ty::Inherent, tcx.mk_alias_ty(alias_ty.def_id, alias_ty.substs)))
+            Ok(Ty::new_fn_ptr(tcx, fty))
         }
 
+        // The substs of opaque types may not all be invariant, so we have
+        // to treat them separately from other aliases.
         (
             &ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, substs: a_substs, .. }),
             &ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, substs: b_substs, .. }),
@@ -568,7 +562,20 @@ pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>(
                 b_substs,
                 false, // do not fetch `type_of(a_def_id)`, as it will cause a cycle
             )?;
-            Ok(tcx.mk_opaque(a_def_id, substs))
+            Ok(Ty::new_opaque(tcx, a_def_id, substs))
+        }
+
+        // Alias tend to mostly already be handled downstream due to normalization.
+        (&ty::Alias(a_kind, a_data), &ty::Alias(b_kind, b_data)) => {
+            // FIXME(-Zlower-impl-trait-in-trait-to-assoc-ty): This if can be removed
+            // and the assert uncommented once the new desugaring is stable.
+            if a_kind == b_kind {
+                let alias_ty = relation.relate(a_data, b_data)?;
+                // assert_eq!(a_kind, b_kind);
+                Ok(Ty::new_alias(tcx, a_kind, alias_ty))
+            } else {
+                Err(TypeError::Sorts(expected_found(relation, a, b)))
+            }
         }
 
         _ => Err(TypeError::Sorts(expected_found(relation, a, b))),
@@ -623,7 +630,11 @@ pub fn structurally_relate_consts<'tcx, R: TypeRelation<'tcx>>(
                 au.substs,
                 bu.substs,
             )?;
-            return Ok(tcx.mk_const(ty::UnevaluatedConst { def: au.def, substs }, a.ty()));
+            return Ok(ty::Const::new_unevaluated(
+                tcx,
+                ty::UnevaluatedConst { def: au.def, substs },
+                a.ty(),
+            ));
         }
         // Before calling relate on exprs, it is necessary to ensure that the nested consts
         // have identical types.
@@ -664,8 +675,7 @@ pub fn structurally_relate_consts<'tcx, R: TypeRelation<'tcx>>(
                 }
                 _ => return Err(TypeError::ConstMismatch(expected_found(r, a, b))),
             };
-            let kind = ty::ConstKind::Expr(expr);
-            return Ok(tcx.mk_const(kind, a.ty()));
+            return Ok(ty::Const::new_expr(tcx, expr, a.ty()));
         }
         _ => false,
     };
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index 113328de176..4a639a2a0fe 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -11,6 +11,7 @@ use crate::ty::{self, AliasTy, InferConst, Lift, Term, TermKind, Ty, TyCtxt};
 use rustc_hir::def::Namespace;
 use rustc_index::{Idx, IndexVec};
 use rustc_target::abi::TyAndLayout;
+use rustc_type_ir::ConstKind;
 
 use std::fmt;
 use std::ops::ControlFlow;
@@ -189,9 +190,6 @@ impl<'tcx> fmt::Debug for ty::ClauseKind<'tcx> {
             ty::ClauseKind::ConstEvaluatable(ct) => {
                 write!(f, "ConstEvaluatable({ct:?})")
             }
-            ty::ClauseKind::TypeWellFormedFromEnv(ty) => {
-                write!(f, "TypeWellFormedFromEnv({:?})", ty)
-            }
         }
     }
 }
@@ -244,24 +242,6 @@ impl<'tcx> fmt::Debug for ty::Const<'tcx> {
     }
 }
 
-impl<'tcx> fmt::Debug for ty::ConstKind<'tcx> {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        use ty::ConstKind::*;
-        match self {
-            Param(param) => write!(f, "{param:?}"),
-            Infer(var) => write!(f, "{var:?}"),
-            Bound(debruijn, var) => rustc_type_ir::debug_bound_var(f, *debruijn, *var),
-            Placeholder(placeholder) => write!(f, "{placeholder:?}"),
-            Unevaluated(uv) => {
-                f.debug_tuple("Unevaluated").field(&uv.substs).field(&uv.def).finish()
-            }
-            Value(valtree) => write!(f, "{valtree:?}"),
-            Error(_) => write!(f, "{{const error}}"),
-            Expr(expr) => write!(f, "{expr:?}"),
-        }
-    }
-}
-
 impl fmt::Debug for ty::BoundTy {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self.kind {
@@ -731,9 +711,20 @@ impl<'tcx> TypeSuperFoldable<TyCtxt<'tcx>> for ty::Const<'tcx> {
         folder: &mut F,
     ) -> Result<Self, F::Error> {
         let ty = self.ty().try_fold_with(folder)?;
-        let kind = self.kind().try_fold_with(folder)?;
+        let kind = match self.kind() {
+            ConstKind::Param(p) => ConstKind::Param(p.try_fold_with(folder)?),
+            ConstKind::Infer(i) => ConstKind::Infer(i.try_fold_with(folder)?),
+            ConstKind::Bound(d, b) => {
+                ConstKind::Bound(d.try_fold_with(folder)?, b.try_fold_with(folder)?)
+            }
+            ConstKind::Placeholder(p) => ConstKind::Placeholder(p.try_fold_with(folder)?),
+            ConstKind::Unevaluated(uv) => ConstKind::Unevaluated(uv.try_fold_with(folder)?),
+            ConstKind::Value(v) => ConstKind::Value(v.try_fold_with(folder)?),
+            ConstKind::Error(e) => ConstKind::Error(e.try_fold_with(folder)?),
+            ConstKind::Expr(e) => ConstKind::Expr(e.try_fold_with(folder)?),
+        };
         if ty != self.ty() || kind != self.kind() {
-            Ok(folder.interner().mk_const(kind, ty))
+            Ok(folder.interner().mk_ct_from_kind(kind, ty))
         } else {
             Ok(self)
         }
@@ -746,7 +737,19 @@ impl<'tcx> TypeSuperVisitable<TyCtxt<'tcx>> for ty::Const<'tcx> {
         visitor: &mut V,
     ) -> ControlFlow<V::BreakTy> {
         self.ty().visit_with(visitor)?;
-        self.kind().visit_with(visitor)
+        match self.kind() {
+            ConstKind::Param(p) => p.visit_with(visitor),
+            ConstKind::Infer(i) => i.visit_with(visitor),
+            ConstKind::Bound(d, b) => {
+                d.visit_with(visitor)?;
+                b.visit_with(visitor)
+            }
+            ConstKind::Placeholder(p) => p.visit_with(visitor),
+            ConstKind::Unevaluated(uv) => uv.visit_with(visitor),
+            ConstKind::Value(v) => v.visit_with(visitor),
+            ConstKind::Error(e) => e.visit_with(visitor),
+            ConstKind::Expr(e) => e.visit_with(visitor),
+        }
     }
 }
 
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index bb6d49e1773..94746fbdc19 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -15,6 +15,7 @@ use hir::def::DefKind;
 use polonius_engine::Atom;
 use rustc_data_structures::captures::Captures;
 use rustc_data_structures::intern::Interned;
+use rustc_error_messages::DiagnosticMessage;
 use rustc_errors::{DiagnosticArgValue, ErrorGuaranteed, IntoDiagnosticArg, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
@@ -25,6 +26,7 @@ use rustc_span::symbol::{kw, sym, Symbol};
 use rustc_span::{Span, DUMMY_SP};
 use rustc_target::abi::{FieldIdx, VariantIdx, FIRST_VARIANT};
 use rustc_target::spec::abi::{self, Abi};
+use std::assert_matches::debug_assert_matches;
 use std::borrow::Cow;
 use std::cmp::Ordering;
 use std::fmt;
@@ -33,13 +35,17 @@ use std::ops::{ControlFlow, Deref, Range};
 use ty::util::IntTypeExt;
 
 use rustc_type_ir::sty::TyKind::*;
-use rustc_type_ir::RegionKind as IrRegionKind;
 use rustc_type_ir::TyKind as IrTyKind;
+use rustc_type_ir::{CollectAndApply, ConstKind as IrConstKind};
+use rustc_type_ir::{DynKind, RegionKind as IrRegionKind};
+
+use super::GenericParamDefKind;
 
 // Re-export the `TyKind` from `rustc_type_ir` here for convenience
 #[rustc_diagnostic_item = "TyKind"]
 pub type TyKind<'tcx> = IrTyKind<TyCtxt<'tcx>>;
 pub type RegionKind<'tcx> = IrRegionKind<TyCtxt<'tcx>>;
+pub type ConstKind<'tcx> = IrConstKind<TyCtxt<'tcx>>;
 
 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)]
 #[derive(HashStable, TypeFoldable, TypeVisitable, Lift)]
@@ -1239,7 +1245,7 @@ impl<'tcx> AliasTy<'tcx> {
     }
 
     pub fn to_ty(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
-        tcx.mk_alias(self.kind(tcx), self)
+        Ty::new_alias(tcx, self.kind(tcx), self)
     }
 }
 
@@ -1430,7 +1436,7 @@ impl<'tcx> ParamTy {
 
     #[inline]
     pub fn to_ty(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
-        tcx.mk_ty_param(self.index, self.name)
+        Ty::new_param(tcx, self.index, self.name)
     }
 
     pub fn span_from_generics(&self, tcx: TyCtxt<'tcx>, item_with_generics: DefId) -> Span {
@@ -1871,6 +1877,390 @@ impl<'tcx> Region<'tcx> {
     }
 }
 
+/// Constructors for `Ty`
+impl<'tcx> Ty<'tcx> {
+    // Avoid this in favour of more specific `new_*` methods, where possible.
+    #[allow(rustc::usage_of_ty_tykind)]
+    #[inline]
+    pub fn new(tcx: TyCtxt<'tcx>, st: TyKind<'tcx>) -> Ty<'tcx> {
+        tcx.mk_ty_from_kind(st)
+    }
+
+    #[inline]
+    pub fn new_infer(tcx: TyCtxt<'tcx>, infer: ty::InferTy) -> Ty<'tcx> {
+        Ty::new(tcx, TyKind::Infer(infer))
+    }
+
+    #[inline]
+    pub fn new_var(tcx: TyCtxt<'tcx>, v: ty::TyVid) -> Ty<'tcx> {
+        // Use a pre-interned one when possible.
+        tcx.types
+            .ty_vars
+            .get(v.as_usize())
+            .copied()
+            .unwrap_or_else(|| Ty::new(tcx, Infer(TyVar(v))))
+    }
+
+    #[inline]
+    pub fn new_int_var(tcx: TyCtxt<'tcx>, v: ty::IntVid) -> Ty<'tcx> {
+        Ty::new_infer(tcx, IntVar(v))
+    }
+
+    #[inline]
+    pub fn new_float_var(tcx: TyCtxt<'tcx>, v: ty::FloatVid) -> Ty<'tcx> {
+        Ty::new_infer(tcx, FloatVar(v))
+    }
+
+    #[inline]
+    pub fn new_fresh(tcx: TyCtxt<'tcx>, n: u32) -> Ty<'tcx> {
+        // Use a pre-interned one when possible.
+        tcx.types
+            .fresh_tys
+            .get(n as usize)
+            .copied()
+            .unwrap_or_else(|| Ty::new_infer(tcx, ty::FreshTy(n)))
+    }
+
+    #[inline]
+    pub fn new_fresh_int(tcx: TyCtxt<'tcx>, n: u32) -> Ty<'tcx> {
+        // Use a pre-interned one when possible.
+        tcx.types
+            .fresh_int_tys
+            .get(n as usize)
+            .copied()
+            .unwrap_or_else(|| Ty::new_infer(tcx, ty::FreshIntTy(n)))
+    }
+
+    #[inline]
+    pub fn new_fresh_float(tcx: TyCtxt<'tcx>, n: u32) -> Ty<'tcx> {
+        // Use a pre-interned one when possible.
+        tcx.types
+            .fresh_float_tys
+            .get(n as usize)
+            .copied()
+            .unwrap_or_else(|| Ty::new_infer(tcx, ty::FreshFloatTy(n)))
+    }
+
+    #[inline]
+    pub fn new_param(tcx: TyCtxt<'tcx>, index: u32, name: Symbol) -> Ty<'tcx> {
+        tcx.mk_ty_from_kind(Param(ParamTy { index, name }))
+    }
+
+    #[inline]
+    pub fn new_bound(
+        tcx: TyCtxt<'tcx>,
+        index: ty::DebruijnIndex,
+        bound_ty: ty::BoundTy,
+    ) -> Ty<'tcx> {
+        Ty::new(tcx, Bound(index, bound_ty))
+    }
+
+    #[inline]
+    pub fn new_placeholder(tcx: TyCtxt<'tcx>, placeholder: ty::PlaceholderType) -> Ty<'tcx> {
+        Ty::new(tcx, Placeholder(placeholder))
+    }
+
+    #[inline]
+    pub fn new_alias(
+        tcx: TyCtxt<'tcx>,
+        kind: ty::AliasKind,
+        alias_ty: ty::AliasTy<'tcx>,
+    ) -> Ty<'tcx> {
+        debug_assert_matches!(
+            (kind, tcx.def_kind(alias_ty.def_id)),
+            (ty::Opaque, DefKind::OpaqueTy)
+                | (ty::Projection | ty::Inherent, DefKind::AssocTy)
+                | (ty::Opaque | ty::Projection, DefKind::ImplTraitPlaceholder)
+                | (ty::Weak, DefKind::TyAlias)
+        );
+        Ty::new(tcx, Alias(kind, alias_ty))
+    }
+
+    #[inline]
+    pub fn new_opaque(tcx: TyCtxt<'tcx>, def_id: DefId, substs: SubstsRef<'tcx>) -> Ty<'tcx> {
+        Ty::new_alias(tcx, ty::Opaque, tcx.mk_alias_ty(def_id, substs))
+    }
+
+    /// Constructs a `TyKind::Error` type with current `ErrorGuaranteed`
+    pub fn new_error(tcx: TyCtxt<'tcx>, reported: ErrorGuaranteed) -> Ty<'tcx> {
+        Ty::new(tcx, Error(reported))
+    }
+
+    /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` to ensure it gets used.
+    #[track_caller]
+    pub fn new_misc_error(tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
+        Ty::new_error_with_message(tcx, DUMMY_SP, "TyKind::Error constructed but no error reported")
+    }
+
+    /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` with the given `msg` to
+    /// ensure it gets used.
+    #[track_caller]
+    pub fn new_error_with_message<S: Into<MultiSpan>>(
+        tcx: TyCtxt<'tcx>,
+        span: S,
+        msg: impl Into<DiagnosticMessage>,
+    ) -> Ty<'tcx> {
+        let reported = tcx.sess.delay_span_bug(span, msg);
+        Ty::new(tcx, Error(reported))
+    }
+
+    #[inline]
+    pub fn new_int(tcx: TyCtxt<'tcx>, i: ty::IntTy) -> Ty<'tcx> {
+        use ty::IntTy::*;
+        match i {
+            Isize => tcx.types.isize,
+            I8 => tcx.types.i8,
+            I16 => tcx.types.i16,
+            I32 => tcx.types.i32,
+            I64 => tcx.types.i64,
+            I128 => tcx.types.i128,
+        }
+    }
+
+    #[inline]
+    pub fn new_uint(tcx: TyCtxt<'tcx>, ui: ty::UintTy) -> Ty<'tcx> {
+        use ty::UintTy::*;
+        match ui {
+            Usize => tcx.types.usize,
+            U8 => tcx.types.u8,
+            U16 => tcx.types.u16,
+            U32 => tcx.types.u32,
+            U64 => tcx.types.u64,
+            U128 => tcx.types.u128,
+        }
+    }
+
+    #[inline]
+    pub fn new_float(tcx: TyCtxt<'tcx>, f: ty::FloatTy) -> Ty<'tcx> {
+        use ty::FloatTy::*;
+        match f {
+            F32 => tcx.types.f32,
+            F64 => tcx.types.f64,
+        }
+    }
+
+    #[inline]
+    pub fn new_ref(tcx: TyCtxt<'tcx>, r: Region<'tcx>, tm: TypeAndMut<'tcx>) -> Ty<'tcx> {
+        Ty::new(tcx, Ref(r, tm.ty, tm.mutbl))
+    }
+
+    #[inline]
+    pub fn new_mut_ref(tcx: TyCtxt<'tcx>, r: Region<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
+        Ty::new_ref(tcx, r, TypeAndMut { ty, mutbl: hir::Mutability::Mut })
+    }
+
+    #[inline]
+    pub fn new_imm_ref(tcx: TyCtxt<'tcx>, r: Region<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
+        Ty::new_ref(tcx, r, TypeAndMut { ty, mutbl: hir::Mutability::Not })
+    }
+
+    #[inline]
+    pub fn new_ptr(tcx: TyCtxt<'tcx>, tm: TypeAndMut<'tcx>) -> Ty<'tcx> {
+        Ty::new(tcx, RawPtr(tm))
+    }
+
+    #[inline]
+    pub fn new_mut_ptr(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
+        Ty::new_ptr(tcx, TypeAndMut { ty, mutbl: hir::Mutability::Mut })
+    }
+
+    #[inline]
+    pub fn new_imm_ptr(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
+        Ty::new_ptr(tcx, TypeAndMut { ty, mutbl: hir::Mutability::Not })
+    }
+
+    #[inline]
+    pub fn new_adt(tcx: TyCtxt<'tcx>, def: AdtDef<'tcx>, substs: SubstsRef<'tcx>) -> Ty<'tcx> {
+        Ty::new(tcx, Adt(def, substs))
+    }
+
+    #[inline]
+    pub fn new_foreign(tcx: TyCtxt<'tcx>, def_id: DefId) -> Ty<'tcx> {
+        Ty::new(tcx, Foreign(def_id))
+    }
+
+    #[inline]
+    pub fn new_array(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, n: u64) -> Ty<'tcx> {
+        Ty::new(tcx, Array(ty, ty::Const::from_target_usize(tcx, n)))
+    }
+
+    #[inline]
+    pub fn new_array_with_const_len(
+        tcx: TyCtxt<'tcx>,
+        ty: Ty<'tcx>,
+        ct: ty::Const<'tcx>,
+    ) -> Ty<'tcx> {
+        Ty::new(tcx, Array(ty, ct))
+    }
+
+    #[inline]
+    pub fn new_slice(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
+        Ty::new(tcx, Slice(ty))
+    }
+
+    #[inline]
+    pub fn new_tup(tcx: TyCtxt<'tcx>, ts: &[Ty<'tcx>]) -> Ty<'tcx> {
+        if ts.is_empty() { tcx.types.unit } else { Ty::new(tcx, Tuple(tcx.mk_type_list(&ts))) }
+    }
+
+    pub fn new_tup_from_iter<I, T>(tcx: TyCtxt<'tcx>, iter: I) -> T::Output
+    where
+        I: Iterator<Item = T>,
+        T: CollectAndApply<Ty<'tcx>, Ty<'tcx>>,
+    {
+        T::collect_and_apply(iter, |ts| Ty::new_tup(tcx, ts))
+    }
+
+    #[inline]
+    pub fn new_fn_def(
+        tcx: TyCtxt<'tcx>,
+        def_id: DefId,
+        substs: impl IntoIterator<Item: Into<GenericArg<'tcx>>>,
+    ) -> Ty<'tcx> {
+        let substs = tcx.check_and_mk_substs(def_id, substs);
+        Ty::new(tcx, FnDef(def_id, substs))
+    }
+
+    #[inline]
+    pub fn new_fn_ptr(tcx: TyCtxt<'tcx>, fty: PolyFnSig<'tcx>) -> Ty<'tcx> {
+        Ty::new(tcx, FnPtr(fty))
+    }
+
+    #[inline]
+    pub fn new_dynamic(
+        tcx: TyCtxt<'tcx>,
+        obj: &'tcx List<PolyExistentialPredicate<'tcx>>,
+        reg: ty::Region<'tcx>,
+        repr: DynKind,
+    ) -> Ty<'tcx> {
+        Ty::new(tcx, Dynamic(obj, reg, repr))
+    }
+
+    #[inline]
+    pub fn new_projection(
+        tcx: TyCtxt<'tcx>,
+        item_def_id: DefId,
+        substs: impl IntoIterator<Item: Into<GenericArg<'tcx>>>,
+    ) -> Ty<'tcx> {
+        Ty::new_alias(tcx, ty::Projection, tcx.mk_alias_ty(item_def_id, substs))
+    }
+
+    #[inline]
+    pub fn new_closure(
+        tcx: TyCtxt<'tcx>,
+        def_id: DefId,
+        closure_substs: SubstsRef<'tcx>,
+    ) -> Ty<'tcx> {
+        debug_assert_eq!(
+            closure_substs.len(),
+            tcx.generics_of(tcx.typeck_root_def_id(def_id)).count() + 3,
+            "closure constructed with incorrect substitutions"
+        );
+        Ty::new(tcx, Closure(def_id, closure_substs))
+    }
+
+    #[inline]
+    pub fn new_generator(
+        tcx: TyCtxt<'tcx>,
+        def_id: DefId,
+        generator_substs: SubstsRef<'tcx>,
+        movability: hir::Movability,
+    ) -> Ty<'tcx> {
+        debug_assert_eq!(
+            generator_substs.len(),
+            tcx.generics_of(tcx.typeck_root_def_id(def_id)).count() + 5,
+            "generator constructed with incorrect number of substitutions"
+        );
+        Ty::new(tcx, Generator(def_id, generator_substs, movability))
+    }
+
+    #[inline]
+    pub fn new_generator_witness(
+        tcx: TyCtxt<'tcx>,
+        types: ty::Binder<'tcx, &'tcx List<Ty<'tcx>>>,
+    ) -> Ty<'tcx> {
+        Ty::new(tcx, GeneratorWitness(types))
+    }
+
+    #[inline]
+    pub fn new_generator_witness_mir(
+        tcx: TyCtxt<'tcx>,
+        id: DefId,
+        substs: SubstsRef<'tcx>,
+    ) -> Ty<'tcx> {
+        Ty::new(tcx, GeneratorWitnessMIR(id, substs))
+    }
+
+    // misc
+
+    #[inline]
+    pub fn new_unit(tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
+        tcx.types.unit
+    }
+
+    #[inline]
+    pub fn new_static_str(tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
+        Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, tcx.types.str_)
+    }
+
+    #[inline]
+    pub fn new_diverging_default(tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
+        if tcx.features().never_type_fallback { tcx.types.never } else { tcx.types.unit }
+    }
+
+    // lang and diagnostic tys
+
+    fn new_generic_adt(tcx: TyCtxt<'tcx>, wrapper_def_id: DefId, ty_param: Ty<'tcx>) -> Ty<'tcx> {
+        let adt_def = tcx.adt_def(wrapper_def_id);
+        let substs =
+            InternalSubsts::for_item(tcx, wrapper_def_id, |param, substs| match param.kind {
+                GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => bug!(),
+                GenericParamDefKind::Type { has_default, .. } => {
+                    if param.index == 0 {
+                        ty_param.into()
+                    } else {
+                        assert!(has_default);
+                        tcx.type_of(param.def_id).subst(tcx, substs).into()
+                    }
+                }
+            });
+        Ty::new(tcx, Adt(adt_def, substs))
+    }
+
+    #[inline]
+    pub fn new_lang_item(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, item: LangItem) -> Option<Ty<'tcx>> {
+        let def_id = tcx.lang_items().get(item)?;
+        Some(Ty::new_generic_adt(tcx, def_id, ty))
+    }
+
+    #[inline]
+    pub fn new_diagnostic_item(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, name: Symbol) -> Option<Ty<'tcx>> {
+        let def_id = tcx.get_diagnostic_item(name)?;
+        Some(Ty::new_generic_adt(tcx, def_id, ty))
+    }
+
+    #[inline]
+    pub fn new_box(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
+        let def_id = tcx.require_lang_item(LangItem::OwnedBox, None);
+        Ty::new_generic_adt(tcx, def_id, ty)
+    }
+
+    #[inline]
+    pub fn new_maybe_uninit(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
+        let def_id = tcx.require_lang_item(LangItem::MaybeUninit, None);
+        Ty::new_generic_adt(tcx, def_id, ty)
+    }
+
+    /// Creates a `&mut Context<'_>` [`Ty`] with erased lifetimes.
+    pub fn new_task_context(tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
+        let context_did = tcx.require_lang_item(LangItem::Context, None);
+        let context_adt_ref = tcx.adt_def(context_did);
+        let context_substs = tcx.mk_substs(&[tcx.lifetimes.re_erased.into()]);
+        let context_ty = Ty::new_adt(tcx, context_adt_ref, context_substs);
+        Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, context_ty)
+    }
+}
+
 /// Type utilities
 impl<'tcx> Ty<'tcx> {
     #[inline(always)]
@@ -2314,7 +2704,7 @@ impl<'tcx> Ty<'tcx> {
                 let assoc_items = tcx.associated_item_def_ids(
                     tcx.require_lang_item(hir::LangItem::DiscriminantKind, None),
                 );
-                tcx.mk_projection(assoc_items[0], tcx.mk_substs(&[self.into()]))
+                Ty::new_projection(tcx, assoc_items[0], tcx.mk_substs(&[self.into()]))
             }
 
             ty::Bool
diff --git a/compiler/rustc_middle/src/ty/subst.rs b/compiler/rustc_middle/src/ty/subst.rs
index 111b1d009b3..4d5f5b8658c 100644
--- a/compiler/rustc_middle/src/ty/subst.rs
+++ b/compiler/rustc_middle/src/ty/subst.rs
@@ -11,6 +11,7 @@ use rustc_errors::{DiagnosticArgValue, IntoDiagnosticArg};
 use rustc_hir::def_id::DefId;
 use rustc_macros::HashStable;
 use rustc_serialize::{self, Decodable, Encodable};
+use rustc_span::sym;
 use rustc_type_ir::WithCachedTypeInfo;
 use smallvec::SmallVec;
 
@@ -451,6 +452,10 @@ impl<'tcx> InternalSubsts<'tcx> {
     pub fn truncate_to(&self, tcx: TyCtxt<'tcx>, generics: &ty::Generics) -> SubstsRef<'tcx> {
         tcx.mk_substs_from_iter(self.iter().take(generics.count()))
     }
+
+    pub fn host_effect_param(&'tcx self) -> Option<ty::Const<'tcx>> {
+        self.consts().rfind(|x| matches!(x.kind(), ty::ConstKind::Param(p) if p.name == sym::host))
+    }
 }
 
 impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for SubstsRef<'tcx> {
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index bb08deff294..720d770eed4 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -223,7 +223,7 @@ impl<'tcx> TyCtxt<'tcx> {
                 };
                 let reported =
                     self.sess.emit_err(crate::error::RecursionLimitReached { ty, suggested_limit });
-                return self.ty_error(reported);
+                return Ty::new_error(self, reported);
             }
             match *ty.kind() {
                 ty::Adt(def, substs) => {
@@ -610,12 +610,12 @@ impl<'tcx> TyCtxt<'tcx> {
         closure_substs: SubstsRef<'tcx>,
         env_region: ty::Region<'tcx>,
     ) -> Option<Ty<'tcx>> {
-        let closure_ty = self.mk_closure(closure_def_id, closure_substs);
+        let closure_ty = Ty::new_closure(self, closure_def_id, closure_substs);
         let closure_kind_ty = closure_substs.as_closure().kind_ty();
         let closure_kind = closure_kind_ty.to_opt_closure_kind()?;
         let env_ty = match closure_kind {
-            ty::ClosureKind::Fn => self.mk_imm_ref(env_region, closure_ty),
-            ty::ClosureKind::FnMut => self.mk_mut_ref(env_region, closure_ty),
+            ty::ClosureKind::Fn => Ty::new_imm_ref(self, env_region, closure_ty),
+            ty::ClosureKind::FnMut => Ty::new_mut_ref(self, env_region, closure_ty),
             ty::ClosureKind::FnOnce => closure_ty,
         };
         Some(env_ty)
@@ -656,12 +656,12 @@ impl<'tcx> TyCtxt<'tcx> {
     pub fn thread_local_ptr_ty(self, def_id: DefId) -> Ty<'tcx> {
         let static_ty = self.type_of(def_id).subst_identity();
         if self.is_mutable_static(def_id) {
-            self.mk_mut_ptr(static_ty)
+            Ty::new_mut_ptr(self, static_ty)
         } else if self.is_foreign_item(def_id) {
-            self.mk_imm_ptr(static_ty)
+            Ty::new_imm_ptr(self, static_ty)
         } else {
             // FIXME: These things don't *really* have 'static lifetime.
-            self.mk_imm_ref(self.lifetimes.re_static, static_ty)
+            Ty::new_imm_ref(self, self.lifetimes.re_static, static_ty)
         }
     }
 
@@ -676,11 +676,11 @@ impl<'tcx> TyCtxt<'tcx> {
         // Make sure that accesses to unsafe statics end up using raw pointers.
         // For thread-locals, this needs to be kept in sync with `Rvalue::ty`.
         if self.is_mutable_static(def_id) {
-            self.mk_mut_ptr(static_ty)
+            Ty::new_mut_ptr(self, static_ty)
         } else if self.is_foreign_item(def_id) {
-            self.mk_imm_ptr(static_ty)
+            Ty::new_imm_ptr(self, static_ty)
         } else {
-            self.mk_imm_ref(self.lifetimes.re_erased, static_ty)
+            Ty::new_imm_ref(self, self.lifetimes.re_erased, static_ty)
         }
     }
 
@@ -854,7 +854,7 @@ impl<'tcx> OpaqueTypeExpander<'tcx> {
                         let hidden_ty = bty.subst(self.tcx, substs);
                         self.fold_ty(hidden_ty);
                     }
-                    let expanded_ty = self.tcx.mk_generator_witness_mir(def_id, substs);
+                    let expanded_ty = Ty::new_generator_witness_mir(self.tcx, def_id, substs);
                     self.expanded_cache.insert((def_id, substs), expanded_ty);
                     expanded_ty
                 }
@@ -1306,7 +1306,7 @@ pub fn needs_drop_components<'tcx>(
         ty::Array(elem_ty, size) => {
             match needs_drop_components(*elem_ty, target_layout) {
                 Ok(v) if v.is_empty() => Ok(v),
-                res => match size.kind().try_to_bits(target_layout.pointer_size) {
+                res => match size.try_to_bits(target_layout.pointer_size) {
                     // Arrays of size zero don't need drop, even if their element
                     // type does.
                     Some(0) => Ok(SmallVec::new()),
diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs
index 8758cd04d67..b0961d91787 100644
--- a/compiler/rustc_middle/src/values.rs
+++ b/compiler/rustc_middle/src/values.rs
@@ -16,7 +16,7 @@ impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for Ty<'_> {
     fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &[QueryInfo<DepKind>]) -> Self {
         // SAFETY: This is never called when `Self` is not `Ty<'tcx>`.
         // FIXME: Represent the above fact in the trait system somehow.
-        unsafe { std::mem::transmute::<Ty<'tcx>, Ty<'_>>(tcx.ty_error_misc()) }
+        unsafe { std::mem::transmute::<Ty<'tcx>, Ty<'_>>(Ty::new_misc_error(tcx)) }
     }
 }
 
@@ -34,7 +34,7 @@ impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for ty::SymbolName<'_> {
 
 impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for ty::Binder<'_, ty::FnSig<'_>> {
     fn from_cycle_error(tcx: TyCtxt<'tcx>, stack: &[QueryInfo<DepKind>]) -> Self {
-        let err = tcx.ty_error_misc();
+        let err = Ty::new_misc_error(tcx);
 
         let arity = if let Some(frame) = stack.get(0)
             && frame.query.dep_kind == DepKind::fn_sig
diff --git a/compiler/rustc_mir_build/src/build/expr/as_constant.rs b/compiler/rustc_mir_build/src/build/expr/as_constant.rs
index 73d5eb62750..3fe751ae0a5 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_constant.rs
+++ b/compiler/rustc_mir_build/src/build/expr/as_constant.rs
@@ -52,7 +52,7 @@ pub fn as_constant_inner<'tcx>(
                 match lit_to_mir_constant(tcx, LitToConstInput { lit: &lit.node, ty, neg }) {
                     Ok(c) => c,
                     Err(LitToConstError::Reported(guar)) => {
-                        ConstantKind::Ty(tcx.const_error(ty, guar))
+                        ConstantKind::Ty(ty::Const::new_error(tcx, guar, ty))
                     }
                     Err(LitToConstError::TypeError) => {
                         bug!("encountered type error in `lit_to_mir_constant`")
@@ -84,7 +84,7 @@ pub fn as_constant_inner<'tcx>(
             Constant { user_ty, span, literal }
         }
         ExprKind::ConstParam { param, def_id: _ } => {
-            let const_param = tcx.mk_const(ty::ConstKind::Param(param), expr.ty);
+            let const_param = ty::Const::new_param(tcx, param, expr.ty);
             let literal = ConstantKind::Ty(const_param);
 
             Constant { user_ty: None, span, literal }
diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs
index 783f6e2085c..ac2d099be50 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_place.rs
+++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs
@@ -683,7 +683,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     ProjectionElem::Deref => {
                         let fake_borrow_deref_ty = base_place.ty(&self.local_decls, tcx).ty;
                         let fake_borrow_ty =
-                            tcx.mk_imm_ref(tcx.lifetimes.re_erased, fake_borrow_deref_ty);
+                            Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, fake_borrow_deref_ty);
                         let fake_borrow_temp =
                             self.local_decls.push(LocalDecl::new(fake_borrow_ty, expr_span));
                         let projection = tcx.mk_place_elems(&base_place.projection);
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 27b1b58d2e9..5c886ac4d9b 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs
+++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs
@@ -162,7 +162,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     [],
                     expr_span,
                 );
-                let storage = this.temp(tcx.mk_mut_ptr(tcx.types.u8), expr_span);
+                let storage = this.temp(Ty::new_mut_ptr(tcx, tcx.types.u8), expr_span);
                 let success = this.cfg.start_new_block();
                 this.cfg.terminate(
                     block,
@@ -564,7 +564,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         let bool_ty = self.tcx.types.bool;
         let rvalue = match op {
             BinOp::Add | BinOp::Sub | BinOp::Mul if self.check_overflow && ty.is_integral() => {
-                let result_tup = self.tcx.mk_tup(&[ty, bool_ty]);
+                let result_tup = Ty::new_tup(self.tcx, &[ty, bool_ty]);
                 let result_value = self.temp(result_tup, span);
 
                 self.cfg.push_assign(
@@ -598,7 +598,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 let (unsigned_rhs, unsigned_ty) = match rhs_ty.kind() {
                     ty::Uint(_) => (rhs.to_copy(), rhs_ty),
                     ty::Int(int_width) => {
-                        let uint_ty = self.tcx.mk_mach_uint(int_width.to_unsigned());
+                        let uint_ty = Ty::new_uint(self.tcx, int_width.to_unsigned());
                         let rhs_temp = self.temp(uint_ty, span);
                         self.cfg.push_assign(
                             block,
diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs
index 6df06df5c60..10770213c9a 100644
--- a/compiler/rustc_mir_build/src/build/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/build/matches/mod.rs
@@ -1751,7 +1751,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     projection: tcx.mk_place_elems(matched_place_ref.projection),
                 };
                 let fake_borrow_deref_ty = matched_place.ty(&self.local_decls, tcx).ty;
-                let fake_borrow_ty = tcx.mk_imm_ref(tcx.lifetimes.re_erased, fake_borrow_deref_ty);
+                let fake_borrow_ty =
+                    Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, fake_borrow_deref_ty);
                 let mut fake_borrow_temp = LocalDecl::new(fake_borrow_ty, temp_span);
                 fake_borrow_temp.internal = self.local_decls[matched_place.local].internal;
                 fake_borrow_temp.local_info = ClearCrossCrate::Set(Box::new(LocalInfo::FakeBorrow));
@@ -2250,7 +2251,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 // This variable isn't mutated but has a name, so has to be
                 // immutable to avoid the unused mut lint.
                 mutability: Mutability::Not,
-                ty: tcx.mk_imm_ref(tcx.lifetimes.re_erased, var_ty),
+                ty: Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, var_ty),
                 user_ty: None,
                 source_info,
                 internal: false,
diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs
index f431023f2b6..3a2c506bb98 100644
--- a/compiler/rustc_mir_build/src/build/matches/test.rs
+++ b/compiler/rustc_mir_build/src/build/matches/test.rs
@@ -244,8 +244,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                         bug!("matching on `String` went through without enabling string_deref_patterns");
                     }
                     let re_erased = tcx.lifetimes.re_erased;
-                    let ref_string = self.temp(tcx.mk_imm_ref(re_erased, ty), test.span);
-                    let ref_str_ty = tcx.mk_imm_ref(re_erased, tcx.types.str_);
+                    let ref_string = self.temp(Ty::new_imm_ref(tcx,re_erased, ty), test.span);
+                    let ref_str_ty = Ty::new_imm_ref(tcx,re_erased, tcx.types.str_);
                     let ref_str = self.temp(ref_str_ty, test.span);
                     let deref = tcx.require_lang_item(LangItem::Deref, None);
                     let method = trait_method(tcx, deref, sym::deref, [ty]);
@@ -415,7 +415,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             (Some((region, elem_ty, _)), _) | (None, Some((region, elem_ty, _))) => {
                 let tcx = self.tcx;
                 // make both a slice
-                ty = tcx.mk_imm_ref(*region, tcx.mk_slice(*elem_ty));
+                ty = Ty::new_imm_ref(tcx, *region, Ty::new_slice(tcx, *elem_ty));
                 if opt_ref_ty.is_some() {
                     let temp = self.temp(ty, source_info.span);
                     self.cfg.push_assign(
@@ -449,7 +449,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 // non_scalar_compare called on non-reference type
                 let temp = self.temp(ty, source_info.span);
                 self.cfg.push_assign(block, source_info, temp, Rvalue::Use(expect));
-                let ref_ty = self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, ty);
+                let ref_ty = Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, ty);
                 let ref_temp = self.temp(ref_ty, source_info.span);
 
                 self.cfg.push_assign(
@@ -871,7 +871,7 @@ fn trait_method<'tcx>(
         .find(|item| item.kind == ty::AssocKind::Fn)
         .expect("trait method not found");
 
-    let method_ty = tcx.mk_fn_def(item.def_id, substs);
+    let method_ty = Ty::new_fn_def(tcx, item.def_id, substs);
 
     ConstantKind::zero_sized(method_ty)
 }
diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs
index 49cbe3e2c9b..d828e71c7ac 100644
--- a/compiler/rustc_mir_build/src/build/mod.rs
+++ b/compiler/rustc_mir_build/src/build/mod.rs
@@ -614,7 +614,7 @@ fn construct_error(tcx: TyCtxt<'_>, def: LocalDefId, err: ErrorGuaranteed) -> Bo
     let generator_kind = tcx.generator_kind(def);
     let body_owner_kind = tcx.hir().body_owner_kind(def);
 
-    let ty = tcx.ty_error(err);
+    let ty = Ty::new_error(tcx, err);
     let num_params = match body_owner_kind {
         hir::BodyOwnerKind::Fn => tcx.fn_sig(def).skip_binder().inputs().skip_binder().len(),
         hir::BodyOwnerKind::Closure => {
@@ -942,7 +942,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         match self.unit_temp {
             Some(tmp) => tmp,
             None => {
-                let ty = self.tcx.mk_unit();
+                let ty = Ty::new_unit(self.tcx);
                 let fn_span = self.fn_span;
                 let tmp = self.temp(ty, fn_span);
                 self.unit_temp = Some(tmp);
diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs
index 7c0fbc6f81c..72374102c8c 100644
--- a/compiler/rustc_mir_build/src/build/scope.rs
+++ b/compiler/rustc_mir_build/src/build/scope.rs
@@ -91,6 +91,7 @@ use rustc_middle::middle::region;
 use rustc_middle::mir::*;
 use rustc_middle::thir::{Expr, LintLevel};
 
+use rustc_middle::ty::Ty;
 use rustc_span::{Span, DUMMY_SP};
 
 #[derive(Debug)]
@@ -724,7 +725,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
     // Add a dummy `Assign` statement to the CFG, with the span for the source code's `continue`
     // statement.
     fn add_dummy_assignment(&mut self, span: Span, block: BasicBlock, source_info: SourceInfo) {
-        let local_decl = LocalDecl::new(self.tcx.mk_unit(), span).internal();
+        let local_decl = LocalDecl::new(Ty::new_unit(self.tcx), span).internal();
         let temp_place = Place::from(self.local_decls.push(local_decl));
         self.cfg.push_assign_unit(block, source_info, temp_place, self.tcx);
     }
diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs
index a7be8e3c903..fbb74650faa 100644
--- a/compiler/rustc_mir_build/src/thir/constant.rs
+++ b/compiler/rustc_mir_build/src/thir/constant.rs
@@ -79,5 +79,5 @@ pub(crate) fn lit_to_const<'tcx>(
         _ => return Err(LitToConstError::TypeError),
     };
 
-    Ok(tcx.mk_const(valtree, ty))
+    Ok(ty::Const::new_value(tcx, valtree, ty))
 }
diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs
index 791c10c1748..a5c141b71f6 100644
--- a/compiler/rustc_mir_build/src/thir/cx/expr.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs
@@ -143,9 +143,11 @@ impl<'tcx> Cx<'tcx> {
 
                 expr = Expr {
                     temp_lifetime,
-                    ty: self
-                        .tcx
-                        .mk_ref(deref.region, ty::TypeAndMut { ty: expr.ty, mutbl: deref.mutbl }),
+                    ty: Ty::new_ref(
+                        self.tcx,
+                        deref.region,
+                        ty::TypeAndMut { ty: expr.ty, mutbl: deref.mutbl },
+                    ),
                     span,
                     kind: ExprKind::Borrow {
                         borrow_kind: deref.mutbl.to_borrow_kind(),
@@ -308,7 +310,7 @@ impl<'tcx> Cx<'tcx> {
 
                     let arg_tys = args.iter().map(|e| self.typeck_results().expr_ty_adjusted(e));
                     let tupled_args = Expr {
-                        ty: tcx.mk_tup_from_iter(arg_tys),
+                        ty: Ty::new_tup_from_iter(tcx, arg_tys),
                         temp_lifetime,
                         span: expr.span,
                         kind: ExprKind::Tuple { fields: self.mirror_exprs(args) },
@@ -855,7 +857,11 @@ impl<'tcx> Cx<'tcx> {
                 let user_ty = self.user_substs_applied_to_res(expr.hir_id, Res::Def(kind, def_id));
                 debug!("method_callee: user_ty={:?}", user_ty);
                 (
-                    self.tcx().mk_fn_def(def_id, self.typeck_results().node_substs(expr.hir_id)),
+                    Ty::new_fn_def(
+                        self.tcx(),
+                        def_id,
+                        self.typeck_results().node_substs(expr.hir_id),
+                    ),
                     user_ty,
                 )
             }
@@ -1008,7 +1014,7 @@ impl<'tcx> Cx<'tcx> {
         let ty::Ref(region, _, mutbl) = *self.thir[args[0]].ty.kind() else {
             span_bug!(span, "overloaded_place: receiver is not a reference");
         };
-        let ref_ty = self.tcx.mk_ref(region, ty::TypeAndMut { ty: place_ty, mutbl });
+        let ref_ty = Ty::new_ref(self.tcx, region, ty::TypeAndMut { ty: place_ty, mutbl });
 
         // construct the complete expression `foo()` for the overloaded call,
         // which will yield the &T type
diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs
index d00fb754c64..e6a98d1aab0 100644
--- a/compiler/rustc_mir_build/src/thir/cx/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs
@@ -15,7 +15,7 @@ use rustc_hir::HirId;
 use rustc_hir::Node;
 use rustc_middle::middle::region;
 use rustc_middle::thir::*;
-use rustc_middle::ty::{self, RvalueScopes, TyCtxt};
+use rustc_middle::ty::{self, RvalueScopes, Ty, TyCtxt};
 use rustc_span::Span;
 
 pub(crate) fn thir_body(
@@ -40,7 +40,7 @@ pub(crate) fn thir_body(
         // It will always be `()` in this case.
         if tcx.def_kind(owner_def) == DefKind::Generator && body.params.is_empty() {
             cx.thir.params.push(Param {
-                ty: tcx.mk_unit(),
+                ty: Ty::new_unit(tcx),
                 pat: None,
                 ty_span: None,
                 self_kind: None,
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 a2e00d3bfc5..050b01294b4 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
@@ -380,7 +380,9 @@ impl<'tcx> ConstToPat<'tcx> {
             ty::Ref(_, pointee_ty, ..) => match *pointee_ty.kind() {
                 // `&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)) },
+                ty::Str => PatKind::Constant {
+                    value: mir::ConstantKind::Ty(ty::Const::new_value(tcx, cv, ty)),
+                },
                 // 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
@@ -427,7 +429,7 @@ impl<'tcx> ConstToPat<'tcx> {
                         // arrays.
                         let pointee_ty = match *pointee_ty.kind() {
                             ty::Array(elem_ty, _) if self.treat_byte_string_as_slice => {
-                                tcx.mk_slice(elem_ty)
+                                Ty::new_slice(tcx, elem_ty)
                             }
                             _ => *pointee_ty,
                         };
@@ -438,9 +440,9 @@ impl<'tcx> ConstToPat<'tcx> {
                     }
                 }
             },
-            ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::FnDef(..) => {
-                PatKind::Constant { value: mir::ConstantKind::Ty(tcx.mk_const(cv, ty)) }
-            }
+            ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::FnDef(..) => PatKind::Constant {
+                value: mir::ConstantKind::Ty(ty::Const::new_value(tcx, cv, ty)),
+            },
             ty::FnPtr(..) | ty::RawPtr(..) => unreachable!(),
             _ => {
                 self.saw_const_match_error.set(true);
diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
index c30c4b65939..60099592784 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -525,7 +525,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
             .tcx
             .const_eval_global_id_for_typeck(param_env_reveal_all, cid, Some(span))
             .map(|val| match val {
-                Some(valtree) => mir::ConstantKind::Ty(self.tcx.mk_const(valtree, ty)),
+                Some(valtree) => mir::ConstantKind::Ty(ty::Const::new_value(self.tcx, valtree, ty)),
                 None => mir::ConstantKind::Val(
                     self.tcx
                         .const_eval_global_id(param_env_reveal_all, cid, Some(span))
@@ -631,7 +631,13 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
         if let Ok(Some(valtree)) =
             self.tcx.const_eval_resolve_for_typeck(self.param_env, ct, Some(span))
         {
-            self.const_to_pat(ConstantKind::Ty(self.tcx.mk_const(valtree, ty)), id, span, None).kind
+            self.const_to_pat(
+                ConstantKind::Ty(ty::Const::new_value(self.tcx, valtree, ty)),
+                id,
+                span,
+                None,
+            )
+            .kind
         } else {
             // If that fails, convert it to an opaque constant pattern.
             match tcx.const_eval_resolve(self.param_env, uneval, None) {
diff --git a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs
index c0102b15a16..0540a5e943b 100644
--- a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs
+++ b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs
@@ -423,7 +423,7 @@ where
         let unique_ty = adt.non_enum_variant().fields[FieldIdx::new(0)].ty(self.tcx(), substs);
         let unique_variant = unique_ty.ty_adt_def().unwrap().non_enum_variant();
         let nonnull_ty = unique_variant.fields[FieldIdx::from_u32(0)].ty(self.tcx(), substs);
-        let ptr_ty = self.tcx().mk_imm_ptr(substs[0].expect_ty());
+        let ptr_ty = Ty::new_imm_ptr(self.tcx(), substs[0].expect_ty());
 
         let unique_place = self.tcx().mk_place_field(self.place, FieldIdx::new(0), unique_ty);
         let nonnull_place = self.tcx().mk_place_field(unique_place, FieldIdx::new(0), nonnull_ty);
@@ -628,10 +628,13 @@ where
         let drop_fn = tcx.associated_item_def_ids(drop_trait)[0];
         let ty = self.place_ty(self.place);
 
-        let ref_ty =
-            tcx.mk_ref(tcx.lifetimes.re_erased, ty::TypeAndMut { ty, mutbl: hir::Mutability::Mut });
+        let ref_ty = Ty::new_ref(
+            tcx,
+            tcx.lifetimes.re_erased,
+            ty::TypeAndMut { ty, mutbl: hir::Mutability::Mut },
+        );
         let ref_place = self.new_temp(ref_ty);
-        let unit_temp = Place::from(self.new_temp(tcx.mk_unit()));
+        let unit_temp = Place::from(self.new_temp(Ty::new_unit(tcx)));
 
         let result = BasicBlockData {
             statements: vec![self.assign(
@@ -693,7 +696,7 @@ where
         let move_ = |place: Place<'tcx>| Operand::Move(place);
         let tcx = self.tcx();
 
-        let ptr_ty = tcx.mk_ptr(ty::TypeAndMut { ty: ety, mutbl: hir::Mutability::Mut });
+        let ptr_ty = Ty::new_ptr(tcx, ty::TypeAndMut { ty: ety, mutbl: hir::Mutability::Mut });
         let ptr = Place::from(self.new_temp(ptr_ty));
         let can_go = Place::from(self.new_temp(tcx.types.bool));
         let one = self.constant_usize(1);
diff --git a/compiler/rustc_mir_transform/src/check_alignment.rs b/compiler/rustc_mir_transform/src/check_alignment.rs
index 05c54ab3097..4892ace53e3 100644
--- a/compiler/rustc_mir_transform/src/check_alignment.rs
+++ b/compiler/rustc_mir_transform/src/check_alignment.rs
@@ -155,7 +155,7 @@ fn insert_alignment_check<'tcx>(
     new_block: BasicBlock,
 ) {
     // Cast the pointer to a *const ()
-    let const_raw_ptr = tcx.mk_ptr(TypeAndMut { ty: tcx.types.unit, mutbl: Mutability::Not });
+    let const_raw_ptr = Ty::new_ptr(tcx, TypeAndMut { ty: tcx.types.unit, mutbl: Mutability::Not });
     let rvalue = Rvalue::Cast(CastKind::PtrToPtr, Operand::Copy(pointer), const_raw_ptr);
     let thin_ptr = local_decls.push(LocalDecl::with_source_info(const_raw_ptr, source_info)).into();
     block_data
diff --git a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
index f31653caa49..cc0d7d51b60 100644
--- a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
+++ b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
@@ -21,7 +21,7 @@ pub fn build_ptr_tys<'tcx>(
     let substs = tcx.mk_substs(&[pointee.into()]);
     let unique_ty = tcx.type_of(unique_did).subst(tcx, substs);
     let nonnull_ty = tcx.type_of(nonnull_did).subst(tcx, substs);
-    let ptr_ty = tcx.mk_imm_ptr(pointee);
+    let ptr_ty = Ty::new_imm_ptr(tcx, pointee);
 
     (unique_ty, nonnull_ty, ptr_ty)
 }
diff --git a/compiler/rustc_mir_transform/src/generator.rs b/compiler/rustc_mir_transform/src/generator.rs
index 029fb2e9ba0..264bc61f1b3 100644
--- a/compiler/rustc_mir_transform/src/generator.rs
+++ b/compiler/rustc_mir_transform/src/generator.rs
@@ -413,8 +413,11 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> {
 fn make_generator_state_argument_indirect<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
     let gen_ty = body.local_decls.raw[1].ty;
 
-    let ref_gen_ty =
-        tcx.mk_ref(tcx.lifetimes.re_erased, ty::TypeAndMut { ty: gen_ty, mutbl: Mutability::Mut });
+    let ref_gen_ty = Ty::new_ref(
+        tcx,
+        tcx.lifetimes.re_erased,
+        ty::TypeAndMut { ty: gen_ty, mutbl: Mutability::Mut },
+    );
 
     // Replace the by value generator argument
     body.local_decls.raw[1].ty = ref_gen_ty;
@@ -429,7 +432,7 @@ fn make_generator_state_argument_pinned<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body
     let pin_did = tcx.require_lang_item(LangItem::Pin, Some(body.span));
     let pin_adt_ref = tcx.adt_def(pin_did);
     let substs = tcx.mk_substs(&[ref_gen_ty.into()]);
-    let pin_ref_gen_ty = tcx.mk_adt(pin_adt_ref, substs);
+    let pin_ref_gen_ty = Ty::new_adt(tcx, pin_adt_ref, substs);
 
     // Replace the by ref generator argument
     body.local_decls.raw[1].ty = pin_ref_gen_ty;
@@ -481,7 +484,7 @@ fn replace_local<'tcx>(
 /// still using the `ResumeTy` indirection for the time being, and that indirection
 /// is removed here. After this transform, the generator body only knows about `&mut Context<'_>`.
 fn transform_async_context<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
-    let context_mut_ref = tcx.mk_task_context();
+    let context_mut_ref = Ty::new_task_context(tcx);
 
     // replace the type of the `resume` argument
     replace_resume_ty_local(tcx, body, Local::new(2), context_mut_ref);
@@ -1130,13 +1133,13 @@ fn create_generator_drop_shim<'tcx>(
     }
 
     // Replace the return variable
-    body.local_decls[RETURN_PLACE] = LocalDecl::with_source_info(tcx.mk_unit(), source_info);
+    body.local_decls[RETURN_PLACE] = LocalDecl::with_source_info(Ty::new_unit(tcx), source_info);
 
     make_generator_state_argument_indirect(tcx, &mut body);
 
     // Change the generator argument from &mut to *mut
     body.local_decls[SELF_ARG] = LocalDecl::with_source_info(
-        tcx.mk_ptr(ty::TypeAndMut { ty: gen_ty, mutbl: hir::Mutability::Mut }),
+        Ty::new_ptr(tcx, ty::TypeAndMut { ty: gen_ty, mutbl: hir::Mutability::Mut }),
         source_info,
     );
 
@@ -1493,7 +1496,7 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
             let state_substs = tcx.mk_substs(&[yield_ty.into(), body.return_ty().into()]);
             (state_adt_ref, state_substs)
         };
-        let ret_ty = tcx.mk_adt(state_adt_ref, state_substs);
+        let ret_ty = Ty::new_adt(tcx, state_adt_ref, state_substs);
 
         // We rename RETURN_PLACE which has type mir.return_ty to new_ret_local
         // RETURN_PLACE then is a fresh unused local with type ret_ty.
@@ -1509,8 +1512,11 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
         // case there is no `Assign` to it that the transform can turn into a store to the generator
         // state. After the yield the slot in the generator state would then be uninitialized.
         let resume_local = Local::new(2);
-        let resume_ty =
-            if is_async_kind { tcx.mk_task_context() } else { body.local_decls[resume_local].ty };
+        let resume_ty = if is_async_kind {
+            Ty::new_task_context(tcx)
+        } else {
+            body.local_decls[resume_local].ty
+        };
         let new_resume_local = replace_local(resume_local, resume_ty, body, tcx);
 
         // When first entering the generator, move the resume argument into its new local.
diff --git a/compiler/rustc_mir_transform/src/large_enums.rs b/compiler/rustc_mir_transform/src/large_enums.rs
index 430a6f6cef5..8ed4706e172 100644
--- a/compiler/rustc_mir_transform/src/large_enums.rs
+++ b/compiler/rustc_mir_transform/src/large_enums.rs
@@ -141,7 +141,7 @@ impl EnumSizeOpt {
                         self.candidate(tcx, param_env, ty, &mut alloc_cache)?;
                     let alloc = tcx.global_alloc(alloc_id).unwrap_memory();
 
-                    let tmp_ty = tcx.mk_array(tcx.types.usize, num_variants as u64);
+                    let tmp_ty = Ty::new_array(tcx, tcx.types.usize, num_variants as u64);
 
                     let size_array_local = local_decls.push(LocalDecl::new(tmp_ty, span));
                     let store_live = Statement {
@@ -208,8 +208,9 @@ impl EnumSizeOpt {
                         ))),
                     };
 
-                    let dst =
-                        Place::from(local_decls.push(LocalDecl::new(tcx.mk_mut_ptr(ty), span)));
+                    let dst = Place::from(
+                        local_decls.push(LocalDecl::new(Ty::new_mut_ptr(tcx, ty), span)),
+                    );
 
                     let dst_ptr = Statement {
                         source_info,
@@ -219,7 +220,7 @@ impl EnumSizeOpt {
                         ))),
                     };
 
-                    let dst_cast_ty = tcx.mk_mut_ptr(tcx.types.u8);
+                    let dst_cast_ty = Ty::new_mut_ptr(tcx, tcx.types.u8);
                     let dst_cast_place =
                         Place::from(local_decls.push(LocalDecl::new(dst_cast_ty, span)));
 
@@ -231,8 +232,9 @@ impl EnumSizeOpt {
                         ))),
                     };
 
-                    let src =
-                        Place::from(local_decls.push(LocalDecl::new(tcx.mk_imm_ptr(ty), span)));
+                    let src = Place::from(
+                        local_decls.push(LocalDecl::new(Ty::new_imm_ptr(tcx, ty), span)),
+                    );
 
                     let src_ptr = Statement {
                         source_info,
@@ -242,7 +244,7 @@ impl EnumSizeOpt {
                         ))),
                     };
 
-                    let src_cast_ty = tcx.mk_imm_ptr(tcx.types.u8);
+                    let src_cast_ty = Ty::new_imm_ptr(tcx, tcx.types.u8);
                     let src_cast_place =
                         Place::from(local_decls.push(LocalDecl::new(src_cast_ty, span)));
 
diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs
index 500595e9f50..b176db3c9e0 100644
--- a/compiler/rustc_mir_transform/src/shim.rs
+++ b/compiler/rustc_mir_transform/src/shim.rs
@@ -485,7 +485,7 @@ impl<'tcx> CloneShimBuilder<'tcx> {
         let tcx = self.tcx;
 
         // `func == Clone::clone(&ty) -> ty`
-        let func_ty = tcx.mk_fn_def(self.def_id, [ty]);
+        let func_ty = Ty::new_fn_def(tcx, self.def_id, [ty]);
         let func = Operand::Constant(Box::new(Constant {
             span: self.span,
             user_ty: None,
@@ -494,7 +494,11 @@ impl<'tcx> CloneShimBuilder<'tcx> {
 
         let ref_loc = self.make_place(
             Mutability::Not,
-            tcx.mk_ref(tcx.lifetimes.re_erased, ty::TypeAndMut { ty, mutbl: hir::Mutability::Not }),
+            Ty::new_ref(
+                tcx,
+                tcx.lifetimes.re_erased,
+                ty::TypeAndMut { ty, mutbl: hir::Mutability::Not },
+            ),
         );
 
         // `let ref_loc: &ty = &src;`
@@ -644,7 +648,7 @@ fn build_call_shim<'tcx>(
         let untuple_args = sig.inputs();
 
         // Create substitutions for the `Self` and `Args` generic parameters of the shim body.
-        let arg_tup = tcx.mk_tup(untuple_args);
+        let arg_tup = Ty::new_tup(tcx, untuple_args);
 
         (Some([ty.into(), arg_tup.into()]), Some(untuple_args))
     } else {
@@ -680,9 +684,9 @@ fn build_call_shim<'tcx>(
         *self_arg = match rcvr_adjustment.unwrap() {
             Adjustment::Identity => fnty,
             Adjustment::Deref { source } => match source {
-                DerefSource::ImmRef => tcx.mk_imm_ref(tcx.lifetimes.re_erased, fnty),
-                DerefSource::MutRef => tcx.mk_mut_ref(tcx.lifetimes.re_erased, fnty),
-                DerefSource::MutPtr => tcx.mk_mut_ptr(fnty),
+                DerefSource::ImmRef => Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, fnty),
+                DerefSource::MutRef => Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, fnty),
+                DerefSource::MutPtr => Ty::new_mut_ptr(tcx, fnty),
             },
             Adjustment::RefMut => bug!("`RefMut` is never used with indirect calls: {instance:?}"),
         };
@@ -696,7 +700,7 @@ fn build_call_shim<'tcx>(
         let mut inputs_and_output = sig.inputs_and_output.to_vec();
         let self_arg = &mut inputs_and_output[0];
         debug_assert!(tcx.generics_of(def_id).has_self && *self_arg == tcx.types.self_param);
-        *self_arg = tcx.mk_mut_ptr(*self_arg);
+        *self_arg = Ty::new_mut_ptr(tcx, *self_arg);
         sig.inputs_and_output = tcx.mk_type_list(&inputs_and_output);
     }
 
@@ -720,7 +724,8 @@ fn build_call_shim<'tcx>(
             // let rcvr = &mut rcvr;
             let ref_rcvr = local_decls.push(
                 LocalDecl::new(
-                    tcx.mk_ref(
+                    Ty::new_ref(
+                        tcx,
                         tcx.lifetimes.re_erased,
                         ty::TypeAndMut { ty: sig.inputs()[0], mutbl: hir::Mutability::Mut },
                     ),
@@ -946,7 +951,7 @@ fn build_fn_ptr_addr_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'t
     let rvalue = Rvalue::Cast(
         CastKind::FnPtrToPtr,
         Operand::Move(Place::from(Local::new(1))),
-        tcx.mk_imm_ptr(tcx.types.unit),
+        Ty::new_imm_ptr(tcx, tcx.types.unit),
     );
     let stmt = Statement {
         source_info,
diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs
index e663f4486f7..f4535fbd58f 100644
--- a/compiler/rustc_monomorphize/src/partitioning.rs
+++ b/compiler/rustc_monomorphize/src/partitioning.rs
@@ -187,7 +187,13 @@ where
     }
 
     // Ensure CGUs are sorted by name, so that we get deterministic results.
-    assert!(codegen_units.is_sorted_by(|a, b| Some(a.name().as_str().cmp(b.name().as_str()))));
+    if !codegen_units.is_sorted_by(|a, b| Some(a.name().as_str().cmp(b.name().as_str()))) {
+        let mut names = String::new();
+        for cgu in codegen_units.iter() {
+            names += &format!("- {}\n", cgu.name());
+        }
+        bug!("unsorted CGUs:\n{names}");
+    }
 
     codegen_units
 }
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index 228eff1269f..0ce6a570d25 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -605,6 +605,22 @@ impl<'a> Parser<'a> {
             }
         }
 
+        if let TokenKind::Ident(prev, _) = &self.prev_token.kind
+          && let TokenKind::Ident(cur, _) = &self.token.kind
+        {
+                let concat = Symbol::intern(&format!("{}{}", prev, cur));
+                let ident = Ident::new(concat, DUMMY_SP);
+                if ident.is_used_keyword() || ident.is_reserved() || ident.is_raw_guess() {
+                    let span = self.prev_token.span.to(self.token.span);
+                    err.span_suggestion_verbose(
+                        span,
+                        format!("consider removing the space to spell keyword `{}`", concat),
+                        concat,
+                        Applicability::MachineApplicable,
+                    );
+                }
+        }
+
         // `pub` may be used for an item or `pub(crate)`
         if self.prev_token.is_ident_named(sym::public)
             && (self.token.can_begin_item()
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 3783ec41b7e..1470180dea7 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -2182,7 +2182,11 @@ impl<'a> Parser<'a> {
             // `extern ABI fn`
             || self.check_keyword_case(kw::Extern, case)
                 && self.look_ahead(1, |t| t.can_begin_literal_maybe_minus())
-                && self.look_ahead(2, |t| t.is_keyword_case(kw::Fn, case))
+                && (self.look_ahead(2, |t| t.is_keyword_case(kw::Fn, case)) ||
+                    // this branch is only for better diagnostic in later, `pub` is not allowed here
+                    (self.may_recover()
+                        && self.look_ahead(2, |t| t.is_keyword(kw::Pub))
+                        && self.look_ahead(3, |t| t.is_keyword_case(kw::Fn, case))))
     }
 
     /// Parses all the "front matter" (or "qualifiers") for a `fn` declaration,
diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs
index 055006373ef..4fcee9396ed 100644
--- a/compiler/rustc_privacy/src/lib.rs
+++ b/compiler/rustc_privacy/src/lib.rs
@@ -174,7 +174,6 @@ where
             }
             ty::ClauseKind::ConstEvaluatable(ct) => ct.visit_with(self),
             ty::ClauseKind::WellFormed(arg) => arg.visit_with(self),
-            ty::ClauseKind::TypeWellFormedFromEnv(_) => bug!("unexpected clause: {clause}"),
         }
     }
 
diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs
index b2bc33c7e0d..4adb4eb7475 100644
--- a/compiler/rustc_query_system/src/query/plumbing.rs
+++ b/compiler/rustc_query_system/src/query/plumbing.rs
@@ -126,18 +126,13 @@ where
 
 #[cold]
 #[inline(never)]
-fn mk_cycle<Q, Qcx>(
-    query: Q,
-    qcx: Qcx,
-    cycle_error: CycleError<Qcx::DepKind>,
-    handler: HandleCycleError,
-) -> Q::Value
+fn mk_cycle<Q, Qcx>(query: Q, qcx: Qcx, cycle_error: CycleError<Qcx::DepKind>) -> Q::Value
 where
     Q: QueryConfig<Qcx>,
     Qcx: QueryContext,
 {
     let error = report_cycle(qcx.dep_context().sess(), &cycle_error);
-    handle_cycle_error(query, qcx, &cycle_error, error, handler)
+    handle_cycle_error(query, qcx, &cycle_error, error)
 }
 
 fn handle_cycle_error<Q, Qcx>(
@@ -145,14 +140,13 @@ fn handle_cycle_error<Q, Qcx>(
     qcx: Qcx,
     cycle_error: &CycleError<Qcx::DepKind>,
     mut error: DiagnosticBuilder<'_, ErrorGuaranteed>,
-    handler: HandleCycleError,
 ) -> Q::Value
 where
     Q: QueryConfig<Qcx>,
     Qcx: QueryContext,
 {
     use HandleCycleError::*;
-    match handler {
+    match query.handle_cycle_error() {
         Error => {
             error.emit();
             query.value_from_cycle_error(*qcx.dep_context(), &cycle_error.cycle)
@@ -277,7 +271,7 @@ where
         &qcx.current_query_job(),
         span,
     );
-    (mk_cycle(query, qcx, error, query.handle_cycle_error()), None)
+    (mk_cycle(query, qcx, error), None)
 }
 
 #[inline(always)]
@@ -314,7 +308,7 @@ where
 
             (v, Some(index))
         }
-        Err(cycle) => (mk_cycle(query, qcx, cycle, query.handle_cycle_error()), None),
+        Err(cycle) => (mk_cycle(query, qcx, cycle), None),
     }
 }
 
diff --git a/compiler/rustc_query_system/src/values.rs b/compiler/rustc_query_system/src/values.rs
index b6e2cfa3dca..ce551078cc0 100644
--- a/compiler/rustc_query_system/src/values.rs
+++ b/compiler/rustc_query_system/src/values.rs
@@ -6,10 +6,13 @@ pub trait Value<Tcx: DepContext, D: DepKind>: Sized {
 }
 
 impl<Tcx: DepContext, T, D: DepKind> Value<Tcx, D> for T {
-    default fn from_cycle_error(tcx: Tcx, _: &[QueryInfo<D>]) -> T {
+    default fn from_cycle_error(tcx: Tcx, cycle: &[QueryInfo<D>]) -> T {
         tcx.sess().abort_if_errors();
         // Ideally we would use `bug!` here. But bug! is only defined in rustc_middle, and it's
         // non-trivial to define it earlier.
-        panic!("Value::from_cycle_error called without errors");
+        panic!(
+            "<{} as Value>::from_cycle_error called without errors: {cycle:#?}",
+            std::any::type_name::<T>()
+        );
     }
 }
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index 72777733345..e6ceedddfa1 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -6,10 +6,10 @@
 //! Imports are also considered items and placed into modules here, but not resolved yet.
 
 use crate::def_collector::collect_definitions;
-use crate::imports::{Import, ImportKind};
+use crate::imports::{ImportData, ImportKind};
 use crate::macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef};
 use crate::Namespace::{self, MacroNS, TypeNS, ValueNS};
-use crate::{errors, BindingKey, MacroData};
+use crate::{errors, BindingKey, MacroData, NameBindingData};
 use crate::{Determinacy, ExternPreludeEntry, Finalize, Module, ModuleKind, ModuleOrUniformRoot};
 use crate::{NameBinding, NameBindingKind, ParentScope, PathResult, PerNS, ResolutionError};
 use crate::{Resolver, ResolverArenas, Segment, ToNameBinding, VisResolutionError};
@@ -31,15 +31,14 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::Span;
 
 use std::cell::Cell;
-use std::ptr;
 
 type Res = def::Res<NodeId>;
 
 impl<'a, Id: Into<DefId>> ToNameBinding<'a>
     for (Module<'a>, ty::Visibility<Id>, Span, LocalExpnId)
 {
-    fn to_name_binding(self, arenas: &'a ResolverArenas<'a>) -> &'a NameBinding<'a> {
-        arenas.alloc_name_binding(NameBinding {
+    fn to_name_binding(self, arenas: &'a ResolverArenas<'a>) -> NameBinding<'a> {
+        arenas.alloc_name_binding(NameBindingData {
             kind: NameBindingKind::Module(self.0),
             ambiguity: None,
             vis: self.1.to_def_id(),
@@ -50,8 +49,8 @@ impl<'a, Id: Into<DefId>> ToNameBinding<'a>
 }
 
 impl<'a, Id: Into<DefId>> ToNameBinding<'a> for (Res, ty::Visibility<Id>, Span, LocalExpnId) {
-    fn to_name_binding(self, arenas: &'a ResolverArenas<'a>) -> &'a NameBinding<'a> {
-        arenas.alloc_name_binding(NameBinding {
+    fn to_name_binding(self, arenas: &'a ResolverArenas<'a>) -> NameBinding<'a> {
+        arenas.alloc_name_binding(NameBindingData {
             kind: NameBindingKind::Res(self.0),
             ambiguity: None,
             vis: self.1.to_def_id(),
@@ -71,7 +70,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         let binding = def.to_name_binding(self.arenas);
         let key = self.new_disambiguated_key(ident, ns);
         if let Err(old_binding) = self.try_define(parent, key, binding) {
-            self.report_conflict(parent, ident, ns, old_binding, &binding);
+            self.report_conflict(parent, ident, ns, old_binding, binding);
         }
     }
 
@@ -142,8 +141,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             Some(def_id) => self.macro_def_scope(def_id),
             None => expn_id
                 .as_local()
-                .and_then(|expn_id| self.ast_transform_scopes.get(&expn_id))
-                .unwrap_or(&self.graph_root),
+                .and_then(|expn_id| self.ast_transform_scopes.get(&expn_id).copied())
+                .unwrap_or(self.graph_root),
         }
     }
 
@@ -354,7 +353,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
         vis: ty::Visibility,
     ) {
         let current_module = self.parent_scope.module;
-        let import = self.r.arenas.alloc_import(Import {
+        let import = self.r.arenas.alloc_import(ImportData {
             kind,
             parent_scope: self.parent_scope,
             module_path,
@@ -378,7 +377,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
                     if !type_ns_only || ns == TypeNS {
                         let key = BindingKey::new(target, ns);
                         let mut resolution = this.resolution(current_module, key).borrow_mut();
-                        resolution.add_single_import(import);
+                        resolution.single_imports.insert(import);
                     }
                 });
             }
@@ -848,7 +847,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
             (used, Some(ModuleOrUniformRoot::Module(module)), binding)
         })
         .unwrap_or((true, None, self.r.dummy_binding));
-        let import = self.r.arenas.alloc_import(Import {
+        let import = self.r.arenas.alloc_import(ImportData {
             kind: ImportKind::ExternCrate { source: orig_name, target: ident, id: item.id },
             root_id: item.id,
             parent_scope: self.parent_scope,
@@ -864,7 +863,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
         });
         self.r.potentially_unused_imports.push(import);
         let imported_binding = self.r.import(binding, import);
-        if ptr::eq(parent, self.r.graph_root) {
+        if parent == self.r.graph_root {
             if let Some(entry) = self.r.extern_prelude.get(&ident.normalize_to_macros_2_0()) {
                 if expansion != LocalExpnId::ROOT
                     && orig_name.is_some()
@@ -996,7 +995,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
     fn add_macro_use_binding(
         &mut self,
         name: Symbol,
-        binding: &'a NameBinding<'a>,
+        binding: NameBinding<'a>,
         span: Span,
         allow_shadowing: bool,
     ) {
@@ -1058,7 +1057,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
         }
 
         let macro_use_import = |this: &Self, span| {
-            this.r.arenas.alloc_import(Import {
+            this.r.arenas.alloc_import(ImportData {
                 kind: ImportKind::MacroUse,
                 root_id: item.id,
                 parent_scope: this.parent_scope,
@@ -1228,7 +1227,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
             self.r.set_binding_parent_module(binding, parent_scope.module);
             self.r.all_macro_rules.insert(ident.name, res);
             if is_macro_export {
-                let import = self.r.arenas.alloc_import(Import {
+                let import = self.r.arenas.alloc_import(ImportData {
                     kind: ImportKind::MacroExport,
                     root_id: item.id,
                     parent_scope: self.parent_scope,
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index d9e4974626d..d3dcdfa4275 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -1,5 +1,3 @@
-use std::ptr;
-
 use rustc_ast::expand::StrippedCfgItem;
 use rustc_ast::ptr::P;
 use rustc_ast::visit::{self, Visitor};
@@ -182,13 +180,13 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         }
     }
 
-    pub(crate) fn report_conflict<'b>(
+    pub(crate) fn report_conflict(
         &mut self,
         parent: Module<'_>,
         ident: Ident,
         ns: Namespace,
-        new_binding: &NameBinding<'b>,
-        old_binding: &NameBinding<'b>,
+        new_binding: NameBinding<'a>,
+        old_binding: NameBinding<'a>,
     ) {
         // Error on the second of two conflicting names
         if old_binding.span.lo() > new_binding.span.lo() {
@@ -262,7 +260,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
 
         // See https://github.com/rust-lang/rust/issues/32354
         use NameBindingKind::Import;
-        let can_suggest = |binding: &NameBinding<'_>, import: &self::Import<'_>| {
+        let can_suggest = |binding: NameBinding<'_>, import: self::Import<'_>| {
             !binding.span.is_dummy()
                 && !matches!(import.kind, ImportKind::MacroUse | ImportKind::MacroExport)
         };
@@ -272,22 +270,22 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             (Import { import: new, .. }, Import { import: old, .. })
                 if {
                     (new.has_attributes || old.has_attributes)
-                        && can_suggest(old_binding, old)
-                        && can_suggest(new_binding, new)
+                        && can_suggest(old_binding, *old)
+                        && can_suggest(new_binding, *new)
                 } =>
             {
                 if old.has_attributes {
-                    Some((new, new_binding.span, true))
+                    Some((*new, new_binding.span, true))
                 } else {
-                    Some((old, old_binding.span, true))
+                    Some((*old, old_binding.span, true))
                 }
             }
             // Otherwise prioritize the new binding.
-            (Import { import, .. }, other) if can_suggest(new_binding, import) => {
-                Some((import, new_binding.span, other.is_import()))
+            (Import { import, .. }, other) if can_suggest(new_binding, *import) => {
+                Some((*import, new_binding.span, other.is_import()))
             }
-            (other, Import { import, .. }) if can_suggest(old_binding, import) => {
-                Some((import, old_binding.span, other.is_import()))
+            (other, Import { import, .. }) if can_suggest(old_binding, *import) => {
+                Some((*import, old_binding.span, other.is_import()))
             }
             _ => None,
         };
@@ -341,7 +339,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         &self,
         err: &mut Diagnostic,
         name: Symbol,
-        import: &Import<'_>,
+        import: Import<'_>,
         binding_span: Span,
     ) {
         let suggested_name = if name.as_str().chars().next().unwrap().is_uppercase() {
@@ -413,7 +411,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
     fn add_suggestion_for_duplicate_nested_use(
         &self,
         err: &mut Diagnostic,
-        import: &Import<'_>,
+        import: Import<'_>,
         binding_span: Span,
     ) {
         assert!(import.is_nested());
@@ -455,7 +453,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         &mut self,
         finalize: Option<Finalize>,
         path: &[Segment],
-        second_binding: Option<&NameBinding<'_>>,
+        second_binding: Option<NameBinding<'_>>,
     ) {
         let Some(Finalize { node_id, root_span, .. }) = finalize else {
             return;
@@ -1198,7 +1196,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                 // avoid suggesting anything with a hygienic name
                 if ident.name == lookup_ident.name
                     && ns == namespace
-                    && !ptr::eq(in_module, parent_scope.module)
+                    && in_module != parent_scope.module
                     && !ident.span.normalize_to_macros_2_0().from_expansion()
                 {
                     let res = name_binding.res();
@@ -1515,7 +1513,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         true
     }
 
-    fn binding_description(&self, b: &NameBinding<'_>, ident: Ident, from_prelude: bool) -> String {
+    fn binding_description(&self, b: NameBinding<'_>, ident: Ident, from_prelude: bool) -> String {
         let res = b.res();
         if b.span.is_dummy() || !self.tcx.sess.source_map().is_span_accessible(b.span) {
             // These already contain the "built-in" prefix or look bad with it.
@@ -1555,7 +1553,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         err.span_label(ident.span, "ambiguous name");
         err.note(format!("ambiguous because of {}", kind.descr()));
 
-        let mut could_refer_to = |b: &NameBinding<'_>, misc: AmbiguityErrorMisc, also: &str| {
+        let mut could_refer_to = |b: NameBinding<'_>, misc: AmbiguityErrorMisc, also: &str| {
             let what = self.binding_description(b, ident, misc == AmbiguityErrorMisc::FromPrelude);
             let note_msg = format!("`{ident}` could{also} refer to {what}");
 
@@ -1595,7 +1593,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
 
     /// If the binding refers to a tuple struct constructor with fields,
     /// returns the span of its fields.
-    fn ctor_fields_span(&self, binding: &NameBinding<'_>) -> Option<Span> {
+    fn ctor_fields_span(&self, binding: NameBinding<'_>) -> Option<Span> {
         if let NameBindingKind::Res(Res::Def(
             DefKind::Ctor(CtorOf::Struct, CtorKind::Fn),
             ctor_def_id,
@@ -1622,7 +1620,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             if ctor_fields_span.is_some() { plain_descr + " constructor" } else { plain_descr };
         let import_descr = nonimport_descr.clone() + " import";
         let get_descr =
-            |b: &NameBinding<'_>| if b.is_import() { &import_descr } else { &nonimport_descr };
+            |b: NameBinding<'_>| if b.is_import() { &import_descr } else { &nonimport_descr };
 
         // Print the primary message.
         let descr = get_descr(binding);
@@ -1702,7 +1700,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                 _ => None,
             };
 
-            let first = ptr::eq(binding, first_binding);
+            let first = binding == first_binding;
             let msg = format!(
                 "{and_refers_to}the {item} `{name}`{which} is defined here{dots}",
                 and_refers_to = if first { "" } else { "...and refers to " },
@@ -1732,7 +1730,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
     pub(crate) fn find_similarly_named_module_or_crate(
         &mut self,
         ident: Symbol,
-        current_module: &Module<'a>,
+        current_module: Module<'a>,
     ) -> Option<Symbol> {
         let mut candidates = self
             .extern_prelude
@@ -1742,7 +1740,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                 self.module_map
                     .iter()
                     .filter(|(_, module)| {
-                        current_module.is_ancestor_of(module) && !ptr::eq(current_module, *module)
+                        current_module.is_ancestor_of(**module) && current_module != **module
                     })
                     .flat_map(|(_, module)| module.kind.name()),
             )
@@ -1762,7 +1760,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         opt_ns: Option<Namespace>, // `None` indicates a module path in import
         parent_scope: &ParentScope<'a>,
         ribs: Option<&PerNS<Vec<Rib<'a>>>>,
-        ignore_binding: Option<&'a NameBinding<'a>>,
+        ignore_binding: Option<NameBinding<'a>>,
         module: Option<ModuleOrUniformRoot<'a>>,
         failed_segment_idx: usize,
         ident: Ident,
@@ -1945,7 +1943,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             }
 
             suggestion = suggestion.or_else(|| {
-                self.find_similarly_named_module_or_crate(ident.name, &parent_scope.module).map(
+                self.find_similarly_named_module_or_crate(ident.name, parent_scope.module).map(
                     |sugg| {
                         (
                             vec![(ident.span, sugg.to_string())],
@@ -2114,7 +2112,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
     /// ```
     pub(crate) fn check_for_module_export_macro(
         &mut self,
-        import: &'a Import<'a>,
+        import: Import<'a>,
         module: ModuleOrUniformRoot<'a>,
         ident: Ident,
     ) -> Option<(Option<Suggestion>, Option<String>)> {
@@ -2126,9 +2124,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             crate_module = parent;
         }
 
-        if ModuleOrUniformRoot::same_def(ModuleOrUniformRoot::Module(crate_module), module) {
-            // Don't make a suggestion if the import was already from the root of the
-            // crate.
+        if module == ModuleOrUniformRoot::Module(crate_module) {
+            // Don't make a suggestion if the import was already from the root of the crate.
             return None;
         }
 
diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs
index 4863c9f4790..eb210532f51 100644
--- a/compiler/rustc_resolve/src/effective_visibilities.rs
+++ b/compiler/rustc_resolve/src/effective_visibilities.rs
@@ -5,7 +5,6 @@ use rustc_ast::visit::Visitor;
 use rustc_ast::Crate;
 use rustc_ast::EnumDef;
 use rustc_data_structures::fx::FxHashSet;
-use rustc_data_structures::intern::Interned;
 use rustc_hir::def_id::LocalDefId;
 use rustc_hir::def_id::CRATE_DEF_ID;
 use rustc_middle::middle::privacy::Level;
@@ -13,12 +12,10 @@ use rustc_middle::middle::privacy::{EffectiveVisibilities, EffectiveVisibility};
 use rustc_middle::ty::Visibility;
 use std::mem;
 
-type ImportId<'a> = Interned<'a, NameBinding<'a>>;
-
 #[derive(Clone, Copy)]
 enum ParentId<'a> {
     Def(LocalDefId),
-    Import(ImportId<'a>),
+    Import(NameBinding<'a>),
 }
 
 impl ParentId<'_> {
@@ -36,7 +33,7 @@ pub(crate) struct EffectiveVisibilitiesVisitor<'r, 'a, 'tcx> {
     /// While walking import chains we need to track effective visibilities per-binding, and def id
     /// keys in `Resolver::effective_visibilities` are not enough for that, because multiple
     /// bindings can correspond to a single def id in imports. So we keep a separate table.
-    import_effective_visibilities: EffectiveVisibilities<ImportId<'a>>,
+    import_effective_visibilities: EffectiveVisibilities<NameBinding<'a>>,
     // It's possible to recalculate this at any point, but it's relatively expensive.
     current_private_vis: Visibility,
     changed: bool,
@@ -47,7 +44,7 @@ impl Resolver<'_, '_> {
         self.get_nearest_non_block_module(def_id.to_def_id()).nearest_parent_mod().expect_local()
     }
 
-    fn private_vis_import(&mut self, binding: ImportId<'_>) -> Visibility {
+    fn private_vis_import(&mut self, binding: NameBinding<'_>) -> Visibility {
         let NameBindingKind::Import { import, .. } = binding.kind else { unreachable!() };
         Visibility::Restricted(
             import
@@ -75,7 +72,7 @@ impl<'r, 'a, 'tcx> EffectiveVisibilitiesVisitor<'r, 'a, 'tcx> {
     pub(crate) fn compute_effective_visibilities<'c>(
         r: &'r mut Resolver<'a, 'tcx>,
         krate: &'c Crate,
-    ) -> FxHashSet<Interned<'a, NameBinding<'a>>> {
+    ) -> FxHashSet<NameBinding<'a>> {
         let mut visitor = EffectiveVisibilitiesVisitor {
             r,
             def_effective_visibilities: Default::default(),
@@ -133,8 +130,7 @@ impl<'r, 'a, 'tcx> EffectiveVisibilitiesVisitor<'r, 'a, 'tcx> {
                 // lint. For all bindings added to the table this way `is_ambiguity` returns true.
                 let mut parent_id = ParentId::Def(module_id);
                 while let NameBindingKind::Import { binding: nested_binding, .. } = binding.kind {
-                    let binding_id = ImportId::new_unchecked(binding);
-                    self.update_import(binding_id, parent_id);
+                    self.update_import(binding, parent_id);
 
                     if binding.ambiguity.is_some() {
                         // Stop at the root ambiguity, further bindings in the chain should not
@@ -143,7 +139,7 @@ impl<'r, 'a, 'tcx> EffectiveVisibilitiesVisitor<'r, 'a, 'tcx> {
                         break;
                     }
 
-                    parent_id = ParentId::Import(binding_id);
+                    parent_id = ParentId::Import(binding);
                     binding = nested_binding;
                 }
 
@@ -192,7 +188,7 @@ impl<'r, 'a, 'tcx> EffectiveVisibilitiesVisitor<'r, 'a, 'tcx> {
         }
     }
 
-    fn update_import(&mut self, binding: ImportId<'a>, parent_id: ParentId<'a>) {
+    fn update_import(&mut self, binding: NameBinding<'a>, parent_id: ParentId<'a>) {
         let nominal_vis = binding.vis.expect_local();
         let Some(cheap_private_vis) = self.may_update(nominal_vis, parent_id) else { return };
         let inherited_eff_vis = self.effective_vis_or_private(parent_id);
diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs
index 36f01676e7e..520fab1f0c8 100644
--- a/compiler/rustc_resolve/src/ident.rs
+++ b/compiler/rustc_resolve/src/ident.rs
@@ -11,8 +11,6 @@ use rustc_span::hygiene::{ExpnId, ExpnKind, LocalExpnId, MacroKind, SyntaxContex
 use rustc_span::symbol::{kw, Ident};
 use rustc_span::{Span, DUMMY_SP};
 
-use std::ptr;
-
 use crate::errors::{ParamKindInEnumDiscriminant, ParamKindInNonTrivialAnonConst};
 use crate::late::{
     ConstantHasGenerics, HasGenericParams, NoConstantGenericsReason, PathSource, Rib, RibKind,
@@ -20,7 +18,7 @@ use crate::late::{
 use crate::macros::{sub_namespace_match, MacroRulesScope};
 use crate::BindingKey;
 use crate::{errors, AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, Determinacy, Finalize};
-use crate::{Import, ImportKind, LexicalScopeBinding, Module, ModuleKind, ModuleOrUniformRoot};
+use crate::{ImportKind, LexicalScopeBinding, Module, ModuleKind, ModuleOrUniformRoot};
 use crate::{NameBinding, NameBindingKind, ParentScope, PathResult, PrivacyError, Res};
 use crate::{ResolutionError, Resolver, Scope, ScopeSet, Segment, ToNameBinding, Weak};
 
@@ -284,7 +282,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         parent_scope: &ParentScope<'a>,
         finalize: Option<Finalize>,
         ribs: &[Rib<'a>],
-        ignore_binding: Option<&'a NameBinding<'a>>,
+        ignore_binding: Option<NameBinding<'a>>,
     ) -> Option<LexicalScopeBinding<'a>> {
         assert!(ns == TypeNS || ns == ValueNS);
         let orig_ident = ident;
@@ -378,8 +376,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         parent_scope: &ParentScope<'a>,
         finalize: Option<Finalize>,
         force: bool,
-        ignore_binding: Option<&'a NameBinding<'a>>,
-    ) -> Result<&'a NameBinding<'a>, Determinacy> {
+        ignore_binding: Option<NameBinding<'a>>,
+    ) -> Result<NameBinding<'a>, Determinacy> {
         bitflags::bitflags! {
             struct Flags: u8 {
                 const MACRO_RULES          = 1 << 0;
@@ -415,7 +413,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         // }
         // So we have to save the innermost solution and continue searching in outer scopes
         // to detect potential ambiguities.
-        let mut innermost_result: Option<(&NameBinding<'_>, Flags)> = None;
+        let mut innermost_result: Option<(NameBinding<'_>, Flags)> = None;
         let mut determinacy = Determinacy::Determined;
 
         // Go through all the scopes and try to resolve the name.
@@ -538,7 +536,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                                         ),
                                     );
                                 }
-                                let misc_flags = if ptr::eq(module, this.graph_root) {
+                                let misc_flags = if module == this.graph_root {
                                     Flags::MISC_SUGGEST_CRATE
                                 } else if module.is_normal() {
                                     Flags::MISC_SUGGEST_SELF
@@ -717,7 +715,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         ident: Ident,
         ns: Namespace,
         parent_scope: &ParentScope<'a>,
-    ) -> Result<&'a NameBinding<'a>, Determinacy> {
+    ) -> Result<NameBinding<'a>, Determinacy> {
         self.resolve_ident_in_module_ext(module, ident, ns, parent_scope, None, None)
             .map_err(|(determinacy, _)| determinacy)
     }
@@ -730,8 +728,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         ns: Namespace,
         parent_scope: &ParentScope<'a>,
         finalize: Option<Finalize>,
-        ignore_binding: Option<&'a NameBinding<'a>>,
-    ) -> Result<&'a NameBinding<'a>, Determinacy> {
+        ignore_binding: Option<NameBinding<'a>>,
+    ) -> Result<NameBinding<'a>, Determinacy> {
         self.resolve_ident_in_module_ext(module, ident, ns, parent_scope, finalize, ignore_binding)
             .map_err(|(determinacy, _)| determinacy)
     }
@@ -744,8 +742,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         ns: Namespace,
         parent_scope: &ParentScope<'a>,
         finalize: Option<Finalize>,
-        ignore_binding: Option<&'a NameBinding<'a>>,
-    ) -> Result<&'a NameBinding<'a>, (Determinacy, Weak)> {
+        ignore_binding: Option<NameBinding<'a>>,
+    ) -> Result<NameBinding<'a>, (Determinacy, Weak)> {
         let tmp_parent_scope;
         let mut adjusted_parent_scope = parent_scope;
         match module {
@@ -782,8 +780,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         ns: Namespace,
         parent_scope: &ParentScope<'a>,
         finalize: Option<Finalize>,
-        ignore_binding: Option<&'a NameBinding<'a>>,
-    ) -> Result<&'a NameBinding<'a>, Determinacy> {
+        ignore_binding: Option<NameBinding<'a>>,
+    ) -> Result<NameBinding<'a>, Determinacy> {
         self.resolve_ident_in_module_unadjusted_ext(
             module,
             ident,
@@ -809,8 +807,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         finalize: Option<Finalize>,
         // This binding should be ignored during in-module resolution, so that we don't get
         // "self-confirming" import resolutions during import validation and checking.
-        ignore_binding: Option<&'a NameBinding<'a>>,
-    ) -> Result<&'a NameBinding<'a>, (Determinacy, Weak)> {
+        ignore_binding: Option<NameBinding<'a>>,
+    ) -> Result<NameBinding<'a>, (Determinacy, Weak)> {
         let module = match module {
             ModuleOrUniformRoot::Module(module) => module,
             ModuleOrUniformRoot::CrateRootAndExternPrelude => {
@@ -873,13 +871,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         // binding if it exists. What we really want here is having two separate scopes in
         // a module - one for non-globs and one for globs, but until that's done use this
         // hack to avoid inconsistent resolution ICEs during import validation.
-        let binding =
-            [resolution.binding, resolution.shadowed_glob].into_iter().find_map(|binding| {
-                match (binding, ignore_binding) {
-                    (Some(binding), Some(ignored)) if ptr::eq(binding, ignored) => None,
-                    _ => binding,
-                }
-            });
+        let binding = [resolution.binding, resolution.shadowed_glob]
+            .into_iter()
+            .find_map(|binding| if binding == ignore_binding { None } else { binding });
 
         if let Some(Finalize { path_span, report_private, .. }) = finalize {
             let Some(binding) = binding else {
@@ -917,11 +911,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             }
 
             if !restricted_shadowing && binding.expansion != LocalExpnId::ROOT {
-                if let NameBindingKind::Import {
-                    import: Import { kind: ImportKind::MacroExport, .. },
-                    ..
-                } = binding.kind
-                {
+                if let NameBindingKind::Import { import, .. } = binding.kind
+                    && matches!(import.kind, ImportKind::MacroExport) {
                     self.macro_expanded_macro_export_errors.insert((path_span, binding.span));
                 }
             }
@@ -930,7 +921,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             return Ok(binding);
         }
 
-        let check_usable = |this: &mut Self, binding: &'a NameBinding<'a>| {
+        let check_usable = |this: &mut Self, binding: NameBinding<'a>| {
             let usable = this.is_accessible_from(binding.vis, parent_scope.module);
             if usable { Ok(binding) } else { Err((Determined, Weak::No)) }
         };
@@ -955,7 +946,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             }
             if let Some(ignored) = ignore_binding &&
                 let NameBindingKind::Import { import, .. } = ignored.kind &&
-                ptr::eq(import, &**single_import) {
+                import == *single_import {
                 // Ignore not just the binding itself, but if it has a shadowed_glob,
                 // ignore that, too, because this loop is supposed to only process
                 // named imports.
@@ -1352,7 +1343,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         opt_ns: Option<Namespace>, // `None` indicates a module path in import
         parent_scope: &ParentScope<'a>,
         finalize: Option<Finalize>,
-        ignore_binding: Option<&'a NameBinding<'a>>,
+        ignore_binding: Option<NameBinding<'a>>,
     ) -> PathResult<'a> {
         self.resolve_path_with_ribs(path, opt_ns, parent_scope, finalize, None, ignore_binding)
     }
@@ -1364,7 +1355,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         parent_scope: &ParentScope<'a>,
         finalize: Option<Finalize>,
         ribs: Option<&PerNS<Vec<Rib<'a>>>>,
-        ignore_binding: Option<&'a NameBinding<'a>>,
+        ignore_binding: Option<NameBinding<'a>>,
     ) -> PathResult<'a> {
         let mut module = None;
         let mut allow_super = true;
diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs
index 074f761c53b..d37fe783bba 100644
--- a/compiler/rustc_resolve/src/imports.rs
+++ b/compiler/rustc_resolve/src/imports.rs
@@ -12,7 +12,7 @@ use crate::{fluent_generated as fluent, Namespace::*};
 use crate::{module_to_string, names_to_string, ImportSuggestion};
 use crate::{AmbiguityKind, BindingKey, ModuleKind, ResolutionError, Resolver, Segment};
 use crate::{Finalize, Module, ModuleOrUniformRoot, ParentScope, PerNS, ScopeSet};
-use crate::{NameBinding, NameBindingKind, PathResult};
+use crate::{NameBinding, NameBindingData, NameBindingKind, PathResult};
 
 use rustc_ast::NodeId;
 use rustc_data_structures::fx::FxHashSet;
@@ -35,7 +35,7 @@ use rustc_span::Span;
 use smallvec::SmallVec;
 
 use std::cell::Cell;
-use std::{mem, ptr};
+use std::mem;
 
 type Res = def::Res<NodeId>;
 
@@ -48,9 +48,9 @@ pub(crate) enum ImportKind<'a> {
         /// `target` in `use prefix::source as target`.
         target: Ident,
         /// Bindings to which `source` refers to.
-        source_bindings: PerNS<Cell<Result<&'a NameBinding<'a>, Determinacy>>>,
+        source_bindings: PerNS<Cell<Result<NameBinding<'a>, Determinacy>>>,
         /// Bindings introduced by `target`.
-        target_bindings: PerNS<Cell<Option<&'a NameBinding<'a>>>>,
+        target_bindings: PerNS<Cell<Option<NameBinding<'a>>>>,
         /// `true` for `...::{self [as target]}` imports, `false` otherwise.
         type_ns_only: bool,
         /// Did this import result from a nested import? ie. `use foo::{bar, baz};`
@@ -135,7 +135,7 @@ impl<'a> std::fmt::Debug for ImportKind<'a> {
 
 /// One import.
 #[derive(Debug, Clone)]
-pub(crate) struct Import<'a> {
+pub(crate) struct ImportData<'a> {
     pub kind: ImportKind<'a>,
 
     /// Node ID of the "root" use item -- this is always the same as `ImportKind`'s `id`
@@ -172,7 +172,11 @@ pub(crate) struct Import<'a> {
     pub used: Cell<bool>,
 }
 
-impl<'a> Import<'a> {
+/// All imports are unique and allocated on a same arena,
+/// so we can use referential equality to compare them.
+pub(crate) type Import<'a> = Interned<'a, ImportData<'a>>;
+
+impl<'a> ImportData<'a> {
     pub(crate) fn is_glob(&self) -> bool {
         matches!(self.kind, ImportKind::Glob { .. })
     }
@@ -214,15 +218,15 @@ impl<'a> Import<'a> {
 pub(crate) struct NameResolution<'a> {
     /// Single imports that may define the name in the namespace.
     /// Imports are arena-allocated, so it's ok to use pointers as keys.
-    pub single_imports: FxHashSet<Interned<'a, Import<'a>>>,
+    pub single_imports: FxHashSet<Import<'a>>,
     /// The least shadowable known binding for this name, or None if there are no known bindings.
-    pub binding: Option<&'a NameBinding<'a>>,
-    pub shadowed_glob: Option<&'a NameBinding<'a>>,
+    pub binding: Option<NameBinding<'a>>,
+    pub shadowed_glob: Option<NameBinding<'a>>,
 }
 
 impl<'a> NameResolution<'a> {
     /// Returns the binding for the name if it is known or None if it not known.
-    pub(crate) fn binding(&self) -> Option<&'a NameBinding<'a>> {
+    pub(crate) fn binding(&self) -> Option<NameBinding<'a>> {
         self.binding.and_then(|binding| {
             if !binding.is_glob_import() || self.single_imports.is_empty() {
                 Some(binding)
@@ -231,10 +235,6 @@ impl<'a> NameResolution<'a> {
             }
         })
     }
-
-    pub(crate) fn add_single_import(&mut self, import: &'a Import<'a>) {
-        self.single_imports.insert(Interned::new_unchecked(import));
-    }
 }
 
 /// An error that may be transformed into a diagnostic later. Used to combine multiple unresolved
@@ -250,15 +250,12 @@ struct UnresolvedImportError {
 
 // Reexports of the form `pub use foo as bar;` where `foo` is `extern crate foo;`
 // are permitted for backward-compatibility under a deprecation lint.
-fn pub_use_of_private_extern_crate_hack(import: &Import<'_>, binding: &NameBinding<'_>) -> bool {
+fn pub_use_of_private_extern_crate_hack(import: Import<'_>, binding: NameBinding<'_>) -> bool {
     match (&import.kind, &binding.kind) {
-        (
-            ImportKind::Single { .. },
-            NameBindingKind::Import {
-                import: Import { kind: ImportKind::ExternCrate { .. }, .. },
-                ..
-            },
-        ) => import.expect_vis().is_public(),
+        (ImportKind::Single { .. }, NameBindingKind::Import { import: binding_import, .. }) => {
+            matches!(binding_import.kind, ImportKind::ExternCrate { .. })
+                && import.expect_vis().is_public()
+        }
         _ => false,
     }
 }
@@ -266,11 +263,7 @@ fn pub_use_of_private_extern_crate_hack(import: &Import<'_>, binding: &NameBindi
 impl<'a, 'tcx> Resolver<'a, 'tcx> {
     /// Given a binding and an import that resolves to it,
     /// return the corresponding binding defined by the import.
-    pub(crate) fn import(
-        &self,
-        binding: &'a NameBinding<'a>,
-        import: &'a Import<'a>,
-    ) -> &'a NameBinding<'a> {
+    pub(crate) fn import(&self, binding: NameBinding<'a>, import: Import<'a>) -> NameBinding<'a> {
         let import_vis = import.expect_vis().to_def_id();
         let vis = if binding.vis.is_at_least(import_vis, self.tcx)
             || pub_use_of_private_extern_crate_hack(import, binding)
@@ -288,7 +281,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             }
         }
 
-        self.arenas.alloc_name_binding(NameBinding {
+        self.arenas.alloc_name_binding(NameBindingData {
             kind: NameBindingKind::Import { binding, import, used: Cell::new(false) },
             ambiguity: None,
             span: import.span,
@@ -302,8 +295,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         &mut self,
         module: Module<'a>,
         key: BindingKey,
-        binding: &'a NameBinding<'a>,
-    ) -> Result<(), &'a NameBinding<'a>> {
+        binding: NameBinding<'a>,
+    ) -> Result<(), NameBinding<'a>> {
         let res = binding.res();
         self.check_reserved_macro_name(key.ident, res);
         self.set_binding_parent_module(binding, module);
@@ -372,12 +365,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
     fn ambiguity(
         &self,
         kind: AmbiguityKind,
-        primary_binding: &'a NameBinding<'a>,
-        secondary_binding: &'a NameBinding<'a>,
-    ) -> &'a NameBinding<'a> {
-        self.arenas.alloc_name_binding(NameBinding {
+        primary_binding: NameBinding<'a>,
+        secondary_binding: NameBinding<'a>,
+    ) -> NameBinding<'a> {
+        self.arenas.alloc_name_binding(NameBindingData {
             ambiguity: Some((secondary_binding, kind)),
-            ..primary_binding.clone()
+            ..(*primary_binding).clone()
         })
     }
 
@@ -395,13 +388,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
 
             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),
-                },
+            if old_binding.is_none() && let Some(binding) = resolution.binding() {
+                (binding, t)
+            } else {
+                return t;
             }
         };
 
@@ -414,7 +404,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                 None => continue,
             };
             if self.is_accessible_from(binding.vis, scope) {
-                let imported_binding = self.import(binding, import);
+                let imported_binding = self.import(binding, *import);
                 let key = BindingKey { ident, ..key };
                 let _ = self.try_define(import.parent_scope.module, key, imported_binding);
             }
@@ -425,7 +415,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
 
     // 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) {
+    fn import_dummy_binding(&mut self, import: Import<'a>, is_indeterminate: bool) {
         if let ImportKind::Single { target, ref target_bindings, .. } = import.kind {
             if !(is_indeterminate || target_bindings.iter().all(|binding| binding.get().is_none()))
             {
@@ -463,7 +453,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             prev_indeterminate_count = indeterminate_count;
             indeterminate_count = 0;
             for import in mem::take(&mut self.indeterminate_imports) {
-                let import_indeterminate_count = self.resolve_import(&import);
+                let import_indeterminate_count = self.resolve_import(import);
                 indeterminate_count += import_indeterminate_count;
                 match import_indeterminate_count {
                     0 => self.determined_imports.push(import),
@@ -475,7 +465,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
 
     pub(crate) fn finalize_imports(&mut self) {
         for module in self.arenas.local_modules().iter() {
-            self.finalize_resolutions_in(module);
+            self.finalize_resolutions_in(*module);
         }
 
         let mut seen_spans = FxHashSet::default();
@@ -546,17 +536,17 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
 
     pub(crate) fn check_hidden_glob_reexports(
         &mut self,
-        exported_ambiguities: FxHashSet<Interned<'a, NameBinding<'a>>>,
+        exported_ambiguities: FxHashSet<NameBinding<'a>>,
     ) {
         for module in self.arenas.local_modules().iter() {
-            for (key, resolution) in self.resolutions(module).borrow().iter() {
+            for (key, resolution) in self.resolutions(*module).borrow().iter() {
                 let resolution = resolution.borrow();
 
                 if let Some(binding) = resolution.binding {
                     if let NameBindingKind::Import { import, .. } = binding.kind
                         && let Some((amb_binding, _)) = binding.ambiguity
                         && binding.res() != Res::Err
-                        && exported_ambiguities.contains(&Interned::new_unchecked(binding))
+                        && exported_ambiguities.contains(&binding)
                     {
                         self.lint_buffer.buffer_lint_with_diagnostic(
                             AMBIGUOUS_GLOB_REEXPORTS,
@@ -612,7 +602,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         }
     }
 
-    fn throw_unresolved_import_error(&mut self, errors: Vec<(&Import<'_>, UnresolvedImportError)>) {
+    fn throw_unresolved_import_error(&mut self, errors: Vec<(Import<'_>, UnresolvedImportError)>) {
         if errors.is_empty() {
             return;
         }
@@ -704,7 +694,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
     ///
     /// Meanwhile, if resolve successful, the resolved bindings are written
     /// into the module.
-    fn resolve_import(&mut self, import: &'a Import<'a>) -> usize {
+    fn resolve_import(&mut self, import: Import<'a>) -> usize {
         debug!(
             "(resolving import for module) resolving import `{}::...` in `{}`",
             Segment::names_to_string(&import.module_path),
@@ -784,7 +774,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                         }
                         let key = BindingKey::new(target, ns);
                         this.update_resolution(parent, key, |_, resolution| {
-                            resolution.single_imports.remove(&Interned::new_unchecked(import));
+                            resolution.single_imports.remove(&import);
                         });
                     }
                 }
@@ -798,7 +788,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
     ///
     /// Optionally returns an unresolved import error. This error is buffered and used to
     /// consolidate multiple unresolved import errors into a single diagnostic.
-    fn finalize_import(&mut self, import: &'a Import<'a>) -> Option<UnresolvedImportError> {
+    fn finalize_import(&mut self, import: Import<'a>) -> Option<UnresolvedImportError> {
         let orig_vis = import.vis.take();
         let ignore_binding = match &import.kind {
             ImportKind::Single { target_bindings, .. } => target_bindings[TypeNS].get(),
@@ -824,7 +814,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             PathResult::Module(module) => {
                 // Consistency checks, analogous to `finalize_macro_resolutions`.
                 if let Some(initial_module) = import.imported_module.get() {
-                    if !ModuleOrUniformRoot::same_def(module, initial_module) && no_ambiguity {
+                    if module != initial_module && no_ambiguity {
                         span_bug!(import.span, "inconsistent resolution for an import");
                     }
                 } else if self.privacy_errors.is_empty() {
@@ -926,7 +916,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                     }
 
                     if let ModuleOrUniformRoot::Module(module) = module {
-                        if ptr::eq(module, import.parent_scope.module) {
+                        if module == import.parent_scope.module {
                             // Importing a module into itself is not allowed.
                             return Some(UnresolvedImportError {
                                 span: import.span,
@@ -1242,9 +1232,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
     fn check_for_redundant_imports(
         &mut self,
         ident: Ident,
-        import: &'a Import<'a>,
-        source_bindings: &PerNS<Cell<Result<&'a NameBinding<'a>, Determinacy>>>,
-        target_bindings: &PerNS<Cell<Option<&'a NameBinding<'a>>>>,
+        import: Import<'a>,
+        source_bindings: &PerNS<Cell<Result<NameBinding<'a>, Determinacy>>>,
+        target_bindings: &PerNS<Cell<Option<NameBinding<'a>>>>,
         target: Ident,
     ) {
         // This function is only called for single imports.
@@ -1305,7 +1295,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         }
     }
 
-    fn resolve_glob_import(&mut self, import: &'a Import<'a>) {
+    fn resolve_glob_import(&mut self, import: Import<'a>) {
         // This function is only called for glob imports.
         let ImportKind::Glob { id, is_prelude, .. } = import.kind else { unreachable!() };
 
@@ -1319,7 +1309,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         if module.is_trait() {
             self.tcx.sess.create_err(ItemsInTraitsAreNotImportable { span: import.span }).emit();
             return;
-        } else if ptr::eq(module, import.parent_scope.module) {
+        } else if module == import.parent_scope.module {
             return;
         } else if is_prelude {
             self.prelude = Some(module);
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 744dcf0db84..90cb312edd2 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -1284,7 +1284,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
         ident: Ident,
         ns: Namespace,
         finalize: Option<Finalize>,
-        ignore_binding: Option<&'a NameBinding<'a>>,
+        ignore_binding: Option<NameBinding<'a>>,
     ) -> Option<LexicalScopeBinding<'a>> {
         self.r.resolve_ident_in_lexical_scope(
             ident,
@@ -2972,7 +2972,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
         F: FnOnce(Ident, String, Option<Symbol>) -> ResolutionError<'a>,
     {
         // If there is a TraitRef in scope for an impl, then the method must be in the trait.
-        let Some((module, _)) = &self.current_trait_ref else { return; };
+        let Some((module, _)) = self.current_trait_ref else { return; };
         ident.span.normalize_to_macros_2_0_and_adjust(module.expansion);
         let key = BindingKey::new(ident, ns);
         let mut binding = self.r.resolution(module, key).try_borrow().ok().and_then(|r| r.binding);
@@ -3503,7 +3503,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
         let report_errors = |this: &mut Self, res: Option<Res>| {
             if this.should_report_errs() {
                 let (err, candidates) =
-                    this.smart_resolve_report_errors(path, path_span, source, res);
+                    this.smart_resolve_report_errors(path, path, path_span, source, res);
 
                 let def_id = this.parent_scope.module.nearest_parent_mod();
                 let instead = res.is_some();
@@ -3560,8 +3560,13 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
                 _ => return Some(parent_err),
             };
 
-            let (mut err, candidates) =
-                this.smart_resolve_report_errors(prefix_path, path_span, PathSource::Type, None);
+            let (mut err, candidates) = this.smart_resolve_report_errors(
+                prefix_path,
+                path,
+                path_span,
+                PathSource::Type,
+                None,
+            );
 
             // There are two different error messages user might receive at
             // this point:
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index c62fc031a16..c0e3f1aaf01 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -4,6 +4,7 @@ use crate::late::{LifetimeBinderKind, LifetimeRes, LifetimeRibKind, LifetimeUseS
 use crate::{errors, path_names_to_string};
 use crate::{Module, ModuleKind, ModuleOrUniformRoot};
 use crate::{PathResult, PathSource, Segment};
+use rustc_hir::def::Namespace::{self, *};
 
 use rustc_ast::visit::{FnCtxt, FnKind, LifetimeCtxt};
 use rustc_ast::{
@@ -17,7 +18,6 @@ use rustc_errors::{
     MultiSpan,
 };
 use rustc_hir as hir;
-use rustc_hir::def::Namespace::{self, *};
 use rustc_hir::def::{self, CtorKind, CtorOf, DefKind};
 use rustc_hir::def_id::{DefId, CRATE_DEF_ID};
 use rustc_hir::PrimTy;
@@ -221,10 +221,14 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
                 let suggestion = if self.current_trait_ref.is_none()
                     && let Some((fn_kind, _)) = self.diagnostic_metadata.current_function
                     && let Some(FnCtxt::Assoc(_)) = fn_kind.ctxt()
+                    && let FnKind::Fn(_, _, sig, ..) = fn_kind
                     && let Some(items) = self.diagnostic_metadata.current_impl_items
                     && let Some(item) = items.iter().find(|i| {
                         if let AssocItemKind::Fn(..) | AssocItemKind::Const(..) = &i.kind
                             && i.ident.name == item_str.name
+                            // don't suggest if the item is in Fn signature arguments
+                            // issue #112590
+                            && !sig.span.contains(item_span)
                         {
                             debug!(?item_str.name);
                             return true
@@ -318,11 +322,56 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
         }
     }
 
+    /// Try to suggest for a module path that cannot be resolved.
+    /// Such as `fmt::Debug` where `fmt` is not resolved without importing,
+    /// here we search with `lookup_import_candidates` for a module named `fmt`
+    /// with `TypeNS` as namespace.
+    ///
+    /// We need a separate function here because we won't suggest for a path with single segment
+    /// and we won't change `SourcePath` api `is_expected` to match `Type` with `DefKind::Mod`
+    pub(crate) fn smart_resolve_partial_mod_path_errors(
+        &mut self,
+        prefix_path: &[Segment],
+        path: &[Segment],
+    ) -> Vec<ImportSuggestion> {
+        let next_seg = if path.len() >= prefix_path.len() + 1 && prefix_path.len() == 1 {
+            path.get(prefix_path.len())
+        } else {
+            None
+        };
+        if let Some(segment) = prefix_path.last() &&
+            let Some(next_seg) = next_seg {
+            let candidates = self.r.lookup_import_candidates(
+                segment.ident,
+                Namespace::TypeNS,
+                &self.parent_scope,
+                &|res: Res| matches!(res, Res::Def(DefKind::Mod, _)),
+            );
+            // double check next seg is valid
+            candidates
+                .into_iter()
+                .filter(|candidate| {
+                    if let Some(def_id) = candidate.did &&
+                        let Some(module) = self.r.get_module(def_id) {
+                            self.r.resolutions(module).borrow().iter().any(|(key, _r)| {
+                                key.ident.name == next_seg.ident.name
+                            })
+                    } else {
+                        false
+                    }
+                })
+                .collect::<Vec<_>>()
+        } else {
+            Vec::new()
+        }
+    }
+
     /// Handles error reporting for `smart_resolve_path_fragment` function.
     /// Creates base error and amends it with one short label and possibly some longer helps/notes.
     pub(crate) fn smart_resolve_report_errors(
         &mut self,
         path: &[Segment],
+        full_path: &[Segment],
         span: Span,
         source: PathSource<'_>,
         res: Option<Res>,
@@ -364,7 +413,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
         }
 
         let (found, candidates) =
-            self.try_lookup_name_relaxed(&mut err, source, path, span, res, &base_error);
+            self.try_lookup_name_relaxed(&mut err, source, path, full_path, span, res, &base_error);
         if found {
             return (err, candidates);
         }
@@ -470,6 +519,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
         err: &mut Diagnostic,
         source: PathSource<'_>,
         path: &[Segment],
+        full_path: &[Segment],
         span: Span,
         res: Option<Res>,
         base_error: &BaseError,
@@ -639,6 +689,10 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
             }
         }
 
+        if candidates.is_empty() {
+            candidates = self.smart_resolve_partial_mod_path_errors(path, full_path);
+        }
+
         return (false, candidates);
     }
 
@@ -1539,7 +1593,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
             return None;
         }
 
-        let resolutions = self.r.resolutions(module);
+        let resolutions = self.r.resolutions(*module);
         let targets = resolutions
             .borrow()
             .iter()
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index cc4cb9fa30c..da3d86a4718 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -14,6 +14,7 @@
 #![feature(iter_intersperse)]
 #![feature(let_chains)]
 #![feature(never_type)]
+#![feature(rustc_attrs)]
 #![recursion_limit = "256"]
 #![allow(rustdoc::private_intra_doc_links)]
 #![allow(rustc::potential_query_instability)]
@@ -61,10 +62,10 @@ use rustc_span::{Span, DUMMY_SP};
 use smallvec::{smallvec, SmallVec};
 use std::cell::{Cell, RefCell};
 use std::collections::BTreeSet;
-use std::{fmt, ptr};
+use std::fmt;
 
 use diagnostics::{ImportSuggestion, LabelSuggestion, Suggestion};
-use imports::{Import, ImportKind, NameResolution};
+use imports::{Import, ImportData, ImportKind, NameResolution};
 use late::{HasGenericParams, PathSource, PatternSource};
 use macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef};
 
@@ -352,7 +353,7 @@ impl<'a> From<&'a ast::PathSegment> for Segment {
 /// forward.
 #[derive(Debug)]
 enum LexicalScopeBinding<'a> {
-    Item(&'a NameBinding<'a>),
+    Item(NameBinding<'a>),
     Res(Res),
 }
 
@@ -365,7 +366,7 @@ impl<'a> LexicalScopeBinding<'a> {
     }
 }
 
-#[derive(Copy, Clone, Debug)]
+#[derive(Copy, Clone, PartialEq, Debug)]
 enum ModuleOrUniformRoot<'a> {
     /// Regular module.
     Module(Module<'a>),
@@ -383,23 +384,6 @@ enum ModuleOrUniformRoot<'a> {
     CurrentScope,
 }
 
-impl ModuleOrUniformRoot<'_> {
-    fn same_def(lhs: Self, rhs: Self) -> bool {
-        match (lhs, rhs) {
-            (ModuleOrUniformRoot::Module(lhs), ModuleOrUniformRoot::Module(rhs)) => {
-                ptr::eq(lhs, rhs)
-            }
-            (
-                ModuleOrUniformRoot::CrateRootAndExternPrelude,
-                ModuleOrUniformRoot::CrateRootAndExternPrelude,
-            )
-            | (ModuleOrUniformRoot::ExternPrelude, ModuleOrUniformRoot::ExternPrelude)
-            | (ModuleOrUniformRoot::CurrentScope, ModuleOrUniformRoot::CurrentScope) => true,
-            _ => false,
-        }
-    }
-}
-
 #[derive(Debug)]
 enum PathResult<'a> {
     Module(ModuleOrUniformRoot<'a>),
@@ -518,11 +502,11 @@ struct ModuleData<'a> {
     /// Whether `#[no_implicit_prelude]` is active.
     no_implicit_prelude: bool,
 
-    glob_importers: RefCell<Vec<&'a Import<'a>>>,
-    globs: RefCell<Vec<&'a Import<'a>>>,
+    glob_importers: RefCell<Vec<Import<'a>>>,
+    globs: RefCell<Vec<Import<'a>>>,
 
     /// Used to memoize the traits in this module for faster searches through all traits in scope.
-    traits: RefCell<Option<Box<[(Ident, &'a NameBinding<'a>)]>>>,
+    traits: RefCell<Option<Box<[(Ident, NameBinding<'a>)]>>>,
 
     /// Span of the module itself. Used for error reporting.
     span: Span,
@@ -530,7 +514,11 @@ struct ModuleData<'a> {
     expansion: ExpnId,
 }
 
-type Module<'a> = &'a ModuleData<'a>;
+/// All modules are unique and allocated on a same arena,
+/// so we can use referential equality to compare them.
+#[derive(Clone, Copy, PartialEq)]
+#[rustc_pass_by_value]
+struct Module<'a>(Interned<'a, ModuleData<'a>>);
 
 impl<'a> ModuleData<'a> {
     fn new(
@@ -558,11 +546,13 @@ impl<'a> ModuleData<'a> {
             expansion,
         }
     }
+}
 
-    fn for_each_child<'tcx, R, F>(&'a self, resolver: &mut R, mut f: F)
+impl<'a> Module<'a> {
+    fn for_each_child<'tcx, R, F>(self, resolver: &mut R, mut f: F)
     where
         R: AsMut<Resolver<'a, 'tcx>>,
-        F: FnMut(&mut R, Ident, Namespace, &'a NameBinding<'a>),
+        F: FnMut(&mut R, Ident, Namespace, NameBinding<'a>),
     {
         for (key, name_resolution) in resolver.as_mut().resolutions(self).borrow().iter() {
             if let Some(binding) = name_resolution.borrow().binding {
@@ -572,7 +562,7 @@ impl<'a> ModuleData<'a> {
     }
 
     /// This modifies `self` in place. The traits will be stored in `self.traits`.
-    fn ensure_traits<'tcx, R>(&'a self, resolver: &mut R)
+    fn ensure_traits<'tcx, R>(self, resolver: &mut R)
     where
         R: AsMut<Resolver<'a, 'tcx>>,
     {
@@ -591,7 +581,7 @@ impl<'a> ModuleData<'a> {
         }
     }
 
-    fn res(&self) -> Option<Res> {
+    fn res(self) -> Option<Res> {
         match self.kind {
             ModuleKind::Def(kind, def_id, _) => Some(Res::Def(kind, def_id)),
             _ => None,
@@ -599,11 +589,11 @@ impl<'a> ModuleData<'a> {
     }
 
     // Public for rustdoc.
-    fn def_id(&self) -> DefId {
+    fn def_id(self) -> DefId {
         self.opt_def_id().expect("`ModuleData::def_id` is called on a block module")
     }
 
-    fn opt_def_id(&self) -> Option<DefId> {
+    fn opt_def_id(self) -> Option<DefId> {
         match self.kind {
             ModuleKind::Def(_, def_id, _) => Some(def_id),
             _ => None,
@@ -611,15 +601,15 @@ impl<'a> ModuleData<'a> {
     }
 
     // `self` resolves to the first module ancestor that `is_normal`.
-    fn is_normal(&self) -> bool {
+    fn is_normal(self) -> bool {
         matches!(self.kind, ModuleKind::Def(DefKind::Mod, _, _))
     }
 
-    fn is_trait(&self) -> bool {
+    fn is_trait(self) -> bool {
         matches!(self.kind, ModuleKind::Def(DefKind::Trait, _, _))
     }
 
-    fn nearest_item_scope(&'a self) -> Module<'a> {
+    fn nearest_item_scope(self) -> Module<'a> {
         match self.kind {
             ModuleKind::Def(DefKind::Enum | DefKind::Trait, ..) => {
                 self.parent.expect("enum or trait module without a parent")
@@ -630,15 +620,15 @@ impl<'a> ModuleData<'a> {
 
     /// The [`DefId`] of the nearest `mod` item ancestor (which may be this module).
     /// This may be the crate root.
-    fn nearest_parent_mod(&self) -> DefId {
+    fn nearest_parent_mod(self) -> DefId {
         match self.kind {
             ModuleKind::Def(DefKind::Mod, def_id, _) => def_id,
             _ => self.parent.expect("non-root module without parent").nearest_parent_mod(),
         }
     }
 
-    fn is_ancestor_of(&self, mut other: &Self) -> bool {
-        while !ptr::eq(self, other) {
+    fn is_ancestor_of(self, mut other: Self) -> bool {
+        while self != other {
             if let Some(parent) = other.parent {
                 other = parent;
             } else {
@@ -649,7 +639,15 @@ impl<'a> ModuleData<'a> {
     }
 }
 
-impl<'a> fmt::Debug for ModuleData<'a> {
+impl<'a> std::ops::Deref for Module<'a> {
+    type Target = ModuleData<'a>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+impl<'a> fmt::Debug for Module<'a> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         write!(f, "{:?}", self.res())
     }
@@ -657,20 +655,24 @@ impl<'a> fmt::Debug for ModuleData<'a> {
 
 /// Records a possibly-private value, type, or module definition.
 #[derive(Clone, Debug)]
-struct NameBinding<'a> {
+struct NameBindingData<'a> {
     kind: NameBindingKind<'a>,
-    ambiguity: Option<(&'a NameBinding<'a>, AmbiguityKind)>,
+    ambiguity: Option<(NameBinding<'a>, AmbiguityKind)>,
     expansion: LocalExpnId,
     span: Span,
     vis: ty::Visibility<DefId>,
 }
 
+/// All name bindings are unique and allocated on a same arena,
+/// so we can use referential equality to compare them.
+type NameBinding<'a> = Interned<'a, NameBindingData<'a>>;
+
 trait ToNameBinding<'a> {
-    fn to_name_binding(self, arenas: &'a ResolverArenas<'a>) -> &'a NameBinding<'a>;
+    fn to_name_binding(self, arenas: &'a ResolverArenas<'a>) -> NameBinding<'a>;
 }
 
-impl<'a> ToNameBinding<'a> for &'a NameBinding<'a> {
-    fn to_name_binding(self, _: &'a ResolverArenas<'a>) -> &'a NameBinding<'a> {
+impl<'a> ToNameBinding<'a> for NameBinding<'a> {
+    fn to_name_binding(self, _: &'a ResolverArenas<'a>) -> NameBinding<'a> {
         self
     }
 }
@@ -679,7 +681,7 @@ impl<'a> ToNameBinding<'a> for &'a NameBinding<'a> {
 enum NameBindingKind<'a> {
     Res(Res),
     Module(Module<'a>),
-    Import { binding: &'a NameBinding<'a>, import: &'a Import<'a>, used: Cell<bool> },
+    Import { binding: NameBinding<'a>, import: Import<'a>, used: Cell<bool> },
 }
 
 impl<'a> NameBindingKind<'a> {
@@ -692,7 +694,7 @@ impl<'a> NameBindingKind<'a> {
 #[derive(Debug)]
 struct PrivacyError<'a> {
     ident: Ident,
-    binding: &'a NameBinding<'a>,
+    binding: NameBinding<'a>,
     dedup_span: Span,
     outermost_res: Option<(Res, Ident)>,
     parent_scope: ParentScope<'a>,
@@ -761,13 +763,13 @@ enum AmbiguityErrorMisc {
 struct AmbiguityError<'a> {
     kind: AmbiguityKind,
     ident: Ident,
-    b1: &'a NameBinding<'a>,
-    b2: &'a NameBinding<'a>,
+    b1: NameBinding<'a>,
+    b2: NameBinding<'a>,
     misc1: AmbiguityErrorMisc,
     misc2: AmbiguityErrorMisc,
 }
 
-impl<'a> NameBinding<'a> {
+impl<'a> NameBindingData<'a> {
     fn module(&self) -> Option<Module<'a>> {
         match self.kind {
             NameBindingKind::Module(module) => Some(module),
@@ -805,14 +807,12 @@ impl<'a> NameBinding<'a> {
 
     fn is_extern_crate(&self) -> bool {
         match self.kind {
-            NameBindingKind::Import {
-                import: &Import { kind: ImportKind::ExternCrate { .. }, .. },
-                ..
-            } => true,
-            NameBindingKind::Module(&ModuleData {
-                kind: ModuleKind::Def(DefKind::Mod, def_id, _),
-                ..
-            }) => def_id.is_crate_root(),
+            NameBindingKind::Import { import, .. } => {
+                matches!(import.kind, ImportKind::ExternCrate { .. })
+            }
+            NameBindingKind::Module(module)
+                if let ModuleKind::Def(DefKind::Mod, def_id, _) = module.kind
+                    => def_id.is_crate_root(),
             _ => false,
         }
     }
@@ -855,7 +855,7 @@ impl<'a> NameBinding<'a> {
     fn may_appear_after(
         &self,
         invoc_parent_expansion: LocalExpnId,
-        binding: &NameBinding<'_>,
+        binding: NameBinding<'_>,
     ) -> bool {
         // self > max(invoc, binding) => !(self <= invoc || self <= binding)
         // Expansions are partially ordered, so "may appear after" is an inversion of
@@ -872,7 +872,7 @@ impl<'a> NameBinding<'a> {
 
 #[derive(Default, Clone)]
 struct ExternPreludeEntry<'a> {
-    extern_crate_item: Option<&'a NameBinding<'a>>,
+    extern_crate_item: Option<NameBinding<'a>>,
     introduced_by_item: bool,
 }
 
@@ -917,10 +917,10 @@ pub struct Resolver<'a, 'tcx> {
     field_visibility_spans: FxHashMap<DefId, Vec<Span>>,
 
     /// All imports known to succeed or fail.
-    determined_imports: Vec<&'a Import<'a>>,
+    determined_imports: Vec<Import<'a>>,
 
     /// All non-determined imports.
-    indeterminate_imports: Vec<&'a Import<'a>>,
+    indeterminate_imports: Vec<Import<'a>>,
 
     // Spans for local variables found during pattern resolution.
     // Used for suggestions during error reporting.
@@ -962,7 +962,7 @@ pub struct Resolver<'a, 'tcx> {
     /// language items.
     empty_module: Module<'a>,
     module_map: FxHashMap<DefId, Module<'a>>,
-    binding_parent_modules: FxHashMap<Interned<'a, NameBinding<'a>>, Module<'a>>,
+    binding_parent_modules: FxHashMap<NameBinding<'a>, Module<'a>>,
 
     underscore_disambiguator: u32,
 
@@ -984,7 +984,7 @@ pub struct Resolver<'a, 'tcx> {
     macro_expanded_macro_export_errors: BTreeSet<(Span, Span)>,
 
     arenas: &'a ResolverArenas<'a>,
-    dummy_binding: &'a NameBinding<'a>,
+    dummy_binding: NameBinding<'a>,
 
     used_extern_options: FxHashSet<Symbol>,
     macro_names: FxHashSet<Ident>,
@@ -993,7 +993,7 @@ pub struct Resolver<'a, 'tcx> {
     /// the surface (`macro` items in libcore), but are actually attributes or derives.
     builtin_macro_kinds: FxHashMap<LocalDefId, MacroKind>,
     registered_tools: &'tcx RegisteredTools,
-    macro_use_prelude: FxHashMap<Symbol, &'a NameBinding<'a>>,
+    macro_use_prelude: FxHashMap<Symbol, NameBinding<'a>>,
     macro_map: FxHashMap<DefId, MacroData>,
     dummy_ext_bang: Lrc<SyntaxExtension>,
     dummy_ext_derive: Lrc<SyntaxExtension>,
@@ -1005,7 +1005,7 @@ pub struct Resolver<'a, 'tcx> {
     proc_macro_stubs: FxHashSet<LocalDefId>,
     /// Traces collected during macro resolution and validated when it's complete.
     single_segment_macro_resolutions:
-        Vec<(Ident, MacroKind, ParentScope<'a>, Option<&'a NameBinding<'a>>)>,
+        Vec<(Ident, MacroKind, ParentScope<'a>, Option<NameBinding<'a>>)>,
     multi_segment_macro_resolutions:
         Vec<(Vec<Segment>, Span, MacroKind, ParentScope<'a>, Option<Res>)>,
     builtin_attrs: Vec<(Ident, ParentScope<'a>)>,
@@ -1030,7 +1030,7 @@ pub struct Resolver<'a, 'tcx> {
     /// Avoid duplicated errors for "name already defined".
     name_already_seen: FxHashMap<Symbol, Span>,
 
-    potentially_unused_imports: Vec<&'a Import<'a>>,
+    potentially_unused_imports: Vec<Import<'a>>,
 
     /// Table for mapping struct IDs into struct constructor IDs,
     /// it's not used during normal resolution, only for better error reporting.
@@ -1085,7 +1085,7 @@ pub struct Resolver<'a, 'tcx> {
 pub struct ResolverArenas<'a> {
     modules: TypedArena<ModuleData<'a>>,
     local_modules: RefCell<Vec<Module<'a>>>,
-    imports: TypedArena<Import<'a>>,
+    imports: TypedArena<ImportData<'a>>,
     name_resolutions: TypedArena<RefCell<NameResolution<'a>>>,
     ast_paths: TypedArena<ast::Path>,
     dropless: DroplessArena,
@@ -1101,8 +1101,13 @@ impl<'a> ResolverArenas<'a> {
         no_implicit_prelude: bool,
         module_map: &mut FxHashMap<DefId, Module<'a>>,
     ) -> Module<'a> {
-        let module =
-            self.modules.alloc(ModuleData::new(parent, kind, expn_id, span, no_implicit_prelude));
+        let module = Module(Interned::new_unchecked(self.modules.alloc(ModuleData::new(
+            parent,
+            kind,
+            expn_id,
+            span,
+            no_implicit_prelude,
+        ))));
         let def_id = module.opt_def_id();
         if def_id.map_or(true, |def_id| def_id.is_local()) {
             self.local_modules.borrow_mut().push(module);
@@ -1115,11 +1120,11 @@ impl<'a> ResolverArenas<'a> {
     fn local_modules(&'a self) -> std::cell::Ref<'a, Vec<Module<'a>>> {
         self.local_modules.borrow()
     }
-    fn alloc_name_binding(&'a self, name_binding: NameBinding<'a>) -> &'a NameBinding<'a> {
-        self.dropless.alloc(name_binding)
+    fn alloc_name_binding(&'a self, name_binding: NameBindingData<'a>) -> NameBinding<'a> {
+        Interned::new_unchecked(self.dropless.alloc(name_binding))
     }
-    fn alloc_import(&'a self, import: Import<'a>) -> &'a Import<'_> {
-        self.imports.alloc(import)
+    fn alloc_import(&'a self, import: ImportData<'a>) -> Import<'a> {
+        Interned::new_unchecked(self.imports.alloc(import))
     }
     fn alloc_name_resolution(&'a self) -> &'a RefCell<NameResolution<'a>> {
         self.name_resolutions.alloc(Default::default())
@@ -1314,7 +1319,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             macro_expanded_macro_export_errors: BTreeSet::new(),
 
             arenas,
-            dummy_binding: arenas.alloc_name_binding(NameBinding {
+            dummy_binding: arenas.alloc_name_binding(NameBindingData {
                 kind: NameBindingKind::Res(Res::Err),
                 ambiguity: None,
                 expansion: LocalExpnId::ROOT,
@@ -1624,7 +1629,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                 self.maybe_unused_trait_imports.insert(def_id);
                 import_ids.push(def_id);
             }
-            self.add_to_glob_map(&import, trait_name);
+            self.add_to_glob_map(*import, trait_name);
             kind = &binding.kind;
         }
         import_ids
@@ -1646,7 +1651,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             module.populate_on_access.set(false);
             self.build_reduced_graph_external(module);
         }
-        &module.lazy_resolutions
+        &module.0.0.lazy_resolutions
     }
 
     fn resolution(
@@ -1679,12 +1684,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         false
     }
 
-    fn record_use(
-        &mut self,
-        ident: Ident,
-        used_binding: &'a NameBinding<'a>,
-        is_lexical_scope: bool,
-    ) {
+    fn record_use(&mut self, ident: Ident, used_binding: NameBinding<'a>, is_lexical_scope: bool) {
         if let Some((b2, kind)) = used_binding.ambiguity {
             let ambiguity_error = AmbiguityError {
                 kind,
@@ -1704,10 +1704,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             // but not introduce it, as used if they are accessed from lexical scope.
             if is_lexical_scope {
                 if let Some(entry) = self.extern_prelude.get(&ident.normalize_to_macros_2_0()) {
-                    if let Some(crate_item) = entry.extern_crate_item {
-                        if ptr::eq(used_binding, crate_item) && !entry.introduced_by_item {
-                            return;
-                        }
+                    if !entry.introduced_by_item && entry.extern_crate_item == Some(used_binding) {
+                        return;
                     }
                 }
             }
@@ -1716,13 +1714,13 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             if let Some(id) = import.id() {
                 self.used_imports.insert(id);
             }
-            self.add_to_glob_map(&import, ident);
+            self.add_to_glob_map(import, ident);
             self.record_use(ident, binding, false);
         }
     }
 
     #[inline]
-    fn add_to_glob_map(&mut self, import: &Import<'_>, ident: Ident) {
+    fn add_to_glob_map(&mut self, import: Import<'_>, ident: Ident) {
         if let ImportKind::Glob { id, .. } = import.kind {
             let def_id = self.local_def_id(id);
             self.glob_map.entry(def_id).or_default().insert(ident.name);
@@ -1831,11 +1829,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         vis.is_accessible_from(module.nearest_parent_mod(), self.tcx)
     }
 
-    fn set_binding_parent_module(&mut self, binding: &'a NameBinding<'a>, module: Module<'a>) {
-        if let Some(old_module) =
-            self.binding_parent_modules.insert(Interned::new_unchecked(binding), module)
-        {
-            if !ptr::eq(module, old_module) {
+    fn set_binding_parent_module(&mut self, binding: NameBinding<'a>, module: Module<'a>) {
+        if let Some(old_module) = self.binding_parent_modules.insert(binding, module) {
+            if module != old_module {
                 span_bug!(binding.span, "parent module is reset for binding");
             }
         }
@@ -1843,25 +1839,25 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
 
     fn disambiguate_macro_rules_vs_modularized(
         &self,
-        macro_rules: &'a NameBinding<'a>,
-        modularized: &'a NameBinding<'a>,
+        macro_rules: NameBinding<'a>,
+        modularized: NameBinding<'a>,
     ) -> bool {
         // Some non-controversial subset of ambiguities "modularized macro name" vs "macro_rules"
         // is disambiguated to mitigate regressions from macro modularization.
         // Scoping for `macro_rules` behaves like scoping for `let` at module level, in general.
         match (
-            self.binding_parent_modules.get(&Interned::new_unchecked(macro_rules)),
-            self.binding_parent_modules.get(&Interned::new_unchecked(modularized)),
+            self.binding_parent_modules.get(&macro_rules),
+            self.binding_parent_modules.get(&modularized),
         ) {
             (Some(macro_rules), Some(modularized)) => {
                 macro_rules.nearest_parent_mod() == modularized.nearest_parent_mod()
-                    && modularized.is_ancestor_of(macro_rules)
+                    && modularized.is_ancestor_of(*macro_rules)
             }
             _ => false,
         }
     }
 
-    fn extern_prelude_get(&mut self, ident: Ident, finalize: bool) -> Option<&'a NameBinding<'a>> {
+    fn extern_prelude_get(&mut self, ident: Ident, finalize: bool) -> Option<NameBinding<'a>> {
         if ident.is_path_segment_keyword() {
             // Make sure `self`, `super` etc produce an error when passed to here.
             return None;
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index d33e8d40b63..d16b7902f60 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -42,7 +42,7 @@ type Res = def::Res<NodeId>;
 /// Not modularized, can shadow previous `macro_rules` bindings, etc.
 #[derive(Debug)]
 pub(crate) struct MacroRulesBinding<'a> {
-    pub(crate) binding: &'a NameBinding<'a>,
+    pub(crate) binding: NameBinding<'a>,
     /// `macro_rules` scope into which the `macro_rules` item was planted.
     pub(crate) parent_macro_rules_scope: MacroRulesScopeRef<'a>,
     pub(crate) ident: Ident,
@@ -870,7 +870,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
 
     fn prohibit_imported_non_macro_attrs(
         &self,
-        binding: Option<&'a NameBinding<'a>>,
+        binding: Option<NameBinding<'a>>,
         res: Option<Res>,
         span: Span,
     ) {
diff --git a/compiler/rustc_serialize/Cargo.toml b/compiler/rustc_serialize/Cargo.toml
index 6046780685a..46b923e8c7b 100644
--- a/compiler/rustc_serialize/Cargo.toml
+++ b/compiler/rustc_serialize/Cargo.toml
@@ -4,7 +4,7 @@ version = "0.0.0"
 edition = "2021"
 
 [dependencies]
-indexmap = "1.9.3"
+indexmap = "2.0.0"
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
 thin-vec = "0.2.12"
 
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 2fe7a6f511b..f97cb3440d2 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -737,14 +737,20 @@ pub enum PrintRequest {
 pub enum TraitSolver {
     /// Classic trait solver in `rustc_trait_selection::traits::select`
     Classic,
-    /// Chalk trait solver
-    Chalk,
     /// Experimental trait solver in `rustc_trait_selection::solve`
     Next,
     /// Use the new trait solver during coherence
     NextCoherence,
 }
 
+#[derive(Default, Debug, Copy, Clone, Hash, PartialEq, Eq)]
+pub enum DumpSolverProofTree {
+    Always,
+    OnError,
+    #[default]
+    Never,
+}
+
 pub enum Input {
     /// Load source code from a file.
     File(PathBuf),
@@ -1021,6 +1027,7 @@ impl Default for Options {
             json_future_incompat: false,
             pretty: None,
             working_dir: RealFileName::LocalPath(std::env::current_dir().unwrap()),
+            color: ColorConfig::Auto,
         }
     }
 }
@@ -2801,6 +2808,7 @@ pub fn build_session_options(
         json_future_incompat,
         pretty,
         working_dir,
+        color,
     }
 }
 
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 16a4c2a8b3d..7840a0ecf0b 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -4,6 +4,7 @@ use crate::search_paths::SearchPath;
 use crate::utils::NativeLib;
 use crate::{lint, EarlyErrorHandler};
 use rustc_data_structures::profiling::TimePassesFormat;
+use rustc_errors::ColorConfig;
 use rustc_errors::{LanguageIdentifier, TerminalUrl};
 use rustc_target::spec::{CodeModel, LinkerFlavorCli, MergeFunctions, PanicStrategy, SanitizerSet};
 use rustc_target::spec::{
@@ -212,6 +213,7 @@ top_level_options!(
 
         /// The (potentially remapped) working directory
         working_dir: RealFileName [TRACKED],
+        color: ColorConfig [UNTRACKED],
     }
 );
 
@@ -386,7 +388,7 @@ mod desc {
     pub const parse_unpretty: &str = "`string` or `string=string`";
     pub const parse_treat_err_as_bug: &str = "either no value or a number bigger than 0";
     pub const parse_trait_solver: &str =
-        "one of the supported solver modes (`classic`, `chalk`, or `next`)";
+        "one of the supported solver modes (`classic`, `next`, or `next-coherence`)";
     pub const parse_lto: &str =
         "either a boolean (`yes`, `no`, `on`, `off`, etc), `thin`, `fat`, or omitted";
     pub const parse_linker_plugin_lto: &str =
@@ -418,6 +420,7 @@ mod desc {
         "a `,` separated combination of `bti`, `b-key`, `pac-ret`, or `leaf`";
     pub const parse_proc_macro_execution_strategy: &str =
         "one of supported execution strategies (`same-thread`, or `cross-thread`)";
+    pub const parse_dump_solver_proof_tree: &str = "one of: `always`, `on-request`, `on-error`";
 }
 
 mod parse {
@@ -983,7 +986,6 @@ mod parse {
     pub(crate) fn parse_trait_solver(slot: &mut TraitSolver, v: Option<&str>) -> bool {
         match v {
             Some("classic") => *slot = TraitSolver::Classic,
-            Some("chalk") => *slot = TraitSolver::Chalk,
             Some("next") => *slot = TraitSolver::Next,
             Some("next-coherence") => *slot = TraitSolver::NextCoherence,
             // default trait solver is subject to change..
@@ -1238,6 +1240,19 @@ mod parse {
         };
         true
     }
+
+    pub(crate) fn parse_dump_solver_proof_tree(
+        slot: &mut DumpSolverProofTree,
+        v: Option<&str>,
+    ) -> bool {
+        match v {
+            None | Some("always") => *slot = DumpSolverProofTree::Always,
+            Some("never") => *slot = DumpSolverProofTree::Never,
+            Some("on-error") => *slot = DumpSolverProofTree::OnError,
+            _ => return false,
+        };
+        true
+    }
 }
 
 options! {
@@ -1463,8 +1478,11 @@ 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"),
+    dump_solver_proof_tree: DumpSolverProofTree = (DumpSolverProofTree::Never, parse_dump_solver_proof_tree, [UNTRACKED],
+        "dump a proof tree for every goal evaluated by the new trait solver. If the flag is specified without any options after it
+        then it defaults to `always`. If the flag is not specified at all it defaults to `on-request`."),
+    dump_solver_proof_tree_use_cache: Option<bool> = (None, parse_opt_bool, [UNTRACKED],
+        "determines whether dumped proof trees use the global cache"),
     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/Cargo.toml b/compiler/rustc_smir/Cargo.toml
index 80360a3c73f..a6e6de5f785 100644
--- a/compiler/rustc_smir/Cargo.toml
+++ b/compiler/rustc_smir/Cargo.toml
@@ -8,6 +8,7 @@ rustc_hir = { path = "../rustc_hir" }
 rustc_middle = { path = "../rustc_middle", optional = true }
 rustc_span = { path = "../rustc_span", optional = true }
 tracing = "0.1"
+scoped-tls = "1.0"
 
 [features]
 default = [
diff --git a/compiler/rustc_smir/src/lib.rs b/compiler/rustc_smir/src/lib.rs
index b00f0a1c153..fb03633b99b 100644
--- a/compiler/rustc_smir/src/lib.rs
+++ b/compiler/rustc_smir/src/lib.rs
@@ -19,3 +19,6 @@ pub mod stable_mir;
 
 // Make this module private for now since external users should not call these directly.
 mod rustc_smir;
+
+#[macro_use]
+extern crate scoped_tls;
diff --git a/compiler/rustc_smir/src/stable_mir/mod.rs b/compiler/rustc_smir/src/stable_mir/mod.rs
index 612777b9c75..5e599a77bcd 100644
--- a/compiler/rustc_smir/src/stable_mir/mod.rs
+++ b/compiler/rustc_smir/src/stable_mir/mod.rs
@@ -100,18 +100,17 @@ pub trait Context {
     fn rustc_tables(&mut self, f: &mut dyn FnMut(&mut Tables<'_>));
 }
 
-thread_local! {
-    /// A thread local variable that stores a pointer to the tables mapping between TyCtxt
-    /// datastructures and stable MIR datastructures.
-    static TLV: Cell<*mut ()> = const { Cell::new(std::ptr::null_mut()) };
-}
+// A thread local variable that stores a pointer to the tables mapping between TyCtxt
+// datastructures and stable MIR datastructures
+scoped_thread_local! (static TLV: Cell<*mut ()>);
 
 pub fn run(mut context: impl Context, f: impl FnOnce()) {
-    assert!(TLV.get().is_null());
+    assert!(!TLV.is_set());
     fn g<'a>(mut context: &mut (dyn Context + 'a), f: impl FnOnce()) {
-        TLV.set(&mut context as *mut &mut _ as _);
-        f();
-        TLV.replace(std::ptr::null_mut());
+        let ptr: *mut () = &mut context as *mut &mut _ as _;
+        TLV.set(&Cell::new(ptr), || {
+            f();
+        });
     }
     g(&mut context, f);
 }
@@ -119,9 +118,10 @@ pub fn run(mut context: impl Context, f: impl FnOnce()) {
 /// Loads the current context and calls a function with it.
 /// Do not nest these, as that will ICE.
 pub(crate) fn with<R>(f: impl FnOnce(&mut dyn Context) -> R) -> R {
-    let ptr = TLV.replace(std::ptr::null_mut()) as *mut &mut dyn Context;
-    assert!(!ptr.is_null());
-    let ret = f(unsafe { *ptr });
-    TLV.set(ptr as _);
-    ret
+    assert!(TLV.is_set());
+    TLV.with(|tlv| {
+        let ptr = tlv.get();
+        assert!(!ptr.is_null());
+        f(unsafe { *(ptr as *mut &mut dyn Context) })
+    })
 }
diff --git a/compiler/rustc_span/Cargo.toml b/compiler/rustc_span/Cargo.toml
index a7c7575f392..ee93f74e750 100644
--- a/compiler/rustc_span/Cargo.toml
+++ b/compiler/rustc_span/Cargo.toml
@@ -18,4 +18,4 @@ tracing = "0.1"
 sha1 = "0.10.0"
 sha2 = "0.10.1"
 md5 = { package = "md-5", version = "0.10.0" }
-indexmap = { version = "1.9.3" }
+indexmap = { version = "2.0.0" }
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index c58d85b99f7..66a627d5aac 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -656,6 +656,7 @@ symbols! {
         dyn_trait,
         e,
         edition_panic,
+        effects,
         eh_catch_typeinfo,
         eh_personality,
         emit,
@@ -793,6 +794,7 @@ symbols! {
         hexagon_target_feature,
         hidden,
         homogeneous_aggregate,
+        host,
         html_favicon_url,
         html_logo_url,
         html_no_source,
@@ -1284,6 +1286,7 @@ symbols! {
         rustc_evaluate_where_clauses,
         rustc_expected_cgu_reuse,
         rustc_has_incoherent_inherent_impls,
+        rustc_host,
         rustc_if_this_changed,
         rustc_inherit_overflow_checks,
         rustc_insignificant_dtor,
diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs
index 254ede4e6a0..ec7032cd3bf 100644
--- a/compiler/rustc_symbol_mangling/src/legacy.rs
+++ b/compiler/rustc_symbol_mangling/src/legacy.rs
@@ -230,7 +230,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolPrinter<'tcx> {
                 self.write_str("[")?;
                 self = self.print_type(ty)?;
                 self.write_str("; ")?;
-                if let Some(size) = size.kind().try_to_bits(self.tcx().data_layout.pointer_size) {
+                if let Some(size) = size.try_to_bits(self.tcx().data_layout.pointer_size) {
                     write!(self, "{size}")?
                 } else if let ty::ConstKind::Param(param) = size.kind() {
                     self = param.print(self)?
diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs
index 85825513ce9..3b46275ec41 100644
--- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs
+++ b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs
@@ -112,7 +112,7 @@ fn encode_const<'tcx>(
         let _ = write!(s, "{value}");
     }
 
-    if let Some(scalar_int) = c.kind().try_to_scalar_int() {
+    if let Some(scalar_int) = c.try_to_scalar_int() {
         let signed = c.ty().is_signed();
         match scalar_int.size().bits() {
             8 if signed => push_signed_value(&mut s, scalar_int.try_to_i8().unwrap(), 0),
@@ -504,8 +504,7 @@ fn encode_ty<'tcx>(
             let _ = write!(
                 s,
                 "{}",
-                &len.kind()
-                    .try_to_scalar()
+                &len.try_to_scalar()
                     .unwrap()
                     .to_u64()
                     .unwrap_or_else(|_| panic!("failed to convert length to u64"))
@@ -644,7 +643,7 @@ fn encode_ty<'tcx>(
             s.push_str("u3refI");
             s.push_str(&encode_ty(tcx, *ty0, dict, options));
             s.push('E');
-            compress(dict, DictKey::Ty(tcx.mk_imm_ref(*region, *ty0), TyQ::None), &mut s);
+            compress(dict, DictKey::Ty(Ty::new_imm_ref(tcx, *region, *ty0), TyQ::None), &mut s);
             if ty.is_mutable_ptr() {
                 s = format!("{}{}", "U3mut", &s);
                 compress(dict, DictKey::Ty(ty, TyQ::Mut), &mut s);
@@ -740,7 +739,7 @@ fn transform_substs<'tcx>(
     options: TransformTyOptions,
 ) -> SubstsRef<'tcx> {
     let substs = substs.iter().map(|subst| match subst.unpack() {
-        GenericArgKind::Type(ty) if ty.is_c_void(tcx) => tcx.mk_unit().into(),
+        GenericArgKind::Type(ty) if ty.is_c_void(tcx) => Ty::new_unit(tcx).into(),
         GenericArgKind::Type(ty) => transform_ty(tcx, ty, options).into(),
         _ => subst,
     });
@@ -810,29 +809,28 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio
         _ if ty.is_unit() => {}
 
         ty::Tuple(tys) => {
-            ty = tcx.mk_tup_from_iter(tys.iter().map(|ty| transform_ty(tcx, ty, options)));
+            ty = Ty::new_tup_from_iter(tcx, tys.iter().map(|ty| transform_ty(tcx, ty, options)));
         }
 
         ty::Array(ty0, len) => {
             let len = len
-                .kind()
                 .try_to_scalar()
                 .unwrap()
                 .to_u64()
                 .unwrap_or_else(|_| panic!("failed to convert length to u64"));
-            ty = tcx.mk_array(transform_ty(tcx, *ty0, options), len);
+            ty = Ty::new_array(tcx, transform_ty(tcx, *ty0, options), len);
         }
 
         ty::Slice(ty0) => {
-            ty = tcx.mk_slice(transform_ty(tcx, *ty0, options));
+            ty = Ty::new_slice(tcx, transform_ty(tcx, *ty0, options));
         }
 
         ty::Adt(adt_def, substs) => {
             if ty.is_c_void(tcx) {
-                ty = tcx.mk_unit();
+                ty = Ty::new_unit(tcx);
             } else if options.contains(TransformTyOptions::GENERALIZE_REPR_C) && adt_def.repr().c()
             {
-                ty = tcx.mk_adt(*adt_def, ty::List::empty());
+                ty = Ty::new_adt(tcx, *adt_def, ty::List::empty());
             } else if adt_def.repr().transparent() && adt_def.is_struct() {
                 // Don't transform repr(transparent) types with an user-defined CFI encoding to
                 // preserve the user-defined CFI encoding.
@@ -863,37 +861,42 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio
                     }
                 } else {
                     // Transform repr(transparent) types without non-ZST field into ()
-                    ty = tcx.mk_unit();
+                    ty = Ty::new_unit(tcx);
                 }
             } else {
-                ty = tcx.mk_adt(*adt_def, transform_substs(tcx, substs, options));
+                ty = Ty::new_adt(tcx, *adt_def, transform_substs(tcx, substs, options));
             }
         }
 
         ty::FnDef(def_id, substs) => {
-            ty = tcx.mk_fn_def(*def_id, transform_substs(tcx, substs, options));
+            ty = Ty::new_fn_def(tcx, *def_id, transform_substs(tcx, substs, options));
         }
 
         ty::Closure(def_id, substs) => {
-            ty = tcx.mk_closure(*def_id, transform_substs(tcx, substs, options));
+            ty = Ty::new_closure(tcx, *def_id, transform_substs(tcx, substs, options));
         }
 
         ty::Generator(def_id, substs, movability) => {
-            ty = tcx.mk_generator(*def_id, transform_substs(tcx, substs, options), *movability);
+            ty = Ty::new_generator(
+                tcx,
+                *def_id,
+                transform_substs(tcx, substs, options),
+                *movability,
+            );
         }
 
         ty::Ref(region, ty0, ..) => {
             if options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
                 if ty.is_mutable_ptr() {
-                    ty = tcx.mk_mut_ref(tcx.lifetimes.re_static, tcx.mk_unit());
+                    ty = Ty::new_mut_ref(tcx, tcx.lifetimes.re_static, Ty::new_unit(tcx));
                 } else {
-                    ty = tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_unit());
+                    ty = Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, Ty::new_unit(tcx));
                 }
             } else {
                 if ty.is_mutable_ptr() {
-                    ty = tcx.mk_mut_ref(*region, transform_ty(tcx, *ty0, options));
+                    ty = Ty::new_mut_ref(tcx, *region, transform_ty(tcx, *ty0, options));
                 } else {
-                    ty = tcx.mk_imm_ref(*region, transform_ty(tcx, *ty0, options));
+                    ty = Ty::new_imm_ref(tcx, *region, transform_ty(tcx, *ty0, options));
                 }
             }
         }
@@ -901,22 +904,22 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio
         ty::RawPtr(tm) => {
             if options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
                 if ty.is_mutable_ptr() {
-                    ty = tcx.mk_mut_ptr(tcx.mk_unit());
+                    ty = Ty::new_mut_ptr(tcx, Ty::new_unit(tcx));
                 } else {
-                    ty = tcx.mk_imm_ptr(tcx.mk_unit());
+                    ty = Ty::new_imm_ptr(tcx, Ty::new_unit(tcx));
                 }
             } else {
                 if ty.is_mutable_ptr() {
-                    ty = tcx.mk_mut_ptr(transform_ty(tcx, tm.ty, options));
+                    ty = Ty::new_mut_ptr(tcx, transform_ty(tcx, tm.ty, options));
                 } else {
-                    ty = tcx.mk_imm_ptr(transform_ty(tcx, tm.ty, options));
+                    ty = Ty::new_imm_ptr(tcx, transform_ty(tcx, tm.ty, options));
                 }
             }
         }
 
         ty::FnPtr(fn_sig) => {
             if options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
-                ty = tcx.mk_imm_ptr(tcx.mk_unit());
+                ty = Ty::new_imm_ptr(tcx, Ty::new_unit(tcx));
             } else {
                 let parameters: Vec<Ty<'tcx>> = fn_sig
                     .skip_binder()
@@ -925,21 +928,25 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio
                     .map(|ty| transform_ty(tcx, *ty, options))
                     .collect();
                 let output = transform_ty(tcx, fn_sig.skip_binder().output(), options);
-                ty = tcx.mk_fn_ptr(ty::Binder::bind_with_vars(
-                    tcx.mk_fn_sig(
-                        parameters,
-                        output,
-                        fn_sig.c_variadic(),
-                        fn_sig.unsafety(),
-                        fn_sig.abi(),
+                ty = Ty::new_fn_ptr(
+                    tcx,
+                    ty::Binder::bind_with_vars(
+                        tcx.mk_fn_sig(
+                            parameters,
+                            output,
+                            fn_sig.c_variadic(),
+                            fn_sig.unsafety(),
+                            fn_sig.abi(),
+                        ),
+                        fn_sig.bound_vars(),
                     ),
-                    fn_sig.bound_vars(),
-                ));
+                );
             }
         }
 
         ty::Dynamic(predicates, _region, kind) => {
-            ty = tcx.mk_dynamic(
+            ty = Ty::new_dynamic(
+                tcx,
                 transform_predicates(tcx, predicates, options),
                 tcx.lifetimes.re_erased,
                 *kind,
@@ -1108,14 +1115,16 @@ pub fn typeid_for_instance<'tcx>(
             )]);
             // Is the concrete self mutable?
             let self_ty = if fn_abi.args[0].layout.ty.is_mutable_ptr() {
-                tcx.mk_mut_ref(
+                Ty::new_mut_ref(
+                    tcx,
                     tcx.lifetimes.re_erased,
-                    tcx.mk_dynamic(existential_predicates, tcx.lifetimes.re_erased, ty::Dyn),
+                    Ty::new_dynamic(tcx, existential_predicates, tcx.lifetimes.re_erased, ty::Dyn),
                 )
             } else {
-                tcx.mk_imm_ref(
+                Ty::new_imm_ref(
+                    tcx,
                     tcx.lifetimes.re_erased,
-                    tcx.mk_dynamic(existential_predicates, tcx.lifetimes.re_erased, ty::Dyn),
+                    Ty::new_dynamic(tcx, existential_predicates, tcx.lifetimes.re_erased, ty::Dyn),
                 )
             };
 
diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs
index 5dc00e31786..5e5cc6e4e4a 100644
--- a/compiler/rustc_symbol_mangling/src/v0.rs
+++ b/compiler/rustc_symbol_mangling/src/v0.rs
@@ -535,7 +535,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
                 match predicate.as_ref().skip_binder() {
                     ty::ExistentialPredicate::Trait(trait_ref) => {
                         // Use a type that can't appear in defaults of type parameters.
-                        let dummy_self = cx.tcx.mk_fresh_ty(0);
+                        let dummy_self = Ty::new_fresh(cx.tcx, 0);
                         let trait_ref = trait_ref.with_self_ty(cx.tcx, dummy_self);
                         cx = cx.print_def_path(trait_ref.def_id, trait_ref.substs)?;
                     }
@@ -651,7 +651,8 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
                             .builtin_deref(true)
                             .expect("tried to dereference on non-ptr type")
                             .ty;
-                        let dereferenced_const = self.tcx.mk_const(ct.kind(), pointee_ty);
+                        // FIXME(const_generics): add an assert that we only do this for valtrees.
+                        let dereferenced_const = self.tcx.mk_ct_from_kind(ct.kind(), pointee_ty);
                         self = dereferenced_const.print(self)?;
                     }
                 }
diff --git a/compiler/rustc_target/src/abi/call/x86_64.rs b/compiler/rustc_target/src/abi/call/x86_64.rs
index 74ef53915c9..b1aefaf0507 100644
--- a/compiler/rustc_target/src/abi/call/x86_64.rs
+++ b/compiler/rustc_target/src/abi/call/x86_64.rs
@@ -153,9 +153,9 @@ fn reg_component(cls: &[Option<Class>], i: &mut usize, size: Size) -> Option<Reg
     }
 }
 
-fn cast_target(cls: &[Option<Class>], size: Size) -> CastTarget {
+fn cast_target(cls: &[Option<Class>], size: Size) -> Option<CastTarget> {
     let mut i = 0;
-    let lo = reg_component(cls, &mut i, size).unwrap();
+    let lo = reg_component(cls, &mut i, size)?;
     let offset = Size::from_bytes(8) * (i as u64);
     let mut target = CastTarget::from(lo);
     if size > offset {
@@ -164,7 +164,7 @@ fn cast_target(cls: &[Option<Class>], size: Size) -> CastTarget {
         }
     }
     assert_eq!(reg_component(cls, &mut i, Size::ZERO), None);
-    target
+    Some(target)
 }
 
 const MAX_INT_REGS: usize = 6; // RDI, RSI, RDX, RCX, R8, R9
@@ -227,7 +227,9 @@ where
                 // split into sized chunks passed individually
                 if arg.layout.is_aggregate() {
                     let size = arg.layout.size;
-                    arg.cast_to(cast_target(cls, size))
+                    if let Some(cast_target) = cast_target(cls, size) {
+                        arg.cast_to(cast_target);
+                    }
                 } else {
                     arg.extend_integer_width_to(32);
                 }
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index 2e5bb3db886..2365dfaf1af 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -1325,6 +1325,7 @@ supported_targets! {
     ("armv7-unknown-netbsd-eabihf", armv7_unknown_netbsd_eabihf),
     ("i686-unknown-netbsd", i686_unknown_netbsd),
     ("powerpc-unknown-netbsd", powerpc_unknown_netbsd),
+    ("riscv64gc-unknown-netbsd", riscv64gc_unknown_netbsd),
     ("sparc64-unknown-netbsd", sparc64_unknown_netbsd),
     ("x86_64-unknown-netbsd", x86_64_unknown_netbsd),
 
diff --git a/compiler/rustc_target/src/spec/riscv64gc_unknown_netbsd.rs b/compiler/rustc_target/src/spec/riscv64gc_unknown_netbsd.rs
new file mode 100644
index 00000000000..a89bd363a47
--- /dev/null
+++ b/compiler/rustc_target/src/spec/riscv64gc_unknown_netbsd.rs
@@ -0,0 +1,19 @@
+use crate::spec::{CodeModel, Target, TargetOptions};
+
+pub fn target() -> Target {
+    Target {
+        llvm_target: "riscv64-unknown-netbsd".into(),
+        pointer_width: 64,
+        data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(),
+        arch: "riscv64".into(),
+        options: TargetOptions {
+            code_model: Some(CodeModel::Medium),
+            cpu: "generic-rv64".into(),
+            features: "+m,+a,+f,+d,+c".into(),
+            llvm_abiname: "lp64d".into(),
+            max_atomic_width: Some(64),
+            mcount: "__mcount".into(),
+            ..super::netbsd_base::opts()
+        },
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs
index ca7fcfd8ca1..422a6ee3442 100644
--- a/compiler/rustc_trait_selection/src/solve/alias_relate.rs
+++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs
@@ -1,6 +1,5 @@
 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;
 
@@ -110,12 +109,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         direction: ty::AliasRelationDirection,
         invert: Invert,
     ) -> QueryResult<'tcx> {
-        self.probe(|r| CandidateKind::Candidate { name: "normalizes-to".into(), result: *r }).enter(
-            |ecx| {
-                ecx.normalizes_to_inner(param_env, alias, other, direction, invert)?;
-                ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
-            },
-        )
+        self.probe_candidate("normalizes-to").enter(|ecx| {
+            ecx.normalizes_to_inner(param_env, alias, other, direction, invert)?;
+            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+        })
     }
 
     fn normalizes_to_inner(
@@ -156,20 +153,18 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         alias_rhs: ty::AliasTy<'tcx>,
         direction: ty::AliasRelationDirection,
     ) -> QueryResult<'tcx> {
-        self.probe(|r| CandidateKind::Candidate { name: "substs relate".into(), result: *r }).enter(
-            |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_candidate("substs relate").enter(|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)
+        })
     }
 
     fn assemble_bidirectional_normalizes_to_candidate(
@@ -179,23 +174,22 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         rhs: ty::Term<'tcx>,
         direction: ty::AliasRelationDirection,
     ) -> QueryResult<'tcx> {
-        self.probe(|r| CandidateKind::Candidate { name: "bidir normalizes-to".into(), result: *r })
-            .enter(|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_candidate("bidir normalizes-to").enter(|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)
+        })
     }
 }
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
index 9e8dbd0cde2..28138054ae5 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
@@ -49,7 +49,7 @@ pub(super) enum CandidateSource {
     /// Notable examples are auto traits, `Sized`, and `DiscriminantKind`.
     /// For a list of all traits with builtin impls, check out the
     /// [`EvalCtxt::assemble_builtin_impl_candidates`] method. Not
-    BuiltinImpl,
+    BuiltinImpl(BuiltinImplSource),
     /// An assumption from the environment.
     ///
     /// More precisely we've used the `n-th` assumption in the `param_env`.
@@ -87,6 +87,16 @@ pub(super) enum CandidateSource {
     AliasBound,
 }
 
+/// Records additional information about what kind of built-in impl this is.
+/// This should only be used by selection.
+#[derive(Debug, Clone, Copy)]
+pub(super) enum BuiltinImplSource {
+    TraitUpcasting,
+    Object,
+    Misc,
+    Ambiguity,
+}
+
 /// Methods used to assemble candidates for either trait or projection goals.
 pub(super) trait GoalKind<'tcx>:
     TypeFoldable<TyCtxt<'tcx>> + Copy + Eq + std::fmt::Display
@@ -295,7 +305,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         // least structurally resolve the type one layer.
         if goal.predicate.self_ty().is_ty_var() {
             return vec![Candidate {
-                source: CandidateSource::BuiltinImpl,
+                source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity),
                 result: self
                     .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
                     .unwrap(),
@@ -321,11 +331,20 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         candidates
     }
 
-    /// If the self type of a goal is an alias, computing the relevant candidates is difficult.
+    /// If the self type of a goal is an alias we first try to normalize the self type
+    /// and compute the candidates for the normalized self type in case that succeeds.
+    ///
+    /// These candidates are used in addition to the ones with the alias as a self type.
+    /// We do this to simplify both builtin candidates and for better performance.
+    ///
+    /// We generate the builtin candidates on the fly by looking at the self type, e.g.
+    /// add `FnPtr` candidates if the self type is a function pointer. Handling builtin
+    /// candidates while the self type is still an alias seems difficult. This is similar
+    /// to `try_structurally_resolve_type` during hir typeck (FIXME once implemented).
     ///
-    /// To deal with this, we first try to normalize the self type and add the candidates for the normalized
-    /// self type to the list of candidates in case that succeeds. We also have to consider candidates with the
-    /// projection as a self type as well
+    /// Looking at all impls for some trait goal is prohibitively expensive. We therefore
+    /// only look at implementations with a matching self type. Because of this function,
+    /// we can avoid looking at all existing impls if the self type is an alias.
     #[instrument(level = "debug", skip_all)]
     fn assemble_candidates_after_normalizing_self_ty<G: GoalKind<'tcx>>(
         &mut self,
@@ -344,7 +363,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
                         let result = ecx.evaluate_added_goals_and_make_canonical_response(
                             Certainty::Maybe(MaybeCause::Overflow),
                         )?;
-                        Ok(vec![Candidate { source: CandidateSource::BuiltinImpl, result }])
+                        Ok(vec![Candidate {
+                            source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity),
+                            result,
+                        }])
                     },
                     |ecx| {
                         let normalized_ty = ecx.next_ty_infer();
@@ -447,9 +469,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         };
 
         match result {
-            Ok(result) => {
-                candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result })
-            }
+            Ok(result) => candidates.push(Candidate {
+                source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
+                result,
+            }),
             Err(NoSolution) => (),
         }
 
@@ -457,7 +480,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         // `trait Foo: Bar<A> + Bar<B>` and `dyn Foo: Unsize<dyn Bar<_>>`
         if lang_items.unsize_trait() == Some(trait_def_id) {
             for result in G::consider_builtin_dyn_upcast_candidates(self, goal) {
-                candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result });
+                candidates.push(Candidate {
+                    source: CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting),
+                    result,
+                });
             }
         }
     }
@@ -621,9 +647,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         };
 
         match result {
-            Ok(result) => {
-                candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result })
-            }
+            Ok(result) => candidates.push(Candidate {
+                source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
+                result,
+            }),
             Err(NoSolution) => (),
         }
     }
@@ -688,9 +715,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
             }
 
             match G::consider_object_bound_candidate(self, goal, assumption) {
-                Ok(result) => {
-                    candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result })
-                }
+                Ok(result) => candidates.push(Candidate {
+                    source: CandidateSource::BuiltinImpl(BuiltinImplSource::Object),
+                    result,
+                }),
                 Err(NoSolution) => (),
             }
         }
@@ -711,8 +739,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
                     Err(_) => match self
                         .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
                     {
-                        Ok(result) => candidates
-                            .push(Candidate { source: CandidateSource::BuiltinImpl, result }),
+                        Ok(result) => candidates.push(Candidate {
+                            source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity),
+                            result,
+                        }),
                         // FIXME: This will be reachable at some point if we're in
                         // `assemble_candidates_after_normalizing_self_ty` and we get a
                         // universe error. We'll deal with it at this point.
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
index 9eac53c3983..3bb8cad15ab 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
@@ -28,7 +28,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>(
         | ty::Char => Ok(vec![]),
 
         // Treat `str` like it's defined as `struct str([u8]);`
-        ty::Str => Ok(vec![tcx.mk_slice(tcx.types.u8)]),
+        ty::Str => Ok(vec![Ty::new_slice(tcx, tcx.types.u8)]),
 
         ty::Dynamic(..)
         | ty::Param(..)
@@ -233,7 +233,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
             {
                 Ok(Some(
                     sig.subst(tcx, substs)
-                        .map_bound(|sig| (tcx.mk_tup(sig.inputs()), sig.output())),
+                        .map_bound(|sig| (Ty::new_tup(tcx, sig.inputs()), sig.output())),
                 ))
             } else {
                 Err(NoSolution)
@@ -242,7 +242,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
         // keep this in sync with assemble_fn_pointer_candidates until the old solver is removed.
         ty::FnPtr(sig) => {
             if sig.is_fn_trait_compatible() {
-                Ok(Some(sig.map_bound(|sig| (tcx.mk_tup(sig.inputs()), sig.output()))))
+                Ok(Some(sig.map_bound(|sig| (Ty::new_tup(tcx, sig.inputs()), sig.output()))))
             } else {
                 Err(NoSolution)
             }
diff --git a/compiler/rustc_trait_selection/src/solve/canonicalize.rs b/compiler/rustc_trait_selection/src/solve/canonicalize.rs
index 05248cb9d17..255620489ff 100644
--- a/compiler/rustc_trait_selection/src/solve/canonicalize.rs
+++ b/compiler/rustc_trait_selection/src/solve/canonicalize.rs
@@ -283,7 +283,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> {
                 // any equated inference vars correctly!
                 let root_vid = self.infcx.root_var(vid);
                 if root_vid != vid {
-                    t = self.infcx.tcx.mk_ty_var(root_vid);
+                    t = Ty::new_var(self.infcx.tcx, root_vid);
                     vid = root_vid;
                 }
 
@@ -366,7 +366,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> {
             }),
         );
         let bt = ty::BoundTy { var, kind: BoundTyKind::Anon };
-        self.interner().mk_bound(self.binder_index, bt)
+        Ty::new_bound(self.infcx.tcx, self.binder_index, bt)
     }
 
     fn fold_const(&mut self, mut c: ty::Const<'tcx>) -> ty::Const<'tcx> {
@@ -377,7 +377,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> {
                 // any equated inference vars correctly!
                 let root_vid = self.infcx.root_const_var(vid);
                 if root_vid != vid {
-                    c = self.infcx.tcx.mk_const(ty::InferConst::Var(root_vid), c.ty());
+                    c = ty::Const::new_var(self.infcx.tcx, root_vid, c.ty());
                     vid = root_vid;
                 }
 
@@ -426,6 +426,6 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> {
                 var
             }),
         );
-        self.interner().mk_const(ty::ConstKind::Bound(self.binder_index, var), c.ty())
+        ty::Const::new_bound(self.infcx.tcx, self.binder_index, var, c.ty())
     }
 }
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
index 6aca40b8dbd..74dfbdddbab 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
@@ -9,7 +9,7 @@ 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::inspect;
 use rustc_middle::traits::solve::{
     CanonicalInput, CanonicalResponse, Certainty, IsNormalizesToHack, MaybeCause,
     PredefinedOpaques, PredefinedOpaquesData, QueryResult,
@@ -19,7 +19,9 @@ use rustc_middle::ty::{
     self, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable,
     TypeVisitableExt, TypeVisitor,
 };
+use rustc_session::config::DumpSolverProofTree;
 use rustc_span::DUMMY_SP;
+use std::io::Write;
 use std::ops::ControlFlow;
 
 use crate::traits::specialization_graph;
@@ -28,9 +30,11 @@ use super::inspect::ProofTreeBuilder;
 use super::search_graph::{self, OverflowHandler};
 use super::SolverMode;
 use super::{search_graph::SearchGraph, Goal};
+pub use select::InferCtxtSelectExt;
 
 mod canonical;
 mod probe;
+mod select;
 
 pub struct EvalCtxt<'a, 'tcx> {
     /// The inference context that backs (mostly) inference and placeholder terms
@@ -111,9 +115,23 @@ impl NestedGoals<'_> {
 
 #[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)]
 pub enum GenerateProofTree {
+    Yes(UseGlobalCache),
+    No,
+}
+
+#[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)]
+pub enum UseGlobalCache {
     Yes,
     No,
 }
+impl UseGlobalCache {
+    pub fn from_bool(use_cache: bool) -> Self {
+        match use_cache {
+            true => UseGlobalCache::Yes,
+            false => UseGlobalCache::No,
+        }
+    }
+}
 
 pub trait InferCtxtEvalExt<'tcx> {
     /// Evaluates a goal from **outside** of the trait solver.
@@ -140,15 +158,34 @@ impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
         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);
+        EvalCtxt::enter_root(self, generate_proof_tree, |ecx| {
+            ecx.evaluate_goal(IsNormalizesToHack::No, goal)
+        })
+    }
+}
+
+impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
+    pub(super) fn solver_mode(&self) -> SolverMode {
+        self.search_graph.solver_mode()
+    }
+
+    /// Creates a root evaluation context and search graph. This should only be
+    /// used from outside of any evaluation, and other methods should be preferred
+    /// over using this manually (such as [`InferCtxtEvalExt::evaluate_root_goal`]).
+    fn enter_root<R>(
+        infcx: &InferCtxt<'tcx>,
+        generate_proof_tree: GenerateProofTree,
+        f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> R,
+    ) -> (R, Option<inspect::GoalEvaluation<'tcx>>) {
+        let mode = if infcx.intercrate { SolverMode::Coherence } else { SolverMode::Normal };
+        let mut search_graph = search_graph::SearchGraph::new(infcx.tcx, mode);
 
         let mut ecx = EvalCtxt {
             search_graph: &mut search_graph,
-            infcx: self,
+            infcx: infcx,
             // Only relevant when canonicalizing the response,
             // which we don't do within this evaluation context.
-            predefined_opaques_in_body: self
+            predefined_opaques_in_body: infcx
                 .tcx
                 .mk_predefined_opaques_in_body(PredefinedOpaquesData::default()),
             // Only relevant when canonicalizing the response.
@@ -156,17 +193,17 @@ 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),
+            inspect: ProofTreeBuilder::new_maybe_root(infcx.tcx, generate_proof_tree),
         };
-        let result = ecx.evaluate_goal(IsNormalizesToHack::No, goal);
+        let result = f(&mut ecx);
 
         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);
+        if let (Some(tree), DumpSolverProofTree::Always) =
+            (&tree, infcx.tcx.sess.opts.unstable_opts.dump_solver_proof_tree)
+        {
+            let mut lock = std::io::stdout().lock();
+            let _ = lock.write_fmt(format_args!("{tree:?}"));
+            let _ = lock.flush();
         }
 
         assert!(
@@ -177,11 +214,66 @@ impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
         assert!(search_graph.is_empty());
         (result, tree)
     }
-}
 
-impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
-    pub(super) fn solver_mode(&self) -> SolverMode {
-        self.search_graph.solver_mode()
+    /// Creates a nested evaluation context that shares the same search graph as the
+    /// one passed in. This is suitable for evaluation, granted that the search graph
+    /// has had the nested goal recorded on its stack ([`SearchGraph::with_new_goal`]),
+    /// but it's preferable to use other methods that call this one rather than this
+    /// method directly.
+    ///
+    /// This function takes care of setting up the inference context, setting the anchor,
+    /// and registering opaques from the canonicalized input.
+    fn enter_canonical<R>(
+        tcx: TyCtxt<'tcx>,
+        search_graph: &'a mut search_graph::SearchGraph<'tcx>,
+        canonical_input: CanonicalInput<'tcx>,
+        goal_evaluation: &mut ProofTreeBuilder<'tcx>,
+        f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>, Goal<'tcx, ty::Predicate<'tcx>>) -> R,
+    ) -> R {
+        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);
+        }
+
+        let result = f(&mut ecx, input.goal);
+
+        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();
+        }
+
+        result
     }
 
     /// The entry point of the solver.
@@ -210,53 +302,17 @@ impl<'a, 'tcx> EvalCtxt<'a, '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,
+                EvalCtxt::enter_canonical(
+                    tcx,
                     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
-                    );
-                }
-
-                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();
-                }
-
-                result
+                    canonical_input,
+                    goal_evaluation,
+                    |ecx, goal| {
+                        let result = ecx.compute_goal(goal);
+                        ecx.inspect.query_result(result);
+                        result
+                    },
+                )
             },
         )
     }
@@ -385,24 +441,20 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
                 ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
                     self.compute_well_formed_goal(Goal { param_env, predicate: arg })
                 }
-                ty::PredicateKind::Ambiguous => {
-                    self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
-                }
-                // FIXME: implement this predicate :)
-                ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(_)) => {
-                    self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+                ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(ct)) => {
+                    self.compute_const_evaluatable_goal(Goal { param_env, predicate: ct })
                 }
                 ty::PredicateKind::ConstEquate(_, _) => {
                     bug!("ConstEquate should not be emitted when `-Ztrait-solver=next` is active")
                 }
-                ty::PredicateKind::Clause(ty::ClauseKind::TypeWellFormedFromEnv(..)) => {
-                    bug!("TypeWellFormedFromEnv is only used for Chalk")
-                }
                 ty::PredicateKind::AliasRelate(lhs, rhs, direction) => self
                     .compute_alias_relate_goal(Goal {
                         param_env,
                         predicate: (lhs, rhs, direction),
                     }),
+                ty::PredicateKind::Ambiguous => {
+                    self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
+                }
             }
         } else {
             let kind = self.infcx.instantiate_binder_with_placeholders(kind);
@@ -785,7 +837,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         }
     }
 
-    pub(super) fn can_define_opaque_ty(&mut self, def_id: LocalDefId) -> bool {
+    pub(super) fn can_define_opaque_ty(&self, def_id: LocalDefId) -> bool {
         self.infcx.opaque_type_origin(def_id).is_some()
     }
 
@@ -843,25 +895,19 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
             if candidate_key.def_id != key.def_id {
                 continue;
             }
-            values.extend(
-                self.probe(|r| CandidateKind::Candidate {
-                    name: "opaque type storage".into(),
-                    result: *r,
-                })
-                .enter(|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)
-                }),
-            );
+            values.extend(self.probe_candidate("opaque type storage").enter(|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)
+            }));
         }
         values
     }
@@ -878,7 +924,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         use rustc_middle::mir::interpret::ErrorHandled;
         match self.infcx.try_const_eval_resolve(param_env, unevaluated, ty, None) {
             Ok(ct) => Some(ct),
-            Err(ErrorHandled::Reported(e)) => Some(self.tcx().const_error(ty, e.into())),
+            Err(ErrorHandled::Reported(e)) => Some(ty::Const::new_error(self.tcx(), e.into(), ty)),
             Err(ErrorHandled::TooGeneric) => None,
         }
     }
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs
index 851edf1fa1c..637d458882c 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs
@@ -20,7 +20,7 @@ use rustc_middle::traits::query::NoSolution;
 use rustc_middle::traits::solve::{
     ExternalConstraints, ExternalConstraintsData, MaybeCause, PredefinedOpaquesData, QueryInput,
 };
-use rustc_middle::ty::{self, BoundVar, GenericArgKind, Ty};
+use rustc_middle::ty::{self, BoundVar, GenericArgKind, Ty, TyCtxt, TypeFoldable};
 use rustc_span::DUMMY_SP;
 use std::iter;
 use std::ops::Deref;
@@ -28,10 +28,10 @@ use std::ops::Deref;
 impl<'tcx> EvalCtxt<'_, 'tcx> {
     /// Canonicalizes the goal remembering the original values
     /// for each bound variable.
-    pub(super) fn canonicalize_goal(
+    pub(super) fn canonicalize_goal<T: TypeFoldable<TyCtxt<'tcx>>>(
         &self,
-        goal: Goal<'tcx, ty::Predicate<'tcx>>,
-    ) -> (Vec<ty::GenericArg<'tcx>>, CanonicalInput<'tcx>) {
+        goal: Goal<'tcx, T>,
+    ) -> (Vec<ty::GenericArg<'tcx>>, CanonicalInput<'tcx, T>) {
         let mut orig_values = Default::default();
         let canonical_goal = Canonicalizer::canonicalize(
             self.infcx,
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs
index 5d912fc039d..4477ea7d501 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs
@@ -1,5 +1,5 @@
 use super::EvalCtxt;
-use rustc_middle::traits::solve::inspect;
+use rustc_middle::traits::solve::{inspect, QueryResult};
 use std::marker::PhantomData;
 
 pub(in crate::solve) struct ProbeCtxt<'me, 'a, 'tcx, F, T> {
@@ -44,4 +44,24 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
     {
         ProbeCtxt { ecx: self, probe_kind, _result: PhantomData }
     }
+
+    pub(in crate::solve) fn probe_candidate(
+        &mut self,
+        name: &'static str,
+    ) -> ProbeCtxt<
+        '_,
+        'a,
+        'tcx,
+        impl FnOnce(&QueryResult<'tcx>) -> inspect::CandidateKind<'tcx>,
+        QueryResult<'tcx>,
+    > {
+        ProbeCtxt {
+            ecx: self,
+            probe_kind: move |result: &QueryResult<'tcx>| inspect::CandidateKind::Candidate {
+                name: name.to_string(),
+                result: *result,
+            },
+            _result: PhantomData,
+        }
+    }
 }
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs
new file mode 100644
index 00000000000..0800738a3f2
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs
@@ -0,0 +1,307 @@
+use std::ops::ControlFlow;
+
+use rustc_hir::def_id::DefId;
+use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk};
+use rustc_infer::traits::util::supertraits;
+use rustc_infer::traits::{
+    Obligation, PredicateObligation, Selection, SelectionResult, TraitObligation,
+};
+use rustc_middle::traits::solve::{CanonicalInput, Certainty, Goal};
+use rustc_middle::traits::{
+    ImplSource, ImplSourceObjectData, ImplSourceTraitUpcastingData, ImplSourceUserDefinedData,
+    ObligationCause, SelectionError,
+};
+use rustc_middle::ty::{self, TyCtxt};
+use rustc_span::DUMMY_SP;
+
+use crate::solve::assembly::{BuiltinImplSource, Candidate, CandidateSource};
+use crate::solve::eval_ctxt::{EvalCtxt, GenerateProofTree};
+use crate::solve::inspect::ProofTreeBuilder;
+use crate::solve::search_graph::OverflowHandler;
+use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment};
+
+pub trait InferCtxtSelectExt<'tcx> {
+    fn select_in_new_trait_solver(
+        &self,
+        obligation: &TraitObligation<'tcx>,
+    ) -> SelectionResult<'tcx, Selection<'tcx>>;
+}
+
+impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> {
+    fn select_in_new_trait_solver(
+        &self,
+        obligation: &TraitObligation<'tcx>,
+    ) -> SelectionResult<'tcx, Selection<'tcx>> {
+        assert!(self.next_trait_solver());
+
+        let trait_goal = Goal::new(
+            self.tcx,
+            obligation.param_env,
+            self.instantiate_binder_with_placeholders(obligation.predicate),
+        );
+
+        let (result, _) = EvalCtxt::enter_root(self, GenerateProofTree::No, |ecx| {
+            let goal = Goal::new(ecx.tcx(), trait_goal.param_env, trait_goal.predicate);
+            let (orig_values, canonical_goal) = ecx.canonicalize_goal(goal);
+            let mut candidates = ecx.compute_canonical_trait_candidates(canonical_goal);
+
+            // pseudo-winnow
+            if candidates.len() == 0 {
+                return Err(SelectionError::Unimplemented);
+            } else if candidates.len() > 1 {
+                let mut i = 0;
+                while i < candidates.len() {
+                    let should_drop_i = (0..candidates.len()).filter(|&j| i != j).any(|j| {
+                        candidate_should_be_dropped_in_favor_of(
+                            ecx.tcx(),
+                            &candidates[i],
+                            &candidates[j],
+                        )
+                    });
+                    if should_drop_i {
+                        candidates.swap_remove(i);
+                    } else {
+                        i += 1;
+                        if i > 1 {
+                            return Ok(None);
+                        }
+                    }
+                }
+            }
+
+            let candidate = candidates.pop().unwrap();
+            let (certainty, nested_goals) = ecx
+                .instantiate_and_apply_query_response(
+                    trait_goal.param_env,
+                    orig_values,
+                    candidate.result,
+                )
+                .map_err(|_| SelectionError::Unimplemented)?;
+
+            Ok(Some((candidate, certainty, nested_goals)))
+        });
+
+        let (candidate, certainty, nested_goals) = match result {
+            Ok(Some((candidate, certainty, nested_goals))) => (candidate, certainty, nested_goals),
+            Ok(None) => return Ok(None),
+            Err(e) => return Err(e),
+        };
+
+        let nested_obligations: Vec<_> = nested_goals
+            .into_iter()
+            .map(|goal| {
+                Obligation::new(self.tcx, ObligationCause::dummy(), goal.param_env, goal.predicate)
+            })
+            .collect();
+
+        let goal = self.resolve_vars_if_possible(trait_goal);
+        match (certainty, candidate.source) {
+            // Rematching the implementation will instantiate the same nested goals that
+            // would have caused the ambiguity, so we can still make progress here regardless.
+            (_, CandidateSource::Impl(def_id)) => {
+                rematch_impl(self, goal, def_id, nested_obligations)
+            }
+
+            // Rematching the dyn upcast or object goal will instantiate the same nested
+            // goals that would have caused the ambiguity, so we can still make progress here
+            // regardless.
+            // FIXME: This doesn't actually check the object bounds hold here.
+            (
+                _,
+                CandidateSource::BuiltinImpl(
+                    BuiltinImplSource::Object | BuiltinImplSource::TraitUpcasting,
+                ),
+            ) => rematch_object(self, goal, nested_obligations),
+
+            // Technically some builtin impls have nested obligations, but if
+            // `Certainty::Yes`, then they should've all been verified and don't
+            // need re-checking.
+            (Certainty::Yes, CandidateSource::BuiltinImpl(BuiltinImplSource::Misc)) => {
+                Ok(Some(ImplSource::Builtin(nested_obligations)))
+            }
+
+            // It's fine not to do anything to rematch these, since there are no
+            // nested obligations.
+            (Certainty::Yes, CandidateSource::ParamEnv(_) | CandidateSource::AliasBound) => {
+                Ok(Some(ImplSource::Param(nested_obligations, ty::BoundConstness::NotConst)))
+            }
+
+            (_, CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity))
+            | (Certainty::Maybe(_), _) => Ok(None),
+        }
+    }
+}
+
+impl<'tcx> EvalCtxt<'_, 'tcx> {
+    fn compute_canonical_trait_candidates(
+        &mut self,
+        canonical_input: CanonicalInput<'tcx>,
+    ) -> Vec<Candidate<'tcx>> {
+        // This doesn't record the canonical goal on the stack during the
+        // candidate assembly step, but that's fine. Selection is conceptually
+        // outside of the solver, and if there were any cycles, we'd encounter
+        // the cycle anyways one step later.
+        EvalCtxt::enter_canonical(
+            self.tcx(),
+            self.search_graph(),
+            canonical_input,
+            // FIXME: This is wrong, idk if we even want to track stuff here.
+            &mut ProofTreeBuilder::new_noop(),
+            |ecx, goal| {
+                let trait_goal = Goal {
+                    param_env: goal.param_env,
+                    predicate: goal
+                        .predicate
+                        .to_opt_poly_trait_pred()
+                        .expect("we canonicalized a trait goal")
+                        .no_bound_vars()
+                        .expect("we instantiated all bound vars"),
+                };
+                ecx.assemble_and_evaluate_candidates(trait_goal)
+            },
+        )
+    }
+}
+
+fn candidate_should_be_dropped_in_favor_of<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    victim: &Candidate<'tcx>,
+    other: &Candidate<'tcx>,
+) -> bool {
+    match (victim.source, other.source) {
+        (CandidateSource::ParamEnv(victim_idx), CandidateSource::ParamEnv(other_idx)) => {
+            victim_idx >= other_idx
+        }
+        (_, CandidateSource::ParamEnv(_)) => true,
+
+        (
+            CandidateSource::BuiltinImpl(BuiltinImplSource::Object),
+            CandidateSource::BuiltinImpl(BuiltinImplSource::Object),
+        ) => false,
+        (_, CandidateSource::BuiltinImpl(BuiltinImplSource::Object)) => true,
+
+        (CandidateSource::Impl(victim_def_id), CandidateSource::Impl(other_def_id)) => {
+            tcx.specializes((other_def_id, victim_def_id))
+                && other.result.value.certainty == Certainty::Yes
+        }
+
+        _ => false,
+    }
+}
+
+fn rematch_impl<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    goal: Goal<'tcx, ty::TraitPredicate<'tcx>>,
+    impl_def_id: DefId,
+    mut nested: Vec<PredicateObligation<'tcx>>,
+) -> SelectionResult<'tcx, Selection<'tcx>> {
+    let substs = infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
+    let impl_trait_ref = infcx.tcx.impl_trait_ref(impl_def_id).unwrap().subst(infcx.tcx, substs);
+
+    nested.extend(
+        infcx
+            .at(&ObligationCause::dummy(), goal.param_env)
+            .eq(DefineOpaqueTypes::No, goal.predicate.trait_ref, impl_trait_ref)
+            .map_err(|_| SelectionError::Unimplemented)?
+            .into_obligations(),
+    );
+
+    nested.extend(
+        infcx.tcx.predicates_of(impl_def_id).instantiate(infcx.tcx, substs).into_iter().map(
+            |(pred, _)| Obligation::new(infcx.tcx, ObligationCause::dummy(), goal.param_env, pred),
+        ),
+    );
+
+    Ok(Some(ImplSource::UserDefined(ImplSourceUserDefinedData { impl_def_id, substs, nested })))
+}
+
+fn rematch_object<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    goal: Goal<'tcx, ty::TraitPredicate<'tcx>>,
+    mut nested: Vec<PredicateObligation<'tcx>>,
+) -> SelectionResult<'tcx, Selection<'tcx>> {
+    let self_ty = goal.predicate.self_ty();
+    let ty::Dynamic(data, _, source_kind) = *self_ty.kind()
+    else {
+        bug!()
+    };
+    let source_trait_ref = data.principal().unwrap().with_self_ty(infcx.tcx, self_ty);
+
+    let (is_upcasting, target_trait_ref_unnormalized) = if Some(goal.predicate.def_id())
+        == infcx.tcx.lang_items().unsize_trait()
+    {
+        assert_eq!(source_kind, ty::Dyn, "cannot upcast dyn*");
+        if let ty::Dynamic(data, _, ty::Dyn) = goal.predicate.trait_ref.substs.type_at(1).kind() {
+            (true, data.principal().unwrap().with_self_ty(infcx.tcx, self_ty))
+        } else {
+            bug!()
+        }
+    } else {
+        (false, ty::Binder::dummy(goal.predicate.trait_ref))
+    };
+
+    let mut target_trait_ref = None;
+    for candidate_trait_ref in supertraits(infcx.tcx, source_trait_ref) {
+        let result = infcx.commit_if_ok(|_| {
+            infcx.at(&ObligationCause::dummy(), goal.param_env).eq(
+                DefineOpaqueTypes::No,
+                target_trait_ref_unnormalized,
+                candidate_trait_ref,
+            )
+
+            // FIXME: We probably should at least shallowly verify these...
+        });
+
+        match result {
+            Ok(InferOk { value: (), obligations }) => {
+                target_trait_ref = Some(candidate_trait_ref);
+                nested.extend(obligations);
+                break;
+            }
+            Err(_) => continue,
+        }
+    }
+
+    let target_trait_ref = target_trait_ref.unwrap();
+
+    let mut offset = 0;
+    let Some((vtable_base, vtable_vptr_slot)) =
+        prepare_vtable_segments(infcx.tcx, source_trait_ref, |segment| {
+            match segment {
+                VtblSegment::MetadataDSA => {
+                    offset += TyCtxt::COMMON_VTABLE_ENTRIES.len();
+                }
+                VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
+                    let own_vtable_entries = count_own_vtable_entries(infcx.tcx, trait_ref);
+
+                    if trait_ref == target_trait_ref {
+                        if emit_vptr {
+                            return ControlFlow::Break((
+                                offset,
+                                Some(offset + count_own_vtable_entries(infcx.tcx, trait_ref)),
+                            ));
+                        } else {
+                            return ControlFlow::Break((offset, None));
+                        }
+                    }
+
+                    offset += own_vtable_entries;
+                    if emit_vptr {
+                        offset += 1;
+                    }
+                }
+            }
+            ControlFlow::Continue(())
+        })
+    else {
+        bug!();
+    };
+
+    // If we're upcasting, get the offset of the vtable pointer, otherwise get
+    // the base of the vtable.
+    Ok(Some(if is_upcasting {
+        ImplSource::TraitUpcasting(ImplSourceTraitUpcastingData { vtable_vptr_slot, nested })
+    } else {
+        ImplSource::Object(ImplSourceObjectData { vtable_base, nested })
+    }))
+}
diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs
index fc848fe3080..88ee14c4db5 100644
--- a/compiler/rustc_trait_selection/src/solve/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs
@@ -41,6 +41,7 @@ impl<'tcx> FulfillmentCtxt<'tcx> {
 }
 
 impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
+    #[instrument(level = "debug", skip(self, infcx))]
     fn register_predicate_obligation(
         &mut self,
         infcx: &InferCtxt<'tcx>,
diff --git a/compiler/rustc_trait_selection/src/solve/inspect.rs b/compiler/rustc_trait_selection/src/solve/inspect.rs
index 6d7804a8fad..2d6717fdad9 100644
--- a/compiler/rustc_trait_selection/src/solve/inspect.rs
+++ b/compiler/rustc_trait_selection/src/solve/inspect.rs
@@ -3,9 +3,11 @@ use rustc_middle::traits::solve::inspect::{self, CacheHit, CandidateKind};
 use rustc_middle::traits::solve::{
     CanonicalInput, Certainty, Goal, IsNormalizesToHack, QueryInput, QueryResult,
 };
-use rustc_middle::ty;
+use rustc_middle::ty::{self, TyCtxt};
+use rustc_session::config::DumpSolverProofTree;
 
-pub mod dump;
+use super::eval_ctxt::UseGlobalCache;
+use super::GenerateProofTree;
 
 #[derive(Eq, PartialEq, Debug, Hash, HashStable)]
 pub struct WipGoalEvaluation<'tcx> {
@@ -144,20 +146,42 @@ impl<'tcx> From<WipGoalCandidate<'tcx>> for DebugSolver<'tcx> {
 }
 
 pub struct ProofTreeBuilder<'tcx> {
-    state: Option<Box<DebugSolver<'tcx>>>,
+    state: Option<Box<BuilderData<'tcx>>>,
+}
+
+struct BuilderData<'tcx> {
+    tree: DebugSolver<'tcx>,
+    use_global_cache: UseGlobalCache,
 }
 
 impl<'tcx> ProofTreeBuilder<'tcx> {
-    fn new(state: impl Into<DebugSolver<'tcx>>) -> ProofTreeBuilder<'tcx> {
-        ProofTreeBuilder { state: Some(Box::new(state.into())) }
+    fn new(
+        state: impl Into<DebugSolver<'tcx>>,
+        use_global_cache: UseGlobalCache,
+    ) -> ProofTreeBuilder<'tcx> {
+        ProofTreeBuilder {
+            state: Some(Box::new(BuilderData { tree: state.into(), use_global_cache })),
+        }
+    }
+
+    fn nested(&self, state: impl Into<DebugSolver<'tcx>>) -> Self {
+        match &self.state {
+            Some(prev_state) => Self {
+                state: Some(Box::new(BuilderData {
+                    tree: state.into(),
+                    use_global_cache: prev_state.use_global_cache,
+                })),
+            },
+            None => Self { state: None },
+        }
     }
 
     fn as_mut(&mut self) -> Option<&mut DebugSolver<'tcx>> {
-        self.state.as_mut().map(|boxed| &mut **boxed)
+        self.state.as_mut().map(|boxed| &mut boxed.tree)
     }
 
     pub fn finalize(self) -> Option<inspect::GoalEvaluation<'tcx>> {
-        match *(self.state?) {
+        match self.state?.tree {
             DebugSolver::GoalEvaluation(wip_goal_evaluation) => {
                 Some(wip_goal_evaluation.finalize())
             }
@@ -165,8 +189,46 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
         }
     }
 
-    pub fn new_root() -> ProofTreeBuilder<'tcx> {
-        ProofTreeBuilder::new(DebugSolver::Root)
+    pub fn use_global_cache(&self) -> bool {
+        self.state
+            .as_ref()
+            .map(|state| matches!(state.use_global_cache, UseGlobalCache::Yes))
+            .unwrap_or(true)
+    }
+
+    pub fn new_maybe_root(
+        tcx: TyCtxt<'tcx>,
+        generate_proof_tree: GenerateProofTree,
+    ) -> ProofTreeBuilder<'tcx> {
+        let generate_proof_tree = match (
+            tcx.sess.opts.unstable_opts.dump_solver_proof_tree,
+            tcx.sess.opts.unstable_opts.dump_solver_proof_tree_use_cache,
+            generate_proof_tree,
+        ) {
+            (_, Some(use_cache), GenerateProofTree::Yes(_)) => {
+                GenerateProofTree::Yes(UseGlobalCache::from_bool(use_cache))
+            }
+
+            (DumpSolverProofTree::Always, use_cache, GenerateProofTree::No) => {
+                let use_cache = use_cache.unwrap_or(true);
+                GenerateProofTree::Yes(UseGlobalCache::from_bool(use_cache))
+            }
+
+            (_, None, GenerateProofTree::Yes(_)) => generate_proof_tree,
+            (DumpSolverProofTree::Never, _, _) => generate_proof_tree,
+            (DumpSolverProofTree::OnError, _, _) => generate_proof_tree,
+        };
+
+        match generate_proof_tree {
+            GenerateProofTree::No => ProofTreeBuilder::new_noop(),
+            GenerateProofTree::Yes(global_cache_disabled) => {
+                ProofTreeBuilder::new_root(global_cache_disabled)
+            }
+        }
+    }
+
+    pub fn new_root(use_global_cache: UseGlobalCache) -> ProofTreeBuilder<'tcx> {
+        ProofTreeBuilder::new(DebugSolver::Root, use_global_cache)
     }
 
     pub fn new_noop() -> ProofTreeBuilder<'tcx> {
@@ -186,7 +248,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
             return ProofTreeBuilder { state: None };
         }
 
-        ProofTreeBuilder::new(WipGoalEvaluation {
+        self.nested(WipGoalEvaluation {
             uncanonicalized_goal: goal,
             canonicalized_goal: None,
             evaluation_steps: vec![],
@@ -232,7 +294,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
     }
     pub fn goal_evaluation(&mut self, goal_evaluation: ProofTreeBuilder<'tcx>) {
         if let Some(this) = self.as_mut() {
-            match (this, *goal_evaluation.state.unwrap()) {
+            match (this, goal_evaluation.state.unwrap().tree) {
                 (
                     DebugSolver::AddedGoalsEvaluation(WipAddedGoalsEvaluation {
                         evaluations, ..
@@ -253,7 +315,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
             return ProofTreeBuilder { state: None };
         }
 
-        ProofTreeBuilder::new(WipGoalEvaluationStep {
+        self.nested(WipGoalEvaluationStep {
             instantiated_goal,
             nested_goal_evaluations: vec![],
             candidates: vec![],
@@ -262,7 +324,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
     }
     pub fn goal_evaluation_step(&mut self, goal_eval_step: ProofTreeBuilder<'tcx>) {
         if let Some(this) = self.as_mut() {
-            match (this, *goal_eval_step.state.unwrap()) {
+            match (this, goal_eval_step.state.unwrap().tree) {
                 (DebugSolver::GoalEvaluation(goal_eval), DebugSolver::GoalEvaluationStep(step)) => {
                     goal_eval.evaluation_steps.push(step);
                 }
@@ -276,7 +338,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
             return ProofTreeBuilder { state: None };
         }
 
-        ProofTreeBuilder::new(WipGoalCandidate {
+        self.nested(WipGoalCandidate {
             nested_goal_evaluations: vec![],
             candidates: vec![],
             kind: None,
@@ -296,7 +358,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
 
     pub fn goal_candidate(&mut self, candidate: ProofTreeBuilder<'tcx>) {
         if let Some(this) = self.as_mut() {
-            match (this, *candidate.state.unwrap()) {
+            match (this, candidate.state.unwrap().tree) {
                 (
                     DebugSolver::GoalCandidate(WipGoalCandidate { candidates, .. })
                     | DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep { candidates, .. }),
@@ -312,7 +374,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
             return ProofTreeBuilder { state: None };
         }
 
-        ProofTreeBuilder::new(WipAddedGoalsEvaluation { evaluations: vec![], result: None })
+        self.nested(WipAddedGoalsEvaluation { evaluations: vec![], result: None })
     }
 
     pub fn evaluate_added_goals_loop_start(&mut self) {
@@ -339,7 +401,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
 
     pub fn added_goals_evaluation(&mut self, goals_evaluation: ProofTreeBuilder<'tcx>) {
         if let Some(this) = self.as_mut() {
-            match (this, *goals_evaluation.state.unwrap()) {
+            match (this, goals_evaluation.state.unwrap().tree) {
                 (
                     DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep {
                         nested_goal_evaluations,
diff --git a/compiler/rustc_trait_selection/src/solve/inspect/dump.rs b/compiler/rustc_trait_selection/src/solve/inspect/dump.rs
deleted file mode 100644
index b755ee86215..00000000000
--- a/compiler/rustc_trait_selection/src/solve/inspect/dump.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-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 49fecedc0ca..77809d8d2ba 100644
--- a/compiler/rustc_trait_selection/src/solve/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/mod.rs
@@ -26,14 +26,18 @@ mod canonicalize;
 mod eval_ctxt;
 mod fulfill;
 pub mod inspect;
+mod normalize;
 mod opaques;
 mod project_goals;
 mod search_graph;
 mod trait_goals;
 mod weak_types;
 
-pub use eval_ctxt::{EvalCtxt, InferCtxtEvalExt};
+pub use eval_ctxt::{
+    EvalCtxt, GenerateProofTree, InferCtxtEvalExt, InferCtxtSelectExt, UseGlobalCache,
+};
 pub use fulfill::FulfillmentCtxt;
+pub(crate) use normalize::deeply_normalize;
 
 #[derive(Debug, Clone, Copy)]
 enum SolverMode {
@@ -157,6 +161,43 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
         }
     }
 
+    #[instrument(level = "debug", skip(self))]
+    fn compute_const_evaluatable_goal(
+        &mut self,
+        Goal { param_env, predicate: ct }: Goal<'tcx, ty::Const<'tcx>>,
+    ) -> QueryResult<'tcx> {
+        match ct.kind() {
+            ty::ConstKind::Unevaluated(uv) => {
+                // We never return `NoSolution` here as `try_const_eval_resolve` emits an
+                // error itself when failing to evaluate, so emitting an additional fulfillment
+                // error in that case is unnecessary noise. This may change in the future once
+                // evaluation failures are allowed to impact selection, e.g. generic const
+                // expressions in impl headers or `where`-clauses.
+
+                // FIXME(generic_const_exprs): Implement handling for generic
+                // const expressions here.
+                if let Some(_normalized) = self.try_const_eval_resolve(param_env, uv, ct.ty()) {
+                    self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+                } else {
+                    self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
+                }
+            }
+            ty::ConstKind::Infer(_) => {
+                self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
+            }
+            ty::ConstKind::Placeholder(_) | ty::ConstKind::Value(_) | ty::ConstKind::Error(_) => {
+                self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+            }
+            // We can freely ICE here as:
+            // - `Param` gets replaced with a placeholder during canonicalization
+            // - `Bound` cannot exist as we don't have a binder around the self Type
+            // - `Expr` is part of `feature(generic_const_exprs)` and is not implemented yet
+            ty::ConstKind::Param(_) | ty::ConstKind::Bound(_, _) | ty::ConstKind::Expr(_) => {
+                bug!("unexpect const kind: {:?}", ct)
+            }
+        }
+    }
+
     #[instrument(level = "debug", skip(self), ret)]
     fn compute_const_arg_has_type_goal(
         &mut self,
diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs
new file mode 100644
index 00000000000..c0ee1a576e5
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/normalize.rs
@@ -0,0 +1,222 @@
+use crate::traits::error_reporting::TypeErrCtxtExt;
+use crate::traits::query::evaluate_obligation::InferCtxtExt;
+use crate::traits::{needs_normalization, BoundVarReplacer, PlaceholderReplacer};
+use rustc_data_structures::stack::ensure_sufficient_stack;
+use rustc_infer::infer::at::At;
+use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+use rustc_infer::traits::TraitEngineExt;
+use rustc_infer::traits::{FulfillmentError, Obligation, TraitEngine};
+use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
+use rustc_middle::traits::Reveal;
+use rustc_middle::ty::{self, AliasTy, Ty, TyCtxt, UniverseIndex};
+use rustc_middle::ty::{FallibleTypeFolder, TypeSuperFoldable};
+use rustc_middle::ty::{TypeFoldable, TypeVisitableExt};
+
+use super::FulfillmentCtxt;
+
+/// Deeply normalize all aliases in `value`. This does not handle inference and expects
+/// its input to be already fully resolved.
+pub(crate) fn deeply_normalize<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
+    at: At<'_, 'tcx>,
+    value: T,
+) -> Result<T, Vec<FulfillmentError<'tcx>>> {
+    let fulfill_cx = FulfillmentCtxt::new(at.infcx);
+    let mut folder = NormalizationFolder { at, fulfill_cx, depth: 0, universes: Vec::new() };
+
+    value.try_fold_with(&mut folder)
+}
+
+struct NormalizationFolder<'me, 'tcx> {
+    at: At<'me, 'tcx>,
+    fulfill_cx: FulfillmentCtxt<'tcx>,
+    depth: usize,
+    universes: Vec<Option<UniverseIndex>>,
+}
+
+impl<'tcx> NormalizationFolder<'_, 'tcx> {
+    fn normalize_alias_ty(
+        &mut self,
+        alias: AliasTy<'tcx>,
+    ) -> Result<Ty<'tcx>, Vec<FulfillmentError<'tcx>>> {
+        let infcx = self.at.infcx;
+        let tcx = infcx.tcx;
+        let recursion_limit = tcx.recursion_limit();
+        if !recursion_limit.value_within_limit(self.depth) {
+            self.at.infcx.err_ctxt().report_overflow_error(
+                &alias.to_ty(tcx),
+                self.at.cause.span,
+                true,
+                |_| {},
+            );
+        }
+
+        self.depth += 1;
+
+        let new_infer_ty = infcx.next_ty_var(TypeVariableOrigin {
+            kind: TypeVariableOriginKind::NormalizeProjectionType,
+            span: self.at.cause.span,
+        });
+        let obligation = Obligation::new(
+            tcx,
+            self.at.cause.clone(),
+            self.at.param_env,
+            ty::Binder::dummy(ty::ProjectionPredicate {
+                projection_ty: alias,
+                term: new_infer_ty.into(),
+            }),
+        );
+
+        // Do not emit an error if normalization is known to fail but instead
+        // keep the projection unnormalized. This is the case for projections
+        // with a `T: Trait` where-clause and opaque types outside of the defining
+        // scope.
+        let result = if infcx.predicate_may_hold(&obligation) {
+            self.fulfill_cx.register_predicate_obligation(infcx, obligation);
+            let errors = self.fulfill_cx.select_all_or_error(infcx);
+            if !errors.is_empty() {
+                return Err(errors);
+            }
+            let ty = infcx.resolve_vars_if_possible(new_infer_ty);
+            ty.try_fold_with(self)?
+        } else {
+            alias.to_ty(tcx).try_super_fold_with(self)?
+        };
+
+        self.depth -= 1;
+        Ok(result)
+    }
+
+    fn normalize_unevaluated_const(
+        &mut self,
+        ty: Ty<'tcx>,
+        uv: ty::UnevaluatedConst<'tcx>,
+    ) -> Result<ty::Const<'tcx>, Vec<FulfillmentError<'tcx>>> {
+        let infcx = self.at.infcx;
+        let tcx = infcx.tcx;
+        let recursion_limit = tcx.recursion_limit();
+        if !recursion_limit.value_within_limit(self.depth) {
+            self.at.infcx.err_ctxt().report_overflow_error(
+                &ty::Const::new_unevaluated(tcx, uv, ty),
+                self.at.cause.span,
+                true,
+                |_| {},
+            );
+        }
+
+        self.depth += 1;
+
+        let new_infer_ct = infcx.next_const_var(
+            ty,
+            ConstVariableOrigin {
+                kind: ConstVariableOriginKind::MiscVariable,
+                span: self.at.cause.span,
+            },
+        );
+        let obligation = Obligation::new(
+            tcx,
+            self.at.cause.clone(),
+            self.at.param_env,
+            ty::Binder::dummy(ty::ProjectionPredicate {
+                projection_ty: tcx.mk_alias_ty(uv.def, uv.substs),
+                term: new_infer_ct.into(),
+            }),
+        );
+
+        let result = if infcx.predicate_may_hold(&obligation) {
+            self.fulfill_cx.register_predicate_obligation(infcx, obligation);
+            let errors = self.fulfill_cx.select_all_or_error(infcx);
+            if !errors.is_empty() {
+                return Err(errors);
+            }
+            let ct = infcx.resolve_vars_if_possible(new_infer_ct);
+            ct.try_fold_with(self)?
+        } else {
+            ty::Const::new_unevaluated(tcx, uv, ty).try_super_fold_with(self)?
+        };
+
+        self.depth -= 1;
+        Ok(result)
+    }
+}
+
+impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for NormalizationFolder<'_, 'tcx> {
+    type Error = Vec<FulfillmentError<'tcx>>;
+
+    fn interner(&self) -> TyCtxt<'tcx> {
+        self.at.infcx.tcx
+    }
+
+    fn try_fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
+        &mut self,
+        t: ty::Binder<'tcx, T>,
+    ) -> Result<ty::Binder<'tcx, T>, Self::Error> {
+        self.universes.push(None);
+        let t = t.try_super_fold_with(self)?;
+        self.universes.pop();
+        Ok(t)
+    }
+
+    fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> {
+        let reveal = self.at.param_env.reveal();
+        let infcx = self.at.infcx;
+        debug_assert_eq!(ty, infcx.shallow_resolve(ty));
+        if !needs_normalization(&ty, reveal) {
+            return Ok(ty);
+        }
+
+        // We don't normalize opaque types unless we have
+        // `Reveal::All`, even if we're in the defining scope.
+        let data = match *ty.kind() {
+            ty::Alias(kind, alias_ty) if kind != ty::Opaque || reveal == Reveal::UserFacing => {
+                alias_ty
+            }
+            _ => return ty.try_super_fold_with(self),
+        };
+
+        if data.has_escaping_bound_vars() {
+            let (data, mapped_regions, mapped_types, mapped_consts) =
+                BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data);
+            let result = ensure_sufficient_stack(|| self.normalize_alias_ty(data))?;
+            Ok(PlaceholderReplacer::replace_placeholders(
+                infcx,
+                mapped_regions,
+                mapped_types,
+                mapped_consts,
+                &mut self.universes,
+                result,
+            ))
+        } else {
+            ensure_sufficient_stack(|| self.normalize_alias_ty(data))
+        }
+    }
+
+    fn try_fold_const(&mut self, ct: ty::Const<'tcx>) -> Result<ty::Const<'tcx>, Self::Error> {
+        let reveal = self.at.param_env.reveal();
+        let infcx = self.at.infcx;
+        debug_assert_eq!(ct, infcx.shallow_resolve(ct));
+        if !needs_normalization(&ct, reveal) {
+            return Ok(ct);
+        }
+
+        let uv = match ct.kind() {
+            ty::ConstKind::Unevaluated(ct) => ct,
+            _ => return ct.try_super_fold_with(self),
+        };
+
+        if uv.has_escaping_bound_vars() {
+            let (uv, mapped_regions, mapped_types, mapped_consts) =
+                BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, uv);
+            let result = ensure_sufficient_stack(|| self.normalize_unevaluated_const(ct.ty(), uv))?;
+            Ok(PlaceholderReplacer::replace_placeholders(
+                infcx,
+                mapped_regions,
+                mapped_types,
+                mapped_consts,
+                &mut self.universes,
+                result,
+            ))
+        } else {
+            ensure_sufficient_stack(|| self.normalize_unevaluated_const(ct.ty(), uv))
+        }
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs
index cbea8009f02..aa690719970 100644
--- a/compiler/rustc_trait_selection/src/solve/project_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs
@@ -112,23 +112,18 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
     ) -> QueryResult<'tcx> {
         if let Some(projection_pred) = assumption.as_projection_clause() {
             if projection_pred.projection_def_id() == goal.predicate.def_id() {
-                ecx.probe(|r| CandidateKind::Candidate { name: "assumption".into(), result: *r })
-                    .enter(|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,
-                        )
+                ecx.probe_candidate("assumption").enter(|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)
-                    })
+                    then(ecx)
+                })
             } else {
                 Err(NoSolution)
             }
@@ -186,14 +181,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
                         "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,
+                        ty::AssocKind::Const => ty::Const::new_error(tcx,
+                            guar,
+                            tcx.type_of(goal.predicate.def_id())
+                                .subst(tcx, goal.predicate.projection_ty.substs),
                             )
                             .into(),
-                        ty::AssocKind::Type => tcx.ty_error(guar).into(),
+                        ty::AssocKind::Type => Ty::new_error(tcx,guar).into(),
                         ty::AssocKind::Fn => unreachable!(),
                     };
                     ecx.eq(goal.param_env, goal.predicate.term, error_term)
@@ -329,69 +323,53 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
         goal: Goal<'tcx, Self>,
     ) -> QueryResult<'tcx> {
         let tcx = ecx.tcx();
-        ecx.probe(|r| CandidateKind::Candidate { name: "builtin pointee".into(), result: *r })
-            .enter(|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
-                    }
+        ecx.probe_candidate("builtin pointee").enter(|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) => Ty::new_error(tcx, *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::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::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::Tuple(elements) => match elements.last() {
+                ty::Adt(def, substs) if def.is_struct() => {
+                    match def.non_enum_variant().fields.raw.last() {
                         None => tcx.types.unit,
-                        Some(&self_ty) => {
+                        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)),
@@ -399,21 +377,35 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
                             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::Adt(_, _) => tcx.types.unit,
 
-                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)
-            })
+                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()
+                ),
+            };
+
+            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)
+        })
     }
 
     fn consider_builtin_future_candidate(
@@ -548,11 +540,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
             ),
         };
 
-        ecx.probe(|r| CandidateKind::Candidate {
-            name: "builtin discriminant kind".into(),
-            result: *r,
-        })
-        .enter(|ecx| {
+        ecx.probe_candidate("builtin discriminant kind").enter(|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)
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 d167ee46b39..f00456e26df 100644
--- a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
@@ -213,7 +213,7 @@ impl<'tcx> SearchGraph<'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 self.should_use_global_cache() && inspect.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);
diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
index 18fec6d9c89..cd68626bed1 100644
--- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
@@ -6,8 +6,8 @@ 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::traits::Reveal;
 use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections};
 use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt};
 use rustc_middle::ty::{TraitPredicate, TypeVisitableExt};
@@ -62,7 +62,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
             },
         };
 
-        ecx.probe(|r| CandidateKind::Candidate { name: "impl".into(), result: *r }).enter(|ecx| {
+        ecx.probe_candidate("impl").enter(|ecx| {
             let impl_substs = ecx.fresh_substs_for_item(impl_def_id);
             let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs);
 
@@ -90,16 +90,15 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
                 && trait_clause.polarity() == goal.predicate.polarity
             {
                 // FIXME: Constness
-                ecx.probe(|r| CandidateKind::Candidate { name: "assumption".into(), result: *r })
-                    .enter(|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)
-                    })
+                ecx.probe_candidate("assumption").enter(|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)
+                })
             } else {
                 Err(NoSolution)
             }
@@ -120,6 +119,32 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
             return result;
         }
 
+        // Don't call `type_of` on a local TAIT that's in the defining scope,
+        // since that may require calling `typeck` on the same item we're
+        // currently type checking, which will result in a fatal cycle that
+        // ideally we want to avoid, since we can make progress on this goal
+        // via an alias bound or a locally-inferred hidden type instead.
+        //
+        // Also, don't call `type_of` on a TAIT in `Reveal::All` mode, since
+        // we already normalize the self type in
+        // `assemble_candidates_after_normalizing_self_ty`, and we'd
+        // just be registering an identical candidate here.
+        //
+        // Returning `Err(NoSolution)` here is ok in `SolverMode::Coherence`
+        // since we'll always be registering an ambiguous candidate in
+        // `assemble_candidates_after_normalizing_self_ty` due to normalizing
+        // the TAIT.
+        if let ty::Alias(ty::Opaque, opaque_ty) = goal.predicate.self_ty().kind() {
+            if matches!(goal.param_env.reveal(), Reveal::All)
+                || opaque_ty
+                    .def_id
+                    .as_local()
+                    .is_some_and(|def_id| ecx.can_define_opaque_ty(def_id))
+            {
+                return Err(NoSolution);
+            }
+        }
+
         ecx.probe_and_evaluate_goal_for_constituent_tys(
             goal,
             structural_traits::instantiate_constituent_tys_for_auto_trait,
@@ -136,15 +161,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
 
         let tcx = ecx.tcx();
 
-        ecx.probe(|r| CandidateKind::Candidate { name: "trait alias".into(), result: *r }).enter(
-            |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_candidate("trait alias").enter(|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)
+        })
     }
 
     fn consider_builtin_sized_candidate(
@@ -350,115 +373,109 @@ 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(|r| CandidateKind::Candidate { name: "builtin unsize".into(), result: *r }).enter(
-            |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)
+        ecx.probe_candidate("builtin unsize").enter(|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);
                     }
-                    // `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 {
+
+                    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);
-                        }
-
-                        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)
+                    // 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);
                     }
-                    _ => 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 = Ty::new_adt(tcx, 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 =
+                        Ty::new_tup_from_iter(tcx, 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),
+            }
+        })
     }
 
     fn consider_builtin_dyn_upcast_candidates(
@@ -488,11 +505,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
         }
 
         let mut unsize_dyn_to_principal = |principal: Option<ty::PolyExistentialTraitRef<'tcx>>| {
-            ecx.probe(|r| CandidateKind::Candidate {
-                name: "upcast dyn to principle".into(),
-                result: *r,
-            })
-            .enter(|ecx| -> Result<_, NoSolution> {
+            ecx.probe_candidate("upcast dyn to principle").enter(|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.
@@ -509,7 +522,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
                             .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);
+                let new_a_ty = Ty::new_dynamic(tcx, 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)?;
@@ -714,21 +727,20 @@ 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(|r| CandidateKind::Candidate { name: "constituent tys".into(), result: *r })
-            .enter(|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_candidate("constituent tys").enter(|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)
+        })
     }
 
     #[instrument(level = "debug", skip(self))]
diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
index fb6bf7211b9..4296ed64cf0 100644
--- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs
+++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
@@ -794,7 +794,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
                                 unevaluated,
                                 Some(obligation.cause.span),
                             ) {
-                                Ok(Some(valtree)) => Ok(selcx.tcx().mk_const(valtree, c.ty())),
+                                Ok(Some(valtree)) => Ok(ty::Const::new_value(selcx.tcx(),valtree, c.ty())),
                                 Ok(None) => {
                                     let tcx = self.tcx;
                                     let reported =
@@ -836,9 +836,6 @@ impl<'tcx> AutoTraitFinder<'tcx> {
                 // FIXME(generic_const_exprs): you can absolutely add this as a where clauses
                 | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
                 | ty::PredicateKind::Coerce(..) => {}
-                ty::PredicateKind::Clause(ty::ClauseKind::TypeWellFormedFromEnv(..)) => {
-                    bug!("predicate should only exist in the environment: {bound_predicate:?}")
-                }
                 ty::PredicateKind::Ambiguous => return false,
             };
         }
diff --git a/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs b/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs
deleted file mode 100644
index 3ecae429c59..00000000000
--- a/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs
+++ /dev/null
@@ -1,149 +0,0 @@
-//! Defines a Chalk-based `TraitEngine`
-
-use crate::infer::canonical::OriginalQueryValues;
-use crate::infer::InferCtxt;
-use crate::traits::query::NoSolution;
-use crate::traits::{
-    ChalkEnvironmentAndGoal, FulfillmentError, FulfillmentErrorCode, PredicateObligation,
-    SelectionError, TraitEngine,
-};
-use rustc_data_structures::fx::FxIndexSet;
-use rustc_middle::ty::TypeVisitableExt;
-
-pub struct FulfillmentContext<'tcx> {
-    obligations: FxIndexSet<PredicateObligation<'tcx>>,
-
-    /// The snapshot in which this context was created. Using the context
-    /// outside of this snapshot leads to subtle bugs if the snapshot
-    /// gets rolled back. Because of this we explicitly check that we only
-    /// use the context in exactly this snapshot.
-    usable_in_snapshot: usize,
-}
-
-impl<'tcx> FulfillmentContext<'tcx> {
-    pub(super) fn new(infcx: &InferCtxt<'tcx>) -> Self {
-        FulfillmentContext {
-            obligations: FxIndexSet::default(),
-            usable_in_snapshot: infcx.num_open_snapshots(),
-        }
-    }
-}
-
-impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
-    fn register_predicate_obligation(
-        &mut self,
-        infcx: &InferCtxt<'tcx>,
-        obligation: PredicateObligation<'tcx>,
-    ) {
-        assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
-        let obligation = infcx.resolve_vars_if_possible(obligation);
-
-        self.obligations.insert(obligation);
-    }
-
-    fn collect_remaining_errors(
-        &mut self,
-        _infcx: &InferCtxt<'tcx>,
-    ) -> Vec<FulfillmentError<'tcx>> {
-        // any remaining obligations are errors
-        self.obligations
-            .iter()
-            .map(|obligation| FulfillmentError {
-                obligation: obligation.clone(),
-                code: FulfillmentErrorCode::CodeAmbiguity { overflow: false },
-                // FIXME - does Chalk have a notation of 'root obligation'?
-                // This is just for diagnostics, so it's okay if this is wrong
-                root_obligation: obligation.clone(),
-            })
-            .collect()
-    }
-
-    fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
-        assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
-
-        let mut errors = Vec::new();
-        let mut next_round = FxIndexSet::default();
-        let mut making_progress;
-
-        loop {
-            making_progress = false;
-
-            // We iterate over all obligations, and record if we are able
-            // to unambiguously prove at least one obligation.
-            for obligation in self.obligations.drain(..) {
-                let obligation = infcx.resolve_vars_if_possible(obligation);
-                let environment = obligation.param_env.caller_bounds();
-                let goal = ChalkEnvironmentAndGoal { environment, goal: obligation.predicate };
-                let mut orig_values = OriginalQueryValues::default();
-                if goal.references_error() {
-                    continue;
-                }
-
-                let canonical_goal =
-                    infcx.canonicalize_query_preserving_universes(goal, &mut orig_values);
-
-                match infcx.tcx.evaluate_goal(canonical_goal) {
-                    Ok(response) => {
-                        if response.is_proven() {
-                            making_progress = true;
-
-                            match infcx.instantiate_query_response_and_region_obligations(
-                                &obligation.cause,
-                                obligation.param_env,
-                                &orig_values,
-                                &response,
-                            ) {
-                                Ok(infer_ok) => {
-                                    next_round.extend(infer_ok.obligations.into_iter().map(
-                                        |obligation| infcx.resolve_vars_if_possible(obligation),
-                                    ))
-                                }
-
-                                Err(_err) => errors.push(FulfillmentError {
-                                    obligation: obligation.clone(),
-                                    code: FulfillmentErrorCode::CodeSelectionError(
-                                        SelectionError::Unimplemented,
-                                    ),
-                                    // FIXME - does Chalk have a notation of 'root obligation'?
-                                    // This is just for diagnostics, so it's okay if this is wrong
-                                    root_obligation: obligation,
-                                }),
-                            }
-                        } else {
-                            // Ambiguous: retry at next round.
-                            next_round.insert(obligation);
-                        }
-                    }
-
-                    Err(NoSolution) => errors.push(FulfillmentError {
-                        obligation: obligation.clone(),
-                        code: FulfillmentErrorCode::CodeSelectionError(
-                            SelectionError::Unimplemented,
-                        ),
-                        // FIXME - does Chalk have a notation of 'root obligation'?
-                        // This is just for diagnostics, so it's okay if this is wrong
-                        root_obligation: obligation,
-                    }),
-                }
-            }
-            next_round = std::mem::replace(&mut self.obligations, next_round);
-
-            if !making_progress {
-                break;
-            }
-        }
-
-        errors
-    }
-
-    fn drain_unstalled_obligations(
-        &mut self,
-        _: &InferCtxt<'tcx>,
-    ) -> Vec<PredicateObligation<'tcx>> {
-        unimplemented!()
-    }
-
-    fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
-        self.obligations.iter().cloned().collect()
-    }
-}
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index 9e1332c1c81..1b1285e1b46 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -30,6 +30,7 @@ use std::fmt::Debug;
 use std::iter;
 use std::ops::ControlFlow;
 
+use super::query::evaluate_obligation::InferCtxtExt;
 use super::NormalizeExt;
 
 /// Whether we do the orphan check relative to this crate or
@@ -290,6 +291,20 @@ fn impl_intersection_has_impossible_obligation<'cx, 'tcx>(
 ) -> bool {
     let infcx = selcx.infcx;
 
+    let obligation_guaranteed_to_fail = move |obligation: &PredicateObligation<'tcx>| {
+        if infcx.next_trait_solver() {
+            infcx.evaluate_obligation(obligation).map_or(false, |result| !result.may_apply())
+        } else {
+            // We use `evaluate_root_obligation` to correctly track
+            // intercrate ambiguity clauses. We do not need this in the
+            // new solver.
+            selcx.evaluate_root_obligation(obligation).map_or(
+                false, // Overflow has occurred, and treat the obligation as possibly holding.
+                |result| !result.may_apply(),
+            )
+        }
+    };
+
     let opt_failing_obligation = [&impl1_header.predicates, &impl2_header.predicates]
         .into_iter()
         .flatten()
@@ -297,12 +312,7 @@ fn impl_intersection_has_impossible_obligation<'cx, 'tcx>(
             Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, predicate)
         })
         .chain(obligations)
-        .find(|o| {
-            selcx.evaluate_root_obligation(o).map_or(
-                false, // Overflow has occurred, and treat the obligation as possibly holding.
-                |result| !result.may_apply(),
-            )
-        });
+        .find(obligation_guaranteed_to_fail);
 
     if let Some(failing_obligation) = opt_failing_obligation {
         debug!("overlap: obligation unsatisfiable {:?}", failing_obligation);
@@ -427,7 +437,11 @@ fn prove_negated_obligation<'tcx>(
     let body_def_id = body_def_id.as_local().unwrap_or(CRATE_DEF_ID);
 
     let ocx = ObligationCtxt::new(&infcx);
-    let wf_tys = ocx.assumed_wf_types(param_env, DUMMY_SP, body_def_id);
+    let Ok(wf_tys) = ocx.assumed_wf_types(param_env, body_def_id)
+    else {
+        return false;
+    };
+
     let outlives_env = OutlivesEnvironment::with_bounds(
         param_env,
         infcx.implied_bounds_tys(param_env, body_def_id, wf_tys),
diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs
index faa675054b7..61f693e1bd1 100644
--- a/compiler/rustc_trait_selection/src/traits/engine.rs
+++ b/compiler/rustc_trait_selection/src/traits/engine.rs
@@ -1,9 +1,10 @@
 use std::cell::RefCell;
 use std::fmt::Debug;
 
+use super::FulfillmentContext;
 use super::TraitEngine;
-use super::{ChalkFulfillmentContext, FulfillmentContext};
 use crate::solve::FulfillmentCtxt as NextFulfillmentCtxt;
+use crate::traits::error_reporting::TypeErrCtxtExt;
 use crate::traits::NormalizeExt;
 use rustc_data_structures::fx::FxIndexSet;
 use rustc_errors::ErrorGuaranteed;
@@ -24,7 +25,6 @@ use rustc_middle::ty::ToPredicate;
 use rustc_middle::ty::TypeFoldable;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_session::config::TraitSolver;
-use rustc_span::Span;
 
 pub trait TraitEngineExt<'tcx> {
     fn new(infcx: &InferCtxt<'tcx>) -> Box<Self>;
@@ -39,7 +39,6 @@ impl<'tcx> TraitEngineExt<'tcx> for dyn TraitEngine<'tcx> {
             (TraitSolver::Next | TraitSolver::NextCoherence, true) => {
                 Box::new(NextFulfillmentCtxt::new(infcx))
             }
-            (TraitSolver::Chalk, false) => Box::new(ChalkFulfillmentContext::new(infcx)),
             _ => bug!(
                 "incompatible combination of -Ztrait-solver flag ({:?}) and InferCtxt::next_trait_solver ({:?})",
                 infcx.tcx.sess.opts.unstable_opts.trait_solver,
@@ -198,17 +197,24 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> {
         }
     }
 
+    pub fn assumed_wf_types_and_report_errors(
+        &self,
+        param_env: ty::ParamEnv<'tcx>,
+        def_id: LocalDefId,
+    ) -> Result<FxIndexSet<Ty<'tcx>>, ErrorGuaranteed> {
+        self.assumed_wf_types(param_env, def_id)
+            .map_err(|errors| self.infcx.err_ctxt().report_fulfillment_errors(&errors))
+    }
+
     pub fn assumed_wf_types(
         &self,
         param_env: ty::ParamEnv<'tcx>,
-        span: Span,
         def_id: LocalDefId,
-    ) -> FxIndexSet<Ty<'tcx>> {
+    ) -> Result<FxIndexSet<Ty<'tcx>>, Vec<FulfillmentError<'tcx>>> {
         let tcx = self.infcx.tcx;
-        let assumed_wf_types = tcx.assumed_wf_types(def_id);
         let mut implied_bounds = FxIndexSet::default();
-        let cause = ObligationCause::misc(span, def_id);
-        for ty in assumed_wf_types {
+        let mut errors = Vec::new();
+        for &(ty, span) in tcx.assumed_wf_types(def_id) {
             // FIXME(@lcnr): rustc currently does not check wf for types
             // pre-normalization, meaning that implied bounds are sometimes
             // incorrect. See #100910 for more details.
@@ -221,10 +227,19 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> {
             // sound and then uncomment this line again.
 
             // implied_bounds.insert(ty);
-            let normalized = self.normalize(&cause, param_env, ty);
-            implied_bounds.insert(normalized);
+            let cause = ObligationCause::misc(span, def_id);
+            match self
+                .infcx
+                .at(&cause, param_env)
+                .deeply_normalize(ty, &mut **self.engine.borrow_mut())
+            {
+                // Insert well-formed types, ignoring duplicates.
+                Ok(normalized) => drop(implied_bounds.insert(normalized)),
+                Err(normalization_errors) => errors.extend(normalization_errors),
+            };
         }
-        implied_bounds
+
+        if errors.is_empty() { Ok(implied_bounds) } else { Err(errors) }
     }
 
     pub fn make_canonicalized_query_response<T>(
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
index f7670d51bdc..5b875e19996 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -10,6 +10,7 @@ use super::{
 use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode};
 use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use crate::infer::{self, InferCtxt};
+use crate::solve::{GenerateProofTree, InferCtxtEvalExt, UseGlobalCache};
 use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
 use crate::traits::query::normalize::QueryNormalizeExt as _;
 use crate::traits::specialize::to_pretty_impl_header;
@@ -28,7 +29,8 @@ use rustc_hir::{GenericParam, Item, Node};
 use rustc_infer::infer::error_reporting::TypeErrCtxt;
 use rustc_infer::infer::{InferOk, TypeTrace};
 use rustc_middle::traits::select::OverflowError;
-use rustc_middle::traits::SelectionOutputTypeParameterMismatch;
+use rustc_middle::traits::solve::Goal;
+use rustc_middle::traits::{DefiningAnchor, SelectionOutputTypeParameterMismatch};
 use rustc_middle::ty::abstract_const::NotConstEvaluatable;
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable};
@@ -37,13 +39,14 @@ use rustc_middle::ty::{
     self, SubtypePredicate, ToPolyTraitRef, ToPredicate, TraitRef, Ty, TyCtxt, TypeFoldable,
     TypeVisitable, TypeVisitableExt,
 };
-use rustc_session::config::TraitSolver;
+use rustc_session::config::{DumpSolverProofTree, TraitSolver};
 use rustc_session::Limit;
 use rustc_span::def_id::LOCAL_CRATE;
 use rustc_span::symbol::sym;
 use rustc_span::{ExpnKind, Span, DUMMY_SP};
 use std::borrow::Cow;
 use std::fmt;
+use std::io::Write;
 use std::iter;
 use std::ops::ControlFlow;
 use suggestions::TypeErrCtxtExt as _;
@@ -630,6 +633,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         error: &SelectionError<'tcx>,
     ) {
         let tcx = self.tcx;
+
+        if tcx.sess.opts.unstable_opts.dump_solver_proof_tree == DumpSolverProofTree::OnError {
+            dump_proof_tree(root_obligation, self.infcx);
+        }
+
         let mut span = obligation.cause.span;
         // FIXME: statically guarantee this by tainting after the diagnostic is emitted
         self.set_tainted_by_errors(
@@ -966,7 +974,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                             && self.fallback_has_occurred
                         {
                             let predicate = trait_predicate.map_bound(|trait_pred| {
-                                trait_pred.with_self_ty(self.tcx, self.tcx.mk_unit())
+                                trait_pred.with_self_ty(self.tcx, Ty::new_unit(self.tcx))
                             });
                             let unit_obligation = obligation.with(tcx, predicate);
                             if self.predicate_may_hold(&unit_obligation) {
@@ -1059,7 +1067,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                                 // (which may fail).
                                 span_bug!(span, "WF predicate not satisfied for {:?}", ty);
                             }
-                            TraitSolver::Chalk | TraitSolver::Next | TraitSolver::NextCoherence => {
+                            TraitSolver::Next | TraitSolver::NextCoherence => {
                                 // FIXME: we'll need a better message which takes into account
                                 // which bounds actually failed to hold.
                                 self.tcx.sess.struct_span_err(
@@ -1094,13 +1102,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
 
                     ty::PredicateKind::Ambiguous => span_bug!(span, "ambiguous"),
 
-                    ty::PredicateKind::Clause(ty::ClauseKind::TypeWellFormedFromEnv(..)) => {
-                        span_bug!(
-                            span,
-                            "TypeWellFormedFromEnv predicate should only exist in the environment"
-                        )
-                    }
-
                     ty::PredicateKind::AliasRelate(..) => span_bug!(
                         span,
                         "AliasRelate predicate should never be the predicate cause of a SelectionError"
@@ -1151,6 +1152,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                 }
             }
 
+            SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id) => self.report_opaque_type_auto_trait_leakage(
+                &obligation,
+                def_id,
+            ),
+
             TraitNotObjectSafe(did) => {
                 let violations = self.tcx.object_safety_violations(did);
                 report_object_safety_error(self.tcx, span, did, violations)
@@ -1169,16 +1175,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
             }
 
             // Already reported in the query.
-            SelectionError::NotConstEvaluatable(NotConstEvaluatable::Error(_)) => {
-                // FIXME(eddyb) remove this once `ErrorGuaranteed` becomes a proof token.
-                self.tcx.sess.delay_span_bug(span, "`ErrorGuaranteed` without an error");
-                return;
-            }
+            SelectionError::NotConstEvaluatable(NotConstEvaluatable::Error(_)) |
             // Already reported.
-            Overflow(OverflowError::Error(_)) => {
-                self.tcx.sess.delay_span_bug(span, "`OverflowError` has been reported");
-                return;
-            }
+            Overflow(OverflowError::Error(_)) => return,
+
             Overflow(_) => {
                 bug!("overflow should be handled before the `report_selection_error` path");
             }
@@ -1470,6 +1470,12 @@ trait InferCtxtPrivExt<'tcx> {
         terr: TypeError<'tcx>,
     ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>;
 
+    fn report_opaque_type_auto_trait_leakage(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        def_id: DefId,
+    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>;
+
     fn report_type_parameter_mismatch_error(
         &self,
         obligation: &PredicateObligation<'tcx>,
@@ -1529,6 +1535,10 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
 
     #[instrument(skip(self), level = "debug")]
     fn report_fulfillment_error(&self, error: &FulfillmentError<'tcx>) {
+        if self.tcx.sess.opts.unstable_opts.dump_solver_proof_tree == DumpSolverProofTree::OnError {
+            dump_proof_tree(&error.root_obligation, self.infcx);
+        }
+
         match error.code {
             FulfillmentErrorCode::CodeSelectionError(ref selection_error) => {
                 self.report_selection_error(
@@ -1615,20 +1625,21 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                     bound_predicate.rebind(data),
                 );
                 let unnormalized_term = match data.term.unpack() {
-                    ty::TermKind::Ty(_) => self
-                        .tcx
-                        .mk_projection(data.projection_ty.def_id, data.projection_ty.substs)
-                        .into(),
-                    ty::TermKind::Const(ct) => self
-                        .tcx
-                        .mk_const(
-                            ty::UnevaluatedConst {
-                                def: data.projection_ty.def_id,
-                                substs: data.projection_ty.substs,
-                            },
-                            ct.ty(),
-                        )
-                        .into(),
+                    ty::TermKind::Ty(_) => Ty::new_projection(
+                        self.tcx,
+                        data.projection_ty.def_id,
+                        data.projection_ty.substs,
+                    )
+                    .into(),
+                    ty::TermKind::Const(ct) => ty::Const::new_unevaluated(
+                        self.tcx,
+                        ty::UnevaluatedConst {
+                            def: data.projection_ty.def_id,
+                            substs: data.projection_ty.substs,
+                        },
+                        ct.ty(),
+                    )
+                    .into(),
                 };
                 let normalized_term =
                     ocx.normalize(&obligation.cause, obligation.param_env, unnormalized_term);
@@ -2642,11 +2653,11 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
             }
 
             fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
-                if let ty::Param(ty::ParamTy { name, .. }) = *ty.kind() {
+                if let ty::Param(_) = *ty.kind() {
                     let infcx = self.infcx;
                     *self.var_map.entry(ty).or_insert_with(|| {
                         infcx.next_ty_var(TypeVariableOrigin {
-                            kind: TypeVariableOriginKind::TypeParameterDefinition(name, None),
+                            kind: TypeVariableOriginKind::MiscVariable,
                             span: DUMMY_SP,
                         })
                     })
@@ -3188,6 +3199,39 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         )
     }
 
+    fn report_opaque_type_auto_trait_leakage(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        def_id: DefId,
+    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+        let name = match self.tcx.opaque_type_origin(def_id.expect_local()) {
+            hir::OpaqueTyOrigin::FnReturn(_) | hir::OpaqueTyOrigin::AsyncFn(_) => {
+                format!("opaque type")
+            }
+            hir::OpaqueTyOrigin::TyAlias { .. } => {
+                format!("`{}`", self.tcx.def_path_debug_str(def_id))
+            }
+        };
+        let mut err = self.tcx.sess.struct_span_err(
+            obligation.cause.span,
+            format!("cannot check whether the hidden type of {name} satisfies auto traits"),
+        );
+        err.span_note(self.tcx.def_span(def_id), "opaque type is declared here");
+        match self.defining_use_anchor {
+            DefiningAnchor::Bubble | DefiningAnchor::Error => {}
+            DefiningAnchor::Bind(bind) => {
+                err.span_note(
+                    self.tcx.def_ident_span(bind).unwrap_or_else(|| self.tcx.def_span(bind)),
+                    "this item depends on auto traits of the hidden type, \
+                    but may also be registering the hidden type. \
+                    This is not supported right now. \
+                    You can try moving the opaque type and the item that actually registers a hidden type into a new submodule".to_string(),
+                );
+            }
+        };
+        err
+    }
+
     fn report_type_parameter_mismatch_error(
         &self,
         obligation: &PredicateObligation<'tcx>,
@@ -3499,3 +3543,16 @@ pub enum DefIdOrName {
     DefId(DefId),
     Name(&'static str),
 }
+
+pub fn dump_proof_tree<'tcx>(o: &Obligation<'tcx, ty::Predicate<'tcx>>, infcx: &InferCtxt<'tcx>) {
+    infcx.probe(|_| {
+        let goal = Goal { predicate: o.predicate, param_env: o.param_env };
+        let tree = infcx
+            .evaluate_root_goal(goal, GenerateProofTree::Yes(UseGlobalCache::No))
+            .1
+            .expect("proof tree should have been generated");
+        let mut lock = std::io::stdout().lock();
+        let _ = lock.write_fmt(format_args!("{tree:?}"));
+        let _ = lock.flush();
+    });
+}
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
index 10bd027b684..1e4d30f48b2 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
@@ -278,7 +278,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
             // Arrays give us `[]`, `[{ty}; _]` and `[{ty}; N]`
             if let ty::Array(aty, len) = self_ty.kind() {
                 flags.push((sym::_Self, Some("[]".to_string())));
-                let len = len.kind().try_to_value().and_then(|v| v.try_to_target_usize(self.tcx));
+                let len = len.try_to_value().and_then(|v| v.try_to_target_usize(self.tcx));
                 flags.push((sym::_Self, Some(format!("[{}; _]", aty))));
                 if let Some(n) = len {
                     flags.push((sym::_Self, Some(format!("[{}; {}]", aty, n))));
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index 619a099fcb5..9ac1ba0275c 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -786,7 +786,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                 if let Some(steps) =
                     autoderef.into_iter().enumerate().find_map(|(steps, (ty, obligations))| {
                         // Re-add the `&`
-                        let ty = self.tcx.mk_ref(region, TypeAndMut { ty, mutbl });
+                        let ty = Ty::new_ref(self.tcx, region, TypeAndMut { ty, mutbl });
 
                         // Remapping bound vars here
                         let real_trait_pred_and_ty =
@@ -1298,13 +1298,13 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
             let trait_pred_and_imm_ref = old_pred.map_bound(|trait_pred| {
                 (
                     trait_pred,
-                    self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, trait_pred.self_ty()),
+                    Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, trait_pred.self_ty()),
                 )
             });
             let trait_pred_and_mut_ref = old_pred.map_bound(|trait_pred| {
                 (
                     trait_pred,
-                    self.tcx.mk_mut_ref(self.tcx.lifetimes.re_static, trait_pred.self_ty()),
+                    Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, trait_pred.self_ty()),
                 )
             });
 
@@ -1465,7 +1465,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
     ) {
         let ty::Ref(_, object_ty, hir::Mutability::Not) = target_ty.kind() else { return; };
         let ty::Dynamic(predicates, _, ty::Dyn) = object_ty.kind() else { return; };
-        let self_ref_ty = self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, self_ty);
+        let self_ref_ty = Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, self_ty);
 
         for predicate in predicates.iter() {
             if !self.predicate_must_hold_modulo_regions(
@@ -1706,8 +1706,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
             if let ty::Ref(region, t_type, mutability) = *trait_pred.skip_binder().self_ty().kind()
             {
                 let suggested_ty = match mutability {
-                    hir::Mutability::Mut => self.tcx.mk_imm_ref(region, t_type),
-                    hir::Mutability::Not => self.tcx.mk_mut_ref(region, t_type),
+                    hir::Mutability::Mut => Ty::new_imm_ref(self.tcx, region, t_type),
+                    hir::Mutability::Not => Ty::new_mut_ref(self.tcx, region, t_type),
                 };
 
                 // Remapping bound vars here
@@ -1951,7 +1951,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                 ),
             };
 
-            infcx.tcx.mk_fn_ptr(trait_ref.rebind(sig))
+            Ty::new_fn_ptr(infcx.tcx, trait_ref.rebind(sig))
         }
 
         let argument_kind = match expected.skip_binder().self_ty().kind() {
@@ -3347,7 +3347,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                 let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0];
                 // `<T as Future>::Output`
                 let projection_ty = trait_pred.map_bound(|trait_pred| {
-                    self.tcx.mk_projection(
+                    Ty::new_projection(
+                        self.tcx,
                         item_def_id,
                         // Future::Output has no substs
                         [trait_pred.self_ty()],
@@ -3501,7 +3502,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         {
             if let hir::Expr { kind: hir::ExprKind::Block(..), .. } = expr {
                 let expr = expr.peel_blocks();
-                let ty = typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(tcx.ty_error_misc());
+                let ty = typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx,));
                 let span = expr.span;
                 if Some(span) != err.span.primary_span() {
                     err.span_label(
@@ -3555,7 +3556,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                 {
                     type_diffs = vec![
                         Sorts(ty::error::ExpectedFound {
-                            expected: self.tcx.mk_alias(ty::Projection, where_pred.skip_binder().projection_ty),
+                            expected: Ty::new_alias(self.tcx,ty::Projection, where_pred.skip_binder().projection_ty),
                             found,
                         }),
                     ];
@@ -3646,7 +3647,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
 
             // Extract `<U as Deref>::Target` assoc type and check that it is `T`
             && let Some(deref_target_did) = tcx.lang_items().deref_target()
-            && let projection = tcx.mk_projection(deref_target_did, tcx.mk_substs(&[ty::GenericArg::from(found_ty)]))
+            && let projection = Ty::new_projection(tcx,deref_target_did, tcx.mk_substs(&[ty::GenericArg::from(found_ty)]))
             && let InferOk { value: deref_target, obligations } = infcx.at(&ObligationCause::dummy(), param_env).normalize(projection)
             && obligations.iter().all(|obligation| infcx.predicate_must_hold_modulo_regions(obligation))
             && infcx.can_eq(param_env, deref_target, target_ty)
@@ -3693,7 +3694,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         let mut assocs = vec![];
         let mut expr = expr;
         let mut prev_ty = self.resolve_vars_if_possible(
-            typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(tcx.ty_error_misc()),
+            typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx)),
         );
         while let hir::ExprKind::MethodCall(_path_segment, rcvr_expr, _args, span) = expr.kind {
             // Point at every method call in the chain with the resulting type.
@@ -3704,7 +3705,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                 self.probe_assoc_types_at_expr(&type_diffs, span, prev_ty, expr.hir_id, param_env);
             assocs.push(assocs_in_this_method);
             prev_ty = self.resolve_vars_if_possible(
-                typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(tcx.ty_error_misc()),
+                typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx)),
             );
 
             if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind
@@ -3722,7 +3723,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                 if let hir::Node::Param(param) = parent {
                     // ...and it is a an fn argument.
                     let prev_ty = self.resolve_vars_if_possible(
-                        typeck_results.node_type_opt(param.hir_id).unwrap_or(tcx.ty_error_misc()),
+                        typeck_results.node_type_opt(param.hir_id).unwrap_or(Ty::new_misc_error(tcx,)),
                     );
                     let assocs_in_this_method = self.probe_assoc_types_at_expr(&type_diffs, param.ty_span, prev_ty, param.hir_id, param_env);
                     if assocs_in_this_method.iter().any(|a| a.is_some()) {
diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs
index e3472a1c4c1..2d8c260126d 100644
--- a/compiler/rustc_trait_selection/src/traits/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs
@@ -359,9 +359,6 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
                     ProcessResult::Changed(mk_pending(vec![obligation.with(infcx.tcx, pred)]))
                 }
                 ty::PredicateKind::Ambiguous => ProcessResult::Unchanged,
-                ty::PredicateKind::Clause(ty::ClauseKind::TypeWellFormedFromEnv(..)) => {
-                    bug!("TypeWellFormedFromEnv is only used for Chalk")
-                }
                 ty::PredicateKind::AliasRelate(..) => {
                     bug!("AliasRelate is only used for new solver")
                 }
@@ -531,7 +528,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
                         debug!("equating consts:\nc1= {:?}\nc2= {:?}", c1, c2);
 
                         use rustc_hir::def::DefKind;
-                        use ty::ConstKind::Unevaluated;
+                        use ty::Unevaluated;
                         match (c1.kind(), c2.kind()) {
                             (Unevaluated(a), Unevaluated(b))
                                 if a.def == b.def && tcx.def_kind(a.def) == DefKind::AssocConst =>
@@ -627,9 +624,6 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
                     }
                 }
                 ty::PredicateKind::Ambiguous => ProcessResult::Unchanged,
-                ty::PredicateKind::Clause(ty::ClauseKind::TypeWellFormedFromEnv(..)) => {
-                    bug!("TypeWellFormedFromEnv is only used for Chalk")
-                }
                 ty::PredicateKind::AliasRelate(..) => {
                     bug!("AliasRelate is only used for new solver")
                 }
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index 5dc5ddbddbd..e2ee347b639 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -3,7 +3,6 @@
 //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html
 
 pub mod auto_trait;
-mod chalk_fulfill;
 pub(crate) mod coherence;
 pub mod const_evaluatable;
 mod engine;
@@ -40,6 +39,8 @@ use rustc_span::Span;
 use std::fmt::Debug;
 use std::ops::ControlFlow;
 
+pub(crate) use self::project::{needs_normalization, BoundVarReplacer, PlaceholderReplacer};
+
 pub use self::FulfillmentErrorCode::*;
 pub use self::ImplSource::*;
 pub use self::ObligationCauseCode::*;
@@ -72,8 +73,6 @@ pub use self::util::{
 pub use self::util::{expand_trait_aliases, TraitAliasExpander};
 pub use self::util::{get_vtable_index_of_object_method, impl_item_is_final, upcast_choices};
 
-pub use self::chalk_fulfill::FulfillmentContext as ChalkFulfillmentContext;
-
 pub use rustc_infer::traits::*;
 
 /// Whether to skip the leak check, as part of a future compatibility warning step.
@@ -407,7 +406,12 @@ pub fn normalize_param_env_or_error<'tcx>(
     )
 }
 
-/// Normalize a type and process all resulting obligations, returning any errors
+/// Normalize a type and process all resulting obligations, returning any errors.
+///
+/// FIXME(-Ztrait-solver=next): This should be replaced by `At::deeply_normalize`
+/// which has the same behavior with the new solver. Because using a separate
+/// fulfillment context worsens caching in the old solver, `At::deeply_normalize`
+/// is still lazy with the old solver as it otherwise negatively impacts perf.
 #[instrument(skip_all)]
 pub fn fully_normalize<'tcx, T>(
     infcx: &InferCtxt<'tcx>,
diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs
index 78270b7d535..c31944c162e 100644
--- a/compiler/rustc_trait_selection/src/traits/object_safety.rs
+++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs
@@ -101,7 +101,7 @@ pub fn is_vtable_safe_method(tcx: TyCtxt<'_>, trait_def_id: DefId, method: ty::A
     debug_assert!(tcx.generics_of(trait_def_id).has_self);
     debug!("is_vtable_safe_method({:?}, {:?})", trait_def_id, method);
     // Any method that has a `Self: Sized` bound cannot be called.
-    if generics_require_sized_self(tcx, method.def_id) {
+    if tcx.generics_require_sized_self(method.def_id) {
         return false;
     }
 
@@ -311,7 +311,7 @@ fn predicate_references_self<'tcx>(
         | ty::ClauseKind::RegionOutlives(..)
         // FIXME(generic_const_exprs): this can mention `Self`
         | ty::ClauseKind::ConstEvaluatable(..)
-        | ty::ClauseKind::TypeWellFormedFromEnv(_) => None,
+         => None,
     }
 }
 
@@ -331,7 +331,7 @@ fn super_predicates_have_non_lifetime_binders(
 }
 
 fn trait_has_sized_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool {
-    generics_require_sized_self(tcx, trait_def_id)
+    tcx.generics_require_sized_self(trait_def_id)
 }
 
 fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
@@ -351,8 +351,7 @@ fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
         | ty::ClauseKind::Projection(_)
         | ty::ClauseKind::ConstArgHasType(_, _)
         | ty::ClauseKind::WellFormed(_)
-        | ty::ClauseKind::ConstEvaluatable(_)
-        | ty::ClauseKind::TypeWellFormedFromEnv(_) => false,
+        | ty::ClauseKind::ConstEvaluatable(_) => false,
     })
 }
 
@@ -365,7 +364,7 @@ fn object_safety_violation_for_assoc_item(
 ) -> Option<ObjectSafetyViolation> {
     // Any item that has a `Self : Sized` requisite is otherwise
     // exempt from the regulations.
-    if generics_require_sized_self(tcx, item.def_id) {
+    if tcx.generics_require_sized_self(item.def_id) {
         return None;
     }
 
@@ -510,7 +509,7 @@ fn virtual_call_violation_for_method<'tcx>(
 
             // e.g., `Rc<()>`
             let unit_receiver_ty =
-                receiver_for_self_ty(tcx, receiver_ty, tcx.mk_unit(), method.def_id);
+                receiver_for_self_ty(tcx, receiver_ty, Ty::new_unit(tcx), method.def_id);
 
             match abi_of_ty(unit_receiver_ty) {
                 Some(Abi::Scalar(..)) => (),
@@ -665,7 +664,7 @@ fn object_ty_for_trait<'tcx>(
     );
     debug!(?existential_predicates);
 
-    tcx.mk_dynamic(existential_predicates, lifetime, ty::Dyn)
+    Ty::new_dynamic(tcx, existential_predicates, lifetime, ty::Dyn)
 }
 
 /// Checks the method's receiver (the `self` argument) can be dispatched on when `Self` is a
@@ -733,7 +732,7 @@ fn receiver_is_dispatchable<'tcx>(
     // FIXME(mikeyhew) this is a total hack. Once object_safe_for_dispatch is stabilized, we can
     // replace this with `dyn Trait`
     let unsized_self_ty: Ty<'tcx> =
-        tcx.mk_ty_param(u32::MAX, Symbol::intern("RustaceansAreAwesome"));
+        Ty::new_param(tcx, u32::MAX, Symbol::intern("RustaceansAreAwesome"));
 
     // `Receiver[Self => U]`
     let unsized_receiver_ty =
@@ -923,5 +922,10 @@ pub fn contains_illegal_impl_trait_in_trait<'tcx>(
 }
 
 pub fn provide(providers: &mut Providers) {
-    *providers = Providers { object_safety_violations, check_is_object_safe, ..*providers };
+    *providers = Providers {
+        object_safety_violations,
+        check_is_object_safe,
+        generics_require_sized_self,
+        ..*providers
+    };
 }
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index 8fd77335074..717b35784d8 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -28,7 +28,9 @@ use rustc_hir::lang_items::LangItem;
 use rustc_infer::infer::at::At;
 use rustc_infer::infer::resolve::OpportunisticRegionResolver;
 use rustc_infer::infer::DefineOpaqueTypes;
+use rustc_infer::traits::FulfillmentError;
 use rustc_infer::traits::ObligationCauseCode;
+use rustc_infer::traits::TraitEngine;
 use rustc_middle::traits::select::OverflowError;
 use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
 use rustc_middle::ty::visit::{MaxUniverse, TypeVisitable, TypeVisitableExt};
@@ -53,14 +55,55 @@ pub trait NormalizeExt<'tcx> {
     /// This normalization should be used when the type contains inference variables or the
     /// projection may be fallible.
     fn normalize<T: TypeFoldable<TyCtxt<'tcx>>>(&self, t: T) -> InferOk<'tcx, T>;
+
+    /// Deeply normalizes `value`, replacing all aliases which can by normalized in
+    /// the current environment. In the new solver this errors in case normalization
+    /// fails or is ambiguous. This only normalizes opaque types with `Reveal::All`.
+    ///
+    /// In the old solver this simply uses `normalizes` and adds the nested obligations
+    /// to the `fulfill_cx`. This is necessary as we otherwise end up recomputing the
+    /// same goals in both a temporary and the shared context which negatively impacts
+    /// performance as these don't share caching.
+    ///
+    /// FIXME(-Ztrait-solver=next): This has the same behavior as `traits::fully_normalize`
+    /// in the new solver, but because of performance reasons, we currently reuse an
+    /// existing fulfillment context in the old solver. Once we also eagerly prove goals with
+    /// the old solver or have removed the old solver, remove `traits::fully_normalize` and
+    /// rename this function to `At::fully_normalize`.
+    fn deeply_normalize<T: TypeFoldable<TyCtxt<'tcx>>>(
+        self,
+        value: T,
+        fulfill_cx: &mut dyn TraitEngine<'tcx>,
+    ) -> Result<T, Vec<FulfillmentError<'tcx>>>;
 }
 
 impl<'tcx> NormalizeExt<'tcx> for At<'_, 'tcx> {
     fn normalize<T: TypeFoldable<TyCtxt<'tcx>>>(&self, value: T) -> InferOk<'tcx, T> {
-        let mut selcx = SelectionContext::new(self.infcx);
-        let Normalized { value, obligations } =
-            normalize_with_depth(&mut selcx, self.param_env, self.cause.clone(), 0, value);
-        InferOk { value, obligations }
+        if self.infcx.next_trait_solver() {
+            InferOk { value, obligations: Vec::new() }
+        } else {
+            let mut selcx = SelectionContext::new(self.infcx);
+            let Normalized { value, obligations } =
+                normalize_with_depth(&mut selcx, self.param_env, self.cause.clone(), 0, value);
+            InferOk { value, obligations }
+        }
+    }
+
+    fn deeply_normalize<T: TypeFoldable<TyCtxt<'tcx>>>(
+        self,
+        value: T,
+        fulfill_cx: &mut dyn TraitEngine<'tcx>,
+    ) -> Result<T, Vec<FulfillmentError<'tcx>>> {
+        if self.infcx.next_trait_solver() {
+            crate::solve::deeply_normalize(self, value)
+        } else {
+            let value = self
+                .normalize(value)
+                .into_value_registering_obligations(self.infcx, &mut *fulfill_cx);
+            let errors = fulfill_cx.select_where_possible(self.infcx);
+            let value = self.infcx.resolve_vars_if_possible(value);
+            if errors.is_empty() { Ok(value) } else { Err(errors) }
+        }
     }
 }
 
@@ -404,6 +447,7 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> {
         depth: usize,
         obligations: &'a mut Vec<PredicateObligation<'tcx>>,
     ) -> AssocTypeNormalizer<'a, 'b, 'tcx> {
+        debug_assert!(!selcx.infcx.next_trait_solver());
         AssocTypeNormalizer {
             selcx,
             param_env,
@@ -862,7 +906,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for BoundVarReplacer<'_, 'tcx> {
                 let universe = self.universe_for(debruijn);
                 let p = ty::PlaceholderType { universe, bound: bound_ty };
                 self.mapped_types.insert(p, bound_ty);
-                self.infcx.tcx.mk_placeholder(p)
+                Ty::new_placeholder(self.infcx.tcx, p)
             }
             _ if t.has_vars_bound_at_or_above(self.current_index) => t.super_fold_with(self),
             _ => t,
@@ -881,7 +925,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for BoundVarReplacer<'_, 'tcx> {
                 let universe = self.universe_for(debruijn);
                 let p = ty::PlaceholderConst { universe, bound: bound_const };
                 self.mapped_consts.insert(p, bound_const);
-                self.infcx.tcx.mk_const(p, ct.ty())
+                ty::Const::new_placeholder(self.infcx.tcx, p, ct.ty())
             }
             _ => ct.super_fold_with(self),
         }
@@ -992,7 +1036,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for PlaceholderReplacer<'_, 'tcx> {
                         let db = ty::DebruijnIndex::from_usize(
                             self.universe_indices.len() - index + self.current_index.as_usize() - 1,
                         );
-                        self.interner().mk_bound(db, *replace_var)
+                        Ty::new_bound(self.infcx.tcx, db, *replace_var)
                     }
                     None => ty,
                 }
@@ -1016,7 +1060,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for PlaceholderReplacer<'_, 'tcx> {
                     let db = ty::DebruijnIndex::from_usize(
                         self.universe_indices.len() - index + self.current_index.as_usize() - 1,
                     );
-                    self.interner().mk_const(ty::ConstKind::Bound(db, *replace_var), ct.ty())
+                    ty::Const::new_bound(self.infcx.tcx, db, *replace_var, ct.ty())
                 }
                 None => ct,
             }
@@ -1079,6 +1123,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
     obligations: &mut Vec<PredicateObligation<'tcx>>,
 ) -> Result<Option<Term<'tcx>>, InProgress> {
     let infcx = selcx.infcx;
+    debug_assert!(!selcx.infcx.next_trait_solver());
     // Don't use the projection cache in intercrate mode -
     // the `infcx` may be re-used between intercrate in non-intercrate
     // mode, which could lead to using incorrect cache results.
@@ -1395,7 +1440,7 @@ struct Progress<'tcx> {
 
 impl<'tcx> Progress<'tcx> {
     fn error(tcx: TyCtxt<'tcx>, guar: ErrorGuaranteed) -> Self {
-        Progress { term: tcx.ty_error(guar).into(), obligations: vec![] }
+        Progress { term: Ty::new_error(tcx, guar).into(), obligations: vec![] }
     }
 
     fn with_addl_obligations(mut self, mut obligations: Vec<PredicateObligation<'tcx>>) -> Self {
@@ -1454,19 +1499,22 @@ fn project<'cx, 'tcx>(
         ProjectionCandidateSet::None => {
             let tcx = selcx.tcx();
             let term = match tcx.def_kind(obligation.predicate.def_id) {
-                DefKind::AssocTy | DefKind::ImplTraitPlaceholder => tcx
-                    .mk_projection(obligation.predicate.def_id, obligation.predicate.substs)
-                    .into(),
-                DefKind::AssocConst => tcx
-                    .mk_const(
-                        ty::ConstKind::Unevaluated(ty::UnevaluatedConst::new(
-                            obligation.predicate.def_id,
-                            obligation.predicate.substs,
-                        )),
-                        tcx.type_of(obligation.predicate.def_id)
-                            .subst(tcx, obligation.predicate.substs),
-                    )
-                    .into(),
+                DefKind::AssocTy | DefKind::ImplTraitPlaceholder => Ty::new_projection(
+                    tcx,
+                    obligation.predicate.def_id,
+                    obligation.predicate.substs,
+                )
+                .into(),
+                DefKind::AssocConst => ty::Const::new_unevaluated(
+                    tcx,
+                    ty::UnevaluatedConst::new(
+                        obligation.predicate.def_id,
+                        obligation.predicate.substs,
+                    ),
+                    tcx.type_of(obligation.predicate.def_id)
+                        .subst(tcx, obligation.predicate.substs),
+                )
+                .into(),
                 kind => {
                     bug!("unknown projection def-id: {}", kind.descr(obligation.predicate.def_id))
                 }
@@ -2306,7 +2354,7 @@ fn confirm_param_env_candidate<'cx, 'tcx>(
                 obligation, poly_cache_entry, e,
             );
             debug!("confirm_param_env_candidate: {}", msg);
-            let err = infcx.tcx.ty_error_with_message(obligation.cause.span, msg);
+            let err = Ty::new_error_with_message(infcx.tcx, obligation.cause.span, msg);
             Progress { term: err.into(), obligations: vec![] }
         }
     }
@@ -2338,7 +2386,7 @@ fn confirm_impl_candidate<'cx, 'tcx>(
             "confirm_impl_candidate: no associated type {:?} for {:?}",
             assoc_ty.item.name, obligation.predicate
         );
-        return Progress { term: tcx.ty_error_misc().into(), obligations: nested };
+        return Progress { term: Ty::new_misc_error(tcx).into(), obligations: nested };
     }
     // If we're trying to normalize `<Vec<u32> as X>::A<S>` using
     //`impl<T> X for Vec<T> { type A<Y> = Box<Y>; }`, then:
@@ -2354,13 +2402,14 @@ fn confirm_impl_candidate<'cx, 'tcx>(
     let term: ty::EarlyBinder<ty::Term<'tcx>> = if is_const {
         let did = assoc_ty.item.def_id;
         let identity_substs = crate::traits::InternalSubsts::identity_for_item(tcx, did);
-        let kind = ty::ConstKind::Unevaluated(ty::UnevaluatedConst::new(did, identity_substs));
-        ty.map_bound(|ty| tcx.mk_const(kind, ty).into())
+        let uv = ty::UnevaluatedConst::new(did, identity_substs);
+        ty.map_bound(|ty| ty::Const::new_unevaluated(tcx, uv, ty).into())
     } else {
         ty.map_bound(|ty| ty.into())
     };
     if !check_substs_compatible(tcx, assoc_ty.item, substs) {
-        let err = tcx.ty_error_with_message(
+        let err = Ty::new_error_with_message(
+            tcx,
             obligation.cause.span,
             "impl item and trait item have different parameters",
         );
@@ -2387,13 +2436,14 @@ fn confirm_impl_trait_in_trait_candidate<'tcx>(
     // We don't support specialization for RPITITs anyways... yet.
     // Also don't try to project to an RPITIT that has no value
     if !leaf_def.is_final() || !leaf_def.item.defaultness(tcx).has_value() {
-        return Progress { term: tcx.ty_error_misc().into(), obligations };
+        return Progress { term: Ty::new_misc_error(tcx).into(), obligations };
     }
 
     // Use the default `impl Trait` for the trait, e.g., for a default trait body
     if leaf_def.item.container == ty::AssocItemContainer::TraitContainer {
         return Progress {
-            term: tcx.mk_opaque(obligation.predicate.def_id, obligation.predicate.substs).into(),
+            term: Ty::new_opaque(tcx, obligation.predicate.def_id, obligation.predicate.substs)
+                .into(),
             obligations,
         };
     }
@@ -2411,7 +2461,8 @@ fn confirm_impl_trait_in_trait_candidate<'tcx>(
     );
 
     if !check_substs_compatible(tcx, leaf_def.item, impl_fn_substs) {
-        let err = tcx.ty_error_with_message(
+        let err = Ty::new_error_with_message(
+            tcx,
             obligation.cause.span,
             "impl method and trait method have different parameters",
         );
@@ -2457,7 +2508,7 @@ fn confirm_impl_trait_in_trait_candidate<'tcx>(
         cause.clone(),
         obligation.recursion_depth + 1,
         tcx.collect_return_position_impl_trait_in_trait_tys(impl_fn_def_id).map_or_else(
-            |guar| tcx.ty_error(guar),
+            |guar| Ty::new_error(tcx, guar),
             |tys| tys[&obligation.predicate.def_id].subst(tcx, impl_fn_substs),
         ),
         &mut obligations,
diff --git a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
index e29e1b25919..a50644bb709 100644
--- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
@@ -97,6 +97,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
                 }
             })
         } else {
+            assert!(!self.intercrate);
             let c_pred = self.canonicalize_query_keep_static(
                 param_env.and(obligation.predicate),
                 &mut _orig_values,
diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
index edad519cec2..7fe79fd865c 100644
--- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
@@ -30,7 +30,7 @@ pub trait QueryNormalizeExt<'tcx> {
     ///
     /// After codegen, when lifetimes do not matter, it is preferable to instead
     /// use [`TyCtxt::normalize_erasing_regions`], which wraps this procedure.
-    fn query_normalize<T>(&self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
+    fn query_normalize<T>(self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
     where
         T: TypeFoldable<TyCtxt<'tcx>>;
 }
@@ -49,7 +49,7 @@ impl<'cx, 'tcx> QueryNormalizeExt<'tcx> for At<'cx, 'tcx> {
     /// normalizing, but for now should be used only when we actually
     /// know that normalization will succeed, since error reporting
     /// and other details are still "under development".
-    fn query_normalize<T>(&self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
+    fn query_normalize<T>(self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
     where
         T: TypeFoldable<TyCtxt<'tcx>>,
     {
@@ -60,6 +60,16 @@ impl<'cx, 'tcx> QueryNormalizeExt<'tcx> for At<'cx, 'tcx> {
             self.param_env,
             self.cause,
         );
+
+        if self.infcx.next_trait_solver() {
+            match crate::solve::deeply_normalize(self, value) {
+                Ok(value) => return Ok(Normalized { value, obligations: vec![] }),
+                Err(_errors) => {
+                    return Err(NoSolution);
+                }
+            }
+        }
+
         if !needs_normalization(&value, self.param_env.reveal()) {
             return Ok(Normalized { value, obligations: vec![] });
         }
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
index 83d536c9ca5..979498fb6e6 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
@@ -1,3 +1,4 @@
+use crate::solve;
 use crate::traits::query::NoSolution;
 use crate::traits::wf;
 use crate::traits::ObligationCtxt;
@@ -6,6 +7,7 @@ use rustc_infer::infer::canonical::Canonical;
 use rustc_infer::infer::outlives::components::{push_outlives_components, Component};
 use rustc_infer::traits::query::OutlivesBound;
 use rustc_middle::infer::canonical::CanonicalQueryResponse;
+use rustc_middle::traits::ObligationCause;
 use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt};
 use rustc_span::def_id::CRATE_DEF_ID;
 use rustc_span::source_map::DUMMY_SP;
@@ -134,7 +136,7 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>(
                 | ty::PredicateKind::ConstEquate(..)
                 | ty::PredicateKind::Ambiguous
                 | ty::PredicateKind::AliasRelate(..)
-                | ty::PredicateKind::Clause(ty::ClauseKind::TypeWellFormedFromEnv(..)) => {}
+                 => {}
 
                 // We need to search through *all* WellFormed predicates
                 ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
@@ -164,19 +166,29 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>(
 
     // We lazily compute the outlives components as
     // `select_all_or_error` constrains inference variables.
-    let implied_bounds = outlives_bounds
-        .into_iter()
-        .flat_map(|ty::OutlivesPredicate(a, r_b)| match a.unpack() {
-            ty::GenericArgKind::Lifetime(r_a) => vec![OutlivesBound::RegionSubRegion(r_b, r_a)],
+    let mut implied_bounds = Vec::new();
+    for ty::OutlivesPredicate(a, r_b) in outlives_bounds {
+        match a.unpack() {
+            ty::GenericArgKind::Lifetime(r_a) => {
+                implied_bounds.push(OutlivesBound::RegionSubRegion(r_b, r_a))
+            }
             ty::GenericArgKind::Type(ty_a) => {
-                let ty_a = ocx.infcx.resolve_vars_if_possible(ty_a);
+                let mut ty_a = ocx.infcx.resolve_vars_if_possible(ty_a);
+                // Need to manually normalize in the new solver as `wf::obligations` does not.
+                if ocx.infcx.next_trait_solver() {
+                    ty_a = solve::deeply_normalize(
+                        ocx.infcx.at(&ObligationCause::dummy(), param_env),
+                        ty_a,
+                    )
+                    .map_err(|_errs| NoSolution)?;
+                }
                 let mut components = smallvec![];
                 push_outlives_components(tcx, ty_a, &mut components);
-                implied_bounds_from_components(r_b, components)
+                implied_bounds.extend(implied_bounds_from_components(r_b, components))
             }
             ty::GenericArgKind::Const(_) => unreachable!(),
-        })
-        .collect();
+        }
+    }
 
     Ok(implied_bounds)
 }
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 412b601c966..a0329259705 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -388,7 +388,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     /// `FnPtr`, when we wanted to report that it doesn't implement `Trait`.
     #[instrument(level = "trace", skip(self), ret)]
     fn reject_fn_ptr_impls(
-        &self,
+        &mut self,
         impl_def_id: DefId,
         obligation: &TraitObligation<'tcx>,
         impl_self_ty: Ty<'tcx>,
@@ -464,7 +464,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred))
                 })),
             );
-            if let Ok(r) = self.infcx.evaluate_obligation(&obligation) {
+            if let Ok(r) = self.evaluate_root_obligation(&obligation) {
                 if !r.may_apply() {
                     return true;
                 }
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index cc6521f3b50..21a223135ed 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -16,7 +16,6 @@ use rustc_middle::ty::{
     self, Binder, GenericParamDefKind, InternalSubsts, SubstsRef, ToPolyTraitRef, ToPredicate,
     TraitPredicate, TraitRef, Ty, TyCtxt, TypeVisitableExt,
 };
-use rustc_session::config::TraitSolver;
 use rustc_span::def_id::DefId;
 
 use crate::traits::project::{normalize_with_depth, normalize_with_depth_to};
@@ -68,7 +67,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             }
 
             AutoImplCandidate => {
-                let data = self.confirm_auto_impl_candidate(obligation);
+                let data = self.confirm_auto_impl_candidate(obligation)?;
                 ImplSource::Builtin(data)
             }
 
@@ -377,12 +376,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     fn confirm_auto_impl_candidate(
         &mut self,
         obligation: &TraitObligation<'tcx>,
-    ) -> Vec<PredicateObligation<'tcx>> {
+    ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
         debug!(?obligation, "confirm_auto_impl_candidate");
 
         let self_ty = self.infcx.shallow_resolve(obligation.predicate.self_ty());
-        let types = self.constituent_types_for_ty(self_ty);
-        self.vtable_auto_impl(obligation, obligation.predicate.def_id(), types)
+        let types = self.constituent_types_for_ty(self_ty)?;
+        Ok(self.vtable_auto_impl(obligation, obligation.predicate.def_id(), types))
     }
 
     /// See `confirm_auto_impl_candidate`.
@@ -588,7 +587,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                                 let kind = ty::BoundTyKind::Param(param.def_id, param.name);
                                 let bound_var = ty::BoundVariableKind::Ty(kind);
                                 bound_vars.push(bound_var);
-                                tcx.mk_bound(
+                                Ty::new_bound(
+                                    tcx,
                                     ty::INNERMOST,
                                     ty::BoundTy {
                                         var: ty::BoundVar::from_usize(bound_vars.len() - 1),
@@ -614,11 +614,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                             GenericParamDefKind::Const { .. } => {
                                 let bound_var = ty::BoundVariableKind::Const;
                                 bound_vars.push(bound_var);
-                                tcx.mk_const(
-                                    ty::ConstKind::Bound(
-                                        ty::INNERMOST,
-                                        ty::BoundVar::from_usize(bound_vars.len() - 1),
-                                    ),
+                                ty::Const::new_bound(
+                                    tcx,
+                                    ty::INNERMOST,
+                                    ty::BoundVar::from_usize(bound_vars.len() - 1),
                                     tcx.type_of(param.def_id)
                                         .no_bound_vars()
                                         .expect("const parameter types cannot be generic"),
@@ -830,13 +829,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
         debug!(?closure_def_id, ?trait_ref, ?nested, "confirm closure candidate obligations");
 
-        // FIXME: Chalk
-        if self.tcx().sess.opts.unstable_opts.trait_solver != TraitSolver::Chalk {
-            nested.push(obligation.with(
-                self.tcx(),
-                ty::Binder::dummy(ty::PredicateKind::ClosureKind(closure_def_id, substs, kind)),
-            ));
-        }
+        nested.push(obligation.with(
+            self.tcx(),
+            ty::Binder::dummy(ty::PredicateKind::ClosureKind(closure_def_id, substs, kind)),
+        ));
 
         Ok(nested)
     }
@@ -951,7 +947,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                             .map(ty::Binder::dummy),
                     );
                 let existential_predicates = tcx.mk_poly_existential_predicates_from_iter(iter);
-                let source_trait = tcx.mk_dynamic(existential_predicates, r_b, repr_a);
+                let source_trait = Ty::new_dynamic(tcx, existential_predicates, r_b, repr_a);
 
                 // Require that the traits involved in this upcast are **equal**;
                 // only the **lifetime bound** is changed.
@@ -1044,7 +1040,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                             .map(ty::Binder::dummy),
                     );
                 let existential_predicates = tcx.mk_poly_existential_predicates_from_iter(iter);
-                let source_trait = tcx.mk_dynamic(existential_predicates, r_b, dyn_a);
+                let source_trait = Ty::new_dynamic(tcx, existential_predicates, r_b, dyn_a);
 
                 // Require that the traits involved in this upcast are **equal**;
                 // only the **lifetime bound** is changed.
@@ -1162,7 +1158,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 let substs = tcx.mk_substs_from_iter(substs_a.iter().enumerate().map(|(i, k)| {
                     if unsizing_params.contains(i as u32) { substs_b[i] } else { k }
                 }));
-                let new_struct = tcx.mk_adt(def, substs);
+                let new_struct = Ty::new_adt(tcx, def, substs);
                 let InferOk { obligations, .. } = self
                     .infcx
                     .at(&obligation.cause, obligation.param_env)
@@ -1193,7 +1189,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 // Check that the source tuple with the target's
                 // last element is equal to the target.
                 let new_tuple =
-                    tcx.mk_tup_from_iter(a_mid.iter().copied().chain(iter::once(b_last)));
+                    Ty::new_tup_from_iter(tcx, a_mid.iter().copied().chain(iter::once(b_last)));
                 let InferOk { obligations, .. } = self
                     .infcx
                     .at(&obligation.cause, obligation.param_env)
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 7cf8479b803..4a55ceb20a1 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -20,6 +20,7 @@ use super::{
 };
 
 use crate::infer::{InferCtxt, InferOk, TypeFreshener};
+use crate::solve::InferCtxtSelectExt;
 use crate::traits::error_reporting::TypeErrCtxtExt;
 use crate::traits::project::try_normalize_with_depth_to;
 use crate::traits::project::ProjectAndUnifyResult;
@@ -33,8 +34,6 @@ use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_infer::infer::DefineOpaqueTypes;
 use rustc_infer::infer::LateBoundRegionConversionTime;
-use rustc_infer::traits::TraitEngine;
-use rustc_infer::traits::TraitEngineExt;
 use rustc_middle::dep_graph::{DepKind, DepNodeIndex};
 use rustc_middle::mir::interpret::ErrorHandled;
 use rustc_middle::ty::abstract_const::NotConstEvaluatable;
@@ -264,6 +263,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         &mut self,
         obligation: &TraitObligation<'tcx>,
     ) -> SelectionResult<'tcx, Selection<'tcx>> {
+        if self.infcx.next_trait_solver() {
+            return self.infcx.select_in_new_trait_solver(obligation);
+        }
+
         let candidate = match self.select_from_obligation(obligation) {
             Err(SelectionError::Overflow(OverflowError::Canonical)) => {
                 // In standard mode, overflow must have been caught and reported
@@ -290,7 +293,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         }
     }
 
-    pub(crate) fn select_from_obligation(
+    fn select_from_obligation(
         &mut self,
         obligation: &TraitObligation<'tcx>,
     ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
@@ -307,6 +310,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         &mut self,
         stack: &TraitObligationStack<'o, 'tcx>,
     ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
+        debug_assert!(!self.infcx.next_trait_solver());
         // Watch out for overflow. This intentionally bypasses (and does
         // not update) the cache.
         self.check_recursion_limit(&stack.obligation, &stack.obligation)?;
@@ -521,21 +525,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     /// Evaluates whether the obligation `obligation` can be satisfied
     /// and returns an `EvaluationResult`. This is meant for the
     /// *initial* call.
+    ///
+    /// Do not use this directly, use `infcx.evaluate_obligation` instead.
     pub fn evaluate_root_obligation(
         &mut self,
         obligation: &PredicateObligation<'tcx>,
     ) -> Result<EvaluationResult, OverflowError> {
+        debug_assert!(!self.infcx.next_trait_solver());
         self.evaluation_probe(|this| {
             let goal =
                 this.infcx.resolve_vars_if_possible((obligation.predicate, obligation.param_env));
-            let mut result = if this.infcx.next_trait_solver() {
-                this.evaluate_predicates_recursively_in_new_solver([obligation.clone()])?
-            } else {
-                this.evaluate_predicate_recursively(
-                    TraitObligationStackList::empty(&ProvisionalEvaluationCache::default()),
-                    obligation.clone(),
-                )?
-            };
+            let mut result = this.evaluate_predicate_recursively(
+                TraitObligationStackList::empty(&ProvisionalEvaluationCache::default()),
+                obligation.clone(),
+            )?;
             // If the predicate has done any inference, then downgrade the
             // result to ambiguous.
             if this.infcx.shallow_resolve(goal) != goal {
@@ -582,42 +585,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     where
         I: IntoIterator<Item = PredicateObligation<'tcx>> + std::fmt::Debug,
     {
-        if self.infcx.next_trait_solver() {
-            self.evaluate_predicates_recursively_in_new_solver(predicates)
-        } else {
-            let mut result = EvaluatedToOk;
-            for mut obligation in predicates {
-                obligation.set_depth_from_parent(stack.depth());
-                let eval = self.evaluate_predicate_recursively(stack, obligation.clone())?;
-                if let EvaluatedToErr = eval {
-                    // fast-path - EvaluatedToErr is the top of the lattice,
-                    // so we don't need to look on the other predicates.
-                    return Ok(EvaluatedToErr);
-                } else {
-                    result = cmp::max(result, eval);
-                }
+        let mut result = EvaluatedToOk;
+        for mut obligation in predicates {
+            obligation.set_depth_from_parent(stack.depth());
+            let eval = self.evaluate_predicate_recursively(stack, obligation.clone())?;
+            if let EvaluatedToErr = eval {
+                // fast-path - EvaluatedToErr is the top of the lattice,
+                // so we don't need to look on the other predicates.
+                return Ok(EvaluatedToErr);
+            } else {
+                result = cmp::max(result, eval);
             }
-            Ok(result)
         }
-    }
-
-    /// Evaluates the predicates using the new solver when `-Ztrait-solver=next` is enabled
-    fn evaluate_predicates_recursively_in_new_solver(
-        &mut self,
-        predicates: impl IntoIterator<Item = PredicateObligation<'tcx>>,
-    ) -> Result<EvaluationResult, OverflowError> {
-        let mut fulfill_cx = crate::solve::FulfillmentCtxt::new(self.infcx);
-        fulfill_cx.register_predicate_obligations(self.infcx, predicates);
-        // True errors
-        // FIXME(-Ztrait-solver=next): Overflows are reported as ambig here, is that OK?
-        if !fulfill_cx.select_where_possible(self.infcx).is_empty() {
-            return Ok(EvaluatedToErr);
-        }
-        if !fulfill_cx.select_all_or_error(self.infcx).is_empty() {
-            return Ok(EvaluatedToAmbig);
-        }
-        // Regions and opaques are handled in the `evaluation_probe` by looking at the snapshot
-        Ok(EvaluatedToOk)
+        Ok(result)
     }
 
     #[instrument(
@@ -631,6 +611,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         previous_stack: TraitObligationStackList<'o, 'tcx>,
         obligation: PredicateObligation<'tcx>,
     ) -> Result<EvaluationResult, OverflowError> {
+        debug_assert!(!self.infcx.next_trait_solver());
         // `previous_stack` stores a `TraitObligation`, while `obligation` is
         // a `PredicateObligation`. These are distinct types, so we can't
         // use any `Option` combinator method that would force them to be
@@ -892,7 +873,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                         );
 
                         use rustc_hir::def::DefKind;
-                        use ty::ConstKind::Unevaluated;
+                        use ty::Unevaluated;
                         match (c1.kind(), c2.kind()) {
                             (Unevaluated(a), Unevaluated(b))
                                 if a.def == b.def && tcx.def_kind(a.def) == DefKind::AssocConst =>
@@ -967,9 +948,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                         }
                     }
                 }
-                ty::PredicateKind::Clause(ty::ClauseKind::TypeWellFormedFromEnv(..)) => {
-                    bug!("TypeWellFormedFromEnv is only used for chalk")
-                }
                 ty::PredicateKind::AliasRelate(..) => {
                     bug!("AliasRelate is only used for new solver")
                 }
@@ -1177,6 +1155,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         &mut self,
         stack: &TraitObligationStack<'o, 'tcx>,
     ) -> Result<EvaluationResult, OverflowError> {
+        debug_assert!(!self.infcx.next_trait_solver());
         // In intercrate mode, whenever any of the generics are unbound,
         // there can always be an impl. Even if there are no impls in
         // this crate, perhaps the type would be unified with
@@ -2292,8 +2271,8 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
     fn constituent_types_for_ty(
         &self,
         t: ty::Binder<'tcx, Ty<'tcx>>,
-    ) -> ty::Binder<'tcx, Vec<Ty<'tcx>>> {
-        match *t.skip_binder().kind() {
+    ) -> Result<ty::Binder<'tcx, Vec<Ty<'tcx>>>, SelectionError<'tcx>> {
+        Ok(match *t.skip_binder().kind() {
             ty::Uint(_)
             | ty::Int(_)
             | ty::Bool
@@ -2306,7 +2285,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
             | ty::Char => ty::Binder::dummy(Vec::new()),
 
             // Treat this like `struct str([u8]);`
-            ty::Str => ty::Binder::dummy(vec![self.tcx().mk_slice(self.tcx().types.u8)]),
+            ty::Str => ty::Binder::dummy(vec![Ty::new_slice(self.tcx(), self.tcx().types.u8)]),
 
             ty::Placeholder(..)
             | ty::Dynamic(..)
@@ -2357,12 +2336,16 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
             }
 
             ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
+                let ty = self.tcx().type_of(def_id);
+                if ty.skip_binder().references_error() {
+                    return Err(SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id));
+                }
                 // We can resolve the `impl Trait` to its concrete type,
                 // which enforces a DAG between the functions requiring
                 // the auto trait bounds in question.
-                t.rebind(vec![self.tcx().type_of(def_id).subst(self.tcx(), substs)])
+                t.rebind(vec![ty.subst(self.tcx(), substs)])
             }
-        }
+        })
     }
 
     fn collect_predicates_for_types(
@@ -2452,7 +2435,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
                     ),
                 );
                 let value = self.infcx.fresh_substs_for_item(obligation.cause.span, impl_def_id);
-                let err = self.tcx().ty_error(guar);
+                let err = Ty::new_error(self.tcx(), guar);
                 let value = value.fold_with(&mut BottomUpFolder {
                     tcx: self.tcx(),
                     ty_op: |_| err,
diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs
index b6c64c0196a..302b6cacf2c 100644
--- a/compiler/rustc_trait_selection/src/traits/util.rs
+++ b/compiler/rustc_trait_selection/src/traits/util.rs
@@ -265,7 +265,7 @@ pub fn closure_trait_ref_and_return_type<'tcx>(
     assert!(!self_ty.has_escaping_bound_vars());
     let arguments_tuple = match tuple_arguments {
         TupleArgumentsFlag::No => sig.skip_binder().inputs()[0],
-        TupleArgumentsFlag::Yes => tcx.mk_tup(sig.skip_binder().inputs()),
+        TupleArgumentsFlag::Yes => Ty::new_tup(tcx, sig.skip_binder().inputs()),
     };
     let trait_ref = ty::TraitRef::new(tcx, fn_trait_def_id, [self_ty, arguments_tuple]);
     sig.map_bound(|sig| (trait_ref, sig.output()))
diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs
index 8bb4288f80d..d81722ce22a 100644
--- a/compiler/rustc_trait_selection/src/traits/wf.rs
+++ b/compiler/rustc_trait_selection/src/traits/wf.rs
@@ -184,8 +184,7 @@ pub fn predicate_obligations<'tcx>(
         | ty::PredicateKind::Coerce(..)
         | ty::PredicateKind::ConstEquate(..)
         | ty::PredicateKind::Ambiguous
-        | ty::PredicateKind::AliasRelate(..)
-        | ty::PredicateKind::Clause(ty::ClauseKind::TypeWellFormedFromEnv(..)) => {
+        | ty::PredicateKind::AliasRelate(..) => {
             bug!("We should only wf check where clauses, unexpected predicate: {predicate:?}")
         }
     }
@@ -303,6 +302,16 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
     }
 
     fn normalize(self, infcx: &InferCtxt<'tcx>) -> Vec<traits::PredicateObligation<'tcx>> {
+        // Do not normalize `wf` obligations with the new solver.
+        //
+        // The current deep normalization routine with the new solver does not
+        // handle ambiguity and the new solver correctly deals with unnnormalized goals.
+        // If the user relies on normalized types, e.g. for `fn implied_outlives_bounds`,
+        // it is their responsibility to normalize while avoiding ambiguity.
+        if infcx.next_trait_solver() {
+            return self.out;
+        }
+
         let cause = self.cause(traits::WellFormed(None));
         let param_env = self.param_env;
         let mut obligations = Vec::with_capacity(self.out.len());
@@ -943,7 +952,7 @@ pub fn object_region_bounds<'tcx>(
     // Since we don't actually *know* the self type for an object,
     // this "open(err)" serves as a kind of dummy standin -- basically
     // a placeholder type.
-    let open_ty = tcx.mk_fresh_ty(0);
+    let open_ty = Ty::new_fresh(tcx, 0);
 
     let predicates = existential_predicates.iter().filter_map(|predicate| {
         if let ty::ExistentialPredicate::Projection(_) = predicate.skip_binder() {
@@ -1005,8 +1014,7 @@ pub(crate) fn required_region_bounds<'tcx>(
                 | ty::ClauseKind::Projection(_)
                 | ty::ClauseKind::ConstArgHasType(_, _)
                 | ty::ClauseKind::WellFormed(_)
-                | ty::ClauseKind::ConstEvaluatable(_)
-                | ty::ClauseKind::TypeWellFormedFromEnv(_) => None,
+                | ty::ClauseKind::ConstEvaluatable(_) => None,
             }
         })
         .collect()
diff --git a/compiler/rustc_traits/Cargo.toml b/compiler/rustc_traits/Cargo.toml
index 189f72d478e..37e00c0e4bc 100644
--- a/compiler/rustc_traits/Cargo.toml
+++ b/compiler/rustc_traits/Cargo.toml
@@ -11,9 +11,6 @@ rustc_hir = { path = "../rustc_hir" }
 rustc_ast = { path = "../rustc_ast" }
 rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
-chalk-ir = "0.92.0"
-chalk-engine = "0.92.0"
-chalk-solve = "0.92.0"
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
 rustc_infer = { path = "../rustc_infer" }
 rustc_trait_selection = { path = "../rustc_trait_selection" }
diff --git a/compiler/rustc_traits/src/chalk/db.rs b/compiler/rustc_traits/src/chalk/db.rs
deleted file mode 100644
index 0435cd63e80..00000000000
--- a/compiler/rustc_traits/src/chalk/db.rs
+++ /dev/null
@@ -1,796 +0,0 @@
-//! Provides the `RustIrDatabase` implementation for `chalk-solve`
-//!
-//! The purpose of the `chalk_solve::RustIrDatabase` is to get data about
-//! specific types, such as bounds, where clauses, or fields. This file contains
-//! the minimal logic to assemble the types for `chalk-solve` by calling out to
-//! either the `TyCtxt` (for information about types) or
-//! `crate::chalk::lowering` (to lower rustc types into Chalk types).
-
-use rustc_middle::traits::ChalkRustInterner as RustInterner;
-use rustc_middle::ty::{self, AssocKind, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable};
-use rustc_middle::ty::{InternalSubsts, SubstsRef};
-use rustc_target::abi::{Integer, IntegerType};
-
-use rustc_ast::ast;
-
-use rustc_hir::def_id::DefId;
-
-use rustc_span::symbol::sym;
-
-use std::fmt;
-use std::sync::Arc;
-
-use crate::chalk::lowering::LowerInto;
-
-pub struct RustIrDatabase<'tcx> {
-    pub(crate) interner: RustInterner<'tcx>,
-}
-
-impl fmt::Debug for RustIrDatabase<'_> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "RustIrDatabase")
-    }
-}
-
-impl<'tcx> RustIrDatabase<'tcx> {
-    fn where_clauses_for(
-        &self,
-        def_id: DefId,
-        bound_vars: SubstsRef<'tcx>,
-    ) -> Vec<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>> {
-        self.interner
-            .tcx
-            .predicates_defined_on(def_id)
-            .instantiate_own(self.interner.tcx, bound_vars)
-            .filter_map(|(wc, _)| LowerInto::lower_into(wc.as_predicate(), self.interner))
-            .collect()
-    }
-
-    fn bounds_for<T>(&self, def_id: DefId, bound_vars: SubstsRef<'tcx>) -> Vec<T>
-    where
-        ty::Predicate<'tcx>: LowerInto<'tcx, std::option::Option<T>>,
-    {
-        self.interner
-            .tcx
-            .explicit_item_bounds(def_id)
-            .subst_iter_copied(self.interner.tcx, &bound_vars)
-            .filter_map(|(bound, _)| {
-                LowerInto::<Option<_>>::lower_into(bound.as_predicate(), self.interner)
-            })
-            .collect()
-    }
-}
-
-impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'tcx> {
-    fn interner(&self) -> RustInterner<'tcx> {
-        self.interner
-    }
-
-    fn associated_ty_data(
-        &self,
-        assoc_type_id: chalk_ir::AssocTypeId<RustInterner<'tcx>>,
-    ) -> Arc<chalk_solve::rust_ir::AssociatedTyDatum<RustInterner<'tcx>>> {
-        let def_id = assoc_type_id.0;
-        let assoc_item = self.interner.tcx.associated_item(def_id);
-        let Some(trait_def_id) = assoc_item.trait_container(self.interner.tcx) else {
-            unimplemented!("Not possible??");
-        };
-        match assoc_item.kind {
-            AssocKind::Type => {}
-            _ => unimplemented!("Not possible??"),
-        }
-        let bound_vars = bound_vars_for_item(self.interner.tcx, def_id);
-        let binders = binders_for(self.interner, bound_vars);
-
-        let where_clauses = self.where_clauses_for(def_id, bound_vars);
-        let bounds = self.bounds_for(def_id, bound_vars);
-
-        Arc::new(chalk_solve::rust_ir::AssociatedTyDatum {
-            trait_id: chalk_ir::TraitId(trait_def_id),
-            id: assoc_type_id,
-            name: (),
-            binders: chalk_ir::Binders::new(
-                binders,
-                chalk_solve::rust_ir::AssociatedTyDatumBound { bounds, where_clauses },
-            ),
-        })
-    }
-
-    fn trait_datum(
-        &self,
-        trait_id: chalk_ir::TraitId<RustInterner<'tcx>>,
-    ) -> Arc<chalk_solve::rust_ir::TraitDatum<RustInterner<'tcx>>> {
-        use chalk_solve::rust_ir::WellKnownTrait::*;
-
-        let def_id = trait_id.0;
-        let trait_def = self.interner.tcx.trait_def(def_id);
-
-        let bound_vars = bound_vars_for_item(self.interner.tcx, def_id);
-        let binders = binders_for(self.interner, bound_vars);
-
-        let where_clauses = self.where_clauses_for(def_id, bound_vars);
-
-        let associated_ty_ids: Vec<_> = self
-            .interner
-            .tcx
-            .associated_items(def_id)
-            .in_definition_order()
-            .filter(|i| i.kind == AssocKind::Type)
-            .map(|i| chalk_ir::AssocTypeId(i.def_id))
-            .collect();
-
-        let lang_items = self.interner.tcx.lang_items();
-        let well_known = if lang_items.sized_trait() == Some(def_id) {
-            Some(Sized)
-        } else if lang_items.copy_trait() == Some(def_id) {
-            Some(Copy)
-        } else if lang_items.clone_trait() == Some(def_id) {
-            Some(Clone)
-        } else if lang_items.drop_trait() == Some(def_id) {
-            Some(Drop)
-        } else if lang_items.fn_trait() == Some(def_id) {
-            Some(Fn)
-        } else if lang_items.fn_once_trait() == Some(def_id) {
-            Some(FnOnce)
-        } else if lang_items.fn_mut_trait() == Some(def_id) {
-            Some(FnMut)
-        } else if lang_items.unsize_trait() == Some(def_id) {
-            Some(Unsize)
-        } else if lang_items.unpin_trait() == Some(def_id) {
-            Some(Unpin)
-        } else if lang_items.coerce_unsized_trait() == Some(def_id) {
-            Some(CoerceUnsized)
-        } else if lang_items.dispatch_from_dyn_trait() == Some(def_id) {
-            Some(DispatchFromDyn)
-        } else if lang_items.tuple_trait() == Some(def_id) {
-            Some(Tuple)
-        } else {
-            None
-        };
-        Arc::new(chalk_solve::rust_ir::TraitDatum {
-            id: trait_id,
-            binders: chalk_ir::Binders::new(
-                binders,
-                chalk_solve::rust_ir::TraitDatumBound { where_clauses },
-            ),
-            flags: chalk_solve::rust_ir::TraitFlags {
-                auto: trait_def.has_auto_impl,
-                marker: trait_def.is_marker,
-                upstream: !def_id.is_local(),
-                fundamental: self.interner.tcx.has_attr(def_id, sym::fundamental),
-                non_enumerable: true,
-                coinductive: false,
-            },
-            associated_ty_ids,
-            well_known,
-        })
-    }
-
-    fn adt_datum(
-        &self,
-        adt_id: chalk_ir::AdtId<RustInterner<'tcx>>,
-    ) -> Arc<chalk_solve::rust_ir::AdtDatum<RustInterner<'tcx>>> {
-        let adt_def = adt_id.0;
-
-        let bound_vars = bound_vars_for_item(self.interner.tcx, adt_def.did());
-        let binders = binders_for(self.interner, bound_vars);
-
-        let where_clauses = self.where_clauses_for(adt_def.did(), bound_vars);
-
-        let variants: Vec<_> = adt_def
-            .variants()
-            .iter()
-            .map(|variant| chalk_solve::rust_ir::AdtVariantDatum {
-                fields: variant
-                    .fields
-                    .iter()
-                    .map(|field| field.ty(self.interner.tcx, bound_vars).lower_into(self.interner))
-                    .collect(),
-            })
-            .collect();
-        Arc::new(chalk_solve::rust_ir::AdtDatum {
-            id: adt_id,
-            binders: chalk_ir::Binders::new(
-                binders,
-                chalk_solve::rust_ir::AdtDatumBound { variants, where_clauses },
-            ),
-            flags: chalk_solve::rust_ir::AdtFlags {
-                upstream: !adt_def.did().is_local(),
-                fundamental: adt_def.is_fundamental(),
-                phantom_data: adt_def.is_phantom_data(),
-            },
-            kind: match adt_def.adt_kind() {
-                ty::AdtKind::Struct => chalk_solve::rust_ir::AdtKind::Struct,
-                ty::AdtKind::Union => chalk_solve::rust_ir::AdtKind::Union,
-                ty::AdtKind::Enum => chalk_solve::rust_ir::AdtKind::Enum,
-            },
-        })
-    }
-
-    fn adt_repr(
-        &self,
-        adt_id: chalk_ir::AdtId<RustInterner<'tcx>>,
-    ) -> Arc<chalk_solve::rust_ir::AdtRepr<RustInterner<'tcx>>> {
-        let adt_def = adt_id.0;
-        let int = |i| chalk_ir::TyKind::Scalar(chalk_ir::Scalar::Int(i)).intern(self.interner);
-        let uint = |i| chalk_ir::TyKind::Scalar(chalk_ir::Scalar::Uint(i)).intern(self.interner);
-        Arc::new(chalk_solve::rust_ir::AdtRepr {
-            c: adt_def.repr().c(),
-            packed: adt_def.repr().packed(),
-            int: adt_def.repr().int.map(|i| match i {
-                IntegerType::Pointer(true) => int(chalk_ir::IntTy::Isize),
-                IntegerType::Pointer(false) => uint(chalk_ir::UintTy::Usize),
-                IntegerType::Fixed(i, true) => match i {
-                    Integer::I8 => int(chalk_ir::IntTy::I8),
-                    Integer::I16 => int(chalk_ir::IntTy::I16),
-                    Integer::I32 => int(chalk_ir::IntTy::I32),
-                    Integer::I64 => int(chalk_ir::IntTy::I64),
-                    Integer::I128 => int(chalk_ir::IntTy::I128),
-                },
-                IntegerType::Fixed(i, false) => match i {
-                    Integer::I8 => uint(chalk_ir::UintTy::U8),
-                    Integer::I16 => uint(chalk_ir::UintTy::U16),
-                    Integer::I32 => uint(chalk_ir::UintTy::U32),
-                    Integer::I64 => uint(chalk_ir::UintTy::U64),
-                    Integer::I128 => uint(chalk_ir::UintTy::U128),
-                },
-            }),
-        })
-    }
-
-    fn adt_size_align(
-        &self,
-        adt_id: chalk_ir::AdtId<RustInterner<'tcx>>,
-    ) -> Arc<chalk_solve::rust_ir::AdtSizeAlign> {
-        let tcx = self.interner.tcx;
-        let did = adt_id.0.did();
-
-        // Grab the ADT and the param we might need to calculate its layout
-        let param_env = tcx.param_env(did);
-        let adt_ty = tcx.type_of(did).subst_identity();
-
-        // The ADT is a 1-zst if it's a ZST and its alignment is 1.
-        // Mark the ADT as _not_ a 1-zst if there was a layout error.
-        let one_zst = if let Ok(layout) = tcx.layout_of(param_env.and(adt_ty)) {
-            layout.is_zst() && layout.align.abi.bytes() == 1
-        } else {
-            false
-        };
-
-        Arc::new(chalk_solve::rust_ir::AdtSizeAlign::from_one_zst(one_zst))
-    }
-
-    fn fn_def_datum(
-        &self,
-        fn_def_id: chalk_ir::FnDefId<RustInterner<'tcx>>,
-    ) -> Arc<chalk_solve::rust_ir::FnDefDatum<RustInterner<'tcx>>> {
-        let def_id = fn_def_id.0;
-        let bound_vars = bound_vars_for_item(self.interner.tcx, def_id);
-        let binders = binders_for(self.interner, bound_vars);
-
-        let where_clauses = self.where_clauses_for(def_id, bound_vars);
-
-        let sig = self.interner.tcx.fn_sig(def_id);
-        let (inputs_and_output, iobinders, _) = crate::chalk::lowering::collect_bound_vars(
-            self.interner,
-            self.interner.tcx,
-            sig.map_bound(|s| s.inputs_and_output()).subst(self.interner.tcx, bound_vars),
-        );
-
-        let argument_types = inputs_and_output[..inputs_and_output.len() - 1]
-            .iter()
-            .map(|t| sig.rebind(*t).subst(self.interner.tcx, &bound_vars).lower_into(self.interner))
-            .collect();
-
-        let return_type = sig
-            .rebind(inputs_and_output[inputs_and_output.len() - 1])
-            .subst(self.interner.tcx, &bound_vars)
-            .lower_into(self.interner);
-
-        let bound = chalk_solve::rust_ir::FnDefDatumBound {
-            inputs_and_output: chalk_ir::Binders::new(
-                iobinders,
-                chalk_solve::rust_ir::FnDefInputsAndOutputDatum { argument_types, return_type },
-            ),
-            where_clauses,
-        };
-        Arc::new(chalk_solve::rust_ir::FnDefDatum {
-            id: fn_def_id,
-            sig: sig.skip_binder().lower_into(self.interner),
-            binders: chalk_ir::Binders::new(binders, bound),
-        })
-    }
-
-    fn impl_datum(
-        &self,
-        impl_id: chalk_ir::ImplId<RustInterner<'tcx>>,
-    ) -> Arc<chalk_solve::rust_ir::ImplDatum<RustInterner<'tcx>>> {
-        let def_id = impl_id.0;
-        let bound_vars = bound_vars_for_item(self.interner.tcx, def_id);
-        let binders = binders_for(self.interner, bound_vars);
-
-        let trait_ref = self.interner.tcx.impl_trait_ref(def_id).expect("not an impl");
-        let trait_ref = trait_ref.subst(self.interner.tcx, bound_vars);
-
-        let where_clauses = self.where_clauses_for(def_id, bound_vars);
-
-        let value = chalk_solve::rust_ir::ImplDatumBound {
-            trait_ref: trait_ref.lower_into(self.interner),
-            where_clauses,
-        };
-
-        let associated_ty_value_ids: Vec<_> = self
-            .interner
-            .tcx
-            .associated_items(def_id)
-            .in_definition_order()
-            .filter(|i| i.kind == AssocKind::Type)
-            .map(|i| chalk_solve::rust_ir::AssociatedTyValueId(i.def_id))
-            .collect();
-
-        Arc::new(chalk_solve::rust_ir::ImplDatum {
-            polarity: self.interner.tcx.impl_polarity(def_id).lower_into(self.interner),
-            binders: chalk_ir::Binders::new(binders, value),
-            impl_type: chalk_solve::rust_ir::ImplType::Local,
-            associated_ty_value_ids,
-        })
-    }
-
-    fn impls_for_trait(
-        &self,
-        trait_id: chalk_ir::TraitId<RustInterner<'tcx>>,
-        parameters: &[chalk_ir::GenericArg<RustInterner<'tcx>>],
-        _binders: &chalk_ir::CanonicalVarKinds<RustInterner<'tcx>>,
-    ) -> Vec<chalk_ir::ImplId<RustInterner<'tcx>>> {
-        let def_id = trait_id.0;
-
-        // FIXME(chalk): use TraitDef::for_each_relevant_impl, but that will
-        // require us to be able to interconvert `Ty<'tcx>`, and we're
-        // not there yet.
-
-        let all_impls = self.interner.tcx.all_impls(def_id);
-        let matched_impls = all_impls.filter(|impl_def_id| {
-            use chalk_ir::could_match::CouldMatch;
-            let trait_ref = self.interner.tcx.impl_trait_ref(*impl_def_id).unwrap();
-            let bound_vars = bound_vars_for_item(self.interner.tcx, *impl_def_id);
-
-            let self_ty = trait_ref.map_bound(|t| t.self_ty());
-            let self_ty = self_ty.subst(self.interner.tcx, bound_vars);
-            let lowered_ty = self_ty.lower_into(self.interner);
-
-            parameters[0].assert_ty_ref(self.interner).could_match(
-                self.interner,
-                self.unification_database(),
-                &lowered_ty,
-            )
-        });
-
-        let impls = matched_impls.map(chalk_ir::ImplId).collect();
-        impls
-    }
-
-    fn impl_provided_for(
-        &self,
-        auto_trait_id: chalk_ir::TraitId<RustInterner<'tcx>>,
-        chalk_ty: &chalk_ir::TyKind<RustInterner<'tcx>>,
-    ) -> bool {
-        use chalk_ir::Scalar::*;
-        use chalk_ir::TyKind::*;
-
-        let trait_def_id = auto_trait_id.0;
-        let all_impls = self.interner.tcx.all_impls(trait_def_id);
-        for impl_def_id in all_impls {
-            let trait_ref = self.interner.tcx.impl_trait_ref(impl_def_id).unwrap().subst_identity();
-            let self_ty = trait_ref.self_ty();
-            let provides = match (self_ty.kind(), chalk_ty) {
-                (&ty::Adt(impl_adt_def, ..), Adt(id, ..)) => impl_adt_def.did() == id.0.did(),
-                (_, AssociatedType(_ty_id, ..)) => {
-                    // FIXME(chalk): See https://github.com/rust-lang/rust/pull/77152#discussion_r494484774
-                    false
-                }
-                (ty::Bool, Scalar(Bool)) => true,
-                (ty::Char, Scalar(Char)) => true,
-                (ty::Int(ty1), Scalar(Int(ty2))) => matches!(
-                    (ty1, ty2),
-                    (ty::IntTy::Isize, chalk_ir::IntTy::Isize)
-                        | (ty::IntTy::I8, chalk_ir::IntTy::I8)
-                        | (ty::IntTy::I16, chalk_ir::IntTy::I16)
-                        | (ty::IntTy::I32, chalk_ir::IntTy::I32)
-                        | (ty::IntTy::I64, chalk_ir::IntTy::I64)
-                        | (ty::IntTy::I128, chalk_ir::IntTy::I128)
-                ),
-                (ty::Uint(ty1), Scalar(Uint(ty2))) => matches!(
-                    (ty1, ty2),
-                    (ty::UintTy::Usize, chalk_ir::UintTy::Usize)
-                        | (ty::UintTy::U8, chalk_ir::UintTy::U8)
-                        | (ty::UintTy::U16, chalk_ir::UintTy::U16)
-                        | (ty::UintTy::U32, chalk_ir::UintTy::U32)
-                        | (ty::UintTy::U64, chalk_ir::UintTy::U64)
-                        | (ty::UintTy::U128, chalk_ir::UintTy::U128)
-                ),
-                (ty::Float(ty1), Scalar(Float(ty2))) => matches!(
-                    (ty1, ty2),
-                    (ty::FloatTy::F32, chalk_ir::FloatTy::F32)
-                        | (ty::FloatTy::F64, chalk_ir::FloatTy::F64)
-                ),
-                (&ty::Tuple(substs), Tuple(len, _)) => substs.len() == *len,
-                (&ty::Array(..), Array(..)) => true,
-                (&ty::Slice(..), Slice(..)) => true,
-                (&ty::RawPtr(type_and_mut), Raw(mutability, _)) => {
-                    match (type_and_mut.mutbl, mutability) {
-                        (ast::Mutability::Mut, chalk_ir::Mutability::Mut) => true,
-                        (ast::Mutability::Mut, chalk_ir::Mutability::Not) => false,
-                        (ast::Mutability::Not, chalk_ir::Mutability::Mut) => false,
-                        (ast::Mutability::Not, chalk_ir::Mutability::Not) => true,
-                    }
-                }
-                (&ty::Ref(.., mutability1), Ref(mutability2, ..)) => {
-                    match (mutability1, mutability2) {
-                        (ast::Mutability::Mut, chalk_ir::Mutability::Mut) => true,
-                        (ast::Mutability::Mut, chalk_ir::Mutability::Not) => false,
-                        (ast::Mutability::Not, chalk_ir::Mutability::Mut) => false,
-                        (ast::Mutability::Not, chalk_ir::Mutability::Not) => true,
-                    }
-                }
-                (
-                    &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }),
-                    OpaqueType(opaque_ty_id, ..),
-                ) => def_id == opaque_ty_id.0,
-                (&ty::FnDef(def_id, ..), FnDef(fn_def_id, ..)) => def_id == fn_def_id.0,
-                (&ty::Str, Str) => true,
-                (&ty::Never, Never) => true,
-                (&ty::Closure(def_id, ..), Closure(closure_id, _)) => def_id == closure_id.0,
-                (&ty::Foreign(def_id), Foreign(foreign_def_id)) => def_id == foreign_def_id.0,
-                (&ty::Error(..), Error) => false,
-                _ => false,
-            };
-            if provides {
-                return true;
-            }
-        }
-        false
-    }
-
-    fn associated_ty_value(
-        &self,
-        associated_ty_id: chalk_solve::rust_ir::AssociatedTyValueId<RustInterner<'tcx>>,
-    ) -> Arc<chalk_solve::rust_ir::AssociatedTyValue<RustInterner<'tcx>>> {
-        let def_id = associated_ty_id.0;
-        let assoc_item = self.interner.tcx.associated_item(def_id);
-        let impl_id = assoc_item.container_id(self.interner.tcx);
-        match assoc_item.kind {
-            AssocKind::Type => {}
-            _ => unimplemented!("Not possible??"),
-        }
-
-        let trait_item_id = assoc_item.trait_item_def_id.expect("assoc_ty with no trait version");
-        let bound_vars = bound_vars_for_item(self.interner.tcx, def_id);
-        let binders = binders_for(self.interner, bound_vars);
-        let ty = self
-            .interner
-            .tcx
-            .type_of(def_id)
-            .subst(self.interner.tcx, bound_vars)
-            .lower_into(self.interner);
-
-        Arc::new(chalk_solve::rust_ir::AssociatedTyValue {
-            impl_id: chalk_ir::ImplId(impl_id),
-            associated_ty_id: chalk_ir::AssocTypeId(trait_item_id),
-            value: chalk_ir::Binders::new(
-                binders,
-                chalk_solve::rust_ir::AssociatedTyValueBound { ty },
-            ),
-        })
-    }
-
-    fn custom_clauses(&self) -> Vec<chalk_ir::ProgramClause<RustInterner<'tcx>>> {
-        vec![]
-    }
-
-    fn local_impls_to_coherence_check(
-        &self,
-        _trait_id: chalk_ir::TraitId<RustInterner<'tcx>>,
-    ) -> Vec<chalk_ir::ImplId<RustInterner<'tcx>>> {
-        unimplemented!()
-    }
-
-    fn opaque_ty_data(
-        &self,
-        opaque_ty_id: chalk_ir::OpaqueTyId<RustInterner<'tcx>>,
-    ) -> Arc<chalk_solve::rust_ir::OpaqueTyDatum<RustInterner<'tcx>>> {
-        let bound_vars = ty::fold::shift_vars(
-            self.interner.tcx,
-            bound_vars_for_item(self.interner.tcx, opaque_ty_id.0),
-            1,
-        );
-        let where_clauses = self.where_clauses_for(opaque_ty_id.0, bound_vars);
-
-        let identity_substs = InternalSubsts::identity_for_item(self.interner.tcx, opaque_ty_id.0);
-
-        let explicit_item_bounds = self.interner.tcx.explicit_item_bounds(opaque_ty_id.0);
-        let bounds =
-            explicit_item_bounds
-                .subst_iter_copied(self.interner.tcx, &bound_vars)
-                .map(|(bound, _)| {
-                    bound.fold_with(&mut ReplaceOpaqueTyFolder {
-                        tcx: self.interner.tcx,
-                        opaque_ty_id,
-                        identity_substs,
-                        binder_index: ty::INNERMOST,
-                    })
-                })
-                .filter_map(|bound| {
-                    LowerInto::<
-                    Option<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>>
-                >::lower_into(bound.as_predicate(), self.interner)
-                })
-                .collect();
-
-        // Binder for the bound variable representing the concrete impl Trait type.
-        let existential_binder = chalk_ir::VariableKinds::from1(
-            self.interner,
-            chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General),
-        );
-
-        let value = chalk_solve::rust_ir::OpaqueTyDatumBound {
-            bounds: chalk_ir::Binders::new(existential_binder.clone(), bounds),
-            where_clauses: chalk_ir::Binders::new(existential_binder, where_clauses),
-        };
-
-        let binders = binders_for(self.interner, bound_vars);
-        Arc::new(chalk_solve::rust_ir::OpaqueTyDatum {
-            opaque_ty_id,
-            bound: chalk_ir::Binders::new(binders, value),
-        })
-    }
-
-    fn program_clauses_for_env(
-        &self,
-        environment: &chalk_ir::Environment<RustInterner<'tcx>>,
-    ) -> chalk_ir::ProgramClauses<RustInterner<'tcx>> {
-        chalk_solve::program_clauses_for_env(self, environment)
-    }
-
-    fn well_known_trait_id(
-        &self,
-        well_known_trait: chalk_solve::rust_ir::WellKnownTrait,
-    ) -> Option<chalk_ir::TraitId<RustInterner<'tcx>>> {
-        use chalk_solve::rust_ir::WellKnownTrait::*;
-        let lang_items = self.interner.tcx.lang_items();
-        let def_id = match well_known_trait {
-            Sized => lang_items.sized_trait(),
-            Copy => lang_items.copy_trait(),
-            Clone => lang_items.clone_trait(),
-            Drop => lang_items.drop_trait(),
-            Fn => lang_items.fn_trait(),
-            FnMut => lang_items.fn_mut_trait(),
-            FnOnce => lang_items.fn_once_trait(),
-            Generator => lang_items.gen_trait(),
-            Unsize => lang_items.unsize_trait(),
-            Unpin => lang_items.unpin_trait(),
-            CoerceUnsized => lang_items.coerce_unsized_trait(),
-            DiscriminantKind => lang_items.discriminant_kind_trait(),
-            DispatchFromDyn => lang_items.dispatch_from_dyn_trait(),
-            Tuple => lang_items.tuple_trait(),
-            Pointee => lang_items.pointee_trait(),
-            FnPtr => lang_items.fn_ptr_trait(),
-        };
-        def_id.map(chalk_ir::TraitId)
-    }
-
-    fn is_object_safe(&self, trait_id: chalk_ir::TraitId<RustInterner<'tcx>>) -> bool {
-        self.interner.tcx.check_is_object_safe(trait_id.0)
-    }
-
-    fn hidden_opaque_type(
-        &self,
-        _id: chalk_ir::OpaqueTyId<RustInterner<'tcx>>,
-    ) -> chalk_ir::Ty<RustInterner<'tcx>> {
-        // FIXME(chalk): actually get hidden ty
-        self.interner.tcx.types.unit.lower_into(self.interner)
-    }
-
-    fn closure_kind(
-        &self,
-        _closure_id: chalk_ir::ClosureId<RustInterner<'tcx>>,
-        substs: &chalk_ir::Substitution<RustInterner<'tcx>>,
-    ) -> chalk_solve::rust_ir::ClosureKind {
-        let kind = &substs.as_slice(self.interner)[substs.len(self.interner) - 3];
-        match kind.assert_ty_ref(self.interner).kind(self.interner) {
-            chalk_ir::TyKind::Scalar(chalk_ir::Scalar::Int(int_ty)) => match int_ty {
-                chalk_ir::IntTy::I8 => chalk_solve::rust_ir::ClosureKind::Fn,
-                chalk_ir::IntTy::I16 => chalk_solve::rust_ir::ClosureKind::FnMut,
-                chalk_ir::IntTy::I32 => chalk_solve::rust_ir::ClosureKind::FnOnce,
-                _ => bug!("bad closure kind"),
-            },
-            _ => bug!("bad closure kind"),
-        }
-    }
-
-    fn closure_inputs_and_output(
-        &self,
-        _closure_id: chalk_ir::ClosureId<RustInterner<'tcx>>,
-        substs: &chalk_ir::Substitution<RustInterner<'tcx>>,
-    ) -> chalk_ir::Binders<chalk_solve::rust_ir::FnDefInputsAndOutputDatum<RustInterner<'tcx>>>
-    {
-        let sig = &substs.as_slice(self.interner)[substs.len(self.interner) - 2];
-        match sig.assert_ty_ref(self.interner).kind(self.interner) {
-            chalk_ir::TyKind::Function(f) => {
-                let substitution = f.substitution.0.as_slice(self.interner);
-                let return_type = substitution.last().unwrap().assert_ty_ref(self.interner).clone();
-                // Closure arguments are tupled
-                let argument_tuple = substitution[0].assert_ty_ref(self.interner);
-                let argument_types = match argument_tuple.kind(self.interner) {
-                    chalk_ir::TyKind::Tuple(_len, substitution) => substitution
-                        .iter(self.interner)
-                        .map(|arg| arg.assert_ty_ref(self.interner))
-                        .cloned()
-                        .collect(),
-                    _ => bug!("Expecting closure FnSig args to be tupled."),
-                };
-
-                chalk_ir::Binders::new(
-                    chalk_ir::VariableKinds::from_iter(
-                        self.interner,
-                        (0..f.num_binders).map(|_| chalk_ir::VariableKind::Lifetime),
-                    ),
-                    chalk_solve::rust_ir::FnDefInputsAndOutputDatum { argument_types, return_type },
-                )
-            }
-            _ => panic!("Invalid sig."),
-        }
-    }
-
-    fn closure_upvars(
-        &self,
-        _closure_id: chalk_ir::ClosureId<RustInterner<'tcx>>,
-        substs: &chalk_ir::Substitution<RustInterner<'tcx>>,
-    ) -> chalk_ir::Binders<chalk_ir::Ty<RustInterner<'tcx>>> {
-        let inputs_and_output = self.closure_inputs_and_output(_closure_id, substs);
-        let tuple = substs.as_slice(self.interner).last().unwrap().assert_ty_ref(self.interner);
-        inputs_and_output.map_ref(|_| tuple.clone())
-    }
-
-    fn closure_fn_substitution(
-        &self,
-        _closure_id: chalk_ir::ClosureId<RustInterner<'tcx>>,
-        substs: &chalk_ir::Substitution<RustInterner<'tcx>>,
-    ) -> chalk_ir::Substitution<RustInterner<'tcx>> {
-        let substitution = &substs.as_slice(self.interner)[0..substs.len(self.interner) - 3];
-        chalk_ir::Substitution::from_iter(self.interner, substitution)
-    }
-
-    fn generator_datum(
-        &self,
-        _generator_id: chalk_ir::GeneratorId<RustInterner<'tcx>>,
-    ) -> Arc<chalk_solve::rust_ir::GeneratorDatum<RustInterner<'tcx>>> {
-        unimplemented!()
-    }
-
-    fn generator_witness_datum(
-        &self,
-        _generator_id: chalk_ir::GeneratorId<RustInterner<'tcx>>,
-    ) -> Arc<chalk_solve::rust_ir::GeneratorWitnessDatum<RustInterner<'tcx>>> {
-        unimplemented!()
-    }
-
-    fn unification_database(&self) -> &dyn chalk_ir::UnificationDatabase<RustInterner<'tcx>> {
-        self
-    }
-
-    fn discriminant_type(
-        &self,
-        _: chalk_ir::Ty<RustInterner<'tcx>>,
-    ) -> chalk_ir::Ty<RustInterner<'tcx>> {
-        unimplemented!()
-    }
-}
-
-impl<'tcx> chalk_ir::UnificationDatabase<RustInterner<'tcx>> for RustIrDatabase<'tcx> {
-    fn fn_def_variance(
-        &self,
-        def_id: chalk_ir::FnDefId<RustInterner<'tcx>>,
-    ) -> chalk_ir::Variances<RustInterner<'tcx>> {
-        let variances = self.interner.tcx.variances_of(def_id.0);
-        chalk_ir::Variances::from_iter(
-            self.interner,
-            variances.iter().map(|v| v.lower_into(self.interner)),
-        )
-    }
-
-    fn adt_variance(
-        &self,
-        adt_id: chalk_ir::AdtId<RustInterner<'tcx>>,
-    ) -> chalk_ir::Variances<RustInterner<'tcx>> {
-        let variances = self.interner.tcx.variances_of(adt_id.0.did());
-        chalk_ir::Variances::from_iter(
-            self.interner,
-            variances.iter().map(|v| v.lower_into(self.interner)),
-        )
-    }
-}
-
-/// Creates an `InternalSubsts` that maps each generic parameter to a higher-ranked
-/// var bound at index `0`. For types, we use a `BoundVar` index equal to
-/// the type parameter index. For regions, we use the `BoundRegionKind::BrNamed`
-/// variant (which has a `DefId`).
-fn bound_vars_for_item(tcx: TyCtxt<'_>, def_id: DefId) -> SubstsRef<'_> {
-    InternalSubsts::for_item(tcx, def_id, |param, substs| match param.kind {
-        ty::GenericParamDefKind::Type { .. } => tcx
-            .mk_bound(
-                ty::INNERMOST,
-                ty::BoundTy {
-                    var: ty::BoundVar::from(param.index),
-                    kind: ty::BoundTyKind::Param(param.def_id, param.name),
-                },
-            )
-            .into(),
-
-        ty::GenericParamDefKind::Lifetime => {
-            let br = ty::BoundRegion {
-                var: ty::BoundVar::from_usize(substs.len()),
-                kind: ty::BrAnon(None),
-            };
-            ty::Region::new_late_bound(tcx, ty::INNERMOST, br).into()
-        }
-
-        ty::GenericParamDefKind::Const { .. } => tcx
-            .mk_const(
-                ty::ConstKind::Bound(ty::INNERMOST, ty::BoundVar::from(param.index)),
-                tcx.type_of(param.def_id).subst_identity(),
-            )
-            .into(),
-    })
-}
-
-fn binders_for<'tcx>(
-    interner: RustInterner<'tcx>,
-    bound_vars: SubstsRef<'tcx>,
-) -> chalk_ir::VariableKinds<RustInterner<'tcx>> {
-    chalk_ir::VariableKinds::from_iter(
-        interner,
-        bound_vars.iter().map(|arg| match arg.unpack() {
-            ty::subst::GenericArgKind::Lifetime(_re) => chalk_ir::VariableKind::Lifetime,
-            ty::subst::GenericArgKind::Type(_ty) => {
-                chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General)
-            }
-            ty::subst::GenericArgKind::Const(c) => {
-                chalk_ir::VariableKind::Const(c.ty().lower_into(interner))
-            }
-        }),
-    )
-}
-
-struct ReplaceOpaqueTyFolder<'tcx> {
-    tcx: TyCtxt<'tcx>,
-    opaque_ty_id: chalk_ir::OpaqueTyId<RustInterner<'tcx>>,
-    identity_substs: SubstsRef<'tcx>,
-    binder_index: ty::DebruijnIndex,
-}
-
-impl<'tcx> ty::TypeFolder<TyCtxt<'tcx>> for ReplaceOpaqueTyFolder<'tcx> {
-    fn interner(&self) -> TyCtxt<'tcx> {
-        self.tcx
-    }
-
-    fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
-        &mut self,
-        t: ty::Binder<'tcx, T>,
-    ) -> ty::Binder<'tcx, T> {
-        self.binder_index.shift_in(1);
-        let t = t.super_fold_with(self);
-        self.binder_index.shift_out(1);
-        t
-    }
-
-    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
-        if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) = *ty.kind() {
-            if def_id == self.opaque_ty_id.0 && substs == self.identity_substs {
-                return self
-                    .tcx
-                    .mk_bound(self.binder_index, ty::BoundTy::from(ty::BoundVar::from_u32(0)));
-            }
-        }
-        ty
-    }
-}
diff --git a/compiler/rustc_traits/src/chalk/lowering.rs b/compiler/rustc_traits/src/chalk/lowering.rs
deleted file mode 100644
index b7db56d3db8..00000000000
--- a/compiler/rustc_traits/src/chalk/lowering.rs
+++ /dev/null
@@ -1,1234 +0,0 @@
-//! Contains the logic to lower rustc types into Chalk types
-//!
-//! In many cases there is a 1:1 relationship between a rustc type and a Chalk type.
-//! For example, a `SubstsRef` maps almost directly to a `Substitution`. In some
-//! other cases, such as `Param`s, there is no Chalk type, so we have to handle
-//! accordingly.
-//!
-//! ## `Ty` lowering
-//! Much of the `Ty` lowering is 1:1 with Chalk. (Or will be eventually). A
-//! helpful table for what types lower to what can be found in the
-//! [Chalk book](https://rust-lang.github.io/chalk/book/types/rust_types.html).
-//! The most notable difference lies with `Param`s. To convert from rustc to
-//! Chalk, we eagerly and deeply convert `Param`s to placeholders (in goals) or
-//! bound variables (for clause generation through functions in `db`).
-//!
-//! ## `Region` lowering
-//! Regions are handled in rustc and Chalk is quite differently. In rustc, there
-//! is a difference between "early bound" and "late bound" regions, where only
-//! the late bound regions have a `DebruijnIndex`. Moreover, in Chalk all
-//! regions (Lifetimes) have an associated index. In rustc, only `BrAnon`s have
-//! an index, whereas `BrNamed` don't. In order to lower regions to Chalk, we
-//! convert all regions into `BrAnon` late-bound regions.
-//!
-//! ## `Const` lowering
-//! Chalk doesn't handle consts currently, so consts are currently lowered to
-//! an empty tuple.
-//!
-//! ## Bound variable collection
-//! Another difference between rustc and Chalk lies in the handling of binders.
-//! Chalk requires that we store the bound parameter kinds, whereas rustc does
-//! not. To lower anything wrapped in a `Binder`, we first deeply find any bound
-//! variables from the current `Binder`.
-
-use rustc_ast::ast;
-use rustc_middle::traits::{ChalkEnvironmentAndGoal, ChalkRustInterner as RustInterner};
-use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
-use rustc_middle::ty::{
-    self, Binder, Region, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
-    TypeSuperVisitable, TypeVisitable, TypeVisitor,
-};
-use rustc_span::def_id::DefId;
-
-use chalk_ir::{FnSig, ForeignDefId};
-use rustc_hir::Unsafety;
-use std::collections::btree_map::{BTreeMap, Entry};
-use std::ops::ControlFlow;
-
-/// Essentially an `Into` with a `&RustInterner` parameter
-pub(crate) trait LowerInto<'tcx, T> {
-    /// Lower a rustc construct (e.g., `ty::TraitPredicate`) to a chalk type, consuming `self`.
-    fn lower_into(self, interner: RustInterner<'tcx>) -> T;
-}
-
-impl<'tcx> LowerInto<'tcx, chalk_ir::Substitution<RustInterner<'tcx>>> for SubstsRef<'tcx> {
-    fn lower_into(
-        self,
-        interner: RustInterner<'tcx>,
-    ) -> chalk_ir::Substitution<RustInterner<'tcx>> {
-        chalk_ir::Substitution::from_iter(interner, self.iter().map(|s| s.lower_into(interner)))
-    }
-}
-
-impl<'tcx> LowerInto<'tcx, chalk_ir::Substitution<RustInterner<'tcx>>>
-    for &'tcx ty::List<Ty<'tcx>>
-{
-    fn lower_into(
-        self,
-        interner: RustInterner<'tcx>,
-    ) -> chalk_ir::Substitution<RustInterner<'tcx>> {
-        chalk_ir::Substitution::from_iter(
-            interner,
-            self.iter().map(|ty| GenericArg::from(ty).lower_into(interner)),
-        )
-    }
-}
-
-impl<'tcx> LowerInto<'tcx, SubstsRef<'tcx>> for &chalk_ir::Substitution<RustInterner<'tcx>> {
-    fn lower_into(self, interner: RustInterner<'tcx>) -> SubstsRef<'tcx> {
-        interner
-            .tcx
-            .mk_substs_from_iter(self.iter(interner).map(|subst| subst.lower_into(interner)))
-    }
-}
-
-impl<'tcx> LowerInto<'tcx, chalk_ir::InEnvironment<chalk_ir::Goal<RustInterner<'tcx>>>>
-    for ChalkEnvironmentAndGoal<'tcx>
-{
-    fn lower_into(
-        self,
-        interner: RustInterner<'tcx>,
-    ) -> chalk_ir::InEnvironment<chalk_ir::Goal<RustInterner<'tcx>>> {
-        let clauses = self.environment.into_iter().map(|predicate| {
-            let (predicate, binders, _named_regions) =
-                collect_bound_vars(interner, interner.tcx, predicate.kind());
-            let consequence = match predicate {
-                ty::ClauseKind::TypeWellFormedFromEnv(ty) => {
-                    chalk_ir::DomainGoal::FromEnv(chalk_ir::FromEnv::Ty(ty.lower_into(interner)))
-                }
-                ty::ClauseKind::Trait(predicate) => chalk_ir::DomainGoal::FromEnv(
-                    chalk_ir::FromEnv::Trait(predicate.trait_ref.lower_into(interner)),
-                ),
-                ty::ClauseKind::RegionOutlives(predicate) => chalk_ir::DomainGoal::Holds(
-                    chalk_ir::WhereClause::LifetimeOutlives(chalk_ir::LifetimeOutlives {
-                        a: predicate.0.lower_into(interner),
-                        b: predicate.1.lower_into(interner),
-                    }),
-                ),
-                ty::ClauseKind::TypeOutlives(predicate) => chalk_ir::DomainGoal::Holds(
-                    chalk_ir::WhereClause::TypeOutlives(chalk_ir::TypeOutlives {
-                        ty: predicate.0.lower_into(interner),
-                        lifetime: predicate.1.lower_into(interner),
-                    }),
-                ),
-                ty::ClauseKind::Projection(predicate) => chalk_ir::DomainGoal::Holds(
-                    chalk_ir::WhereClause::AliasEq(predicate.lower_into(interner)),
-                ),
-                ty::ClauseKind::WellFormed(arg) => match arg.unpack() {
-                    ty::GenericArgKind::Type(ty) => chalk_ir::DomainGoal::WellFormed(
-                        chalk_ir::WellFormed::Ty(ty.lower_into(interner)),
-                    ),
-                    // FIXME(chalk): we need to change `WellFormed` in Chalk to take a `GenericArg`
-                    _ => chalk_ir::DomainGoal::WellFormed(chalk_ir::WellFormed::Ty(
-                        interner.tcx.types.unit.lower_into(interner),
-                    )),
-                },
-                ty::ClauseKind::ConstArgHasType(..) | ty::ClauseKind::ConstEvaluatable(_) => {
-                    bug!("unexpected predicate {}", predicate)
-                }
-            };
-            let value = chalk_ir::ProgramClauseImplication {
-                consequence,
-                conditions: chalk_ir::Goals::empty(interner),
-                priority: chalk_ir::ClausePriority::High,
-                constraints: chalk_ir::Constraints::empty(interner),
-            };
-            chalk_ir::ProgramClauseData(chalk_ir::Binders::new(binders, value)).intern(interner)
-        });
-
-        let goal: chalk_ir::GoalData<RustInterner<'tcx>> = self.goal.lower_into(interner);
-        chalk_ir::InEnvironment {
-            environment: chalk_ir::Environment {
-                clauses: chalk_ir::ProgramClauses::from_iter(interner, clauses),
-            },
-            goal: goal.intern(interner),
-        }
-    }
-}
-
-impl<'tcx> LowerInto<'tcx, chalk_ir::GoalData<RustInterner<'tcx>>> for ty::Predicate<'tcx> {
-    fn lower_into(self, interner: RustInterner<'tcx>) -> chalk_ir::GoalData<RustInterner<'tcx>> {
-        let (predicate, binders, _named_regions) =
-            collect_bound_vars(interner, interner.tcx, self.kind());
-
-        let value = match predicate {
-            ty::PredicateKind::Clause(ty::ClauseKind::Trait(predicate)) => {
-                chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(
-                    chalk_ir::WhereClause::Implemented(predicate.trait_ref.lower_into(interner)),
-                ))
-            }
-            ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(predicate)) => {
-                chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(
-                    chalk_ir::WhereClause::LifetimeOutlives(chalk_ir::LifetimeOutlives {
-                        a: predicate.0.lower_into(interner),
-                        b: predicate.1.lower_into(interner),
-                    }),
-                ))
-            }
-            ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(predicate)) => {
-                chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(
-                    chalk_ir::WhereClause::TypeOutlives(chalk_ir::TypeOutlives {
-                        ty: predicate.0.lower_into(interner),
-                        lifetime: predicate.1.lower_into(interner),
-                    }),
-                ))
-            }
-            ty::PredicateKind::Clause(ty::ClauseKind::Projection(predicate)) => {
-                chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(
-                    chalk_ir::WhereClause::AliasEq(predicate.lower_into(interner)),
-                ))
-            }
-            ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => match arg.unpack() {
-                GenericArgKind::Type(ty) => match ty.kind() {
-                    // FIXME(chalk): In Chalk, a placeholder is WellFormed if it
-                    // `FromEnv`. However, when we "lower" Params, we don't update
-                    // the environment.
-                    ty::Placeholder(..) => {
-                        chalk_ir::GoalData::All(chalk_ir::Goals::empty(interner))
-                    }
-
-                    _ => chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::WellFormed(
-                        chalk_ir::WellFormed::Ty(ty.lower_into(interner)),
-                    )),
-                },
-                // FIXME(chalk): handle well formed consts
-                GenericArgKind::Const(..) => {
-                    chalk_ir::GoalData::All(chalk_ir::Goals::empty(interner))
-                }
-                GenericArgKind::Lifetime(lt) => bug!("unexpected well formed predicate: {:?}", lt),
-            },
-
-            ty::PredicateKind::ObjectSafe(t) => chalk_ir::GoalData::DomainGoal(
-                chalk_ir::DomainGoal::ObjectSafe(chalk_ir::TraitId(t)),
-            ),
-
-            ty::PredicateKind::Subtype(ty::SubtypePredicate { a, b, a_is_expected: _ }) => {
-                chalk_ir::GoalData::SubtypeGoal(chalk_ir::SubtypeGoal {
-                    a: a.lower_into(interner),
-                    b: b.lower_into(interner),
-                })
-            }
-
-            // FIXME(chalk): other predicates
-            //
-            // We can defer this, but ultimately we'll want to express
-            // some of these in terms of chalk operations.
-            ty::PredicateKind::ClosureKind(..)
-            | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..))
-            | ty::PredicateKind::AliasRelate(..)
-            | ty::PredicateKind::Coerce(..)
-            | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
-            | ty::PredicateKind::Ambiguous
-            | ty::PredicateKind::ConstEquate(..) => {
-                chalk_ir::GoalData::All(chalk_ir::Goals::empty(interner))
-            }
-            ty::PredicateKind::Clause(ty::ClauseKind::TypeWellFormedFromEnv(ty)) => {
-                chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::FromEnv(
-                    chalk_ir::FromEnv::Ty(ty.lower_into(interner)),
-                ))
-            }
-        };
-
-        chalk_ir::GoalData::Quantified(
-            chalk_ir::QuantifierKind::ForAll,
-            chalk_ir::Binders::new(binders, value.intern(interner)),
-        )
-    }
-}
-
-impl<'tcx> LowerInto<'tcx, chalk_ir::TraitRef<RustInterner<'tcx>>>
-    for rustc_middle::ty::TraitRef<'tcx>
-{
-    fn lower_into(self, interner: RustInterner<'tcx>) -> chalk_ir::TraitRef<RustInterner<'tcx>> {
-        chalk_ir::TraitRef {
-            trait_id: chalk_ir::TraitId(self.def_id),
-            substitution: self.substs.lower_into(interner),
-        }
-    }
-}
-
-impl<'tcx> LowerInto<'tcx, chalk_ir::AliasEq<RustInterner<'tcx>>>
-    for rustc_middle::ty::ProjectionPredicate<'tcx>
-{
-    fn lower_into(self, interner: RustInterner<'tcx>) -> chalk_ir::AliasEq<RustInterner<'tcx>> {
-        // FIXME(associated_const_equality): teach chalk about terms for alias eq.
-        chalk_ir::AliasEq {
-            ty: self.term.ty().unwrap().lower_into(interner),
-            alias: chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy {
-                associated_ty_id: chalk_ir::AssocTypeId(self.projection_ty.def_id),
-                substitution: self.projection_ty.substs.lower_into(interner),
-            }),
-        }
-    }
-}
-
-/*
-// FIXME(...): Where do I add this to Chalk? I can't find it in the rustc repo anywhere.
-impl<'tcx> LowerInto<'tcx, chalk_ir::Term<RustInterner<'tcx>>> for rustc_middle::ty::Term<'tcx> {
-  fn lower_into(self, interner: RustInterner<'tcx>) -> chalk_ir::Term<RustInterner<'tcx>> {
-    match self {
-      ty::Term::Ty(ty) => ty.lower_into(interner).into(),
-      ty::Term::Const(c) => c.lower_into(interner).into(),
-    }
-  }
-}
-*/
-
-impl<'tcx> LowerInto<'tcx, chalk_ir::Ty<RustInterner<'tcx>>> for Ty<'tcx> {
-    fn lower_into(self, interner: RustInterner<'tcx>) -> chalk_ir::Ty<RustInterner<'tcx>> {
-        let int = |i| chalk_ir::TyKind::Scalar(chalk_ir::Scalar::Int(i));
-        let uint = |i| chalk_ir::TyKind::Scalar(chalk_ir::Scalar::Uint(i));
-        let float = |f| chalk_ir::TyKind::Scalar(chalk_ir::Scalar::Float(f));
-
-        match *self.kind() {
-            ty::Bool => chalk_ir::TyKind::Scalar(chalk_ir::Scalar::Bool),
-            ty::Char => chalk_ir::TyKind::Scalar(chalk_ir::Scalar::Char),
-            ty::Int(ty) => match ty {
-                ty::IntTy::Isize => int(chalk_ir::IntTy::Isize),
-                ty::IntTy::I8 => int(chalk_ir::IntTy::I8),
-                ty::IntTy::I16 => int(chalk_ir::IntTy::I16),
-                ty::IntTy::I32 => int(chalk_ir::IntTy::I32),
-                ty::IntTy::I64 => int(chalk_ir::IntTy::I64),
-                ty::IntTy::I128 => int(chalk_ir::IntTy::I128),
-            },
-            ty::Uint(ty) => match ty {
-                ty::UintTy::Usize => uint(chalk_ir::UintTy::Usize),
-                ty::UintTy::U8 => uint(chalk_ir::UintTy::U8),
-                ty::UintTy::U16 => uint(chalk_ir::UintTy::U16),
-                ty::UintTy::U32 => uint(chalk_ir::UintTy::U32),
-                ty::UintTy::U64 => uint(chalk_ir::UintTy::U64),
-                ty::UintTy::U128 => uint(chalk_ir::UintTy::U128),
-            },
-            ty::Float(ty) => match ty {
-                ty::FloatTy::F32 => float(chalk_ir::FloatTy::F32),
-                ty::FloatTy::F64 => float(chalk_ir::FloatTy::F64),
-            },
-            ty::Adt(def, substs) => {
-                chalk_ir::TyKind::Adt(chalk_ir::AdtId(def), substs.lower_into(interner))
-            }
-            ty::Foreign(def_id) => chalk_ir::TyKind::Foreign(ForeignDefId(def_id)),
-            ty::Str => chalk_ir::TyKind::Str,
-            ty::Array(ty, len) => {
-                chalk_ir::TyKind::Array(ty.lower_into(interner), len.lower_into(interner))
-            }
-            ty::Slice(ty) => chalk_ir::TyKind::Slice(ty.lower_into(interner)),
-
-            ty::RawPtr(ptr) => {
-                chalk_ir::TyKind::Raw(ptr.mutbl.lower_into(interner), ptr.ty.lower_into(interner))
-            }
-            ty::Ref(region, ty, mutability) => chalk_ir::TyKind::Ref(
-                mutability.lower_into(interner),
-                region.lower_into(interner),
-                ty.lower_into(interner),
-            ),
-            ty::FnDef(def_id, substs) => {
-                chalk_ir::TyKind::FnDef(chalk_ir::FnDefId(def_id), substs.lower_into(interner))
-            }
-            ty::FnPtr(sig) => {
-                let (inputs_and_outputs, binders, _named_regions) =
-                    collect_bound_vars(interner, interner.tcx, sig.inputs_and_output());
-                chalk_ir::TyKind::Function(chalk_ir::FnPointer {
-                    num_binders: binders.len(interner),
-                    sig: sig.lower_into(interner),
-                    substitution: chalk_ir::FnSubst(chalk_ir::Substitution::from_iter(
-                        interner,
-                        inputs_and_outputs.iter().map(|ty| {
-                            chalk_ir::GenericArgData::Ty(ty.lower_into(interner)).intern(interner)
-                        }),
-                    )),
-                })
-            }
-            // FIXME(dyn-star): handle the dynamic kind (dyn or dyn*)
-            ty::Dynamic(predicates, region, _kind) => chalk_ir::TyKind::Dyn(chalk_ir::DynTy {
-                bounds: predicates.lower_into(interner),
-                lifetime: region.lower_into(interner),
-            }),
-            ty::Closure(def_id, substs) => {
-                chalk_ir::TyKind::Closure(chalk_ir::ClosureId(def_id), substs.lower_into(interner))
-            }
-            ty::Generator(def_id, substs, _) => chalk_ir::TyKind::Generator(
-                chalk_ir::GeneratorId(def_id),
-                substs.lower_into(interner),
-            ),
-            ty::GeneratorWitness(_) => unimplemented!(),
-            ty::GeneratorWitnessMIR(..) => unimplemented!(),
-            ty::Never => chalk_ir::TyKind::Never,
-            ty::Tuple(types) => chalk_ir::TyKind::Tuple(types.len(), types.lower_into(interner)),
-            ty::Alias(ty::Projection, ty::AliasTy { def_id, substs, .. }) => {
-                chalk_ir::TyKind::Alias(chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy {
-                    associated_ty_id: chalk_ir::AssocTypeId(def_id),
-                    substitution: substs.lower_into(interner),
-                }))
-            }
-            ty::Alias(ty::Weak, ty::AliasTy { .. }) => unimplemented!(),
-            ty::Alias(ty::Inherent, _) => unimplemented!(),
-            ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
-                chalk_ir::TyKind::Alias(chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy {
-                    opaque_ty_id: chalk_ir::OpaqueTyId(def_id),
-                    substitution: substs.lower_into(interner),
-                }))
-            }
-            // This should have been done eagerly prior to this, and all Params
-            // should have been substituted to placeholders
-            ty::Param(_) => panic!("Lowering Param when not expected."),
-            ty::Bound(db, bound) => chalk_ir::TyKind::BoundVar(chalk_ir::BoundVar::new(
-                chalk_ir::DebruijnIndex::new(db.as_u32()),
-                bound.var.index(),
-            )),
-            ty::Placeholder(_placeholder) => {
-                chalk_ir::TyKind::Placeholder(chalk_ir::PlaceholderIndex {
-                    ui: chalk_ir::UniverseIndex { counter: _placeholder.universe.as_usize() },
-                    idx: _placeholder.bound.var.as_usize(),
-                })
-            }
-            ty::Infer(_infer) => unimplemented!(),
-            ty::Error(_) => chalk_ir::TyKind::Error,
-        }
-        .intern(interner)
-    }
-}
-
-impl<'tcx> LowerInto<'tcx, Ty<'tcx>> for &chalk_ir::Ty<RustInterner<'tcx>> {
-    fn lower_into(self, interner: RustInterner<'tcx>) -> Ty<'tcx> {
-        use chalk_ir::TyKind;
-
-        let kind = match self.kind(interner) {
-            TyKind::Adt(struct_id, substitution) => {
-                ty::Adt(struct_id.0, substitution.lower_into(interner))
-            }
-            TyKind::Scalar(scalar) => match scalar {
-                chalk_ir::Scalar::Bool => ty::Bool,
-                chalk_ir::Scalar::Char => ty::Char,
-                chalk_ir::Scalar::Int(int_ty) => match int_ty {
-                    chalk_ir::IntTy::Isize => ty::Int(ty::IntTy::Isize),
-                    chalk_ir::IntTy::I8 => ty::Int(ty::IntTy::I8),
-                    chalk_ir::IntTy::I16 => ty::Int(ty::IntTy::I16),
-                    chalk_ir::IntTy::I32 => ty::Int(ty::IntTy::I32),
-                    chalk_ir::IntTy::I64 => ty::Int(ty::IntTy::I64),
-                    chalk_ir::IntTy::I128 => ty::Int(ty::IntTy::I128),
-                },
-                chalk_ir::Scalar::Uint(int_ty) => match int_ty {
-                    chalk_ir::UintTy::Usize => ty::Uint(ty::UintTy::Usize),
-                    chalk_ir::UintTy::U8 => ty::Uint(ty::UintTy::U8),
-                    chalk_ir::UintTy::U16 => ty::Uint(ty::UintTy::U16),
-                    chalk_ir::UintTy::U32 => ty::Uint(ty::UintTy::U32),
-                    chalk_ir::UintTy::U64 => ty::Uint(ty::UintTy::U64),
-                    chalk_ir::UintTy::U128 => ty::Uint(ty::UintTy::U128),
-                },
-                chalk_ir::Scalar::Float(float_ty) => match float_ty {
-                    chalk_ir::FloatTy::F32 => ty::Float(ty::FloatTy::F32),
-                    chalk_ir::FloatTy::F64 => ty::Float(ty::FloatTy::F64),
-                },
-            },
-            TyKind::Array(ty, c) => {
-                let ty = ty.lower_into(interner);
-                let c = c.lower_into(interner);
-                ty::Array(ty, c)
-            }
-            TyKind::FnDef(id, substitution) => ty::FnDef(id.0, substitution.lower_into(interner)),
-            TyKind::Closure(closure, substitution) => {
-                ty::Closure(closure.0, substitution.lower_into(interner))
-            }
-            TyKind::Generator(generator, substitution) => ty::Generator(
-                generator.0,
-                substitution.lower_into(interner),
-                ast::Movability::Static,
-            ),
-            TyKind::GeneratorWitness(..) => unimplemented!(),
-            TyKind::Never => ty::Never,
-            TyKind::Tuple(_len, substitution) => {
-                ty::Tuple(substitution.lower_into(interner).into_type_list(interner.tcx))
-            }
-            TyKind::Slice(ty) => ty::Slice(ty.lower_into(interner)),
-            TyKind::Raw(mutbl, ty) => ty::RawPtr(ty::TypeAndMut {
-                ty: ty.lower_into(interner),
-                mutbl: mutbl.lower_into(interner),
-            }),
-            TyKind::Ref(mutbl, lifetime, ty) => ty::Ref(
-                lifetime.lower_into(interner),
-                ty.lower_into(interner),
-                mutbl.lower_into(interner),
-            ),
-            TyKind::Str => ty::Str,
-            TyKind::OpaqueType(opaque_ty, substitution) => ty::Alias(
-                ty::Opaque,
-                interner.tcx.mk_alias_ty(opaque_ty.0, substitution.lower_into(interner)),
-            ),
-            TyKind::AssociatedType(assoc_ty, substitution) => ty::Alias(
-                ty::Projection,
-                interner.tcx.mk_alias_ty(assoc_ty.0, substitution.lower_into(interner)),
-            ),
-            TyKind::Foreign(def_id) => ty::Foreign(def_id.0),
-            TyKind::Error => return interner.tcx.ty_error_misc(),
-            TyKind::Alias(alias_ty) => match alias_ty {
-                chalk_ir::AliasTy::Projection(projection) => ty::Alias(
-                    ty::Projection,
-                    interner.tcx.mk_alias_ty(
-                        projection.associated_ty_id.0,
-                        projection.substitution.lower_into(interner),
-                    ),
-                ),
-                chalk_ir::AliasTy::Opaque(opaque) => ty::Alias(
-                    ty::Opaque,
-                    interner.tcx.mk_alias_ty(
-                        opaque.opaque_ty_id.0,
-                        opaque.substitution.lower_into(interner),
-                    ),
-                ),
-            },
-            TyKind::Function(_quantified_ty) => unimplemented!(),
-            TyKind::BoundVar(bound) => ty::Bound(
-                ty::DebruijnIndex::from_usize(bound.debruijn.depth() as usize),
-                ty::BoundTy {
-                    var: ty::BoundVar::from_usize(bound.index),
-                    kind: ty::BoundTyKind::Anon,
-                },
-            ),
-            TyKind::Placeholder(placeholder) => ty::Placeholder(ty::Placeholder {
-                universe: ty::UniverseIndex::from_usize(placeholder.ui.counter),
-                bound: ty::BoundTy {
-                    var: ty::BoundVar::from_usize(placeholder.idx),
-                    kind: ty::BoundTyKind::Anon,
-                },
-            }),
-            TyKind::InferenceVar(_, _) => unimplemented!(),
-            TyKind::Dyn(_) => unimplemented!(),
-        };
-        interner.tcx.mk_ty_from_kind(kind)
-    }
-}
-
-impl<'tcx> LowerInto<'tcx, chalk_ir::Lifetime<RustInterner<'tcx>>> for Region<'tcx> {
-    fn lower_into(self, interner: RustInterner<'tcx>) -> chalk_ir::Lifetime<RustInterner<'tcx>> {
-        match *self {
-            ty::ReEarlyBound(_) => {
-                panic!("Should have already been substituted.");
-            }
-            ty::ReError(_) => {
-                panic!("Error lifetime should not have already been lowered.");
-            }
-            ty::ReLateBound(db, br) => chalk_ir::LifetimeData::BoundVar(chalk_ir::BoundVar::new(
-                chalk_ir::DebruijnIndex::new(db.as_u32()),
-                br.var.as_usize(),
-            ))
-            .intern(interner),
-            ty::ReFree(_) => unimplemented!(),
-            ty::ReStatic => chalk_ir::LifetimeData::Static.intern(interner),
-            ty::ReVar(_) => unimplemented!(),
-            ty::RePlaceholder(placeholder_region) => {
-                chalk_ir::LifetimeData::Placeholder(chalk_ir::PlaceholderIndex {
-                    ui: chalk_ir::UniverseIndex { counter: placeholder_region.universe.index() },
-                    idx: 0, // FIXME: This `idx: 0` is sus.
-                })
-                .intern(interner)
-            }
-            ty::ReErased => chalk_ir::LifetimeData::Erased.intern(interner),
-        }
-    }
-}
-
-impl<'tcx> LowerInto<'tcx, Region<'tcx>> for &chalk_ir::Lifetime<RustInterner<'tcx>> {
-    fn lower_into(self, interner: RustInterner<'tcx>) -> Region<'tcx> {
-        let tcx = interner.tcx;
-        match self.data(interner) {
-            chalk_ir::LifetimeData::BoundVar(var) => ty::Region::new_late_bound(
-                tcx,
-                ty::DebruijnIndex::from_u32(var.debruijn.depth()),
-                ty::BoundRegion {
-                    var: ty::BoundVar::from_usize(var.index),
-                    kind: ty::BrAnon(None),
-                },
-            ),
-            chalk_ir::LifetimeData::InferenceVar(_var) => unimplemented!(),
-            chalk_ir::LifetimeData::Placeholder(p) => ty::Region::new_placeholder(
-                tcx,
-                ty::Placeholder {
-                    universe: ty::UniverseIndex::from_usize(p.ui.counter),
-                    bound: ty::BoundRegion {
-                        var: ty::BoundVar::from_usize(p.idx),
-                        kind: ty::BoundRegionKind::BrAnon(None),
-                    },
-                },
-            ),
-            chalk_ir::LifetimeData::Static => tcx.lifetimes.re_static,
-            chalk_ir::LifetimeData::Erased => tcx.lifetimes.re_erased,
-            chalk_ir::LifetimeData::Phantom(void, _) => match *void {},
-        }
-    }
-}
-
-impl<'tcx> LowerInto<'tcx, chalk_ir::Const<RustInterner<'tcx>>> for ty::Const<'tcx> {
-    fn lower_into(self, interner: RustInterner<'tcx>) -> chalk_ir::Const<RustInterner<'tcx>> {
-        let ty = self.ty().lower_into(interner);
-        let value = match self.kind() {
-            ty::ConstKind::Value(val) => {
-                chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { interned: val })
-            }
-            ty::ConstKind::Bound(db, bound) => chalk_ir::ConstValue::BoundVar(
-                chalk_ir::BoundVar::new(chalk_ir::DebruijnIndex::new(db.as_u32()), bound.index()),
-            ),
-            _ => unimplemented!("Const not implemented. {:?}", self),
-        };
-        chalk_ir::ConstData { ty, value }.intern(interner)
-    }
-}
-
-impl<'tcx> LowerInto<'tcx, ty::Const<'tcx>> for &chalk_ir::Const<RustInterner<'tcx>> {
-    fn lower_into(self, interner: RustInterner<'tcx>) -> ty::Const<'tcx> {
-        let data = self.data(interner);
-        let ty = data.ty.lower_into(interner);
-        let kind = match data.value {
-            chalk_ir::ConstValue::BoundVar(var) => ty::ConstKind::Bound(
-                ty::DebruijnIndex::from_u32(var.debruijn.depth()),
-                ty::BoundVar::from_u32(var.index as u32),
-            ),
-            chalk_ir::ConstValue::InferenceVar(_var) => unimplemented!(),
-            chalk_ir::ConstValue::Placeholder(_p) => unimplemented!(),
-            chalk_ir::ConstValue::Concrete(c) => ty::ConstKind::Value(c.interned),
-        };
-        interner.tcx.mk_const(kind, ty)
-    }
-}
-
-impl<'tcx> LowerInto<'tcx, chalk_ir::GenericArg<RustInterner<'tcx>>> for GenericArg<'tcx> {
-    fn lower_into(self, interner: RustInterner<'tcx>) -> chalk_ir::GenericArg<RustInterner<'tcx>> {
-        match self.unpack() {
-            ty::subst::GenericArgKind::Type(ty) => {
-                chalk_ir::GenericArgData::Ty(ty.lower_into(interner))
-            }
-            ty::subst::GenericArgKind::Lifetime(lifetime) => {
-                chalk_ir::GenericArgData::Lifetime(lifetime.lower_into(interner))
-            }
-            ty::subst::GenericArgKind::Const(c) => {
-                chalk_ir::GenericArgData::Const(c.lower_into(interner))
-            }
-        }
-        .intern(interner)
-    }
-}
-
-impl<'tcx> LowerInto<'tcx, ty::subst::GenericArg<'tcx>>
-    for &chalk_ir::GenericArg<RustInterner<'tcx>>
-{
-    fn lower_into(self, interner: RustInterner<'tcx>) -> ty::subst::GenericArg<'tcx> {
-        match self.data(interner) {
-            chalk_ir::GenericArgData::Ty(ty) => {
-                let t: Ty<'tcx> = ty.lower_into(interner);
-                t.into()
-            }
-            chalk_ir::GenericArgData::Lifetime(lifetime) => {
-                let r: Region<'tcx> = lifetime.lower_into(interner);
-                r.into()
-            }
-            chalk_ir::GenericArgData::Const(c) => {
-                let c: ty::Const<'tcx> = c.lower_into(interner);
-                c.into()
-            }
-        }
-    }
-}
-
-// We lower into an Option here since there are some predicates which Chalk
-// doesn't have a representation for yet (as a `WhereClause`), but are so common
-// that we just are accepting the unsoundness for now. The `Option` will
-// eventually be removed.
-impl<'tcx> LowerInto<'tcx, Option<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>>>
-    for ty::Predicate<'tcx>
-{
-    fn lower_into(
-        self,
-        interner: RustInterner<'tcx>,
-    ) -> Option<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>> {
-        let (predicate, binders, _named_regions) =
-            collect_bound_vars(interner, interner.tcx, self.kind());
-        let value = match predicate {
-            ty::PredicateKind::Clause(ty::ClauseKind::Trait(predicate)) => {
-                Some(chalk_ir::WhereClause::Implemented(predicate.trait_ref.lower_into(interner)))
-            }
-            ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(predicate)) => {
-                Some(chalk_ir::WhereClause::LifetimeOutlives(chalk_ir::LifetimeOutlives {
-                    a: predicate.0.lower_into(interner),
-                    b: predicate.1.lower_into(interner),
-                }))
-            }
-            ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(predicate)) => {
-                Some(chalk_ir::WhereClause::TypeOutlives(chalk_ir::TypeOutlives {
-                    ty: predicate.0.lower_into(interner),
-                    lifetime: predicate.1.lower_into(interner),
-                }))
-            }
-            ty::PredicateKind::Clause(ty::ClauseKind::Projection(predicate)) => {
-                Some(chalk_ir::WhereClause::AliasEq(predicate.lower_into(interner)))
-            }
-            ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_ty)) => None,
-            ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..)) => None,
-
-            ty::PredicateKind::ObjectSafe(..)
-            | ty::PredicateKind::AliasRelate(..)
-            | ty::PredicateKind::ClosureKind(..)
-            | ty::PredicateKind::Subtype(..)
-            | ty::PredicateKind::Coerce(..)
-            | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
-            | ty::PredicateKind::ConstEquate(..)
-            | ty::PredicateKind::Ambiguous
-            | ty::PredicateKind::Clause(ty::ClauseKind::TypeWellFormedFromEnv(..)) => {
-                bug!("unexpected predicate {self}")
-            }
-        };
-        value.map(|value| chalk_ir::Binders::new(binders, value))
-    }
-}
-
-impl<'tcx> LowerInto<'tcx, chalk_ir::Binders<chalk_ir::QuantifiedWhereClauses<RustInterner<'tcx>>>>
-    for &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>
-{
-    fn lower_into(
-        self,
-        interner: RustInterner<'tcx>,
-    ) -> chalk_ir::Binders<chalk_ir::QuantifiedWhereClauses<RustInterner<'tcx>>> {
-        // `Self` has one binder:
-        // Binder<&'tcx ty::List<ty::ExistentialPredicate<'tcx>>>
-        // The return type has two:
-        // Binders<&[Binders<WhereClause<I>>]>
-        // This means that any variables that are escaping `self` need to be
-        // shifted in by one so that they are still escaping.
-        let predicates = ty::fold::shift_vars(interner.tcx, self, 1);
-
-        let self_ty = interner.tcx.mk_bound(
-            // This is going to be wrapped in a binder
-            ty::DebruijnIndex::from_usize(1),
-            ty::BoundTy { var: ty::BoundVar::from_usize(0), kind: ty::BoundTyKind::Anon },
-        );
-        let where_clauses = predicates.into_iter().map(|predicate| {
-            let (predicate, binders, _named_regions) =
-                collect_bound_vars(interner, interner.tcx, predicate);
-            match predicate {
-                ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef { def_id, substs }) => {
-                    chalk_ir::Binders::new(
-                        binders.clone(),
-                        chalk_ir::WhereClause::Implemented(chalk_ir::TraitRef {
-                            trait_id: chalk_ir::TraitId(def_id),
-                            substitution: interner
-                                .tcx
-                                .mk_substs_trait(self_ty, substs)
-                                .lower_into(interner),
-                        }),
-                    )
-                }
-                ty::ExistentialPredicate::Projection(predicate) => chalk_ir::Binders::new(
-                    binders.clone(),
-                    chalk_ir::WhereClause::AliasEq(chalk_ir::AliasEq {
-                        alias: chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy {
-                            associated_ty_id: chalk_ir::AssocTypeId(predicate.def_id),
-                            substitution: interner
-                                .tcx
-                                .mk_substs_trait(self_ty, predicate.substs)
-                                .lower_into(interner),
-                        }),
-                        // FIXME(associated_const_equality): teach chalk about terms for alias eq.
-                        ty: predicate.term.ty().unwrap().lower_into(interner),
-                    }),
-                ),
-                ty::ExistentialPredicate::AutoTrait(def_id) => chalk_ir::Binders::new(
-                    binders.clone(),
-                    chalk_ir::WhereClause::Implemented(chalk_ir::TraitRef {
-                        trait_id: chalk_ir::TraitId(def_id),
-                        substitution: interner
-                            .tcx
-                            .mk_substs_trait(self_ty, [])
-                            .lower_into(interner),
-                    }),
-                ),
-            }
-        });
-
-        // Binder for the bound variable representing the concrete underlying type.
-        let existential_binder = chalk_ir::VariableKinds::from1(
-            interner,
-            chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General),
-        );
-        let value = chalk_ir::QuantifiedWhereClauses::from_iter(interner, where_clauses);
-        chalk_ir::Binders::new(existential_binder, value)
-    }
-}
-
-impl<'tcx> LowerInto<'tcx, chalk_ir::FnSig<RustInterner<'tcx>>>
-    for ty::Binder<'tcx, ty::FnSig<'tcx>>
-{
-    fn lower_into(self, _interner: RustInterner<'_>) -> FnSig<RustInterner<'tcx>> {
-        chalk_ir::FnSig {
-            abi: self.abi(),
-            safety: match self.unsafety() {
-                Unsafety::Normal => chalk_ir::Safety::Safe,
-                Unsafety::Unsafe => chalk_ir::Safety::Unsafe,
-            },
-            variadic: self.c_variadic(),
-        }
-    }
-}
-
-// We lower into an Option here since there are some predicates which Chalk
-// doesn't have a representation for yet (as an `InlineBound`). The `Option` will
-// eventually be removed.
-impl<'tcx> LowerInto<'tcx, Option<chalk_solve::rust_ir::QuantifiedInlineBound<RustInterner<'tcx>>>>
-    for ty::Predicate<'tcx>
-{
-    fn lower_into(
-        self,
-        interner: RustInterner<'tcx>,
-    ) -> Option<chalk_solve::rust_ir::QuantifiedInlineBound<RustInterner<'tcx>>> {
-        let (predicate, binders, _named_regions) =
-            collect_bound_vars(interner, interner.tcx, self.kind());
-        match predicate {
-            ty::PredicateKind::Clause(ty::ClauseKind::Trait(predicate)) => {
-                Some(chalk_ir::Binders::new(
-                    binders,
-                    chalk_solve::rust_ir::InlineBound::TraitBound(
-                        predicate.trait_ref.lower_into(interner),
-                    ),
-                ))
-            }
-            ty::PredicateKind::Clause(ty::ClauseKind::Projection(predicate)) => {
-                Some(chalk_ir::Binders::new(
-                    binders,
-                    chalk_solve::rust_ir::InlineBound::AliasEqBound(predicate.lower_into(interner)),
-                ))
-            }
-            ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(_predicate)) => None,
-            ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_ty)) => None,
-            ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..)) => None,
-
-            ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(..))
-            | ty::PredicateKind::AliasRelate(..)
-            | ty::PredicateKind::ObjectSafe(..)
-            | ty::PredicateKind::ClosureKind(..)
-            | ty::PredicateKind::Subtype(..)
-            | ty::PredicateKind::Coerce(..)
-            | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
-            | ty::PredicateKind::ConstEquate(..)
-            | ty::PredicateKind::Ambiguous
-            | ty::PredicateKind::Clause(ty::ClauseKind::TypeWellFormedFromEnv(..)) => {
-                bug!("unexpected predicate {}", &self)
-            }
-        }
-    }
-}
-
-impl<'tcx> LowerInto<'tcx, chalk_solve::rust_ir::TraitBound<RustInterner<'tcx>>>
-    for ty::TraitRef<'tcx>
-{
-    fn lower_into(
-        self,
-        interner: RustInterner<'tcx>,
-    ) -> chalk_solve::rust_ir::TraitBound<RustInterner<'tcx>> {
-        chalk_solve::rust_ir::TraitBound {
-            trait_id: chalk_ir::TraitId(self.def_id),
-            args_no_self: self.substs[1..].iter().map(|arg| arg.lower_into(interner)).collect(),
-        }
-    }
-}
-
-impl<'tcx> LowerInto<'tcx, chalk_ir::Mutability> for ast::Mutability {
-    fn lower_into(self, _interner: RustInterner<'tcx>) -> chalk_ir::Mutability {
-        match self {
-            rustc_ast::Mutability::Mut => chalk_ir::Mutability::Mut,
-            rustc_ast::Mutability::Not => chalk_ir::Mutability::Not,
-        }
-    }
-}
-
-impl<'tcx> LowerInto<'tcx, ast::Mutability> for chalk_ir::Mutability {
-    fn lower_into(self, _interner: RustInterner<'tcx>) -> ast::Mutability {
-        match self {
-            chalk_ir::Mutability::Mut => ast::Mutability::Mut,
-            chalk_ir::Mutability::Not => ast::Mutability::Not,
-        }
-    }
-}
-
-impl<'tcx> LowerInto<'tcx, chalk_solve::rust_ir::Polarity> for ty::ImplPolarity {
-    fn lower_into(self, _interner: RustInterner<'tcx>) -> chalk_solve::rust_ir::Polarity {
-        match self {
-            ty::ImplPolarity::Positive => chalk_solve::rust_ir::Polarity::Positive,
-            ty::ImplPolarity::Negative => chalk_solve::rust_ir::Polarity::Negative,
-            // FIXME(chalk) reservation impls
-            ty::ImplPolarity::Reservation => chalk_solve::rust_ir::Polarity::Negative,
-        }
-    }
-}
-impl<'tcx> LowerInto<'tcx, chalk_ir::Variance> for ty::Variance {
-    fn lower_into(self, _interner: RustInterner<'tcx>) -> chalk_ir::Variance {
-        match self {
-            ty::Variance::Covariant => chalk_ir::Variance::Covariant,
-            ty::Variance::Invariant => chalk_ir::Variance::Invariant,
-            ty::Variance::Contravariant => chalk_ir::Variance::Contravariant,
-            ty::Variance::Bivariant => unimplemented!(),
-        }
-    }
-}
-
-impl<'tcx> LowerInto<'tcx, chalk_solve::rust_ir::AliasEqBound<RustInterner<'tcx>>>
-    for ty::ProjectionPredicate<'tcx>
-{
-    fn lower_into(
-        self,
-        interner: RustInterner<'tcx>,
-    ) -> chalk_solve::rust_ir::AliasEqBound<RustInterner<'tcx>> {
-        let (trait_ref, own_substs) = self.projection_ty.trait_ref_and_own_substs(interner.tcx);
-        chalk_solve::rust_ir::AliasEqBound {
-            trait_bound: trait_ref.lower_into(interner),
-            associated_ty_id: chalk_ir::AssocTypeId(self.projection_ty.def_id),
-            parameters: own_substs.iter().map(|arg| arg.lower_into(interner)).collect(),
-            value: self.term.ty().unwrap().lower_into(interner),
-        }
-    }
-}
-
-/// To collect bound vars, we have to do two passes. In the first pass, we
-/// collect all `BoundRegionKind`s and `ty::Bound`s. In the second pass, we then
-/// replace `BrNamed` into `BrAnon`. The two separate passes are important,
-/// since we can only replace `BrNamed` with `BrAnon`s with indices *after* all
-/// "real" `BrAnon`s.
-///
-/// It's important to note that because of prior substitution, we may have
-/// late-bound regions, even outside of fn contexts, since this is the best way
-/// to prep types for chalk lowering.
-pub(crate) fn collect_bound_vars<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
-    interner: RustInterner<'tcx>,
-    tcx: TyCtxt<'tcx>,
-    ty: Binder<'tcx, T>,
-) -> (T, chalk_ir::VariableKinds<RustInterner<'tcx>>, BTreeMap<DefId, u32>) {
-    let mut bound_vars_collector = BoundVarsCollector::new();
-    ty.as_ref().skip_binder().visit_with(&mut bound_vars_collector);
-    let mut parameters = bound_vars_collector.parameters;
-    let named_parameters: BTreeMap<DefId, u32> = bound_vars_collector
-        .named_parameters
-        .into_iter()
-        .enumerate()
-        .map(|(i, def_id)| (def_id, (i + parameters.len()) as u32))
-        .collect();
-
-    let mut bound_var_substitutor = NamedBoundVarSubstitutor::new(tcx, &named_parameters);
-    let new_ty = ty.skip_binder().fold_with(&mut bound_var_substitutor);
-
-    for var in named_parameters.values() {
-        parameters.insert(*var, chalk_ir::VariableKind::Lifetime);
-    }
-
-    (0..parameters.len()).for_each(|i| {
-        parameters
-            .get(&(i as u32))
-            .or_else(|| bug!("Skipped bound var index: parameters={:?}", parameters));
-    });
-
-    let binders = chalk_ir::VariableKinds::from_iter(interner, parameters.into_values());
-
-    (new_ty, binders, named_parameters)
-}
-
-pub(crate) struct BoundVarsCollector<'tcx> {
-    binder_index: ty::DebruijnIndex,
-    pub(crate) parameters: BTreeMap<u32, chalk_ir::VariableKind<RustInterner<'tcx>>>,
-    pub(crate) named_parameters: Vec<DefId>,
-}
-
-impl<'tcx> BoundVarsCollector<'tcx> {
-    pub(crate) fn new() -> Self {
-        BoundVarsCollector {
-            binder_index: ty::INNERMOST,
-            parameters: BTreeMap::new(),
-            named_parameters: vec![],
-        }
-    }
-}
-
-impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for BoundVarsCollector<'tcx> {
-    fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
-        &mut self,
-        t: &Binder<'tcx, T>,
-    ) -> ControlFlow<Self::BreakTy> {
-        self.binder_index.shift_in(1);
-        let result = t.super_visit_with(self);
-        self.binder_index.shift_out(1);
-        result
-    }
-
-    fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
-        match *t.kind() {
-            ty::Bound(debruijn, bound_ty) if debruijn == self.binder_index => {
-                match self.parameters.entry(bound_ty.var.as_u32()) {
-                    Entry::Vacant(entry) => {
-                        entry.insert(chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General));
-                    }
-                    Entry::Occupied(entry) => match entry.get() {
-                        chalk_ir::VariableKind::Ty(_) => {}
-                        _ => panic!(),
-                    },
-                }
-            }
-
-            _ => (),
-        };
-
-        t.super_visit_with(self)
-    }
-
-    fn visit_region(&mut self, r: Region<'tcx>) -> ControlFlow<Self::BreakTy> {
-        match *r {
-            ty::ReLateBound(index, br) if index == self.binder_index => match br.kind {
-                ty::BoundRegionKind::BrNamed(def_id, _name) => {
-                    if !self.named_parameters.iter().any(|d| *d == def_id) {
-                        self.named_parameters.push(def_id);
-                    }
-                }
-
-                ty::BoundRegionKind::BrAnon(_) => match self.parameters.entry(br.var.as_u32()) {
-                    Entry::Vacant(entry) => {
-                        entry.insert(chalk_ir::VariableKind::Lifetime);
-                    }
-                    Entry::Occupied(entry) => match entry.get() {
-                        chalk_ir::VariableKind::Lifetime => {}
-                        _ => panic!(),
-                    },
-                },
-
-                ty::BoundRegionKind::BrEnv => unimplemented!(),
-            },
-
-            ty::ReEarlyBound(_re) => {
-                // FIXME(chalk): jackh726 - I think we should always have already
-                // substituted away `ReEarlyBound`s for `ReLateBound`s, but need to confirm.
-                unimplemented!();
-            }
-
-            _ => (),
-        };
-
-        ControlFlow::Continue(())
-    }
-}
-
-/// This is used to replace `BoundRegionKind::BrNamed` with `BoundRegionKind::BrAnon`.
-/// Note: we assume that we will always have room for more bound vars. (i.e. we
-/// won't ever hit the `u32` limit in `BrAnon`s).
-struct NamedBoundVarSubstitutor<'a, 'tcx> {
-    tcx: TyCtxt<'tcx>,
-    binder_index: ty::DebruijnIndex,
-    named_parameters: &'a BTreeMap<DefId, u32>,
-}
-
-impl<'a, 'tcx> NamedBoundVarSubstitutor<'a, 'tcx> {
-    fn new(tcx: TyCtxt<'tcx>, named_parameters: &'a BTreeMap<DefId, u32>) -> Self {
-        NamedBoundVarSubstitutor { tcx, binder_index: ty::INNERMOST, named_parameters }
-    }
-}
-
-impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for NamedBoundVarSubstitutor<'a, 'tcx> {
-    fn interner(&self) -> TyCtxt<'tcx> {
-        self.tcx
-    }
-
-    fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
-        &mut self,
-        t: Binder<'tcx, T>,
-    ) -> Binder<'tcx, T> {
-        self.binder_index.shift_in(1);
-        let result = t.super_fold_with(self);
-        self.binder_index.shift_out(1);
-        result
-    }
-
-    fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> {
-        match *r {
-            ty::ReLateBound(index, br) if index == self.binder_index => match br.kind {
-                ty::BrNamed(def_id, _name) => match self.named_parameters.get(&def_id) {
-                    Some(_) => {
-                        let new_br = ty::BoundRegion { var: br.var, kind: ty::BrAnon(None) };
-                        return ty::Region::new_late_bound(self.tcx, index, new_br);
-                    }
-                    None => panic!("Missing `BrNamed`."),
-                },
-                ty::BrEnv => unimplemented!(),
-                ty::BrAnon(..) => {}
-            },
-            _ => (),
-        };
-
-        r
-    }
-}
-
-/// Used to substitute `Param`s with placeholders. We do this since Chalk
-/// have a notion of `Param`s.
-pub(crate) struct ParamsSubstitutor<'tcx> {
-    tcx: TyCtxt<'tcx>,
-    binder_index: ty::DebruijnIndex,
-    list: Vec<rustc_middle::ty::ParamTy>,
-    next_ty_placeholder: usize,
-    pub(crate) params: rustc_data_structures::fx::FxHashMap<u32, rustc_middle::ty::ParamTy>,
-    pub(crate) named_regions: BTreeMap<DefId, u32>,
-}
-
-impl<'tcx> ParamsSubstitutor<'tcx> {
-    pub(crate) fn new(tcx: TyCtxt<'tcx>, next_ty_placeholder: usize) -> Self {
-        ParamsSubstitutor {
-            tcx,
-            binder_index: ty::INNERMOST,
-            list: vec![],
-            next_ty_placeholder,
-            params: rustc_data_structures::fx::FxHashMap::default(),
-            named_regions: BTreeMap::default(),
-        }
-    }
-}
-
-impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ParamsSubstitutor<'tcx> {
-    fn interner(&self) -> TyCtxt<'tcx> {
-        self.tcx
-    }
-
-    fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
-        &mut self,
-        t: Binder<'tcx, T>,
-    ) -> Binder<'tcx, T> {
-        self.binder_index.shift_in(1);
-        let result = t.super_fold_with(self);
-        self.binder_index.shift_out(1);
-        result
-    }
-
-    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
-        match *t.kind() {
-            ty::Param(param) => match self.list.iter().position(|r| r == &param) {
-                Some(idx) => self.tcx.mk_placeholder(ty::PlaceholderType {
-                    universe: ty::UniverseIndex::from_usize(0),
-                    bound: ty::BoundTy {
-                        var: ty::BoundVar::from_usize(idx),
-                        kind: ty::BoundTyKind::Anon,
-                    },
-                }),
-                None => {
-                    self.list.push(param);
-                    let idx = self.list.len() - 1 + self.next_ty_placeholder;
-                    self.params.insert(idx as u32, param);
-                    self.tcx.mk_placeholder(ty::PlaceholderType {
-                        universe: ty::UniverseIndex::from_usize(0),
-                        bound: ty::BoundTy {
-                            var: ty::BoundVar::from_usize(idx),
-                            kind: ty::BoundTyKind::Anon,
-                        },
-                    })
-                }
-            },
-            _ => t.super_fold_with(self),
-        }
-    }
-
-    fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> {
-        match *r {
-            // FIXME(chalk) - jackh726 - this currently isn't hit in any tests,
-            // since canonicalization will already change these to canonical
-            // variables (ty::ReLateBound).
-            ty::ReEarlyBound(_re) => match self.named_regions.get(&_re.def_id) {
-                Some(idx) => {
-                    let br = ty::BoundRegion {
-                        var: ty::BoundVar::from_u32(*idx),
-                        kind: ty::BrAnon(None),
-                    };
-                    ty::Region::new_late_bound(self.tcx, self.binder_index, br)
-                }
-                None => {
-                    let idx = self.named_regions.len() as u32;
-                    let br = ty::BoundRegion {
-                        var: ty::BoundVar::from_u32(idx),
-                        kind: ty::BrAnon(None),
-                    };
-                    self.named_regions.insert(_re.def_id, idx);
-                    ty::Region::new_late_bound(self.tcx, self.binder_index, br)
-                }
-            },
-
-            _ => r,
-        }
-    }
-}
-
-pub(crate) struct ReverseParamsSubstitutor<'tcx> {
-    tcx: TyCtxt<'tcx>,
-    params: rustc_data_structures::fx::FxHashMap<u32, rustc_middle::ty::ParamTy>,
-}
-
-impl<'tcx> ReverseParamsSubstitutor<'tcx> {
-    pub(crate) fn new(
-        tcx: TyCtxt<'tcx>,
-        params: rustc_data_structures::fx::FxHashMap<u32, rustc_middle::ty::ParamTy>,
-    ) -> Self {
-        Self { tcx, params }
-    }
-}
-
-impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReverseParamsSubstitutor<'tcx> {
-    fn interner(&self) -> TyCtxt<'tcx> {
-        self.tcx
-    }
-
-    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
-        match *t.kind() {
-            ty::Placeholder(ty::PlaceholderType { universe: ty::UniverseIndex::ROOT, bound }) => {
-                match self.params.get(&bound.var.as_u32()) {
-                    Some(&ty::ParamTy { index, name }) => self.tcx.mk_ty_param(index, name),
-                    None => t,
-                }
-            }
-
-            _ => t.super_fold_with(self),
-        }
-    }
-}
-
-/// Used to collect `Placeholder`s.
-pub(crate) struct PlaceholdersCollector {
-    universe_index: ty::UniverseIndex,
-    pub(crate) next_ty_placeholder: usize,
-    pub(crate) next_anon_region_placeholder: u32,
-}
-
-impl PlaceholdersCollector {
-    pub(crate) fn new() -> Self {
-        PlaceholdersCollector {
-            universe_index: ty::UniverseIndex::ROOT,
-            next_ty_placeholder: 0,
-            next_anon_region_placeholder: 0,
-        }
-    }
-}
-
-impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for PlaceholdersCollector {
-    fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
-        match t.kind() {
-            ty::Placeholder(p) if p.universe == self.universe_index => {
-                self.next_ty_placeholder = self.next_ty_placeholder.max(p.bound.var.as_usize() + 1);
-            }
-
-            _ => (),
-        };
-
-        t.super_visit_with(self)
-    }
-
-    fn visit_region(&mut self, r: Region<'tcx>) -> ControlFlow<Self::BreakTy> {
-        match *r {
-            ty::RePlaceholder(p) if p.universe == self.universe_index => {
-                if let ty::BoundRegionKind::BrAnon(_) = p.bound.kind {
-                    self.next_anon_region_placeholder =
-                        self.next_anon_region_placeholder.max(p.bound.var.as_u32());
-                }
-                // FIXME: This doesn't seem to handle BrNamed at all?
-            }
-
-            _ => (),
-        };
-
-        ControlFlow::Continue(())
-    }
-}
diff --git a/compiler/rustc_traits/src/chalk/mod.rs b/compiler/rustc_traits/src/chalk/mod.rs
deleted file mode 100644
index 8834449c9a4..00000000000
--- a/compiler/rustc_traits/src/chalk/mod.rs
+++ /dev/null
@@ -1,169 +0,0 @@
-//! Calls `chalk-solve` to solve a `ty::Predicate`
-//!
-//! In order to call `chalk-solve`, this file must convert a `CanonicalChalkEnvironmentAndGoal` into
-//! a Chalk uncanonical goal. It then calls Chalk, and converts the answer back into rustc solution.
-
-pub(crate) mod db;
-pub(crate) mod lowering;
-
-use rustc_middle::infer::canonical::{CanonicalTyVarKind, CanonicalVarKind};
-use rustc_middle::query::Providers;
-use rustc_middle::traits::ChalkRustInterner;
-use rustc_middle::ty::{self, TyCtxt, TypeFoldable, TypeVisitable};
-
-use rustc_infer::infer::canonical::{
-    Canonical, CanonicalVarValues, Certainty, QueryRegionConstraints, QueryResponse,
-};
-use rustc_infer::traits::{self, CanonicalChalkEnvironmentAndGoal};
-
-use crate::chalk::db::RustIrDatabase as ChalkRustIrDatabase;
-use crate::chalk::lowering::LowerInto;
-use crate::chalk::lowering::{ParamsSubstitutor, PlaceholdersCollector, ReverseParamsSubstitutor};
-
-use chalk_solve::Solution;
-
-pub(crate) fn provide(p: &mut Providers) {
-    *p = Providers { evaluate_goal, ..*p };
-}
-
-pub(crate) fn evaluate_goal<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    obligation: CanonicalChalkEnvironmentAndGoal<'tcx>,
-) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, ()>>, traits::query::NoSolution> {
-    let interner = ChalkRustInterner { tcx };
-
-    // Chalk doesn't have a notion of `Params`, so instead we use placeholders.
-    let mut placeholders_collector = PlaceholdersCollector::new();
-    obligation.visit_with(&mut placeholders_collector);
-
-    let mut params_substitutor =
-        ParamsSubstitutor::new(tcx, placeholders_collector.next_ty_placeholder);
-    let obligation = obligation.fold_with(&mut params_substitutor);
-    let params = params_substitutor.params;
-
-    let max_universe = obligation.max_universe.index();
-
-    let lowered_goal: chalk_ir::UCanonical<
-        chalk_ir::InEnvironment<chalk_ir::Goal<ChalkRustInterner<'tcx>>>,
-    > = chalk_ir::UCanonical {
-        canonical: chalk_ir::Canonical {
-            binders: chalk_ir::CanonicalVarKinds::from_iter(
-                interner,
-                obligation.variables.iter().map(|v| match v.kind {
-                    CanonicalVarKind::PlaceholderTy(_ty) => unimplemented!(),
-                    CanonicalVarKind::PlaceholderRegion(_ui) => unimplemented!(),
-                    CanonicalVarKind::Ty(ty) => match ty {
-                        CanonicalTyVarKind::General(ui) => chalk_ir::WithKind::new(
-                            chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General),
-                            chalk_ir::UniverseIndex { counter: ui.index() },
-                        ),
-                        CanonicalTyVarKind::Int => chalk_ir::WithKind::new(
-                            chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::Integer),
-                            chalk_ir::UniverseIndex::root(),
-                        ),
-                        CanonicalTyVarKind::Float => chalk_ir::WithKind::new(
-                            chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::Float),
-                            chalk_ir::UniverseIndex::root(),
-                        ),
-                    },
-                    CanonicalVarKind::Region(ui) => chalk_ir::WithKind::new(
-                        chalk_ir::VariableKind::Lifetime,
-                        chalk_ir::UniverseIndex { counter: ui.index() },
-                    ),
-                    CanonicalVarKind::Const(_ui, _ty) => unimplemented!(),
-                    CanonicalVarKind::PlaceholderConst(_pc, _ty) => unimplemented!(),
-                }),
-            ),
-            value: obligation.value.lower_into(interner),
-        },
-        universes: max_universe + 1,
-    };
-
-    use chalk_solve::Solver;
-    let mut solver = chalk_engine::solve::SLGSolver::new(32, None);
-    let db = ChalkRustIrDatabase { interner };
-    debug!(?lowered_goal);
-    let solution = solver.solve(&db, &lowered_goal);
-    debug!(?obligation, ?solution, "evaluate goal");
-
-    // Ideally, the code to convert *back* to rustc types would live close to
-    // the code to convert *from* rustc types. Right now though, we don't
-    // really need this and so it's really minimal.
-    // Right now, we also treat a `Unique` solution the same as
-    // `Ambig(Definite)`. This really isn't right.
-    let make_solution = |subst: chalk_ir::Substitution<_>,
-                         binders: chalk_ir::CanonicalVarKinds<_>| {
-        use rustc_middle::infer::canonical::CanonicalVarInfo;
-
-        let mut reverse_param_substitutor = ReverseParamsSubstitutor::new(tcx, params);
-        let var_values = tcx.mk_substs_from_iter(
-            subst
-                .as_slice(interner)
-                .iter()
-                .map(|p| p.lower_into(interner).fold_with(&mut reverse_param_substitutor)),
-        );
-        let variables = binders.iter(interner).map(|var| {
-            let kind = match var.kind {
-                chalk_ir::VariableKind::Ty(ty_kind) => CanonicalVarKind::Ty(match ty_kind {
-                    chalk_ir::TyVariableKind::General => CanonicalTyVarKind::General(
-                        ty::UniverseIndex::from_usize(var.skip_kind().counter),
-                    ),
-                    chalk_ir::TyVariableKind::Integer => CanonicalTyVarKind::Int,
-                    chalk_ir::TyVariableKind::Float => CanonicalTyVarKind::Float,
-                }),
-                chalk_ir::VariableKind::Lifetime => {
-                    CanonicalVarKind::Region(ty::UniverseIndex::from_usize(var.skip_kind().counter))
-                }
-                // FIXME(compiler-errors): We don't currently have a way of turning
-                // a Chalk ty back into a rustc ty, right?
-                chalk_ir::VariableKind::Const(_) => todo!(),
-            };
-            CanonicalVarInfo { kind }
-        });
-        let max_universe = binders.iter(interner).map(|v| v.skip_kind().counter).max().unwrap_or(0);
-        let sol = Canonical {
-            max_universe: ty::UniverseIndex::from_usize(max_universe),
-            variables: tcx.mk_canonical_var_infos_from_iter(variables),
-            value: QueryResponse {
-                var_values: CanonicalVarValues { var_values },
-                region_constraints: QueryRegionConstraints::default(),
-                certainty: Certainty::Proven,
-                opaque_types: vec![],
-                value: (),
-            },
-        };
-        tcx.arena.alloc(sol)
-    };
-    solution
-        .map(|s| match s {
-            Solution::Unique(subst) => {
-                // FIXME(chalk): handle constraints
-                make_solution(subst.value.subst, subst.binders)
-            }
-            Solution::Ambig(guidance) => {
-                match guidance {
-                    chalk_solve::Guidance::Definite(subst) => {
-                        make_solution(subst.value, subst.binders)
-                    }
-                    chalk_solve::Guidance::Suggested(_) => unimplemented!(),
-                    chalk_solve::Guidance::Unknown => {
-                        // chalk_fulfill doesn't use the var_values here, so
-                        // let's just ignore that
-                        let sol = Canonical {
-                            max_universe: ty::UniverseIndex::from_usize(0),
-                            variables: obligation.variables,
-                            value: QueryResponse {
-                                var_values: CanonicalVarValues::dummy(),
-                                region_constraints: QueryRegionConstraints::default(),
-                                certainty: Certainty::Ambiguous,
-                                opaque_types: vec![],
-                                value: (),
-                            },
-                        };
-                        &*tcx.arena.alloc(sol)
-                    }
-                }
-            }
-        })
-        .ok_or(traits::query::NoSolution)
-}
diff --git a/compiler/rustc_traits/src/lib.rs b/compiler/rustc_traits/src/lib.rs
index 590d0bd0e42..dfa5814219b 100644
--- a/compiler/rustc_traits/src/lib.rs
+++ b/compiler/rustc_traits/src/lib.rs
@@ -1,5 +1,4 @@
-//! New recursive solver modeled on Chalk's recursive solver. Most of
-//! the guts are broken up into modules; see the comments in those modules.
+//! Queries that are independent from the main solver code.
 
 #![deny(rustc::untranslatable_diagnostic)]
 #![deny(rustc::diagnostic_outside_of_impl)]
@@ -11,7 +10,6 @@ extern crate tracing;
 #[macro_use]
 extern crate rustc_middle;
 
-mod chalk;
 mod codegen;
 mod dropck_outlives;
 mod evaluate_obligation;
@@ -29,7 +27,6 @@ pub fn provide(p: &mut Providers) {
     dropck_outlives::provide(p);
     evaluate_obligation::provide(p);
     implied_outlives_bounds::provide(p);
-    chalk::provide(p);
     normalize_projection_ty::provide(p);
     normalize_erasing_regions::provide(p);
     type_op::provide(p);
diff --git a/compiler/rustc_traits/src/normalize_erasing_regions.rs b/compiler/rustc_traits/src/normalize_erasing_regions.rs
index b83abf98592..2563e3ed1a3 100644
--- a/compiler/rustc_traits/src/normalize_erasing_regions.rs
+++ b/compiler/rustc_traits/src/normalize_erasing_regions.rs
@@ -69,7 +69,6 @@ fn not_outlives_predicate(p: ty::Predicate<'_>) -> bool {
         | ty::PredicateKind::Coerce(..)
         | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
         | ty::PredicateKind::ConstEquate(..)
-        | ty::PredicateKind::Ambiguous
-        | ty::PredicateKind::Clause(ty::ClauseKind::TypeWellFormedFromEnv(..)) => true,
+        | ty::PredicateKind::Ambiguous => true,
     }
 }
diff --git a/compiler/rustc_ty_utils/Cargo.toml b/compiler/rustc_ty_utils/Cargo.toml
index 51885c9b47e..50dac3a37a4 100644
--- a/compiler/rustc_ty_utils/Cargo.toml
+++ b/compiler/rustc_ty_utils/Cargo.toml
@@ -5,6 +5,7 @@ edition = "2021"
 
 [dependencies]
 tracing = "0.1"
+itertools = "0.10.1"
 rustc_middle = { path = "../rustc_middle" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs
index 8e2a79dd4d4..55484f5c72e 100644
--- a/compiler/rustc_ty_utils/src/abi.rs
+++ b/compiler/rustc_ty_utils/src/abi.rs
@@ -64,7 +64,7 @@ fn fn_sig_for_fn_abi<'tcx>(
                 // Modify `fn(self, ...)` to `fn(self: *mut Self, ...)`.
                 sig = sig.map_bound(|mut sig| {
                     let mut inputs_and_output = sig.inputs_and_output.to_vec();
-                    inputs_and_output[0] = tcx.mk_mut_ptr(inputs_and_output[0]);
+                    inputs_and_output[0] = Ty::new_mut_ptr(tcx, inputs_and_output[0]);
                     sig.inputs_and_output = tcx.mk_type_list(&inputs_and_output);
                     sig
                 });
@@ -106,12 +106,13 @@ fn fn_sig_for_fn_abi<'tcx>(
                 var: ty::BoundVar::from_usize(bound_vars.len() - 1),
                 kind: ty::BoundRegionKind::BrEnv,
             };
-            let env_ty = tcx.mk_mut_ref(ty::Region::new_late_bound(tcx, ty::INNERMOST, br), ty);
+            let env_ty =
+                Ty::new_mut_ref(tcx, ty::Region::new_late_bound(tcx, ty::INNERMOST, br), ty);
 
             let pin_did = tcx.require_lang_item(LangItem::Pin, None);
             let pin_adt_ref = tcx.adt_def(pin_did);
             let pin_substs = tcx.mk_substs(&[env_ty.into()]);
-            let env_ty = tcx.mk_adt(pin_adt_ref, pin_substs);
+            let env_ty = Ty::new_adt(tcx, pin_adt_ref, pin_substs);
 
             let sig = sig.skip_binder();
             // The `FnSig` and the `ret_ty` here is for a generators main
@@ -123,7 +124,7 @@ fn fn_sig_for_fn_abi<'tcx>(
                 let poll_did = tcx.require_lang_item(LangItem::Poll, None);
                 let poll_adt_ref = tcx.adt_def(poll_did);
                 let poll_substs = tcx.mk_substs(&[sig.return_ty.into()]);
-                let ret_ty = tcx.mk_adt(poll_adt_ref, poll_substs);
+                let ret_ty = Ty::new_adt(tcx, poll_adt_ref, poll_substs);
 
                 // We have to replace the `ResumeTy` that is used for type and borrow checking
                 // with `&mut Context<'_>` which is used in codegen.
@@ -137,7 +138,7 @@ fn fn_sig_for_fn_abi<'tcx>(
                         panic!("expected `ResumeTy`, found `{:?}`", sig.resume_ty);
                     };
                 }
-                let context_mut_ref = tcx.mk_task_context();
+                let context_mut_ref = Ty::new_task_context(tcx);
 
                 (context_mut_ref, ret_ty)
             } else {
@@ -145,7 +146,7 @@ fn fn_sig_for_fn_abi<'tcx>(
                 let state_did = tcx.require_lang_item(LangItem::GeneratorState, None);
                 let state_adt_ref = tcx.adt_def(state_did);
                 let state_substs = tcx.mk_substs(&[sig.yield_ty.into(), sig.return_ty.into()]);
-                let ret_ty = tcx.mk_adt(state_adt_ref, state_substs);
+                let ret_ty = Ty::new_adt(tcx, state_adt_ref, state_substs);
 
                 (sig.resume_ty, ret_ty)
             };
@@ -566,7 +567,7 @@ fn make_thin_self_ptr<'tcx>(
     let fat_pointer_ty = if layout.is_unsized() {
         // unsized `self` is passed as a pointer to `self`
         // FIXME (mikeyhew) change this to use &own if it is ever added to the language
-        tcx.mk_mut_ptr(layout.ty)
+        Ty::new_mut_ptr(tcx, layout.ty)
     } else {
         match layout.abi {
             Abi::ScalarPair(..) | Abi::Scalar(..) => (),
@@ -600,7 +601,7 @@ fn make_thin_self_ptr<'tcx>(
     // we now have a type like `*mut RcBox<dyn Trait>`
     // change its layout to that of `*mut ()`, a thin pointer, but keep the same type
     // this is understood as a special case elsewhere in the compiler
-    let unit_ptr_ty = tcx.mk_mut_ptr(tcx.mk_unit());
+    let unit_ptr_ty = Ty::new_mut_ptr(tcx, Ty::new_unit(tcx));
 
     TyAndLayout {
         ty: fat_pointer_ty,
diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs
index b59458bbf35..897e7aad48b 100644
--- a/compiler/rustc_ty_utils/src/assoc.rs
+++ b/compiler/rustc_ty_utils/src/assoc.rs
@@ -5,7 +5,7 @@ use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId};
 use rustc_hir::definitions::DefPathData;
 use rustc_hir::intravisit::{self, Visitor};
 use rustc_middle::query::Providers;
-use rustc_middle::ty::{self, ImplTraitInTraitData, InternalSubsts, TyCtxt};
+use rustc_middle::ty::{self, ImplTraitInTraitData, InternalSubsts, Ty, TyCtxt};
 use rustc_span::symbol::kw;
 
 pub fn provide(providers: &mut Providers) {
@@ -301,7 +301,8 @@ fn associated_type_for_impl_trait_in_trait(
     trait_assoc_ty.defaultness(tcx.defaultness(fn_def_id));
 
     // Copy type_of of the opaque.
-    trait_assoc_ty.type_of(ty::EarlyBinder::bind(tcx.mk_opaque(
+    trait_assoc_ty.type_of(ty::EarlyBinder::bind(Ty::new_opaque(
+        tcx,
         opaque_ty_def_id.to_def_id(),
         InternalSubsts::identity_for_item(tcx, opaque_ty_def_id),
     )));
@@ -337,6 +338,7 @@ fn associated_type_for_impl_trait_in_trait(
             param_def_id_to_index,
             has_self: opaque_ty_generics.has_self,
             has_late_bound_regions: opaque_ty_generics.has_late_bound_regions,
+            host_effect_index: parent_generics.host_effect_index,
         }
     });
 
@@ -415,6 +417,7 @@ fn associated_type_for_impl_trait_in_impl(
             param_def_id_to_index,
             has_self: false,
             has_late_bound_regions: trait_assoc_generics.has_late_bound_regions,
+            host_effect_index: parent_generics.host_effect_index,
         }
     });
 
diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs
index d79ed220570..8cc75a6e2e3 100644
--- a/compiler/rustc_ty_utils/src/consts.rs
+++ b/compiler/rustc_ty_utils/src/consts.rs
@@ -33,8 +33,10 @@ pub(crate) fn destructure_const<'tcx>(
     let (fields, variant) = match const_.ty().kind() {
         ty::Array(inner_ty, _) | ty::Slice(inner_ty) => {
             // construct the consts for the elements of the array/slice
-            let field_consts =
-                branches.iter().map(|b| tcx.mk_const(*b, *inner_ty)).collect::<Vec<_>>();
+            let field_consts = branches
+                .iter()
+                .map(|b| ty::Const::new_value(tcx, *b, *inner_ty))
+                .collect::<Vec<_>>();
             debug!(?field_consts);
 
             (field_consts, None)
@@ -52,7 +54,7 @@ pub(crate) fn destructure_const<'tcx>(
 
             for (field, field_valtree) in iter::zip(fields, branches) {
                 let field_ty = field.ty(tcx, substs);
-                let field_const = tcx.mk_const(*field_valtree, field_ty);
+                let field_const = ty::Const::new_value(tcx, *field_valtree, field_ty);
                 field_consts.push(field_const);
             }
             debug!(?field_consts);
@@ -61,7 +63,7 @@ pub(crate) fn destructure_const<'tcx>(
         }
         ty::Tuple(elem_tys) => {
             let fields = iter::zip(*elem_tys, branches)
-                .map(|(elem_ty, elem_valtree)| tcx.mk_const(*elem_valtree, elem_ty))
+                .map(|(elem_ty, elem_valtree)| ty::Const::new_value(tcx, *elem_valtree, elem_ty))
                 .collect::<Vec<_>>();
 
             (fields, None)
@@ -117,7 +119,7 @@ fn recurse_build<'tcx>(
             let sp = node.span;
             match tcx.at(sp).lit_to_const(LitToConstInput { lit: &lit.node, ty: node.ty, neg }) {
                 Ok(c) => c,
-                Err(LitToConstError::Reported(guar)) => tcx.const_error(node.ty, guar),
+                Err(LitToConstError::Reported(guar)) => ty::Const::new_error(tcx, guar, node.ty),
                 Err(LitToConstError::TypeError) => {
                     bug!("encountered type error in lit_to_const")
                 }
@@ -125,17 +127,17 @@ fn recurse_build<'tcx>(
         }
         &ExprKind::NonHirLiteral { lit, user_ty: _ } => {
             let val = ty::ValTree::from_scalar_int(lit);
-            tcx.mk_const(val, node.ty)
+            ty::Const::new_value(tcx, val, node.ty)
         }
         &ExprKind::ZstLiteral { user_ty: _ } => {
             let val = ty::ValTree::zst();
-            tcx.mk_const(val, node.ty)
+            ty::Const::new_value(tcx, val, node.ty)
         }
         &ExprKind::NamedConst { def_id, substs, user_ty: _ } => {
             let uneval = ty::UnevaluatedConst::new(def_id, substs);
-            tcx.mk_const(uneval, node.ty)
+            ty::Const::new_unevaluated(tcx, uneval, node.ty)
         }
-        ExprKind::ConstParam { param, .. } => tcx.mk_const(*param, node.ty),
+        ExprKind::ConstParam { param, .. } => ty::Const::new_param(tcx, *param, node.ty),
 
         ExprKind::Call { fun, args, .. } => {
             let fun = recurse_build(tcx, body, *fun, root_span)?;
@@ -145,16 +147,16 @@ fn recurse_build<'tcx>(
                 new_args.push(recurse_build(tcx, body, id, root_span)?);
             }
             let new_args = tcx.mk_const_list(&new_args);
-            tcx.mk_const(Expr::FunctionCall(fun, new_args), node.ty)
+            ty::Const::new_expr(tcx, Expr::FunctionCall(fun, new_args), node.ty)
         }
         &ExprKind::Binary { op, lhs, rhs } if check_binop(op) => {
             let lhs = recurse_build(tcx, body, lhs, root_span)?;
             let rhs = recurse_build(tcx, body, rhs, root_span)?;
-            tcx.mk_const(Expr::Binop(op, lhs, rhs), node.ty)
+            ty::Const::new_expr(tcx, Expr::Binop(op, lhs, rhs), node.ty)
         }
         &ExprKind::Unary { op, arg } if check_unop(op) => {
             let arg = recurse_build(tcx, body, arg, root_span)?;
-            tcx.mk_const(Expr::UnOp(op, arg), node.ty)
+            ty::Const::new_expr(tcx, Expr::UnOp(op, arg), node.ty)
         }
         // This is necessary so that the following compiles:
         //
@@ -175,11 +177,11 @@ fn recurse_build<'tcx>(
         // This is important so that `N as usize as usize` doesnt unify with `N as usize`. (untested)
         &ExprKind::Use { source } => {
             let arg = recurse_build(tcx, body, source, root_span)?;
-            tcx.mk_const(Expr::Cast(CastKind::Use, arg, node.ty), node.ty)
+            ty::Const::new_expr(tcx, Expr::Cast(CastKind::Use, arg, node.ty), node.ty)
         }
         &ExprKind::Cast { source } => {
             let arg = recurse_build(tcx, body, source, root_span)?;
-            tcx.mk_const(Expr::Cast(CastKind::As, arg, node.ty), node.ty)
+            ty::Const::new_expr(tcx, Expr::Cast(CastKind::As, arg, node.ty), node.ty)
         }
         ExprKind::Borrow { arg, .. } => {
             let arg_node = &body.exprs[*arg];
diff --git a/compiler/rustc_ty_utils/src/implied_bounds.rs b/compiler/rustc_ty_utils/src/implied_bounds.rs
index 081be065864..10dec9a7a32 100644
--- a/compiler/rustc_ty_utils/src/implied_bounds.rs
+++ b/compiler/rustc_ty_utils/src/implied_bounds.rs
@@ -1,45 +1,56 @@
-use rustc_hir::{def::DefKind, def_id::DefId};
+use rustc_hir as hir;
+use rustc_hir::def::DefKind;
+use rustc_hir::def_id::LocalDefId;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_span::Span;
+use std::iter;
 
 pub fn provide(providers: &mut Providers) {
     *providers = Providers { assumed_wf_types, ..*providers };
 }
 
-fn assumed_wf_types(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::List<Ty<'_>> {
+fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<'tcx>, Span)] {
     match tcx.def_kind(def_id) {
         DefKind::Fn => {
             let sig = tcx.fn_sig(def_id).subst_identity();
-            let liberated_sig = tcx.liberate_late_bound_regions(def_id, sig);
-            liberated_sig.inputs_and_output
+            let liberated_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), sig);
+            tcx.arena.alloc_from_iter(itertools::zip_eq(
+                liberated_sig.inputs_and_output,
+                fn_sig_spans(tcx, def_id),
+            ))
         }
         DefKind::AssocFn => {
             let sig = tcx.fn_sig(def_id).subst_identity();
-            let liberated_sig = tcx.liberate_late_bound_regions(def_id, sig);
+            let liberated_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), sig);
             let mut assumed_wf_types: Vec<_> =
-                tcx.assumed_wf_types(tcx.parent(def_id)).as_slice().into();
-            assumed_wf_types.extend(liberated_sig.inputs_and_output);
-            tcx.mk_type_list(&assumed_wf_types)
+                tcx.assumed_wf_types(tcx.local_parent(def_id)).into();
+            assumed_wf_types.extend(itertools::zip_eq(
+                liberated_sig.inputs_and_output,
+                fn_sig_spans(tcx, def_id),
+            ));
+            tcx.arena.alloc_slice(&assumed_wf_types)
         }
         DefKind::Impl { .. } => {
-            match tcx.impl_trait_ref(def_id) {
-                Some(trait_ref) => {
-                    let types: Vec<_> = trait_ref.skip_binder().substs.types().collect();
-                    tcx.mk_type_list(&types)
-                }
-                // Only the impl self type
-                None => tcx.mk_type_list(&[tcx.type_of(def_id).subst_identity()]),
-            }
+            // Trait arguments and the self type for trait impls or only the self type for
+            // inherent impls.
+            let tys = match tcx.impl_trait_ref(def_id) {
+                Some(trait_ref) => trait_ref.skip_binder().substs.types().collect(),
+                None => vec![tcx.type_of(def_id).subst_identity()],
+            };
+
+            let mut impl_spans = impl_spans(tcx, def_id);
+            tcx.arena.alloc_from_iter(tys.into_iter().map(|ty| (ty, impl_spans.next().unwrap())))
         }
-        DefKind::AssocConst | DefKind::AssocTy => tcx.assumed_wf_types(tcx.parent(def_id)),
-        DefKind::OpaqueTy => match tcx.def_kind(tcx.parent(def_id)) {
+        DefKind::AssocConst | DefKind::AssocTy => tcx.assumed_wf_types(tcx.local_parent(def_id)),
+        DefKind::OpaqueTy => match tcx.def_kind(tcx.local_parent(def_id)) {
             DefKind::TyAlias => ty::List::empty(),
-            DefKind::AssocTy => tcx.assumed_wf_types(tcx.parent(def_id)),
+            DefKind::AssocTy => tcx.assumed_wf_types(tcx.local_parent(def_id)),
             // Nested opaque types only occur in associated types:
             // ` type Opaque<T> = impl Trait<&'static T, AssocTy = impl Nested>; `
             // assumed_wf_types should include those of `Opaque<T>`, `Opaque<T>` itself
             // and `&'static T`.
-            DefKind::OpaqueTy => bug!("unimplemented implied bounds for neseted opaque types"),
+            DefKind::OpaqueTy => bug!("unimplemented implied bounds for nested opaque types"),
             def_kind @ _ => {
                 bug!("unimplemented implied bounds for opaque types with parent {def_kind:?}")
             }
@@ -72,3 +83,28 @@ fn assumed_wf_types(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::List<Ty<'_>> {
         | DefKind::Generator => ty::List::empty(),
     }
 }
+
+fn fn_sig_spans(tcx: TyCtxt<'_>, def_id: LocalDefId) -> impl Iterator<Item = Span> + '_ {
+    let node = tcx.hir().get(tcx.local_def_id_to_hir_id(def_id));
+    if let Some(decl) = node.fn_decl() {
+        decl.inputs.iter().map(|ty| ty.span).chain(iter::once(decl.output.span()))
+    } else {
+        bug!("unexpected item for fn {def_id:?}: {node:?}")
+    }
+}
+
+fn impl_spans(tcx: TyCtxt<'_>, def_id: LocalDefId) -> impl Iterator<Item = Span> + '_ {
+    let item = tcx.hir().expect_item(def_id);
+    if let hir::ItemKind::Impl(impl_) = item.kind {
+        let trait_args = impl_
+            .of_trait
+            .into_iter()
+            .flat_map(|trait_ref| trait_ref.path.segments.last().unwrap().args().args)
+            .map(|arg| arg.span());
+        let dummy_spans_for_default_args =
+            impl_.of_trait.into_iter().flat_map(|trait_ref| iter::repeat(trait_ref.path.span));
+        iter::once(impl_.self_ty.span).chain(trait_args).chain(dummy_spans_for_default_args)
+    } else {
+        bug!("unexpected item for impl {def_id:?}: {item:?}")
+    }
+}
diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs
index ac015ddcb49..9ef9120e294 100644
--- a/compiler/rustc_ty_utils/src/layout.rs
+++ b/compiler/rustc_ty_utils/src/layout.rs
@@ -159,7 +159,7 @@ fn layout_of_uncached<'tcx>(
                 // fall back to structurally deducing metadata.
                 && !pointee.references_error()
             {
-                let pointee_metadata = tcx.mk_projection(metadata_def_id, [pointee]);
+                let pointee_metadata = Ty::new_projection(tcx,metadata_def_id, [pointee]);
                 let metadata_ty = match tcx.try_normalize_erasing_regions(
                     param_env,
                     pointee_metadata,
@@ -672,7 +672,7 @@ fn generator_layout<'tcx>(
     let promoted_layouts = ineligible_locals
         .iter()
         .map(|local| subst_field(info.field_tys[local].ty))
-        .map(|ty| tcx.mk_maybe_uninit(ty))
+        .map(|ty| Ty::new_maybe_uninit(tcx, ty))
         .map(|ty| Ok(cx.layout_of(ty)?.layout));
     let prefix_layouts = substs
         .as_generator()
diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs
index 2b391f94a63..466616cd22b 100644
--- a/compiler/rustc_ty_utils/src/ty.rs
+++ b/compiler/rustc_ty_utils/src/ty.rs
@@ -1,13 +1,11 @@
-use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
+use rustc_data_structures::fx::FxHashSet;
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_index::bit_set::BitSet;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::{
-    self, EarlyBinder, ImplTraitInTraitData, ToPredicate, Ty, TyCtxt, TypeSuperVisitable,
-    TypeVisitable, TypeVisitor,
+    self, EarlyBinder, ToPredicate, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor,
 };
-use rustc_session::config::TraitSolver;
 use rustc_span::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
 use rustc_span::DUMMY_SP;
 use rustc_trait_selection::traits;
@@ -97,7 +95,7 @@ fn defaultness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Defaultness {
 fn adt_sized_constraint(tcx: TyCtxt<'_>, def_id: DefId) -> &[Ty<'_>] {
     if let Some(def_id) = def_id.as_local() {
         if matches!(tcx.representability(def_id), ty::Representability::Infinite) {
-            return tcx.mk_type_list(&[tcx.ty_error_misc()]);
+            return tcx.mk_type_list(&[Ty::new_misc_error(tcx)]);
         }
     }
     let def = tcx.adt_def(def_id);
@@ -120,22 +118,6 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> {
     let ty::InstantiatedPredicates { mut predicates, .. } =
         tcx.predicates_of(def_id).instantiate_identity(tcx);
 
-    // When computing the param_env of an RPITIT, use predicates of the containing function,
-    // *except* for the additional assumption that the RPITIT normalizes to the trait method's
-    // default opaque type. This is needed to properly check the item bounds of the assoc
-    // type hold (`check_type_bounds`), since that method already installs a similar projection
-    // bound, so they will conflict.
-    // FIXME(-Zlower-impl-trait-in-trait-to-assoc-ty): I don't like this, we should
-    // at least be making sure that the generics in RPITITs and their parent fn don't
-    // get out of alignment, or else we do actually need to substitute these predicates.
-    if let Some(ImplTraitInTraitData::Trait { fn_def_id, .. })
-    | Some(ImplTraitInTraitData::Impl { fn_def_id, .. }) = tcx.opt_rpitit_info(def_id)
-    {
-        // FIXME(-Zlower-impl-trait-in-trait-to-assoc-ty): Should not need to add the predicates
-        // from the parent fn to our assumptions
-        predicates.extend(tcx.predicates_of(fn_def_id).instantiate_identity(tcx).predicates);
-    }
-
     // Finally, we have to normalize the bounds in the environment, in
     // case they contain any associated type projections. This process
     // can yield errors if the put in illegal associated types, like
@@ -148,11 +130,6 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> {
     // are any errors at that point, so outside of type inference you can be
     // sure that this will succeed without errors anyway.
 
-    if tcx.sess.opts.unstable_opts.trait_solver == TraitSolver::Chalk {
-        let environment = well_formed_types_in_env(tcx, def_id);
-        predicates.extend(environment);
-    }
-
     if tcx.def_kind(def_id) == DefKind::AssocFn
         && tcx.associated_item(def_id).container == ty::AssocItemContainer::TraitContainer
     {
@@ -308,7 +285,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ImplTraitInTraitFinder<'_, 'tcx> {
             let default_ty = if self.tcx.lower_impl_trait_in_trait_to_assoc_ty() {
                 self.tcx.type_of(shifted_alias_ty.def_id).subst(self.tcx, shifted_alias_ty.substs)
             } else {
-                self.tcx.mk_alias(ty::Opaque, shifted_alias_ty)
+                Ty::new_alias(self.tcx,ty::Opaque, shifted_alias_ty)
             };
 
             self.predicates.push(
@@ -336,116 +313,6 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ImplTraitInTraitFinder<'_, 'tcx> {
     }
 }
 
-/// Elaborate the environment.
-///
-/// Collect a list of `Predicate`'s used for building the `ParamEnv`. Adds `TypeWellFormedFromEnv`'s
-/// that are assumed to be well-formed (because they come from the environment).
-///
-/// Used only in chalk mode.
-fn well_formed_types_in_env(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::List<ty::Clause<'_>> {
-    use rustc_hir::{ForeignItemKind, ImplItemKind, ItemKind, Node, TraitItemKind};
-
-    debug!("environment(def_id = {:?})", def_id);
-
-    // The environment of an impl Trait type is its defining function's environment.
-    if let Some(parent) = ty::is_impl_trait_defn(tcx, def_id) {
-        return well_formed_types_in_env(tcx, parent.to_def_id());
-    }
-
-    // Compute the bounds on `Self` and the type parameters.
-    let ty::InstantiatedPredicates { predicates, .. } =
-        tcx.predicates_of(def_id).instantiate_identity(tcx);
-
-    let clauses = predicates.into_iter();
-
-    if !def_id.is_local() {
-        return ty::List::empty();
-    }
-    let node = tcx.hir().get_by_def_id(def_id.expect_local());
-
-    enum NodeKind {
-        TraitImpl,
-        InherentImpl,
-        Fn,
-        Other,
-    }
-
-    let node_kind = match node {
-        Node::TraitItem(item) => match item.kind {
-            TraitItemKind::Fn(..) => NodeKind::Fn,
-            _ => NodeKind::Other,
-        },
-
-        Node::ImplItem(item) => match item.kind {
-            ImplItemKind::Fn(..) => NodeKind::Fn,
-            _ => NodeKind::Other,
-        },
-
-        Node::Item(item) => match item.kind {
-            ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) => NodeKind::TraitImpl,
-            ItemKind::Impl(hir::Impl { of_trait: None, .. }) => NodeKind::InherentImpl,
-            ItemKind::Fn(..) => NodeKind::Fn,
-            _ => NodeKind::Other,
-        },
-
-        Node::ForeignItem(item) => match item.kind {
-            ForeignItemKind::Fn(..) => NodeKind::Fn,
-            _ => NodeKind::Other,
-        },
-
-        // FIXME: closures?
-        _ => NodeKind::Other,
-    };
-
-    // FIXME(eddyb) isn't the unordered nature of this a hazard?
-    let mut inputs = FxIndexSet::default();
-
-    match node_kind {
-        // In a trait impl, we assume that the header trait ref and all its
-        // constituents are well-formed.
-        NodeKind::TraitImpl => {
-            let trait_ref = tcx.impl_trait_ref(def_id).expect("not an impl").subst_identity();
-
-            // FIXME(chalk): this has problems because of late-bound regions
-            //inputs.extend(trait_ref.substs.iter().flat_map(|arg| arg.walk()));
-            inputs.extend(trait_ref.substs.iter());
-        }
-
-        // In an inherent impl, we assume that the receiver type and all its
-        // constituents are well-formed.
-        NodeKind::InherentImpl => {
-            let self_ty = tcx.type_of(def_id).subst_identity();
-            inputs.extend(self_ty.walk());
-        }
-
-        // In an fn, we assume that the arguments and all their constituents are
-        // well-formed.
-        NodeKind::Fn => {
-            let fn_sig = tcx.fn_sig(def_id).subst_identity();
-            let fn_sig = tcx.liberate_late_bound_regions(def_id, fn_sig);
-
-            inputs.extend(fn_sig.inputs().iter().flat_map(|ty| ty.walk()));
-        }
-
-        NodeKind::Other => (),
-    }
-    let input_clauses = inputs.into_iter().filter_map(|arg| {
-        match arg.unpack() {
-            ty::GenericArgKind::Type(ty) => {
-                Some(ty::ClauseKind::TypeWellFormedFromEnv(ty).to_predicate(tcx))
-            }
-
-            // FIXME(eddyb) no WF conditions from lifetimes?
-            ty::GenericArgKind::Lifetime(_) => None,
-
-            // FIXME(eddyb) support const generics in Chalk
-            ty::GenericArgKind::Const(_) => None,
-        }
-    });
-
-    tcx.mk_clauses_from_iter(clauses.chain(input_clauses))
-}
-
 fn param_env_reveal_all_normalized(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> {
     tcx.param_env(def_id).with_reveal_all_normalized(tcx)
 }
diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs
index ae16fbb162e..878a6b784ed 100644
--- a/compiler/rustc_type_ir/src/lib.rs
+++ b/compiler/rustc_type_ir/src/lib.rs
@@ -57,11 +57,19 @@ pub trait Interner: Sized {
     type ParamTy: Clone + Debug + Hash + Ord;
     type BoundTy: Clone + Debug + Hash + Ord;
     type PlaceholderType: Clone + Debug + Hash + Ord;
-    type InferTy: Clone + Debug + Hash + Ord;
     type ErrorGuaranteed: Clone + Debug + Hash + Ord;
     type PredicateKind: Clone + Debug + Hash + PartialEq + Eq;
     type AllocId: Clone + Debug + Hash + Ord;
 
+    type InferConst: Clone + Debug + Hash + Ord;
+    type AliasConst: Clone + Debug + Hash + Ord;
+    type PlaceholderConst: Clone + Debug + Hash + Ord;
+    type ParamConst: Clone + Debug + Hash + Ord;
+    type BoundConst: Clone + Debug + Hash + Ord;
+    type InferTy: Clone + Debug + Hash + Ord;
+    type ValueConst: Clone + Debug + Hash + Ord;
+    type ExprConst: Clone + Debug + Hash + Ord;
+
     type EarlyBoundRegion: Clone + Debug + Hash + Ord;
     type BoundRegion: Clone + Debug + Hash + Ord;
     type FreeRegion: Clone + Debug + Hash + Ord;
diff --git a/compiler/rustc_type_ir/src/structural_impls.rs b/compiler/rustc_type_ir/src/structural_impls.rs
index 553d7f31b2d..1e42175f6e3 100644
--- a/compiler/rustc_type_ir/src/structural_impls.rs
+++ b/compiler/rustc_type_ir/src/structural_impls.rs
@@ -4,7 +4,7 @@
 
 use crate::fold::{FallibleTypeFolder, TypeFoldable};
 use crate::visit::{TypeVisitable, TypeVisitor};
-use crate::{FloatTy, IntTy, Interner, UintTy};
+use crate::{ConstKind, FloatTy, IntTy, Interner, UintTy};
 use rustc_data_structures::functor::IdFunctor;
 use rustc_data_structures::sync::Lrc;
 use rustc_index::{Idx, IndexVec};
@@ -182,3 +182,21 @@ impl fmt::Debug for FloatTy {
         write!(f, "{}", self.name_str())
     }
 }
+
+impl<I: Interner> fmt::Debug for ConstKind<I> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        use ConstKind::*;
+        match self {
+            Param(param) => write!(f, "{param:?}"),
+            Infer(var) => write!(f, "{var:?}"),
+            Bound(debruijn, var) => crate::debug_bound_var(f, *debruijn, var.clone()),
+            Placeholder(placeholder) => write!(f, "{placeholder:?}"),
+            Unevaluated(uv) => {
+                write!(f, "{uv:?}")
+            }
+            Value(valtree) => write!(f, "{valtree:?}"),
+            Error(_) => write!(f, "{{const error}}"),
+            Expr(expr) => write!(f, "{expr:?}"),
+        }
+    }
+}
diff --git a/compiler/rustc_type_ir/src/sty.rs b/compiler/rustc_type_ir/src/sty.rs
index f621673f1d6..b696f9b9b59 100644
--- a/compiler/rustc_type_ir/src/sty.rs
+++ b/compiler/rustc_type_ir/src/sty.rs
@@ -876,6 +876,224 @@ where
     }
 }
 
+/// Represents a constant in Rust.
+// #[derive(derive_more::From)]
+pub enum ConstKind<I: Interner> {
+    /// A const generic parameter.
+    Param(I::ParamConst),
+
+    /// Infer the value of the const.
+    Infer(I::InferConst),
+
+    /// Bound const variable, used only when preparing a trait query.
+    Bound(DebruijnIndex, I::BoundConst),
+
+    /// A placeholder const - universally quantified higher-ranked const.
+    Placeholder(I::PlaceholderConst),
+
+    /// An unnormalized const item such as an anon const or assoc const or free const item.
+    /// Right now anything other than anon consts does not actually work properly but this
+    /// should
+    Unevaluated(I::AliasConst),
+
+    /// Used to hold computed value.
+    Value(I::ValueConst),
+
+    /// A placeholder for a const which could not be computed; this is
+    /// propagated to avoid useless error messages.
+    Error(I::ErrorGuaranteed),
+
+    /// Unevaluated non-const-item, used by `feature(generic_const_exprs)` to represent
+    /// const arguments such as `N + 1` or `foo(N)`
+    Expr(I::ExprConst),
+}
+
+const fn const_kind_discriminant<I: Interner>(value: &ConstKind<I>) -> usize {
+    match value {
+        ConstKind::Param(_) => 0,
+        ConstKind::Infer(_) => 1,
+        ConstKind::Bound(_, _) => 2,
+        ConstKind::Placeholder(_) => 3,
+        ConstKind::Unevaluated(_) => 4,
+        ConstKind::Value(_) => 5,
+        ConstKind::Error(_) => 6,
+        ConstKind::Expr(_) => 7,
+    }
+}
+
+impl<I: Interner> hash::Hash for ConstKind<I> {
+    fn hash<H: hash::Hasher>(&self, state: &mut H) {
+        const_kind_discriminant(self).hash(state);
+        match self {
+            ConstKind::Param(p) => p.hash(state),
+            ConstKind::Infer(i) => i.hash(state),
+            ConstKind::Bound(d, b) => {
+                d.hash(state);
+                b.hash(state);
+            }
+            ConstKind::Placeholder(p) => p.hash(state),
+            ConstKind::Unevaluated(u) => u.hash(state),
+            ConstKind::Value(v) => v.hash(state),
+            ConstKind::Error(e) => e.hash(state),
+            ConstKind::Expr(e) => e.hash(state),
+        }
+    }
+}
+
+impl<CTX: HashStableContext, I: Interner> HashStable<CTX> for ConstKind<I>
+where
+    I::ParamConst: HashStable<CTX>,
+    I::InferConst: HashStable<CTX>,
+    I::BoundConst: HashStable<CTX>,
+    I::PlaceholderConst: HashStable<CTX>,
+    I::AliasConst: HashStable<CTX>,
+    I::ValueConst: HashStable<CTX>,
+    I::ErrorGuaranteed: HashStable<CTX>,
+    I::ExprConst: HashStable<CTX>,
+{
+    fn hash_stable(
+        &self,
+        hcx: &mut CTX,
+        hasher: &mut rustc_data_structures::stable_hasher::StableHasher,
+    ) {
+        const_kind_discriminant(self).hash_stable(hcx, hasher);
+        match self {
+            ConstKind::Param(p) => p.hash_stable(hcx, hasher),
+            ConstKind::Infer(i) => i.hash_stable(hcx, hasher),
+            ConstKind::Bound(d, b) => {
+                d.hash_stable(hcx, hasher);
+                b.hash_stable(hcx, hasher);
+            }
+            ConstKind::Placeholder(p) => p.hash_stable(hcx, hasher),
+            ConstKind::Unevaluated(u) => u.hash_stable(hcx, hasher),
+            ConstKind::Value(v) => v.hash_stable(hcx, hasher),
+            ConstKind::Error(e) => e.hash_stable(hcx, hasher),
+            ConstKind::Expr(e) => e.hash_stable(hcx, hasher),
+        }
+    }
+}
+
+impl<I: Interner, D: TyDecoder<I = I>> Decodable<D> for ConstKind<I>
+where
+    I::ParamConst: Decodable<D>,
+    I::InferConst: Decodable<D>,
+    I::BoundConst: Decodable<D>,
+    I::PlaceholderConst: Decodable<D>,
+    I::AliasConst: Decodable<D>,
+    I::ValueConst: Decodable<D>,
+    I::ErrorGuaranteed: Decodable<D>,
+    I::ExprConst: Decodable<D>,
+{
+    fn decode(d: &mut D) -> Self {
+        match Decoder::read_usize(d) {
+            0 => ConstKind::Param(Decodable::decode(d)),
+            1 => ConstKind::Infer(Decodable::decode(d)),
+            2 => ConstKind::Bound(Decodable::decode(d), Decodable::decode(d)),
+            3 => ConstKind::Placeholder(Decodable::decode(d)),
+            4 => ConstKind::Unevaluated(Decodable::decode(d)),
+            5 => ConstKind::Value(Decodable::decode(d)),
+            6 => ConstKind::Error(Decodable::decode(d)),
+            7 => ConstKind::Expr(Decodable::decode(d)),
+            _ => panic!(
+                "{}",
+                format!(
+                    "invalid enum variant tag while decoding `{}`, expected 0..{}",
+                    "ConstKind", 8,
+                )
+            ),
+        }
+    }
+}
+
+impl<I: Interner, E: TyEncoder<I = I>> Encodable<E> for ConstKind<I>
+where
+    I::ParamConst: Encodable<E>,
+    I::InferConst: Encodable<E>,
+    I::BoundConst: Encodable<E>,
+    I::PlaceholderConst: Encodable<E>,
+    I::AliasConst: Encodable<E>,
+    I::ValueConst: Encodable<E>,
+    I::ErrorGuaranteed: Encodable<E>,
+    I::ExprConst: Encodable<E>,
+{
+    fn encode(&self, e: &mut E) {
+        let disc = const_kind_discriminant(self);
+        match self {
+            ConstKind::Param(p) => e.emit_enum_variant(disc, |e| p.encode(e)),
+            ConstKind::Infer(i) => e.emit_enum_variant(disc, |e| i.encode(e)),
+            ConstKind::Bound(d, b) => e.emit_enum_variant(disc, |e| {
+                d.encode(e);
+                b.encode(e);
+            }),
+            ConstKind::Placeholder(p) => e.emit_enum_variant(disc, |e| p.encode(e)),
+            ConstKind::Unevaluated(u) => e.emit_enum_variant(disc, |e| u.encode(e)),
+            ConstKind::Value(v) => e.emit_enum_variant(disc, |e| v.encode(e)),
+            ConstKind::Error(er) => e.emit_enum_variant(disc, |e| er.encode(e)),
+            ConstKind::Expr(ex) => e.emit_enum_variant(disc, |e| ex.encode(e)),
+        }
+    }
+}
+
+impl<I: Interner> PartialOrd for ConstKind<I> {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+impl<I: Interner> Ord for ConstKind<I> {
+    fn cmp(&self, other: &Self) -> Ordering {
+        const_kind_discriminant(self)
+            .cmp(&const_kind_discriminant(other))
+            .then_with(|| match (self, other) {
+                (ConstKind::Param(p1), ConstKind::Param(p2)) => p1.cmp(p2),
+                (ConstKind::Infer(i1), ConstKind::Infer(i2)) => i1.cmp(i2),
+                (ConstKind::Bound(d1, b1), ConstKind::Bound(d2, b2)) => d1.cmp(d2).then_with(|| b1.cmp(b2)),
+                (ConstKind::Placeholder(p1), ConstKind::Placeholder(p2)) => p1.cmp(p2),
+                (ConstKind::Unevaluated(u1), ConstKind::Unevaluated(u2)) => u1.cmp(u2),
+                (ConstKind::Value(v1), ConstKind::Value(v2)) => v1.cmp(v2),
+                (ConstKind::Error(e1), ConstKind::Error(e2)) => e1.cmp(e2),
+                (ConstKind::Expr(e1), ConstKind::Expr(e2)) => e1.cmp(e2),
+                _ => {
+                    debug_assert!(false, "This branch must be unreachable, maybe the match is missing an arm? self = {self:?}, other = {other:?}");
+                    Ordering::Equal
+                }
+            })
+    }
+}
+
+impl<I: Interner> PartialEq for ConstKind<I> {
+    fn eq(&self, other: &Self) -> bool {
+        match (self, other) {
+            (Self::Param(l0), Self::Param(r0)) => l0 == r0,
+            (Self::Infer(l0), Self::Infer(r0)) => l0 == r0,
+            (Self::Bound(l0, l1), Self::Bound(r0, r1)) => l0 == r0 && l1 == r1,
+            (Self::Placeholder(l0), Self::Placeholder(r0)) => l0 == r0,
+            (Self::Unevaluated(l0), Self::Unevaluated(r0)) => l0 == r0,
+            (Self::Value(l0), Self::Value(r0)) => l0 == r0,
+            (Self::Error(l0), Self::Error(r0)) => l0 == r0,
+            (Self::Expr(l0), Self::Expr(r0)) => l0 == r0,
+            _ => false,
+        }
+    }
+}
+
+impl<I: Interner> Eq for ConstKind<I> {}
+
+impl<I: Interner> Clone for ConstKind<I> {
+    fn clone(&self) -> Self {
+        match self {
+            Self::Param(arg0) => Self::Param(arg0.clone()),
+            Self::Infer(arg0) => Self::Infer(arg0.clone()),
+            Self::Bound(arg0, arg1) => Self::Bound(arg0.clone(), arg1.clone()),
+            Self::Placeholder(arg0) => Self::Placeholder(arg0.clone()),
+            Self::Unevaluated(arg0) => Self::Unevaluated(arg0.clone()),
+            Self::Value(arg0) => Self::Value(arg0.clone()),
+            Self::Error(arg0) => Self::Error(arg0.clone()),
+            Self::Expr(arg0) => Self::Expr(arg0.clone()),
+        }
+    }
+}
+
 /// Representation of regions. Note that the NLL checker uses a distinct
 /// representation of regions. For this reason, it internally replaces all the
 /// regions with inference variables -- the index of the variable is then used