about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc/Cargo.toml5
-rw-r--r--compiler/rustc_apfloat/src/lib.rs2
-rw-r--r--compiler/rustc_arena/src/lib.rs85
-rw-r--r--compiler/rustc_arena/src/tests.rs11
-rw-r--r--compiler/rustc_ast/src/ast.rs58
-rw-r--r--compiler/rustc_ast/src/attr/mod.rs29
-rw-r--r--compiler/rustc_ast/src/lib.rs10
-rw-r--r--compiler/rustc_ast/src/mut_visit.rs38
-rw-r--r--compiler/rustc_ast/src/token.rs37
-rw-r--r--compiler/rustc_ast/src/tokenstream.rs41
-rw-r--r--compiler/rustc_ast/src/util/lev_distance.rs3
-rw-r--r--compiler/rustc_ast/src/visit.rs4
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs5
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs17
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs2
-rw-r--r--compiler/rustc_ast_passes/Cargo.toml2
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs19
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs37
-rw-r--r--compiler/rustc_ast_pretty/src/pprust.rs24
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/tests.rs7
-rw-r--r--compiler/rustc_attr/src/builtin.rs6
-rw-r--r--compiler/rustc_builtin_macros/src/cmdline_attrs.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/concat_idents.rs1
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/debug.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/generic/mod.rs15
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/mod.rs1
-rw-r--r--compiler/rustc_builtin_macros/src/format.rs49
-rw-r--r--compiler/rustc_builtin_macros/src/format_foreign.rs15
-rw-r--r--compiler/rustc_builtin_macros/src/global_asm.rs7
-rw-r--r--compiler/rustc_builtin_macros/src/lib.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/proc_macro_harness.rs8
-rw-r--r--compiler/rustc_builtin_macros/src/test.rs9
-rw-r--r--compiler/rustc_builtin_macros/src/test_harness.rs3
-rw-r--r--compiler/rustc_codegen_llvm/Cargo.toml2
-rw-r--r--compiler/rustc_codegen_llvm/src/back/lto.rs6
-rw-r--r--compiler/rustc_codegen_llvm/src/back/write.rs25
-rw-r--r--compiler/rustc_codegen_llvm/src/base.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/builder.rs23
-rw-r--r--compiler/rustc_codegen_llvm/src/consts.rs16
-rw-r--r--compiler/rustc_codegen_llvm/src/context.rs11
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs38
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/mod.rs6
-rw-r--r--compiler/rustc_codegen_llvm/src/declare.rs43
-rw-r--r--compiler/rustc_codegen_llvm/src/intrinsic.rs581
-rw-r--r--compiler/rustc_codegen_llvm/src/lib.rs9
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm/ffi.rs5
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm_util.rs1
-rw-r--r--compiler/rustc_codegen_llvm/src/type_.rs4
-rw-r--r--compiler/rustc_codegen_llvm/src/type_of.rs55
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs123
-rw-r--r--compiler/rustc_codegen_ssa/src/back/write.rs59
-rw-r--r--compiler/rustc_codegen_ssa/src/base.rs63
-rw-r--r--compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/glue.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/lib.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/analyze.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/block.rs58
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/intrinsic.rs596
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/mod.rs5
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/operand.rs50
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/place.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/rvalue.rs34
-rw-r--r--compiler/rustc_codegen_ssa/src/mono_item.rs8
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/backend.rs7
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/builder.rs14
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/declare.rs46
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/misc.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/mod.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/type_.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/write.rs6
-rw-r--r--compiler/rustc_data_structures/Cargo.toml7
-rw-r--r--compiler/rustc_data_structures/src/fingerprint.rs30
-rw-r--r--compiler/rustc_data_structures/src/graph/iterate/mod.rs15
-rw-r--r--compiler/rustc_data_structures/src/jobserver.rs52
-rw-r--r--compiler/rustc_data_structures/src/lib.rs23
-rw-r--r--compiler/rustc_data_structures/src/macros.rs12
-rw-r--r--compiler/rustc_data_structures/src/mini_map.rs61
-rw-r--r--compiler/rustc_data_structures/src/mini_set.rs41
-rw-r--r--compiler/rustc_data_structures/src/profiling.rs5
-rw-r--r--compiler/rustc_data_structures/src/sorted_map.rs4
-rw-r--r--compiler/rustc_data_structures/src/sync.rs4
-rw-r--r--compiler/rustc_data_structures/src/tagged_ptr/copy.rs2
-rw-r--r--compiler/rustc_data_structures/src/temp_dir.rs2
-rw-r--r--compiler/rustc_data_structures/src/unhash.rs29
-rw-r--r--compiler/rustc_driver/Cargo.toml4
-rw-r--r--compiler/rustc_driver/src/args.rs3
-rw-r--r--compiler/rustc_driver/src/lib.rs18
-rw-r--r--compiler/rustc_error_codes/src/error_codes.rs6
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0118.md27
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0224.md4
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0433.md16
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0607.md14
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0755.md28
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0756.md29
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0764.md22
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0769.md22
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0773.md38
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0774.md24
-rw-r--r--compiler/rustc_errors/src/emitter.rs28
-rw-r--r--compiler/rustc_errors/src/lib.rs69
-rw-r--r--compiler/rustc_errors/src/snippet.rs8
-rw-r--r--compiler/rustc_expand/src/base.rs3
-rw-r--r--compiler/rustc_expand/src/build.rs56
-rw-r--r--compiler/rustc_expand/src/config.rs13
-rw-r--r--compiler/rustc_expand/src/expand.rs52
-rw-r--r--compiler/rustc_expand/src/lib.rs6
-rw-r--r--compiler/rustc_expand/src/mbe/transcribe.rs4
-rw-r--r--compiler/rustc_expand/src/module.rs15
-rw-r--r--compiler/rustc_expand/src/parse/lexer/tests.rs252
-rw-r--r--compiler/rustc_expand/src/placeholders.rs32
-rw-r--r--compiler/rustc_expand/src/proc_macro.rs41
-rw-r--r--compiler/rustc_expand/src/proc_macro_server.rs18
-rw-r--r--compiler/rustc_feature/Cargo.toml1
-rw-r--r--compiler/rustc_feature/src/accepted.rs2
-rw-r--r--compiler/rustc_feature/src/active.rs13
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs15
-rw-r--r--compiler/rustc_feature/src/lib.rs4
-rw-r--r--compiler/rustc_graphviz/src/lib.rs31
-rw-r--r--compiler/rustc_hir/Cargo.toml1
-rw-r--r--compiler/rustc_hir/src/def.rs20
-rw-r--r--compiler/rustc_hir/src/definitions.rs1
-rw-r--r--compiler/rustc_hir/src/lang_items.rs16
-rw-r--r--compiler/rustc_hir/src/lib.rs2
-rw-r--r--compiler/rustc_hir/src/weak_lang_items.rs14
-rw-r--r--compiler/rustc_incremental/src/lib.rs2
-rw-r--r--compiler/rustc_index/Cargo.toml2
-rw-r--r--compiler/rustc_index/src/bit_set.rs54
-rw-r--r--compiler/rustc_infer/src/infer/canonical/canonicalizer.rs4
-rw-r--r--compiler/rustc_infer/src/infer/canonical/query_response.rs2
-rw-r--r--compiler/rustc_infer/src/infer/combine.rs258
-rw-r--r--compiler/rustc_infer/src/infer/equate.rs2
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs92
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs47
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs2
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs20
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs6
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs2
-rw-r--r--compiler/rustc_infer/src/infer/freshen.rs2
-rw-r--r--compiler/rustc_infer/src/infer/fudge.rs2
-rw-r--r--compiler/rustc_infer/src/infer/lattice.rs2
-rw-r--r--compiler/rustc_infer/src/infer/lub.rs2
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs12
-rw-r--r--compiler/rustc_infer/src/infer/nll_relate/mod.rs8
-rw-r--r--compiler/rustc_infer/src/infer/outlives/mod.rs3
-rw-r--r--compiler/rustc_infer/src/infer/outlives/obligations.rs2
-rw-r--r--compiler/rustc_infer/src/infer/outlives/verify.rs44
-rw-r--r--compiler/rustc_infer/src/infer/resolve.rs4
-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/lib.rs4
-rw-r--r--compiler/rustc_infer/src/traits/util.rs3
-rw-r--r--compiler/rustc_interface/Cargo.toml1
-rw-r--r--compiler/rustc_interface/src/lib.rs1
-rw-r--r--compiler/rustc_interface/src/passes.rs7
-rw-r--r--compiler/rustc_interface/src/tests.rs4
-rw-r--r--compiler/rustc_interface/src/util.rs22
-rw-r--r--compiler/rustc_lexer/Cargo.toml3
-rw-r--r--compiler/rustc_lexer/src/lib.rs16
-rw-r--r--compiler/rustc_lexer/src/tests.rs160
-rw-r--r--compiler/rustc_lint/src/array_into_iter.rs33
-rw-r--r--compiler/rustc_lint/src/builtin.rs585
-rw-r--r--compiler/rustc_lint/src/context.rs33
-rw-r--r--compiler/rustc_lint/src/internal.rs32
-rw-r--r--compiler/rustc_lint/src/lib.rs3
-rw-r--r--compiler/rustc_lint/src/non_ascii_idents.rs132
-rw-r--r--compiler/rustc_lint/src/nonstandard_style.rs53
-rw-r--r--compiler/rustc_lint/src/redundant_semicolon.rs15
-rw-r--r--compiler/rustc_lint/src/types.rs156
-rw-r--r--compiler/rustc_lint/src/unused.rs148
-rw-r--r--compiler/rustc_llvm/Cargo.toml16
-rw-r--r--compiler/rustc_llvm/build.rs322
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/.editorconfig6
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp226
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp70
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h115
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/Linker.cpp48
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp1655
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/README16
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp1721
-rw-r--r--compiler/rustc_llvm/src/lib.rs173
-rw-r--r--compiler/rustc_macros/src/lib.rs13
-rw-r--r--compiler/rustc_macros/src/query.rs31
-rw-r--r--compiler/rustc_macros/src/session_diagnostic.rs666
-rw-r--r--compiler/rustc_metadata/Cargo.toml1
-rw-r--r--compiler/rustc_metadata/src/lib.rs2
-rw-r--r--compiler/rustc_metadata/src/native_libs.rs4
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder.rs20
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs9
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs39
-rw-r--r--compiler/rustc_metadata/src/rmeta/mod.rs1
-rw-r--r--compiler/rustc_middle/Cargo.toml3
-rw-r--r--compiler/rustc_middle/src/infer/unify_key.rs1
-rw-r--r--compiler/rustc_middle/src/lib.rs9
-rw-r--r--compiler/rustc_middle/src/middle/cstore.rs2
-rw-r--r--compiler/rustc_middle/src/middle/lang_items.rs6
-rw-r--r--compiler/rustc_middle/src/middle/stability.rs3
-rw-r--r--compiler/rustc_middle/src/mir/abstract_const.rs20
-rw-r--r--compiler/rustc_middle/src/mir/interpret/allocation.rs12
-rw-r--r--compiler/rustc_middle/src/mir/interpret/error.rs12
-rw-r--r--compiler/rustc_middle/src/mir/interpret/mod.rs61
-rw-r--r--compiler/rustc_middle/src/mir/interpret/queries.rs16
-rw-r--r--compiler/rustc_middle/src/mir/interpret/value.rs9
-rw-r--r--compiler/rustc_middle/src/mir/mod.rs88
-rw-r--r--compiler/rustc_middle/src/mir/mono.rs39
-rw-r--r--compiler/rustc_middle/src/mir/predecessors.rs2
-rw-r--r--compiler/rustc_middle/src/mir/tcx.rs4
-rw-r--r--compiler/rustc_middle/src/mir/terminator/mod.rs2
-rw-r--r--compiler/rustc_middle/src/mir/type_foldable.rs2
-rw-r--r--compiler/rustc_middle/src/mir/visit.rs2
-rw-r--r--compiler/rustc_middle/src/query/mod.rs117
-rw-r--r--compiler/rustc_middle/src/traits/chalk.rs83
-rw-r--r--compiler/rustc_middle/src/traits/mod.rs9
-rw-r--r--compiler/rustc_middle/src/traits/query.rs68
-rw-r--r--compiler/rustc_middle/src/ty/_match.rs2
-rw-r--r--compiler/rustc_middle/src/ty/adjustment.rs4
-rw-r--r--compiler/rustc_middle/src/ty/cast.rs2
-rw-r--r--compiler/rustc_middle/src/ty/codec.rs24
-rw-r--r--compiler/rustc_middle/src/ty/context.rs81
-rw-r--r--compiler/rustc_middle/src/ty/diagnostics.rs74
-rw-r--r--compiler/rustc_middle/src/ty/error.rs52
-rw-r--r--compiler/rustc_middle/src/ty/fast_reject.rs2
-rw-r--r--compiler/rustc_middle/src/ty/flags.rs5
-rw-r--r--compiler/rustc_middle/src/ty/fold.rs25
-rw-r--r--compiler/rustc_middle/src/ty/inhabitedness/mod.rs2
-rw-r--r--compiler/rustc_middle/src/ty/instance.rs60
-rw-r--r--compiler/rustc_middle/src/ty/layout.rs38
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs73
-rw-r--r--compiler/rustc_middle/src/ty/outlives.rs35
-rw-r--r--compiler/rustc_middle/src/ty/print/mod.rs34
-rw-r--r--compiler/rustc_middle/src/ty/print/obsolete.rs251
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs200
-rw-r--r--compiler/rustc_middle/src/ty/query/keys.rs16
-rw-r--r--compiler/rustc_middle/src/ty/query/mod.rs2
-rw-r--r--compiler/rustc_middle/src/ty/query/on_disk_cache.rs6
-rw-r--r--compiler/rustc_middle/src/ty/relate.rs21
-rw-r--r--compiler/rustc_middle/src/ty/structural_impls.rs50
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs181
-rw-r--r--compiler/rustc_middle/src/ty/subst.rs2
-rw-r--r--compiler/rustc_middle/src/ty/trait_def.rs2
-rw-r--r--compiler/rustc_middle/src/ty/util.rs50
-rw-r--r--compiler/rustc_middle/src/ty/walk.rs44
-rw-r--r--compiler/rustc_mir/Cargo.toml3
-rw-r--r--compiler/rustc_mir/src/borrow_check/def_use.rs3
-rw-r--r--compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs54
-rw-r--r--compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs6
-rw-r--r--compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs254
-rw-r--r--compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs14
-rw-r--r--compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs13
-rw-r--r--compiler/rustc_mir/src/borrow_check/diagnostics/outlives_suggestion.rs7
-rw-r--r--compiler/rustc_mir/src/borrow_check/diagnostics/region_errors.rs12
-rw-r--r--compiler/rustc_mir/src/borrow_check/diagnostics/region_name.rs2
-rw-r--r--compiler/rustc_mir/src/borrow_check/member_constraints.rs2
-rw-r--r--compiler/rustc_mir/src/borrow_check/mod.rs17
-rw-r--r--compiler/rustc_mir/src/borrow_check/place_ext.rs2
-rw-r--r--compiler/rustc_mir/src/borrow_check/places_conflict.rs4
-rw-r--r--compiler/rustc_mir/src/borrow_check/prefixes.rs2
-rw-r--r--compiler/rustc_mir/src/borrow_check/region_infer/values.rs4
-rw-r--r--compiler/rustc_mir/src/borrow_check/type_check/mod.rs38
-rw-r--r--compiler/rustc_mir/src/borrow_check/universal_regions.rs4
-rw-r--r--compiler/rustc_mir/src/const_eval/eval_queries.rs145
-rw-r--r--compiler/rustc_mir/src/const_eval/machine.rs73
-rw-r--r--compiler/rustc_mir/src/const_eval/mod.rs2
-rw-r--r--compiler/rustc_mir/src/dataflow/drop_flag_effects.rs2
-rw-r--r--compiler/rustc_mir/src/dataflow/framework/cursor.rs41
-rw-r--r--compiler/rustc_mir/src/dataflow/framework/direction.rs30
-rw-r--r--compiler/rustc_mir/src/dataflow/framework/engine.rs137
-rw-r--r--compiler/rustc_mir/src/dataflow/framework/fmt.rs172
-rw-r--r--compiler/rustc_mir/src/dataflow/framework/graphviz.rs561
-rw-r--r--compiler/rustc_mir/src/dataflow/framework/lattice.rs230
-rw-r--r--compiler/rustc_mir/src/dataflow/framework/mod.rs135
-rw-r--r--compiler/rustc_mir/src/dataflow/framework/tests.rs29
-rw-r--r--compiler/rustc_mir/src/dataflow/framework/visitor.rs13
-rw-r--r--compiler/rustc_mir/src/dataflow/impls/borrowed_locals.rs17
-rw-r--r--compiler/rustc_mir/src/dataflow/impls/borrows.rs26
-rw-r--r--compiler/rustc_mir/src/dataflow/impls/init_locals.rs19
-rw-r--r--compiler/rustc_mir/src/dataflow/impls/liveness.rs18
-rw-r--r--compiler/rustc_mir/src/dataflow/impls/mod.rs87
-rw-r--r--compiler/rustc_mir/src/dataflow/impls/storage_liveness.rs33
-rw-r--r--compiler/rustc_mir/src/dataflow/mod.rs6
-rw-r--r--compiler/rustc_mir/src/dataflow/move_paths/builder.rs16
-rw-r--r--compiler/rustc_mir/src/interpret/cast.rs18
-rw-r--r--compiler/rustc_mir/src/interpret/eval_context.rs46
-rw-r--r--compiler/rustc_mir/src/interpret/intern.rs6
-rw-r--r--compiler/rustc_mir/src/interpret/intrinsics.rs50
-rw-r--r--compiler/rustc_mir/src/interpret/intrinsics/type_name.rs2
-rw-r--r--compiler/rustc_mir/src/interpret/memory.rs14
-rw-r--r--compiler/rustc_mir/src/interpret/operand.rs18
-rw-r--r--compiler/rustc_mir/src/interpret/operator.rs4
-rw-r--r--compiler/rustc_mir/src/interpret/place.rs20
-rw-r--r--compiler/rustc_mir/src/interpret/terminator.rs13
-rw-r--r--compiler/rustc_mir/src/interpret/util.rs4
-rw-r--r--compiler/rustc_mir/src/interpret/validity.rs76
-rw-r--r--compiler/rustc_mir/src/interpret/visitor.rs2
-rw-r--r--compiler/rustc_mir/src/lib.rs11
-rw-r--r--compiler/rustc_mir/src/monomorphize/collector.rs109
-rw-r--r--compiler/rustc_mir/src/monomorphize/partitioning/default.rs32
-rw-r--r--compiler/rustc_mir/src/monomorphize/partitioning/merging.rs19
-rw-r--r--compiler/rustc_mir/src/monomorphize/partitioning/mod.rs48
-rw-r--r--compiler/rustc_mir/src/monomorphize/polymorphize.rs4
-rw-r--r--compiler/rustc_mir/src/shim.rs66
-rw-r--r--compiler/rustc_mir/src/transform/add_retag.rs2
-rw-r--r--compiler/rustc_mir/src/transform/check_const_item_mutation.rs114
-rw-r--r--compiler/rustc_mir/src/transform/check_consts/mod.rs48
-rw-r--r--compiler/rustc_mir/src/transform/check_consts/ops.rs384
-rw-r--r--compiler/rustc_mir/src/transform/check_consts/post_drop_elaboration.rs16
-rw-r--r--compiler/rustc_mir/src/transform/check_consts/qualifs.rs2
-rw-r--r--compiler/rustc_mir/src/transform/check_consts/resolver.rs18
-rw-r--r--compiler/rustc_mir/src/transform/check_consts/validation.rs376
-rw-r--r--compiler/rustc_mir/src/transform/check_unsafety.rs8
-rw-r--r--compiler/rustc_mir/src/transform/const_prop.rs2
-rw-r--r--compiler/rustc_mir/src/transform/dest_prop.rs1057
-rw-r--r--compiler/rustc_mir/src/transform/early_otherwise_branch.rs339
-rw-r--r--compiler/rustc_mir/src/transform/elaborate_drops.rs3
-rw-r--r--compiler/rustc_mir/src/transform/generator.rs13
-rw-r--r--compiler/rustc_mir/src/transform/inline.rs74
-rw-r--r--compiler/rustc_mir/src/transform/instcombine.rs171
-rw-r--r--compiler/rustc_mir/src/transform/instrument_coverage.rs300
-rw-r--r--compiler/rustc_mir/src/transform/mod.rs17
-rw-r--r--compiler/rustc_mir/src/transform/nrvo.rs15
-rw-r--r--compiler/rustc_mir/src/transform/promote_consts.rs100
-rw-r--r--compiler/rustc_mir/src/transform/qualify_min_const_fn.rs18
-rw-r--r--compiler/rustc_mir/src/transform/remove_noop_landing_pads.rs19
-rw-r--r--compiler/rustc_mir/src/transform/remove_unneeded_drops.rs58
-rw-r--r--compiler/rustc_mir/src/transform/rustc_peek.rs21
-rw-r--r--compiler/rustc_mir/src/transform/simplify.rs3
-rw-r--r--compiler/rustc_mir/src/transform/simplify_comparison_integral.rs43
-rw-r--r--compiler/rustc_mir/src/transform/simplify_try.rs62
-rw-r--r--compiler/rustc_mir/src/transform/validate.rs30
-rw-r--r--compiler/rustc_mir/src/util/alignment.rs2
-rw-r--r--compiler/rustc_mir/src/util/borrowck_errors.rs2
-rw-r--r--compiler/rustc_mir/src/util/elaborate_drops.rs2
-rw-r--r--compiler/rustc_mir/src/util/find_self_call.rs36
-rw-r--r--compiler/rustc_mir/src/util/graphviz.rs29
-rw-r--r--compiler/rustc_mir/src/util/mod.rs3
-rw-r--r--compiler/rustc_mir/src/util/pretty.rs24
-rw-r--r--compiler/rustc_mir/src/util/spanview.rs672
-rw-r--r--compiler/rustc_mir_build/src/build/block.rs3
-rw-r--r--compiler/rustc_mir_build/src/build/expr/as_constant.rs2
-rw-r--r--compiler/rustc_mir_build/src/build/expr/as_place.rs4
-rw-r--r--compiler/rustc_mir_build/src/build/expr/as_temp.rs3
-rw-r--r--compiler/rustc_mir_build/src/build/expr/into.rs2
-rw-r--r--compiler/rustc_mir_build/src/build/matches/mod.rs4
-rw-r--r--compiler/rustc_mir_build/src/build/matches/simplify.rs2
-rw-r--r--compiler/rustc_mir_build/src/build/matches/test.rs8
-rw-r--r--compiler/rustc_mir_build/src/build/matches/util.rs4
-rw-r--r--compiler/rustc_mir_build/src/build/mod.rs10
-rw-r--r--compiler/rustc_mir_build/src/lib.rs3
-rw-r--r--compiler/rustc_mir_build/src/lints.rs8
-rw-r--r--compiler/rustc_mir_build/src/thir/constant.rs12
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/expr.rs54
-rw-r--r--compiler/rustc_mir_build/src/thir/mod.rs4
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/_match.rs277
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs10
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs13
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/mod.rs26
-rw-r--r--compiler/rustc_mir_build/src/thir/util.rs2
-rw-r--r--compiler/rustc_parse/src/lexer/mod.rs160
-rw-r--r--compiler/rustc_parse/src/lexer/tokentrees.rs57
-rw-r--r--compiler/rustc_parse/src/lexer/unicode_chars.rs4
-rw-r--r--compiler/rustc_parse/src/lib.rs57
-rw-r--r--compiler/rustc_parse/src/parser/attr.rs2
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs57
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs2
-rw-r--r--compiler/rustc_parse/src/parser/item.rs84
-rw-r--r--compiler/rustc_parse/src/parser/mod.rs57
-rw-r--r--compiler/rustc_parse/src/parser/nonterminal.rs75
-rw-r--r--compiler/rustc_parse/src/parser/path.rs9
-rw-r--r--compiler/rustc_parse/src/parser/stmt.rs37
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs32
-rw-r--r--compiler/rustc_parse_format/src/lib.rs13
-rw-r--r--compiler/rustc_passes/src/check_attr.rs209
-rw-r--r--compiler/rustc_passes/src/dead.rs8
-rw-r--r--compiler/rustc_passes/src/diagnostic_items.rs14
-rw-r--r--compiler/rustc_passes/src/intrinsicck.rs12
-rw-r--r--compiler/rustc_passes/src/lib.rs2
-rw-r--r--compiler/rustc_passes/src/liveness.rs2
-rw-r--r--compiler/rustc_passes/src/stability.rs73
-rw-r--r--compiler/rustc_plugin_impl/src/lib.rs2
-rw-r--r--compiler/rustc_privacy/src/lib.rs16
-rw-r--r--compiler/rustc_query_system/Cargo.toml2
-rw-r--r--compiler/rustc_resolve/src/build_reduced_graph.rs21
-rw-r--r--compiler/rustc_resolve/src/check_unused.rs2
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs20
-rw-r--r--compiler/rustc_resolve/src/late.rs48
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs82
-rw-r--r--compiler/rustc_resolve/src/lib.rs66
-rw-r--r--compiler/rustc_resolve/src/macros.rs22
-rw-r--r--compiler/rustc_save_analysis/src/lib.rs58
-rw-r--r--compiler/rustc_save_analysis/src/sig.rs2
-rw-r--r--compiler/rustc_serialize/src/lib.rs2
-rw-r--r--compiler/rustc_session/src/config.rs96
-rw-r--r--compiler/rustc_session/src/lib.rs1
-rw-r--r--compiler/rustc_session/src/lint.rs58
-rw-r--r--compiler/rustc_session/src/lint/builtin.rs2045
-rw-r--r--compiler/rustc_session/src/options.rs67
-rw-r--r--compiler/rustc_session/src/search_paths.rs20
-rw-r--r--compiler/rustc_session/src/session.rs84
-rw-r--r--compiler/rustc_span/src/lib.rs178
-rw-r--r--compiler/rustc_span/src/source_map.rs9
-rw-r--r--compiler/rustc_span/src/symbol.rs7
-rw-r--r--compiler/rustc_symbol_mangling/src/legacy.rs6
-rw-r--r--compiler/rustc_symbol_mangling/src/lib.rs2
-rw-r--r--compiler/rustc_symbol_mangling/src/v0.rs13
-rw-r--r--compiler/rustc_target/src/lib.rs2
-rw-r--r--compiler/rustc_target/src/spec/avr_gnu_base.rs2
-rw-r--r--compiler/rustc_target/src/spec/mod.rs1
-rw-r--r--compiler/rustc_target/src/spec/riscv32gc_unknown_linux_gnu.rs25
-rw-r--r--compiler/rustc_target/src/spec/wasm32_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/wasm32_wasi.rs2
-rw-r--r--compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs2
-rw-r--r--compiler/rustc_trait_selection/src/autoderef.rs7
-rw-r--r--compiler/rustc_trait_selection/src/lib.rs4
-rw-r--r--compiler/rustc_trait_selection/src/opaque_types.rs11
-rw-r--r--compiler/rustc_trait_selection/src/traits/auto_trait.rs8
-rw-r--r--compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs142
-rw-r--r--compiler/rustc_trait_selection/src/traits/coherence.rs8
-rw-r--r--compiler/rustc_trait_selection/src/traits/const_evaluatable.rs470
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs49
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs33
-rw-r--r--compiler/rustc_trait_selection/src/traits/fulfill.rs83
-rw-r--r--compiler/rustc_trait_selection/src/traits/misc.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs31
-rw-r--r--compiler/rustc_trait_selection/src/traits/object_safety.rs14
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs10
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/normalize.rs13
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs14
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs12
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs292
-rw-r--r--compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs6
-rw-r--r--compiler/rustc_trait_selection/src/traits/structural_match.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/wf.rs33
-rw-r--r--compiler/rustc_traits/Cargo.toml5
-rw-r--r--compiler/rustc_traits/src/chalk/db.rs401
-rw-r--r--compiler/rustc_traits/src/chalk/lowering.rs656
-rw-r--r--compiler/rustc_traits/src/chalk/mod.rs156
-rw-r--r--compiler/rustc_traits/src/dropck_outlives.rs4
-rw-r--r--compiler/rustc_traits/src/implied_outlives_bounds.rs3
-rw-r--r--compiler/rustc_traits/src/lib.rs1
-rw-r--r--compiler/rustc_traits/src/normalize_erasing_regions.rs3
-rw-r--r--compiler/rustc_ty/src/instance.rs8
-rw-r--r--compiler/rustc_ty/src/lib.rs3
-rw-r--r--compiler/rustc_ty/src/needs_drop.rs4
-rw-r--r--compiler/rustc_ty/src/ty.rs152
-rw-r--r--compiler/rustc_typeck/Cargo.toml1
-rw-r--r--compiler/rustc_typeck/src/astconv/generics.rs85
-rw-r--r--compiler/rustc_typeck/src/astconv/mod.rs84
-rw-r--r--compiler/rustc_typeck/src/check/_match.rs86
-rw-r--r--compiler/rustc_typeck/src/check/autoderef.rs21
-rw-r--r--compiler/rustc_typeck/src/check/callee.rs16
-rw-r--r--compiler/rustc_typeck/src/check/cast.rs18
-rw-r--r--compiler/rustc_typeck/src/check/check.rs1344
-rw-r--r--compiler/rustc_typeck/src/check/closure.rs6
-rw-r--r--compiler/rustc_typeck/src/check/coercion.rs75
-rw-r--r--compiler/rustc_typeck/src/check/compare_method.rs43
-rw-r--r--compiler/rustc_typeck/src/check/demand.rs41
-rw-r--r--compiler/rustc_typeck/src/check/diverges.rs78
-rw-r--r--compiler/rustc_typeck/src/check/dropck.rs2
-rw-r--r--compiler/rustc_typeck/src/check/expectation.rs117
-rw-r--r--compiler/rustc_typeck/src/check/expr.rs287
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt.rs3200
-rw-r--r--compiler/rustc_typeck/src/check/gather_locals.rs120
-rw-r--r--compiler/rustc_typeck/src/check/inherited.rs167
-rw-r--r--compiler/rustc_typeck/src/check/intrinsic.rs47
-rw-r--r--compiler/rustc_typeck/src/check/method/confirm.rs7
-rw-r--r--compiler/rustc_typeck/src/check/method/mod.rs4
-rw-r--r--compiler/rustc_typeck/src/check/method/probe.rs35
-rw-r--r--compiler/rustc_typeck/src/check/method/suggest.rs28
-rw-r--r--compiler/rustc_typeck/src/check/mod.rs5043
-rw-r--r--compiler/rustc_typeck/src/check/op.rs42
-rw-r--r--compiler/rustc_typeck/src/check/pat.rs253
-rw-r--r--compiler/rustc_typeck/src/check/place_op.rs39
-rw-r--r--compiler/rustc_typeck/src/check/regionck.rs6
-rw-r--r--compiler/rustc_typeck/src/check/upvar.rs4
-rw-r--r--compiler/rustc_typeck/src/check/wfcheck.rs18
-rw-r--r--compiler/rustc_typeck/src/check/writeback.rs4
-rw-r--r--compiler/rustc_typeck/src/coherence/builtin.rs35
-rw-r--r--compiler/rustc_typeck/src/coherence/inherent_impls.rs31
-rw-r--r--compiler/rustc_typeck/src/coherence/mod.rs2
-rw-r--r--compiler/rustc_typeck/src/coherence/orphan.rs14
-rw-r--r--compiler/rustc_typeck/src/collect.rs189
-rw-r--r--compiler/rustc_typeck/src/collect/type_of.rs15
-rw-r--r--compiler/rustc_typeck/src/constrained_generic_params.rs2
-rw-r--r--compiler/rustc_typeck/src/errors.rs199
-rw-r--r--compiler/rustc_typeck/src/expr_use_visitor.rs2
-rw-r--r--compiler/rustc_typeck/src/impl_wf_check.rs18
-rw-r--r--compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs3
-rw-r--r--compiler/rustc_typeck/src/lib.rs7
-rw-r--r--compiler/rustc_typeck/src/mem_categorization.rs8
-rw-r--r--compiler/rustc_typeck/src/outlives/explicit.rs3
-rw-r--r--compiler/rustc_typeck/src/outlives/implicit_infer.rs2
-rw-r--r--compiler/rustc_typeck/src/variance/constraints.rs4
-rw-r--r--compiler/rustc_typeck/src/variance/solve.rs2
494 files changed, 26963 insertions, 11585 deletions
diff --git a/compiler/rustc/Cargo.toml b/compiler/rustc/Cargo.toml
index 4340d50f845..6e6c0c71a1f 100644
--- a/compiler/rustc/Cargo.toml
+++ b/compiler/rustc/Cargo.toml
@@ -4,10 +4,6 @@ name = "rustc-main"
 version = "0.0.0"
 edition = '2018'
 
-[[bin]]
-name = "rustc_binary"
-path = "src/main.rs"
-
 [dependencies]
 rustc_driver = { path = "../rustc_driver" }
 
@@ -23,3 +19,4 @@ features = ['unprefixed_malloc_on_supported_platforms']
 [features]
 jemalloc = ['jemalloc-sys']
 llvm = ['rustc_driver/llvm']
+max_level_info = ['rustc_driver/max_level_info']
diff --git a/compiler/rustc_apfloat/src/lib.rs b/compiler/rustc_apfloat/src/lib.rs
index ba3adc4a135..4a845fcb691 100644
--- a/compiler/rustc_apfloat/src/lib.rs
+++ b/compiler/rustc_apfloat/src/lib.rs
@@ -30,7 +30,7 @@
 //!
 //! This API is completely unstable and subject to change.
 
-#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
+#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![no_std]
 #![forbid(unsafe_code)]
 #![feature(nll)]
diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs
index 5e6a0340d12..b4bf31b1aba 100644
--- a/compiler/rustc_arena/src/lib.rs
+++ b/compiler/rustc_arena/src/lib.rs
@@ -8,31 +8,26 @@
 //! This crate implements several kinds of arena.
 
 #![doc(
-    html_root_url = "https://doc.rust-lang.org/nightly/",
+    html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/",
     test(no_crate_inject, attr(deny(warnings)))
 )]
-#![feature(core_intrinsics)]
 #![feature(dropck_eyepatch)]
-#![feature(raw_vec_internals)]
+#![feature(new_uninit)]
+#![feature(maybe_uninit_slice)]
 #![cfg_attr(test, feature(test))]
 #![allow(deprecated)]
 
-extern crate alloc;
-
 use rustc_data_structures::cold_path;
 use smallvec::SmallVec;
 
 use std::alloc::Layout;
 use std::cell::{Cell, RefCell};
 use std::cmp;
-use std::intrinsics;
 use std::marker::{PhantomData, Send};
-use std::mem;
+use std::mem::{self, MaybeUninit};
 use std::ptr;
 use std::slice;
 
-use alloc::raw_vec::RawVec;
-
 /// An arena that can hold objects of only one type.
 pub struct TypedArena<T> {
     /// A pointer to the next object to be allocated.
@@ -52,7 +47,7 @@ pub struct TypedArena<T> {
 
 struct TypedArenaChunk<T> {
     /// The raw storage for the arena chunk.
-    storage: RawVec<T>,
+    storage: Box<[MaybeUninit<T>]>,
     /// The number of valid entries in the chunk.
     entries: usize,
 }
@@ -60,7 +55,7 @@ struct TypedArenaChunk<T> {
 impl<T> TypedArenaChunk<T> {
     #[inline]
     unsafe fn new(capacity: usize) -> TypedArenaChunk<T> {
-        TypedArenaChunk { storage: RawVec::with_capacity(capacity), entries: 0 }
+        TypedArenaChunk { storage: Box::new_uninit_slice(capacity), entries: 0 }
     }
 
     /// Destroys this arena chunk.
@@ -69,30 +64,25 @@ impl<T> TypedArenaChunk<T> {
         // The branch on needs_drop() is an -O1 performance optimization.
         // Without the branch, dropping TypedArena<u8> takes linear time.
         if mem::needs_drop::<T>() {
-            let mut start = self.start();
-            // Destroy all allocated objects.
-            for _ in 0..len {
-                ptr::drop_in_place(start);
-                start = start.offset(1);
-            }
+            ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(&mut self.storage[..len]));
         }
     }
 
     // Returns a pointer to the first allocated object.
     #[inline]
-    fn start(&self) -> *mut T {
-        self.storage.ptr()
+    fn start(&mut self) -> *mut T {
+        MaybeUninit::slice_as_mut_ptr(&mut self.storage)
     }
 
     // Returns a pointer to the end of the allocated space.
     #[inline]
-    fn end(&self) -> *mut T {
+    fn end(&mut self) -> *mut T {
         unsafe {
             if mem::size_of::<T>() == 0 {
                 // A pointer as large as possible for zero-sized elements.
                 !0 as *mut T
             } else {
-                self.start().add(self.storage.capacity())
+                self.start().add(self.storage.len())
             }
         }
     }
@@ -130,7 +120,7 @@ impl<T> TypedArena<T> {
 
         unsafe {
             if mem::size_of::<T>() == 0 {
-                self.ptr.set(intrinsics::arith_offset(self.ptr.get() as *mut u8, 1) as *mut T);
+                self.ptr.set((self.ptr.get() as *mut u8).wrapping_offset(1) as *mut T);
                 let ptr = mem::align_of::<T>() as *mut T;
                 // Don't drop the object. This `write` is equivalent to `forget`.
                 ptr::write(ptr, object);
@@ -226,10 +216,10 @@ impl<T> TypedArena<T> {
                 let used_bytes = self.ptr.get() as usize - last_chunk.start() as usize;
                 last_chunk.entries = used_bytes / mem::size_of::<T>();
 
-                // If the previous chunk's capacity is less than HUGE_PAGE
+                // If the previous chunk's len is less than HUGE_PAGE
                 // bytes, then this chunk will be least double the previous
                 // chunk's size.
-                new_cap = last_chunk.storage.capacity();
+                new_cap = last_chunk.storage.len();
                 if new_cap < HUGE_PAGE / elem_size {
                     new_cap = new_cap.checked_mul(2).unwrap();
                 }
@@ -239,7 +229,7 @@ impl<T> TypedArena<T> {
             // Also ensure that this chunk can fit `additional`.
             new_cap = cmp::max(additional, new_cap);
 
-            let chunk = TypedArenaChunk::<T>::new(new_cap);
+            let mut chunk = TypedArenaChunk::<T>::new(new_cap);
             self.ptr.set(chunk.start());
             self.end.set(chunk.end());
             chunks.push(chunk);
@@ -301,7 +291,7 @@ unsafe impl<#[may_dangle] T> Drop for TypedArena<T> {
                     chunk.destroy(chunk.entries);
                 }
             }
-            // RawVec handles deallocation of `last_chunk` and `self.chunks`.
+            // Box handles deallocation of `last_chunk` and `self.chunks`.
         }
     }
 }
@@ -309,11 +299,13 @@ unsafe impl<#[may_dangle] T> Drop for TypedArena<T> {
 unsafe impl<T: Send> Send for TypedArena<T> {}
 
 pub struct DroplessArena {
-    /// A pointer to the next object to be allocated.
-    ptr: Cell<*mut u8>,
+    /// A pointer to the start of the free space.
+    start: Cell<*mut u8>,
 
-    /// A pointer to the end of the allocated area. When this pointer is
-    /// reached, a new chunk is allocated.
+    /// A pointer to the end of free space.
+    ///
+    /// The allocation proceeds from the end of the chunk towards the start.
+    /// When this pointer crosses the start pointer, a new chunk is allocated.
     end: Cell<*mut u8>,
 
     /// A vector of arena chunks.
@@ -326,7 +318,7 @@ impl Default for DroplessArena {
     #[inline]
     fn default() -> DroplessArena {
         DroplessArena {
-            ptr: Cell::new(ptr::null_mut()),
+            start: Cell::new(ptr::null_mut()),
             end: Cell::new(ptr::null_mut()),
             chunks: Default::default(),
         }
@@ -344,10 +336,10 @@ impl DroplessArena {
                 // There is no need to update `last_chunk.entries` because that
                 // field isn't used by `DroplessArena`.
 
-                // If the previous chunk's capacity is less than HUGE_PAGE
+                // If the previous chunk's len is less than HUGE_PAGE
                 // bytes, then this chunk will be least double the previous
                 // chunk's size.
-                new_cap = last_chunk.storage.capacity();
+                new_cap = last_chunk.storage.len();
                 if new_cap < HUGE_PAGE {
                     new_cap = new_cap.checked_mul(2).unwrap();
                 }
@@ -357,8 +349,8 @@ impl DroplessArena {
             // Also ensure that this chunk can fit `additional`.
             new_cap = cmp::max(additional, new_cap);
 
-            let chunk = TypedArenaChunk::<u8>::new(new_cap);
-            self.ptr.set(chunk.start());
+            let mut chunk = TypedArenaChunk::<u8>::new(new_cap);
+            self.start.set(chunk.start());
             self.end.set(chunk.end());
             chunks.push(chunk);
         }
@@ -369,24 +361,17 @@ impl DroplessArena {
     /// request.
     #[inline]
     fn alloc_raw_without_grow(&self, layout: Layout) -> Option<*mut u8> {
-        let ptr = self.ptr.get() as usize;
+        let start = self.start.get() as usize;
         let end = self.end.get() as usize;
+
         let align = layout.align();
         let bytes = layout.size();
-        // The allocation request fits into the current chunk iff:
-        //
-        // let aligned = align_to(ptr, align);
-        // ptr <= aligned && aligned + bytes <= end
-        //
-        // Except that we work with fixed width integers and need to be careful
-        // about potential overflow in the calcuation. If the overflow does
-        // happen, then we definitely don't have enough free and need to grow
-        // the arena.
-        let aligned = ptr.checked_add(align - 1)? & !(align - 1);
-        let new_ptr = aligned.checked_add(bytes)?;
-        if new_ptr <= end {
-            self.ptr.set(new_ptr as *mut u8);
-            Some(aligned as *mut u8)
+
+        let new_end = end.checked_sub(bytes)? & !(align - 1);
+        if start <= new_end {
+            let new_end = new_end as *mut u8;
+            self.end.set(new_end);
+            Some(new_end)
         } else {
             None
         }
diff --git a/compiler/rustc_arena/src/tests.rs b/compiler/rustc_arena/src/tests.rs
index 8e63bdf5458..e8a1f2db1a1 100644
--- a/compiler/rustc_arena/src/tests.rs
+++ b/compiler/rustc_arena/src/tests.rs
@@ -121,6 +121,17 @@ pub fn bench_typed_arena_clear(b: &mut Bencher) {
     })
 }
 
+#[bench]
+pub fn bench_typed_arena_clear_100(b: &mut Bencher) {
+    let mut arena = TypedArena::default();
+    b.iter(|| {
+        for _ in 0..100 {
+            arena.alloc(Point { x: 1, y: 2, z: 3 });
+        }
+        arena.clear();
+    })
+}
+
 // Drop tests
 
 struct DropCounter<'a> {
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 127a53cad2b..95abf552915 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -96,6 +96,7 @@ pub struct Path {
     /// The segments in the path: the things separated by `::`.
     /// Global paths begin with `kw::PathRoot`.
     pub segments: Vec<PathSegment>,
+    pub tokens: Option<TokenStream>,
 }
 
 impl PartialEq<Symbol> for Path {
@@ -117,7 +118,7 @@ impl Path {
     // Convert a span and an identifier to the corresponding
     // one-segment path.
     pub fn from_ident(ident: Ident) -> Path {
-        Path { segments: vec![PathSegment::from_ident(ident)], span: ident.span }
+        Path { segments: vec![PathSegment::from_ident(ident)], span: ident.span, tokens: None }
     }
 
     pub fn is_global(&self) -> bool {
@@ -540,6 +541,7 @@ pub struct Block {
     /// Distinguishes between `unsafe { ... }` and `{ ... }`.
     pub rules: BlockCheckMode,
     pub span: Span,
+    pub tokens: Option<TokenStream>,
 }
 
 /// A match pattern.
@@ -586,7 +588,7 @@ impl Pat {
             _ => return None,
         };
 
-        Some(P(Ty { kind, id: self.id, span: self.span }))
+        Some(P(Ty { kind, id: self.id, span: self.span, tokens: None }))
     }
 
     /// Walk top-down and call `it` in each place where a pattern occurs
@@ -916,15 +918,20 @@ pub struct Stmt {
     pub id: NodeId,
     pub kind: StmtKind,
     pub span: Span,
+    pub tokens: Option<TokenStream>,
 }
 
 impl Stmt {
     pub fn add_trailing_semicolon(mut self) -> Self {
         self.kind = match self.kind {
             StmtKind::Expr(expr) => StmtKind::Semi(expr),
-            StmtKind::MacCall(mac) => StmtKind::MacCall(
-                mac.map(|(mac, _style, attrs)| (mac, MacStmtStyle::Semicolon, attrs)),
-            ),
+            StmtKind::MacCall(mac) => {
+                StmtKind::MacCall(mac.map(|MacCallStmt { mac, style: _, attrs }| MacCallStmt {
+                    mac,
+                    style: MacStmtStyle::Semicolon,
+                    attrs,
+                }))
+            }
             kind => kind,
         };
         self
@@ -958,7 +965,14 @@ pub enum StmtKind {
     /// Just a trailing semi-colon.
     Empty,
     /// Macro.
-    MacCall(P<(MacCall, MacStmtStyle, AttrVec)>),
+    MacCall(P<MacCallStmt>),
+}
+
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct MacCallStmt {
+    pub mac: MacCall,
+    pub style: MacStmtStyle,
+    pub attrs: AttrVec,
 }
 
 #[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug)]
@@ -1057,7 +1071,7 @@ pub struct Expr {
 
 // `Expr` is used a lot. Make sure it doesn't unintentionally get bigger.
 #[cfg(target_arch = "x86_64")]
-rustc_data_structures::static_assert_size!(Expr, 104);
+rustc_data_structures::static_assert_size!(Expr, 112);
 
 impl Expr {
     /// Returns `true` if this expression would be valid somewhere that expects a value;
@@ -1157,7 +1171,7 @@ impl Expr {
             _ => return None,
         };
 
-        Some(P(Ty { kind, id: self.id, span: self.span }))
+        Some(P(Ty { kind, id: self.id, span: self.span, tokens: None }))
     }
 
     pub fn precedence(&self) -> ExprPrecedence {
@@ -1855,6 +1869,7 @@ pub struct Ty {
     pub id: NodeId,
     pub kind: TyKind,
     pub span: Span,
+    pub tokens: Option<TokenStream>,
 }
 
 #[derive(Clone, Encodable, Decodable, Debug)]
@@ -1916,7 +1931,7 @@ pub enum TyKind {
 
 impl TyKind {
     pub fn is_implicit_self(&self) -> bool {
-        if let TyKind::ImplicitSelf = *self { true } else { false }
+        matches!(self, TyKind::ImplicitSelf)
     }
 
     pub fn is_unit(&self) -> bool {
@@ -2133,7 +2148,7 @@ impl Param {
     /// Builds a `Param` object from `ExplicitSelf`.
     pub fn from_self(attrs: AttrVec, eself: ExplicitSelf, eself_ident: Ident) -> Param {
         let span = eself.span.to(eself_ident.span);
-        let infer_ty = P(Ty { id: DUMMY_NODE_ID, kind: TyKind::ImplicitSelf, span });
+        let infer_ty = P(Ty { id: DUMMY_NODE_ID, kind: TyKind::ImplicitSelf, span, tokens: None });
         let param = |mutbl, ty| Param {
             attrs,
             pat: P(Pat {
@@ -2156,6 +2171,7 @@ impl Param {
                     id: DUMMY_NODE_ID,
                     kind: TyKind::Rptr(lt, MutTy { ty: infer_ty, mutbl }),
                     span,
+                    tokens: None,
                 }),
             ),
         }
@@ -2211,7 +2227,7 @@ pub enum Async {
 
 impl Async {
     pub fn is_async(self) -> bool {
-        if let Async::Yes { .. } = self { true } else { false }
+        matches!(self, Async::Yes { .. })
     }
 
     /// In this case this is an `async` return, the `NodeId` for the generated `impl Trait` item.
@@ -2278,12 +2294,15 @@ impl FnRetTy {
 /// Module declaration.
 ///
 /// E.g., `mod foo;` or `mod foo { .. }`.
-#[derive(Clone, Encodable, Decodable, Debug, Default)]
+#[derive(Clone, Encodable, Decodable, Debug)]
 pub struct Mod {
     /// A span from the first token past `{` to the last token until `}`.
     /// For `mod foo;`, the inner span ranges from the first token
     /// to the last token in the external file.
     pub inner: Span,
+    /// `unsafe` keyword accepted syntactically for macro DSLs, but not
+    /// semantically by Rust.
+    pub unsafety: Unsafe,
     pub items: Vec<P<Item>>,
     /// `true` for `mod foo { .. }`; `false` for `mod foo;`.
     pub inline: bool,
@@ -2291,9 +2310,12 @@ pub struct Mod {
 
 /// Foreign module declaration.
 ///
-/// E.g., `extern { .. }` or `extern C { .. }`.
+/// E.g., `extern { .. }` or `extern "C" { .. }`.
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub struct ForeignMod {
+    /// `unsafe` keyword accepted syntactically for macro DSLs, but not
+    /// semantically by Rust.
+    pub unsafety: Unsafe,
     pub abi: Option<StrLit>,
     pub items: Vec<P<ForeignItem>>,
 }
@@ -2399,6 +2421,7 @@ impl<D: Decoder> rustc_serialize::Decodable<D> for AttrId {
 pub struct AttrItem {
     pub path: Path,
     pub args: MacArgs,
+    pub tokens: Option<TokenStream>,
 }
 
 /// A list of attributes.
@@ -2468,7 +2491,12 @@ pub enum CrateSugar {
     JustCrate,
 }
 
-pub type Visibility = Spanned<VisibilityKind>;
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct Visibility {
+    pub kind: VisibilityKind,
+    pub span: Span,
+    pub tokens: Option<TokenStream>,
+}
 
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub enum VisibilityKind {
@@ -2480,7 +2508,7 @@ pub enum VisibilityKind {
 
 impl VisibilityKind {
     pub fn is_pub(&self) -> bool {
-        if let VisibilityKind::Public = *self { true } else { false }
+        matches!(self, VisibilityKind::Public)
     }
 }
 
diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs
index edcbce3e2cf..2782869fb88 100644
--- a/compiler/rustc_ast/src/attr/mod.rs
+++ b/compiler/rustc_ast/src/attr/mod.rs
@@ -8,7 +8,7 @@ use crate::ast::{Path, PathSegment};
 use crate::mut_visit::visit_clobber;
 use crate::ptr::P;
 use crate::token::{self, CommentKind, Token};
-use crate::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndJoint};
+use crate::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndSpacing};
 
 use rustc_index::bit_set::GrowableBitSet;
 use rustc_span::source_map::{BytePos, Spanned};
@@ -16,7 +16,6 @@ use rustc_span::symbol::{sym, Ident, Symbol};
 use rustc_span::Span;
 
 use std::iter;
-use std::ops::DerefMut;
 
 pub struct MarkedAttrs(GrowableBitSet<AttrId>);
 
@@ -331,7 +330,7 @@ crate fn mk_attr_id() -> AttrId {
 }
 
 pub fn mk_attr(style: AttrStyle, path: Path, args: MacArgs, span: Span) -> Attribute {
-    mk_attr_from_item(style, AttrItem { path, args }, span)
+    mk_attr_from_item(style, AttrItem { path, args, tokens: None }, span)
 }
 
 pub fn mk_attr_from_item(style: AttrStyle, item: AttrItem, span: Span) -> Attribute {
@@ -362,7 +361,7 @@ pub fn list_contains_name(items: &[NestedMetaItem], name: Symbol) -> bool {
 }
 
 impl MetaItem {
-    fn token_trees_and_joints(&self) -> Vec<TreeAndJoint> {
+    fn token_trees_and_spacings(&self) -> Vec<TreeAndSpacing> {
         let mut idents = vec![];
         let mut last_pos = BytePos(0 as u32);
         for (i, segment) in self.path.segments.iter().enumerate() {
@@ -375,7 +374,7 @@ impl MetaItem {
             idents.push(TokenTree::Token(Token::from_ast_ident(segment.ident)).into());
             last_pos = segment.ident.span.hi();
         }
-        idents.extend(self.kind.token_trees_and_joints(self.span));
+        idents.extend(self.kind.token_trees_and_spacings(self.span));
         idents
     }
 
@@ -416,7 +415,7 @@ impl MetaItem {
                     }
                 }
                 let span = span.with_hi(segments.last().unwrap().ident.span.hi());
-                Path { span, segments }
+                Path { span, segments, tokens: None }
             }
             Some(TokenTree::Token(Token { kind: token::Interpolated(nt), .. })) => match *nt {
                 token::Nonterminal::NtMeta(ref item) => return item.meta(item.path.span),
@@ -448,7 +447,7 @@ impl MetaItemKind {
                     if i > 0 {
                         tts.push(TokenTree::token(token::Comma, span).into());
                     }
-                    tts.extend(item.token_trees_and_joints())
+                    tts.extend(item.token_trees_and_spacings())
                 }
                 MacArgs::Delimited(
                     DelimSpan::from_single(span),
@@ -459,7 +458,7 @@ impl MetaItemKind {
         }
     }
 
-    fn token_trees_and_joints(&self, span: Span) -> Vec<TreeAndJoint> {
+    fn token_trees_and_spacings(&self, span: Span) -> Vec<TreeAndSpacing> {
         match *self {
             MetaItemKind::Word => vec![],
             MetaItemKind::NameValue(ref lit) => {
@@ -471,7 +470,7 @@ impl MetaItemKind {
                     if i > 0 {
                         tokens.push(TokenTree::token(token::Comma, span).into());
                     }
-                    tokens.extend(item.token_trees_and_joints())
+                    tokens.extend(item.token_trees_and_spacings())
                 }
                 vec![
                     TokenTree::Delimited(
@@ -554,9 +553,9 @@ impl NestedMetaItem {
         }
     }
 
-    fn token_trees_and_joints(&self) -> Vec<TreeAndJoint> {
+    fn token_trees_and_spacings(&self) -> Vec<TreeAndSpacing> {
         match *self {
-            NestedMetaItem::MetaItem(ref item) => item.token_trees_and_joints(),
+            NestedMetaItem::MetaItem(ref item) => item.token_trees_and_spacings(),
             NestedMetaItem::Literal(ref lit) => vec![lit.token_tree().into()],
         }
     }
@@ -634,10 +633,7 @@ impl HasAttrs for StmtKind {
             StmtKind::Local(ref local) => local.attrs(),
             StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => expr.attrs(),
             StmtKind::Empty | StmtKind::Item(..) => &[],
-            StmtKind::MacCall(ref mac) => {
-                let (_, _, ref attrs) = **mac;
-                attrs.attrs()
-            }
+            StmtKind::MacCall(ref mac) => mac.attrs.attrs(),
         }
     }
 
@@ -647,8 +643,7 @@ impl HasAttrs for StmtKind {
             StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.visit_attrs(f),
             StmtKind::Empty | StmtKind::Item(..) => {}
             StmtKind::MacCall(mac) => {
-                let (_mac, _style, attrs) = mac.deref_mut();
-                attrs.visit_attrs(f);
+                mac.attrs.visit_attrs(f);
             }
         }
     }
diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs
index b556c1a446b..6e47ff7d740 100644
--- a/compiler/rustc_ast/src/lib.rs
+++ b/compiler/rustc_ast/src/lib.rs
@@ -4,18 +4,18 @@
 //!
 //! This API is completely unstable and subject to change.
 
-#![doc(html_root_url = "https://doc.rust-lang.org/nightly/", test(attr(deny(warnings))))]
-#![feature(bool_to_option)]
+#![doc(
+    html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/",
+    test(attr(deny(warnings)))
+)]
 #![feature(box_syntax)]
 #![feature(const_fn)] // For the `transmute` in `P::new`
-#![feature(const_panic)]
 #![feature(const_fn_transmute)]
+#![feature(const_panic)]
 #![feature(crate_visibility_modifier)]
 #![feature(label_break_value)]
 #![feature(nll)]
 #![feature(or_patterns)]
-#![feature(try_trait)]
-#![feature(unicode_internals)]
 #![recursion_limit = "256"]
 
 #[macro_use]
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index 965571aaa54..425ef83b57a 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -14,7 +14,7 @@ use crate::tokenstream::*;
 
 use rustc_data_structures::map_in_place::MapInPlace;
 use rustc_data_structures::sync::Lrc;
-use rustc_span::source_map::{respan, Spanned};
+use rustc_span::source_map::Spanned;
 use rustc_span::symbol::Ident;
 use rustc_span::Span;
 
@@ -451,7 +451,7 @@ pub fn noop_visit_ty_constraint<T: MutVisitor>(
 }
 
 pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) {
-    let Ty { id, kind, span } = ty.deref_mut();
+    let Ty { id, kind, span, tokens: _ } = ty.deref_mut();
     vis.visit_id(id);
     match kind {
         TyKind::Infer | TyKind::ImplicitSelf | TyKind::Err | TyKind::Never | TyKind::CVarArgs => {}
@@ -490,7 +490,7 @@ pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) {
 }
 
 pub fn noop_visit_foreign_mod<T: MutVisitor>(foreign_mod: &mut ForeignMod, vis: &mut T) {
-    let ForeignMod { abi: _, items } = foreign_mod;
+    let ForeignMod { unsafety: _, abi: _, items } = foreign_mod;
     items.flat_map_in_place(|item| vis.flat_map_foreign_item(item));
 }
 
@@ -513,7 +513,7 @@ pub fn noop_visit_ident<T: MutVisitor>(Ident { name: _, span }: &mut Ident, vis:
     vis.visit_span(span);
 }
 
-pub fn noop_visit_path<T: MutVisitor>(Path { segments, span }: &mut Path, vis: &mut T) {
+pub fn noop_visit_path<T: MutVisitor>(Path { segments, span, tokens: _ }: &mut Path, vis: &mut T) {
     vis.visit_span(span);
     for PathSegment { ident, id, args } in segments {
         vis.visit_ident(ident);
@@ -579,7 +579,7 @@ pub fn noop_visit_local<T: MutVisitor>(local: &mut P<Local>, vis: &mut T) {
 pub fn noop_visit_attribute<T: MutVisitor>(attr: &mut Attribute, vis: &mut T) {
     let Attribute { kind, id: _, style: _, span } = attr;
     match kind {
-        AttrKind::Normal(AttrItem { path, args }) => {
+        AttrKind::Normal(AttrItem { path, args, tokens: _ }) => {
             vis.visit_path(path);
             visit_mac_args(args, vis);
         }
@@ -709,7 +709,7 @@ pub fn noop_visit_interpolated<T: MutVisitor>(nt: &mut token::Nonterminal, vis:
         token::NtLifetime(ident) => vis.visit_ident(ident),
         token::NtLiteral(expr) => vis.visit_expr(expr),
         token::NtMeta(item) => {
-            let AttrItem { path, args } = item.deref_mut();
+            let AttrItem { path, args, tokens: _ } = item.deref_mut();
             vis.visit_path(path);
             visit_mac_args(args, vis);
         }
@@ -871,7 +871,7 @@ pub fn noop_visit_mt<T: MutVisitor>(MutTy { ty, mutbl: _ }: &mut MutTy, vis: &mu
 }
 
 pub fn noop_visit_block<T: MutVisitor>(block: &mut P<Block>, vis: &mut T) {
-    let Block { id, stmts, rules: _, span } = block.deref_mut();
+    let Block { id, stmts, rules: _, span, tokens: _ } = block.deref_mut();
     vis.visit_id(id);
     stmts.flat_map_in_place(|stmt| vis.flat_map_stmt(stmt));
     vis.visit_span(span);
@@ -970,18 +970,21 @@ pub fn noop_visit_fn_header<T: MutVisitor>(header: &mut FnHeader, vis: &mut T) {
     vis.visit_asyncness(asyncness);
 }
 
-pub fn noop_visit_mod<T: MutVisitor>(Mod { inner, items, inline: _ }: &mut Mod, vis: &mut T) {
+pub fn noop_visit_mod<T: MutVisitor>(module: &mut Mod, vis: &mut T) {
+    let Mod { inner, unsafety: _, items, inline: _ } = module;
     vis.visit_span(inner);
     items.flat_map_in_place(|item| vis.flat_map_item(item));
 }
 
 pub fn noop_visit_crate<T: MutVisitor>(krate: &mut Crate, vis: &mut T) {
     visit_clobber(krate, |Crate { module, attrs, span, proc_macros }| {
+        let item_vis =
+            Visibility { kind: VisibilityKind::Public, span: span.shrink_to_lo(), tokens: None };
         let item = P(Item {
             ident: Ident::invalid(),
             attrs,
             id: DUMMY_NODE_ID,
-            vis: respan(span.shrink_to_lo(), VisibilityKind::Public),
+            vis: item_vis,
             span,
             kind: ItemKind::Mod(module),
             tokens: None,
@@ -990,7 +993,7 @@ pub fn noop_visit_crate<T: MutVisitor>(krate: &mut Crate, vis: &mut T) {
 
         let len = items.len();
         if len == 0 {
-            let module = Mod { inner: span, items: vec![], inline: true };
+            let module = Mod { inner: span, unsafety: Unsafe::No, items: vec![], inline: true };
             Crate { module, attrs: vec![], span, proc_macros }
         } else if len == 1 {
             let Item { attrs, span, kind, .. } = items.into_iter().next().unwrap().into_inner();
@@ -1283,12 +1286,15 @@ pub fn noop_filter_map_expr<T: MutVisitor>(mut e: P<Expr>, vis: &mut T) -> Optio
 }
 
 pub fn noop_flat_map_stmt<T: MutVisitor>(
-    Stmt { kind, mut span, mut id }: Stmt,
+    Stmt { kind, mut span, mut id, tokens }: Stmt,
     vis: &mut T,
 ) -> SmallVec<[Stmt; 1]> {
     vis.visit_id(&mut id);
     vis.visit_span(&mut span);
-    noop_flat_map_stmt_kind(kind, vis).into_iter().map(|kind| Stmt { id, kind, span }).collect()
+    noop_flat_map_stmt_kind(kind, vis)
+        .into_iter()
+        .map(|kind| Stmt { id, kind, span, tokens: tokens.clone() })
+        .collect()
 }
 
 pub fn noop_flat_map_stmt_kind<T: MutVisitor>(
@@ -1305,7 +1311,7 @@ pub fn noop_flat_map_stmt_kind<T: MutVisitor>(
         StmtKind::Semi(expr) => vis.filter_map_expr(expr).into_iter().map(StmtKind::Semi).collect(),
         StmtKind::Empty => smallvec![StmtKind::Empty],
         StmtKind::MacCall(mut mac) => {
-            let (mac_, _semi, attrs) = mac.deref_mut();
+            let MacCallStmt { mac: mac_, style: _, attrs } = mac.deref_mut();
             vis.visit_mac(mac_);
             visit_thin_attrs(attrs, vis);
             smallvec![StmtKind::MacCall(mac)]
@@ -1313,13 +1319,13 @@ pub fn noop_flat_map_stmt_kind<T: MutVisitor>(
     }
 }
 
-pub fn noop_visit_vis<T: MutVisitor>(Spanned { node, span }: &mut Visibility, vis: &mut T) {
-    match node {
+pub fn noop_visit_vis<T: MutVisitor>(visibility: &mut Visibility, vis: &mut T) {
+    match &mut visibility.kind {
         VisibilityKind::Public | VisibilityKind::Crate(_) | VisibilityKind::Inherited => {}
         VisibilityKind::Restricted { path, id } => {
             vis.visit_path(path);
             vis.visit_id(id);
         }
     }
-    vis.visit_span(span);
+    vis.visit_span(&mut visibility.span);
 }
diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs
index 4a8bf6b4f19..d5b3e87adc3 100644
--- a/compiler/rustc_ast/src/token.rs
+++ b/compiler/rustc_ast/src/token.rs
@@ -173,6 +173,7 @@ pub fn ident_can_begin_expr(name: Symbol, span: Span, is_raw: bool) -> bool {
             kw::Move,
             kw::Return,
             kw::True,
+            kw::Try,
             kw::Unsafe,
             kw::While,
             kw::Yield,
@@ -251,17 +252,6 @@ pub enum TokenKind {
     /// similarly to symbols in string literal tokens.
     DocComment(CommentKind, ast::AttrStyle, Symbol),
 
-    // Junk. These carry no data because we don't really care about the data
-    // they *would* carry, and don't really want to allocate a new ident for
-    // them. Instead, users could extract that from the associated span.
-    /// Whitespace.
-    Whitespace,
-    /// A comment.
-    Comment,
-    Shebang(Symbol),
-    /// A completely invalid token which should be skipped.
-    Unknown(Symbol),
-
     Eof,
 }
 
@@ -331,7 +321,7 @@ impl Token {
 
     /// Some token that will be thrown away later.
     pub fn dummy() -> Self {
-        Token::new(TokenKind::Whitespace, DUMMY_SP)
+        Token::new(TokenKind::Question, DUMMY_SP)
     }
 
     /// Recovers a `Token` from an `Ident`. This creates a raw identifier if necessary.
@@ -360,7 +350,7 @@ impl Token {
     pub fn is_op(&self) -> bool {
         match self.kind {
             OpenDelim(..) | CloseDelim(..) | Literal(..) | DocComment(..) | Ident(..)
-            | Lifetime(..) | Interpolated(..) | Whitespace | Comment | Shebang(..) | Eof => false,
+            | Lifetime(..) | Interpolated(..) | Eof => false,
             _ => true,
         }
     }
@@ -676,8 +666,7 @@ impl Token {
             Le | EqEq | Ne | Ge | AndAnd | OrOr | Tilde | BinOpEq(..) | At | DotDotDot
             | DotDotEq | Comma | Semi | ModSep | RArrow | LArrow | FatArrow | Pound | Dollar
             | Question | OpenDelim(..) | CloseDelim(..) | Literal(..) | Ident(..)
-            | Lifetime(..) | Interpolated(..) | DocComment(..) | Whitespace | Comment
-            | Shebang(..) | Unknown(..) | Eof => return None,
+            | Lifetime(..) | Interpolated(..) | DocComment(..) | Eof => return None,
         };
 
         Some(Token::new(kind, self.span.to(joint.span)))
@@ -711,7 +700,7 @@ pub enum Nonterminal {
 
 // `Nonterminal` is used a lot. Make sure it doesn't unintentionally get bigger.
 #[cfg(target_arch = "x86_64")]
-rustc_data_structures::static_assert_size!(Nonterminal, 40);
+rustc_data_structures::static_assert_size!(Nonterminal, 48);
 
 #[derive(Debug, Copy, Clone, PartialEq, Encodable, Decodable)]
 pub enum NonterminalKind {
@@ -821,9 +810,19 @@ impl Nonterminal {
             if let ExpnKind::Macro(_, macro_name) = orig_span.ctxt().outer_expn_data().kind {
                 let filename = source_map.span_to_filename(orig_span);
                 if let FileName::Real(RealFileName::Named(path)) = filename {
-                    if (path.ends_with("time-macros-impl/src/lib.rs")
-                        && macro_name == sym::impl_macros)
-                        || (path.ends_with("js-sys/src/lib.rs") && macro_name == sym::arrays)
+                    let matches_prefix = |prefix| {
+                        // Check for a path that ends with 'prefix*/src/lib.rs'
+                        let mut iter = path.components().rev();
+                        iter.next().and_then(|p| p.as_os_str().to_str()) == Some("lib.rs")
+                            && iter.next().and_then(|p| p.as_os_str().to_str()) == Some("src")
+                            && iter
+                                .next()
+                                .and_then(|p| p.as_os_str().to_str())
+                                .map_or(false, |p| p.starts_with(prefix))
+                    };
+
+                    if (macro_name == sym::impl_macros && matches_prefix("time-macros-impl"))
+                        || (macro_name == sym::arrays && matches_prefix("js-sys"))
                     {
                         let snippet = source_map.span_to_snippet(orig_span);
                         if snippet.as_deref() == Ok("$name") {
diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs
index 151acddae84..f201f0b5c66 100644
--- a/compiler/rustc_ast/src/tokenstream.rs
+++ b/compiler/rustc_ast/src/tokenstream.rs
@@ -83,7 +83,7 @@ impl TokenTree {
     }
 
     pub fn joint(self) -> TokenStream {
-        TokenStream::new(vec![(self, Joint)])
+        TokenStream::new(vec![(self, Spacing::Joint)])
     }
 
     pub fn token(kind: TokenKind, span: Span) -> TokenTree {
@@ -125,22 +125,20 @@ where
 /// instead of a representation of the abstract syntax tree.
 /// Today's `TokenTree`s can still contain AST via `token::Interpolated` for back-compat.
 #[derive(Clone, Debug, Default, Encodable, Decodable)]
-pub struct TokenStream(pub Lrc<Vec<TreeAndJoint>>);
+pub struct TokenStream(pub Lrc<Vec<TreeAndSpacing>>);
 
-pub type TreeAndJoint = (TokenTree, IsJoint);
+pub type TreeAndSpacing = (TokenTree, Spacing);
 
 // `TokenStream` is used a lot. Make sure it doesn't unintentionally get bigger.
 #[cfg(target_arch = "x86_64")]
 rustc_data_structures::static_assert_size!(TokenStream, 8);
 
 #[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable)]
-pub enum IsJoint {
+pub enum Spacing {
+    Alone,
     Joint,
-    NonJoint,
 }
 
-use IsJoint::*;
-
 impl TokenStream {
     /// Given a `TokenStream` with a `Stream` of only two arguments, return a new `TokenStream`
     /// separating the two arguments with a comma for diagnostic suggestions.
@@ -153,7 +151,7 @@ impl TokenStream {
                 let sp = match (&ts, &next) {
                     (_, (TokenTree::Token(Token { kind: token::Comma, .. }), _)) => continue,
                     (
-                        (TokenTree::Token(token_left), NonJoint),
+                        (TokenTree::Token(token_left), Spacing::Alone),
                         (TokenTree::Token(token_right), _),
                     ) if ((token_left.is_ident() && !token_left.is_reserved_ident())
                         || token_left.is_lit())
@@ -162,11 +160,11 @@ impl TokenStream {
                     {
                         token_left.span
                     }
-                    ((TokenTree::Delimited(sp, ..), NonJoint), _) => sp.entire(),
+                    ((TokenTree::Delimited(sp, ..), Spacing::Alone), _) => sp.entire(),
                     _ => continue,
                 };
                 let sp = sp.shrink_to_hi();
-                let comma = (TokenTree::token(token::Comma, sp), NonJoint);
+                let comma = (TokenTree::token(token::Comma, sp), Spacing::Alone);
                 suggestion = Some((pos, comma, sp));
             }
         }
@@ -184,19 +182,19 @@ impl TokenStream {
 
 impl From<TokenTree> for TokenStream {
     fn from(tree: TokenTree) -> TokenStream {
-        TokenStream::new(vec![(tree, NonJoint)])
+        TokenStream::new(vec![(tree, Spacing::Alone)])
     }
 }
 
-impl From<TokenTree> for TreeAndJoint {
-    fn from(tree: TokenTree) -> TreeAndJoint {
-        (tree, NonJoint)
+impl From<TokenTree> for TreeAndSpacing {
+    fn from(tree: TokenTree) -> TreeAndSpacing {
+        (tree, Spacing::Alone)
     }
 }
 
 impl iter::FromIterator<TokenTree> for TokenStream {
     fn from_iter<I: IntoIterator<Item = TokenTree>>(iter: I) -> Self {
-        TokenStream::new(iter.into_iter().map(Into::into).collect::<Vec<TreeAndJoint>>())
+        TokenStream::new(iter.into_iter().map(Into::into).collect::<Vec<TreeAndSpacing>>())
     }
 }
 
@@ -209,7 +207,7 @@ impl PartialEq<TokenStream> for TokenStream {
 }
 
 impl TokenStream {
-    pub fn new(streams: Vec<TreeAndJoint>) -> TokenStream {
+    pub fn new(streams: Vec<TreeAndSpacing>) -> TokenStream {
         TokenStream(Lrc::new(streams))
     }
 
@@ -320,11 +318,11 @@ impl TokenStreamBuilder {
         // If `self` is not empty and the last tree within the last stream is a
         // token tree marked with `Joint`...
         if let Some(TokenStream(ref mut last_stream_lrc)) = self.0.last_mut() {
-            if let Some((TokenTree::Token(last_token), Joint)) = last_stream_lrc.last() {
+            if let Some((TokenTree::Token(last_token), Spacing::Joint)) = last_stream_lrc.last() {
                 // ...and `stream` is not empty and the first tree within it is
                 // a token tree...
                 let TokenStream(ref mut stream_lrc) = stream;
-                if let Some((TokenTree::Token(token), is_joint)) = stream_lrc.first() {
+                if let Some((TokenTree::Token(token), spacing)) = stream_lrc.first() {
                     // ...and the two tokens can be glued together...
                     if let Some(glued_tok) = last_token.glue(&token) {
                         // ...then do so, by overwriting the last token
@@ -337,8 +335,7 @@ impl TokenStreamBuilder {
                         // Overwrite the last token tree with the merged
                         // token.
                         let last_vec_mut = Lrc::make_mut(last_stream_lrc);
-                        *last_vec_mut.last_mut().unwrap() =
-                            (TokenTree::Token(glued_tok), *is_joint);
+                        *last_vec_mut.last_mut().unwrap() = (TokenTree::Token(glued_tok), *spacing);
 
                         // Remove the first token tree from `stream`. (This
                         // is almost always the only tree in `stream`.)
@@ -375,7 +372,7 @@ impl Iterator for Cursor {
     type Item = TokenTree;
 
     fn next(&mut self) -> Option<TokenTree> {
-        self.next_with_joint().map(|(tree, _)| tree)
+        self.next_with_spacing().map(|(tree, _)| tree)
     }
 }
 
@@ -384,7 +381,7 @@ impl Cursor {
         Cursor { stream, index: 0 }
     }
 
-    pub fn next_with_joint(&mut self) -> Option<TreeAndJoint> {
+    pub fn next_with_spacing(&mut self) -> Option<TreeAndSpacing> {
         if self.index < self.stream.len() {
             self.index += 1;
             Some(self.stream.0[self.index - 1].clone())
diff --git a/compiler/rustc_ast/src/util/lev_distance.rs b/compiler/rustc_ast/src/util/lev_distance.rs
index d4e0e3ba051..754b1f13381 100644
--- a/compiler/rustc_ast/src/util/lev_distance.rs
+++ b/compiler/rustc_ast/src/util/lev_distance.rs
@@ -103,6 +103,7 @@ fn find_match_by_sorted_words<'a>(iter_names: Vec<&'a Symbol>, lookup: &str) ->
 
 fn sort_by_words(name: &str) -> String {
     let mut split_words: Vec<&str> = name.split('_').collect();
-    split_words.sort();
+    // We are sorting primitive &strs and can use unstable sort here
+    split_words.sort_unstable();
     split_words.join("_")
 }
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index b65a88cb90e..86fd87f6c42 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -692,7 +692,7 @@ pub fn walk_stmt<'a, V: Visitor<'a>>(visitor: &mut V, statement: &'a Stmt) {
         StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => visitor.visit_expr(expr),
         StmtKind::Empty => {}
         StmtKind::MacCall(ref mac) => {
-            let (ref mac, _, ref attrs) = **mac;
+            let MacCallStmt { ref mac, style: _, ref attrs } = **mac;
             visitor.visit_mac(mac);
             for attr in attrs.iter() {
                 visitor.visit_attribute(attr);
@@ -879,7 +879,7 @@ pub fn walk_arm<'a, V: Visitor<'a>>(visitor: &mut V, arm: &'a Arm) {
 }
 
 pub fn walk_vis<'a, V: Visitor<'a>>(visitor: &mut V, vis: &'a Visibility) {
-    if let VisibilityKind::Restricted { ref path, id } = vis.node {
+    if let VisibilityKind::Restricted { ref path, id } = vis.kind {
         visitor.visit_path(path, id);
     }
 }
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index df452825bba..c97f80cf09b 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -1121,7 +1121,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 // features. We check that at least one type is available for
                 // the current target.
                 let reg_class = reg.reg_class();
-                let mut required_features = vec![];
+                let mut required_features: Vec<&str> = vec![];
                 for &(_, feature) in reg_class.supported_types(asm_arch) {
                     if let Some(feature) = feature {
                         if self.sess.target_features.contains(&Symbol::intern(feature)) {
@@ -1135,7 +1135,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
                         break;
                     }
                 }
-                required_features.sort();
+                // We are sorting primitive strs here and can use unstable sort here
+                required_features.sort_unstable();
                 required_features.dedup();
                 match &required_features[..] {
                     [] => {}
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index f3309afec7d..617cacee0e7 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -27,7 +27,7 @@ pub(super) struct ItemLowerer<'a, 'lowering, 'hir> {
 impl ItemLowerer<'_, '_, '_> {
     fn with_trait_impl_ref(&mut self, impl_ref: &Option<TraitRef>, f: impl FnOnce(&mut Self)) {
         let old = self.lctx.is_in_trait_impl;
-        self.lctx.is_in_trait_impl = if let &None = impl_ref { false } else { true };
+        self.lctx.is_in_trait_impl = impl_ref.is_some();
         f(self);
         self.lctx.is_in_trait_impl = old;
     }
@@ -251,7 +251,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
             ItemKind::ExternCrate(orig_name) => hir::ItemKind::ExternCrate(orig_name),
             ItemKind::Use(ref use_tree) => {
                 // Start with an empty prefix.
-                let prefix = Path { segments: vec![], span: use_tree.span };
+                let prefix = Path { segments: vec![], span: use_tree.span, tokens: None };
 
                 self.lower_use_tree(use_tree, &prefix, id, vis, ident, attrs)
             }
@@ -488,7 +488,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 *ident = tree.ident();
 
                 // First, apply the prefix to the path.
-                let mut path = Path { segments, span: path.span };
+                let mut path = Path { segments, span: path.span, tokens: None };
 
                 // Correctly resolve `self` imports.
                 if path.segments.len() > 1
@@ -540,8 +540,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 hir::ItemKind::Use(path, hir::UseKind::Single)
             }
             UseTreeKind::Glob => {
-                let path =
-                    self.lower_path(id, &Path { segments, span: path.span }, ParamMode::Explicit);
+                let path = self.lower_path(
+                    id,
+                    &Path { segments, span: path.span, tokens: None },
+                    ParamMode::Explicit,
+                );
                 hir::ItemKind::Use(path, hir::UseKind::Glob)
             }
             UseTreeKind::Nested(ref trees) => {
@@ -569,7 +572,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 // for that we return the `{}` import (called the
                 // `ListStem`).
 
-                let prefix = Path { segments, span: prefix.span.to(path.span) };
+                let prefix = Path { segments, span: prefix.span.to(path.span), tokens: None };
 
                 // Add all the nested `PathListItem`s to the HIR.
                 for &(ref use_tree, id) in trees {
@@ -927,7 +930,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         v: &Visibility,
         explicit_owner: Option<NodeId>,
     ) -> hir::Visibility<'hir> {
-        let node = match v.node {
+        let node = match v.kind {
             VisibilityKind::Public => hir::VisibilityKind::Public,
             VisibilityKind::Crate(sugar) => hir::VisibilityKind::Crate(sugar),
             VisibilityKind::Restricted { ref path, id } => {
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 586355fe613..a28d022c661 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -967,6 +967,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             AttrKind::Normal(ref item) => AttrKind::Normal(AttrItem {
                 path: item.path.clone(),
                 args: self.lower_mac_args(&item.args),
+                tokens: None,
             }),
             AttrKind::DocComment(comment_kind, data) => AttrKind::DocComment(comment_kind, data),
         };
@@ -1106,6 +1107,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                                 id: node_id,
                                 kind: TyKind::ImplTrait(impl_trait_node_id, bounds.clone()),
                                 span: constraint.span,
+                                tokens: None,
                             },
                             itctx,
                         );
diff --git a/compiler/rustc_ast_passes/Cargo.toml b/compiler/rustc_ast_passes/Cargo.toml
index 7cf3e752c92..9ed6bdc3d6a 100644
--- a/compiler/rustc_ast_passes/Cargo.toml
+++ b/compiler/rustc_ast_passes/Cargo.toml
@@ -5,7 +5,7 @@ version = "0.0.0"
 edition = "2018"
 
 [dependencies]
-itertools = "0.8"
+itertools = "0.9"
 tracing = "0.1"
 rustc_ast_pretty = { path = "../rustc_ast_pretty" }
 rustc_attr = { path = "../rustc_attr" }
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index a01dd8c939c..232ee35c4f7 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -198,13 +198,13 @@ impl<'a> AstValidator<'a> {
     }
 
     fn invalid_visibility(&self, vis: &Visibility, note: Option<&str>) {
-        if let VisibilityKind::Inherited = vis.node {
+        if let VisibilityKind::Inherited = vis.kind {
             return;
         }
 
         let mut err =
             struct_span_err!(self.session, vis.span, E0449, "unnecessary visibility qualifier");
-        if vis.node.is_pub() {
+        if vis.kind.is_pub() {
             err.span_label(vis.span, "`pub` not permitted here because it's implied");
         }
         if let Some(note) = note {
@@ -868,10 +868,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                     .emit();
                 }
 
-                if !bounds
-                    .iter()
-                    .any(|b| if let GenericBound::Trait(..) = *b { true } else { false })
-                {
+                if !bounds.iter().any(|b| matches!(b, GenericBound::Trait(..))) {
                     self.err_handler().span_err(ty.span, "at least one trait must be specified");
                 }
 
@@ -990,12 +987,15 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                     self.error_item_without_body(item.span, "function", msg, " { <body> }");
                 }
             }
-            ItemKind::ForeignMod(_) => {
+            ItemKind::ForeignMod(ForeignMod { unsafety, .. }) => {
                 let old_item = mem::replace(&mut self.extern_mod, Some(item));
                 self.invalid_visibility(
                     &item.vis,
                     Some("place qualifiers on individual foreign items instead"),
                 );
+                if let Unsafe::Yes(span) = unsafety {
+                    self.err_handler().span_err(span, "extern block cannot be declared unsafe");
+                }
                 visit::walk_item(self, item);
                 self.extern_mod = old_item;
                 return; // Avoid visiting again.
@@ -1029,7 +1029,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 walk_list!(self, visit_attribute, &item.attrs);
                 return;
             }
-            ItemKind::Mod(Mod { inline, .. }) => {
+            ItemKind::Mod(Mod { inline, unsafety, .. }) => {
+                if let Unsafe::Yes(span) = unsafety {
+                    self.err_handler().span_err(span, "module cannot be declared unsafe");
+                }
                 // Ensure that `path` attributes on modules are recorded as used (cf. issue #35584).
                 if !inline && !self.session.contains_name(&item.attrs, sym::path) {
                     self.check_mod_file_item_asciionly(item.ident);
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 0ee8ef55e61..00d3db73766 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -260,7 +260,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
                     cfg => doc_cfg
                     masked => doc_masked
                     spotlight => doc_spotlight
-                    alias => doc_alias
                     keyword => doc_keyword
                 );
             }
@@ -594,7 +593,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
     }
 
     fn visit_vis(&mut self, vis: &'a ast::Visibility) {
-        if let ast::VisibilityKind::Crate(ast::CrateSugar::JustCrate) = vis.node {
+        if let ast::VisibilityKind::Crate(ast::CrateSugar::JustCrate) = vis.kind {
             gate_feature_post!(
                 &self,
                 crate_visibility_modifier,
@@ -608,6 +607,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
 
 pub fn check_crate(krate: &ast::Crate, sess: &Session) {
     maybe_stage_features(sess, krate);
+    check_incompatible_features(sess);
     let mut visitor = PostExpansionVisitor { sess, features: &sess.features_untracked() };
 
     let spans = sess.parse_sess.gated_spans.spans.borrow();
@@ -677,3 +677,36 @@ fn maybe_stage_features(sess: &Session, krate: &ast::Crate) {
         }
     }
 }
+
+fn check_incompatible_features(sess: &Session) {
+    let features = sess.features_untracked();
+
+    let declared_features = features
+        .declared_lang_features
+        .iter()
+        .copied()
+        .map(|(name, span, _)| (name, span))
+        .chain(features.declared_lib_features.iter().copied());
+
+    for (f1, f2) in rustc_feature::INCOMPATIBLE_FEATURES
+        .iter()
+        .filter(|&&(f1, f2)| features.enabled(f1) && features.enabled(f2))
+    {
+        if let Some((f1_name, f1_span)) = declared_features.clone().find(|(name, _)| name == f1) {
+            if let Some((f2_name, f2_span)) = declared_features.clone().find(|(name, _)| name == f2)
+            {
+                let spans = vec![f1_span, f2_span];
+                sess.struct_span_err(
+                    spans.clone(),
+                    &format!(
+                        "features `{}` and `{}` are incompatible, using them at the same time \
+                        is not allowed",
+                        f1_name, f2_name
+                    ),
+                )
+                .help("remove one of these features")
+                .emit();
+            }
+        }
+    }
+}
diff --git a/compiler/rustc_ast_pretty/src/pprust.rs b/compiler/rustc_ast_pretty/src/pprust.rs
index cb48deb5886..d16b541c699 100644
--- a/compiler/rustc_ast_pretty/src/pprust.rs
+++ b/compiler/rustc_ast_pretty/src/pprust.rs
@@ -289,10 +289,6 @@ fn token_kind_to_string_ext(tok: &TokenKind, convert_dollar_crate: Option<Span>)
             doc_comment_to_string(comment_kind, attr_style, data)
         }
         token::Eof => "<eof>".to_string(),
-        token::Whitespace => " ".to_string(),
-        token::Comment => "/* */".to_string(),
-        token::Shebang(s) => format!("/* shebang: {}*/", s),
-        token::Unknown(s) => s.to_string(),
 
         token::Interpolated(ref nt) => nonterminal_to_string(nt),
     }
@@ -1143,7 +1139,11 @@ impl<'a> State<'a> {
                 self.print_fn_full(sig, item.ident, gen, &item.vis, def, body, &item.attrs);
             }
             ast::ItemKind::Mod(ref _mod) => {
-                self.head(visibility_qualified(&item.vis, "mod"));
+                self.head(to_string(|s| {
+                    s.print_visibility(&item.vis);
+                    s.print_unsafety(_mod.unsafety);
+                    s.word("mod");
+                }));
                 self.print_ident(item.ident);
 
                 if _mod.inline || self.is_expanded {
@@ -1158,7 +1158,10 @@ impl<'a> State<'a> {
                 }
             }
             ast::ItemKind::ForeignMod(ref nmod) => {
-                self.head("extern");
+                self.head(to_string(|s| {
+                    s.print_unsafety(nmod.unsafety);
+                    s.word("extern");
+                }));
                 if let Some(abi) = nmod.abi {
                     self.print_literal(&abi.as_lit());
                     self.nbsp();
@@ -1356,7 +1359,7 @@ impl<'a> State<'a> {
     }
 
     crate fn print_visibility(&mut self, vis: &ast::Visibility) {
-        match vis.node {
+        match vis.kind {
             ast::VisibilityKind::Public => self.word_nbsp("pub"),
             ast::VisibilityKind::Crate(sugar) => match sugar {
                 ast::CrateSugar::PubCrate => self.word_nbsp("pub(crate)"),
@@ -1507,11 +1510,10 @@ impl<'a> State<'a> {
                 self.s.word(";");
             }
             ast::StmtKind::MacCall(ref mac) => {
-                let (ref mac, style, ref attrs) = **mac;
                 self.space_if_not_bol();
-                self.print_outer_attributes(attrs);
-                self.print_mac(mac);
-                if style == ast::MacStmtStyle::Semicolon {
+                self.print_outer_attributes(&mac.attrs);
+                self.print_mac(&mac.mac);
+                if mac.style == ast::MacStmtStyle::Semicolon {
                     self.s.word(";");
                 }
             }
diff --git a/compiler/rustc_ast_pretty/src/pprust/tests.rs b/compiler/rustc_ast_pretty/src/pprust/tests.rs
index fdbf3feb900..b1a73a0bf02 100644
--- a/compiler/rustc_ast_pretty/src/pprust/tests.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/tests.rs
@@ -1,7 +1,6 @@
 use super::*;
 
 use rustc_ast as ast;
-use rustc_span::source_map::respan;
 use rustc_span::symbol::Ident;
 use rustc_span::with_default_session_globals;
 
@@ -45,7 +44,11 @@ fn test_variant_to_string() {
 
         let var = ast::Variant {
             ident,
-            vis: respan(rustc_span::DUMMY_SP, ast::VisibilityKind::Inherited),
+            vis: ast::Visibility {
+                span: rustc_span::DUMMY_SP,
+                kind: ast::VisibilityKind::Inherited,
+                tokens: None,
+            },
             attrs: Vec::new(),
             id: ast::DUMMY_NODE_ID,
             data: ast::VariantData::Unit(ast::DUMMY_NODE_ID),
diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs
index b8929fe0889..1808eb270ba 100644
--- a/compiler/rustc_attr/src/builtin.rs
+++ b/compiler/rustc_attr/src/builtin.rs
@@ -160,10 +160,10 @@ pub enum StabilityLevel {
 
 impl StabilityLevel {
     pub fn is_unstable(&self) -> bool {
-        if let StabilityLevel::Unstable { .. } = *self { true } else { false }
+        matches!(self, StabilityLevel::Unstable { .. })
     }
     pub fn is_stable(&self) -> bool {
-        if let StabilityLevel::Stable { .. } = *self { true } else { false }
+        matches!(self, StabilityLevel::Stable { .. })
     }
 }
 
@@ -301,7 +301,7 @@ where
                                                 .emit();
                                             };
                                             match issue.parse() {
-                                                Ok(num) if num == 0 => {
+                                                Ok(0) => {
                                                     emit_diag(
                                                         "`issue` must not be \"0\", \
                                                         use \"none\" instead",
diff --git a/compiler/rustc_builtin_macros/src/cmdline_attrs.rs b/compiler/rustc_builtin_macros/src/cmdline_attrs.rs
index 34e2accc615..5ed8b69d92a 100644
--- a/compiler/rustc_builtin_macros/src/cmdline_attrs.rs
+++ b/compiler/rustc_builtin_macros/src/cmdline_attrs.rs
@@ -15,7 +15,7 @@ pub fn inject(mut krate: ast::Crate, parse_sess: &ParseSess, attrs: &[String]) -
         );
 
         let start_span = parser.token.span;
-        let AttrItem { path, args } = match parser.parse_attr_item() {
+        let AttrItem { path, args, tokens: _ } = match parser.parse_attr_item() {
             Ok(ai) => ai,
             Err(mut err) => {
                 err.emit();
diff --git a/compiler/rustc_builtin_macros/src/concat_idents.rs b/compiler/rustc_builtin_macros/src/concat_idents.rs
index 8223cdda072..c4d1c6eee31 100644
--- a/compiler/rustc_builtin_macros/src/concat_idents.rs
+++ b/compiler/rustc_builtin_macros/src/concat_idents.rs
@@ -61,6 +61,7 @@ pub fn expand_concat_idents<'cx>(
                 id: ast::DUMMY_NODE_ID,
                 kind: ast::TyKind::Path(None, ast::Path::from_ident(self.ident)),
                 span: self.ident.span,
+                tokens: None,
             }))
         }
     }
diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs
index 120e859f2b1..d84b3956475 100644
--- a/compiler/rustc_builtin_macros/src/deriving/debug.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs
@@ -133,5 +133,5 @@ fn stmt_let_underscore(cx: &mut ExtCtxt<'_>, sp: Span, expr: P<ast::Expr>) -> as
         span: sp,
         attrs: ast::AttrVec::new(),
     });
-    ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Local(local), span: sp }
+    ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Local(local), span: sp, tokens: None }
 }
diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
index 849e8b136e1..f4924997d1a 100644
--- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
@@ -187,7 +187,6 @@ use rustc_ast::{GenericArg, GenericParamKind, VariantData};
 use rustc_attr as attr;
 use rustc_data_structures::map_in_place::MapInPlace;
 use rustc_expand::base::{Annotatable, ExtCtxt};
-use rustc_span::source_map::respan;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::Span;
 
@@ -532,7 +531,11 @@ impl<'a> TraitDef<'a> {
                 id: ast::DUMMY_NODE_ID,
                 span: self.span,
                 ident,
-                vis: respan(self.span.shrink_to_lo(), ast::VisibilityKind::Inherited),
+                vis: ast::Visibility {
+                    span: self.span.shrink_to_lo(),
+                    kind: ast::VisibilityKind::Inherited,
+                    tokens: None,
+                },
                 attrs: Vec::new(),
                 kind: ast::AssocItemKind::TyAlias(
                     ast::Defaultness::Final,
@@ -933,7 +936,11 @@ impl<'a> MethodDef<'a> {
             id: ast::DUMMY_NODE_ID,
             attrs: self.attributes.clone(),
             span: trait_.span,
-            vis: respan(trait_lo_sp, ast::VisibilityKind::Inherited),
+            vis: ast::Visibility {
+                span: trait_lo_sp,
+                kind: ast::VisibilityKind::Inherited,
+                tokens: None,
+            },
             ident: method_ident,
             kind: ast::AssocItemKind::Fn(def, sig, fn_generics, Some(body_block)),
             tokens: None,
@@ -1522,7 +1529,7 @@ impl<'a> TraitDef<'a> {
             }
         }
 
-        let is_tuple = if let ast::VariantData::Tuple(..) = struct_def { true } else { false };
+        let is_tuple = matches!(struct_def, ast::VariantData::Tuple(..));
         match (just_spans.is_empty(), named_idents.is_empty()) {
             (false, false) => cx.span_bug(
                 self.span,
diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs
index 7e3fd131d44..9c8e0fc2f01 100644
--- a/compiler/rustc_builtin_macros/src/deriving/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs
@@ -75,6 +75,7 @@ fn call_intrinsic(
         id: ast::DUMMY_NODE_ID,
         rules: ast::BlockCheckMode::Unsafe(ast::CompilerGenerated),
         span,
+        tokens: None,
     }))
 }
 
diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs
index 373277f525d..5d6f791f137 100644
--- a/compiler/rustc_builtin_macros/src/format.rs
+++ b/compiler/rustc_builtin_macros/src/format.rs
@@ -135,21 +135,52 @@ fn parse_args<'a>(
         return Err(ecx.struct_span_err(sp, "requires at least a format string argument"));
     }
 
-    let fmtstr = p.parse_expr()?;
+    let first_token = &p.token;
+    let fmtstr = match first_token.kind {
+        token::TokenKind::Literal(token::Lit {
+            kind: token::LitKind::Str | token::LitKind::StrRaw(_),
+            ..
+        }) => {
+            // If the first token is a string literal, then a format expression
+            // is constructed from it.
+            //
+            // This allows us to properly handle cases when the first comma
+            // after the format string is mistakenly replaced with any operator,
+            // which cause the expression parser to eat too much tokens.
+            p.parse_literal_maybe_minus()?
+        }
+        _ => {
+            // Otherwise, we fall back to the expression parser.
+            p.parse_expr()?
+        }
+    };
+
     let mut first = true;
     let mut named = false;
 
     while p.token != token::Eof {
         if !p.eat(&token::Comma) {
             if first {
-                // After `format!(""` we always expect *only* a comma...
-                let mut err = ecx.struct_span_err(p.token.span, "expected token: `,`");
-                err.span_label(p.token.span, "expected `,`");
-                p.maybe_annotate_with_ascription(&mut err, false);
-                return Err(err);
-            } else {
-                // ...after that delegate to `expect` to also include the other expected tokens.
-                let _ = p.expect(&token::Comma)?;
+                p.clear_expected_tokens();
+            }
+
+            // `Parser::expect` tries to recover using the
+            // `Parser::unexpected_try_recover` function. This function is able
+            // to recover if the expected token is a closing delimiter.
+            //
+            // As `,` is not a closing delimiter, it will always return an `Err`
+            // variant.
+            let mut err = p.expect(&token::Comma).unwrap_err();
+
+            match token::TokenKind::Comma.similar_tokens() {
+                Some(tks) if tks.contains(&p.token.kind) => {
+                    // If a similar token is found, then it may be a typo. We
+                    // consider it as a comma, and continue parsing.
+                    err.emit();
+                    p.bump();
+                }
+                // Otherwise stop the parsing and return the error.
+                _ => return Err(err),
             }
         }
         first = false;
diff --git a/compiler/rustc_builtin_macros/src/format_foreign.rs b/compiler/rustc_builtin_macros/src/format_foreign.rs
index 85cf4c42e94..ff81b5eca13 100644
--- a/compiler/rustc_builtin_macros/src/format_foreign.rs
+++ b/compiler/rustc_builtin_macros/src/format_foreign.rs
@@ -166,14 +166,14 @@ pub mod printf {
             let cap = self.span.len() + if has_options { 2 } else { 0 };
             let mut s = String::with_capacity(cap);
 
-            s.push_str("{");
+            s.push('{');
 
             if let Some(arg) = self.parameter {
                 write!(s, "{}", arg.checked_sub(1)?).ok()?;
             }
 
             if has_options {
-                s.push_str(":");
+                s.push(':');
 
                 let align = if let Some(fill) = fill {
                     s.push_str(fill);
@@ -191,11 +191,11 @@ pub mod printf {
                 }
 
                 if alt {
-                    s.push_str("#");
+                    s.push('#');
                 }
 
                 if zero_fill {
-                    s.push_str("0");
+                    s.push('0');
                 }
 
                 if let Some(width) = width {
@@ -203,7 +203,7 @@ pub mod printf {
                 }
 
                 if let Some(precision) = precision {
-                    s.push_str(".");
+                    s.push('.');
                     precision.translate(&mut s).ok()?;
                 }
 
@@ -212,7 +212,7 @@ pub mod printf {
                 }
             }
 
-            s.push_str("}");
+            s.push('}');
             Some(s)
         }
     }
@@ -518,8 +518,7 @@ pub mod printf {
                         .and_then(|end| end.at_next_cp())
                         .map(|end| (next.slice_between(end).unwrap(), end));
                     let end = match end {
-                        Some(("32", end)) => end,
-                        Some(("64", end)) => end,
+                        Some(("32" | "64", end)) => end,
                         _ => next,
                     };
                     state = Type;
diff --git a/compiler/rustc_builtin_macros/src/global_asm.rs b/compiler/rustc_builtin_macros/src/global_asm.rs
index 2465f33622e..3689e33be6f 100644
--- a/compiler/rustc_builtin_macros/src/global_asm.rs
+++ b/compiler/rustc_builtin_macros/src/global_asm.rs
@@ -14,7 +14,6 @@ use rustc_ast::token;
 use rustc_ast::tokenstream::TokenStream;
 use rustc_errors::DiagnosticBuilder;
 use rustc_expand::base::{self, *};
-use rustc_span::source_map::respan;
 use rustc_span::symbol::Ident;
 use rustc_span::Span;
 use smallvec::smallvec;
@@ -30,7 +29,11 @@ pub fn expand_global_asm<'cx>(
             attrs: Vec::new(),
             id: ast::DUMMY_NODE_ID,
             kind: ast::ItemKind::GlobalAsm(P(global_asm)),
-            vis: respan(sp.shrink_to_lo(), ast::VisibilityKind::Inherited),
+            vis: ast::Visibility {
+                span: sp.shrink_to_lo(),
+                kind: ast::VisibilityKind::Inherited,
+                tokens: None,
+            },
             span: cx.with_def_site_ctxt(sp),
             tokens: None,
         })]),
diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs
index 87be6d1743a..97cadb913ca 100644
--- a/compiler/rustc_builtin_macros/src/lib.rs
+++ b/compiler/rustc_builtin_macros/src/lib.rs
@@ -1,7 +1,7 @@
 //! This crate contains implementations of built-in macros and other code generating facilities
 //! injecting code into the crate before it is lowered to HIR.
 
-#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
+#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![feature(bool_to_option)]
 #![feature(crate_visibility_modifier)]
 #![feature(decl_macro)]
diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
index 0c6769906f3..c6ab3faf568 100644
--- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
+++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
@@ -98,7 +98,7 @@ pub fn inject(
 
 impl<'a> CollectProcMacros<'a> {
     fn check_not_pub_in_root(&self, vis: &ast::Visibility, sp: Span) {
-        if self.is_proc_macro_crate && self.in_root && vis.node.is_pub() {
+        if self.is_proc_macro_crate && self.in_root && vis.kind.is_pub() {
             self.handler.span_err(
                 sp,
                 "`proc-macro` crate types currently cannot export any items other \
@@ -184,7 +184,7 @@ impl<'a> CollectProcMacros<'a> {
             Vec::new()
         };
 
-        if self.in_root && item.vis.node.is_pub() {
+        if self.in_root && item.vis.kind.is_pub() {
             self.macros.push(ProcMacro::Derive(ProcMacroDerive {
                 id: item.id,
                 span: item.span,
@@ -204,7 +204,7 @@ impl<'a> CollectProcMacros<'a> {
     }
 
     fn collect_attr_proc_macro(&mut self, item: &'a ast::Item) {
-        if self.in_root && item.vis.node.is_pub() {
+        if self.in_root && item.vis.kind.is_pub() {
             self.macros.push(ProcMacro::Def(ProcMacroDef {
                 id: item.id,
                 span: item.span,
@@ -223,7 +223,7 @@ impl<'a> CollectProcMacros<'a> {
     }
 
     fn collect_bang_proc_macro(&mut self, item: &'a ast::Item) {
-        if self.in_root && item.vis.node.is_pub() {
+        if self.in_root && item.vis.kind.is_pub() {
             self.macros.push(ProcMacro::Def(ProcMacroDef {
                 id: item.id,
                 span: item.span,
diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs
index 8e56e80bba2..1de0b32f519 100644
--- a/compiler/rustc_builtin_macros/src/test.rs
+++ b/compiler/rustc_builtin_macros/src/test.rs
@@ -7,7 +7,6 @@ use rustc_ast::attr;
 use rustc_ast_pretty::pprust;
 use rustc_expand::base::*;
 use rustc_session::Session;
-use rustc_span::source_map::respan;
 use rustc_span::symbol::{sym, Ident, Symbol};
 use rustc_span::Span;
 
@@ -35,7 +34,11 @@ pub fn expand_test_case(
     let sp = ecx.with_def_site_ctxt(attr_sp);
     let mut item = anno_item.expect_item();
     item = item.map(|mut item| {
-        item.vis = respan(item.vis.span, ast::VisibilityKind::Public);
+        item.vis = ast::Visibility {
+            span: item.vis.span,
+            kind: ast::VisibilityKind::Public,
+            tokens: None,
+        };
         item.ident.span = item.ident.span.with_ctxt(sp.ctxt());
         item.attrs.push(ecx.attribute(ecx.meta_word(sp, sym::rustc_test_marker)));
         item
@@ -292,7 +295,7 @@ pub fn expand_test_or_bench(
         ),
     );
     test_const = test_const.map(|mut tc| {
-        tc.vis.node = ast::VisibilityKind::Public;
+        tc.vis.kind = ast::VisibilityKind::Public;
         tc
     });
 
diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs
index 0ea60665d67..0a60ca8faaa 100644
--- a/compiler/rustc_builtin_macros/src/test_harness.rs
+++ b/compiler/rustc_builtin_macros/src/test_harness.rs
@@ -10,7 +10,6 @@ use rustc_expand::expand::{AstFragment, ExpansionConfig};
 use rustc_feature::Features;
 use rustc_session::Session;
 use rustc_span::hygiene::{AstPass, SyntaxContext, Transparency};
-use rustc_span::source_map::respan;
 use rustc_span::symbol::{sym, Ident, Symbol};
 use rustc_span::{Span, DUMMY_SP};
 use rustc_target::spec::PanicStrategy;
@@ -333,7 +332,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
         attrs: vec![main_attr],
         id: ast::DUMMY_NODE_ID,
         kind: main,
-        vis: respan(sp, ast::VisibilityKind::Public),
+        vis: ast::Visibility { span: sp, kind: ast::VisibilityKind::Public, tokens: None },
         span: sp,
         tokens: None,
     });
diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml
index 38f552558c8..04792b334d5 100644
--- a/compiler/rustc_codegen_llvm/Cargo.toml
+++ b/compiler/rustc_codegen_llvm/Cargo.toml
@@ -25,7 +25,7 @@ rustc_fs_util = { path = "../rustc_fs_util" }
 rustc_hir = { path = "../rustc_hir" }
 rustc_incremental = { path = "../rustc_incremental" }
 rustc_index = { path = "../rustc_index" }
-rustc_llvm = { path = "../../src/librustc_llvm" }
+rustc_llvm = { path = "../rustc_llvm" }
 rustc_session = { path = "../rustc_session" }
 rustc_serialize = { path = "../rustc_serialize" }
 rustc_target = { path = "../rustc_target" }
diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs
index 7c710a1cb3d..4b2d5907a02 100644
--- a/compiler/rustc_codegen_llvm/src/back/lto.rs
+++ b/compiler/rustc_codegen_llvm/src/back/lto.rs
@@ -346,14 +346,14 @@ fn fat_lto(
     Ok(LtoModuleCodegen::Fat { module: Some(module), _serialized_bitcode: serialized_bitcode })
 }
 
-struct Linker<'a>(&'a mut llvm::Linker<'a>);
+crate struct Linker<'a>(&'a mut llvm::Linker<'a>);
 
 impl Linker<'a> {
-    fn new(llmod: &'a llvm::Module) -> Self {
+    crate fn new(llmod: &'a llvm::Module) -> Self {
         unsafe { Linker(llvm::LLVMRustLinkerNew(llmod)) }
     }
 
-    fn add(&mut self, bytecode: &[u8]) -> Result<(), ()> {
+    crate fn add(&mut self, bytecode: &[u8]) -> Result<(), ()> {
         unsafe {
             if llvm::LLVMRustLinkerAdd(
                 self.0,
diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs
index 6f386c1287c..937821e9d4f 100644
--- a/compiler/rustc_codegen_llvm/src/back/write.rs
+++ b/compiler/rustc_codegen_llvm/src/back/write.rs
@@ -617,6 +617,31 @@ unsafe fn add_sanitizer_passes(config: &ModuleConfig, passes: &mut Vec<&'static
     }
 }
 
+pub(crate) fn link(
+    cgcx: &CodegenContext<LlvmCodegenBackend>,
+    diag_handler: &Handler,
+    mut modules: Vec<ModuleCodegen<ModuleLlvm>>,
+) -> Result<ModuleCodegen<ModuleLlvm>, FatalError> {
+    use super::lto::{Linker, ModuleBuffer};
+    // Sort the modules by name to ensure to ensure deterministic behavior.
+    modules.sort_by(|a, b| a.name.cmp(&b.name));
+    let (first, elements) =
+        modules.split_first().expect("Bug! modules must contain at least one module.");
+
+    let mut linker = Linker::new(first.module_llvm.llmod());
+    for module in elements {
+        let _timer =
+            cgcx.prof.generic_activity_with_arg("LLVM_link_module", format!("{:?}", module.name));
+        let buffer = ModuleBuffer::new(module.module_llvm.llmod());
+        linker.add(&buffer.data()).map_err(|()| {
+            let msg = format!("failed to serialize module {:?}", module.name);
+            llvm_err(&diag_handler, &msg)
+        })?;
+    }
+    drop(linker);
+    Ok(modules.remove(0))
+}
+
 pub(crate) unsafe fn codegen(
     cgcx: &CodegenContext<LlvmCodegenBackend>,
     diag_handler: &Handler,
diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs
index 6a1b373ef07..f35708b1d09 100644
--- a/compiler/rustc_codegen_llvm/src/base.rs
+++ b/compiler/rustc_codegen_llvm/src/base.rs
@@ -108,7 +108,7 @@ pub fn compile_codegen_unit(
 
     // We assume that the cost to run LLVM on a CGU is proportional to
     // the time we needed for codegenning it.
-    let cost = time_to_codegen.as_secs() * 1_000_000_000 + time_to_codegen.subsec_nanos() as u64;
+    let cost = time_to_codegen.as_nanos() as u64;
 
     fn module_codegen(tcx: TyCtxt<'_>, cgu_name: Symbol) -> ModuleCodegen<ModuleLlvm> {
         let cgu = tcx.codegen_unit(cgu_name);
diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs
index 4ece08f6293..23a3be1a2f2 100644
--- a/compiler/rustc_codegen_llvm/src/builder.rs
+++ b/compiler/rustc_codegen_llvm/src/builder.rs
@@ -6,7 +6,6 @@ use crate::type_::Type;
 use crate::type_of::LayoutLlvmExt;
 use crate::value::Value;
 use libc::{c_char, c_uint};
-use rustc_codegen_ssa::base::to_immediate;
 use rustc_codegen_ssa::common::{IntPredicate, RealPredicate, TypeKind};
 use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue};
 use rustc_codegen_ssa::mir::place::PlaceRef;
@@ -307,10 +306,10 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
         use rustc_ast::UintTy::*;
         use rustc_middle::ty::{Int, Uint};
 
-        let new_kind = match ty.kind {
+        let new_kind = match ty.kind() {
             Int(t @ Isize) => Int(t.normalize(self.tcx.sess.target.ptr_width)),
             Uint(t @ Usize) => Uint(t.normalize(self.tcx.sess.target.ptr_width)),
-            ref t @ (Uint(_) | Int(_)) => t.clone(),
+            t @ (Uint(_) | Int(_)) => t.clone(),
             _ => panic!("tried to get overflow intrinsic for op applied to non-int type"),
         };
 
@@ -367,6 +366,20 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
         (self.extract_value(res, 0), self.extract_value(res, 1))
     }
 
+    fn from_immediate(&mut self, val: Self::Value) -> Self::Value {
+        if self.cx().val_ty(val) == self.cx().type_i1() {
+            self.zext(val, self.cx().type_i8())
+        } else {
+            val
+        }
+    }
+    fn to_immediate_scalar(&mut self, val: Self::Value, scalar: &abi::Scalar) -> Self::Value {
+        if scalar.is_bool() {
+            return self.trunc(val, self.cx().type_i1());
+        }
+        val
+    }
+
     fn alloca(&mut self, ty: &'ll Type, align: Align) -> &'ll Value {
         let mut bx = Builder::with_cx(self.cx);
         bx.position_at_start(unsafe { llvm::LLVMGetFirstBasicBlock(self.llfn()) });
@@ -471,7 +484,7 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
                 }
                 load
             });
-            OperandValue::Immediate(to_immediate(self, llval, place.layout))
+            OperandValue::Immediate(self.to_immediate(llval, place.layout))
         } else if let abi::Abi::ScalarPair(ref a, ref b) = place.layout.abi {
             let b_offset = a.value.size(self).align_to(b.value.align(self).abi);
 
@@ -479,7 +492,7 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
                 let llptr = self.struct_gep(place.llval, i as u64);
                 let load = self.load(llptr, align);
                 scalar_load_metadata(self, load, scalar);
-                if scalar.is_bool() { self.trunc(load, self.type_i1()) } else { load }
+                self.to_immediate_scalar(load, scalar)
             };
 
             OperandValue::Pair(
diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs
index 86a5ec59254..6d3582d3027 100644
--- a/compiler/rustc_codegen_llvm/src/consts.rs
+++ b/compiler/rustc_codegen_llvm/src/consts.rs
@@ -7,12 +7,13 @@ use crate::type_of::LayoutLlvmExt;
 use crate::value::Value;
 use libc::c_uint;
 use rustc_codegen_ssa::traits::*;
+use rustc_data_structures::const_cstr;
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_hir::Node;
 use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
 use rustc_middle::mir::interpret::{
-    read_target_uint, Allocation, ConstValue, ErrorHandled, GlobalAlloc, Pointer,
+    read_target_uint, Allocation, ErrorHandled, GlobalAlloc, Pointer,
 };
 use rustc_middle::mir::mono::MonoItem;
 use rustc_middle::ty::{self, Instance, Ty};
@@ -22,8 +23,6 @@ use rustc_span::Span;
 use rustc_target::abi::{AddressSpace, Align, HasDataLayout, LayoutOf, Primitive, Scalar, Size};
 use tracing::debug;
 
-use std::ffi::CStr;
-
 pub fn const_alloc_to_llvm(cx: &CodegenCx<'ll, '_>, alloc: &Allocation) -> &'ll Value {
     let mut llvals = Vec::with_capacity(alloc.relocations().len() + 1);
     let dl = cx.data_layout();
@@ -85,10 +84,7 @@ pub fn codegen_static_initializer(
     cx: &CodegenCx<'ll, 'tcx>,
     def_id: DefId,
 ) -> Result<(&'ll Value, &'tcx Allocation), ErrorHandled> {
-    let alloc = match cx.tcx.const_eval_poly(def_id)? {
-        ConstValue::ByRef { alloc, offset } if offset.bytes() == 0 => alloc,
-        val => bug!("static const eval returned {:#?}", val),
-    };
+    let alloc = cx.tcx.eval_static_initializer(def_id)?;
     Ok((const_alloc_to_llvm(cx, alloc), alloc))
 }
 
@@ -125,7 +121,7 @@ fn check_and_apply_linkage(
         // extern "C" fn() from being non-null, so we can't just declare a
         // static and call it a day. Some linkages (like weak) will make it such
         // that the static actually has a null value.
-        let llty2 = if let ty::RawPtr(ref mt) = ty.kind {
+        let llty2 = if let ty::RawPtr(ref mt) = ty.kind() {
             cx.layout_of(mt.ty).llvm_type(cx)
         } else {
             cx.sess().span_fatal(
@@ -457,9 +453,9 @@ impl StaticMethods for CodegenCx<'ll, 'tcx> {
                             .all(|&byte| byte == 0);
 
                     let sect_name = if all_bytes_are_zero {
-                        CStr::from_bytes_with_nul_unchecked(b"__DATA,__thread_bss\0")
+                        const_cstr!("__DATA,__thread_bss")
                     } else {
-                        CStr::from_bytes_with_nul_unchecked(b"__DATA,__thread_data\0")
+                        const_cstr!("__DATA,__thread_data")
                     };
                     llvm::LLVMSetSection(g, sect_name.as_ptr());
                 }
diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs
index 1c51a9df5d8..1696f35563d 100644
--- a/compiler/rustc_codegen_llvm/src/context.rs
+++ b/compiler/rustc_codegen_llvm/src/context.rs
@@ -433,6 +433,17 @@ impl MiscMethods<'tcx> for CodegenCx<'ll, 'tcx> {
             llvm::LLVMSetSection(g, section.as_ptr());
         }
     }
+
+    fn declare_c_main(&self, fn_type: Self::Type) -> Option<Self::Function> {
+        if self.get_declared_value("main").is_none() {
+            Some(self.declare_cfn("main", fn_type))
+        } else {
+            // If the symbol already exists, it is an error: for example, the user wrote
+            // #[no_mangle] extern "C" fn main(..) {..}
+            // instead of #[start]
+            None
+        }
+    }
 }
 
 impl CodegenCx<'b, 'tcx> {
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
index 9d92d53775c..868eb74cf09 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
@@ -343,7 +343,7 @@ fn fixed_vec_metadata(
 
     let (size, align) = cx.size_and_align_of(array_or_slice_type);
 
-    let upper_bound = match array_or_slice_type.kind {
+    let upper_bound = match array_or_slice_type.kind() {
         ty::Array(_, len) => len.eval_usize(cx.tcx, ty::ParamEnv::reveal_all()) as c_longlong,
         _ => -1,
     };
@@ -432,7 +432,7 @@ fn subroutine_type_metadata(
 
     let signature_metadata: Vec<_> = iter::once(
         // return type
-        match signature.output().kind {
+        match signature.output().kind() {
             ty::Tuple(ref tys) if tys.is_empty() => None,
             _ => Some(type_metadata(cx, signature.output(), span)),
         },
@@ -472,7 +472,7 @@ fn trait_pointer_metadata(
     // type is assigned the correct name, size, namespace, and source location.
     // However, it does not describe the trait's methods.
 
-    let containing_scope = match trait_type.kind {
+    let containing_scope = match trait_type.kind() {
         ty::Dynamic(ref data, ..) => {
             data.principal_def_id().map(|did| get_namespace_for_item(cx, did))
         }
@@ -572,7 +572,7 @@ pub fn type_metadata(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>, usage_site_span: Sp
 
     debug!("type_metadata: {:?}", t);
 
-    let ptr_metadata = |ty: Ty<'tcx>| match ty.kind {
+    let ptr_metadata = |ty: Ty<'tcx>| match *ty.kind() {
         ty::Slice(typ) => Ok(vec_slice_metadata(cx, t, typ, unique_type_id, usage_site_span)),
         ty::Str => Ok(vec_slice_metadata(cx, t, cx.tcx.types.u8, unique_type_id, usage_site_span)),
         ty::Dynamic(..) => Ok(MetadataCreationResult::new(
@@ -592,7 +592,7 @@ pub fn type_metadata(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>, usage_site_span: Sp
         }
     };
 
-    let MetadataCreationResult { metadata, already_stored_in_typemap } = match t.kind {
+    let MetadataCreationResult { metadata, already_stored_in_typemap } = match *t.kind() {
         ty::Never | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) => {
             MetadataCreationResult::new(basic_type_metadata(cx, t), false)
         }
@@ -876,7 +876,7 @@ fn basic_type_metadata(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll DIType {
     // .natvis visualizers (and perhaps other existing native debuggers?)
     let msvc_like_names = cx.tcx.sess.target.target.options.is_like_msvc;
 
-    let (name, encoding) = match t.kind {
+    let (name, encoding) = match t.kind() {
         ty::Never => ("!", DW_ATE_unsigned),
         ty::Tuple(ref elements) if elements.is_empty() => ("()", DW_ATE_unsigned),
         ty::Bool => ("bool", DW_ATE_boolean),
@@ -904,7 +904,7 @@ fn basic_type_metadata(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll DIType {
         return ty_metadata;
     }
 
-    let typedef_name = match t.kind {
+    let typedef_name = match t.kind() {
         ty::Int(int_ty) => int_ty.name_str(),
         ty::Uint(uint_ty) => uint_ty.name_str(),
         ty::Float(float_ty) => float_ty.name_str(),
@@ -1239,7 +1239,7 @@ fn prepare_struct_metadata(
 ) -> RecursiveTypeDescription<'ll, 'tcx> {
     let struct_name = compute_debuginfo_type_name(cx.tcx, struct_type, false);
 
-    let (struct_def_id, variant) = match struct_type.kind {
+    let (struct_def_id, variant) = match struct_type.kind() {
         ty::Adt(def, _) => (def.did, def.non_enum_variant()),
         _ => bug!("prepare_struct_metadata on a non-ADT"),
     };
@@ -1373,7 +1373,7 @@ fn prepare_union_metadata(
 ) -> RecursiveTypeDescription<'ll, 'tcx> {
     let union_name = compute_debuginfo_type_name(cx.tcx, union_type, false);
 
-    let (union_def_id, variant) = match union_type.kind {
+    let (union_def_id, variant) = match union_type.kind() {
         ty::Adt(def, _) => (def.did, def.non_enum_variant()),
         _ => bug!("prepare_union_metadata on a non-ADT"),
     };
@@ -1457,14 +1457,14 @@ struct EnumMemberDescriptionFactory<'ll, 'tcx> {
 
 impl EnumMemberDescriptionFactory<'ll, 'tcx> {
     fn create_member_descriptions(&self, cx: &CodegenCx<'ll, 'tcx>) -> Vec<MemberDescription<'ll>> {
-        let generator_variant_info_data = match self.enum_type.kind {
+        let generator_variant_info_data = match *self.enum_type.kind() {
             ty::Generator(def_id, ..) => {
                 Some(generator_layout_and_saved_local_names(cx.tcx, def_id))
             }
             _ => None,
         };
 
-        let variant_info_for = |index: VariantIdx| match self.enum_type.kind {
+        let variant_info_for = |index: VariantIdx| match *self.enum_type.kind() {
             ty::Adt(adt, _) => VariantInfo::Adt(&adt.variants[index]),
             ty::Generator(def_id, _, _) => {
                 let (generator_layout, generator_saved_local_names) =
@@ -1486,14 +1486,14 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> {
         } else {
             type_metadata(cx, self.enum_type, self.span)
         };
-        let flags = match self.enum_type.kind {
+        let flags = match self.enum_type.kind() {
             ty::Generator(..) => DIFlags::FlagArtificial,
             _ => DIFlags::FlagZero,
         };
 
         match self.layout.variants {
             Variants::Single { index } => {
-                if let ty::Adt(adt, _) = &self.enum_type.kind {
+                if let ty::Adt(adt, _) = self.enum_type.kind() {
                     if adt.variants.is_empty() {
                         return vec![];
                     }
@@ -1942,7 +1942,7 @@ fn prepare_enum_metadata(
     let tcx = cx.tcx;
     let enum_name = compute_debuginfo_type_name(tcx, enum_type, false);
     // FIXME(tmandry): This doesn't seem to have any effect.
-    let enum_flags = match enum_type.kind {
+    let enum_flags = match enum_type.kind() {
         ty::Generator(..) => DIFlags::FlagArtificial,
         _ => DIFlags::FlagZero,
     };
@@ -1957,13 +1957,13 @@ fn prepare_enum_metadata(
     let file_metadata = unknown_file_metadata(cx);
 
     let discriminant_type_metadata = |discr: Primitive| {
-        let enumerators_metadata: Vec<_> = match enum_type.kind {
+        let enumerators_metadata: Vec<_> = match enum_type.kind() {
             ty::Adt(def, _) => def
                 .discriminants(tcx)
                 .zip(&def.variants)
                 .map(|((_, discr), v)| {
                     let name = v.ident.as_str();
-                    let is_unsigned = match discr.ty.kind {
+                    let is_unsigned = match discr.ty.kind() {
                         ty::Int(_) => false,
                         ty::Uint(_) => true,
                         _ => bug!("non integer discriminant"),
@@ -2012,7 +2012,7 @@ fn prepare_enum_metadata(
                     type_metadata(cx, discr.to_ty(tcx), rustc_span::DUMMY_SP);
 
                 let item_name;
-                let discriminant_name = match enum_type.kind {
+                let discriminant_name = match enum_type.kind() {
                     ty::Adt(..) => {
                         item_name = tcx.item_name(enum_def_id).as_str();
                         &*item_name
@@ -2105,7 +2105,7 @@ fn prepare_enum_metadata(
         );
     }
 
-    let discriminator_name = match &enum_type.kind {
+    let discriminator_name = match enum_type.kind() {
         ty::Generator(..) => "__state",
         _ => "",
     };
@@ -2328,7 +2328,7 @@ fn set_members_of_composite_type(
 
 /// Computes the type parameters for a type, if any, for the given metadata.
 fn compute_type_parameters(cx: &CodegenCx<'ll, 'tcx>, ty: Ty<'tcx>) -> Option<&'ll DIArray> {
-    if let ty::Adt(def, substs) = ty.kind {
+    if let ty::Adt(def, substs) = *ty.kind() {
         if substs.types().next().is_some() {
             let generics = cx.tcx.generics_of(def.did);
             let names = get_parameter_names(cx, generics);
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
index b414426af8c..7cdd366175d 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
@@ -361,9 +361,9 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
                 // already inaccurate due to ABI adjustments (see #42800).
                 signature.extend(fn_abi.args.iter().map(|arg| {
                     let t = arg.layout.ty;
-                    let t = match t.kind {
+                    let t = match t.kind() {
                         ty::Array(ct, _)
-                            if (ct == cx.tcx.types.u8) || cx.layout_of(ct).is_zst() =>
+                            if (*ct == cx.tcx.types.u8) || cx.layout_of(ct).is_zst() =>
                         {
                             cx.tcx.mk_imm_ptr(ct)
                         }
@@ -467,7 +467,7 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
 
                     // Only "class" methods are generally understood by LLVM,
                     // so avoid methods on other types (e.g., `<*mut T>::null`).
-                    match impl_self_ty.kind {
+                    match impl_self_ty.kind() {
                         ty::Adt(def, ..) if !def.is_box() => {
                             // Again, only create type information if full debuginfo is enabled
                             if cx.sess().opts.debuginfo == DebugInfo::Full
diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs
index ec42bd4a039..a3d6882940a 100644
--- a/compiler/rustc_codegen_llvm/src/declare.rs
+++ b/compiler/rustc_codegen_llvm/src/declare.rs
@@ -51,17 +51,32 @@ fn declare_raw_fn(
     llfn
 }
 
-impl DeclareMethods<'tcx> for CodegenCx<'ll, 'tcx> {
-    fn declare_global(&self, name: &str, ty: &'ll Type) -> &'ll Value {
+impl CodegenCx<'ll, 'tcx> {
+    /// Declare a global value.
+    ///
+    /// If there’s a value with the same name already declared, the function will
+    /// return its Value instead.
+    pub fn declare_global(&self, name: &str, ty: &'ll Type) -> &'ll Value {
         debug!("declare_global(name={:?})", name);
         unsafe { llvm::LLVMRustGetOrInsertGlobal(self.llmod, name.as_ptr().cast(), name.len(), ty) }
     }
 
-    fn declare_cfn(&self, name: &str, fn_type: &'ll Type) -> &'ll Value {
+    /// Declare a C ABI function.
+    ///
+    /// Only use this for foreign function ABIs and glue. For Rust functions use
+    /// `declare_fn` instead.
+    ///
+    /// If there’s a value with the same name already declared, the function will
+    /// update the declaration and return existing Value instead.
+    pub fn declare_cfn(&self, name: &str, fn_type: &'ll Type) -> &'ll Value {
         declare_raw_fn(self, name, llvm::CCallConv, fn_type)
     }
 
-    fn declare_fn(&self, name: &str, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> &'ll Value {
+    /// Declare a Rust function.
+    ///
+    /// If there’s a value with the same name already declared, the function will
+    /// update the declaration and return existing Value instead.
+    pub fn declare_fn(&self, name: &str, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> &'ll Value {
         debug!("declare_rust_fn(name={:?}, fn_abi={:?})", name, fn_abi);
 
         let llfn = declare_raw_fn(self, name, fn_abi.llvm_cconv(), fn_abi.llvm_type(self));
@@ -69,7 +84,13 @@ impl DeclareMethods<'tcx> for CodegenCx<'ll, 'tcx> {
         llfn
     }
 
-    fn define_global(&self, name: &str, ty: &'ll Type) -> Option<&'ll Value> {
+    /// Declare a global with an intention to define it.
+    ///
+    /// Use this function when you intend to define a global. This function will
+    /// return `None` if the name already has a definition associated with it. In that
+    /// case an error should be reported to the user, because it usually happens due
+    /// to user’s fault (e.g., misuse of `#[no_mangle]` or `#[export_name]` attributes).
+    pub fn define_global(&self, name: &str, ty: &'ll Type) -> Option<&'ll Value> {
         if self.get_defined_value(name).is_some() {
             None
         } else {
@@ -77,16 +98,22 @@ impl DeclareMethods<'tcx> for CodegenCx<'ll, 'tcx> {
         }
     }
 
-    fn define_private_global(&self, ty: &'ll Type) -> &'ll Value {
+    /// Declare a private global
+    ///
+    /// Use this function when you intend to define a global without a name.
+    pub fn define_private_global(&self, ty: &'ll Type) -> &'ll Value {
         unsafe { llvm::LLVMRustInsertPrivateGlobal(self.llmod, ty) }
     }
 
-    fn get_declared_value(&self, name: &str) -> Option<&'ll Value> {
+    /// Gets declared value by name.
+    pub fn get_declared_value(&self, name: &str) -> Option<&'ll Value> {
         debug!("get_declared_value(name={:?})", name);
         unsafe { llvm::LLVMRustGetNamedValue(self.llmod, name.as_ptr().cast(), name.len()) }
     }
 
-    fn get_defined_value(&self, name: &str) -> Option<&'ll Value> {
+    /// Gets defined or externally defined (AvailableExternally linkage) value by
+    /// name.
+    pub fn get_defined_value(&self, name: &str) -> Option<&'ll Value> {
         self.get_declared_value(name).and_then(|val| {
             let declaration = unsafe { llvm::LLVMIsDeclaration(val) != 0 };
             if !declaration { Some(val) } else { None }
diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs
index c1dfb83b135..7f5b09eac4f 100644
--- a/compiler/rustc_codegen_llvm/src/intrinsic.rs
+++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs
@@ -7,15 +7,12 @@ use crate::type_of::LayoutLlvmExt;
 use crate::va_arg::emit_va_arg;
 use crate::value::Value;
 
-use rustc_ast as ast;
-use rustc_codegen_ssa::base::{compare_simd_types, to_immediate, wants_msvc_seh};
+use rustc_codegen_ssa::base::{compare_simd_types, wants_msvc_seh};
 use rustc_codegen_ssa::common::span_invalid_monomorphization_error;
 use rustc_codegen_ssa::common::{IntPredicate, TypeKind};
-use rustc_codegen_ssa::glue;
-use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue};
+use rustc_codegen_ssa::mir::operand::OperandRef;
 use rustc_codegen_ssa::mir::place::PlaceRef;
 use rustc_codegen_ssa::traits::*;
-use rustc_codegen_ssa::MemFlags;
 use rustc_hir as hir;
 use rustc_middle::ty::layout::{FnAbiExt, HasTyCtxt};
 use rustc_middle::ty::{self, Ty};
@@ -71,8 +68,6 @@ fn get_simple_intrinsic(cx: &CodegenCx<'ll, '_>, name: Symbol) -> Option<&'ll Va
         sym::nearbyintf64 => "llvm.nearbyint.f64",
         sym::roundf32 => "llvm.round.f32",
         sym::roundf64 => "llvm.round.f64",
-        sym::assume => "llvm.assume",
-        sym::abort => "llvm.trap",
         _ => return None,
     };
     Some(cx.get_intrinsic(&llvm_name))
@@ -90,7 +85,7 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
         let tcx = self.tcx;
         let callee_ty = instance.ty(tcx, ty::ParamEnv::reveal_all());
 
-        let (def_id, substs) = match callee_ty.kind {
+        let (def_id, substs) = match *callee_ty.kind() {
             ty::FnDef(def_id, substs) => (def_id, substs),
             _ => bug!("expected fn item type, found {}", callee_ty),
         };
@@ -112,9 +107,6 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
                 &args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(),
                 None,
             ),
-            sym::unreachable => {
-                return;
-            }
             sym::likely => {
                 let expect = self.get_intrinsic(&("llvm.expect.i1"));
                 self.call(expect, &[args[0].immediate(), self.const_bool(true)], None)
@@ -137,8 +129,6 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
                 let llfn = self.get_intrinsic(&("llvm.debugtrap"));
                 self.call(llfn, &[], None)
             }
-            sym::va_start => self.va_start(args[0].immediate()),
-            sym::va_end => self.va_end(args[0].immediate()),
             sym::va_copy => {
                 let intrinsic = self.cx().get_intrinsic(&("llvm.va_copy"));
                 self.call(intrinsic, &[args[0].immediate(), args[1].immediate()], None)
@@ -169,123 +159,7 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
                     _ => bug!("the va_arg intrinsic does not work with non-scalar types"),
                 }
             }
-            sym::size_of_val => {
-                let tp_ty = substs.type_at(0);
-                if let OperandValue::Pair(_, meta) = args[0].val {
-                    let (llsize, _) = glue::size_and_align_of_dst(self, tp_ty, Some(meta));
-                    llsize
-                } else {
-                    self.const_usize(self.size_of(tp_ty).bytes())
-                }
-            }
-            sym::min_align_of_val => {
-                let tp_ty = substs.type_at(0);
-                if let OperandValue::Pair(_, meta) = args[0].val {
-                    let (_, llalign) = glue::size_and_align_of_dst(self, tp_ty, Some(meta));
-                    llalign
-                } else {
-                    self.const_usize(self.align_of(tp_ty).bytes())
-                }
-            }
-            sym::size_of
-            | sym::pref_align_of
-            | sym::min_align_of
-            | sym::needs_drop
-            | sym::type_id
-            | sym::type_name
-            | sym::variant_count => {
-                let value = self
-                    .tcx
-                    .const_eval_instance(ty::ParamEnv::reveal_all(), instance, None)
-                    .unwrap();
-                OperandRef::from_const(self, value, ret_ty).immediate_or_packed_pair(self)
-            }
-            // Effectively no-op
-            sym::forget => {
-                return;
-            }
-            sym::offset => {
-                let ptr = args[0].immediate();
-                let offset = args[1].immediate();
-                self.inbounds_gep(ptr, &[offset])
-            }
-            sym::arith_offset => {
-                let ptr = args[0].immediate();
-                let offset = args[1].immediate();
-                self.gep(ptr, &[offset])
-            }
 
-            sym::copy_nonoverlapping => {
-                copy_intrinsic(
-                    self,
-                    false,
-                    false,
-                    substs.type_at(0),
-                    args[1].immediate(),
-                    args[0].immediate(),
-                    args[2].immediate(),
-                );
-                return;
-            }
-            sym::copy => {
-                copy_intrinsic(
-                    self,
-                    true,
-                    false,
-                    substs.type_at(0),
-                    args[1].immediate(),
-                    args[0].immediate(),
-                    args[2].immediate(),
-                );
-                return;
-            }
-            sym::write_bytes => {
-                memset_intrinsic(
-                    self,
-                    false,
-                    substs.type_at(0),
-                    args[0].immediate(),
-                    args[1].immediate(),
-                    args[2].immediate(),
-                );
-                return;
-            }
-
-            sym::volatile_copy_nonoverlapping_memory => {
-                copy_intrinsic(
-                    self,
-                    false,
-                    true,
-                    substs.type_at(0),
-                    args[0].immediate(),
-                    args[1].immediate(),
-                    args[2].immediate(),
-                );
-                return;
-            }
-            sym::volatile_copy_memory => {
-                copy_intrinsic(
-                    self,
-                    true,
-                    true,
-                    substs.type_at(0),
-                    args[0].immediate(),
-                    args[1].immediate(),
-                    args[2].immediate(),
-                );
-                return;
-            }
-            sym::volatile_set_memory => {
-                memset_intrinsic(
-                    self,
-                    true,
-                    substs.type_at(0),
-                    args[0].immediate(),
-                    args[1].immediate(),
-                    args[2].immediate(),
-                );
-                return;
-            }
             sym::volatile_load | sym::unaligned_volatile_load => {
                 let tp_ty = substs.type_at(0);
                 let mut ptr = args[0].immediate();
@@ -301,7 +175,7 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
                 unsafe {
                     llvm::LLVMSetAlignment(load, align);
                 }
-                to_immediate(self, load, self.layout_of(tp_ty))
+                self.to_immediate(load, self.layout_of(tp_ty))
             }
             sym::volatile_store => {
                 let dst = args[0].deref(self.cx());
@@ -343,20 +217,6 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
             | sym::ctpop
             | sym::bswap
             | sym::bitreverse
-            | sym::add_with_overflow
-            | sym::sub_with_overflow
-            | sym::mul_with_overflow
-            | sym::wrapping_add
-            | sym::wrapping_sub
-            | sym::wrapping_mul
-            | sym::unchecked_div
-            | sym::unchecked_rem
-            | sym::unchecked_shl
-            | sym::unchecked_shr
-            | sym::unchecked_add
-            | sym::unchecked_sub
-            | sym::unchecked_mul
-            | sym::exact_div
             | sym::rotate_left
             | sym::rotate_right
             | sym::saturating_add
@@ -396,84 +256,6 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
                             &[args[0].immediate()],
                             None,
                         ),
-                        sym::add_with_overflow
-                        | sym::sub_with_overflow
-                        | sym::mul_with_overflow => {
-                            let intrinsic = format!(
-                                "llvm.{}{}.with.overflow.i{}",
-                                if signed { 's' } else { 'u' },
-                                &name_str[..3],
-                                width
-                            );
-                            let llfn = self.get_intrinsic(&intrinsic);
-
-                            // Convert `i1` to a `bool`, and write it to the out parameter
-                            let pair =
-                                self.call(llfn, &[args[0].immediate(), args[1].immediate()], None);
-                            let val = self.extract_value(pair, 0);
-                            let overflow = self.extract_value(pair, 1);
-                            let overflow = self.zext(overflow, self.type_bool());
-
-                            let dest = result.project_field(self, 0);
-                            self.store(val, dest.llval, dest.align);
-                            let dest = result.project_field(self, 1);
-                            self.store(overflow, dest.llval, dest.align);
-
-                            return;
-                        }
-                        sym::wrapping_add => self.add(args[0].immediate(), args[1].immediate()),
-                        sym::wrapping_sub => self.sub(args[0].immediate(), args[1].immediate()),
-                        sym::wrapping_mul => self.mul(args[0].immediate(), args[1].immediate()),
-                        sym::exact_div => {
-                            if signed {
-                                self.exactsdiv(args[0].immediate(), args[1].immediate())
-                            } else {
-                                self.exactudiv(args[0].immediate(), args[1].immediate())
-                            }
-                        }
-                        sym::unchecked_div => {
-                            if signed {
-                                self.sdiv(args[0].immediate(), args[1].immediate())
-                            } else {
-                                self.udiv(args[0].immediate(), args[1].immediate())
-                            }
-                        }
-                        sym::unchecked_rem => {
-                            if signed {
-                                self.srem(args[0].immediate(), args[1].immediate())
-                            } else {
-                                self.urem(args[0].immediate(), args[1].immediate())
-                            }
-                        }
-                        sym::unchecked_shl => self.shl(args[0].immediate(), args[1].immediate()),
-                        sym::unchecked_shr => {
-                            if signed {
-                                self.ashr(args[0].immediate(), args[1].immediate())
-                            } else {
-                                self.lshr(args[0].immediate(), args[1].immediate())
-                            }
-                        }
-                        sym::unchecked_add => {
-                            if signed {
-                                self.unchecked_sadd(args[0].immediate(), args[1].immediate())
-                            } else {
-                                self.unchecked_uadd(args[0].immediate(), args[1].immediate())
-                            }
-                        }
-                        sym::unchecked_sub => {
-                            if signed {
-                                self.unchecked_ssub(args[0].immediate(), args[1].immediate())
-                            } else {
-                                self.unchecked_usub(args[0].immediate(), args[1].immediate())
-                            }
-                        }
-                        sym::unchecked_mul => {
-                            if signed {
-                                self.unchecked_smul(args[0].immediate(), args[1].immediate())
-                            } else {
-                                self.unchecked_umul(args[0].immediate(), args[1].immediate())
-                            }
-                        }
                         sym::rotate_left | sym::rotate_right => {
                             let is_left = name == sym::rotate_left;
                             let val = args[0].immediate();
@@ -513,75 +295,6 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
                     }
                 }
             }
-            sym::fadd_fast | sym::fsub_fast | sym::fmul_fast | sym::fdiv_fast | sym::frem_fast => {
-                match float_type_width(arg_tys[0]) {
-                    Some(_width) => match name {
-                        sym::fadd_fast => self.fadd_fast(args[0].immediate(), args[1].immediate()),
-                        sym::fsub_fast => self.fsub_fast(args[0].immediate(), args[1].immediate()),
-                        sym::fmul_fast => self.fmul_fast(args[0].immediate(), args[1].immediate()),
-                        sym::fdiv_fast => self.fdiv_fast(args[0].immediate(), args[1].immediate()),
-                        sym::frem_fast => self.frem_fast(args[0].immediate(), args[1].immediate()),
-                        _ => bug!(),
-                    },
-                    None => {
-                        span_invalid_monomorphization_error(
-                            tcx.sess,
-                            span,
-                            &format!(
-                                "invalid monomorphization of `{}` intrinsic: \
-                                      expected basic float type, found `{}`",
-                                name, arg_tys[0]
-                            ),
-                        );
-                        return;
-                    }
-                }
-            }
-
-            sym::float_to_int_unchecked => {
-                if float_type_width(arg_tys[0]).is_none() {
-                    span_invalid_monomorphization_error(
-                        tcx.sess,
-                        span,
-                        &format!(
-                            "invalid monomorphization of `float_to_int_unchecked` \
-                                  intrinsic: expected basic float type, \
-                                  found `{}`",
-                            arg_tys[0]
-                        ),
-                    );
-                    return;
-                }
-                let (width, signed) = match int_type_width_signed(ret_ty, self.cx) {
-                    Some(pair) => pair,
-                    None => {
-                        span_invalid_monomorphization_error(
-                            tcx.sess,
-                            span,
-                            &format!(
-                                "invalid monomorphization of `float_to_int_unchecked` \
-                                      intrinsic:  expected basic integer type, \
-                                      found `{}`",
-                                ret_ty
-                            ),
-                        );
-                        return;
-                    }
-                };
-                if signed {
-                    self.fptosi(args[0].immediate(), self.cx.type_ix(width))
-                } else {
-                    self.fptoui(args[0].immediate(), self.cx.type_ix(width))
-                }
-            }
-
-            sym::discriminant_value => {
-                if ret_ty.is_integral() {
-                    args[0].deref(self.cx()).codegen_get_discr(self, ret_ty)
-                } else {
-                    span_bug!(span, "Invalid discriminant type for `{:?}`", arg_tys[0])
-                }
-            }
 
             _ if name_str.starts_with("simd_") => {
                 match generic_simd_intrinsic(self, name, callee_ty, args, ret_ty, llret_ty, span) {
@@ -589,174 +302,6 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
                     Err(()) => return,
                 }
             }
-            // This requires that atomic intrinsics follow a specific naming pattern:
-            // "atomic_<operation>[_<ordering>]", and no ordering means SeqCst
-            name if name_str.starts_with("atomic_") => {
-                use rustc_codegen_ssa::common::AtomicOrdering::*;
-                use rustc_codegen_ssa::common::{AtomicRmwBinOp, SynchronizationScope};
-
-                let split: Vec<&str> = name_str.split('_').collect();
-
-                let is_cxchg = split[1] == "cxchg" || split[1] == "cxchgweak";
-                let (order, failorder) = match split.len() {
-                    2 => (SequentiallyConsistent, SequentiallyConsistent),
-                    3 => match split[2] {
-                        "unordered" => (Unordered, Unordered),
-                        "relaxed" => (Monotonic, Monotonic),
-                        "acq" => (Acquire, Acquire),
-                        "rel" => (Release, Monotonic),
-                        "acqrel" => (AcquireRelease, Acquire),
-                        "failrelaxed" if is_cxchg => (SequentiallyConsistent, Monotonic),
-                        "failacq" if is_cxchg => (SequentiallyConsistent, Acquire),
-                        _ => self.sess().fatal("unknown ordering in atomic intrinsic"),
-                    },
-                    4 => match (split[2], split[3]) {
-                        ("acq", "failrelaxed") if is_cxchg => (Acquire, Monotonic),
-                        ("acqrel", "failrelaxed") if is_cxchg => (AcquireRelease, Monotonic),
-                        _ => self.sess().fatal("unknown ordering in atomic intrinsic"),
-                    },
-                    _ => self.sess().fatal("Atomic intrinsic not in correct format"),
-                };
-
-                let invalid_monomorphization = |ty| {
-                    span_invalid_monomorphization_error(
-                        tcx.sess,
-                        span,
-                        &format!(
-                            "invalid monomorphization of `{}` intrinsic: \
-                                  expected basic integer type, found `{}`",
-                            name, ty
-                        ),
-                    );
-                };
-
-                match split[1] {
-                    "cxchg" | "cxchgweak" => {
-                        let ty = substs.type_at(0);
-                        if int_type_width_signed(ty, self).is_some() {
-                            let weak = split[1] == "cxchgweak";
-                            let pair = self.atomic_cmpxchg(
-                                args[0].immediate(),
-                                args[1].immediate(),
-                                args[2].immediate(),
-                                order,
-                                failorder,
-                                weak,
-                            );
-                            let val = self.extract_value(pair, 0);
-                            let success = self.extract_value(pair, 1);
-                            let success = self.zext(success, self.type_bool());
-
-                            let dest = result.project_field(self, 0);
-                            self.store(val, dest.llval, dest.align);
-                            let dest = result.project_field(self, 1);
-                            self.store(success, dest.llval, dest.align);
-                            return;
-                        } else {
-                            return invalid_monomorphization(ty);
-                        }
-                    }
-
-                    "load" => {
-                        let ty = substs.type_at(0);
-                        if int_type_width_signed(ty, self).is_some() {
-                            let size = self.size_of(ty);
-                            self.atomic_load(args[0].immediate(), order, size)
-                        } else {
-                            return invalid_monomorphization(ty);
-                        }
-                    }
-
-                    "store" => {
-                        let ty = substs.type_at(0);
-                        if int_type_width_signed(ty, self).is_some() {
-                            let size = self.size_of(ty);
-                            self.atomic_store(
-                                args[1].immediate(),
-                                args[0].immediate(),
-                                order,
-                                size,
-                            );
-                            return;
-                        } else {
-                            return invalid_monomorphization(ty);
-                        }
-                    }
-
-                    "fence" => {
-                        self.atomic_fence(order, SynchronizationScope::CrossThread);
-                        return;
-                    }
-
-                    "singlethreadfence" => {
-                        self.atomic_fence(order, SynchronizationScope::SingleThread);
-                        return;
-                    }
-
-                    // These are all AtomicRMW ops
-                    op => {
-                        let atom_op = match op {
-                            "xchg" => AtomicRmwBinOp::AtomicXchg,
-                            "xadd" => AtomicRmwBinOp::AtomicAdd,
-                            "xsub" => AtomicRmwBinOp::AtomicSub,
-                            "and" => AtomicRmwBinOp::AtomicAnd,
-                            "nand" => AtomicRmwBinOp::AtomicNand,
-                            "or" => AtomicRmwBinOp::AtomicOr,
-                            "xor" => AtomicRmwBinOp::AtomicXor,
-                            "max" => AtomicRmwBinOp::AtomicMax,
-                            "min" => AtomicRmwBinOp::AtomicMin,
-                            "umax" => AtomicRmwBinOp::AtomicUMax,
-                            "umin" => AtomicRmwBinOp::AtomicUMin,
-                            _ => self.sess().fatal("unknown atomic operation"),
-                        };
-
-                        let ty = substs.type_at(0);
-                        if int_type_width_signed(ty, self).is_some() {
-                            self.atomic_rmw(
-                                atom_op,
-                                args[0].immediate(),
-                                args[1].immediate(),
-                                order,
-                            )
-                        } else {
-                            return invalid_monomorphization(ty);
-                        }
-                    }
-                }
-            }
-
-            sym::nontemporal_store => {
-                let dst = args[0].deref(self.cx());
-                args[1].val.nontemporal_store(self, dst);
-                return;
-            }
-
-            sym::ptr_guaranteed_eq | sym::ptr_guaranteed_ne => {
-                let a = args[0].immediate();
-                let b = args[1].immediate();
-                if name == sym::ptr_guaranteed_eq {
-                    self.icmp(IntPredicate::IntEQ, a, b)
-                } else {
-                    self.icmp(IntPredicate::IntNE, a, b)
-                }
-            }
-
-            sym::ptr_offset_from => {
-                let ty = substs.type_at(0);
-                let pointee_size = self.size_of(ty);
-
-                // This is the same sequence that Clang emits for pointer subtraction.
-                // It can be neither `nsw` nor `nuw` because the input is treated as
-                // unsigned but then the output is treated as signed, so neither works.
-                let a = args[0].immediate();
-                let b = args[1].immediate();
-                let a = self.ptrtoint(a, self.type_isize());
-                let b = self.ptrtoint(b, self.type_isize());
-                let d = self.sub(a, b);
-                let pointee_size = self.const_usize(pointee_size.bytes());
-                // this is where the signed magic happens (notice the `s` in `exactsdiv`)
-                self.exactsdiv(d, pointee_size)
-            }
 
             _ => bug!("unknown intrinsic '{}'", name),
         };
@@ -807,39 +352,6 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
     }
 }
 
-fn copy_intrinsic(
-    bx: &mut Builder<'a, 'll, 'tcx>,
-    allow_overlap: bool,
-    volatile: bool,
-    ty: Ty<'tcx>,
-    dst: &'ll Value,
-    src: &'ll Value,
-    count: &'ll Value,
-) {
-    let (size, align) = bx.size_and_align_of(ty);
-    let size = bx.mul(bx.const_usize(size.bytes()), count);
-    let flags = if volatile { MemFlags::VOLATILE } else { MemFlags::empty() };
-    if allow_overlap {
-        bx.memmove(dst, align, src, align, size, flags);
-    } else {
-        bx.memcpy(dst, align, src, align, size, flags);
-    }
-}
-
-fn memset_intrinsic(
-    bx: &mut Builder<'a, 'll, 'tcx>,
-    volatile: bool,
-    ty: Ty<'tcx>,
-    dst: &'ll Value,
-    val: &'ll Value,
-    count: &'ll Value,
-) {
-    let (size, align) = bx.size_and_align_of(ty);
-    let size = bx.mul(bx.const_usize(size.bytes()), count);
-    let flags = if volatile { MemFlags::VOLATILE } else { MemFlags::empty() };
-    bx.memset(dst, val, size, align, flags);
-}
-
 fn try_intrinsic(
     bx: &mut Builder<'a, 'll, 'tcx>,
     try_func: &'ll Value,
@@ -1271,7 +783,7 @@ fn generic_simd_intrinsic(
 
     if name == sym::simd_select_bitmask {
         let in_ty = arg_tys[0];
-        let m_len = match in_ty.kind {
+        let m_len = match in_ty.kind() {
             // Note that this `.unwrap()` crashes for isize/usize, that's sort
             // of intentional as there's not currently a use case for that.
             ty::Int(i) => i.bit_width().unwrap(),
@@ -1436,7 +948,7 @@ fn generic_simd_intrinsic(
             m_len,
             v_len
         );
-        match m_elem_ty.kind {
+        match m_elem_ty.kind() {
             ty::Int(_) => {}
             _ => return_error!("mask element type is `{}`, expected `i_`", m_elem_ty),
         }
@@ -1455,13 +967,13 @@ fn generic_simd_intrinsic(
         // If the vector has less than 8 lanes, an u8 is returned with zeroed
         // trailing bits.
         let expected_int_bits = in_len.max(8);
-        match ret_ty.kind {
+        match ret_ty.kind() {
             ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => (),
             _ => return_error!("bitmask `{}`, expected `u{}`", ret_ty, expected_int_bits),
         }
 
         // Integer vector <i{in_bitwidth} x in_len>:
-        let (i_xn, in_elem_bitwidth) = match in_elem.kind {
+        let (i_xn, in_elem_bitwidth) = match in_elem.kind() {
             ty::Int(i) => {
                 (args[0].immediate(), i.bit_width().unwrap_or(bx.data_layout().pointer_size.bits()))
             }
@@ -1518,7 +1030,7 @@ fn generic_simd_intrinsic(
                 }
             }
         }
-        let ety = match in_elem.kind {
+        let ety = match in_elem.kind() {
             ty::Float(f) if f.bit_width() == 32 => {
                 if in_len < 2 || in_len > 16 {
                     return_error!(
@@ -1612,7 +1124,7 @@ fn generic_simd_intrinsic(
     //  https://github.com/llvm-mirror/llvm/blob/master/include/llvm/IR/Intrinsics.h#L81
     fn llvm_vector_str(elem_ty: Ty<'_>, vec_len: u64, no_pointers: usize) -> String {
         let p0s: String = "p0".repeat(no_pointers);
-        match elem_ty.kind {
+        match *elem_ty.kind() {
             ty::Int(v) => format!("v{}{}i{}", vec_len, p0s, v.bit_width().unwrap()),
             ty::Uint(v) => format!("v{}{}i{}", vec_len, p0s, v.bit_width().unwrap()),
             ty::Float(v) => format!("v{}{}f{}", vec_len, p0s, v.bit_width()),
@@ -1627,7 +1139,7 @@ fn generic_simd_intrinsic(
         mut no_pointers: usize,
     ) -> &'ll Type {
         // FIXME: use cx.layout_of(ty).llvm_type() ?
-        let mut elem_ty = match elem_ty.kind {
+        let mut elem_ty = match *elem_ty.kind() {
             ty::Int(v) => cx.type_int_from_ty(v),
             ty::Uint(v) => cx.type_uint_from_ty(v),
             ty::Float(v) => cx.type_float_from_ty(v),
@@ -1680,7 +1192,7 @@ fn generic_simd_intrinsic(
 
         // This counts how many pointers
         fn ptr_count(t: Ty<'_>) -> usize {
-            match t.kind {
+            match t.kind() {
                 ty::RawPtr(p) => 1 + ptr_count(p.ty),
                 _ => 0,
             }
@@ -1688,7 +1200,7 @@ fn generic_simd_intrinsic(
 
         // Non-ptr type
         fn non_ptr(t: Ty<'_>) -> Ty<'_> {
-            match t.kind {
+            match t.kind() {
                 ty::RawPtr(p) => non_ptr(p.ty),
                 _ => t,
             }
@@ -1696,7 +1208,7 @@ fn generic_simd_intrinsic(
 
         // The second argument must be a simd vector with an element type that's a pointer
         // to the element type of the first argument
-        let (pointer_count, underlying_ty) = match arg_tys[1].simd_type(tcx).kind {
+        let (pointer_count, underlying_ty) = match arg_tys[1].simd_type(tcx).kind() {
             ty::RawPtr(p) if p.ty == in_elem => {
                 (ptr_count(arg_tys[1].simd_type(tcx)), non_ptr(arg_tys[1].simd_type(tcx)))
             }
@@ -1721,7 +1233,7 @@ fn generic_simd_intrinsic(
         assert_eq!(underlying_ty, non_ptr(arg_tys[0].simd_type(tcx)));
 
         // The element type of the third argument must be a signed integer type of any width:
-        match arg_tys[2].simd_type(tcx).kind {
+        match arg_tys[2].simd_type(tcx).kind() {
             ty::Int(_) => (),
             _ => {
                 require!(
@@ -1803,7 +1315,7 @@ fn generic_simd_intrinsic(
 
         // This counts how many pointers
         fn ptr_count(t: Ty<'_>) -> usize {
-            match t.kind {
+            match t.kind() {
                 ty::RawPtr(p) => 1 + ptr_count(p.ty),
                 _ => 0,
             }
@@ -1811,7 +1323,7 @@ fn generic_simd_intrinsic(
 
         // Non-ptr type
         fn non_ptr(t: Ty<'_>) -> Ty<'_> {
-            match t.kind {
+            match t.kind() {
                 ty::RawPtr(p) => non_ptr(p.ty),
                 _ => t,
             }
@@ -1819,7 +1331,7 @@ fn generic_simd_intrinsic(
 
         // The second argument must be a simd vector with an element type that's a pointer
         // to the element type of the first argument
-        let (pointer_count, underlying_ty) = match arg_tys[1].simd_type(tcx).kind {
+        let (pointer_count, underlying_ty) = match arg_tys[1].simd_type(tcx).kind() {
             ty::RawPtr(p) if p.ty == in_elem && p.mutbl == hir::Mutability::Mut => {
                 (ptr_count(arg_tys[1].simd_type(tcx)), non_ptr(arg_tys[1].simd_type(tcx)))
             }
@@ -1844,7 +1356,7 @@ fn generic_simd_intrinsic(
         assert_eq!(underlying_ty, non_ptr(arg_tys[0].simd_type(tcx)));
 
         // The element type of the third argument must be a signed integer type of any width:
-        match arg_tys[2].simd_type(tcx).kind {
+        match arg_tys[2].simd_type(tcx).kind() {
             ty::Int(_) => (),
             _ => {
                 require!(
@@ -1900,7 +1412,7 @@ fn generic_simd_intrinsic(
                     in_ty,
                     ret_ty
                 );
-                return match in_elem.kind {
+                return match in_elem.kind() {
                     ty::Int(_) | ty::Uint(_) => {
                         let r = bx.$integer_reduce(args[0].immediate());
                         if $ordered {
@@ -1972,7 +1484,7 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#,
                     in_ty,
                     ret_ty
                 );
-                return match in_elem.kind {
+                return match in_elem.kind() {
                     ty::Int(_i) => Ok(bx.$int_red(args[0].immediate(), true)),
                     ty::Uint(_u) => Ok(bx.$int_red(args[0].immediate(), false)),
                     ty::Float(_f) => Ok(bx.$float_red(args[0].immediate())),
@@ -2007,7 +1519,7 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#,
                     );
                     args[0].immediate()
                 } else {
-                    match in_elem.kind {
+                    match in_elem.kind() {
                         ty::Int(_) | ty::Uint(_) => {}
                         _ => return_error!(
                             "unsupported {} from `{}` with element `{}` to `{}`",
@@ -2023,7 +1535,7 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#,
                     let i1xn = bx.type_vector(i1, in_len as u64);
                     bx.trunc(args[0].immediate(), i1xn)
                 };
-                return match in_elem.kind {
+                return match in_elem.kind() {
                     ty::Int(_) | ty::Uint(_) => {
                         let r = bx.$red(input);
                         Ok(if !$boolean { r } else { bx.zext(r, bx.type_bool()) })
@@ -2071,7 +1583,7 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#,
             Unsupported,
         }
 
-        let (in_style, in_width) = match in_elem.kind {
+        let (in_style, in_width) = match in_elem.kind() {
             // vectors of pointer-sized integers should've been
             // disallowed before here, so this unwrap is safe.
             ty::Int(i) => (Style::Int(true), i.bit_width().unwrap()),
@@ -2079,7 +1591,7 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#,
             ty::Float(f) => (Style::Float, f.bit_width()),
             _ => (Style::Unsupported, 0),
         };
-        let (out_style, out_width) = match out_elem.kind {
+        let (out_style, out_width) = match out_elem.kind() {
             ty::Int(i) => (Style::Int(true), i.bit_width().unwrap()),
             ty::Uint(u) => (Style::Int(false), u.bit_width().unwrap()),
             ty::Float(f) => (Style::Float, f.bit_width()),
@@ -2135,7 +1647,7 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#,
     macro_rules! arith {
         ($($name: ident: $($($p: ident),* => $call: ident),*;)*) => {
             $(if name == sym::$name {
-                match in_elem.kind {
+                match in_elem.kind() {
                     $($(ty::$p(_))|* => {
                         return Ok(bx.$call(args[0].immediate(), args[1].immediate()))
                     })*
@@ -2169,7 +1681,7 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#,
         let rhs = args[1].immediate();
         let is_add = name == sym::simd_saturating_add;
         let ptr_bits = bx.tcx().data_layout.pointer_size.bits() as _;
-        let (signed, elem_width, elem_ty) = match in_elem.kind {
+        let (signed, elem_width, elem_ty) = match *in_elem.kind() {
             ty::Int(i) => (true, i.bit_width().unwrap_or(ptr_bits), bx.cx.type_int_from_ty(i)),
             ty::Uint(i) => (false, i.bit_width().unwrap_or(ptr_bits), bx.cx.type_uint_from_ty(i)),
             _ => {
@@ -2204,38 +1716,13 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#,
 // FIXME: there’s multiple of this functions, investigate using some of the already existing
 // stuffs.
 fn int_type_width_signed(ty: Ty<'_>, cx: &CodegenCx<'_, '_>) -> Option<(u64, bool)> {
-    match ty.kind {
-        ty::Int(t) => Some((
-            match t {
-                ast::IntTy::Isize => u64::from(cx.tcx.sess.target.ptr_width),
-                ast::IntTy::I8 => 8,
-                ast::IntTy::I16 => 16,
-                ast::IntTy::I32 => 32,
-                ast::IntTy::I64 => 64,
-                ast::IntTy::I128 => 128,
-            },
-            true,
-        )),
-        ty::Uint(t) => Some((
-            match t {
-                ast::UintTy::Usize => u64::from(cx.tcx.sess.target.ptr_width),
-                ast::UintTy::U8 => 8,
-                ast::UintTy::U16 => 16,
-                ast::UintTy::U32 => 32,
-                ast::UintTy::U64 => 64,
-                ast::UintTy::U128 => 128,
-            },
-            false,
-        )),
-        _ => None,
-    }
-}
-
-// Returns the width of a float Ty
-// Returns None if the type is not a float
-fn float_type_width(ty: Ty<'_>) -> Option<u64> {
-    match ty.kind {
-        ty::Float(t) => Some(t.bit_width()),
+    match ty.kind() {
+        ty::Int(t) => {
+            Some((t.bit_width().unwrap_or(u64::from(cx.tcx.sess.target.ptr_width)), true))
+        }
+        ty::Uint(t) => {
+            Some((t.bit_width().unwrap_or(u64::from(cx.tcx.sess.target.ptr_width)), false))
+        }
         _ => None,
     }
 }
diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs
index 67d4b2642c0..456e9c7ce75 100644
--- a/compiler/rustc_codegen_llvm/src/lib.rs
+++ b/compiler/rustc_codegen_llvm/src/lib.rs
@@ -4,7 +4,7 @@
 //!
 //! This API is completely unstable and subject to change.
 
-#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
+#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![feature(bool_to_option)]
 #![feature(const_cstr_unchecked)]
 #![feature(crate_visibility_modifier)]
@@ -130,6 +130,13 @@ impl WriteBackendMethods for LlvmCodegenBackend {
             llvm::LLVMRustPrintPassTimings();
         }
     }
+    fn run_link(
+        cgcx: &CodegenContext<Self>,
+        diag_handler: &Handler,
+        modules: Vec<ModuleCodegen<Self::Module>>,
+    ) -> Result<ModuleCodegen<Self::Module>, FatalError> {
+        back::write::link(cgcx, diag_handler, modules)
+    }
     fn run_fat_lto(
         cgcx: &CodegenContext<Self>,
         modules: Vec<FatLTOInput<Self>>,
diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
index 32822eba930..45c5f56f447 100644
--- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
@@ -96,7 +96,7 @@ pub enum DLLStorageClass {
     DllExport = 2, // Function to be accessible from DLL.
 }
 
-/// Matches LLVMRustAttribute in rustllvm.h
+/// Matches LLVMRustAttribute in LLVMWrapper.h
 /// Semantically a subset of the C++ enum llvm::Attribute::AttrKind,
 /// though it is not ABI compatible (since it's a C++ enum)
 #[repr(C)]
@@ -948,7 +948,6 @@ extern "C" {
 
     // Operations on other types
     pub fn LLVMVoidTypeInContext(C: &Context) -> &Type;
-    pub fn LLVMX86MMXTypeInContext(C: &Context) -> &Type;
     pub fn LLVMRustMetadataTypeInContext(C: &Context) -> &Type;
 
     // Operations on all values
@@ -1705,7 +1704,7 @@ extern "C" {
         PM: &PassManager<'_>,
     );
 
-    // Stuff that's in rustllvm/ because it's not upstream yet.
+    // Stuff that's in llvm-wrapper/ because it's not upstream yet.
 
     /// Opens an object file.
     pub fn LLVMCreateObjectFile(
diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs
index f0b50459837..900f2df383a 100644
--- a/compiler/rustc_codegen_llvm/src/llvm_util.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs
@@ -203,7 +203,6 @@ const X86_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
     ("fma", None),
     ("fxsr", None),
     ("lzcnt", None),
-    ("mmx", Some(sym::mmx_target_feature)),
     ("movbe", Some(sym::movbe_target_feature)),
     ("pclmulqdq", None),
     ("popcnt", None),
diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs
index 3b53b4fe77b..a43724fd495 100644
--- a/compiler/rustc_codegen_llvm/src/type_.rs
+++ b/compiler/rustc_codegen_llvm/src/type_.rs
@@ -62,10 +62,6 @@ impl CodegenCx<'ll, 'tcx> {
         unsafe { llvm::LLVMIntTypeInContext(self.llcx, num_bits as c_uint) }
     }
 
-    crate fn type_x86_mmx(&self) -> &'ll Type {
-        unsafe { llvm::LLVMX86MMXTypeInContext(self.llcx) }
-    }
-
     crate fn type_vector(&self, ty: &'ll Type, len: u64) -> &'ll Type {
         unsafe { llvm::LLVMVectorType(ty, len as c_uint) }
     }
diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs
index 40870c66475..e0754d21df1 100644
--- a/compiler/rustc_codegen_llvm/src/type_of.rs
+++ b/compiler/rustc_codegen_llvm/src/type_of.rs
@@ -4,7 +4,7 @@ use crate::type_::Type;
 use rustc_codegen_ssa::traits::*;
 use rustc_middle::bug;
 use rustc_middle::ty::layout::{FnAbiExt, TyAndLayout};
-use rustc_middle::ty::print::obsolete::DefPathBasedNames;
+use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{self, Ty, TypeFoldable};
 use rustc_target::abi::{Abi, AddressSpace, Align, FieldsShape};
 use rustc_target::abi::{Int, Pointer, F32, F64};
@@ -21,23 +21,8 @@ fn uncached_llvm_type<'a, 'tcx>(
     match layout.abi {
         Abi::Scalar(_) => bug!("handled elsewhere"),
         Abi::Vector { ref element, count } => {
-            // LLVM has a separate type for 64-bit SIMD vectors on X86 called
-            // `x86_mmx` which is needed for some SIMD operations. As a bit of a
-            // hack (all SIMD definitions are super unstable anyway) we
-            // recognize any one-element SIMD vector as "this should be an
-            // x86_mmx" type. In general there shouldn't be a need for other
-            // one-element SIMD vectors, so it's assumed this won't clash with
-            // much else.
-            let use_x86_mmx = count == 1
-                && layout.size.bits() == 64
-                && (cx.sess().target.target.arch == "x86"
-                    || cx.sess().target.target.arch == "x86_64");
-            if use_x86_mmx {
-                return cx.type_x86_mmx();
-            } else {
-                let element = layout.scalar_llvm_type_at(cx, element, Size::ZERO);
-                return cx.type_vector(element, count);
-            }
+            let element = layout.scalar_llvm_type_at(cx, element, Size::ZERO);
+            return cx.type_vector(element, count);
         }
         Abi::ScalarPair(..) => {
             return cx.type_struct(
@@ -51,33 +36,35 @@ fn uncached_llvm_type<'a, 'tcx>(
         Abi::Uninhabited | Abi::Aggregate { .. } => {}
     }
 
-    let name = match layout.ty.kind {
-        ty::Closure(..) |
-        ty::Generator(..) |
-        ty::Adt(..) |
+    let name = match layout.ty.kind() {
         // FIXME(eddyb) producing readable type names for trait objects can result
         // in problematically distinct types due to HRTB and subtyping (see #47638).
         // ty::Dynamic(..) |
-        ty::Foreign(..) |
-        ty::Str => {
-            let mut name = String::with_capacity(32);
-            let printer = DefPathBasedNames::new(cx.tcx, true, true);
-            printer.push_type_name(layout.ty, &mut name, false);
-            if let (&ty::Adt(def, _), &Variants::Single { index })
-                 = (&layout.ty.kind, &layout.variants)
+        ty::Adt(..) | ty::Closure(..) | ty::Foreign(..) | ty::Generator(..) | ty::Str
+            if !cx.sess().fewer_names() =>
+        {
+            let mut name = with_no_trimmed_paths(|| layout.ty.to_string());
+            if let (&ty::Adt(def, _), &Variants::Single { index }) =
+                (layout.ty.kind(), &layout.variants)
             {
                 if def.is_enum() && !def.variants.is_empty() {
                     write!(&mut name, "::{}", def.variants[index].ident).unwrap();
                 }
             }
-            if let (&ty::Generator(_, _, _), &Variants::Single { index })
-                 = (&layout.ty.kind, &layout.variants)
+            if let (&ty::Generator(_, _, _), &Variants::Single { index }) =
+                (layout.ty.kind(), &layout.variants)
             {
                 write!(&mut name, "::{}", ty::GeneratorSubsts::variant_name(index)).unwrap();
             }
             Some(name)
         }
-        _ => None
+        ty::Adt(..) => {
+            // If `Some` is returned then a named struct is created in LLVM. Name collisions are
+            // avoided by LLVM (with increasing suffixes). If rustc doesn't generate names then that
+            // can improve perf.
+            Some(String::new())
+        }
+        _ => None,
     };
 
     match layout.fields {
@@ -236,7 +223,7 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> {
             if let Some(&llty) = cx.scalar_lltypes.borrow().get(&self.ty) {
                 return llty;
             }
-            let llty = match self.ty.kind {
+            let llty = match *self.ty.kind() {
                 ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => {
                     cx.type_ptr_to(cx.layout_of(ty).llvm_type(cx))
                 }
@@ -329,7 +316,7 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> {
     ) -> &'a Type {
         // HACK(eddyb) special-case fat pointers until LLVM removes
         // pointee types, to avoid bitcasting every `OperandRef::deref`.
-        match self.ty.kind {
+        match self.ty.kind() {
             ty::Ref(..) | ty::RawPtr(_) => {
                 return self.field(cx, index).llvm_type(cx);
             }
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index bfcf979d125..faeb727202c 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -1014,86 +1014,7 @@ fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLib]) {
     }
 }
 
-// Because windows-gnu target is meant to be self-contained for pure Rust code it bundles
-// own mingw-w64 libraries. These libraries are usually not compatible with mingw-w64
-// installed in the system. This breaks many cases where Rust is mixed with other languages
-// (e.g. *-sys crates).
-// We prefer system mingw-w64 libraries if they are available to avoid this issue.
-fn get_crt_libs_path(sess: &Session) -> Option<PathBuf> {
-    fn find_exe_in_path<P>(exe_name: P) -> Option<PathBuf>
-    where
-        P: AsRef<Path>,
-    {
-        for dir in env::split_paths(&env::var_os("PATH")?) {
-            let full_path = dir.join(&exe_name);
-            if full_path.is_file() {
-                return Some(fix_windows_verbatim_for_gcc(&full_path));
-            }
-        }
-        None
-    }
-
-    fn probe(sess: &Session) -> Option<PathBuf> {
-        if let (linker, LinkerFlavor::Gcc) = linker_and_flavor(&sess) {
-            let linker_path = if cfg!(windows) && linker.extension().is_none() {
-                linker.with_extension("exe")
-            } else {
-                linker
-            };
-            if let Some(linker_path) = find_exe_in_path(linker_path) {
-                let mingw_arch = match &sess.target.target.arch {
-                    x if x == "x86" => "i686",
-                    x => x,
-                };
-                let mingw_bits = &sess.target.target.target_pointer_width;
-                let mingw_dir = format!("{}-w64-mingw32", mingw_arch);
-                // Here we have path/bin/gcc but we need path/
-                let mut path = linker_path;
-                path.pop();
-                path.pop();
-                // Loosely based on Clang MinGW driver
-                let probe_paths = vec![
-                    path.join(&mingw_dir).join("lib"),                // Typical path
-                    path.join(&mingw_dir).join("sys-root/mingw/lib"), // Rare path
-                    path.join(format!(
-                        "lib/mingw/tools/install/mingw{}/{}/lib",
-                        &mingw_bits, &mingw_dir
-                    )), // Chocolatey is creative
-                ];
-                for probe_path in probe_paths {
-                    if probe_path.join("crt2.o").exists() {
-                        return Some(probe_path);
-                    };
-                }
-            };
-        };
-        None
-    }
-
-    let mut system_library_path = sess.system_library_path.borrow_mut();
-    match &*system_library_path {
-        Some(Some(compiler_libs_path)) => Some(compiler_libs_path.clone()),
-        Some(None) => None,
-        None => {
-            let path = probe(sess);
-            *system_library_path = Some(path.clone());
-            path
-        }
-    }
-}
-
 fn get_object_file_path(sess: &Session, name: &str, self_contained: bool) -> PathBuf {
-    // prefer system {,dll}crt2.o libs, see get_crt_libs_path comment for more details
-    if sess.opts.debugging_opts.link_self_contained.is_none()
-        && sess.target.target.llvm_target.contains("windows-gnu")
-    {
-        if let Some(compiler_libs_path) = get_crt_libs_path(sess) {
-            let file_path = compiler_libs_path.join(name);
-            if file_path.exists() {
-                return file_path;
-            }
-        }
-    }
     let fs = sess.target_filesearch(PathKind::Native);
     let file_path = fs.get_lib_path().join(name);
     if file_path.exists() {
@@ -1155,7 +1076,7 @@ fn exec_linker(
             }
             .to_string(),
         );
-        args.push_str("\n");
+        args.push('\n');
     }
     let file = tmpdir.join("linker-arguments");
     let bytes = if sess.target.target.options.is_like_msvc {
@@ -1286,10 +1207,32 @@ fn link_output_kind(sess: &Session, crate_type: CrateType) -> LinkOutputKind {
     }
 }
 
+// Returns true if linker is located within sysroot
+fn detect_self_contained_mingw(sess: &Session) -> bool {
+    let (linker, _) = linker_and_flavor(&sess);
+    // Assume `-C linker=rust-lld` as self-contained mode
+    if linker == Path::new("rust-lld") {
+        return true;
+    }
+    let linker_with_extension = if cfg!(windows) && linker.extension().is_none() {
+        linker.with_extension("exe")
+    } else {
+        linker
+    };
+    for dir in env::split_paths(&env::var_os("PATH").unwrap_or_default()) {
+        let full_path = dir.join(&linker_with_extension);
+        // If linker comes from sysroot assume self-contained mode
+        if full_path.is_file() && !full_path.starts_with(&sess.sysroot) {
+            return false;
+        }
+    }
+    true
+}
+
 /// Whether we link to our own CRT objects instead of relying on gcc to pull them.
 /// We only provide such support for a very limited number of targets.
 fn crt_objects_fallback(sess: &Session, crate_type: CrateType) -> bool {
-    if let Some(self_contained) = sess.opts.debugging_opts.link_self_contained {
+    if let Some(self_contained) = sess.opts.cg.link_self_contained {
         return self_contained;
     }
 
@@ -1298,10 +1241,10 @@ fn crt_objects_fallback(sess: &Session, crate_type: CrateType) -> bool {
         // based on host and linker path, for example.
         // (https://github.com/rust-lang/rust/pull/71769#issuecomment-626330237).
         Some(CrtObjectsFallback::Musl) => sess.crt_static(Some(crate_type)),
-        // FIXME: Find some heuristic for "native mingw toolchain is available",
-        // likely based on `get_crt_libs_path` (https://github.com/rust-lang/rust/pull/67429).
         Some(CrtObjectsFallback::Mingw) => {
-            sess.host == sess.target.target && sess.target.target.target_vendor != "uwp"
+            sess.host == sess.target.target
+                && sess.target.target.target_vendor != "uwp"
+                && detect_self_contained_mingw(&sess)
         }
         // FIXME: Figure out cases in which WASM needs to link with a native toolchain.
         Some(CrtObjectsFallback::Wasm) => true,
@@ -1498,16 +1441,6 @@ fn link_local_crate_native_libs_and_dependent_crate_libs<'a, B: ArchiveBuilder<'
 
 /// Add sysroot and other globally set directories to the directory search list.
 fn add_library_search_dirs(cmd: &mut dyn Linker, sess: &Session, self_contained: bool) {
-    // Prefer system mingw-w64 libs, see get_crt_libs_path comment for more details.
-    if sess.opts.debugging_opts.link_self_contained.is_none()
-        && cfg!(windows)
-        && sess.target.target.llvm_target.contains("windows-gnu")
-    {
-        if let Some(compiler_libs_path) = get_crt_libs_path(sess) {
-            cmd.include_path(&compiler_libs_path);
-        }
-    }
-
     // The default library location, we need this to find the runtime.
     // The location of crates will be determined as needed.
     let lib_path = sess.target_filesearch(PathKind::All).get_lib_path();
@@ -1668,7 +1601,7 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
     // FIXME: Order dependent, applies to the following objects. Where should it be placed?
     // Try to strip as much out of the generated object by removing unused
     // sections if possible. See more comments in linker.rs
-    if sess.opts.cg.link_dead_code != Some(true) {
+    if !sess.link_dead_code() {
         let keep_metadata = crate_type == CrateType::Dylib;
         cmd.gc_sections(keep_metadata);
     }
diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs
index 7d69bb983dd..0edf0fcd1a2 100644
--- a/compiler/rustc_codegen_ssa/src/back/write.rs
+++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -702,6 +702,7 @@ impl<B: WriteBackendMethods> WorkItem<B> {
 
 enum WorkItemResult<B: WriteBackendMethods> {
     Compiled(CompiledModule),
+    NeedsLink(ModuleCodegen<B::Module>),
     NeedsFatLTO(FatLTOInput<B>),
     NeedsThinLTO(String, B::ThinBuffer),
 }
@@ -801,11 +802,8 @@ fn execute_optimize_work_item<B: ExtraBackendMethods>(
         None
     };
 
-    Ok(match lto_type {
-        ComputedLtoType::No => {
-            let module = unsafe { B::codegen(cgcx, &diag_handler, module, module_config)? };
-            WorkItemResult::Compiled(module)
-        }
+    match lto_type {
+        ComputedLtoType::No => finish_intra_module_work(cgcx, module, module_config),
         ComputedLtoType::Thin => {
             let (name, thin_buffer) = B::prepare_thin(module);
             if let Some(path) = bitcode {
@@ -813,7 +811,7 @@ fn execute_optimize_work_item<B: ExtraBackendMethods>(
                     panic!("Error writing pre-lto-bitcode file `{}`: {}", path.display(), e);
                 });
             }
-            WorkItemResult::NeedsThinLTO(name, thin_buffer)
+            Ok(WorkItemResult::NeedsThinLTO(name, thin_buffer))
         }
         ComputedLtoType::Fat => match bitcode {
             Some(path) => {
@@ -821,11 +819,11 @@ fn execute_optimize_work_item<B: ExtraBackendMethods>(
                 fs::write(&path, buffer.data()).unwrap_or_else(|e| {
                     panic!("Error writing pre-lto-bitcode file `{}`: {}", path.display(), e);
                 });
-                WorkItemResult::NeedsFatLTO(FatLTOInput::Serialized { name, buffer })
+                Ok(WorkItemResult::NeedsFatLTO(FatLTOInput::Serialized { name, buffer }))
             }
-            None => WorkItemResult::NeedsFatLTO(FatLTOInput::InMemory(module)),
+            None => Ok(WorkItemResult::NeedsFatLTO(FatLTOInput::InMemory(module))),
         },
-    })
+    }
 }
 
 fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>(
@@ -871,12 +869,25 @@ fn execute_lto_work_item<B: ExtraBackendMethods>(
     mut module: lto::LtoModuleCodegen<B>,
     module_config: &ModuleConfig,
 ) -> Result<WorkItemResult<B>, FatalError> {
+    let module = unsafe { module.optimize(cgcx)? };
+    finish_intra_module_work(cgcx, module, module_config)
+}
+
+fn finish_intra_module_work<B: ExtraBackendMethods>(
+    cgcx: &CodegenContext<B>,
+    module: ModuleCodegen<B::Module>,
+    module_config: &ModuleConfig,
+) -> Result<WorkItemResult<B>, FatalError> {
     let diag_handler = cgcx.create_diag_handler();
 
-    unsafe {
-        let module = module.optimize(cgcx)?;
-        let module = B::codegen(cgcx, &diag_handler, module, module_config)?;
+    if !cgcx.opts.debugging_opts.combine_cgu
+        || module.kind == ModuleKind::Metadata
+        || module.kind == ModuleKind::Allocator
+    {
+        let module = unsafe { B::codegen(cgcx, &diag_handler, module, module_config)? };
         Ok(WorkItemResult::Compiled(module))
+    } else {
+        Ok(WorkItemResult::NeedsLink(module))
     }
 }
 
@@ -891,6 +902,10 @@ pub enum Message<B: WriteBackendMethods> {
         thin_buffer: B::ThinBuffer,
         worker_id: usize,
     },
+    NeedsLink {
+        module: ModuleCodegen<B::Module>,
+        worker_id: usize,
+    },
     Done {
         result: Result<CompiledModule, Option<WorkerFatalError>>,
         worker_id: usize,
@@ -1178,6 +1193,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
         let mut compiled_modules = vec![];
         let mut compiled_metadata_module = None;
         let mut compiled_allocator_module = None;
+        let mut needs_link = Vec::new();
         let mut needs_fat_lto = Vec::new();
         let mut needs_thin_lto = Vec::new();
         let mut lto_import_only_modules = Vec::new();
@@ -1434,6 +1450,10 @@ fn start_executing_work<B: ExtraBackendMethods>(
                         }
                     }
                 }
+                Message::NeedsLink { module, worker_id } => {
+                    free_worker(worker_id);
+                    needs_link.push(module);
+                }
                 Message::NeedsFatLTO { result, worker_id } => {
                     assert!(!started_lto);
                     free_worker(worker_id);
@@ -1462,6 +1482,18 @@ fn start_executing_work<B: ExtraBackendMethods>(
             }
         }
 
+        let needs_link = mem::take(&mut needs_link);
+        if !needs_link.is_empty() {
+            assert!(compiled_modules.is_empty());
+            let diag_handler = cgcx.create_diag_handler();
+            let module = B::run_link(&cgcx, &diag_handler, needs_link).map_err(|_| ())?;
+            let module = unsafe {
+                B::codegen(&cgcx, &diag_handler, module, cgcx.config(ModuleKind::Regular))
+                    .map_err(|_| ())?
+            };
+            compiled_modules.push(module);
+        }
+
         // Drop to print timings
         drop(llvm_start_time);
 
@@ -1521,6 +1553,9 @@ fn spawn_work<B: ExtraBackendMethods>(cgcx: CodegenContext<B>, work: WorkItem<B>
                     Some(Ok(WorkItemResult::Compiled(m))) => {
                         Message::Done::<B> { result: Ok(m), worker_id }
                     }
+                    Some(Ok(WorkItemResult::NeedsLink(m))) => {
+                        Message::NeedsLink::<B> { module: m, worker_id }
+                    }
                     Some(Ok(WorkItemResult::NeedsFatLTO(m))) => {
                         Message::NeedsFatLTO::<B> { result: m, worker_id }
                     }
diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs
index 77c12c410d5..d82fc2c9f63 100644
--- a/compiler/rustc_codegen_ssa/src/base.rs
+++ b/compiler/rustc_codegen_ssa/src/base.rs
@@ -38,7 +38,7 @@ use rustc_middle::middle::cstore::EncodedMetadata;
 use rustc_middle::middle::cstore::{self, LinkagePreference};
 use rustc_middle::middle::lang_items;
 use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, MonoItem};
-use rustc_middle::ty::layout::{self, HasTyCtxt, TyAndLayout};
+use rustc_middle::ty::layout::{HasTyCtxt, TyAndLayout};
 use rustc_middle::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA};
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
@@ -48,7 +48,7 @@ use rustc_session::utils::NativeLibKind;
 use rustc_session::Session;
 use rustc_span::Span;
 use rustc_symbol_mangling::test as symbol_names_test;
-use rustc_target::abi::{Abi, Align, LayoutOf, Scalar, VariantIdx};
+use rustc_target::abi::{Align, LayoutOf, VariantIdx};
 
 use std::cmp;
 use std::ops::{Deref, DerefMut};
@@ -120,7 +120,7 @@ pub fn compare_simd_types<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     ret_ty: Bx::Type,
     op: hir::BinOpKind,
 ) -> Bx::Value {
-    let signed = match t.kind {
+    let signed = match t.kind() {
         ty::Float(_) => {
             let cmp = bin_op_to_fcmp_predicate(op);
             let cmp = bx.fcmp(cmp, lhs, rhs);
@@ -153,7 +153,7 @@ pub fn unsized_info<'tcx, Cx: CodegenMethods<'tcx>>(
 ) -> Cx::Value {
     let (source, target) =
         cx.tcx().struct_lockstep_tails_erasing_lifetimes(source, target, cx.param_env());
-    match (&source.kind, &target.kind) {
+    match (source.kind(), target.kind()) {
         (&ty::Array(_, len), &ty::Slice(_)) => {
             cx.const_usize(len.eval_usize(cx.tcx(), ty::ParamEnv::reveal_all()))
         }
@@ -182,7 +182,7 @@ pub fn unsize_thin_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     dst_ty: Ty<'tcx>,
 ) -> (Bx::Value, Bx::Value) {
     debug!("unsize_thin_ptr: {:?} => {:?}", src_ty, dst_ty);
-    match (&src_ty.kind, &dst_ty.kind) {
+    match (src_ty.kind(), dst_ty.kind()) {
         (&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(ty::TypeAndMut { ty: b, .. }))
         | (&ty::RawPtr(ty::TypeAndMut { ty: a, .. }), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => {
             assert!(bx.cx().type_is_sized(a));
@@ -231,7 +231,7 @@ pub fn coerce_unsized_into<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
 ) {
     let src_ty = src.layout.ty;
     let dst_ty = dst.layout.ty;
-    match (&src_ty.kind, &dst_ty.kind) {
+    match (src_ty.kind(), dst_ty.kind()) {
         (&ty::Ref(..), &ty::Ref(..) | &ty::RawPtr(..)) | (&ty::RawPtr(..), &ty::RawPtr(..)) => {
             let (base, info) = match bx.load_operand(src).val {
                 OperandValue::Pair(base, info) => {
@@ -330,35 +330,6 @@ pub fn wants_msvc_seh(sess: &Session) -> bool {
     sess.target.target.options.is_like_msvc
 }
 
-pub fn from_immediate<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
-    bx: &mut Bx,
-    val: Bx::Value,
-) -> Bx::Value {
-    if bx.cx().val_ty(val) == bx.cx().type_i1() { bx.zext(val, bx.cx().type_i8()) } else { val }
-}
-
-pub fn to_immediate<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
-    bx: &mut Bx,
-    val: Bx::Value,
-    layout: layout::TyAndLayout<'_>,
-) -> Bx::Value {
-    if let Abi::Scalar(ref scalar) = layout.abi {
-        return to_immediate_scalar(bx, val, scalar);
-    }
-    val
-}
-
-pub fn to_immediate_scalar<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
-    bx: &mut Bx,
-    val: Bx::Value,
-    scalar: &Scalar,
-) -> Bx::Value {
-    if scalar.is_bool() {
-        return bx.trunc(val, bx.cx().type_i1());
-    }
-    val
-}
-
 pub fn memcpy_ty<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     bx: &mut Bx,
     dst: Bx::Value,
@@ -436,16 +407,18 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
         // listing.
         let main_ret_ty = cx.tcx().erase_regions(&main_ret_ty.no_bound_vars().unwrap());
 
-        if cx.get_declared_value("main").is_some() {
-            // FIXME: We should be smart and show a better diagnostic here.
-            cx.sess()
-                .struct_span_err(sp, "entry symbol `main` declared multiple times")
-                .help("did you use `#[no_mangle]` on `fn main`? Use `#[start]` instead")
-                .emit();
-            cx.sess().abort_if_errors();
-            bug!();
-        }
-        let llfn = cx.declare_cfn("main", llfty);
+        let llfn = match cx.declare_c_main(llfty) {
+            Some(llfn) => llfn,
+            None => {
+                // FIXME: We should be smart and show a better diagnostic here.
+                cx.sess()
+                    .struct_span_err(sp, "entry symbol `main` declared multiple times")
+                    .help("did you use `#[no_mangle]` on `fn main`? Use `#[start]` instead")
+                    .emit();
+                cx.sess().abort_if_errors();
+                bug!();
+            }
+        };
 
         // `main` should respect same config for frame pointer elimination as rest of code
         cx.set_frame_pointer_elimination(llfn);
diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
index fb8f5a62989..0c0f1bc681c 100644
--- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
+++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
@@ -33,11 +33,11 @@ pub fn push_debuginfo_type_name<'tcx>(
     // .natvis visualizers (and perhaps other existing native debuggers?)
     let cpp_like_names = tcx.sess.target.target.options.is_like_msvc;
 
-    match t.kind {
+    match *t.kind() {
         ty::Bool => output.push_str("bool"),
         ty::Char => output.push_str("char"),
         ty::Str => output.push_str("str"),
-        ty::Never => output.push_str("!"),
+        ty::Never => output.push('!'),
         ty::Int(int_ty) => output.push_str(int_ty.name_str()),
         ty::Uint(uint_ty) => output.push_str(uint_ty.name_str()),
         ty::Float(float_ty) => output.push_str(float_ty.name_str()),
diff --git a/compiler/rustc_codegen_ssa/src/glue.rs b/compiler/rustc_codegen_ssa/src/glue.rs
index 5b086bc43ff..b88de0b2411 100644
--- a/compiler/rustc_codegen_ssa/src/glue.rs
+++ b/compiler/rustc_codegen_ssa/src/glue.rs
@@ -19,7 +19,7 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
         let align = bx.const_usize(layout.align.abi.bytes());
         return (size, align);
     }
-    match t.kind {
+    match t.kind() {
         ty::Dynamic(..) => {
             // load size/align from vtable
             let vtable = info.unwrap();
@@ -64,7 +64,7 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
             let size = bx.add(sized_size, unsized_size);
 
             // Packed types ignore the alignment of their fields.
-            if let ty::Adt(def, _) = t.kind {
+            if let ty::Adt(def, _) = t.kind() {
                 if def.repr.packed() {
                     unsized_align = sized_align;
                 }
diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs
index 73e33369175..8568bd64f4c 100644
--- a/compiler/rustc_codegen_ssa/src/lib.rs
+++ b/compiler/rustc_codegen_ssa/src/lib.rs
@@ -1,4 +1,4 @@
-#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
+#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![feature(bool_to_option)]
 #![feature(option_expect_none)]
 #![feature(box_patterns)]
@@ -8,8 +8,6 @@
 #![feature(or_patterns)]
 #![feature(trusted_len)]
 #![feature(associated_type_bounds)]
-#![feature(const_fn)] // for rustc_index::newtype_index
-#![feature(const_panic)] // for rustc_index::newtype_index
 #![recursion_limit = "256"]
 
 //! This crate contains codegen code that is used by all codegen backends (LLVM and others).
diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs
index 2e386c1e594..bdde07d3fa9 100644
--- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs
@@ -236,7 +236,7 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
     fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) {
         let check = match terminator.kind {
             mir::TerminatorKind::Call { func: mir::Operand::Constant(ref c), ref args, .. } => {
-                match c.literal.ty.kind {
+                match *c.literal.ty.kind() {
                     ty::FnDef(did, _) => Some((did, args)),
                     _ => None,
                 }
diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs
index 8048a569f79..703a17b200a 100644
--- a/compiler/rustc_codegen_ssa/src/mir/block.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/block.rs
@@ -13,9 +13,10 @@ use rustc_ast as ast;
 use rustc_hir::lang_items::LangItem;
 use rustc_index::vec::Idx;
 use rustc_middle::mir;
-use rustc_middle::mir::interpret::{AllocId, ConstValue, Pointer, Scalar};
+use rustc_middle::mir::interpret::ConstValue;
 use rustc_middle::mir::AssertKind;
 use rustc_middle::ty::layout::{FnAbiExt, HasTyCtxt};
+use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{self, Instance, Ty, TypeFoldable};
 use rustc_span::source_map::Span;
 use rustc_span::{sym, Symbol};
@@ -331,7 +332,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             args1 = [place.llval];
             &args1[..]
         };
-        let (drop_fn, fn_abi) = match ty.kind {
+        let (drop_fn, fn_abi) = match ty.kind() {
             // FIXME(eddyb) perhaps move some of this logic into
             // `Instance::resolve_drop_in_place`?
             ty::Dynamic(..) => {
@@ -479,14 +480,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 UninitValid => !layout.might_permit_raw_init(bx, /*zero:*/ false).unwrap(),
             };
             if do_panic {
-                let msg_str = if layout.abi.is_uninhabited() {
-                    // Use this error even for the other intrinsics as it is more precise.
-                    format!("attempted to instantiate uninhabited type `{}`", ty)
-                } else if intrinsic == ZeroValid {
-                    format!("attempted to zero-initialize type `{}`, which is invalid", ty)
-                } else {
-                    format!("attempted to leave type `{}` uninitialized, which is invalid", ty)
-                };
+                let msg_str = with_no_trimmed_paths(|| {
+                    if layout.abi.is_uninhabited() {
+                        // Use this error even for the other intrinsics as it is more precise.
+                        format!("attempted to instantiate uninhabited type `{}`", ty)
+                    } else if intrinsic == ZeroValid {
+                        format!("attempted to zero-initialize type `{}`, which is invalid", ty)
+                    } else {
+                        format!("attempted to leave type `{}` uninitialized, which is invalid", ty)
+                    }
+                });
                 let msg = bx.const_str(Symbol::intern(&msg_str));
                 let location = self.get_caller_location(bx, span).immediate();
 
@@ -537,7 +540,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         // Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar.
         let callee = self.codegen_operand(&mut bx, func);
 
-        let (instance, mut llfn) = match callee.layout.ty.kind {
+        let (instance, mut llfn) = match *callee.layout.ty.kind() {
             ty::FnDef(def_id, substs) => (
                 Some(
                     ty::Instance::resolve(bx.tcx(), ty::ParamEnv::reveal_all(), def_id, substs)
@@ -684,7 +687,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 })
                 .collect();
 
-            bx.codegen_intrinsic_call(
+            Self::codegen_intrinsic_call(
+                &mut bx,
                 *instance.as_ref().unwrap(),
                 &fn_abi,
                 &args,
@@ -864,27 +868,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                         let ty = constant.literal.ty;
                         let size = bx.layout_of(ty).size;
                         let scalar = match const_value {
-                            // Promoted constants are evaluated into a ByRef instead of a Scalar,
-                            // but we want the scalar value here.
-                            ConstValue::ByRef { alloc, offset } => {
-                                let ptr = Pointer::new(AllocId(0), offset);
-                                alloc
-                                    .read_scalar(&bx, ptr, size)
-                                    .and_then(|s| s.check_init())
-                                    .unwrap_or_else(|e| {
-                                        bx.tcx().sess.span_err(
-                                            span,
-                                            &format!("Could not evaluate asm const: {}", e),
-                                        );
-
-                                        // We are erroring out, just emit a dummy constant.
-                                        Scalar::from_u64(0)
-                                    })
-                            }
-                            _ => span_bug!(span, "expected ByRef for promoted asm const"),
+                            ConstValue::Scalar(s) => s,
+                            _ => span_bug!(
+                                span,
+                                "expected Scalar for promoted asm const, but got {:#?}",
+                                const_value
+                            ),
                         };
                         let value = scalar.assert_bits(size);
-                        let string = match ty.kind {
+                        let string = match ty.kind() {
                             ty::Uint(_) => value.to_string(),
                             ty::Int(int_ty) => {
                                 match int_ty.normalize(bx.tcx().sess.target.ptr_width) {
@@ -911,7 +903,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 }
                 mir::InlineAsmOperand::SymFn { ref value } => {
                     let literal = self.monomorphize(&value.literal);
-                    if let ty::FnDef(def_id, substs) = literal.ty.kind {
+                    if let ty::FnDef(def_id, substs) = *literal.ty.kind() {
                         let instance = ty::Instance::resolve_for_fn_ptr(
                             bx.tcx(),
                             ty::ParamEnv::reveal_all(),
@@ -1143,7 +1135,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     }
                 }
                 // We store bools as `i8` so we need to truncate to `i1`.
-                llval = base::to_immediate(bx, llval, arg.layout);
+                llval = bx.to_immediate(llval, arg.layout);
             }
         }
 
diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
new file mode 100644
index 00000000000..14f1ed59a67
--- /dev/null
+++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
@@ -0,0 +1,596 @@
+use super::operand::{OperandRef, OperandValue};
+use super::place::PlaceRef;
+use super::FunctionCx;
+use crate::common::{span_invalid_monomorphization_error, IntPredicate};
+use crate::glue;
+use crate::traits::*;
+use crate::MemFlags;
+
+use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_span::{sym, Span};
+use rustc_target::abi::call::{FnAbi, PassMode};
+
+fn copy_intrinsic<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
+    bx: &mut Bx,
+    allow_overlap: bool,
+    volatile: bool,
+    ty: Ty<'tcx>,
+    dst: Bx::Value,
+    src: Bx::Value,
+    count: Bx::Value,
+) {
+    let layout = bx.layout_of(ty);
+    let size = layout.size;
+    let align = layout.align.abi;
+    let size = bx.mul(bx.const_usize(size.bytes()), count);
+    let flags = if volatile { MemFlags::VOLATILE } else { MemFlags::empty() };
+    if allow_overlap {
+        bx.memmove(dst, align, src, align, size, flags);
+    } else {
+        bx.memcpy(dst, align, src, align, size, flags);
+    }
+}
+
+fn memset_intrinsic<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
+    bx: &mut Bx,
+    volatile: bool,
+    ty: Ty<'tcx>,
+    dst: Bx::Value,
+    val: Bx::Value,
+    count: Bx::Value,
+) {
+    let layout = bx.layout_of(ty);
+    let size = layout.size;
+    let align = layout.align.abi;
+    let size = bx.mul(bx.const_usize(size.bytes()), count);
+    let flags = if volatile { MemFlags::VOLATILE } else { MemFlags::empty() };
+    bx.memset(dst, val, size, align, flags);
+}
+
+impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
+    pub fn codegen_intrinsic_call(
+        bx: &mut Bx,
+        instance: ty::Instance<'tcx>,
+        fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
+        args: &[OperandRef<'tcx, Bx::Value>],
+        llresult: Bx::Value,
+        span: Span,
+    ) {
+        let callee_ty = instance.ty(bx.tcx(), ty::ParamEnv::reveal_all());
+
+        let (def_id, substs) = match *callee_ty.kind() {
+            ty::FnDef(def_id, substs) => (def_id, substs),
+            _ => bug!("expected fn item type, found {}", callee_ty),
+        };
+
+        let sig = callee_ty.fn_sig(bx.tcx());
+        let sig = bx.tcx().normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig);
+        let arg_tys = sig.inputs();
+        let ret_ty = sig.output();
+        let name = bx.tcx().item_name(def_id);
+        let name_str = &*name.as_str();
+
+        let llret_ty = bx.backend_type(bx.layout_of(ret_ty));
+        let result = PlaceRef::new_sized(llresult, fn_abi.ret.layout);
+
+        let llval = match name {
+            sym::assume => {
+                bx.assume(args[0].immediate());
+                return;
+            }
+            sym::abort => {
+                bx.abort();
+                return;
+            }
+
+            sym::unreachable => {
+                return;
+            }
+            sym::va_start => bx.va_start(args[0].immediate()),
+            sym::va_end => bx.va_end(args[0].immediate()),
+            sym::size_of_val => {
+                let tp_ty = substs.type_at(0);
+                if let OperandValue::Pair(_, meta) = args[0].val {
+                    let (llsize, _) = glue::size_and_align_of_dst(bx, tp_ty, Some(meta));
+                    llsize
+                } else {
+                    bx.const_usize(bx.layout_of(tp_ty).size.bytes())
+                }
+            }
+            sym::min_align_of_val => {
+                let tp_ty = substs.type_at(0);
+                if let OperandValue::Pair(_, meta) = args[0].val {
+                    let (_, llalign) = glue::size_and_align_of_dst(bx, tp_ty, Some(meta));
+                    llalign
+                } else {
+                    bx.const_usize(bx.layout_of(tp_ty).align.abi.bytes())
+                }
+            }
+            sym::size_of
+            | sym::pref_align_of
+            | sym::min_align_of
+            | sym::needs_drop
+            | sym::type_id
+            | sym::type_name
+            | sym::variant_count => {
+                let value = bx
+                    .tcx()
+                    .const_eval_instance(ty::ParamEnv::reveal_all(), instance, None)
+                    .unwrap();
+                OperandRef::from_const(bx, value, ret_ty).immediate_or_packed_pair(bx)
+            }
+            // Effectively no-op
+            sym::forget => {
+                return;
+            }
+            sym::offset => {
+                let ptr = args[0].immediate();
+                let offset = args[1].immediate();
+                bx.inbounds_gep(ptr, &[offset])
+            }
+            sym::arith_offset => {
+                let ptr = args[0].immediate();
+                let offset = args[1].immediate();
+                bx.gep(ptr, &[offset])
+            }
+
+            sym::copy_nonoverlapping => {
+                copy_intrinsic(
+                    bx,
+                    false,
+                    false,
+                    substs.type_at(0),
+                    args[1].immediate(),
+                    args[0].immediate(),
+                    args[2].immediate(),
+                );
+                return;
+            }
+            sym::copy => {
+                copy_intrinsic(
+                    bx,
+                    true,
+                    false,
+                    substs.type_at(0),
+                    args[1].immediate(),
+                    args[0].immediate(),
+                    args[2].immediate(),
+                );
+                return;
+            }
+            sym::write_bytes => {
+                memset_intrinsic(
+                    bx,
+                    false,
+                    substs.type_at(0),
+                    args[0].immediate(),
+                    args[1].immediate(),
+                    args[2].immediate(),
+                );
+                return;
+            }
+
+            sym::volatile_copy_nonoverlapping_memory => {
+                copy_intrinsic(
+                    bx,
+                    false,
+                    true,
+                    substs.type_at(0),
+                    args[0].immediate(),
+                    args[1].immediate(),
+                    args[2].immediate(),
+                );
+                return;
+            }
+            sym::volatile_copy_memory => {
+                copy_intrinsic(
+                    bx,
+                    true,
+                    true,
+                    substs.type_at(0),
+                    args[0].immediate(),
+                    args[1].immediate(),
+                    args[2].immediate(),
+                );
+                return;
+            }
+            sym::volatile_set_memory => {
+                memset_intrinsic(
+                    bx,
+                    true,
+                    substs.type_at(0),
+                    args[0].immediate(),
+                    args[1].immediate(),
+                    args[2].immediate(),
+                );
+                return;
+            }
+            sym::volatile_store => {
+                let dst = args[0].deref(bx.cx());
+                args[1].val.volatile_store(bx, dst);
+                return;
+            }
+            sym::unaligned_volatile_store => {
+                let dst = args[0].deref(bx.cx());
+                args[1].val.unaligned_volatile_store(bx, dst);
+                return;
+            }
+            sym::add_with_overflow
+            | sym::sub_with_overflow
+            | sym::mul_with_overflow
+            | sym::wrapping_add
+            | sym::wrapping_sub
+            | sym::wrapping_mul
+            | sym::unchecked_div
+            | sym::unchecked_rem
+            | sym::unchecked_shl
+            | sym::unchecked_shr
+            | sym::unchecked_add
+            | sym::unchecked_sub
+            | sym::unchecked_mul
+            | sym::exact_div => {
+                let ty = arg_tys[0];
+                match int_type_width_signed(ty, bx.tcx()) {
+                    Some((_width, signed)) => match name {
+                        sym::add_with_overflow
+                        | sym::sub_with_overflow
+                        | sym::mul_with_overflow => {
+                            let op = match name {
+                                sym::add_with_overflow => OverflowOp::Add,
+                                sym::sub_with_overflow => OverflowOp::Sub,
+                                sym::mul_with_overflow => OverflowOp::Mul,
+                                _ => bug!(),
+                            };
+                            let (val, overflow) =
+                                bx.checked_binop(op, ty, args[0].immediate(), args[1].immediate());
+                            // Convert `i1` to a `bool`, and write it to the out parameter
+                            let val = bx.from_immediate(val);
+                            let overflow = bx.from_immediate(overflow);
+
+                            let dest = result.project_field(bx, 0);
+                            bx.store(val, dest.llval, dest.align);
+                            let dest = result.project_field(bx, 1);
+                            bx.store(overflow, dest.llval, dest.align);
+
+                            return;
+                        }
+                        sym::wrapping_add => bx.add(args[0].immediate(), args[1].immediate()),
+                        sym::wrapping_sub => bx.sub(args[0].immediate(), args[1].immediate()),
+                        sym::wrapping_mul => bx.mul(args[0].immediate(), args[1].immediate()),
+                        sym::exact_div => {
+                            if signed {
+                                bx.exactsdiv(args[0].immediate(), args[1].immediate())
+                            } else {
+                                bx.exactudiv(args[0].immediate(), args[1].immediate())
+                            }
+                        }
+                        sym::unchecked_div => {
+                            if signed {
+                                bx.sdiv(args[0].immediate(), args[1].immediate())
+                            } else {
+                                bx.udiv(args[0].immediate(), args[1].immediate())
+                            }
+                        }
+                        sym::unchecked_rem => {
+                            if signed {
+                                bx.srem(args[0].immediate(), args[1].immediate())
+                            } else {
+                                bx.urem(args[0].immediate(), args[1].immediate())
+                            }
+                        }
+                        sym::unchecked_shl => bx.shl(args[0].immediate(), args[1].immediate()),
+                        sym::unchecked_shr => {
+                            if signed {
+                                bx.ashr(args[0].immediate(), args[1].immediate())
+                            } else {
+                                bx.lshr(args[0].immediate(), args[1].immediate())
+                            }
+                        }
+                        sym::unchecked_add => {
+                            if signed {
+                                bx.unchecked_sadd(args[0].immediate(), args[1].immediate())
+                            } else {
+                                bx.unchecked_uadd(args[0].immediate(), args[1].immediate())
+                            }
+                        }
+                        sym::unchecked_sub => {
+                            if signed {
+                                bx.unchecked_ssub(args[0].immediate(), args[1].immediate())
+                            } else {
+                                bx.unchecked_usub(args[0].immediate(), args[1].immediate())
+                            }
+                        }
+                        sym::unchecked_mul => {
+                            if signed {
+                                bx.unchecked_smul(args[0].immediate(), args[1].immediate())
+                            } else {
+                                bx.unchecked_umul(args[0].immediate(), args[1].immediate())
+                            }
+                        }
+                        _ => bug!(),
+                    },
+                    None => {
+                        span_invalid_monomorphization_error(
+                            bx.tcx().sess,
+                            span,
+                            &format!(
+                                "invalid monomorphization of `{}` intrinsic: \
+                                      expected basic integer type, found `{}`",
+                                name, ty
+                            ),
+                        );
+                        return;
+                    }
+                }
+            }
+            sym::fadd_fast | sym::fsub_fast | sym::fmul_fast | sym::fdiv_fast | sym::frem_fast => {
+                match float_type_width(arg_tys[0]) {
+                    Some(_width) => match name {
+                        sym::fadd_fast => bx.fadd_fast(args[0].immediate(), args[1].immediate()),
+                        sym::fsub_fast => bx.fsub_fast(args[0].immediate(), args[1].immediate()),
+                        sym::fmul_fast => bx.fmul_fast(args[0].immediate(), args[1].immediate()),
+                        sym::fdiv_fast => bx.fdiv_fast(args[0].immediate(), args[1].immediate()),
+                        sym::frem_fast => bx.frem_fast(args[0].immediate(), args[1].immediate()),
+                        _ => bug!(),
+                    },
+                    None => {
+                        span_invalid_monomorphization_error(
+                            bx.tcx().sess,
+                            span,
+                            &format!(
+                                "invalid monomorphization of `{}` intrinsic: \
+                                      expected basic float type, found `{}`",
+                                name, arg_tys[0]
+                            ),
+                        );
+                        return;
+                    }
+                }
+            }
+
+            sym::float_to_int_unchecked => {
+                if float_type_width(arg_tys[0]).is_none() {
+                    span_invalid_monomorphization_error(
+                        bx.tcx().sess,
+                        span,
+                        &format!(
+                            "invalid monomorphization of `float_to_int_unchecked` \
+                                  intrinsic: expected basic float type, \
+                                  found `{}`",
+                            arg_tys[0]
+                        ),
+                    );
+                    return;
+                }
+                let (_width, signed) = match int_type_width_signed(ret_ty, bx.tcx()) {
+                    Some(pair) => pair,
+                    None => {
+                        span_invalid_monomorphization_error(
+                            bx.tcx().sess,
+                            span,
+                            &format!(
+                                "invalid monomorphization of `float_to_int_unchecked` \
+                                      intrinsic:  expected basic integer type, \
+                                      found `{}`",
+                                ret_ty
+                            ),
+                        );
+                        return;
+                    }
+                };
+                if signed {
+                    bx.fptosi(args[0].immediate(), llret_ty)
+                } else {
+                    bx.fptoui(args[0].immediate(), llret_ty)
+                }
+            }
+
+            sym::discriminant_value => {
+                if ret_ty.is_integral() {
+                    args[0].deref(bx.cx()).codegen_get_discr(bx, ret_ty)
+                } else {
+                    span_bug!(span, "Invalid discriminant type for `{:?}`", arg_tys[0])
+                }
+            }
+
+            // This requires that atomic intrinsics follow a specific naming pattern:
+            // "atomic_<operation>[_<ordering>]", and no ordering means SeqCst
+            name if name_str.starts_with("atomic_") => {
+                use crate::common::AtomicOrdering::*;
+                use crate::common::{AtomicRmwBinOp, SynchronizationScope};
+
+                let split: Vec<&str> = name_str.split('_').collect();
+
+                let is_cxchg = split[1] == "cxchg" || split[1] == "cxchgweak";
+                let (order, failorder) = match split.len() {
+                    2 => (SequentiallyConsistent, SequentiallyConsistent),
+                    3 => match split[2] {
+                        "unordered" => (Unordered, Unordered),
+                        "relaxed" => (Monotonic, Monotonic),
+                        "acq" => (Acquire, Acquire),
+                        "rel" => (Release, Monotonic),
+                        "acqrel" => (AcquireRelease, Acquire),
+                        "failrelaxed" if is_cxchg => (SequentiallyConsistent, Monotonic),
+                        "failacq" if is_cxchg => (SequentiallyConsistent, Acquire),
+                        _ => bx.sess().fatal("unknown ordering in atomic intrinsic"),
+                    },
+                    4 => match (split[2], split[3]) {
+                        ("acq", "failrelaxed") if is_cxchg => (Acquire, Monotonic),
+                        ("acqrel", "failrelaxed") if is_cxchg => (AcquireRelease, Monotonic),
+                        _ => bx.sess().fatal("unknown ordering in atomic intrinsic"),
+                    },
+                    _ => bx.sess().fatal("Atomic intrinsic not in correct format"),
+                };
+
+                let invalid_monomorphization = |ty| {
+                    span_invalid_monomorphization_error(
+                        bx.tcx().sess,
+                        span,
+                        &format!(
+                            "invalid monomorphization of `{}` intrinsic: \
+                                  expected basic integer type, found `{}`",
+                            name, ty
+                        ),
+                    );
+                };
+
+                match split[1] {
+                    "cxchg" | "cxchgweak" => {
+                        let ty = substs.type_at(0);
+                        if int_type_width_signed(ty, bx.tcx()).is_some() {
+                            let weak = split[1] == "cxchgweak";
+                            let pair = bx.atomic_cmpxchg(
+                                args[0].immediate(),
+                                args[1].immediate(),
+                                args[2].immediate(),
+                                order,
+                                failorder,
+                                weak,
+                            );
+                            let val = bx.extract_value(pair, 0);
+                            let success = bx.extract_value(pair, 1);
+                            let val = bx.from_immediate(val);
+                            let success = bx.from_immediate(success);
+
+                            let dest = result.project_field(bx, 0);
+                            bx.store(val, dest.llval, dest.align);
+                            let dest = result.project_field(bx, 1);
+                            bx.store(success, dest.llval, dest.align);
+                            return;
+                        } else {
+                            return invalid_monomorphization(ty);
+                        }
+                    }
+
+                    "load" => {
+                        let ty = substs.type_at(0);
+                        if int_type_width_signed(ty, bx.tcx()).is_some() {
+                            let size = bx.layout_of(ty).size;
+                            bx.atomic_load(args[0].immediate(), order, size)
+                        } else {
+                            return invalid_monomorphization(ty);
+                        }
+                    }
+
+                    "store" => {
+                        let ty = substs.type_at(0);
+                        if int_type_width_signed(ty, bx.tcx()).is_some() {
+                            let size = bx.layout_of(ty).size;
+                            bx.atomic_store(args[1].immediate(), args[0].immediate(), order, size);
+                            return;
+                        } else {
+                            return invalid_monomorphization(ty);
+                        }
+                    }
+
+                    "fence" => {
+                        bx.atomic_fence(order, SynchronizationScope::CrossThread);
+                        return;
+                    }
+
+                    "singlethreadfence" => {
+                        bx.atomic_fence(order, SynchronizationScope::SingleThread);
+                        return;
+                    }
+
+                    // These are all AtomicRMW ops
+                    op => {
+                        let atom_op = match op {
+                            "xchg" => AtomicRmwBinOp::AtomicXchg,
+                            "xadd" => AtomicRmwBinOp::AtomicAdd,
+                            "xsub" => AtomicRmwBinOp::AtomicSub,
+                            "and" => AtomicRmwBinOp::AtomicAnd,
+                            "nand" => AtomicRmwBinOp::AtomicNand,
+                            "or" => AtomicRmwBinOp::AtomicOr,
+                            "xor" => AtomicRmwBinOp::AtomicXor,
+                            "max" => AtomicRmwBinOp::AtomicMax,
+                            "min" => AtomicRmwBinOp::AtomicMin,
+                            "umax" => AtomicRmwBinOp::AtomicUMax,
+                            "umin" => AtomicRmwBinOp::AtomicUMin,
+                            _ => bx.sess().fatal("unknown atomic operation"),
+                        };
+
+                        let ty = substs.type_at(0);
+                        if int_type_width_signed(ty, bx.tcx()).is_some() {
+                            bx.atomic_rmw(atom_op, args[0].immediate(), args[1].immediate(), order)
+                        } else {
+                            return invalid_monomorphization(ty);
+                        }
+                    }
+                }
+            }
+
+            sym::nontemporal_store => {
+                let dst = args[0].deref(bx.cx());
+                args[1].val.nontemporal_store(bx, dst);
+                return;
+            }
+
+            sym::ptr_guaranteed_eq | sym::ptr_guaranteed_ne => {
+                let a = args[0].immediate();
+                let b = args[1].immediate();
+                if name == sym::ptr_guaranteed_eq {
+                    bx.icmp(IntPredicate::IntEQ, a, b)
+                } else {
+                    bx.icmp(IntPredicate::IntNE, a, b)
+                }
+            }
+
+            sym::ptr_offset_from => {
+                let ty = substs.type_at(0);
+                let pointee_size = bx.layout_of(ty).size;
+
+                // This is the same sequence that Clang emits for pointer subtraction.
+                // It can be neither `nsw` nor `nuw` because the input is treated as
+                // unsigned but then the output is treated as signed, so neither works.
+                let a = args[0].immediate();
+                let b = args[1].immediate();
+                let a = bx.ptrtoint(a, bx.type_isize());
+                let b = bx.ptrtoint(b, bx.type_isize());
+                let d = bx.sub(a, b);
+                let pointee_size = bx.const_usize(pointee_size.bytes());
+                // this is where the signed magic happens (notice the `s` in `exactsdiv`)
+                bx.exactsdiv(d, pointee_size)
+            }
+
+            _ => {
+                // Need to use backend-specific things in the implementation.
+                bx.codegen_intrinsic_call(instance, fn_abi, args, llresult, span);
+                return;
+            }
+        };
+
+        if !fn_abi.ret.is_ignore() {
+            if let PassMode::Cast(ty) = fn_abi.ret.mode {
+                let ptr_llty = bx.type_ptr_to(bx.cast_backend_type(&ty));
+                let ptr = bx.pointercast(result.llval, ptr_llty);
+                bx.store(llval, ptr, result.align);
+            } else {
+                OperandRef::from_immediate_or_packed_pair(bx, llval, result.layout)
+                    .val
+                    .store(bx, result);
+            }
+        }
+    }
+}
+
+// Returns the width of an int Ty, and if it's signed or not
+// Returns None if the type is not an integer
+// FIXME: there’s multiple of this functions, investigate using some of the already existing
+// stuffs.
+fn int_type_width_signed(ty: Ty<'_>, tcx: TyCtxt<'_>) -> Option<(u64, bool)> {
+    match ty.kind() {
+        ty::Int(t) => Some((t.bit_width().unwrap_or(u64::from(tcx.sess.target.ptr_width)), true)),
+        ty::Uint(t) => Some((t.bit_width().unwrap_or(u64::from(tcx.sess.target.ptr_width)), false)),
+        _ => None,
+    }
+}
+
+// Returns the width of a float Ty
+// Returns None if the type is not a float
+fn float_type_width(ty: Ty<'_>) -> Option<u64> {
+    match ty.kind() {
+        ty::Float(t) => Some(t.bit_width()),
+        _ => None,
+    }
+}
diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs
index 26e6c354702..64d456fb7aa 100644
--- a/compiler/rustc_codegen_ssa/src/mir/mod.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs
@@ -369,8 +369,8 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
                 // individual LLVM function arguments.
 
                 let arg_ty = fx.monomorphize(&arg_decl.ty);
-                let tupled_arg_tys = match arg_ty.kind {
-                    ty::Tuple(ref tys) => tys,
+                let tupled_arg_tys = match arg_ty.kind() {
+                    ty::Tuple(tys) => tys,
                     _ => bug!("spread argument isn't a tuple?!"),
                 };
 
@@ -486,6 +486,7 @@ mod block;
 pub mod constant;
 pub mod coverageinfo;
 pub mod debuginfo;
+mod intrinsic;
 pub mod operand;
 pub mod place;
 mod rvalue;
diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs
index 937c7457c63..bbd004be875 100644
--- a/compiler/rustc_codegen_ssa/src/mir/operand.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs
@@ -147,8 +147,8 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
             debug!("Operand::immediate_or_packed_pair: packing {:?} into {:?}", self, llty);
             // Reconstruct the immediate aggregate.
             let mut llpair = bx.cx().const_undef(llty);
-            let imm_a = base::from_immediate(bx, a);
-            let imm_b = base::from_immediate(bx, b);
+            let imm_a = bx.from_immediate(a);
+            let imm_b = bx.from_immediate(b);
             llpair = bx.insert_value(llpair, imm_a, 0);
             llpair = bx.insert_value(llpair, imm_b, 1);
             llpair
@@ -168,9 +168,9 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
 
             // Deconstruct the immediate aggregate.
             let a_llval = bx.extract_value(llval, 0);
-            let a_llval = base::to_immediate_scalar(bx, a_llval, a);
+            let a_llval = bx.to_immediate_scalar(a_llval, a);
             let b_llval = bx.extract_value(llval, 1);
-            let b_llval = base::to_immediate_scalar(bx, b_llval, b);
+            let b_llval = bx.to_immediate_scalar(b_llval, b);
             OperandValue::Pair(a_llval, b_llval)
         } else {
             OperandValue::Immediate(llval)
@@ -220,29 +220,23 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
             _ => bug!("OperandRef::extract_field({:?}): not applicable", self),
         };
 
-        // HACK(eddyb) have to bitcast pointers until LLVM removes pointee types.
-        // Bools in union fields needs to be truncated.
-        let to_immediate_or_cast = |bx: &mut Bx, val, ty| {
-            if ty == bx.cx().type_i1() { bx.trunc(val, ty) } else { bx.bitcast(val, ty) }
-        };
-
-        match val {
-            OperandValue::Immediate(ref mut llval) => {
-                *llval = to_immediate_or_cast(bx, *llval, bx.cx().immediate_backend_type(field));
+        match (&mut val, &field.abi) {
+            (OperandValue::Immediate(llval), _) => {
+                // Bools in union fields needs to be truncated.
+                *llval = bx.to_immediate(*llval, field);
+                // HACK(eddyb) have to bitcast pointers until LLVM removes pointee types.
+                *llval = bx.bitcast(*llval, bx.cx().immediate_backend_type(field));
             }
-            OperandValue::Pair(ref mut a, ref mut b) => {
-                *a = to_immediate_or_cast(
-                    bx,
-                    *a,
-                    bx.cx().scalar_pair_element_backend_type(field, 0, true),
-                );
-                *b = to_immediate_or_cast(
-                    bx,
-                    *b,
-                    bx.cx().scalar_pair_element_backend_type(field, 1, true),
-                );
+            (OperandValue::Pair(a, b), Abi::ScalarPair(a_abi, b_abi)) => {
+                // Bools in union fields needs to be truncated.
+                *a = bx.to_immediate_scalar(*a, a_abi);
+                *b = bx.to_immediate_scalar(*b, b_abi);
+                // HACK(eddyb) have to bitcast pointers until LLVM removes pointee types.
+                *a = bx.bitcast(*a, bx.cx().scalar_pair_element_backend_type(field, 0, true));
+                *b = bx.bitcast(*b, bx.cx().scalar_pair_element_backend_type(field, 1, true));
             }
-            OperandValue::Ref(..) => bug!(),
+            (OperandValue::Pair(..), _) => bug!(),
+            (OperandValue::Ref(..), _) => bug!(),
         }
 
         OperandRef { val, layout: field }
@@ -302,7 +296,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue<V> {
                 bug!("cannot directly store unsized values");
             }
             OperandValue::Immediate(s) => {
-                let val = base::from_immediate(bx, s);
+                let val = bx.from_immediate(s);
                 bx.store_with_flags(val, dest.llval, dest.align, flags);
             }
             OperandValue::Pair(a, b) => {
@@ -313,12 +307,12 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue<V> {
                 let b_offset = a_scalar.value.size(bx).align_to(b_scalar.value.align(bx).abi);
 
                 let llptr = bx.struct_gep(dest.llval, 0);
-                let val = base::from_immediate(bx, a);
+                let val = bx.from_immediate(a);
                 let align = dest.align;
                 bx.store_with_flags(val, llptr, align, flags);
 
                 let llptr = bx.struct_gep(dest.llval, 1);
-                let val = base::from_immediate(bx, b);
+                let val = bx.from_immediate(b);
                 let align = dest.align.restrict_for_offset(b_offset);
                 bx.store_with_flags(val, llptr, align, flags);
             }
diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs
index 799f3b3a575..91609b22615 100644
--- a/compiler/rustc_codegen_ssa/src/mir/place.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/place.rs
@@ -134,7 +134,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
         //   * no metadata available - just log the case
         //   * known alignment - sized types, `[T]`, `str` or a foreign type
         //   * packed struct - there is no alignment padding
-        match field.ty.kind {
+        match field.ty.kind() {
             _ if self.llextra.is_none() => {
                 debug!(
                     "unsized field `{}`, of `{:?}` has no metadata for adjustment",
diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
index 71f924df119..7ce110dcbfc 100644
--- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
@@ -98,7 +98,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     }
 
                     // Use llvm.memset.p0i8.* to initialize byte arrays
-                    let v = base::from_immediate(&mut bx, v);
+                    let v = bx.from_immediate(v);
                     if bx.cx().val_ty(v) == bx.cx().type_i8() {
                         bx.memset(start, v, size, dest.align, MemFlags::empty());
                         return bx;
@@ -185,7 +185,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
 
                 let val = match *kind {
                     mir::CastKind::Pointer(PointerCast::ReifyFnPointer) => {
-                        match operand.layout.ty.kind {
+                        match *operand.layout.ty.kind() {
                             ty::FnDef(def_id, substs) => {
                                 if bx.cx().tcx().has_attr(def_id, sym::rustc_args_required_const) {
                                     bug!("reifying a fn ptr that requires const arguments");
@@ -204,7 +204,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                         }
                     }
                     mir::CastKind::Pointer(PointerCast::ClosureFnPointer(_)) => {
-                        match operand.layout.ty.kind {
+                        match *operand.layout.ty.kind() {
                             ty::Closure(def_id, substs) => {
                                 let instance = Instance::resolve_closure(
                                     bx.cx().tcx(),
@@ -327,13 +327,29 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                                 if er.end != er.start
                                     && scalar.valid_range.end() > scalar.valid_range.start()
                                 {
-                                    // We want `table[e as usize]` to not
+                                    // We want `table[e as usize ± k]` to not
                                     // have bound checks, and this is the most
-                                    // convenient place to put the `assume`.
-                                    let ll_t_in_const =
+                                    // convenient place to put the `assume`s.
+                                    if *scalar.valid_range.start() > 0 {
+                                        let enum_value_lower_bound = bx
+                                            .cx()
+                                            .const_uint_big(ll_t_in, *scalar.valid_range.start());
+                                        let cmp_start = bx.icmp(
+                                            IntPredicate::IntUGE,
+                                            llval,
+                                            enum_value_lower_bound,
+                                        );
+                                        bx.assume(cmp_start);
+                                    }
+
+                                    let enum_value_upper_bound =
                                         bx.cx().const_uint_big(ll_t_in, *scalar.valid_range.end());
-                                    let cmp = bx.icmp(IntPredicate::IntULE, llval, ll_t_in_const);
-                                    bx.assume(cmp);
+                                    let cmp_end = bx.icmp(
+                                        IntPredicate::IntULE,
+                                        llval,
+                                        enum_value_upper_bound,
+                                    );
+                                    bx.assume(cmp_end);
                                 }
                             }
                         }
@@ -548,7 +564,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         // because codegen_place() panics if Local is operand.
         if let Some(index) = place.as_local() {
             if let LocalRef::Operand(Some(op)) = self.locals[index] {
-                if let ty::Array(_, n) = op.layout.ty.kind {
+                if let ty::Array(_, n) = op.layout.ty.kind() {
                     let n = n.eval_usize(bx.cx().tcx(), ty::ParamEnv::reveal_all());
                     return bx.cx().const_usize(n);
                 }
diff --git a/compiler/rustc_codegen_ssa/src/mono_item.rs b/compiler/rustc_codegen_ssa/src/mono_item.rs
index fc65149937f..607b5459673 100644
--- a/compiler/rustc_codegen_ssa/src/mono_item.rs
+++ b/compiler/rustc_codegen_ssa/src/mono_item.rs
@@ -21,7 +21,7 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> {
     fn define<Bx: BuilderMethods<'a, 'tcx>>(&self, cx: &'a Bx::CodegenCx) {
         debug!(
             "BEGIN IMPLEMENTING '{} ({})' in cgu {}",
-            self.to_string(cx.tcx(), true),
+            self,
             self.to_raw_string(),
             cx.codegen_unit().name()
         );
@@ -45,7 +45,7 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> {
 
         debug!(
             "END IMPLEMENTING '{} ({})' in cgu {}",
-            self.to_string(cx.tcx(), true),
+            self,
             self.to_raw_string(),
             cx.codegen_unit().name()
         );
@@ -59,7 +59,7 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> {
     ) {
         debug!(
             "BEGIN PREDEFINING '{} ({})' in cgu {}",
-            self.to_string(cx.tcx(), true),
+            self,
             self.to_raw_string(),
             cx.codegen_unit().name()
         );
@@ -80,7 +80,7 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> {
 
         debug!(
             "END PREDEFINING '{} ({})' in cgu {}",
-            self.to_string(cx.tcx(), true),
+            self,
             self.to_raw_string(),
             cx.codegen_unit().name()
         );
diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs
index 3522ea01153..90520f77e3c 100644
--- a/compiler/rustc_codegen_ssa/src/traits/backend.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs
@@ -15,6 +15,7 @@ use rustc_session::{
 };
 use rustc_span::symbol::Symbol;
 use rustc_target::abi::LayoutOf;
+use rustc_target::spec::Target;
 
 pub use rustc_data_structures::sync::MetadataRef;
 
@@ -54,6 +55,12 @@ pub trait CodegenBackend {
     fn print_passes(&self) {}
     fn print_version(&self) {}
 
+    /// If this plugin provides additional builtin targets, provide the one enabled by the options here.
+    /// Be careful: this is called *before* init() is called.
+    fn target_override(&self, _opts: &config::Options) -> Option<Target> {
+        None
+    }
+
     fn metadata_loader(&self) -> Box<MetadataLoaderDyn>;
     fn provide(&self, _providers: &mut Providers);
     fn provide_extern(&self, _providers: &mut Providers);
diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs
index 5ffc83c5f99..5142922260a 100644
--- a/compiler/rustc_codegen_ssa/src/traits/builder.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs
@@ -13,9 +13,9 @@ use crate::mir::operand::OperandRef;
 use crate::mir::place::PlaceRef;
 use crate::MemFlags;
 
-use rustc_middle::ty::layout::HasParamEnv;
+use rustc_middle::ty::layout::{HasParamEnv, TyAndLayout};
 use rustc_middle::ty::Ty;
-use rustc_target::abi::{Align, Size};
+use rustc_target::abi::{Abi, Align, Scalar, Size};
 use rustc_target::spec::HasTargetSpec;
 
 use std::iter::TrustedLen;
@@ -115,6 +115,16 @@ pub trait BuilderMethods<'a, 'tcx>:
         rhs: Self::Value,
     ) -> (Self::Value, Self::Value);
 
+    fn from_immediate(&mut self, val: Self::Value) -> Self::Value;
+    fn to_immediate(&mut self, val: Self::Value, layout: TyAndLayout<'_>) -> Self::Value {
+        if let Abi::Scalar(ref scalar) = layout.abi {
+            self.to_immediate_scalar(val, scalar)
+        } else {
+            val
+        }
+    }
+    fn to_immediate_scalar(&mut self, val: Self::Value, scalar: &Scalar) -> Self::Value;
+
     fn alloca(&mut self, ty: Self::Type, align: Align) -> Self::Value;
     fn dynamic_alloca(&mut self, ty: Self::Type, align: Align) -> Self::Value;
     fn array_alloca(&mut self, ty: Self::Type, len: Self::Value, align: Align) -> Self::Value;
diff --git a/compiler/rustc_codegen_ssa/src/traits/declare.rs b/compiler/rustc_codegen_ssa/src/traits/declare.rs
index 690aacd2056..655afcd17f0 100644
--- a/compiler/rustc_codegen_ssa/src/traits/declare.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/declare.rs
@@ -1,51 +1,7 @@
 use super::BackendTypes;
 use rustc_hir::def_id::DefId;
 use rustc_middle::mir::mono::{Linkage, Visibility};
-use rustc_middle::ty::{Instance, Ty};
-use rustc_target::abi::call::FnAbi;
-
-pub trait DeclareMethods<'tcx>: BackendTypes {
-    /// Declare a global value.
-    ///
-    /// If there’s a value with the same name already declared, the function will
-    /// return its Value instead.
-    fn declare_global(&self, name: &str, ty: Self::Type) -> Self::Value;
-
-    /// Declare a C ABI function.
-    ///
-    /// Only use this for foreign function ABIs and glue. For Rust functions use
-    /// `declare_fn` instead.
-    ///
-    /// If there’s a value with the same name already declared, the function will
-    /// update the declaration and return existing Value instead.
-    fn declare_cfn(&self, name: &str, fn_type: Self::Type) -> Self::Function;
-
-    /// Declare a Rust function.
-    ///
-    /// If there’s a value with the same name already declared, the function will
-    /// update the declaration and return existing Value instead.
-    fn declare_fn(&self, name: &str, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> Self::Function;
-
-    /// Declare a global with an intention to define it.
-    ///
-    /// Use this function when you intend to define a global. This function will
-    /// return `None` if the name already has a definition associated with it. In that
-    /// case an error should be reported to the user, because it usually happens due
-    /// to user’s fault (e.g., misuse of `#[no_mangle]` or `#[export_name]` attributes).
-    fn define_global(&self, name: &str, ty: Self::Type) -> Option<Self::Value>;
-
-    /// Declare a private global
-    ///
-    /// Use this function when you intend to define a global without a name.
-    fn define_private_global(&self, ty: Self::Type) -> Self::Value;
-
-    /// Gets declared value by name.
-    fn get_declared_value(&self, name: &str) -> Option<Self::Value>;
-
-    /// Gets defined or externally defined (AvailableExternally linkage) value by
-    /// name.
-    fn get_defined_value(&self, name: &str) -> Option<Self::Value>;
-}
+use rustc_middle::ty::Instance;
 
 pub trait PreDefineMethods<'tcx>: BackendTypes {
     fn predefine_static(
diff --git a/compiler/rustc_codegen_ssa/src/traits/misc.rs b/compiler/rustc_codegen_ssa/src/traits/misc.rs
index fc57a9a80b2..6fff64bfcb6 100644
--- a/compiler/rustc_codegen_ssa/src/traits/misc.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/misc.rs
@@ -19,4 +19,6 @@ pub trait MiscMethods<'tcx>: BackendTypes {
     fn set_frame_pointer_elimination(&self, llfn: Self::Function);
     fn apply_target_cpu_attr(&self, llfn: Self::Function);
     fn create_used_variable(&self);
+    /// Declares the extern "C" main function for the entry point. Returns None if the symbol already exists.
+    fn declare_c_main(&self, fn_type: Self::Type) -> Option<Self::Function>;
 }
diff --git a/compiler/rustc_codegen_ssa/src/traits/mod.rs b/compiler/rustc_codegen_ssa/src/traits/mod.rs
index 0ac519dd0b1..698ef6083e6 100644
--- a/compiler/rustc_codegen_ssa/src/traits/mod.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/mod.rs
@@ -35,7 +35,7 @@ pub use self::builder::{BuilderMethods, OverflowOp};
 pub use self::consts::ConstMethods;
 pub use self::coverageinfo::{CoverageInfoBuilderMethods, CoverageInfoMethods};
 pub use self::debuginfo::{DebugInfoBuilderMethods, DebugInfoMethods};
-pub use self::declare::{DeclareMethods, PreDefineMethods};
+pub use self::declare::PreDefineMethods;
 pub use self::intrinsic::IntrinsicCallMethods;
 pub use self::misc::MiscMethods;
 pub use self::statics::{StaticBuilderMethods, StaticMethods};
@@ -60,7 +60,6 @@ pub trait CodegenMethods<'tcx>:
     + StaticMethods
     + CoverageInfoMethods
     + DebugInfoMethods<'tcx>
-    + DeclareMethods<'tcx>
     + AsmMethods
     + PreDefineMethods<'tcx>
     + HasParamEnv<'tcx>
@@ -77,7 +76,6 @@ impl<'tcx, T> CodegenMethods<'tcx> for T where
         + StaticMethods
         + CoverageInfoMethods
         + DebugInfoMethods<'tcx>
-        + DeclareMethods<'tcx>
         + AsmMethods
         + PreDefineMethods<'tcx>
         + HasParamEnv<'tcx>
diff --git a/compiler/rustc_codegen_ssa/src/traits/type_.rs b/compiler/rustc_codegen_ssa/src/traits/type_.rs
index 726d948cfd4..cec07b977e6 100644
--- a/compiler/rustc_codegen_ssa/src/traits/type_.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/type_.rs
@@ -89,7 +89,7 @@ pub trait DerivedTypeMethods<'tcx>: BaseTypeMethods<'tcx> + MiscMethods<'tcx> {
         }
 
         let tail = self.tcx().struct_tail_erasing_lifetimes(ty, param_env);
-        match tail.kind {
+        match tail.kind() {
             ty::Foreign(..) => false,
             ty::Str | ty::Slice(..) | ty::Dynamic(..) => true,
             _ => bug!("unexpected unsized tail: {:?}", tail),
diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs
index 27d52e9b9c5..264e7c2aa92 100644
--- a/compiler/rustc_codegen_ssa/src/traits/write.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/write.rs
@@ -13,6 +13,12 @@ pub trait WriteBackendMethods: 'static + Sized + Clone {
     type ThinData: Send + Sync;
     type ThinBuffer: ThinBufferMethods;
 
+    /// Merge all modules into main_module and returning it
+    fn run_link(
+        cgcx: &CodegenContext<Self>,
+        diag_handler: &Handler,
+        modules: Vec<ModuleCodegen<Self::Module>>,
+    ) -> Result<ModuleCodegen<Self::Module>, FatalError>;
     /// Performs fat LTO by merging all modules into a single one and returning it
     /// for further optimization.
     fn run_fat_lto(
diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml
index f929d9911f9..caaf7c0c3c2 100644
--- a/compiler/rustc_data_structures/Cargo.toml
+++ b/compiler/rustc_data_structures/Cargo.toml
@@ -8,12 +8,11 @@ edition = "2018"
 doctest = false
 
 [dependencies]
+arrayvec = { version = "0.5.1", default-features = false }
 ena = "0.14"
 indexmap = "1.5.1"
 tracing = "0.1"
 jobserver_crate = { version = "0.1.13", package = "jobserver" }
-lazy_static = "1"
-once_cell = { version = "1", features = ["parking_lot"] }
 rustc_serialize = { path = "../rustc_serialize" }
 rustc_macros = { path = "../rustc_macros" }
 rustc_graphviz = { path = "../rustc_graphviz" }
@@ -28,11 +27,11 @@ rustc_index = { path = "../rustc_index", package = "rustc_index" }
 bitflags = "1.2.1"
 measureme = "0.7.1"
 libc = "0.2"
-stacker = "0.1.11"
+stacker = "0.1.12"
 tempfile = "3.0.5"
 
 [dependencies.parking_lot]
-version = "0.10"
+version = "0.11"
 features = ["nightly"]
 
 [target.'cfg(windows)'.dependencies]
diff --git a/compiler/rustc_data_structures/src/fingerprint.rs b/compiler/rustc_data_structures/src/fingerprint.rs
index f8d631ce01e..aba0bbbac80 100644
--- a/compiler/rustc_data_structures/src/fingerprint.rs
+++ b/compiler/rustc_data_structures/src/fingerprint.rs
@@ -3,9 +3,10 @@ use rustc_serialize::{
     opaque::{self, EncodeResult},
     Decodable, Encodable,
 };
+use std::hash::{Hash, Hasher};
 use std::mem;
 
-#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Clone, Copy)]
+#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Copy)]
 pub struct Fingerprint(u64, u64);
 
 impl Fingerprint {
@@ -76,6 +77,33 @@ impl ::std::fmt::Display for Fingerprint {
     }
 }
 
+impl Hash for Fingerprint {
+    #[inline]
+    fn hash<H: Hasher>(&self, state: &mut H) {
+        state.write_fingerprint(self);
+    }
+}
+
+trait FingerprintHasher {
+    fn write_fingerprint(&mut self, fingerprint: &Fingerprint);
+}
+
+impl<H: Hasher> FingerprintHasher for H {
+    #[inline]
+    default fn write_fingerprint(&mut self, fingerprint: &Fingerprint) {
+        self.write_u64(fingerprint.0);
+        self.write_u64(fingerprint.1);
+    }
+}
+
+impl FingerprintHasher for crate::unhash::Unhasher {
+    #[inline]
+    fn write_fingerprint(&mut self, fingerprint: &Fingerprint) {
+        // `Unhasher` only wants a single `u64`
+        self.write_u64(fingerprint.0);
+    }
+}
+
 impl stable_hasher::StableHasherResult for Fingerprint {
     #[inline]
     fn finish(hasher: stable_hasher::StableHasher) -> Self {
diff --git a/compiler/rustc_data_structures/src/graph/iterate/mod.rs b/compiler/rustc_data_structures/src/graph/iterate/mod.rs
index 64ff6130ddf..bc3d1ce53ba 100644
--- a/compiler/rustc_data_structures/src/graph/iterate/mod.rs
+++ b/compiler/rustc_data_structures/src/graph/iterate/mod.rs
@@ -87,11 +87,8 @@ where
 }
 
 /// Allows searches to terminate early with a value.
-#[derive(Clone, Copy, Debug)]
-pub enum ControlFlow<T> {
-    Break(T),
-    Continue,
-}
+// FIXME (#75744): remove the alias once the generics are in a better order and `C=()`.
+pub type ControlFlow<T> = std::ops::ControlFlow<(), T>;
 
 /// The status of a node in the depth-first search.
 ///
@@ -260,12 +257,12 @@ where
         _node: G::Node,
         _prior_status: Option<NodeStatus>,
     ) -> ControlFlow<Self::BreakVal> {
-        ControlFlow::Continue
+        ControlFlow::CONTINUE
     }
 
     /// Called after all nodes reachable from this one have been examined.
     fn node_settled(&mut self, _node: G::Node) -> ControlFlow<Self::BreakVal> {
-        ControlFlow::Continue
+        ControlFlow::CONTINUE
     }
 
     /// Behave as if no edges exist from `source` to `target`.
@@ -289,8 +286,8 @@ where
         prior_status: Option<NodeStatus>,
     ) -> ControlFlow<Self::BreakVal> {
         match prior_status {
-            Some(NodeStatus::Visited) => ControlFlow::Break(()),
-            _ => ControlFlow::Continue,
+            Some(NodeStatus::Visited) => ControlFlow::BREAK,
+            _ => ControlFlow::CONTINUE,
         }
     }
 }
diff --git a/compiler/rustc_data_structures/src/jobserver.rs b/compiler/rustc_data_structures/src/jobserver.rs
index a811c88839d..41605afb44e 100644
--- a/compiler/rustc_data_structures/src/jobserver.rs
+++ b/compiler/rustc_data_structures/src/jobserver.rs
@@ -1,33 +1,31 @@
 pub use jobserver_crate::Client;
-use lazy_static::lazy_static;
+use std::lazy::SyncLazy;
 
-lazy_static! {
-    // We can only call `from_env` once per process
+// We can only call `from_env` once per process
 
-    // Note that this is unsafe because it may misinterpret file descriptors
-    // on Unix as jobserver file descriptors. We hopefully execute this near
-    // the beginning of the process though to ensure we don't get false
-    // positives, or in other words we try to execute this before we open
-    // any file descriptors ourselves.
-    //
-    // Pick a "reasonable maximum" if we don't otherwise have
-    // a jobserver in our environment, capping out at 32 so we
-    // don't take everything down by hogging the process run queue.
-    // The fixed number is used to have deterministic compilation
-    // across machines.
-    //
-    // Also note that we stick this in a global because there could be
-    // multiple rustc instances in this process, and the jobserver is
-    // per-process.
-    static ref GLOBAL_CLIENT: Client = unsafe {
-        Client::from_env().unwrap_or_else(|| {
-            let client = Client::new(32).expect("failed to create jobserver");
-            // Acquire a token for the main thread which we can release later
-            client.acquire_raw().ok();
-            client
-        })
-    };
-}
+// Note that this is unsafe because it may misinterpret file descriptors
+// on Unix as jobserver file descriptors. We hopefully execute this near
+// the beginning of the process though to ensure we don't get false
+// positives, or in other words we try to execute this before we open
+// any file descriptors ourselves.
+//
+// Pick a "reasonable maximum" if we don't otherwise have
+// a jobserver in our environment, capping out at 32 so we
+// don't take everything down by hogging the process run queue.
+// The fixed number is used to have deterministic compilation
+// across machines.
+//
+// Also note that we stick this in a global because there could be
+// multiple rustc instances in this process, and the jobserver is
+// per-process.
+static GLOBAL_CLIENT: SyncLazy<Client> = SyncLazy::new(|| unsafe {
+    Client::from_env().unwrap_or_else(|| {
+        let client = Client::new(32).expect("failed to create jobserver");
+        // Acquire a token for the main thread which we can release later
+        client.acquire_raw().ok();
+        client
+    })
+});
 
 pub fn client() -> Client {
     GLOBAL_CLIENT.clone()
diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs
index af4a7bd1881..9ded10e9c26 100644
--- a/compiler/rustc_data_structures/src/lib.rs
+++ b/compiler/rustc_data_structures/src/lib.rs
@@ -6,13 +6,15 @@
 //!
 //! This API is completely unstable and subject to change.
 
-#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
+#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![allow(incomplete_features)]
+#![feature(array_windows)]
+#![feature(control_flow_enum)]
 #![feature(in_band_lifetimes)]
 #![feature(unboxed_closures)]
-#![feature(generators)]
 #![feature(generator_trait)]
 #![feature(fn_traits)]
+#![feature(int_bits_const)]
 #![feature(min_specialization)]
 #![feature(optin_builtin_traits)]
 #![feature(nll)]
@@ -25,7 +27,8 @@
 #![feature(thread_id_value)]
 #![feature(extend_one)]
 #![feature(const_panic)]
-#![feature(const_generics)]
+#![feature(min_const_generics)]
+#![feature(once_cell)]
 #![allow(rustc::default_hash_types)]
 
 #[macro_use]
@@ -85,23 +88,27 @@ pub mod sorted_map;
 pub mod stable_set;
 #[macro_use]
 pub mod stable_hasher;
+mod atomic_ref;
+pub mod fingerprint;
+pub mod profiling;
 pub mod sharded;
 pub mod stack;
 pub mod sync;
 pub mod thin_vec;
 pub mod tiny_list;
 pub mod transitive_relation;
-pub use ena::undo_log;
-pub use ena::unify;
-mod atomic_ref;
-pub mod fingerprint;
-pub mod profiling;
 pub mod vec_linked_list;
 pub mod work_queue;
 pub use atomic_ref::AtomicRef;
 pub mod frozen;
+pub mod mini_map;
+pub mod mini_set;
 pub mod tagged_ptr;
 pub mod temp_dir;
+pub mod unhash;
+
+pub use ena::undo_log;
+pub use ena::unify;
 
 pub struct OnDrop<F: Fn()>(pub F);
 
diff --git a/compiler/rustc_data_structures/src/macros.rs b/compiler/rustc_data_structures/src/macros.rs
index 67fbe3058cd..b918ed9458c 100644
--- a/compiler/rustc_data_structures/src/macros.rs
+++ b/compiler/rustc_data_structures/src/macros.rs
@@ -1,15 +1,3 @@
-/// A simple static assertion macro.
-#[macro_export]
-#[allow_internal_unstable(type_ascription)]
-macro_rules! static_assert {
-    ($test:expr) => {
-        // Use the bool to access an array such that if the bool is false, the access
-        // is out-of-bounds.
-        #[allow(dead_code)]
-        const _: () = [()][!($test: bool) as usize];
-    };
-}
-
 /// Type size assertion. The first argument is a type and the second argument is its expected size.
 #[macro_export]
 macro_rules! static_assert_size {
diff --git a/compiler/rustc_data_structures/src/mini_map.rs b/compiler/rustc_data_structures/src/mini_map.rs
new file mode 100644
index 00000000000..cd3e949d383
--- /dev/null
+++ b/compiler/rustc_data_structures/src/mini_map.rs
@@ -0,0 +1,61 @@
+use crate::fx::FxHashMap;
+use arrayvec::ArrayVec;
+
+use std::hash::Hash;
+
+/// Small-storage-optimized implementation of a map
+/// made specifically for caching results.
+///
+/// Stores elements in a small array up to a certain length
+/// and switches to `HashMap` when that length is exceeded.
+pub enum MiniMap<K, V> {
+    Array(ArrayVec<[(K, V); 8]>),
+    Map(FxHashMap<K, V>),
+}
+
+impl<K: Eq + Hash, V> MiniMap<K, V> {
+    /// Creates an empty `MiniMap`.
+    pub fn new() -> Self {
+        MiniMap::Array(ArrayVec::new())
+    }
+
+    /// Inserts or updates value in the map.
+    pub fn insert(&mut self, key: K, value: V) {
+        match self {
+            MiniMap::Array(array) => {
+                for pair in array.iter_mut() {
+                    if pair.0 == key {
+                        pair.1 = value;
+                        return;
+                    }
+                }
+                if let Err(error) = array.try_push((key, value)) {
+                    let mut map: FxHashMap<K, V> = array.drain(..).collect();
+                    let (key, value) = error.element();
+                    map.insert(key, value);
+                    *self = MiniMap::Map(map);
+                }
+            }
+            MiniMap::Map(map) => {
+                map.insert(key, value);
+            }
+        }
+    }
+
+    /// Return value by key if any.
+    pub fn get(&self, key: &K) -> Option<&V> {
+        match self {
+            MiniMap::Array(array) => {
+                for pair in array {
+                    if pair.0 == *key {
+                        return Some(&pair.1);
+                    }
+                }
+                return None;
+            }
+            MiniMap::Map(map) => {
+                return map.get(key);
+            }
+        }
+    }
+}
diff --git a/compiler/rustc_data_structures/src/mini_set.rs b/compiler/rustc_data_structures/src/mini_set.rs
new file mode 100644
index 00000000000..9d45af723de
--- /dev/null
+++ b/compiler/rustc_data_structures/src/mini_set.rs
@@ -0,0 +1,41 @@
+use crate::fx::FxHashSet;
+use arrayvec::ArrayVec;
+use std::hash::Hash;
+/// Small-storage-optimized implementation of a set.
+///
+/// Stores elements in a small array up to a certain length
+/// and switches to `HashSet` when that length is exceeded.
+pub enum MiniSet<T> {
+    Array(ArrayVec<[T; 8]>),
+    Set(FxHashSet<T>),
+}
+
+impl<T: Eq + Hash> MiniSet<T> {
+    /// Creates an empty `MiniSet`.
+    pub fn new() -> Self {
+        MiniSet::Array(ArrayVec::new())
+    }
+
+    /// Adds a value to the set.
+    ///
+    /// If the set did not have this value present, true is returned.
+    ///
+    /// If the set did have this value present, false is returned.
+    pub fn insert(&mut self, elem: T) -> bool {
+        match self {
+            MiniSet::Array(array) => {
+                if array.iter().any(|e| *e == elem) {
+                    false
+                } else {
+                    if let Err(error) = array.try_push(elem) {
+                        let mut set: FxHashSet<T> = array.drain(..).collect();
+                        set.insert(error.element());
+                        *self = MiniSet::Set(set);
+                    }
+                    true
+                }
+            }
+            MiniSet::Set(set) => set.insert(elem),
+        }
+    }
+}
diff --git a/compiler/rustc_data_structures/src/profiling.rs b/compiler/rustc_data_structures/src/profiling.rs
index 07d16c6483e..363879cbb1d 100644
--- a/compiler/rustc_data_structures/src/profiling.rs
+++ b/compiler/rustc_data_structures/src/profiling.rs
@@ -600,10 +600,7 @@ pub fn print_time_passes_entry(do_it: bool, what: &str, dur: Duration) {
 // Hack up our own formatting for the duration to make it easier for scripts
 // to parse (always use the same number of decimal places and the same unit).
 pub fn duration_to_secs_str(dur: std::time::Duration) -> String {
-    const NANOS_PER_SEC: f64 = 1_000_000_000.0;
-    let secs = dur.as_secs() as f64 + dur.subsec_nanos() as f64 / NANOS_PER_SEC;
-
-    format!("{:.3}", secs)
+    format!("{:.3}", dur.as_secs_f64())
 }
 
 // Memory reporting
diff --git a/compiler/rustc_data_structures/src/sorted_map.rs b/compiler/rustc_data_structures/src/sorted_map.rs
index 856eb73e629..4807380595d 100644
--- a/compiler/rustc_data_structures/src/sorted_map.rs
+++ b/compiler/rustc_data_structures/src/sorted_map.rs
@@ -34,7 +34,7 @@ impl<K: Ord, V> SortedMap<K, V> {
     /// and that there are no duplicates.
     #[inline]
     pub fn from_presorted_elements(elements: Vec<(K, V)>) -> SortedMap<K, V> {
-        debug_assert!(elements.windows(2).all(|w| w[0].0 < w[1].0));
+        debug_assert!(elements.array_windows().all(|[fst, snd]| fst.0 < snd.0));
 
         SortedMap { data: elements }
     }
@@ -159,7 +159,7 @@ impl<K: Ord, V> SortedMap<K, V> {
             return;
         }
 
-        debug_assert!(elements.windows(2).all(|w| w[0].0 < w[1].0));
+        debug_assert!(elements.array_windows().all(|[fst, snd]| fst.0 < snd.0));
 
         let start_index = self.lookup_index_for(&elements[0].0);
 
diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs
index 53d831749ce..d22f3adfb01 100644
--- a/compiler/rustc_data_structures/src/sync.rs
+++ b/compiler/rustc_data_structures/src/sync.rs
@@ -229,7 +229,7 @@ cfg_if! {
         pub use std::cell::RefMut as LockGuard;
         pub use std::cell::RefMut as MappedLockGuard;
 
-        pub use once_cell::unsync::OnceCell;
+        pub use std::lazy::OnceCell;
 
         use std::cell::RefCell as InnerRwLock;
         use std::cell::RefCell as InnerLock;
@@ -314,7 +314,7 @@ cfg_if! {
         pub use parking_lot::MutexGuard as LockGuard;
         pub use parking_lot::MappedMutexGuard as MappedLockGuard;
 
-        pub use once_cell::sync::OnceCell;
+        pub use std::lazy::SyncOnceCell as OnceCell;
 
         pub use std::sync::atomic::{AtomicBool, AtomicUsize, AtomicU32, AtomicU64};
 
diff --git a/compiler/rustc_data_structures/src/tagged_ptr/copy.rs b/compiler/rustc_data_structures/src/tagged_ptr/copy.rs
index d39d146db31..d63bcdb3c2b 100644
--- a/compiler/rustc_data_structures/src/tagged_ptr/copy.rs
+++ b/compiler/rustc_data_structures/src/tagged_ptr/copy.rs
@@ -48,7 +48,7 @@ where
     P: Pointer,
     T: Tag,
 {
-    const TAG_BIT_SHIFT: usize = (8 * std::mem::size_of::<usize>()) - T::BITS;
+    const TAG_BIT_SHIFT: usize = usize::BITS as usize - T::BITS;
     const ASSERTION: () = {
         assert!(T::BITS <= P::BITS);
         // Used for the transmute_copy's below
diff --git a/compiler/rustc_data_structures/src/temp_dir.rs b/compiler/rustc_data_structures/src/temp_dir.rs
index 0d9b3e3ca25..a780d2386a6 100644
--- a/compiler/rustc_data_structures/src/temp_dir.rs
+++ b/compiler/rustc_data_structures/src/temp_dir.rs
@@ -12,7 +12,7 @@ pub struct MaybeTempDir {
 
 impl Drop for MaybeTempDir {
     fn drop(&mut self) {
-        // Safety: We are in the destructor, and no further access will
+        // SAFETY: We are in the destructor, and no further access will
         // occur.
         let dir = unsafe { ManuallyDrop::take(&mut self.dir) };
         if self.keep {
diff --git a/compiler/rustc_data_structures/src/unhash.rs b/compiler/rustc_data_structures/src/unhash.rs
new file mode 100644
index 00000000000..48e21a9dab1
--- /dev/null
+++ b/compiler/rustc_data_structures/src/unhash.rs
@@ -0,0 +1,29 @@
+use std::collections::{HashMap, HashSet};
+use std::hash::{BuildHasherDefault, Hasher};
+
+pub type UnhashMap<K, V> = HashMap<K, V, BuildHasherDefault<Unhasher>>;
+pub type UnhashSet<V> = HashSet<V, BuildHasherDefault<Unhasher>>;
+
+/// This no-op hasher expects only a single `write_u64` call. It's intended for
+/// map keys that already have hash-like quality, like `Fingerprint`.
+#[derive(Default)]
+pub struct Unhasher {
+    value: u64,
+}
+
+impl Hasher for Unhasher {
+    #[inline]
+    fn finish(&self) -> u64 {
+        self.value
+    }
+
+    fn write(&mut self, _bytes: &[u8]) {
+        unimplemented!("use write_u64");
+    }
+
+    #[inline]
+    fn write_u64(&mut self, value: u64) {
+        debug_assert_eq!(0, self.value, "Unhasher doesn't mix values!");
+        self.value = value;
+    }
+}
diff --git a/compiler/rustc_driver/Cargo.toml b/compiler/rustc_driver/Cargo.toml
index 76e8592254c..adfce1008e1 100644
--- a/compiler/rustc_driver/Cargo.toml
+++ b/compiler/rustc_driver/Cargo.toml
@@ -8,9 +8,8 @@ edition = "2018"
 crate-type = ["dylib"]
 
 [dependencies]
-lazy_static = "1.0"
 libc = "0.2"
-tracing = { version = "0.1.18", features = ["release_max_level_info"]  }
+tracing = { version = "0.1.18" }
 tracing-subscriber = { version = "0.2.10", default-features = false, features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"] }
 rustc_middle = { path = "../rustc_middle" }
 rustc_ast_pretty = { path = "../rustc_ast_pretty" }
@@ -39,3 +38,4 @@ winapi = { version = "0.3", features = ["consoleapi", "debugapi", "processenv"]
 
 [features]
 llvm = ['rustc_interface/llvm']
+max_level_info = ['tracing/max_level_info']
diff --git a/compiler/rustc_driver/src/args.rs b/compiler/rustc_driver/src/args.rs
index 5686819c61b..4f2febf04b1 100644
--- a/compiler/rustc_driver/src/args.rs
+++ b/compiler/rustc_driver/src/args.rs
@@ -4,8 +4,7 @@ use std::fs;
 use std::io;
 
 pub fn arg_expand(arg: String) -> Result<Vec<String>, Error> {
-    if arg.starts_with('@') {
-        let path = &arg[1..];
+    if let Some(path) = arg.strip_prefix('@') {
         let file = match fs::read_to_string(path) {
             Ok(file) => file,
             Err(ref err) if err.kind() == io::ErrorKind::InvalidData => {
diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs
index 09f5b22cc63..7118437c0c8 100644
--- a/compiler/rustc_driver/src/lib.rs
+++ b/compiler/rustc_driver/src/lib.rs
@@ -4,14 +4,13 @@
 //!
 //! This API is completely unstable and subject to change.
 
-#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
+#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![feature(nll)]
+#![feature(once_cell)]
 #![recursion_limit = "256"]
 
 #[macro_use]
 extern crate tracing;
-#[macro_use]
-extern crate lazy_static;
 
 pub extern crate rustc_plugin_impl as plugin;
 
@@ -33,7 +32,7 @@ use rustc_save_analysis as save;
 use rustc_save_analysis::DumpHandler;
 use rustc_serialize::json::{self, ToJson};
 use rustc_session::config::nightly_options;
-use rustc_session::config::{ErrorOutputType, Input, OutputType, PrintRequest};
+use rustc_session::config::{ErrorOutputType, Input, OutputType, PrintRequest, TrimmedDefPaths};
 use rustc_session::getopts;
 use rustc_session::lint::{Lint, LintId};
 use rustc_session::{config, DiagnosticOutput, Session};
@@ -48,6 +47,7 @@ use std::env;
 use std::ffi::OsString;
 use std::fs;
 use std::io::{self, Read, Write};
+use std::lazy::SyncLazy;
 use std::mem;
 use std::panic::{self, catch_unwind};
 use std::path::PathBuf;
@@ -126,6 +126,7 @@ impl Callbacks for TimePassesCallbacks {
         // time because it will mess up the --prints output. See #64339.
         self.time_passes = config.opts.prints.is_empty()
             && (config.opts.debugging_opts.time_passes || config.opts.debugging_opts.time);
+        config.opts.trimmed_def_paths = TrimmedDefPaths::GoodPath;
     }
 }
 
@@ -1141,13 +1142,12 @@ pub fn catch_with_exit_code(f: impl FnOnce() -> interface::Result<()>) -> i32 {
     }
 }
 
-lazy_static! {
-    static ref DEFAULT_HOOK: Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static> = {
+static DEFAULT_HOOK: SyncLazy<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static>> =
+    SyncLazy::new(|| {
         let hook = panic::take_hook();
         panic::set_hook(Box::new(|info| report_ice(info, BUG_REPORT_URL)));
         hook
-    };
-}
+    });
 
 /// Prints the ICE message, including backtrace and query stack.
 ///
@@ -1222,7 +1222,7 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
 ///
 /// A custom rustc driver can skip calling this to set up a custom ICE hook.
 pub fn install_ice_hook() {
-    lazy_static::initialize(&DEFAULT_HOOK);
+    SyncLazy::force(&DEFAULT_HOOK);
 }
 
 /// This allows tools to enable rust logging without having to magically match rustc's
diff --git a/compiler/rustc_error_codes/src/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs
index 4e5e77f80c2..a202736ea6c 100644
--- a/compiler/rustc_error_codes/src/error_codes.rs
+++ b/compiler/rustc_error_codes/src/error_codes.rs
@@ -440,6 +440,8 @@ E0751: include_str!("./error_codes/E0751.md"),
 E0752: include_str!("./error_codes/E0752.md"),
 E0753: include_str!("./error_codes/E0753.md"),
 E0754: include_str!("./error_codes/E0754.md"),
+E0755: include_str!("./error_codes/E0755.md"),
+E0756: include_str!("./error_codes/E0756.md"),
 E0758: include_str!("./error_codes/E0758.md"),
 E0759: include_str!("./error_codes/E0759.md"),
 E0760: include_str!("./error_codes/E0760.md"),
@@ -454,6 +456,8 @@ E0768: include_str!("./error_codes/E0768.md"),
 E0769: include_str!("./error_codes/E0769.md"),
 E0770: include_str!("./error_codes/E0770.md"),
 E0771: include_str!("./error_codes/E0771.md"),
+E0773: include_str!("./error_codes/E0773.md"),
+E0774: include_str!("./error_codes/E0774.md"),
 ;
 //  E0006, // merged with E0005
 //  E0008, // cannot bind by-move into a pattern guard
@@ -630,8 +634,6 @@ E0771: include_str!("./error_codes/E0771.md"),
     E0722, // Malformed `#[optimize]` attribute
     E0726, // non-explicit (not `'_`) elided lifetime in unsupported position
 //  E0738, // Removed; errored on `#[track_caller] fn`s in `extern "Rust" { ... }`.
-    E0755, // `#[ffi_pure]` is only allowed on foreign functions
-    E0756, // `#[ffi_const]` is only allowed on foreign functions
     E0757, // `#[ffi_const]` functions cannot be `#[ffi_pure]`
     E0772, // `'static' obligation coming from `impl dyn Trait {}` or `impl Foo for dyn Bar {}`.
 }
diff --git a/compiler/rustc_error_codes/src/error_codes/E0118.md b/compiler/rustc_error_codes/src/error_codes/E0118.md
index 5cb5f506e0a..345ec341c3f 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0118.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0118.md
@@ -1,10 +1,10 @@
-An inherent implementation was defined for something which isn't a struct nor
-an enum.
+An inherent implementation was defined for something which isn't a struct,
+enum, union, or trait object.
 
 Erroneous code example:
 
 ```compile_fail,E0118
-impl (u8, u8) { // error: no base type found for inherent implementation
+impl (u8, u8) { // error: no nominal type found for inherent implementation
     fn get_state(&self) -> String {
         // ...
     }
@@ -41,3 +41,24 @@ impl TypeWrapper {
     }
 }
 ```
+
+Instead of defining an inherent implementation on a reference, you could also
+move the reference inside the implementation:
+
+```compile_fail,E0118
+struct Foo;
+
+impl &Foo { // error: no nominal type found for inherent implementation
+    fn bar(self, other: Self) {}
+}
+```
+
+becomes
+
+```
+struct Foo;
+
+impl Foo {
+    fn bar(&self, other: &Self) {}
+}
+```
diff --git a/compiler/rustc_error_codes/src/error_codes/E0224.md b/compiler/rustc_error_codes/src/error_codes/E0224.md
index fd89c1d5256..628488575b2 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0224.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0224.md
@@ -1,4 +1,4 @@
-A trait object was declaired with no traits.
+A trait object was declared with no traits.
 
 Erroneous code example:
 
@@ -8,7 +8,7 @@ type Foo = dyn 'static +;
 
 Rust does not currently support this.
 
-To solve ensure the the trait object has at least one trait:
+To solve, ensure that the trait object has at least one trait:
 
 ```
 type Foo = dyn 'static + Copy;
diff --git a/compiler/rustc_error_codes/src/error_codes/E0433.md b/compiler/rustc_error_codes/src/error_codes/E0433.md
index f9e333e8ccd..5a64c13c9af 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0433.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0433.md
@@ -1,17 +1,27 @@
-An undeclared type or module was used.
+An undeclared crate, module, or type was used.
 
 Erroneous code example:
 
 ```compile_fail,E0433
 let map = HashMap::new();
-// error: failed to resolve: use of undeclared type or module `HashMap`
+// error: failed to resolve: use of undeclared type `HashMap`
 ```
 
 Please verify you didn't misspell the type/module's name or that you didn't
 forget to import it:
 
-
 ```
 use std::collections::HashMap; // HashMap has been imported.
 let map: HashMap<u32, u32> = HashMap::new(); // So it can be used!
 ```
+
+If you've expected to use a crate name:
+
+```compile_fail
+use ferris_wheel::BigO;
+// error: failed to resolve: use of undeclared crate or module `ferris_wheel`
+```
+
+Make sure the crate has been added as a dependency in `Cargo.toml`.
+
+To use a module from your current crate, add the `crate::` prefix to the path.
diff --git a/compiler/rustc_error_codes/src/error_codes/E0607.md b/compiler/rustc_error_codes/src/error_codes/E0607.md
index ea6e10105b0..0545246929f 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0607.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0607.md
@@ -12,15 +12,15 @@ First: what are thin and fat pointers?
 Thin pointers are "simple" pointers: they are purely a reference to a memory
 address.
 
-Fat pointers are pointers referencing Dynamically Sized Types (also called DST).
-DST don't have a statically known size, therefore they can only exist behind
-some kind of pointers that contain additional information. Slices and trait
-objects are DSTs. In the case of slices, the additional information the fat
-pointer holds is their size.
+Fat pointers are pointers referencing Dynamically Sized Types (also called
+DSTs). DSTs don't have a statically known size, therefore they can only exist
+behind some kind of pointer that contains additional information. For example,
+slices and trait objects are DSTs. In the case of slices, the additional
+information the fat pointer holds is their size.
 
 To fix this error, don't try to cast directly between thin and fat pointers.
 
-For more information about casts, take a look at the Type cast section in
-[The Reference Book][1].
+For more information about type casts, take a look at the section of the
+[The Rust Reference][1] on type cast expressions.
 
 [1]: https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions
diff --git a/compiler/rustc_error_codes/src/error_codes/E0755.md b/compiler/rustc_error_codes/src/error_codes/E0755.md
new file mode 100644
index 00000000000..88b7f484969
--- /dev/null
+++ b/compiler/rustc_error_codes/src/error_codes/E0755.md
@@ -0,0 +1,28 @@
+The `ffi_pure` attribute was used on a non-foreign function.
+
+Erroneous code example:
+
+```compile_fail,E0755
+#![feature(ffi_pure)]
+
+#[ffi_pure] // error!
+pub fn foo() {}
+# fn main() {}
+```
+
+The `ffi_pure` attribute can only be used on foreign functions which do not have
+side effects or infinite loops:
+
+```
+#![feature(ffi_pure)]
+
+extern "C" {
+    #[ffi_pure] // ok!
+    pub fn strlen(s: *const i8) -> isize;
+}
+# fn main() {}
+```
+
+You can find more information about it in the [unstable Rust Book].
+
+[unstable Rust Book]: https://doc.rust-lang.org/unstable-book/language-features/ffi-pure.html
diff --git a/compiler/rustc_error_codes/src/error_codes/E0756.md b/compiler/rustc_error_codes/src/error_codes/E0756.md
new file mode 100644
index 00000000000..ffdc421aab5
--- /dev/null
+++ b/compiler/rustc_error_codes/src/error_codes/E0756.md
@@ -0,0 +1,29 @@
+The `ffi_const` attribute was used on something other than a foreign function
+declaration.
+
+Erroneous code example:
+
+```compile_fail,E0756
+#![feature(ffi_const)]
+
+#[ffi_const] // error!
+pub fn foo() {}
+# fn main() {}
+```
+
+The `ffi_const` attribute can only be used on foreign function declarations
+which have no side effects except for their return value:
+
+```
+#![feature(ffi_const)]
+
+extern "C" {
+    #[ffi_const] // ok!
+    pub fn strlen(s: *const i8) -> i32;
+}
+# fn main() {}
+```
+
+You can get more information about it in the [unstable Rust Book].
+
+[unstable Rust Book]: https://doc.rust-lang.org/nightly/unstable-book/language-features/ffi-const.html
diff --git a/compiler/rustc_error_codes/src/error_codes/E0764.md b/compiler/rustc_error_codes/src/error_codes/E0764.md
index e9061f988ac..0a2e2290e77 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0764.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0764.md
@@ -1,12 +1,4 @@
-Mutable references (`&mut`) can only be used in constant functions, not statics
-or constants. This limitation exists to prevent the creation of constants that
-have a mutable reference in their final value. If you had a constant of `&mut
-i32` type, you could modify the value through that reference, making the
-constant essentially mutable. While there could be a more fine-grained scheme
-in the future that allows mutable references if they are not "leaked" to the
-final value, a more conservative approach was chosen for now. `const fn` do not
-have this problem, as the borrow checker will prevent the `const fn` from
-returning new mutable references.
+A mutable reference was used in a constant.
 
 Erroneous code example:
 
@@ -19,6 +11,18 @@ fn main() {
 }
 ```
 
+Mutable references (`&mut`) can only be used in constant functions, not statics
+or constants. This limitation exists to prevent the creation of constants that
+have a mutable reference in their final value. If you had a constant of
+`&mut i32` type, you could modify the value through that reference, making the
+constant essentially mutable.
+
+While there could be a more fine-grained scheme in the future that allows
+mutable references if they are not "leaked" to the final value, a more
+conservative approach was chosen for now. `const fn` do not have this problem,
+as the borrow checker will prevent the `const fn` from returning new mutable
+references.
+
 Remember: you cannot use a function call inside a constant or static. However,
 you can totally use it in constant functions:
 
diff --git a/compiler/rustc_error_codes/src/error_codes/E0769.md b/compiler/rustc_error_codes/src/error_codes/E0769.md
index d1995be9899..4a3b674b058 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0769.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0769.md
@@ -1,5 +1,5 @@
-A tuple struct or tuple variant was used in a pattern as if it were a
-struct or struct variant.
+A tuple struct or tuple variant was used in a pattern as if it were a struct or
+struct variant.
 
 Erroneous code example:
 
@@ -7,9 +7,13 @@ Erroneous code example:
 enum E {
     A(i32),
 }
+
 let e = E::A(42);
+
 match e {
-    E::A { number } => println!("{}", x),
+    E::A { number } => { // error!
+        println!("{}", number);
+    }
 }
 ```
 
@@ -21,12 +25,14 @@ To fix this error, you can use the tuple pattern:
 # }
 # let e = E::A(42);
 match e {
-    E::A(number) => println!("{}", number),
+    E::A(number) => { // ok!
+        println!("{}", number);
+    }
 }
 ```
 
-Alternatively, you can also use the struct pattern by using the correct
-field names and binding them to new identifiers:
+Alternatively, you can also use the struct pattern by using the correct field
+names and binding them to new identifiers:
 
 ```
 # enum E {
@@ -34,6 +40,8 @@ field names and binding them to new identifiers:
 # }
 # let e = E::A(42);
 match e {
-    E::A { 0: number } => println!("{}", number),
+    E::A { 0: number } => { // ok!
+        println!("{}", number);
+    }
 }
 ```
diff --git a/compiler/rustc_error_codes/src/error_codes/E0773.md b/compiler/rustc_error_codes/src/error_codes/E0773.md
new file mode 100644
index 00000000000..b19a58bf33d
--- /dev/null
+++ b/compiler/rustc_error_codes/src/error_codes/E0773.md
@@ -0,0 +1,38 @@
+A builtin-macro was defined more than once.
+
+Erroneous code example:
+
+```compile_fail,E0773
+#![feature(decl_macro)]
+#![feature(rustc_attrs)]
+
+#[rustc_builtin_macro]
+pub macro test($item:item) {
+    /* compiler built-in */
+}
+
+mod inner {
+    #[rustc_builtin_macro]
+    pub macro test($item:item) {
+        /* compiler built-in */
+    }
+}
+```
+
+To fix the issue, remove the duplicate declaration:
+
+```
+#![feature(decl_macro)]
+#![feature(rustc_attrs)]
+
+#[rustc_builtin_macro]
+pub macro test($item:item) {
+    /* compiler built-in */
+}
+```
+
+In very rare edge cases, this may happen when loading `core` or `std` twice,
+once with `check` metadata and once with `build` metadata.
+For more information, see [#75176].
+
+[#75176]: https://github.com/rust-lang/rust/pull/75176#issuecomment-683234468
diff --git a/compiler/rustc_error_codes/src/error_codes/E0774.md b/compiler/rustc_error_codes/src/error_codes/E0774.md
new file mode 100644
index 00000000000..79793ba9d7d
--- /dev/null
+++ b/compiler/rustc_error_codes/src/error_codes/E0774.md
@@ -0,0 +1,24 @@
+`derive` was applied on something which is not a struct, a union or an enum.
+
+Erroneous code example:
+
+```compile_fail,E0774
+trait Foo {
+    #[derive(Clone)] // error!
+    type Bar;
+}
+```
+
+As said above, the `derive` attribute is only allowed on structs, unions or
+enums:
+
+```
+#[derive(Clone)] // ok!
+struct Bar {
+    field: u32,
+}
+```
+
+You can find more information about `derive` in the [Rust Book].
+
+[Rust Book]: https://doc.rust-lang.org/book/appendix-03-derivable-traits.html
diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs
index 5a654e83aed..98cbf98df92 100644
--- a/compiler/rustc_errors/src/emitter.rs
+++ b/compiler/rustc_errors/src/emitter.rs
@@ -959,15 +959,15 @@ impl EmitterWriter {
                         '_',
                         line_offset + pos,
                         width_offset + depth,
-                        code_offset + annotation.start_col - left,
+                        (code_offset + annotation.start_col).saturating_sub(left),
                         style,
                     );
                 }
                 _ if self.teach => {
                     buffer.set_style_range(
                         line_offset,
-                        code_offset + annotation.start_col - left,
-                        code_offset + annotation.end_col - left,
+                        (code_offset + annotation.start_col).saturating_sub(left),
+                        (code_offset + annotation.end_col).saturating_sub(left),
                         style,
                         annotation.is_primary,
                     );
@@ -1227,18 +1227,14 @@ impl EmitterWriter {
             }
             draw_note_separator(&mut buffer, 0, max_line_num_len + 1);
             if *level != Level::FailureNote {
-                let level_str = level.to_string();
-                if !level_str.is_empty() {
-                    buffer.append(0, &level_str, Style::MainHeaderMsg);
-                    buffer.append(0, ": ", Style::NoStyle);
-                }
+                buffer.append(0, level.to_str(), Style::MainHeaderMsg);
+                buffer.append(0, ": ", Style::NoStyle);
             }
             self.msg_to_buffer(&mut buffer, msg, max_line_num_len, "note", None);
         } else {
-            let level_str = level.to_string();
             // The failure note level itself does not provide any useful diagnostic information
-            if *level != Level::FailureNote && !level_str.is_empty() {
-                buffer.append(0, &level_str, Style::Level(*level));
+            if *level != Level::FailureNote {
+                buffer.append(0, level.to_str(), Style::Level(*level));
             }
             // only render error codes, not lint codes
             if let Some(DiagnosticId::Error(ref code)) = *code {
@@ -1246,7 +1242,7 @@ impl EmitterWriter {
                 buffer.append(0, &code, Style::Level(*level));
                 buffer.append(0, "]", Style::Level(*level));
             }
-            if *level != Level::FailureNote && !level_str.is_empty() {
+            if *level != Level::FailureNote {
                 buffer.append(0, ": ", header_style);
             }
             for &(ref text, _) in msg.iter() {
@@ -1548,11 +1544,9 @@ impl EmitterWriter {
         let mut buffer = StyledBuffer::new();
 
         // Render the suggestion message
-        let level_str = level.to_string();
-        if !level_str.is_empty() {
-            buffer.append(0, &level_str, Style::Level(*level));
-            buffer.append(0, ": ", Style::HeaderMsg);
-        }
+        buffer.append(0, level.to_str(), Style::Level(*level));
+        buffer.append(0, ": ", Style::HeaderMsg);
+
         self.msg_to_buffer(
             &mut buffer,
             &[(suggestion.msg.to_owned(), Style::NoStyle)],
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index d4f0a9d83ef..2e8a4ef327a 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -2,8 +2,9 @@
 //!
 //! This module contains the code for creating and emitting diagnostics.
 
-#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
+#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![feature(crate_visibility_modifier)]
+#![feature(backtrace)]
 #![feature(nll)]
 
 #[macro_use]
@@ -296,9 +297,11 @@ struct HandlerInner {
     /// This is not necessarily the count that's reported to the user once
     /// compilation ends.
     err_count: usize,
+    warn_count: usize,
     deduplicated_err_count: usize,
     emitter: Box<dyn Emitter + sync::Send>,
     delayed_span_bugs: Vec<Diagnostic>,
+    delayed_good_path_bugs: Vec<Diagnostic>,
 
     /// This set contains the `DiagnosticId` of all emitted diagnostics to avoid
     /// emitting the same diagnostic with extended help (`--teach`) twice, which
@@ -361,13 +364,15 @@ impl Drop for HandlerInner {
 
         if !self.has_errors() {
             let bugs = std::mem::replace(&mut self.delayed_span_bugs, Vec::new());
-            let has_bugs = !bugs.is_empty();
-            for bug in bugs {
-                self.emit_diagnostic(&bug);
-            }
-            if has_bugs {
-                panic!("no errors encountered even though `delay_span_bug` issued");
-            }
+            self.flush_delayed(bugs, "no errors encountered even though `delay_span_bug` issued");
+        }
+
+        if !self.has_any_message() {
+            let bugs = std::mem::replace(&mut self.delayed_good_path_bugs, Vec::new());
+            self.flush_delayed(
+                bugs,
+                "no warnings or errors encountered even though `delayed_good_path_bugs` issued",
+            );
         }
     }
 }
@@ -422,10 +427,12 @@ impl Handler {
             inner: Lock::new(HandlerInner {
                 flags,
                 err_count: 0,
+                warn_count: 0,
                 deduplicated_err_count: 0,
                 deduplicated_warn_count: 0,
                 emitter,
                 delayed_span_bugs: Vec::new(),
+                delayed_good_path_bugs: Vec::new(),
                 taught_diagnostics: Default::default(),
                 emitted_diagnostic_codes: Default::default(),
                 emitted_diagnostics: Default::default(),
@@ -448,11 +455,13 @@ impl Handler {
     pub fn reset_err_count(&self) {
         let mut inner = self.inner.borrow_mut();
         inner.err_count = 0;
+        inner.warn_count = 0;
         inner.deduplicated_err_count = 0;
         inner.deduplicated_warn_count = 0;
 
         // actually free the underlying memory (which `clear` would not do)
         inner.delayed_span_bugs = Default::default();
+        inner.delayed_good_path_bugs = Default::default();
         inner.taught_diagnostics = Default::default();
         inner.emitted_diagnostic_codes = Default::default();
         inner.emitted_diagnostics = Default::default();
@@ -629,6 +638,10 @@ impl Handler {
         self.inner.borrow_mut().delay_span_bug(span, msg)
     }
 
+    pub fn delay_good_path_bug(&self, msg: &str) {
+        self.inner.borrow_mut().delay_good_path_bug(msg)
+    }
+
     pub fn span_bug_no_panic(&self, span: impl Into<MultiSpan>, msg: &str) {
         self.emit_diag_at_span(Diagnostic::new(Bug, msg), span);
     }
@@ -768,6 +781,8 @@ impl HandlerInner {
         }
         if diagnostic.is_error() {
             self.bump_err_count();
+        } else {
+            self.bump_warn_count();
         }
     }
 
@@ -859,6 +874,9 @@ impl HandlerInner {
     fn has_errors_or_delayed_span_bugs(&self) -> bool {
         self.has_errors() || !self.delayed_span_bugs.is_empty()
     }
+    fn has_any_message(&self) -> bool {
+        self.err_count() > 0 || self.warn_count > 0
+    }
 
     fn abort_if_errors(&mut self) {
         self.emit_stashed_diagnostics();
@@ -892,6 +910,15 @@ impl HandlerInner {
         self.delay_as_bug(diagnostic)
     }
 
+    fn delay_good_path_bug(&mut self, msg: &str) {
+        let mut diagnostic = Diagnostic::new(Level::Bug, msg);
+        if self.flags.report_delayed_bugs {
+            self.emit_diagnostic(&diagnostic);
+        }
+        diagnostic.note(&format!("delayed at {}", std::backtrace::Backtrace::force_capture()));
+        self.delayed_good_path_bugs.push(diagnostic);
+    }
+
     fn failure(&mut self, msg: &str) {
         self.emit_diagnostic(&Diagnostic::new(FailureNote, msg));
     }
@@ -925,23 +952,35 @@ impl HandlerInner {
         self.delayed_span_bugs.push(diagnostic);
     }
 
+    fn flush_delayed(&mut self, bugs: Vec<Diagnostic>, explanation: &str) {
+        let has_bugs = !bugs.is_empty();
+        for bug in bugs {
+            self.emit_diagnostic(&bug);
+        }
+        if has_bugs {
+            panic!("{}", explanation);
+        }
+    }
+
     fn bump_err_count(&mut self) {
         self.err_count += 1;
         self.panic_if_treat_err_as_bug();
     }
 
+    fn bump_warn_count(&mut self) {
+        self.warn_count += 1;
+    }
+
     fn panic_if_treat_err_as_bug(&self) {
         if self.treat_err_as_bug() {
-            let s = match (self.err_count(), self.flags.treat_err_as_bug.unwrap_or(0)) {
-                (0, _) => return,
-                (1, 1) => "aborting due to `-Z treat-err-as-bug=1`".to_string(),
-                (1, _) => return,
-                (count, as_bug) => format!(
+            match (self.err_count(), self.flags.treat_err_as_bug.unwrap_or(0)) {
+                (1, 1) => panic!("aborting due to `-Z treat-err-as-bug=1`"),
+                (0, _) | (1, _) => {}
+                (count, as_bug) => panic!(
                     "aborting after {} errors due to `-Z treat-err-as-bug={}`",
                     count, as_bug,
                 ),
-            };
-            panic!(s);
+            }
         }
     }
 }
diff --git a/compiler/rustc_errors/src/snippet.rs b/compiler/rustc_errors/src/snippet.rs
index 160bf577799..fae5b94b3a8 100644
--- a/compiler/rustc_errors/src/snippet.rs
+++ b/compiler/rustc_errors/src/snippet.rs
@@ -118,17 +118,15 @@ pub struct Annotation {
 impl Annotation {
     /// Whether this annotation is a vertical line placeholder.
     pub fn is_line(&self) -> bool {
-        if let AnnotationType::MultilineLine(_) = self.annotation_type { true } else { false }
+        matches!(self.annotation_type, AnnotationType::MultilineLine(_))
     }
 
     pub fn is_multiline(&self) -> bool {
-        match self.annotation_type {
+        matches!(self.annotation_type,
             AnnotationType::Multiline(_)
             | AnnotationType::MultilineStart(_)
             | AnnotationType::MultilineLine(_)
-            | AnnotationType::MultilineEnd(_) => true,
-            _ => false,
-        }
+            | AnnotationType::MultilineEnd(_))
     }
 
     pub fn len(&self) -> usize {
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index 4c01cb8159a..926e3dbfc52 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -400,6 +400,7 @@ macro_rules! make_stmts_default {
                 id: ast::DUMMY_NODE_ID,
                 span: e.span,
                 kind: ast::StmtKind::Expr(e),
+                tokens: None
             }]
         })
     };
@@ -607,6 +608,7 @@ impl DummyResult {
             id: ast::DUMMY_NODE_ID,
             kind: if is_error { ast::TyKind::Err } else { ast::TyKind::Tup(Vec::new()) },
             span: sp,
+            tokens: None,
         })
     }
 }
@@ -641,6 +643,7 @@ impl MacResult for DummyResult {
             id: ast::DUMMY_NODE_ID,
             kind: ast::StmtKind::Expr(DummyResult::raw_expr(self.span, self.is_error)),
             span: self.span,
+            tokens: None
         }])
     }
 
diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs
index 9490b62aa17..a5a7ee6c9a3 100644
--- a/compiler/rustc_expand/src/build.rs
+++ b/compiler/rustc_expand/src/build.rs
@@ -3,7 +3,7 @@ use crate::base::ExtCtxt;
 use rustc_ast::attr;
 use rustc_ast::ptr::P;
 use rustc_ast::{self as ast, AttrVec, BlockCheckMode, Expr, PatKind, UnOp};
-use rustc_span::source_map::{respan, Spanned};
+use rustc_span::source_map::Spanned;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 
 use rustc_span::Span;
@@ -46,7 +46,7 @@ impl<'a> ExtCtxt<'a> {
             id: ast::DUMMY_NODE_ID,
             args,
         });
-        ast::Path { span, segments }
+        ast::Path { span, segments, tokens: None }
     }
 
     pub fn ty_mt(&self, ty: P<ast::Ty>, mutbl: ast::Mutability) -> ast::MutTy {
@@ -54,7 +54,7 @@ impl<'a> ExtCtxt<'a> {
     }
 
     pub fn ty(&self, span: Span, kind: ast::TyKind) -> P<ast::Ty> {
-        P(ast::Ty { id: ast::DUMMY_NODE_ID, span, kind })
+        P(ast::Ty { id: ast::DUMMY_NODE_ID, span, kind, tokens: None })
     }
 
     pub fn ty_path(&self, path: ast::Path) -> P<ast::Ty> {
@@ -158,7 +158,12 @@ impl<'a> ExtCtxt<'a> {
     }
 
     pub fn stmt_expr(&self, expr: P<ast::Expr>) -> ast::Stmt {
-        ast::Stmt { id: ast::DUMMY_NODE_ID, span: expr.span, kind: ast::StmtKind::Expr(expr) }
+        ast::Stmt {
+            id: ast::DUMMY_NODE_ID,
+            span: expr.span,
+            kind: ast::StmtKind::Expr(expr),
+            tokens: None,
+        }
     }
 
     pub fn stmt_let(&self, sp: Span, mutbl: bool, ident: Ident, ex: P<ast::Expr>) -> ast::Stmt {
@@ -176,7 +181,12 @@ impl<'a> ExtCtxt<'a> {
             span: sp,
             attrs: AttrVec::new(),
         });
-        ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Local(local), span: sp }
+        ast::Stmt {
+            id: ast::DUMMY_NODE_ID,
+            kind: ast::StmtKind::Local(local),
+            span: sp,
+            tokens: None,
+        }
     }
 
     // Generates `let _: Type;`, which is usually used for type assertions.
@@ -189,11 +199,16 @@ impl<'a> ExtCtxt<'a> {
             span,
             attrs: AttrVec::new(),
         });
-        ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Local(local), span }
+        ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Local(local), span, tokens: None }
     }
 
     pub fn stmt_item(&self, sp: Span, item: P<ast::Item>) -> ast::Stmt {
-        ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Item(item), span: sp }
+        ast::Stmt {
+            id: ast::DUMMY_NODE_ID,
+            kind: ast::StmtKind::Item(item),
+            span: sp,
+            tokens: None,
+        }
     }
 
     pub fn block_expr(&self, expr: P<ast::Expr>) -> P<ast::Block> {
@@ -203,11 +218,18 @@ impl<'a> ExtCtxt<'a> {
                 id: ast::DUMMY_NODE_ID,
                 span: expr.span,
                 kind: ast::StmtKind::Expr(expr),
+                tokens: None,
             }],
         )
     }
     pub fn block(&self, span: Span, stmts: Vec<ast::Stmt>) -> P<ast::Block> {
-        P(ast::Block { stmts, id: ast::DUMMY_NODE_ID, rules: BlockCheckMode::Default, span })
+        P(ast::Block {
+            stmts,
+            id: ast::DUMMY_NODE_ID,
+            rules: BlockCheckMode::Default,
+            span,
+            tokens: None,
+        })
     }
 
     pub fn expr(&self, span: Span, kind: ast::ExprKind) -> P<ast::Expr> {
@@ -578,7 +600,11 @@ impl<'a> ExtCtxt<'a> {
             attrs,
             id: ast::DUMMY_NODE_ID,
             kind,
-            vis: respan(span.shrink_to_lo(), ast::VisibilityKind::Inherited),
+            vis: ast::Visibility {
+                span: span.shrink_to_lo(),
+                kind: ast::VisibilityKind::Inherited,
+                tokens: None,
+            },
             span,
             tokens: None,
         })
@@ -592,7 +618,11 @@ impl<'a> ExtCtxt<'a> {
                 span: ty.span,
                 ty,
                 ident: None,
-                vis: respan(vis_span, ast::VisibilityKind::Inherited),
+                vis: ast::Visibility {
+                    span: vis_span,
+                    kind: ast::VisibilityKind::Inherited,
+                    tokens: None,
+                },
                 attrs: Vec::new(),
                 id: ast::DUMMY_NODE_ID,
                 is_placeholder: false,
@@ -611,7 +641,11 @@ impl<'a> ExtCtxt<'a> {
             disr_expr: None,
             id: ast::DUMMY_NODE_ID,
             ident,
-            vis: respan(vis_span, ast::VisibilityKind::Inherited),
+            vis: ast::Visibility {
+                span: vis_span,
+                kind: ast::VisibilityKind::Inherited,
+                tokens: None,
+            },
             span,
             is_placeholder: false,
         }
diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs
index afd1e606402..dd087ab9150 100644
--- a/compiler/rustc_expand/src/config.rs
+++ b/compiler/rustc_expand/src/config.rs
@@ -399,14 +399,10 @@ impl<'a> StripUnconfigured<'a> {
     }
 
     pub fn configure_foreign_mod(&mut self, foreign_mod: &mut ast::ForeignMod) {
-        let ast::ForeignMod { abi: _, items } = foreign_mod;
+        let ast::ForeignMod { unsafety: _, abi: _, items } = foreign_mod;
         items.flat_map_in_place(|item| self.configure(item));
     }
 
-    pub fn configure_generic_params(&mut self, params: &mut Vec<ast::GenericParam>) {
-        params.flat_map_in_place(|param| self.configure(param));
-    }
-
     fn configure_variant_data(&mut self, vdata: &mut ast::VariantData) {
         match vdata {
             ast::VariantData::Struct(fields, ..) | ast::VariantData::Tuple(fields, _) => {
@@ -496,6 +492,13 @@ impl<'a> MutVisitor for StripUnconfigured<'a> {
         Some(expr)
     }
 
+    fn flat_map_generic_param(
+        &mut self,
+        param: ast::GenericParam,
+    ) -> SmallVec<[ast::GenericParam; 1]> {
+        noop_flat_map_generic_param(configure!(self, param), self)
+    }
+
     fn flat_map_stmt(&mut self, stmt: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> {
         noop_flat_map_stmt(configure!(self, stmt), self)
     }
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index 7a21caf255a..e5cfb866938 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -13,7 +13,7 @@ use rustc_ast::token;
 use rustc_ast::tokenstream::TokenStream;
 use rustc_ast::visit::{self, AssocCtxt, Visitor};
 use rustc_ast::{self as ast, AttrItem, Block, LitKind, NodeId, PatKind, Path};
-use rustc_ast::{ItemKind, MacArgs, MacStmtStyle, StmtKind};
+use rustc_ast::{ItemKind, MacArgs, MacCallStmt, MacStmtStyle, StmtKind, Unsafe};
 use rustc_ast_pretty::pprust;
 use rustc_attr::{self as attr, is_builtin_attr, HasAttrs};
 use rustc_data_structures::map_in_place::MapInPlace;
@@ -26,7 +26,6 @@ use rustc_session::lint::builtin::UNUSED_DOC_COMMENTS;
 use rustc_session::lint::BuiltinLintDiagnostics;
 use rustc_session::parse::{feature_err, ParseSess};
 use rustc_session::Limit;
-use rustc_span::source_map::respan;
 use rustc_span::symbol::{sym, Ident, Symbol};
 use rustc_span::{ExpnId, FileName, Span, DUMMY_SP};
 
@@ -358,7 +357,11 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
             kind: ast::ItemKind::Mod(krate.module),
             ident: Ident::invalid(),
             id: ast::DUMMY_NODE_ID,
-            vis: respan(krate.span.shrink_to_lo(), ast::VisibilityKind::Public),
+            vis: ast::Visibility {
+                span: krate.span.shrink_to_lo(),
+                kind: ast::VisibilityKind::Public,
+                tokens: None,
+            },
             tokens: None,
         })]);
 
@@ -370,11 +373,21 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
             None => {
                 // Resolution failed so we return an empty expansion
                 krate.attrs = vec![];
-                krate.module = ast::Mod { inner: orig_mod_span, items: vec![], inline: true };
+                krate.module = ast::Mod {
+                    inner: orig_mod_span,
+                    unsafety: Unsafe::No,
+                    items: vec![],
+                    inline: true,
+                };
             }
             Some(ast::Item { span, kind, .. }) => {
                 krate.attrs = vec![];
-                krate.module = ast::Mod { inner: orig_mod_span, items: vec![], inline: true };
+                krate.module = ast::Mod {
+                    inner: orig_mod_span,
+                    unsafety: Unsafe::No,
+                    items: vec![],
+                    inline: true,
+                };
                 self.cx.span_err(
                     span,
                     &format!(
@@ -529,9 +542,12 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
     fn error_derive_forbidden_on_non_adt(&self, derives: &[Path], item: &Annotatable) {
         let attr = self.cx.sess.find_by_name(item.attrs(), sym::derive);
         let span = attr.map_or(item.span(), |attr| attr.span);
-        let mut err = self
-            .cx
-            .struct_span_err(span, "`derive` may only be applied to structs, enums and unions");
+        let mut err = rustc_errors::struct_span_err!(
+            self.cx.sess,
+            span,
+            E0774,
+            "`derive` may only be applied to structs, enums and unions",
+        );
         if let Some(ast::Attribute { style: ast::AttrStyle::Inner, .. }) = attr {
             let trait_list = derives.iter().map(|t| pprust::path_to_string(t)).collect::<Vec<_>>();
             let suggestion = format!("#[derive({})]", trait_list.join(", "));
@@ -1363,7 +1379,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
         }
 
         if let StmtKind::MacCall(mac) = stmt.kind {
-            let (mac, style, attrs) = mac.into_inner();
+            let MacCallStmt { mac, style, attrs } = mac.into_inner();
             self.check_attributes(&attrs);
             let mut placeholder =
                 self.collect_bang(mac, stmt.span, AstFragmentKind::Stmts).make_stmts();
@@ -1380,10 +1396,10 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
         }
 
         // The placeholder expander gives ids to statements, so we avoid folding the id here.
-        let ast::Stmt { id, kind, span } = stmt;
+        let ast::Stmt { id, kind, span, tokens } = stmt;
         noop_flat_map_stmt_kind(kind, self)
             .into_iter()
-            .map(|kind| ast::Stmt { id, kind, span })
+            .map(|kind| ast::Stmt { id, kind, span, tokens: tokens.clone() })
             .collect()
     }
 
@@ -1438,8 +1454,15 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
                     push_directory(&self.cx.sess, ident, &item.attrs, dir)
                 } else {
                     // We have an outline `mod foo;` so we need to parse the file.
-                    let (new_mod, dir) =
-                        parse_external_mod(&self.cx.sess, ident, span, dir, &mut attrs, pushed);
+                    let (new_mod, dir) = parse_external_mod(
+                        &self.cx.sess,
+                        ident,
+                        span,
+                        old_mod.unsafety,
+                        dir,
+                        &mut attrs,
+                        pushed,
+                    );
 
                     let krate = ast::Crate {
                         span: new_mod.inner,
@@ -1757,6 +1780,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
                 kind: ast::AttrKind::Normal(AttrItem {
                     path: meta.path,
                     args: meta.kind.mac_args(meta.span),
+                    tokens: None,
                 }),
                 span: at.span,
                 id: at.id,
@@ -1788,6 +1812,7 @@ pub struct ExpansionConfig<'feat> {
     pub should_test: bool, // If false, strip `#[test]` nodes
     pub keep_macs: bool,
     pub span_debug: bool, // If true, use verbose debugging for `proc_macro::Span`
+    pub proc_macro_backtrace: bool, // If true, show backtraces for proc-macro panics
 }
 
 impl<'feat> ExpansionConfig<'feat> {
@@ -1800,6 +1825,7 @@ impl<'feat> ExpansionConfig<'feat> {
             should_test: false,
             keep_macs: false,
             span_debug: false,
+            proc_macro_backtrace: false,
         }
     }
 
diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs
index 7f631cb71af..47247294f5d 100644
--- a/compiler/rustc_expand/src/lib.rs
+++ b/compiler/rustc_expand/src/lib.rs
@@ -1,5 +1,4 @@
 #![feature(bool_to_option)]
-#![feature(cow_is_borrowed)]
 #![feature(crate_visibility_modifier)]
 #![feature(decl_macro)]
 #![feature(or_patterns)]
@@ -39,11 +38,6 @@ mod tests;
 mod parse {
     #[cfg(test)]
     mod tests;
-    #[cfg(test)]
-    mod lexer {
-        #[cfg(test)]
-        mod tests;
-    }
 }
 #[cfg(test)]
 mod tokenstream {
diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs
index b908a12c1fc..0e5c5fe4d44 100644
--- a/compiler/rustc_expand/src/mbe/transcribe.rs
+++ b/compiler/rustc_expand/src/mbe/transcribe.rs
@@ -4,7 +4,7 @@ use crate::mbe::macro_parser::{MatchedNonterminal, MatchedSeq, NamedMatch};
 
 use rustc_ast::mut_visit::{self, MutVisitor};
 use rustc_ast::token::{self, NtTT, Token};
-use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndJoint};
+use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndSpacing};
 use rustc_ast::MacCall;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sync::Lrc;
@@ -111,7 +111,7 @@ pub(super) fn transcribe<'a>(
     //
     // Thus, if we try to pop the `result_stack` and it is empty, we have reached the top-level
     // again, and we are done transcribing.
-    let mut result: Vec<TreeAndJoint> = Vec::new();
+    let mut result: Vec<TreeAndSpacing> = Vec::new();
     let mut result_stack = Vec::new();
     let mut marker = Marker(cx.current_expansion.id, transparency);
 
diff --git a/compiler/rustc_expand/src/module.rs b/compiler/rustc_expand/src/module.rs
index 1e123a2e145..171cb3fa8e6 100644
--- a/compiler/rustc_expand/src/module.rs
+++ b/compiler/rustc_expand/src/module.rs
@@ -1,4 +1,4 @@
-use rustc_ast::{token, Attribute, Mod};
+use rustc_ast::{token, Attribute, Mod, Unsafe};
 use rustc_errors::{struct_span_err, PResult};
 use rustc_parse::new_parser_from_file;
 use rustc_session::parse::ParseSess;
@@ -42,6 +42,7 @@ crate fn parse_external_mod(
     sess: &Session,
     id: Ident,
     span: Span, // The span to blame on errors.
+    unsafety: Unsafe,
     Directory { mut ownership, path }: Directory,
     attrs: &mut Vec<Attribute>,
     pop_mod_stack: &mut bool,
@@ -60,13 +61,16 @@ crate fn parse_external_mod(
         drop(included_mod_stack);
 
         // Actually parse the external file as a module.
-        let mut module =
-            new_parser_from_file(&sess.parse_sess, &mp.path, Some(span)).parse_mod(&token::Eof)?;
+        let mut parser = new_parser_from_file(&sess.parse_sess, &mp.path, Some(span));
+        let mut module = parser.parse_mod(&token::Eof, unsafety)?;
         module.0.inline = false;
         module
     };
     // (1) ...instead, we return a dummy module.
-    let (module, mut new_attrs) = result.map_err(|mut err| err.emit()).unwrap_or_default();
+    let (module, mut new_attrs) = result.map_err(|mut err| err.emit()).unwrap_or_else(|_| {
+        let module = Mod { inner: Span::default(), unsafety, items: Vec::new(), inline: false };
+        (module, Vec::new())
+    });
     attrs.append(&mut new_attrs);
 
     // Extract the directory path for submodules of `module`.
@@ -219,8 +223,7 @@ fn error_cannot_declare_mod_here<'a, T>(
 
 /// Derive a submodule path from the first found `#[path = "path_string"]`.
 /// The provided `dir_path` is joined with the `path_string`.
-// Public for rustfmt usage.
-pub fn submod_path_from_attr(
+pub(super) fn submod_path_from_attr(
     sess: &Session,
     attrs: &[Attribute],
     dir_path: &Path,
diff --git a/compiler/rustc_expand/src/parse/lexer/tests.rs b/compiler/rustc_expand/src/parse/lexer/tests.rs
deleted file mode 100644
index 87184444283..00000000000
--- a/compiler/rustc_expand/src/parse/lexer/tests.rs
+++ /dev/null
@@ -1,252 +0,0 @@
-use rustc_ast::ast::AttrStyle;
-use rustc_ast::token::{self, CommentKind, Token, TokenKind};
-use rustc_data_structures::sync::Lrc;
-use rustc_errors::{emitter::EmitterWriter, Handler};
-use rustc_parse::lexer::StringReader;
-use rustc_session::parse::ParseSess;
-use rustc_span::source_map::{FilePathMapping, SourceMap};
-use rustc_span::symbol::Symbol;
-use rustc_span::with_default_session_globals;
-use rustc_span::{BytePos, Span};
-
-use std::io;
-use std::path::PathBuf;
-
-fn mk_sess(sm: Lrc<SourceMap>) -> ParseSess {
-    let emitter = EmitterWriter::new(
-        Box::new(io::sink()),
-        Some(sm.clone()),
-        false,
-        false,
-        false,
-        None,
-        false,
-    );
-    ParseSess::with_span_handler(Handler::with_emitter(true, None, Box::new(emitter)), sm)
-}
-
-// Creates a string reader for the given string.
-fn setup<'a>(sm: &SourceMap, sess: &'a ParseSess, teststr: String) -> StringReader<'a> {
-    let sf = sm.new_source_file(PathBuf::from(teststr.clone()).into(), teststr);
-    StringReader::new(sess, sf, None)
-}
-
-#[test]
-fn t1() {
-    with_default_session_globals(|| {
-        let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
-        let sh = mk_sess(sm.clone());
-        let mut string_reader = setup(
-            &sm,
-            &sh,
-            "/* my source file */ fn main() { println!(\"zebra\"); }\n".to_string(),
-        );
-        assert_eq!(string_reader.next_token(), token::Comment);
-        assert_eq!(string_reader.next_token(), token::Whitespace);
-        let tok1 = string_reader.next_token();
-        let tok2 = Token::new(mk_ident("fn"), Span::with_root_ctxt(BytePos(21), BytePos(23)));
-        assert_eq!(tok1.kind, tok2.kind);
-        assert_eq!(tok1.span, tok2.span);
-        assert_eq!(string_reader.next_token(), token::Whitespace);
-        // Read another token.
-        let tok3 = string_reader.next_token();
-        assert_eq!(string_reader.pos(), BytePos(28));
-        let tok4 = Token::new(mk_ident("main"), Span::with_root_ctxt(BytePos(24), BytePos(28)));
-        assert_eq!(tok3.kind, tok4.kind);
-        assert_eq!(tok3.span, tok4.span);
-
-        assert_eq!(string_reader.next_token(), token::OpenDelim(token::Paren));
-        assert_eq!(string_reader.pos(), BytePos(29))
-    })
-}
-
-// Checks that the given reader produces the desired stream
-// of tokens (stop checking after exhausting `expected`).
-fn check_tokenization(mut string_reader: StringReader<'_>, expected: Vec<TokenKind>) {
-    for expected_tok in &expected {
-        assert_eq!(&string_reader.next_token(), expected_tok);
-    }
-}
-
-// Makes the identifier by looking up the string in the interner.
-fn mk_ident(id: &str) -> TokenKind {
-    token::Ident(Symbol::intern(id), false)
-}
-
-fn mk_lit(kind: token::LitKind, symbol: &str, suffix: Option<&str>) -> TokenKind {
-    TokenKind::lit(kind, Symbol::intern(symbol), suffix.map(Symbol::intern))
-}
-
-#[test]
-fn doublecolon_parsing() {
-    with_default_session_globals(|| {
-        let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
-        let sh = mk_sess(sm.clone());
-        check_tokenization(
-            setup(&sm, &sh, "a b".to_string()),
-            vec![mk_ident("a"), token::Whitespace, mk_ident("b")],
-        );
-    })
-}
-
-#[test]
-fn doublecolon_parsing_2() {
-    with_default_session_globals(|| {
-        let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
-        let sh = mk_sess(sm.clone());
-        check_tokenization(
-            setup(&sm, &sh, "a::b".to_string()),
-            vec![mk_ident("a"), token::Colon, token::Colon, mk_ident("b")],
-        );
-    })
-}
-
-#[test]
-fn doublecolon_parsing_3() {
-    with_default_session_globals(|| {
-        let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
-        let sh = mk_sess(sm.clone());
-        check_tokenization(
-            setup(&sm, &sh, "a ::b".to_string()),
-            vec![mk_ident("a"), token::Whitespace, token::Colon, token::Colon, mk_ident("b")],
-        );
-    })
-}
-
-#[test]
-fn doublecolon_parsing_4() {
-    with_default_session_globals(|| {
-        let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
-        let sh = mk_sess(sm.clone());
-        check_tokenization(
-            setup(&sm, &sh, "a:: b".to_string()),
-            vec![mk_ident("a"), token::Colon, token::Colon, token::Whitespace, mk_ident("b")],
-        );
-    })
-}
-
-#[test]
-fn character_a() {
-    with_default_session_globals(|| {
-        let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
-        let sh = mk_sess(sm.clone());
-        assert_eq!(setup(&sm, &sh, "'a'".to_string()).next_token(), mk_lit(token::Char, "a", None),);
-    })
-}
-
-#[test]
-fn character_space() {
-    with_default_session_globals(|| {
-        let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
-        let sh = mk_sess(sm.clone());
-        assert_eq!(setup(&sm, &sh, "' '".to_string()).next_token(), mk_lit(token::Char, " ", None),);
-    })
-}
-
-#[test]
-fn character_escaped() {
-    with_default_session_globals(|| {
-        let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
-        let sh = mk_sess(sm.clone());
-        assert_eq!(
-            setup(&sm, &sh, "'\\n'".to_string()).next_token(),
-            mk_lit(token::Char, "\\n", None),
-        );
-    })
-}
-
-#[test]
-fn lifetime_name() {
-    with_default_session_globals(|| {
-        let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
-        let sh = mk_sess(sm.clone());
-        assert_eq!(
-            setup(&sm, &sh, "'abc".to_string()).next_token(),
-            token::Lifetime(Symbol::intern("'abc")),
-        );
-    })
-}
-
-#[test]
-fn raw_string() {
-    with_default_session_globals(|| {
-        let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
-        let sh = mk_sess(sm.clone());
-        assert_eq!(
-            setup(&sm, &sh, "r###\"\"#a\\b\x00c\"\"###".to_string()).next_token(),
-            mk_lit(token::StrRaw(3), "\"#a\\b\x00c\"", None),
-        );
-    })
-}
-
-#[test]
-fn literal_suffixes() {
-    with_default_session_globals(|| {
-        let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
-        let sh = mk_sess(sm.clone());
-        macro_rules! test {
-            ($input: expr, $tok_type: ident, $tok_contents: expr) => {{
-                assert_eq!(
-                    setup(&sm, &sh, format!("{}suffix", $input)).next_token(),
-                    mk_lit(token::$tok_type, $tok_contents, Some("suffix")),
-                );
-                // with a whitespace separator
-                assert_eq!(
-                    setup(&sm, &sh, format!("{} suffix", $input)).next_token(),
-                    mk_lit(token::$tok_type, $tok_contents, None),
-                );
-            }};
-        }
-
-        test!("'a'", Char, "a");
-        test!("b'a'", Byte, "a");
-        test!("\"a\"", Str, "a");
-        test!("b\"a\"", ByteStr, "a");
-        test!("1234", Integer, "1234");
-        test!("0b101", Integer, "0b101");
-        test!("0xABC", Integer, "0xABC");
-        test!("1.0", Float, "1.0");
-        test!("1.0e10", Float, "1.0e10");
-
-        assert_eq!(
-            setup(&sm, &sh, "2us".to_string()).next_token(),
-            mk_lit(token::Integer, "2", Some("us")),
-        );
-        assert_eq!(
-            setup(&sm, &sh, "r###\"raw\"###suffix".to_string()).next_token(),
-            mk_lit(token::StrRaw(3), "raw", Some("suffix")),
-        );
-        assert_eq!(
-            setup(&sm, &sh, "br###\"raw\"###suffix".to_string()).next_token(),
-            mk_lit(token::ByteStrRaw(3), "raw", Some("suffix")),
-        );
-    })
-}
-
-#[test]
-fn nested_block_comments() {
-    with_default_session_globals(|| {
-        let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
-        let sh = mk_sess(sm.clone());
-        let mut lexer = setup(&sm, &sh, "/* /* */ */'a'".to_string());
-        assert_eq!(lexer.next_token(), token::Comment);
-        assert_eq!(lexer.next_token(), mk_lit(token::Char, "a", None));
-    })
-}
-
-#[test]
-fn crlf_comments() {
-    with_default_session_globals(|| {
-        let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
-        let sh = mk_sess(sm.clone());
-        let mut lexer = setup(&sm, &sh, "// test\r\n/// test\r\n".to_string());
-        let comment = lexer.next_token();
-        assert_eq!(comment.kind, token::Comment);
-        assert_eq!((comment.span.lo(), comment.span.hi()), (BytePos(0), BytePos(7)));
-        assert_eq!(lexer.next_token(), token::Whitespace);
-        assert_eq!(
-            lexer.next_token(),
-            token::DocComment(CommentKind::Line, AttrStyle::Outer, Symbol::intern(" test"))
-        );
-    })
-}
diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs
index 29fb4c95ec6..4c9271a58df 100644
--- a/compiler/rustc_expand/src/placeholders.rs
+++ b/compiler/rustc_expand/src/placeholders.rs
@@ -4,7 +4,7 @@ use crate::expand::{AstFragment, AstFragmentKind};
 use rustc_ast as ast;
 use rustc_ast::mut_visit::*;
 use rustc_ast::ptr::P;
-use rustc_span::source_map::{dummy_spanned, DUMMY_SP};
+use rustc_span::source_map::DUMMY_SP;
 use rustc_span::symbol::Ident;
 
 use smallvec::{smallvec, SmallVec};
@@ -18,7 +18,7 @@ pub fn placeholder(
 ) -> AstFragment {
     fn mac_placeholder() -> ast::MacCall {
         ast::MacCall {
-            path: ast::Path { span: DUMMY_SP, segments: Vec::new() },
+            path: ast::Path { span: DUMMY_SP, segments: Vec::new(), tokens: None },
             args: P(ast::MacArgs::Empty),
             prior_type_ascription: None,
         }
@@ -26,7 +26,11 @@ pub fn placeholder(
 
     let ident = Ident::invalid();
     let attrs = Vec::new();
-    let vis = vis.unwrap_or_else(|| dummy_spanned(ast::VisibilityKind::Inherited));
+    let vis = vis.unwrap_or(ast::Visibility {
+        span: DUMMY_SP,
+        kind: ast::VisibilityKind::Inherited,
+        tokens: None,
+    });
     let span = DUMMY_SP;
     let expr_placeholder = || {
         P(ast::Expr {
@@ -37,7 +41,8 @@ pub fn placeholder(
             tokens: None,
         })
     };
-    let ty = || P(ast::Ty { id, kind: ast::TyKind::MacCall(mac_placeholder()), span });
+    let ty =
+        || P(ast::Ty { id, kind: ast::TyKind::MacCall(mac_placeholder()), span, tokens: None });
     let pat =
         || P(ast::Pat { id, kind: ast::PatKind::MacCall(mac_placeholder()), span, tokens: None });
 
@@ -88,12 +93,19 @@ pub fn placeholder(
             kind: ast::PatKind::MacCall(mac_placeholder()),
             tokens: None,
         })),
-        AstFragmentKind::Ty => {
-            AstFragment::Ty(P(ast::Ty { id, span, kind: ast::TyKind::MacCall(mac_placeholder()) }))
-        }
+        AstFragmentKind::Ty => AstFragment::Ty(P(ast::Ty {
+            id,
+            span,
+            kind: ast::TyKind::MacCall(mac_placeholder()),
+            tokens: None,
+        })),
         AstFragmentKind::Stmts => AstFragment::Stmts(smallvec![{
-            let mac = P((mac_placeholder(), ast::MacStmtStyle::Braces, ast::AttrVec::new()));
-            ast::Stmt { id, span, kind: ast::StmtKind::MacCall(mac) }
+            let mac = P(ast::MacCallStmt {
+                mac: mac_placeholder(),
+                style: ast::MacStmtStyle::Braces,
+                attrs: ast::AttrVec::new(),
+            });
+            ast::Stmt { id, span, kind: ast::StmtKind::MacCall(mac), tokens: None }
         }]),
         AstFragmentKind::Arms => AstFragment::Arms(smallvec![ast::Arm {
             attrs: Default::default(),
@@ -293,7 +305,7 @@ impl<'a, 'b> MutVisitor for PlaceholderExpander<'a, 'b> {
 
     fn flat_map_stmt(&mut self, stmt: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> {
         let (style, mut stmts) = match stmt.kind {
-            ast::StmtKind::MacCall(mac) => (mac.1, self.remove(stmt.id).make_stmts()),
+            ast::StmtKind::MacCall(mac) => (mac.style, self.remove(stmt.id).make_stmts()),
             _ => return noop_flat_map_stmt(stmt, self),
         };
 
diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs
index 4e865c20d6f..94b3fcf2850 100644
--- a/compiler/rustc_expand/src/proc_macro.rs
+++ b/compiler/rustc_expand/src/proc_macro.rs
@@ -24,7 +24,7 @@ impl base::ProcMacro for BangProcMacro {
         input: TokenStream,
     ) -> Result<TokenStream, ErrorReported> {
         let server = proc_macro_server::Rustc::new(ecx);
-        self.client.run(&EXEC_STRATEGY, server, input).map_err(|e| {
+        self.client.run(&EXEC_STRATEGY, server, input, ecx.ecfg.proc_macro_backtrace).map_err(|e| {
             let mut err = ecx.struct_span_err(span, "proc macro panicked");
             if let Some(s) = e.as_str() {
                 err.help(&format!("message: {}", s));
@@ -48,14 +48,16 @@ impl base::AttrProcMacro for AttrProcMacro {
         annotated: TokenStream,
     ) -> Result<TokenStream, ErrorReported> {
         let server = proc_macro_server::Rustc::new(ecx);
-        self.client.run(&EXEC_STRATEGY, server, annotation, annotated).map_err(|e| {
-            let mut err = ecx.struct_span_err(span, "custom attribute panicked");
-            if let Some(s) = e.as_str() {
-                err.help(&format!("message: {}", s));
-            }
-            err.emit();
-            ErrorReported
-        })
+        self.client
+            .run(&EXEC_STRATEGY, server, annotation, annotated, ecx.ecfg.proc_macro_backtrace)
+            .map_err(|e| {
+                let mut err = ecx.struct_span_err(span, "custom attribute panicked");
+                if let Some(s) = e.as_str() {
+                    err.help(&format!("message: {}", s));
+                }
+                err.emit();
+                ErrorReported
+            })
     }
 }
 
@@ -111,17 +113,18 @@ impl MultiItemModifier for ProcMacroDerive {
         };
 
         let server = proc_macro_server::Rustc::new(ecx);
-        let stream = match self.client.run(&EXEC_STRATEGY, server, input) {
-            Ok(stream) => stream,
-            Err(e) => {
-                let mut err = ecx.struct_span_err(span, "proc-macro derive panicked");
-                if let Some(s) = e.as_str() {
-                    err.help(&format!("message: {}", s));
+        let stream =
+            match self.client.run(&EXEC_STRATEGY, server, input, ecx.ecfg.proc_macro_backtrace) {
+                Ok(stream) => stream,
+                Err(e) => {
+                    let mut err = ecx.struct_span_err(span, "proc-macro derive panicked");
+                    if let Some(s) = e.as_str() {
+                        err.help(&format!("message: {}", s));
+                    }
+                    err.emit();
+                    return ExpandResult::Ready(vec![]);
                 }
-                err.emit();
-                return ExpandResult::Ready(vec![]);
-            }
-        };
+            };
 
         let error_count_before = ecx.sess.parse_sess.span_diagnostic.err_count();
         let mut parser =
diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs
index 409784812f5..4cfb188783b 100644
--- a/compiler/rustc_expand/src/proc_macro_server.rs
+++ b/compiler/rustc_expand/src/proc_macro_server.rs
@@ -2,7 +2,7 @@ use crate::base::ExtCtxt;
 
 use rustc_ast as ast;
 use rustc_ast::token;
-use rustc_ast::tokenstream::{self, DelimSpan, IsJoint::*, TokenStream, TreeAndJoint};
+use rustc_ast::tokenstream::{self, DelimSpan, Spacing::*, TokenStream, TreeAndSpacing};
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::Diagnostic;
@@ -47,15 +47,15 @@ impl ToInternal<token::DelimToken> for Delimiter {
     }
 }
 
-impl FromInternal<(TreeAndJoint, &'_ ParseSess, &'_ mut Vec<Self>)>
+impl FromInternal<(TreeAndSpacing, &'_ ParseSess, &'_ mut Vec<Self>)>
     for TokenTree<Group, Punct, Ident, Literal>
 {
     fn from_internal(
-        ((tree, is_joint), sess, stack): (TreeAndJoint, &ParseSess, &mut Vec<Self>),
+        ((tree, spacing), sess, stack): (TreeAndSpacing, &ParseSess, &mut Vec<Self>),
     ) -> Self {
         use rustc_ast::token::*;
 
-        let joint = is_joint == Joint;
+        let joint = spacing == Joint;
         let Token { kind, span } = match tree {
             tokenstream::TokenTree::Delimited(span, delim, tts) => {
                 let delimiter = Delimiter::from_internal(delim);
@@ -189,7 +189,7 @@ impl FromInternal<(TreeAndJoint, &'_ ParseSess, &'_ mut Vec<Self>)>
             }
 
             OpenDelim(..) | CloseDelim(..) => unreachable!(),
-            Whitespace | Comment | Shebang(..) | Unknown(..) | Eof => unreachable!(),
+            Eof => unreachable!(),
         }
     }
 }
@@ -261,7 +261,7 @@ impl ToInternal<TokenStream> for TokenTree<Group, Punct, Ident, Literal> {
         };
 
         let tree = tokenstream::TokenTree::token(kind, span);
-        TokenStream::new(vec![(tree, if joint { Joint } else { NonJoint })])
+        TokenStream::new(vec![(tree, if joint { Joint } else { Alone })])
     }
 }
 
@@ -444,7 +444,7 @@ impl server::TokenStreamIter for Rustc<'_> {
     ) -> Option<TokenTree<Self::Group, Self::Punct, Self::Ident, Self::Literal>> {
         loop {
             let tree = iter.stack.pop().or_else(|| {
-                let next = iter.cursor.next_with_joint()?;
+                let next = iter.cursor.next_with_spacing()?;
                 Some(TokenTree::from_internal((next, self.sess, &mut iter.stack)))
             })?;
             // A hack used to pass AST fragments to attribute and derive macros
@@ -584,12 +584,12 @@ impl server::Literal for Rustc<'_> {
 
         let start = match start {
             Bound::Included(lo) => lo,
-            Bound::Excluded(lo) => lo + 1,
+            Bound::Excluded(lo) => lo.checked_add(1)?,
             Bound::Unbounded => 0,
         };
 
         let end = match end {
-            Bound::Included(hi) => hi + 1,
+            Bound::Included(hi) => hi.checked_add(1)?,
             Bound::Excluded(hi) => hi,
             Bound::Unbounded => length,
         };
diff --git a/compiler/rustc_feature/Cargo.toml b/compiler/rustc_feature/Cargo.toml
index 3f8047e931e..7a06bce13c8 100644
--- a/compiler/rustc_feature/Cargo.toml
+++ b/compiler/rustc_feature/Cargo.toml
@@ -9,5 +9,4 @@ doctest = false
 
 [dependencies]
 rustc_data_structures = { path = "../rustc_data_structures" }
-lazy_static = "1.0.0"
 rustc_span = { path = "../rustc_span" }
diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs
index d16f023c00a..0477f6f149b 100644
--- a/compiler/rustc_feature/src/accepted.rs
+++ b/compiler/rustc_feature/src/accepted.rs
@@ -268,6 +268,8 @@ declare_features! (
     /// Allows `#[track_caller]` to be used which provides
     /// accurate caller location reporting during panic (RFC 2091).
     (accepted, track_caller, "1.46.0", Some(47809), None),
+    /// Allows `#[doc(alias = "...")]`.
+    (accepted, doc_alias, "1.48.0", Some(50146), None),
 
     // -------------------------------------------------------------------------
     // feature-group-end: accepted features
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index e858980738d..6452bda293e 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -229,7 +229,6 @@ declare_features! (
     (active, powerpc_target_feature, "1.27.0", Some(44839), None),
     (active, mips_target_feature, "1.27.0", Some(44839), None),
     (active, avx512_target_feature, "1.27.0", Some(44839), None),
-    (active, mmx_target_feature, "1.27.0", Some(44839), None),
     (active, sse4a_target_feature, "1.27.0", Some(44839), None),
     (active, tbm_target_feature, "1.27.0", Some(44839), None),
     (active, wasm_target_feature, "1.30.0", Some(44839), None),
@@ -404,9 +403,6 @@ declare_features! (
     /// Allows dereferencing raw pointers during const eval.
     (active, const_raw_ptr_deref, "1.27.0", Some(51911), None),
 
-    /// Allows `#[doc(alias = "...")]`.
-    (active, doc_alias, "1.27.0", Some(50146), None),
-
     /// Allows inconsistent bounds in where clauses.
     (active, trivial_bounds, "1.28.0", Some(48214), None),
 
@@ -585,6 +581,9 @@ declare_features! (
     /// Allows `if let` guard in match arms.
     (active, if_let_guard, "1.47.0", Some(51114), None),
 
+    /// Allows non trivial generic constants which have to be manually propageted upwards.
+    (active, const_evaluatable_checked, "1.48.0", Some(76560), None),
+
     // -------------------------------------------------------------------------
     // feature-group-end: actual feature gates
     // -------------------------------------------------------------------------
@@ -600,8 +599,14 @@ pub const INCOMPLETE_FEATURES: &[Symbol] = &[
     sym::const_generics,
     sym::let_chains,
     sym::raw_dylib,
+    sym::const_evaluatable_checked,
     sym::const_trait_impl,
     sym::const_trait_bound_opt_out,
     sym::lazy_normalization_consts,
     sym::specialization,
 ];
+
+/// Some features are not allowed to be used together at the same time, if
+/// the two are present, produce an error.
+pub const INCOMPATIBLE_FEATURES: &[(Symbol, Symbol)] =
+    &[(sym::const_generics, sym::min_const_generics)];
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index 879f06f89a7..8b7fd59cd87 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -5,10 +5,11 @@ use AttributeType::*;
 
 use crate::{Features, Stability};
 
-use lazy_static::lazy_static;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_span::symbol::{sym, Symbol};
 
+use std::lazy::SyncLazy;
+
 type GateFn = fn(&Features) -> bool;
 
 macro_rules! cfg_fn {
@@ -25,6 +26,11 @@ const GATED_CFGS: &[GatedCfg] = &[
     (sym::target_thread_local, sym::cfg_target_thread_local, cfg_fn!(cfg_target_thread_local)),
     (sym::target_has_atomic, sym::cfg_target_has_atomic, cfg_fn!(cfg_target_has_atomic)),
     (sym::target_has_atomic_load_store, sym::cfg_target_has_atomic, cfg_fn!(cfg_target_has_atomic)),
+    (
+        sym::target_has_atomic_equal_alignment,
+        sym::cfg_target_has_atomic,
+        cfg_fn!(cfg_target_has_atomic),
+    ),
     (sym::sanitize, sym::cfg_sanitize, cfg_fn!(cfg_sanitize)),
     (sym::version, sym::cfg_version, cfg_fn!(cfg_version)),
 ];
@@ -589,8 +595,8 @@ pub fn is_builtin_attr_name(name: Symbol) -> bool {
     BUILTIN_ATTRIBUTE_MAP.get(&name).is_some()
 }
 
-lazy_static! {
-    pub static ref BUILTIN_ATTRIBUTE_MAP: FxHashMap<Symbol, &'static BuiltinAttribute> = {
+pub static BUILTIN_ATTRIBUTE_MAP: SyncLazy<FxHashMap<Symbol, &'static BuiltinAttribute>> =
+    SyncLazy::new(|| {
         let mut map = FxHashMap::default();
         for attr in BUILTIN_ATTRIBUTES.iter() {
             if map.insert(attr.0, attr).is_some() {
@@ -598,5 +604,4 @@ lazy_static! {
             }
         }
         map
-    };
-}
+    });
diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs
index f8bf0315d0c..15564a59658 100644
--- a/compiler/rustc_feature/src/lib.rs
+++ b/compiler/rustc_feature/src/lib.rs
@@ -11,6 +11,8 @@
 //! even if it is stabilized or removed, *do not remove it*. Instead, move the
 //! symbol to the `accepted` or `removed` modules respectively.
 
+#![feature(once_cell)]
+
 mod accepted;
 mod active;
 mod builtin_attrs;
@@ -129,7 +131,7 @@ pub fn find_feature_issue(feature: Symbol, issue: GateIssue) -> Option<NonZeroU3
 }
 
 pub use accepted::ACCEPTED_FEATURES;
-pub use active::{Features, ACTIVE_FEATURES, INCOMPLETE_FEATURES};
+pub use active::{Features, ACTIVE_FEATURES, INCOMPATIBLE_FEATURES, INCOMPLETE_FEATURES};
 pub use builtin_attrs::{
     deprecated_attributes, find_gated_cfg, is_builtin_attr_name, AttributeGate, AttributeTemplate,
     AttributeType, BuiltinAttribute, GatedCfg, BUILTIN_ATTRIBUTES, BUILTIN_ATTRIBUTE_MAP,
diff --git a/compiler/rustc_graphviz/src/lib.rs b/compiler/rustc_graphviz/src/lib.rs
index 4339092b63e..76e33bed97f 100644
--- a/compiler/rustc_graphviz/src/lib.rs
+++ b/compiler/rustc_graphviz/src/lib.rs
@@ -272,7 +272,7 @@
 //! * [DOT language](http://www.graphviz.org/doc/info/lang.html)
 
 #![doc(
-    html_root_url = "https://doc.rust-lang.org/nightly/",
+    html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/",
     test(attr(allow(unused_variables), deny(warnings)))
 )]
 #![feature(nll)]
@@ -591,14 +591,15 @@ pub trait GraphWalk<'a> {
     fn target(&'a self, edge: &Self::Edge) -> Self::Node;
 }
 
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+#[derive(Clone, PartialEq, Eq, Debug)]
 pub enum RenderOption {
     NoEdgeLabels,
     NoNodeLabels,
     NoEdgeStyles,
     NoNodeStyles,
 
-    Monospace,
+    Fontname(String),
+    DarkTheme,
 }
 
 /// Returns vec holding all the default render options.
@@ -630,10 +631,26 @@ where
     writeln!(w, "digraph {} {{", g.graph_id().as_slice())?;
 
     // Global graph properties
-    if options.contains(&RenderOption::Monospace) {
-        writeln!(w, r#"    graph[fontname="monospace"];"#)?;
-        writeln!(w, r#"    node[fontname="monospace"];"#)?;
-        writeln!(w, r#"    edge[fontname="monospace"];"#)?;
+    let mut graph_attrs = Vec::new();
+    let mut content_attrs = Vec::new();
+    let font;
+    if let Some(fontname) = options.iter().find_map(|option| {
+        if let RenderOption::Fontname(fontname) = option { Some(fontname) } else { None }
+    }) {
+        font = format!(r#"fontname="{}""#, fontname);
+        graph_attrs.push(&font[..]);
+        content_attrs.push(&font[..]);
+    }
+    if options.contains(&RenderOption::DarkTheme) {
+        graph_attrs.push(r#"bgcolor="black""#);
+        content_attrs.push(r#"color="white""#);
+        content_attrs.push(r#"fontcolor="white""#);
+    }
+    if !(graph_attrs.is_empty() && content_attrs.is_empty()) {
+        writeln!(w, r#"    graph[{}];"#, graph_attrs.join(" "))?;
+        let content_attrs_str = content_attrs.join(" ");
+        writeln!(w, r#"    node[{}];"#, content_attrs_str)?;
+        writeln!(w, r#"    edge[{}];"#, content_attrs_str)?;
     }
 
     for n in g.nodes().iter() {
diff --git a/compiler/rustc_hir/Cargo.toml b/compiler/rustc_hir/Cargo.toml
index ed295ff0058..b24c208c76a 100644
--- a/compiler/rustc_hir/Cargo.toml
+++ b/compiler/rustc_hir/Cargo.toml
@@ -15,6 +15,5 @@ rustc_index = { path = "../rustc_index" }
 rustc_span = { path = "../rustc_span" }
 rustc_serialize = { path = "../rustc_serialize" }
 rustc_ast = { path = "../rustc_ast" }
-lazy_static = "1"
 tracing = "0.1"
 smallvec = { version = "1.0", features = ["union", "may_dangle"] }
diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs
index 0d61dc037c6..96fde48d96c 100644
--- a/compiler/rustc_hir/src/def.rs
+++ b/compiler/rustc_hir/src/def.rs
@@ -6,6 +6,7 @@ use rustc_ast::NodeId;
 use rustc_macros::HashStable_Generic;
 use rustc_span::hygiene::MacroKind;
 
+use std::array::IntoIter;
 use std::fmt::Debug;
 
 /// Encodes if a `DefKind::Ctor` is the constructor of an enum variant or a struct.
@@ -198,7 +199,16 @@ pub enum Res<Id = hir::HirId> {
 
     // Type namespace
     PrimTy(hir::PrimTy),
-    SelfTy(Option<DefId> /* trait */, Option<DefId> /* impl */),
+    /// `Self`, with both an optional trait and impl `DefId`.
+    ///
+    /// HACK(min_const_generics): impl self types also have an optional requirement to not mention
+    /// any generic parameters to allow the following with `min_const_generics`:
+    /// ```rust
+    /// impl Foo { fn test() -> [u8; std::mem::size_of::<Self>()] {} }
+    /// ```
+    ///
+    /// FIXME(lazy_normalization_consts): Remove this bodge once this feature is stable.
+    SelfTy(Option<DefId> /* trait */, Option<(DefId, bool)> /* impl */),
     ToolMod, // e.g., `rustfmt` in `#[rustfmt::skip]`
 
     // Value namespace
@@ -291,6 +301,14 @@ impl<T> PerNS<T> {
     pub fn map<U, F: FnMut(T) -> U>(self, mut f: F) -> PerNS<U> {
         PerNS { value_ns: f(self.value_ns), type_ns: f(self.type_ns), macro_ns: f(self.macro_ns) }
     }
+
+    pub fn into_iter(self) -> IntoIter<T, 3> {
+        IntoIter::new([self.value_ns, self.type_ns, self.macro_ns])
+    }
+
+    pub fn iter(&self) -> IntoIter<&T, 3> {
+        IntoIter::new([&self.value_ns, &self.type_ns, &self.macro_ns])
+    }
 }
 
 impl<T> ::std::ops::Index<Namespace> for PerNS<T> {
diff --git a/compiler/rustc_hir/src/definitions.rs b/compiler/rustc_hir/src/definitions.rs
index 45735ead256..45befc7b115 100644
--- a/compiler/rustc_hir/src/definitions.rs
+++ b/compiler/rustc_hir/src/definitions.rs
@@ -313,6 +313,7 @@ impl Definitions {
     }
 
     #[inline]
+    #[track_caller]
     pub fn local_def_id_to_hir_id(&self, id: LocalDefId) -> hir::HirId {
         self.def_id_to_hir_id[id].unwrap()
     }
diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs
index acf6847c014..5e4c03bec83 100644
--- a/compiler/rustc_hir/src/lang_items.rs
+++ b/compiler/rustc_hir/src/lang_items.rs
@@ -17,7 +17,7 @@ use rustc_macros::HashStable_Generic;
 use rustc_span::symbol::{kw, sym, Symbol};
 use rustc_span::Span;
 
-use lazy_static::lazy_static;
+use std::lazy::SyncLazy;
 
 pub enum LangItemGroup {
     Op,
@@ -117,14 +117,12 @@ macro_rules! language_item_table {
             )*
         }
 
-        lazy_static! {
-            /// A mapping from the name of the lang item to its order and the form it must be of.
-            pub static ref ITEM_REFS: FxHashMap<Symbol, (usize, Target)> = {
-                let mut item_refs = FxHashMap::default();
-                $( item_refs.insert($name, (LangItem::$variant as usize, $target)); )*
-                item_refs
-            };
-        }
+        /// A mapping from the name of the lang item to its order and the form it must be of.
+        pub static ITEM_REFS: SyncLazy<FxHashMap<Symbol, (usize, Target)>> = SyncLazy::new(|| {
+            let mut item_refs = FxHashMap::default();
+            $( item_refs.insert($name, (LangItem::$variant as usize, $target)); )*
+            item_refs
+        });
 
 // End of the macro
     }
diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs
index 19ea1de5683..9d931b3a9e1 100644
--- a/compiler/rustc_hir/src/lib.rs
+++ b/compiler/rustc_hir/src/lib.rs
@@ -2,10 +2,12 @@
 //!
 //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/hir.html
 
+#![feature(array_value_iter)]
 #![feature(crate_visibility_modifier)]
 #![feature(const_fn)] // For the unsizing cast on `&[]`
 #![feature(const_panic)]
 #![feature(in_band_lifetimes)]
+#![feature(once_cell)]
 #![feature(or_patterns)]
 #![recursion_limit = "256"]
 
diff --git a/compiler/rustc_hir/src/weak_lang_items.rs b/compiler/rustc_hir/src/weak_lang_items.rs
index 129eec7d29e..52f28bf8f4c 100644
--- a/compiler/rustc_hir/src/weak_lang_items.rs
+++ b/compiler/rustc_hir/src/weak_lang_items.rs
@@ -7,18 +7,16 @@ use rustc_ast as ast;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_span::symbol::{sym, Symbol};
 
-use lazy_static::lazy_static;
+use std::lazy::SyncLazy;
 
 macro_rules! weak_lang_items {
     ($($name:ident, $item:ident, $sym:ident;)*) => (
 
-lazy_static! {
-    pub static ref WEAK_ITEMS_REFS: FxHashMap<Symbol, LangItem> = {
-        let mut map = FxHashMap::default();
-        $(map.insert(sym::$name, LangItem::$item);)*
-        map
-    };
-}
+pub static WEAK_ITEMS_REFS: SyncLazy<FxHashMap<Symbol, LangItem>> = SyncLazy::new(|| {
+    let mut map = FxHashMap::default();
+    $(map.insert(sym::$name, LangItem::$item);)*
+    map
+});
 
 /// The `check_name` argument avoids the need for `librustc_hir` to depend on
 /// `librustc_session`.
diff --git a/compiler/rustc_incremental/src/lib.rs b/compiler/rustc_incremental/src/lib.rs
index ad189138054..a80c4be3e93 100644
--- a/compiler/rustc_incremental/src/lib.rs
+++ b/compiler/rustc_incremental/src/lib.rs
@@ -1,6 +1,6 @@
 //! Support for serializing the dep-graph and reloading it.
 
-#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
+#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![feature(in_band_lifetimes)]
 #![feature(nll)]
 #![recursion_limit = "256"]
diff --git a/compiler/rustc_index/Cargo.toml b/compiler/rustc_index/Cargo.toml
index 6ac7c06ee83..6e1471df195 100644
--- a/compiler/rustc_index/Cargo.toml
+++ b/compiler/rustc_index/Cargo.toml
@@ -8,6 +8,6 @@ edition = "2018"
 doctest = false
 
 [dependencies]
-arrayvec = "0.5.1"
+arrayvec = { version = "0.5.1", default-features = false }
 rustc_serialize = { path = "../rustc_serialize" }
 rustc_macros = { path = "../rustc_macros" }
diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs
index c43d1a6830d..8e00e54650d 100644
--- a/compiler/rustc_index/src/bit_set.rs
+++ b/compiler/rustc_index/src/bit_set.rs
@@ -28,13 +28,20 @@ pub const WORD_BITS: usize = WORD_BYTES * 8;
 /// will panic if the bitsets have differing domain sizes.
 ///
 /// [`GrowableBitSet`]: struct.GrowableBitSet.html
-#[derive(Clone, Eq, PartialEq, Decodable, Encodable)]
-pub struct BitSet<T: Idx> {
+#[derive(Eq, PartialEq, Decodable, Encodable)]
+pub struct BitSet<T> {
     domain_size: usize,
     words: Vec<Word>,
     marker: PhantomData<T>,
 }
 
+impl<T> BitSet<T> {
+    /// Gets the domain size.
+    pub fn domain_size(&self) -> usize {
+        self.domain_size
+    }
+}
+
 impl<T: Idx> BitSet<T> {
     /// Creates a new, empty bitset with a given `domain_size`.
     #[inline]
@@ -52,11 +59,6 @@ impl<T: Idx> BitSet<T> {
         result
     }
 
-    /// Gets the domain size.
-    pub fn domain_size(&self) -> usize {
-        self.domain_size
-    }
-
     /// Clear all elements.
     #[inline]
     pub fn clear(&mut self) {
@@ -75,12 +77,6 @@ impl<T: Idx> BitSet<T> {
         }
     }
 
-    /// Efficiently overwrite `self` with `other`.
-    pub fn overwrite(&mut self, other: &BitSet<T>) {
-        assert!(self.domain_size == other.domain_size);
-        self.words.clone_from_slice(&other.words);
-    }
-
     /// Count the number of set bits in the set.
     pub fn count(&self) -> usize {
         self.words.iter().map(|e| e.count_ones() as usize).sum()
@@ -243,6 +239,21 @@ impl<T: Idx> SubtractFromBitSet<T> for BitSet<T> {
     }
 }
 
+impl<T> Clone for BitSet<T> {
+    fn clone(&self) -> Self {
+        BitSet { domain_size: self.domain_size, words: self.words.clone(), marker: PhantomData }
+    }
+
+    fn clone_from(&mut self, from: &Self) {
+        if self.domain_size != from.domain_size {
+            self.words.resize(from.domain_size, 0);
+            self.domain_size = from.domain_size;
+        }
+
+        self.words.copy_from_slice(&from.words);
+    }
+}
+
 impl<T: Idx> fmt::Debug for BitSet<T> {
     fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result {
         w.debug_list().entries(self.iter()).finish()
@@ -363,7 +374,7 @@ const SPARSE_MAX: usize = 8;
 ///
 /// This type is used by `HybridBitSet`; do not use directly.
 #[derive(Clone, Debug)]
-pub struct SparseBitSet<T: Idx> {
+pub struct SparseBitSet<T> {
     domain_size: usize,
     elems: ArrayVec<[T; SPARSE_MAX]>,
 }
@@ -464,18 +475,27 @@ impl<T: Idx> SubtractFromBitSet<T> for SparseBitSet<T> {
 /// All operations that involve an element will panic if the element is equal
 /// to or greater than the domain size. All operations that involve two bitsets
 /// will panic if the bitsets have differing domain sizes.
-#[derive(Clone, Debug)]
-pub enum HybridBitSet<T: Idx> {
+#[derive(Clone)]
+pub enum HybridBitSet<T> {
     Sparse(SparseBitSet<T>),
     Dense(BitSet<T>),
 }
 
+impl<T: Idx> fmt::Debug for HybridBitSet<T> {
+    fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            Self::Sparse(b) => b.fmt(w),
+            Self::Dense(b) => b.fmt(w),
+        }
+    }
+}
+
 impl<T: Idx> HybridBitSet<T> {
     pub fn new_empty(domain_size: usize) -> Self {
         HybridBitSet::Sparse(SparseBitSet::new_empty(domain_size))
     }
 
-    fn domain_size(&self) -> usize {
+    pub fn domain_size(&self) -> usize {
         match self {
             HybridBitSet::Sparse(sparse) => sparse.domain_size,
             HybridBitSet::Dense(dense) => dense.domain_size,
diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
index ea32a1ae5a5..871fc4fafe2 100644
--- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
+++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
@@ -340,7 +340,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> {
     }
 
     fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
-        match t.kind {
+        match *t.kind() {
             ty::Infer(ty::TyVar(vid)) => {
                 debug!("canonical: type var found with vid {:?}", vid);
                 match self.infcx.unwrap().probe_ty_var(vid) {
@@ -418,7 +418,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> {
             | ty::Foreign(..)
             | ty::Param(..)
             | ty::Opaque(..) => {
-                if t.flags.intersects(self.needs_canonical_flags) {
+                if t.flags().intersects(self.needs_canonical_flags) {
                     t.super_fold_with(self)
                 } else {
                     t
diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs
index 0dbebac7e36..93e19521893 100644
--- a/compiler/rustc_infer/src/infer/canonical/query_response.rs
+++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs
@@ -422,7 +422,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
             match result_value.unpack() {
                 GenericArgKind::Type(result_value) => {
                     // e.g., here `result_value` might be `?0` in the example above...
-                    if let ty::Bound(debruijn, b) = result_value.kind {
+                    if let ty::Bound(debruijn, b) = *result_value.kind() {
                         // ...in which case we would set `canonical_vars[0]` to `Some(?U)`.
 
                         // We only allow a `ty::INNERMOST` index in substitutions.
diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs
index 3a54a647530..a540face4f2 100644
--- a/compiler/rustc_infer/src/infer/combine.rs
+++ b/compiler/rustc_infer/src/infer/combine.rs
@@ -35,6 +35,7 @@ use super::{InferCtxt, MiscVariable, TypeTrace};
 use crate::traits::{Obligation, PredicateObligations};
 
 use rustc_ast as ast;
+use rustc_data_structures::mini_map::MiniMap;
 use rustc_hir::def_id::DefId;
 use rustc_middle::traits::ObligationCause;
 use rustc_middle::ty::error::TypeError;
@@ -42,7 +43,7 @@ use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
 use rustc_middle::ty::subst::SubstsRef;
 use rustc_middle::ty::{self, InferConst, ToPredicate, Ty, TyCtxt, TypeFoldable};
 use rustc_middle::ty::{IntType, UintType};
-use rustc_span::DUMMY_SP;
+use rustc_span::{Span, DUMMY_SP};
 
 #[derive(Clone)]
 pub struct CombineFields<'infcx, 'tcx> {
@@ -72,7 +73,7 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> {
     {
         let a_is_expected = relation.a_is_expected();
 
-        match (&a.kind, &b.kind) {
+        match (a.kind(), b.kind()) {
             // Relate integral variables to other types
             (&ty::Infer(ty::IntVar(a_id)), &ty::Infer(ty::IntVar(b_id))) => {
                 self.inner
@@ -159,11 +160,11 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> {
             }
 
             (ty::ConstKind::Infer(InferConst::Var(vid)), _) => {
-                return self.unify_const_variable(a_is_expected, vid, b);
+                return self.unify_const_variable(relation.param_env(), vid, b, a_is_expected);
             }
 
             (_, ty::ConstKind::Infer(InferConst::Var(vid))) => {
-                return self.unify_const_variable(!a_is_expected, vid, a);
+                return self.unify_const_variable(relation.param_env(), vid, a, !a_is_expected);
             }
             (ty::ConstKind::Unevaluated(..), _) if self.tcx.lazy_normalization() => {
                 // FIXME(#59490): Need to remove the leak check to accommodate
@@ -187,17 +188,66 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> {
         ty::relate::super_relate_consts(relation, a, b)
     }
 
-    pub fn unify_const_variable(
+    /// Unifies the const variable `target_vid` with the given constant.
+    ///
+    /// This also tests if the given const `ct` contains an inference variable which was previously
+    /// unioned with `target_vid`. If this is the case, inferring `target_vid` to `ct`
+    /// would result in an infinite type as we continously replace an inference variable
+    /// in `ct` with `ct` itself.
+    ///
+    /// This is especially important as unevaluated consts use their parents generics.
+    /// They therefore often contain unused substs, making these errors far more likely.
+    ///
+    /// A good example of this is the following:
+    ///
+    /// ```rust
+    /// #![feature(const_generics)]
+    ///
+    /// fn bind<const N: usize>(value: [u8; N]) -> [u8; 3 + 4] {
+    ///     todo!()
+    /// }
+    ///
+    /// fn main() {
+    ///     let mut arr = Default::default();
+    ///     arr = bind(arr);
+    /// }
+    /// ```
+    ///
+    /// Here `3 + 4` ends up as `ConstKind::Unevaluated` which uses the generics
+    /// of `fn bind` (meaning that its substs contain `N`).
+    ///
+    /// `bind(arr)` now infers that the type of `arr` must be `[u8; N]`.
+    /// The assignment `arr = bind(arr)` now tries to equate `N` with `3 + 4`.
+    ///
+    /// As `3 + 4` contains `N` in its substs, this must not succeed.
+    ///
+    /// See `src/test/ui/const-generics/occurs-check/` for more examples where this is relevant.
+    fn unify_const_variable(
         &self,
+        param_env: ty::ParamEnv<'tcx>,
+        target_vid: ty::ConstVid<'tcx>,
+        ct: &'tcx ty::Const<'tcx>,
         vid_is_expected: bool,
-        vid: ty::ConstVid<'tcx>,
-        value: &'tcx ty::Const<'tcx>,
     ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
+        let (for_universe, span) = {
+            let mut inner = self.inner.borrow_mut();
+            let variable_table = &mut inner.const_unification_table();
+            let var_value = variable_table.probe_value(target_vid);
+            match var_value.val {
+                ConstVariableValue::Known { value } => {
+                    bug!("instantiating {:?} which has a known value {:?}", target_vid, value)
+                }
+                ConstVariableValue::Unknown { universe } => (universe, var_value.origin.span),
+            }
+        };
+        let value = ConstInferUnifier { infcx: self, span, param_env, for_universe, target_vid }
+            .relate(ct, ct)?;
+
         self.inner
             .borrow_mut()
             .const_unification_table()
             .unify_var_value(
-                vid,
+                target_vid,
                 ConstVarValue {
                     origin: ConstVariableOrigin {
                         kind: ConstVariableOriginKind::ConstInference,
@@ -206,8 +256,8 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> {
                     val: ConstVariableValue::Known { value },
                 },
             )
-            .map_err(|e| const_unification_error(vid_is_expected, e))?;
-        Ok(value)
+            .map(|()| value)
+            .map_err(|e| const_unification_error(vid_is_expected, e))
     }
 
     fn unify_integral_variable(
@@ -362,7 +412,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
 
         let for_universe = match self.infcx.inner.borrow_mut().type_variables().probe(for_vid) {
             v @ TypeVariableValue::Known { .. } => {
-                panic!("instantiating {:?} which has a known value {:?}", for_vid, v,)
+                bug!("instantiating {:?} which has a known value {:?}", for_vid, v,)
             }
             TypeVariableValue::Unknown { universe } => universe,
         };
@@ -379,6 +429,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
             needs_wf: false,
             root_ty: ty,
             param_env: self.param_env,
+            cache: MiniMap::new(),
         };
 
         let ty = match generalize.relate(ty, ty) {
@@ -438,6 +489,8 @@ struct Generalizer<'cx, 'tcx> {
     root_ty: Ty<'tcx>,
 
     param_env: ty::ParamEnv<'tcx>,
+
+    cache: MiniMap<Ty<'tcx>, RelateResult<'tcx, Ty<'tcx>>>,
 }
 
 /// Result from a generalization operation. This includes
@@ -535,13 +588,16 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
     fn tys(&mut self, t: Ty<'tcx>, t2: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
         assert_eq!(t, t2); // we are abusing TypeRelation here; both LHS and RHS ought to be ==
 
+        if let Some(result) = self.cache.get(&t) {
+            return result.clone();
+        }
         debug!("generalize: t={:?}", t);
 
         // Check to see whether the type we are generalizing references
         // any other type variable related to `vid` via
         // subtyping. This is basically our "occurs check", preventing
         // us from creating infinitely sized types.
-        match t.kind {
+        let result = match *t.kind() {
             ty::Infer(ty::TyVar(vid)) => {
                 let vid = self.infcx.inner.borrow_mut().type_variables().root_var(vid);
                 let sub_vid = self.infcx.inner.borrow_mut().type_variables().sub_root_var(vid);
@@ -598,7 +654,10 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
                 Ok(t)
             }
             _ => relate::super_relate_tys(self, t, t),
-        }
+        };
+
+        self.cache.insert(t, result.clone());
+        return result;
     }
 
     fn regions(
@@ -671,7 +730,6 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
                     }
                 }
             }
-            ty::ConstKind::Unevaluated(..) if self.tcx().lazy_normalization() => Ok(c),
             _ => relate::super_relate_consts(self, c, c),
         }
     }
@@ -721,3 +779,175 @@ fn float_unification_error<'tcx>(
     let (ty::FloatVarValue(a), ty::FloatVarValue(b)) = v;
     TypeError::FloatMismatch(ty::relate::expected_found_bool(a_is_expected, a, b))
 }
+
+struct ConstInferUnifier<'cx, 'tcx> {
+    infcx: &'cx InferCtxt<'cx, 'tcx>,
+
+    span: Span,
+
+    param_env: ty::ParamEnv<'tcx>,
+
+    for_universe: ty::UniverseIndex,
+
+    /// The vid of the const variable that is in the process of being
+    /// instantiated; if we find this within the const we are folding,
+    /// that means we would have created a cyclic const.
+    target_vid: ty::ConstVid<'tcx>,
+}
+
+// We use `TypeRelation` here to propagate `RelateResult` upwards.
+//
+// Both inputs are expected to be the same.
+impl TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> {
+    fn tcx(&self) -> TyCtxt<'tcx> {
+        self.infcx.tcx
+    }
+
+    fn param_env(&self) -> ty::ParamEnv<'tcx> {
+        self.param_env
+    }
+
+    fn tag(&self) -> &'static str {
+        "ConstInferUnifier"
+    }
+
+    fn a_is_expected(&self) -> bool {
+        true
+    }
+
+    fn relate_with_variance<T: Relate<'tcx>>(
+        &mut self,
+        _variance: ty::Variance,
+        a: T,
+        b: T,
+    ) -> RelateResult<'tcx, T> {
+        // We don't care about variance here.
+        self.relate(a, b)
+    }
+
+    fn binders<T>(
+        &mut self,
+        a: ty::Binder<T>,
+        b: ty::Binder<T>,
+    ) -> RelateResult<'tcx, ty::Binder<T>>
+    where
+        T: Relate<'tcx>,
+    {
+        Ok(ty::Binder::bind(self.relate(a.skip_binder(), b.skip_binder())?))
+    }
+
+    fn tys(&mut self, t: Ty<'tcx>, _t: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
+        debug_assert_eq!(t, _t);
+        debug!("ConstInferUnifier: t={:?}", t);
+
+        match t.kind() {
+            &ty::Infer(ty::TyVar(vid)) => {
+                let vid = self.infcx.inner.borrow_mut().type_variables().root_var(vid);
+                let probe = self.infcx.inner.borrow_mut().type_variables().probe(vid);
+                match probe {
+                    TypeVariableValue::Known { value: u } => {
+                        debug!("ConstOccursChecker: known value {:?}", u);
+                        self.tys(u, u)
+                    }
+                    TypeVariableValue::Unknown { universe } => {
+                        if self.for_universe.can_name(universe) {
+                            return Ok(t);
+                        }
+
+                        let origin =
+                            *self.infcx.inner.borrow_mut().type_variables().var_origin(vid);
+                        let new_var_id = self.infcx.inner.borrow_mut().type_variables().new_var(
+                            self.for_universe,
+                            false,
+                            origin,
+                        );
+                        let u = self.tcx().mk_ty_var(new_var_id);
+                        debug!(
+                            "ConstInferUnifier: replacing original vid={:?} with new={:?}",
+                            vid, u
+                        );
+                        Ok(u)
+                    }
+                }
+            }
+            _ => relate::super_relate_tys(self, t, t),
+        }
+    }
+
+    fn regions(
+        &mut self,
+        r: ty::Region<'tcx>,
+        _r: ty::Region<'tcx>,
+    ) -> RelateResult<'tcx, ty::Region<'tcx>> {
+        debug_assert_eq!(r, _r);
+        debug!("ConstInferUnifier: r={:?}", r);
+
+        match r {
+            // Never make variables for regions bound within the type itself,
+            // nor for erased regions.
+            ty::ReLateBound(..) | ty::ReErased => {
+                return Ok(r);
+            }
+
+            ty::RePlaceholder(..)
+            | ty::ReVar(..)
+            | ty::ReEmpty(_)
+            | ty::ReStatic
+            | ty::ReEarlyBound(..)
+            | ty::ReFree(..) => {
+                // see common code below
+            }
+        }
+
+        let r_universe = self.infcx.universe_of_region(r);
+        if self.for_universe.can_name(r_universe) {
+            return Ok(r);
+        } else {
+            // FIXME: This is non-ideal because we don't give a
+            // very descriptive origin for this region variable.
+            Ok(self.infcx.next_region_var_in_universe(MiscVariable(self.span), self.for_universe))
+        }
+    }
+
+    fn consts(
+        &mut self,
+        c: &'tcx ty::Const<'tcx>,
+        _c: &'tcx ty::Const<'tcx>,
+    ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
+        debug_assert_eq!(c, _c);
+        debug!("ConstInferUnifier: c={:?}", c);
+
+        match c.val {
+            ty::ConstKind::Infer(InferConst::Var(vid)) => {
+                let mut inner = self.infcx.inner.borrow_mut();
+                let variable_table = &mut inner.const_unification_table();
+
+                // Check if the current unification would end up
+                // unifying `target_vid` with a const which contains
+                // an inference variable which is unioned with `target_vid`.
+                //
+                // Not doing so can easily result in stack overflows.
+                if variable_table.unioned(self.target_vid, vid) {
+                    return Err(TypeError::CyclicConst(c));
+                }
+
+                let var_value = variable_table.probe_value(vid);
+                match var_value.val {
+                    ConstVariableValue::Known { value: u } => self.consts(u, u),
+                    ConstVariableValue::Unknown { universe } => {
+                        if self.for_universe.can_name(universe) {
+                            Ok(c)
+                        } else {
+                            let new_var_id = variable_table.new_key(ConstVarValue {
+                                origin: var_value.origin,
+                                val: ConstVariableValue::Unknown { universe: self.for_universe },
+                            });
+                            Ok(self.tcx().mk_const_var(new_var_id, c.ty))
+                        }
+                    }
+                }
+            }
+            _ => relate::super_relate_consts(self, c, c),
+        }
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/equate.rs b/compiler/rustc_infer/src/infer/equate.rs
index 7de752d1de0..7c388b5503e 100644
--- a/compiler/rustc_infer/src/infer/equate.rs
+++ b/compiler/rustc_infer/src/infer/equate.rs
@@ -77,7 +77,7 @@ impl TypeRelation<'tcx> for Equate<'combine, 'infcx, 'tcx> {
 
         debug!("{}.tys: replacements ({:?}, {:?})", self.tag(), a, b);
 
-        match (&a.kind, &b.kind) {
+        match (a.kind(), b.kind()) {
             (&ty::Infer(TyVar(a_id)), &ty::Infer(TyVar(b_id))) => {
                 infcx.inner.borrow_mut().type_variables().equate(a_id, b_id);
             }
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index d72ed72e3a8..bcfcee23d13 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -70,7 +70,7 @@ use rustc_middle::ty::{
     subst::{Subst, SubstsRef},
     Region, Ty, TyCtxt, TypeFoldable,
 };
-use rustc_span::{DesugaringKind, Pos, Span};
+use rustc_span::{BytePos, DesugaringKind, Pos, Span};
 use rustc_target::spec::abi;
 use std::{cmp, fmt};
 
@@ -570,7 +570,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 // if they are both "path types", there's a chance of ambiguity
                 // due to different versions of the same crate
                 if let (&ty::Adt(exp_adt, _), &ty::Adt(found_adt, _)) =
-                    (&exp_found.expected.kind, &exp_found.found.kind)
+                    (exp_found.expected.kind(), exp_found.found.kind())
                 {
                     report_path_match(err, exp_adt.did, found_adt.did);
                 }
@@ -617,11 +617,20 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 ref prior_arms,
                 last_ty,
                 scrut_hir_id,
+                opt_suggest_box_span,
+                arm_span,
                 ..
             }) => match source {
                 hir::MatchSource::IfLetDesugar { .. } => {
                     let msg = "`if let` arms have incompatible types";
                     err.span_label(cause.span, msg);
+                    if let Some(ret_sp) = opt_suggest_box_span {
+                        self.suggest_boxing_for_return_impl_trait(
+                            err,
+                            ret_sp,
+                            prior_arms.iter().chain(std::iter::once(&arm_span)).map(|s| *s),
+                        );
+                    }
                 }
                 hir::MatchSource::TryDesugar => {
                     if let Some(ty::error::ExpectedFound { expected, .. }) = exp_found {
@@ -675,9 +684,23 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                             Applicability::MachineApplicable,
                         );
                     }
+                    if let Some(ret_sp) = opt_suggest_box_span {
+                        // Get return type span and point to it.
+                        self.suggest_boxing_for_return_impl_trait(
+                            err,
+                            ret_sp,
+                            prior_arms.iter().chain(std::iter::once(&arm_span)).map(|s| *s),
+                        );
+                    }
                 }
             },
-            ObligationCauseCode::IfExpression(box IfExpressionCause { then, outer, semicolon }) => {
+            ObligationCauseCode::IfExpression(box IfExpressionCause {
+                then,
+                else_sp,
+                outer,
+                semicolon,
+                opt_suggest_box_span,
+            }) => {
                 err.span_label(then, "expected because of this");
                 if let Some(sp) = outer {
                     err.span_label(sp, "`if` and `else` have incompatible types");
@@ -690,11 +713,48 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                         Applicability::MachineApplicable,
                     );
                 }
+                if let Some(ret_sp) = opt_suggest_box_span {
+                    self.suggest_boxing_for_return_impl_trait(
+                        err,
+                        ret_sp,
+                        vec![then, else_sp].into_iter(),
+                    );
+                }
             }
             _ => (),
         }
     }
 
+    fn suggest_boxing_for_return_impl_trait(
+        &self,
+        err: &mut DiagnosticBuilder<'tcx>,
+        return_sp: Span,
+        arm_spans: impl Iterator<Item = Span>,
+    ) {
+        err.multipart_suggestion(
+            "you could change the return type to be a boxed trait object",
+            vec![
+                (return_sp.with_hi(return_sp.lo() + BytePos(4)), "Box<dyn".to_string()),
+                (return_sp.shrink_to_hi(), ">".to_string()),
+            ],
+            Applicability::MaybeIncorrect,
+        );
+        let sugg = arm_spans
+            .flat_map(|sp| {
+                vec![
+                    (sp.shrink_to_lo(), "Box::new(".to_string()),
+                    (sp.shrink_to_hi(), ")".to_string()),
+                ]
+                .into_iter()
+            })
+            .collect::<Vec<_>>();
+        err.multipart_suggestion(
+            "if you change the return type to expect trait objects, box the returned expressions",
+            sugg,
+            Applicability::MaybeIncorrect,
+        );
+    }
+
     /// Given that `other_ty` is the same as a type argument for `name` in `sub`, populate `value`
     /// highlighting `name` and every type argument that isn't at `pos` (which is `other_ty`), and
     /// populate `other_value` with `other_ty`.
@@ -796,7 +856,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 self.highlight_outer(&mut t1_out, &mut t2_out, path, sub, i, &other_ty);
                 return Some(());
             }
-            if let &ty::Adt(def, _) = &ta.kind {
+            if let &ty::Adt(def, _) = ta.kind() {
                 let path_ = self.tcx.def_path_str(def.did);
                 if path_ == other_path {
                     self.highlight_outer(&mut t1_out, &mut t2_out, path, sub, i, &other_ty);
@@ -977,11 +1037,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     /// Compares two given types, eliding parts that are the same between them and highlighting
     /// relevant differences, and return two representation of those types for highlighted printing.
     fn cmp(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) -> (DiagnosticStyledString, DiagnosticStyledString) {
-        debug!("cmp(t1={}, t1.kind={:?}, t2={}, t2.kind={:?})", t1, t1.kind, t2, t2.kind);
+        debug!("cmp(t1={}, t1.kind={:?}, t2={}, t2.kind={:?})", t1, t1.kind(), t2, t2.kind());
 
         // helper functions
         fn equals<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
-            match (&a.kind, &b.kind) {
+            match (a.kind(), b.kind()) {
                 (a, b) if *a == *b => true,
                 (&ty::Int(_), &ty::Infer(ty::InferTy::IntVar(_)))
                 | (
@@ -1014,7 +1074,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         }
 
         // process starts here
-        match (&t1.kind, &t2.kind) {
+        match (t1.kind(), t2.kind()) {
             (&ty::Adt(def1, sub1), &ty::Adt(def2, sub2)) => {
                 let sub_no_defaults_1 = self.strip_generic_default_params(def1.did, sub1);
                 let sub_no_defaults_2 = self.strip_generic_default_params(def2.did, sub2);
@@ -1476,7 +1536,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
             };
             match (&terr, expected == found) {
                 (TypeError::Sorts(values), extra) => {
-                    let sort_string = |ty: Ty<'tcx>| match (extra, &ty.kind) {
+                    let sort_string = |ty: Ty<'tcx>| match (extra, ty.kind()) {
                         (true, ty::Opaque(def_id, _)) => format!(
                             " (opaque type at {})",
                             self.tcx
@@ -1563,7 +1623,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
             exp_span, exp_found.expected, exp_found.found
         );
 
-        if let ty::Opaque(def_id, _) = exp_found.expected.kind {
+        if let ty::Opaque(def_id, _) = *exp_found.expected.kind() {
             let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
             // Future::Output
             let item_def_id = self
@@ -1616,9 +1676,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         diag: &mut DiagnosticBuilder<'tcx>,
     ) {
         if let (ty::Adt(exp_def, exp_substs), ty::Ref(_, found_ty, _)) =
-            (&exp_found.expected.kind, &exp_found.found.kind)
+            (exp_found.expected.kind(), exp_found.found.kind())
         {
-            if let ty::Adt(found_def, found_substs) = found_ty.kind {
+            if let ty::Adt(found_def, found_substs) = *found_ty.kind() {
                 let path_str = format!("{:?}", exp_def);
                 if exp_def == &found_def {
                     let opt_msg = "you can convert from `&Option<T>` to `Option<&T>` using \
@@ -1637,9 +1697,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                     {
                         let mut show_suggestion = true;
                         for (exp_ty, found_ty) in exp_substs.types().zip(found_substs.types()) {
-                            match exp_ty.kind {
+                            match *exp_ty.kind() {
                                 ty::Ref(_, exp_ty, _) => {
-                                    match (&exp_ty.kind, &found_ty.kind) {
+                                    match (exp_ty.kind(), found_ty.kind()) {
                                         (_, ty::Param(_))
                                         | (_, ty::Infer(_))
                                         | (ty::Param(_), _)
@@ -1989,7 +2049,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 );
                 if let Some(infer::RelateParamBound(_, t)) = origin {
                     let t = self.resolve_vars_if_possible(&t);
-                    match t.kind {
+                    match t.kind() {
                         // We've got:
                         // fn get_later<G, T>(g: G, dest: &mut T) -> impl FnOnce() + '_
                         // suggest:
@@ -2093,7 +2153,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 _ => String::new(),
             };
             if !s.is_empty() {
-                s.push_str(" ");
+                s.push(' ');
             }
             s
         };
@@ -2231,7 +2291,7 @@ impl TyCategory {
     }
 
     pub fn from_ty(ty: Ty<'_>) -> Option<(Self, DefId)> {
-        match ty.kind {
+        match *ty.kind() {
             ty::Closure(def_id, _) => Some((Self::Closure, def_id)),
             ty::Opaque(def_id, _) => Some((Self::Opaque, def_id)),
             ty::Generator(def_id, ..) => Some((Self::Generator, 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 bf087dfacfa..f87406c2ce4 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
@@ -6,9 +6,10 @@ use rustc_hir::def::{DefKind, Namespace};
 use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
 use rustc_hir::{Body, Expr, ExprKind, FnRetTy, HirId, Local, Pat};
 use rustc_middle::hir::map::Map;
+use rustc_middle::infer::unify_key::ConstVariableOriginKind;
 use rustc_middle::ty::print::Print;
 use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
-use rustc_middle::ty::{self, DefIdTree, Ty};
+use rustc_middle::ty::{self, DefIdTree, InferConst, Ty};
 use rustc_span::source_map::DesugaringKind;
 use rustc_span::symbol::kw;
 use rustc_span::Span;
@@ -53,7 +54,7 @@ impl<'a, 'tcx> FindHirNodeVisitor<'a, 'tcx> {
                     inner == self.target
                         || match (inner.unpack(), self.target.unpack()) {
                             (GenericArgKind::Type(inner_ty), GenericArgKind::Type(target_ty)) => {
-                                match (&inner_ty.kind, &target_ty.kind) {
+                                match (inner_ty.kind(), target_ty.kind()) {
                                     (
                                         &ty::Infer(ty::TyVar(a_vid)),
                                         &ty::Infer(ty::TyVar(b_vid)),
@@ -222,7 +223,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         ty: Ty<'tcx>,
         highlight: Option<ty::print::RegionHighlightMode>,
     ) -> (String, Option<Span>, Cow<'static, str>, Option<String>, Option<&'static str>) {
-        if let ty::Infer(ty::TyVar(ty_vid)) = ty.kind {
+        if let ty::Infer(ty::TyVar(ty_vid)) = *ty.kind() {
             let mut inner = self.inner.borrow_mut();
             let ty_vars = &inner.type_variables();
             let var_origin = ty_vars.var_origin(ty_vid);
@@ -288,7 +289,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 None
             };
             printer.name_resolver = Some(Box::new(&getter));
-            let _ = if let ty::FnDef(..) = ty.kind {
+            let _ = if let ty::FnDef(..) = ty.kind() {
                 // We don't want the regular output for `fn`s because it includes its path in
                 // invalid pseudo-syntax, we want the `fn`-pointer output instead.
                 ty.fn_sig(self.tcx).print(printer)
@@ -336,7 +337,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
 
         let ty_msg = match (local_visitor.found_node_ty, local_visitor.found_exact_method_call) {
             (_, Some(_)) => String::new(),
-            (Some(ty::TyS { kind: ty::Closure(_, substs), .. }), _) => {
+            (Some(ty), _) if ty.is_closure() => {
+                let substs =
+                    if let ty::Closure(_, substs) = *ty.kind() { substs } else { unreachable!() };
                 let fn_sig = substs.as_closure().sig();
                 let args = closure_args(&fn_sig);
                 let ret = fn_sig.output().skip_binder().to_string();
@@ -370,7 +373,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         );
 
         let suffix = match local_visitor.found_node_ty {
-            Some(ty::TyS { kind: ty::Closure(_, substs), .. }) => {
+            Some(ty) if ty.is_closure() => {
+                let substs =
+                    if let ty::Closure(_, substs) = *ty.kind() { substs } else { unreachable!() };
                 let fn_sig = substs.as_closure().sig();
                 let ret = fn_sig.output().skip_binder().to_string();
 
@@ -565,14 +570,26 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
             local_visitor.visit_expr(expr);
         }
 
+        let mut param_name = None;
+        let span = if let ty::ConstKind::Infer(InferConst::Var(vid)) = ct.val {
+            let origin = self.inner.borrow_mut().const_unification_table().probe_value(vid).origin;
+            if let ConstVariableOriginKind::ConstParameterDefinition(param) = origin.kind {
+                param_name = Some(param);
+            }
+            origin.span
+        } else {
+            local_visitor.target_span
+        };
+
         let error_code = error_code.into();
-        let mut err = self.tcx.sess.struct_span_err_with_code(
-            local_visitor.target_span,
-            "type annotations needed",
-            error_code,
-        );
+        let mut err =
+            self.tcx.sess.struct_span_err_with_code(span, "type annotations needed", error_code);
 
-        err.note("unable to infer the value of a const parameter");
+        if let Some(param_name) = param_name {
+            err.note(&format!("cannot infer the value of the const parameter `{}`", param_name));
+        } else {
+            err.note("unable to infer the value of a const parameter");
+        }
 
         err
     }
@@ -611,11 +628,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                     let sig = self.tcx.fn_sig(did);
                     let bound_output = sig.output();
                     let output = bound_output.skip_binder();
-                    err.span_label(e.span, &format!("this method call resolves to `{:?}`", output));
-                    let kind = &output.kind;
+                    err.span_label(e.span, &format!("this method call resolves to `{}`", output));
+                    let kind = output.kind();
                     if let ty::Projection(proj) = kind {
                         if let Some(span) = self.tcx.hir().span_if_local(proj.item_def_id) {
-                            err.span_label(span, &format!("`{:?}` defined here", output));
+                            err.span_label(span, &format!("`{}` defined here", output));
                         }
                     }
                 }
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs
index 89142edb2dc..e3c613b1d6a 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs
@@ -85,7 +85,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
 
                 debug!("try_report_named_anon_conflict: ret ty {:?}", ty);
                 if sub == &ty::ReStatic
-                    && v.0.into_iter().find(|t| t.span.desugaring_kind().is_none()).is_some()
+                    && v.0.into_iter().any(|t| t.span.desugaring_kind().is_none())
                 {
                     // If the failure is due to a `'static` requirement coming from a `dyn` or
                     // `impl` Trait that *isn't* caused by `async fn` desugaring, handle this case
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 7493b8b0a9f..441cfeea20a 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
@@ -465,7 +465,7 @@ struct TraitObjectVisitor(Vec<DefId>);
 
 impl TypeVisitor<'_> for TraitObjectVisitor {
     fn visit_ty(&mut self, t: Ty<'_>) -> bool {
-        match t.kind {
+        match t.kind() {
             ty::Dynamic(preds, RegionKind::ReStatic) => {
                 if let Some(def_id) = preds.principal_def_id() {
                     self.0.push(def_id);
@@ -488,18 +488,16 @@ impl<'tcx> Visitor<'tcx> for HirTraitObjectVisitor {
     }
 
     fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) {
-        match t.kind {
-            TyKind::TraitObject(
-                poly_trait_refs,
-                Lifetime { name: LifetimeName::ImplicitObjectLifetimeDefault, .. },
-            ) => {
-                for ptr in poly_trait_refs {
-                    if Some(self.1) == ptr.trait_ref.trait_def_id() {
-                        self.0.push(ptr.span);
-                    }
+        if let TyKind::TraitObject(
+            poly_trait_refs,
+            Lifetime { name: LifetimeName::ImplicitObjectLifetimeDefault, .. },
+        ) = t.kind
+        {
+            for ptr in poly_trait_refs {
+                if Some(self.1) == ptr.trait_ref.trait_def_id() {
+                    self.0.push(ptr.span);
                 }
             }
-            _ => {}
         }
         walk_ty(self, t);
     }
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 788eabf296d..c061f485c1c 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
@@ -58,8 +58,8 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
             .tcx()
             .sess
             .struct_span_err(sp, "`impl` item signature doesn't match `trait` item signature");
-        err.span_label(sp, &format!("found `{:?}`", found));
-        err.span_label(trait_sp, &format!("expected `{:?}`", expected));
+        err.span_label(sp, &format!("found `{}`", found));
+        err.span_label(trait_sp, &format!("expected `{}`", expected));
 
         // Get the span of all the used type parameters in the method.
         let assoc_item = self.tcx().associated_item(trait_def_id);
@@ -92,7 +92,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
             err.note_expected_found(&"", expected, &"", found);
         } else {
             // This fallback shouldn't be necessary, but let's keep it in just in case.
-            err.note(&format!("expected `{:?}`\n   found `{:?}`", expected, found));
+            err.note(&format!("expected `{}`\n   found `{}`", expected, found));
         }
         err.span_help(
             type_param_span,
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs
index 6e2d49f1ad7..c055fed43f6 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs
@@ -95,7 +95,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
         decl: &hir::FnDecl<'_>,
     ) -> Option<Span> {
         let ret_ty = self.tcx().type_of(scope_def_id);
-        if let ty::FnDef(_, _) = ret_ty.kind {
+        if let ty::FnDef(_, _) = ret_ty.kind() {
             let sig = ret_ty.fn_sig(self.tcx());
             let late_bound_regions =
                 self.tcx().collect_referenced_late_bound_regions(&sig.output());
diff --git a/compiler/rustc_infer/src/infer/freshen.rs b/compiler/rustc_infer/src/infer/freshen.rs
index 02bebe10ed0..b3d7876c6e8 100644
--- a/compiler/rustc_infer/src/infer/freshen.rs
+++ b/compiler/rustc_infer/src/infer/freshen.rs
@@ -144,7 +144,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> {
 
         let tcx = self.infcx.tcx;
 
-        match t.kind {
+        match *t.kind() {
             ty::Infer(ty::TyVar(v)) => {
                 let opt_ty = self.infcx.inner.borrow_mut().type_variables().probe(v).known();
                 self.freshen_ty(opt_ty, ty::TyVar(v), ty::FreshTy)
diff --git a/compiler/rustc_infer/src/infer/fudge.rs b/compiler/rustc_infer/src/infer/fudge.rs
index c6651108df5..d7bc636db8f 100644
--- a/compiler/rustc_infer/src/infer/fudge.rs
+++ b/compiler/rustc_infer/src/infer/fudge.rs
@@ -182,7 +182,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for InferenceFudger<'a, 'tcx> {
     }
 
     fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
-        match ty.kind {
+        match *ty.kind() {
             ty::Infer(ty::InferTy::TyVar(vid)) => {
                 if self.type_vars.0.contains(&vid) {
                     // This variable was created during the fudging.
diff --git a/compiler/rustc_infer/src/infer/lattice.rs b/compiler/rustc_infer/src/infer/lattice.rs
index 1bf43e74dcd..c47d4769637 100644
--- a/compiler/rustc_infer/src/infer/lattice.rs
+++ b/compiler/rustc_infer/src/infer/lattice.rs
@@ -58,7 +58,7 @@ where
     let infcx = this.infcx();
     let a = infcx.inner.borrow_mut().type_variables().replace_if_possible(a);
     let b = infcx.inner.borrow_mut().type_variables().replace_if_possible(b);
-    match (&a.kind, &b.kind) {
+    match (a.kind(), b.kind()) {
         // If one side is known to be a variable and one is not,
         // create a variable (`v`) to represent the LUB. Make sure to
         // relate `v` to the non-type-variable first (by passing it
diff --git a/compiler/rustc_infer/src/infer/lub.rs b/compiler/rustc_infer/src/infer/lub.rs
index 3e2ea3d0f8f..9f43fac0916 100644
--- a/compiler/rustc_infer/src/infer/lub.rs
+++ b/compiler/rustc_infer/src/infer/lub.rs
@@ -50,7 +50,7 @@ impl TypeRelation<'tcx> for Lub<'combine, 'infcx, 'tcx> {
             ty::Invariant => self.fields.equate(self.a_is_expected).relate(a, b),
             ty::Covariant => self.relate(a, b),
             // FIXME(#41044) -- not correct, need test
-            ty::Bivariant => Ok(a.clone()),
+            ty::Bivariant => Ok(a),
             ty::Contravariant => self.fields.glb(self.a_is_expected).relate(a, b),
         }
     }
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index 3744ad5d032..2cbdc954e20 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -21,7 +21,7 @@ use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues};
 use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue};
 use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind, ToType};
 use rustc_middle::mir;
-use rustc_middle::mir::interpret::ConstEvalResult;
+use rustc_middle::mir::interpret::EvalToConstValueResult;
 use rustc_middle::traits::select;
 use rustc_middle::ty::error::{ExpectedFound, TypeError, UnconstrainedNumeric};
 use rustc_middle::ty::fold::{TypeFoldable, TypeFolder};
@@ -680,7 +680,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     }
 
     pub fn type_var_diverges(&'a self, ty: Ty<'_>) -> bool {
-        match ty.kind {
+        match *ty.kind() {
             ty::Infer(ty::TyVar(vid)) => self.inner.borrow_mut().type_variables().var_diverges(vid),
             _ => false,
         }
@@ -693,7 +693,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     pub fn type_is_unconstrained_numeric(&'a self, ty: Ty<'_>) -> UnconstrainedNumeric {
         use rustc_middle::ty::error::UnconstrainedNumeric::Neither;
         use rustc_middle::ty::error::UnconstrainedNumeric::{UnconstrainedFloat, UnconstrainedInt};
-        match ty.kind {
+        match *ty.kind() {
             ty::Infer(ty::IntVar(vid)) => {
                 if self.inner.borrow_mut().int_unification_table().probe_value(vid).is_some() {
                     Neither
@@ -1542,7 +1542,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         substs: SubstsRef<'tcx>,
         promoted: Option<mir::Promoted>,
         span: Option<Span>,
-    ) -> ConstEvalResult<'tcx> {
+    ) -> EvalToConstValueResult<'tcx> {
         let mut original_values = OriginalQueryValues::default();
         let canonical = self.canonicalize_query(&(param_env, substs), &mut original_values);
 
@@ -1557,7 +1557,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     /// not a type variable, just return it unmodified.
     // FIXME(eddyb) inline into `ShallowResolver::visit_ty`.
     fn shallow_resolve_ty(&self, typ: Ty<'tcx>) -> Ty<'tcx> {
-        match typ.kind {
+        match *typ.kind() {
             ty::Infer(ty::TyVar(v)) => {
                 // Not entirely obvious: if `typ` is a type variable,
                 // it can be resolved to an int/float variable, which
@@ -1677,7 +1677,7 @@ impl TyOrConstInferVar<'tcx> {
     /// Tries to extract an inference variable from a type, returns `None`
     /// for types other than `ty::Infer(_)` (or `InferTy::Fresh*`).
     pub fn maybe_from_ty(ty: Ty<'tcx>) -> Option<Self> {
-        match ty.kind {
+        match *ty.kind() {
             ty::Infer(ty::TyVar(v)) => Some(TyOrConstInferVar::Ty(v)),
             ty::Infer(ty::IntVar(v)) => Some(TyOrConstInferVar::TyInt(v)),
             ty::Infer(ty::FloatVar(v)) => Some(TyOrConstInferVar::TyFloat(v)),
diff --git a/compiler/rustc_infer/src/infer/nll_relate/mod.rs b/compiler/rustc_infer/src/infer/nll_relate/mod.rs
index 3f5ed36035c..839891f322c 100644
--- a/compiler/rustc_infer/src/infer/nll_relate/mod.rs
+++ b/compiler/rustc_infer/src/infer/nll_relate/mod.rs
@@ -265,7 +265,7 @@ where
         use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
         use rustc_span::DUMMY_SP;
 
-        match value_ty.kind {
+        match *value_ty.kind() {
             ty::Projection(other_projection_ty) => {
                 let var = self.infcx.next_ty_var(TypeVariableOrigin {
                     kind: TypeVariableOriginKind::MiscVariable,
@@ -311,7 +311,7 @@ where
         // This only presently applies to chalk integration, as NLL
         // doesn't permit type variables to appear on both sides (and
         // doesn't use lazy norm).
-        match value_ty.kind {
+        match *value_ty.kind() {
             ty::Infer(ty::TyVar(value_vid)) => {
                 // Two type variables: just equate them.
                 self.infcx.inner.borrow_mut().type_variables().equate(vid, value_vid);
@@ -531,7 +531,7 @@ where
             }
         }
 
-        match (&a.kind, &b.kind) {
+        match (a.kind(), b.kind()) {
             (_, &ty::Infer(ty::TyVar(vid))) => {
                 if D::forbid_inference_vars() {
                     // Forbid inference variables in the RHS.
@@ -868,7 +868,7 @@ where
 
         debug!("TypeGeneralizer::tys(a={:?})", a);
 
-        match a.kind {
+        match *a.kind() {
             ty::Infer(ty::TyVar(_)) | ty::Infer(ty::IntVar(_)) | ty::Infer(ty::FloatVar(_))
                 if D::forbid_inference_vars() =>
             {
diff --git a/compiler/rustc_infer/src/infer/outlives/mod.rs b/compiler/rustc_infer/src/infer/outlives/mod.rs
index a1e7f1fa3e5..de98cccf256 100644
--- a/compiler/rustc_infer/src/infer/outlives/mod.rs
+++ b/compiler/rustc_infer/src/infer/outlives/mod.rs
@@ -26,7 +26,8 @@ pub fn explicit_outlives_bounds<'tcx>(
             | ty::PredicateAtom::ClosureKind(..)
             | ty::PredicateAtom::TypeOutlives(..)
             | ty::PredicateAtom::ConstEvaluatable(..)
-            | ty::PredicateAtom::ConstEquate(..) => None,
+            | ty::PredicateAtom::ConstEquate(..)
+            | ty::PredicateAtom::TypeWellFormedFromEnv(..) => None,
             ty::PredicateAtom::RegionOutlives(ty::OutlivesPredicate(r_a, r_b)) => {
                 Some(OutlivesBound::RegionSubRegion(r_b, r_a))
             }
diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs
index 48f6d937f2f..2851da89ab2 100644
--- a/compiler/rustc_infer/src/infer/outlives/obligations.rs
+++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs
@@ -383,7 +383,7 @@ where
         // #55756) in cases where you have e.g., `<T as Foo<'a>>::Item:
         // 'a` in the environment but `trait Foo<'b> { type Item: 'b
         // }` in the trait definition.
-        approx_env_bounds.retain(|bound| match bound.0.kind {
+        approx_env_bounds.retain(|bound| match *bound.0.kind() {
             ty::Projection(projection_ty) => self
                 .verify_bound
                 .projection_declared_bounds_from_trait(projection_ty)
diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs
index 8f20b5743df..21b0836563f 100644
--- a/compiler/rustc_infer/src/infer/outlives/verify.rs
+++ b/compiler/rustc_infer/src/infer/outlives/verify.rs
@@ -1,6 +1,7 @@
 use crate::infer::outlives::env::RegionBoundPairs;
 use crate::infer::{GenericKind, VerifyBound};
 use rustc_data_structures::captures::Captures;
+use rustc_data_structures::mini_set::MiniSet;
 use rustc_hir::def_id::DefId;
 use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst};
 use rustc_middle::ty::{self, Ty, TyCtxt};
@@ -31,16 +32,23 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
     /// Returns a "verify bound" that encodes what we know about
     /// `generic` and the regions it outlives.
     pub fn generic_bound(&self, generic: GenericKind<'tcx>) -> VerifyBound<'tcx> {
+        let mut visited = MiniSet::new();
         match generic {
             GenericKind::Param(param_ty) => self.param_bound(param_ty),
-            GenericKind::Projection(projection_ty) => self.projection_bound(projection_ty),
+            GenericKind::Projection(projection_ty) => {
+                self.projection_bound(projection_ty, &mut visited)
+            }
         }
     }
 
-    fn type_bound(&self, ty: Ty<'tcx>) -> VerifyBound<'tcx> {
-        match ty.kind {
+    fn type_bound(
+        &self,
+        ty: Ty<'tcx>,
+        visited: &mut MiniSet<GenericArg<'tcx>>,
+    ) -> VerifyBound<'tcx> {
+        match *ty.kind() {
             ty::Param(p) => self.param_bound(p),
-            ty::Projection(data) => self.projection_bound(data),
+            ty::Projection(data) => self.projection_bound(data, visited),
             ty::FnDef(_, substs) => {
                 // HACK(eddyb) ignore lifetimes found shallowly in `substs`.
                 // This is inconsistent with `ty::Adt` (including all substs),
@@ -50,9 +58,9 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
                 let mut bounds = substs
                     .iter()
                     .filter_map(|child| match child.unpack() {
-                        GenericArgKind::Type(ty) => Some(self.type_bound(ty)),
+                        GenericArgKind::Type(ty) => Some(self.type_bound(ty, visited)),
                         GenericArgKind::Lifetime(_) => None,
-                        GenericArgKind::Const(_) => Some(self.recursive_bound(child)),
+                        GenericArgKind::Const(_) => Some(self.recursive_bound(child, visited)),
                     })
                     .filter(|bound| {
                         // Remove bounds that must hold, since they are not interesting.
@@ -66,7 +74,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
                     ),
                 }
             }
-            _ => self.recursive_bound(ty.into()),
+            _ => self.recursive_bound(ty.into(), visited),
         }
     }
 
@@ -118,7 +126,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
         let projection_ty = GenericKind::Projection(projection_ty).to_ty(self.tcx);
         let erased_projection_ty = self.tcx.erase_regions(&projection_ty);
         self.declared_generic_bounds_from_env_with_compare_fn(|ty| {
-            if let ty::Projection(..) = ty.kind {
+            if let ty::Projection(..) = ty.kind() {
                 let erased_ty = self.tcx.erase_regions(&ty);
                 erased_ty == erased_projection_ty
             } else {
@@ -137,7 +145,11 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
         self.declared_projection_bounds_from_trait(projection_ty)
     }
 
-    pub fn projection_bound(&self, projection_ty: ty::ProjectionTy<'tcx>) -> VerifyBound<'tcx> {
+    pub fn projection_bound(
+        &self,
+        projection_ty: ty::ProjectionTy<'tcx>,
+        visited: &mut MiniSet<GenericArg<'tcx>>,
+    ) -> VerifyBound<'tcx> {
         debug!("projection_bound(projection_ty={:?})", projection_ty);
 
         let projection_ty_as_ty =
@@ -166,21 +178,25 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
 
         // see the extensive comment in projection_must_outlive
         let ty = self.tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs);
-        let recursive_bound = self.recursive_bound(ty.into());
+        let recursive_bound = self.recursive_bound(ty.into(), visited);
 
         VerifyBound::AnyBound(env_bounds.chain(trait_bounds).collect()).or(recursive_bound)
     }
 
-    fn recursive_bound(&self, parent: GenericArg<'tcx>) -> VerifyBound<'tcx> {
+    fn recursive_bound(
+        &self,
+        parent: GenericArg<'tcx>,
+        visited: &mut MiniSet<GenericArg<'tcx>>,
+    ) -> VerifyBound<'tcx> {
         let mut bounds = parent
-            .walk_shallow()
+            .walk_shallow(visited)
             .filter_map(|child| match child.unpack() {
-                GenericArgKind::Type(ty) => Some(self.type_bound(ty)),
+                GenericArgKind::Type(ty) => Some(self.type_bound(ty, visited)),
                 GenericArgKind::Lifetime(lt) => {
                     // Ignore late-bound regions.
                     if !lt.is_late_bound() { Some(VerifyBound::OutlivedBy(lt)) } else { None }
                 }
-                GenericArgKind::Const(_) => Some(self.recursive_bound(child)),
+                GenericArgKind::Const(_) => Some(self.recursive_bound(child, visited)),
             })
             .filter(|bound| {
                 // Remove bounds that must hold, since they are not interesting.
diff --git a/compiler/rustc_infer/src/infer/resolve.rs b/compiler/rustc_infer/src/infer/resolve.rs
index 74f365ced23..337772d70b8 100644
--- a/compiler/rustc_infer/src/infer/resolve.rs
+++ b/compiler/rustc_infer/src/infer/resolve.rs
@@ -124,7 +124,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for UnresolvedTypeFinder<'a, 'tcx> {
     fn visit_ty(&mut self, t: Ty<'tcx>) -> bool {
         let t = self.infcx.shallow_resolve(t);
         if t.has_infer_types() {
-            if let ty::Infer(infer_ty) = t.kind {
+            if let ty::Infer(infer_ty) = *t.kind() {
                 // Since we called `shallow_resolve` above, this must
                 // be an (as yet...) unresolved inference variable.
                 let ty_var_span = if let ty::TyVar(ty_vid) = infer_ty {
@@ -191,7 +191,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for FullTypeResolver<'a, 'tcx> {
             t // micro-optimize -- if there is nothing in this type that this fold affects...
         } else {
             let t = self.infcx.shallow_resolve(t);
-            match t.kind {
+            match *t.kind() {
                 ty::Infer(ty::TyVar(vid)) => {
                     self.err = Some(FixupError::UnresolvedTy(vid));
                     self.tcx().ty_error()
diff --git a/compiler/rustc_infer/src/infer/sub.rs b/compiler/rustc_infer/src/infer/sub.rs
index 308f884f9a6..a676c5e65a7 100644
--- a/compiler/rustc_infer/src/infer/sub.rs
+++ b/compiler/rustc_infer/src/infer/sub.rs
@@ -83,7 +83,7 @@ impl TypeRelation<'tcx> for Sub<'combine, 'infcx, 'tcx> {
         let infcx = self.fields.infcx;
         let a = infcx.inner.borrow_mut().type_variables().replace_if_possible(a);
         let b = infcx.inner.borrow_mut().type_variables().replace_if_possible(b);
-        match (&a.kind, &b.kind) {
+        match (a.kind(), b.kind()) {
             (&ty::Infer(TyVar(a_vid)), &ty::Infer(TyVar(b_vid))) => {
                 // Shouldn't have any LBR here, so we can safely put
                 // this under a binder below without fear of accidental
diff --git a/compiler/rustc_infer/src/infer/type_variable.rs b/compiler/rustc_infer/src/infer/type_variable.rs
index 53c7dcc6377..35b97fff3da 100644
--- a/compiler/rustc_infer/src/infer/type_variable.rs
+++ b/compiler/rustc_infer/src/infer/type_variable.rs
@@ -306,7 +306,7 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> {
     /// instantiated, then return the with which it was
     /// instantiated. Otherwise, returns `t`.
     pub fn replace_if_possible(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
-        match t.kind {
+        match *t.kind() {
             ty::Infer(ty::TyVar(v)) => match self.probe(v) {
                 TypeVariableValue::Unknown { .. } => t,
                 TypeVariableValue::Known { value } => value,
diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs
index e05041d8846..ea9a4661348 100644
--- a/compiler/rustc_infer/src/lib.rs
+++ b/compiler/rustc_infer/src/lib.rs
@@ -12,8 +12,7 @@
 //!
 //! This API is completely unstable and subject to change.
 
-#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
-#![feature(bindings_after_at)]
+#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![feature(bool_to_option)]
 #![feature(box_patterns)]
 #![feature(box_syntax)]
@@ -23,7 +22,6 @@
 #![feature(never_type)]
 #![feature(or_patterns)]
 #![feature(in_band_lifetimes)]
-#![feature(crate_visibility_modifier)]
 #![recursion_limit = "512"] // For rustdoc
 
 #[macro_use]
diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs
index 93fc7f1f3b8..9c0d934a035 100644
--- a/compiler/rustc_infer/src/traits/util.rs
+++ b/compiler/rustc_infer/src/traits/util.rs
@@ -236,6 +236,9 @@ impl Elaborator<'tcx> {
                         .map(|predicate| predicate_obligation(predicate, None)),
                 );
             }
+            ty::PredicateAtom::TypeWellFormedFromEnv(..) => {
+                // Nothing to elaborate
+            }
         }
     }
 }
diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml
index 9affe4ec6d8..e214493a567 100644
--- a/compiler/rustc_interface/Cargo.toml
+++ b/compiler/rustc_interface/Cargo.toml
@@ -43,7 +43,6 @@ rustc_resolve = { path = "../rustc_resolve" }
 rustc_trait_selection = { path = "../rustc_trait_selection" }
 rustc_ty = { path = "../rustc_ty" }
 tempfile = "3.0.5"
-once_cell = "1"
 
 [target.'cfg(windows)'.dependencies]
 winapi = { version = "0.3", features = ["libloaderapi"] }
diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs
index fe40c615f79..88d2efe96d1 100644
--- a/compiler/rustc_interface/src/lib.rs
+++ b/compiler/rustc_interface/src/lib.rs
@@ -4,6 +4,7 @@
 #![feature(nll)]
 #![feature(generator_trait)]
 #![feature(generators)]
+#![feature(once_cell)]
 #![recursion_limit = "256"]
 
 mod callbacks;
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index 403aea8b304..66d3765d347 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -2,7 +2,6 @@ use crate::interface::{Compiler, Result};
 use crate::proc_macro_decls;
 use crate::util;
 
-use once_cell::sync::Lazy;
 use rustc_ast::mut_visit::MutVisitor;
 use rustc_ast::{self as ast, visit};
 use rustc_codegen_ssa::back::link::emit_metadata;
@@ -46,6 +45,7 @@ use std::any::Any;
 use std::cell::RefCell;
 use std::ffi::OsString;
 use std::io::{self, BufWriter, Write};
+use std::lazy::SyncLazy;
 use std::path::PathBuf;
 use std::rc::Rc;
 use std::{env, fs, iter, mem};
@@ -291,6 +291,7 @@ fn configure_and_expand_inner<'a>(
             trace_mac: sess.opts.debugging_opts.trace_macros,
             should_test: sess.opts.test,
             span_debug: sess.opts.debugging_opts.span_debug,
+            proc_macro_backtrace: sess.opts.debugging_opts.proc_macro_backtrace,
             ..rustc_expand::expand::ExpansionConfig::default(crate_name.to_string())
         };
 
@@ -680,7 +681,7 @@ pub fn prepare_outputs(
     Ok(outputs)
 }
 
-pub static DEFAULT_QUERY_PROVIDERS: Lazy<Providers> = Lazy::new(|| {
+pub static DEFAULT_QUERY_PROVIDERS: SyncLazy<Providers> = SyncLazy::new(|| {
     let providers = &mut Providers::default();
     providers.analysis = analysis;
     proc_macro_decls::provide(providers);
@@ -703,7 +704,7 @@ pub static DEFAULT_QUERY_PROVIDERS: Lazy<Providers> = Lazy::new(|| {
     *providers
 });
 
-pub static DEFAULT_EXTERN_QUERY_PROVIDERS: Lazy<Providers> = Lazy::new(|| {
+pub static DEFAULT_EXTERN_QUERY_PROVIDERS: SyncLazy<Providers> = SyncLazy::new(|| {
     let mut extern_providers = *DEFAULT_QUERY_PROVIDERS;
     rustc_metadata::provide_extern(&mut extern_providers);
     rustc_codegen_ssa::provide_extern(&mut extern_providers);
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index e94745519a4..72e10bc4304 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -40,6 +40,7 @@ fn mk_session(matches: getopts::Matches) -> (Session, CfgSpecs) {
         DiagnosticOutput::Default,
         Default::default(),
         None,
+        None,
     );
     (sess, cfg)
 }
@@ -402,6 +403,7 @@ fn test_codegen_options_tracking_hash() {
     // `link_arg` is omitted because it just forwards to `link_args`.
     untracked!(link_args, vec![String::from("abc"), String::from("def")]);
     untracked!(link_dead_code, Some(true));
+    untracked!(link_self_contained, Some(true));
     untracked!(linker, Some(PathBuf::from("linker")));
     untracked!(linker_flavor, Some(LinkerFlavor::Gcc));
     untracked!(no_stack_check, true);
@@ -502,6 +504,7 @@ fn test_debugging_options_tracking_hash() {
     untracked!(print_llvm_passes, true);
     untracked!(print_mono_items, Some(String::from("abc")));
     untracked!(print_type_sizes, true);
+    untracked!(proc_macro_backtrace, true);
     untracked!(query_dep_graph, true);
     untracked!(query_stats, true);
     untracked!(save_analysis, true);
@@ -516,6 +519,7 @@ fn test_debugging_options_tracking_hash() {
     untracked!(time_llvm_passes, true);
     untracked!(time_passes, true);
     untracked!(trace_macros, true);
+    untracked!(trim_diagnostic_paths, false);
     untracked!(ui_testing, true);
     untracked!(unpretty, Some("expanded".to_string()));
     untracked!(unstable_options, true);
diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs
index 8816ba198cf..0eed6938c31 100644
--- a/compiler/rustc_interface/src/util.rs
+++ b/compiler/rustc_interface/src/util.rs
@@ -25,6 +25,7 @@ use rustc_span::symbol::{sym, Symbol};
 use smallvec::SmallVec;
 use std::env;
 use std::io::{self, Write};
+use std::lazy::SyncOnceCell;
 use std::mem;
 use std::ops::DerefMut;
 use std::path::{Path, PathBuf};
@@ -64,6 +65,10 @@ pub fn create_session(
     lint_caps: FxHashMap<lint::LintId, lint::Level>,
     descriptions: Registry,
 ) -> (Lrc<Session>, Lrc<Box<dyn CodegenBackend>>) {
+    let codegen_backend = get_codegen_backend(&sopts);
+    // target_override is documented to be called before init(), so this is okay
+    let target_override = codegen_backend.target_override(&sopts);
+
     let mut sess = session::build_session(
         sopts,
         input_path,
@@ -71,9 +76,10 @@ pub fn create_session(
         diagnostic_output,
         lint_caps,
         file_loader,
+        target_override,
     );
 
-    let codegen_backend = get_codegen_backend(&sess);
+    codegen_backend.init(&sess);
 
     let mut cfg = config::build_configuration(&sess, config::to_crate_config(cfg));
     add_configuration(&mut cfg, &mut sess, &*codegen_backend);
@@ -218,13 +224,13 @@ fn load_backend_from_dylib(path: &Path) -> fn() -> Box<dyn CodegenBackend> {
     }
 }
 
-pub fn get_codegen_backend(sess: &Session) -> Box<dyn CodegenBackend> {
+pub fn get_codegen_backend(sopts: &config::Options) -> Box<dyn CodegenBackend> {
     static INIT: Once = Once::new();
 
     static mut LOAD: fn() -> Box<dyn CodegenBackend> = || unreachable!();
 
     INIT.call_once(|| {
-        let codegen_name = sess.opts.debugging_opts.codegen_backend.as_deref().unwrap_or("llvm");
+        let codegen_name = sopts.debugging_opts.codegen_backend.as_deref().unwrap_or("llvm");
         let backend = match codegen_name {
             filename if filename.contains('.') => load_backend_from_dylib(filename.as_ref()),
             codegen_name => get_builtin_codegen_backend(codegen_name),
@@ -234,17 +240,14 @@ pub fn get_codegen_backend(sess: &Session) -> Box<dyn CodegenBackend> {
             LOAD = backend;
         }
     });
-    let backend = unsafe { LOAD() };
-    backend.init(sess);
-    backend
+    unsafe { LOAD() }
 }
 
 // This is used for rustdoc, but it uses similar machinery to codegen backend
 // loading, so we leave the code here. It is potentially useful for other tools
 // that want to invoke the rustc binary while linking to rustc as well.
 pub fn rustc_path<'a>() -> Option<&'a Path> {
-    static RUSTC_PATH: once_cell::sync::OnceCell<Option<PathBuf>> =
-        once_cell::sync::OnceCell::new();
+    static RUSTC_PATH: SyncOnceCell<Option<PathBuf>> = SyncOnceCell::new();
 
     const BIN_PATH: &str = env!("RUSTC_INSTALL_BINDIR");
 
@@ -693,6 +696,7 @@ impl<'a> MutVisitor for ReplaceBodyWithLoop<'a, '_> {
                 rules,
                 id: resolver.next_node_id(),
                 span: rustc_span::DUMMY_SP,
+                tokens: None,
             }
         }
 
@@ -709,6 +713,7 @@ impl<'a> MutVisitor for ReplaceBodyWithLoop<'a, '_> {
                 id: resolver.next_node_id(),
                 kind: ast::StmtKind::Expr(expr),
                 span: rustc_span::DUMMY_SP,
+                tokens: None,
             }
         }
 
@@ -725,6 +730,7 @@ impl<'a> MutVisitor for ReplaceBodyWithLoop<'a, '_> {
             id: self.resolver.next_node_id(),
             span: rustc_span::DUMMY_SP,
             kind: ast::StmtKind::Expr(loop_expr),
+            tokens: None,
         };
 
         if self.within_static_or_const {
diff --git a/compiler/rustc_lexer/Cargo.toml b/compiler/rustc_lexer/Cargo.toml
index 28b56f6fef4..12101776de2 100644
--- a/compiler/rustc_lexer/Cargo.toml
+++ b/compiler/rustc_lexer/Cargo.toml
@@ -14,11 +14,10 @@ Rust lexer used by rustc. No stability guarantees are provided.
 # This will be used when publishing this crate as `rustc-ap-rustc_lexer`.
 [lib]
 doctest = false
-name = "rustc_lexer"
 
 # Note that this crate purposefully does not depend on other rustc crates
 [dependencies]
 unicode-xid = "0.2.0"
 
 [dev-dependencies]
-expect-test = "0.1"
+expect-test = "1.0"
diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs
index b7d6194cd77..d784a86f14c 100644
--- a/compiler/rustc_lexer/src/lib.rs
+++ b/compiler/rustc_lexer/src/lib.rs
@@ -2,7 +2,7 @@
 //!
 //! The idea with `librustc_lexer` is to make a reusable library,
 //! by separating out pure lexing and rustc-specific concerns, like spans,
-//! error reporting an interning.  So, rustc_lexer operates directly on `&str`,
+//! error reporting, and interning.  So, rustc_lexer operates directly on `&str`,
 //! produces simple tokens which are a pair of type-tag and a bit of original text,
 //! and does not report errors, instead storing them as flags on the token.
 //!
@@ -191,12 +191,16 @@ pub fn strip_shebang(input: &str) -> Option<usize> {
     // For simplicity we consider any line starting with `#!` a shebang,
     // regardless of restrictions put on shebangs by specific platforms.
     if let Some(input_tail) = input.strip_prefix("#!") {
-        // Ok, this is a shebang but if the next non-whitespace token is `[` or maybe
-        // a doc comment (due to `TokenKind::(Line,Block)Comment` ambiguity at lexer level),
+        // Ok, this is a shebang but if the next non-whitespace token is `[`,
         // then it may be valid Rust code, so consider it Rust code.
-        let next_non_whitespace_token = tokenize(input_tail).map(|tok| tok.kind).find(|tok|
-            !matches!(tok, TokenKind::Whitespace | TokenKind::LineComment { .. } | TokenKind::BlockComment { .. })
-        );
+        let next_non_whitespace_token = tokenize(input_tail).map(|tok| tok.kind).find(|tok| {
+            !matches!(
+                tok,
+                TokenKind::Whitespace
+                    | TokenKind::LineComment { doc_style: None }
+                    | TokenKind::BlockComment { doc_style: None, .. }
+            )
+        });
         if next_non_whitespace_token != Some(TokenKind::OpenBracket) {
             // No other choice than to consider this a shebang.
             return Some(2 + input_tail.lines().next().unwrap_or_default().len());
diff --git a/compiler/rustc_lexer/src/tests.rs b/compiler/rustc_lexer/src/tests.rs
index a1ea5ceb1f6..94017b7b286 100644
--- a/compiler/rustc_lexer/src/tests.rs
+++ b/compiler/rustc_lexer/src/tests.rs
@@ -129,6 +129,34 @@ fn check_lexing(src: &str, expect: Expect) {
 }
 
 #[test]
+fn smoke_test() {
+    check_lexing(
+        "/* my source file */ fn main() { println!(\"zebra\"); }\n",
+        expect![[r#"
+            Token { kind: BlockComment { doc_style: None, terminated: true }, len: 20 }
+            Token { kind: Whitespace, len: 1 }
+            Token { kind: Ident, len: 2 }
+            Token { kind: Whitespace, len: 1 }
+            Token { kind: Ident, len: 4 }
+            Token { kind: OpenParen, len: 1 }
+            Token { kind: CloseParen, len: 1 }
+            Token { kind: Whitespace, len: 1 }
+            Token { kind: OpenBrace, len: 1 }
+            Token { kind: Whitespace, len: 1 }
+            Token { kind: Ident, len: 7 }
+            Token { kind: Bang, len: 1 }
+            Token { kind: OpenParen, len: 1 }
+            Token { kind: Literal { kind: Str { terminated: true }, suffix_start: 7 }, len: 7 }
+            Token { kind: CloseParen, len: 1 }
+            Token { kind: Semi, len: 1 }
+            Token { kind: Whitespace, len: 1 }
+            Token { kind: CloseBrace, len: 1 }
+            Token { kind: Whitespace, len: 1 }
+        "#]],
+    )
+}
+
+#[test]
 fn comment_flavors() {
     check_lexing(
         r"
@@ -143,25 +171,117 @@ fn comment_flavors() {
 /*! inner doc block */
 ",
         expect![[r#"
-                Token { kind: Whitespace, len: 1 }
-                Token { kind: LineComment { doc_style: None }, len: 7 }
-                Token { kind: Whitespace, len: 1 }
-                Token { kind: LineComment { doc_style: None }, len: 17 }
-                Token { kind: Whitespace, len: 1 }
-                Token { kind: LineComment { doc_style: Some(Outer) }, len: 18 }
-                Token { kind: Whitespace, len: 1 }
-                Token { kind: LineComment { doc_style: Some(Inner) }, len: 18 }
-                Token { kind: Whitespace, len: 1 }
-                Token { kind: BlockComment { doc_style: None, terminated: true }, len: 11 }
-                Token { kind: Whitespace, len: 1 }
-                Token { kind: BlockComment { doc_style: None, terminated: true }, len: 4 }
-                Token { kind: Whitespace, len: 1 }
-                Token { kind: BlockComment { doc_style: None, terminated: true }, len: 18 }
-                Token { kind: Whitespace, len: 1 }
-                Token { kind: BlockComment { doc_style: Some(Outer), terminated: true }, len: 22 }
-                Token { kind: Whitespace, len: 1 }
-                Token { kind: BlockComment { doc_style: Some(Inner), terminated: true }, len: 22 }
-                Token { kind: Whitespace, len: 1 }
-            "#]],
+            Token { kind: Whitespace, len: 1 }
+            Token { kind: LineComment { doc_style: None }, len: 7 }
+            Token { kind: Whitespace, len: 1 }
+            Token { kind: LineComment { doc_style: None }, len: 17 }
+            Token { kind: Whitespace, len: 1 }
+            Token { kind: LineComment { doc_style: Some(Outer) }, len: 18 }
+            Token { kind: Whitespace, len: 1 }
+            Token { kind: LineComment { doc_style: Some(Inner) }, len: 18 }
+            Token { kind: Whitespace, len: 1 }
+            Token { kind: BlockComment { doc_style: None, terminated: true }, len: 11 }
+            Token { kind: Whitespace, len: 1 }
+            Token { kind: BlockComment { doc_style: None, terminated: true }, len: 4 }
+            Token { kind: Whitespace, len: 1 }
+            Token { kind: BlockComment { doc_style: None, terminated: true }, len: 18 }
+            Token { kind: Whitespace, len: 1 }
+            Token { kind: BlockComment { doc_style: Some(Outer), terminated: true }, len: 22 }
+            Token { kind: Whitespace, len: 1 }
+            Token { kind: BlockComment { doc_style: Some(Inner), terminated: true }, len: 22 }
+            Token { kind: Whitespace, len: 1 }
+        "#]],
+    )
+}
+
+#[test]
+fn nested_block_comments() {
+    check_lexing(
+        "/* /* */ */'a'",
+        expect![[r#"
+            Token { kind: BlockComment { doc_style: None, terminated: true }, len: 11 }
+            Token { kind: Literal { kind: Char { terminated: true }, suffix_start: 3 }, len: 3 }
+        "#]],
+    )
+}
+
+#[test]
+fn characters() {
+    check_lexing(
+        "'a' ' ' '\\n'",
+        expect![[r#"
+            Token { kind: Literal { kind: Char { terminated: true }, suffix_start: 3 }, len: 3 }
+            Token { kind: Whitespace, len: 1 }
+            Token { kind: Literal { kind: Char { terminated: true }, suffix_start: 3 }, len: 3 }
+            Token { kind: Whitespace, len: 1 }
+            Token { kind: Literal { kind: Char { terminated: true }, suffix_start: 4 }, len: 4 }
+        "#]],
+    );
+}
+
+#[test]
+fn lifetime() {
+    check_lexing(
+        "'abc",
+        expect![[r#"
+            Token { kind: Lifetime { starts_with_number: false }, len: 4 }
+        "#]],
+    );
+}
+
+#[test]
+fn raw_string() {
+    check_lexing(
+        "r###\"\"#a\\b\x00c\"\"###",
+        expect![[r#"
+            Token { kind: Literal { kind: RawStr { n_hashes: 3, err: None }, suffix_start: 17 }, len: 17 }
+        "#]],
+    )
+}
+
+#[test]
+fn literal_suffixes() {
+    check_lexing(
+        r####"
+'a'
+b'a'
+"a"
+b"a"
+1234
+0b101
+0xABC
+1.0
+1.0e10
+2us
+r###"raw"###suffix
+br###"raw"###suffix
+"####,
+        expect![[r#"
+            Token { kind: Whitespace, len: 1 }
+            Token { kind: Literal { kind: Char { terminated: true }, suffix_start: 3 }, len: 3 }
+            Token { kind: Whitespace, len: 1 }
+            Token { kind: Literal { kind: Byte { terminated: true }, suffix_start: 4 }, len: 4 }
+            Token { kind: Whitespace, len: 1 }
+            Token { kind: Literal { kind: Str { terminated: true }, suffix_start: 3 }, len: 3 }
+            Token { kind: Whitespace, len: 1 }
+            Token { kind: Literal { kind: ByteStr { terminated: true }, suffix_start: 4 }, len: 4 }
+            Token { kind: Whitespace, len: 1 }
+            Token { kind: Literal { kind: Int { base: Decimal, empty_int: false }, suffix_start: 4 }, len: 4 }
+            Token { kind: Whitespace, len: 1 }
+            Token { kind: Literal { kind: Int { base: Binary, empty_int: false }, suffix_start: 5 }, len: 5 }
+            Token { kind: Whitespace, len: 1 }
+            Token { kind: Literal { kind: Int { base: Hexadecimal, empty_int: false }, suffix_start: 5 }, len: 5 }
+            Token { kind: Whitespace, len: 1 }
+            Token { kind: Literal { kind: Float { base: Decimal, empty_exponent: false }, suffix_start: 3 }, len: 3 }
+            Token { kind: Whitespace, len: 1 }
+            Token { kind: Literal { kind: Float { base: Decimal, empty_exponent: false }, suffix_start: 6 }, len: 6 }
+            Token { kind: Whitespace, len: 1 }
+            Token { kind: Literal { kind: Int { base: Decimal, empty_int: false }, suffix_start: 1 }, len: 3 }
+            Token { kind: Whitespace, len: 1 }
+            Token { kind: Literal { kind: RawStr { n_hashes: 3, err: None }, suffix_start: 12 }, len: 18 }
+            Token { kind: Whitespace, len: 1 }
+            Token { kind: Literal { kind: RawByteStr { n_hashes: 3, err: None }, suffix_start: 13 }, len: 19 }
+            Token { kind: Whitespace, len: 1 }
+        "#]],
     )
 }
diff --git a/compiler/rustc_lint/src/array_into_iter.rs b/compiler/rustc_lint/src/array_into_iter.rs
index 9d74ad3b2f5..e6be082da0e 100644
--- a/compiler/rustc_lint/src/array_into_iter.rs
+++ b/compiler/rustc_lint/src/array_into_iter.rs
@@ -7,6 +7,31 @@ use rustc_session::lint::FutureIncompatibleInfo;
 use rustc_span::symbol::sym;
 
 declare_lint! {
+    /// The `array_into_iter` lint detects calling `into_iter` on arrays.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// # #![allow(unused)]
+    /// [1, 2, 3].into_iter().for_each(|n| { *n; });
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// In the future, it is planned to add an `IntoIter` implementation for
+    /// arrays such that it will iterate over *values* of the array instead of
+    /// references. Due to how method resolution works, this will change
+    /// existing code that uses `into_iter` on arrays. The solution to avoid
+    /// this warning is to use `iter()` instead of `into_iter()`.
+    ///
+    /// This is a [future-incompatible] lint to transition this to a hard error
+    /// in the future. See [issue #66145] for more details and a more thorough
+    /// description of the lint.
+    ///
+    /// [issue #66145]: https://github.com/rust-lang/rust/issues/66145
+    /// [future-incompatible]: ../index.md#future-incompatible-lints
     pub ARRAY_INTO_ITER,
     Warn,
     "detects calling `into_iter` on arrays",
@@ -53,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter {
             }
 
             // Make sure we found an array after peeling the boxes.
-            if !matches!(recv_ty.kind, ty::Array(..)) {
+            if !matches!(recv_ty.kind(), ty::Array(..)) {
                 return;
             }
 
@@ -66,9 +91,9 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter {
             }
 
             // Emit lint diagnostic.
-            let target = match cx.typeck_results().expr_ty_adjusted(receiver_arg).kind {
-                ty::Ref(_, ty::TyS { kind: ty::Array(..), .. }, _) => "[T; N]",
-                ty::Ref(_, ty::TyS { kind: ty::Slice(..), .. }, _) => "[T]",
+            let target = match *cx.typeck_results().expr_ty_adjusted(receiver_arg).kind() {
+                ty::Ref(_, inner_ty, _) if inner_ty.is_array() => "[T; N]",
+                ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), ty::Slice(..)) => "[T]",
 
                 // We know the original first argument type is an array type,
                 // we know that the first adjustment was an autoref coercion
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index ea624b9ed30..abd899e8db4 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -21,7 +21,8 @@
 //! `late_lint_methods!` invocation in `lib.rs`.
 
 use crate::{
-    types::CItemKind, EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext,
+    types::{transparent_newtype_field, CItemKind},
+    EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext,
 };
 use rustc_ast::attr::{self, HasAttrs};
 use rustc_ast::tokenstream::{TokenStream, TokenTree};
@@ -40,6 +41,7 @@ use rustc_hir::{ForeignItemKind, GenericParamKind, PatKind};
 use rustc_hir::{HirId, HirIdSet, Node};
 use rustc_index::vec::Idx;
 use rustc_middle::lint::LintDiagnosticBuilder;
+use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::subst::{GenericArgKind, Subst};
 use rustc_middle::ty::{self, layout::LayoutError, Ty, TyCtxt};
 use rustc_session::lint::FutureIncompatibleInfo;
@@ -60,6 +62,23 @@ use tracing::{debug, trace};
 pub use rustc_session::lint::builtin::*;
 
 declare_lint! {
+    /// The `while_true` lint detects `while true { }`.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,no_run
+    /// while true {
+    ///
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// `while true` should be replaced with `loop`. A `loop` expression is
+    /// the preferred way to write an infinite loop because it more directly
+    /// expresses the intent of the loop.
     WHILE_TRUE,
     Warn,
     "suggest using `loop { }` instead of `while true { }`"
@@ -101,6 +120,24 @@ impl EarlyLintPass for WhileTrue {
 }
 
 declare_lint! {
+    /// The `box_pointers` lints use of the Box type.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// #![deny(box_pointers)]
+    /// struct Foo {
+    ///     x: Box<isize>,
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// This lint is mostly historical, and not particularly useful. `Box<T>`
+    /// used to be built into the language, and the only way to do heap
+    /// allocation. Today's Rust can call into other allocators, etc.
     BOX_POINTERS,
     Allow,
     "use of owned (Box type) heap memory"
@@ -155,6 +192,36 @@ impl<'tcx> LateLintPass<'tcx> for BoxPointers {
 }
 
 declare_lint! {
+    /// The `non_shorthand_field_patterns` lint detects using `Struct { x: x }`
+    /// instead of `Struct { x }` in a pattern.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// struct Point {
+    ///     x: i32,
+    ///     y: i32,
+    /// }
+    ///
+    ///
+    /// fn main() {
+    ///     let p = Point {
+    ///         x: 5,
+    ///         y: 5,
+    ///     };
+    ///
+    ///     match p {
+    ///         Point { x: x, y: y } => (),
+    ///     }
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// The preferred style is to avoid the repetition of specifying both the
+    /// field name and the binding name if both identifiers are the same.
     NON_SHORTHAND_FIELD_PATTERNS,
     Warn,
     "using `Struct { x: x }` instead of `Struct { x }` in a pattern"
@@ -215,6 +282,25 @@ impl<'tcx> LateLintPass<'tcx> for NonShorthandFieldPatterns {
 }
 
 declare_lint! {
+    /// The `unsafe_code` lint catches usage of `unsafe` code.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// #![deny(unsafe_code)]
+    /// fn main() {
+    ///     unsafe {
+    ///
+    ///     }
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// This lint is intended to restrict the usage of `unsafe`, which can be
+    /// difficult to use correctly.
     UNSAFE_CODE,
     Allow,
     "usage of `unsafe` code"
@@ -302,6 +388,25 @@ impl EarlyLintPass for UnsafeCode {
 }
 
 declare_lint! {
+    /// The `missing_docs` lint detects missing documentation for public items.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// #![deny(missing_docs)]
+    /// pub fn foo() {}
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// This lint is intended to ensure that a library is well-documented.
+    /// Items without documentation can be difficult for users to understand
+    /// how to use properly.
+    ///
+    /// This lint is "allow" by default because it can be noisy, and not all
+    /// projects may want to enforce everything to be documented.
     pub MISSING_DOCS,
     Allow,
     "detects missing documentation for public members",
@@ -508,6 +613,19 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
         );
     }
 
+    fn check_foreign_item(&mut self, cx: &LateContext<'_>, foreign_item: &hir::ForeignItem<'_>) {
+        let def_id = cx.tcx.hir().local_def_id(foreign_item.hir_id);
+        let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id());
+        self.check_missing_docs_attrs(
+            cx,
+            Some(foreign_item.hir_id),
+            &foreign_item.attrs,
+            foreign_item.span,
+            article,
+            desc,
+        );
+    }
+
     fn check_struct_field(&mut self, cx: &LateContext<'_>, sf: &hir::StructField<'_>) {
         if !sf.is_positional() {
             self.check_missing_docs_attrs(
@@ -527,6 +645,34 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
 }
 
 declare_lint! {
+    /// The `missing_copy_implementations` lint detects potentially-forgotten
+    /// implementations of [`Copy`].
+    ///
+    /// [`Copy`]: https://doc.rust-lang.org/std/marker/trait.Copy.html
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// #![deny(missing_copy_implementations)]
+    /// pub struct Foo {
+    ///     pub field: i32
+    /// }
+    /// # fn main() {}
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Historically (before 1.0), types were automatically marked as `Copy`
+    /// if possible. This was changed so that it required an explicit opt-in
+    /// by implementing the `Copy` trait. As part of this change, a lint was
+    /// added to alert if a copyable type was not marked `Copy`.
+    ///
+    /// This lint is "allow" by default because this code isn't bad; it is
+    /// common to write newtypes like this specifically so that a `Copy` type
+    /// is no longer `Copy`. `Copy` types can result in unintended copies of
+    /// large data which can impact performance.
     pub MISSING_COPY_IMPLEMENTATIONS,
     Allow,
     "detects potentially-forgotten implementations of `Copy`"
@@ -583,6 +729,32 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations {
 }
 
 declare_lint! {
+    /// The `missing_debug_implementations` lint detects missing
+    /// implementations of [`fmt::Debug`].
+    ///
+    /// [`fmt::Debug`]: https://doc.rust-lang.org/std/fmt/trait.Debug.html
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// #![deny(missing_debug_implementations)]
+    /// pub struct Foo;
+    /// # fn main() {}
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Having a `Debug` implementation on all types can assist with
+    /// debugging, as it provides a convenient way to format and display a
+    /// value. Using the `#[derive(Debug)]` attribute will automatically
+    /// generate a typical implementation, or a custom implementation can be
+    /// added by manually implementing the `Debug` trait.
+    ///
+    /// This lint is "allow" by default because adding `Debug` to all types can
+    /// have a negative impact on compile time and code size. It also requires
+    /// boilerplate to be added to every type, which can be an impediment.
     MISSING_DEBUG_IMPLEMENTATIONS,
     Allow,
     "detects missing implementations of Debug"
@@ -639,6 +811,45 @@ impl<'tcx> LateLintPass<'tcx> for MissingDebugImplementations {
 }
 
 declare_lint! {
+    /// The `anonymous_parameters` lint detects anonymous parameters in trait
+    /// definitions.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,edition2015,compile_fail
+    /// #![deny(anonymous_parameters)]
+    /// // edition 2015
+    /// pub trait Foo {
+    ///     fn foo(usize);
+    /// }
+    /// fn main() {}
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// This syntax is mostly a historical accident, and can be worked around
+    /// quite easily by adding an `_` pattern or a descriptive identifier:
+    ///
+    /// ```rust
+    /// trait Foo {
+    ///     fn foo(_: usize);
+    /// }
+    /// ```
+    ///
+    /// This syntax is now a hard error in the 2018 edition. In the 2015
+    /// edition, this lint is "allow" by default, because the old code is
+    /// still valid, and warning for all old code can be noisy. This lint
+    /// enables the [`cargo fix`] tool with the `--edition` flag to
+    /// automatically transition old code from the 2015 edition to 2018. The
+    /// tool will switch this lint to "warn" and will automatically apply the
+    /// suggested fix from the compiler (which is to add `_` to each
+    /// parameter). This provides a completely automated way to update old
+    /// code for a new edition. See [issue #41686] for more details.
+    ///
+    /// [issue #41686]: https://github.com/rust-lang/rust/issues/41686
+    /// [`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html
     pub ANONYMOUS_PARAMETERS,
     Allow,
     "detects anonymous parameters",
@@ -764,7 +975,7 @@ fn warn_if_doc(cx: &EarlyContext<'_>, node_span: Span, node_kind: &str, attrs: &
             continue;
         }
 
-        let span = sugared_span.take().unwrap_or_else(|| attr.span);
+        let span = sugared_span.take().unwrap_or(attr.span);
 
         if attr.is_doc_comment() || cx.sess().check_name(attr, sym::doc) {
             cx.struct_span_lint(UNUSED_DOC_COMMENTS, span, |lint| {
@@ -805,12 +1016,54 @@ impl EarlyLintPass for UnusedDocComment {
 }
 
 declare_lint! {
+    /// The `no_mangle_const_items` lint detects any `const` items with the
+    /// [`no_mangle` attribute].
+    ///
+    /// [`no_mangle` attribute]: https://doc.rust-lang.org/reference/abi.html#the-no_mangle-attribute
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// #[no_mangle]
+    /// const FOO: i32 = 5;
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Constants do not have their symbols exported, and therefore, this
+    /// probably means you meant to use a [`static`], not a [`const`].
+    ///
+    /// [`static`]: https://doc.rust-lang.org/reference/items/static-items.html
+    /// [`const`]: https://doc.rust-lang.org/reference/items/constant-items.html
     NO_MANGLE_CONST_ITEMS,
     Deny,
     "const items will not have their symbols exported"
 }
 
 declare_lint! {
+    /// The `no_mangle_generic_items` lint detects generic items that must be
+    /// mangled.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// #[no_mangle]
+    /// fn foo<T>(t: T) {
+    ///
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// An function with generics must have its symbol mangled to accommodate
+    /// the generic parameter. The [`no_mangle` attribute] has no effect in
+    /// this situation, and should be removed.
+    ///
+    /// [`no_mangle` attribute]: https://doc.rust-lang.org/reference/abi.html#the-no_mangle-attribute
     NO_MANGLE_GENERIC_ITEMS,
     Warn,
     "generic items must be mangled"
@@ -881,6 +1134,27 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
 }
 
 declare_lint! {
+    /// The `mutable_transmutes` lint catches transmuting from `&T` to `&mut
+    /// T` because it is [undefined behavior].
+    ///
+    /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// unsafe {
+    ///     let y = std::mem::transmute::<&i32, &mut i32>(&5);
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Certain assumptions are made about aliasing of data, and this transmute
+    /// violates those assumptions. Consider using [`UnsafeCell`] instead.
+    ///
+    /// [`UnsafeCell`]: https://doc.rust-lang.org/std/cell/struct.UnsafeCell.html
     MUTABLE_TRANSMUTES,
     Deny,
     "mutating transmuted &mut T from &T may cause undefined behavior"
@@ -892,7 +1166,7 @@ impl<'tcx> LateLintPass<'tcx> for MutableTransmutes {
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
         use rustc_target::spec::abi::Abi::RustIntrinsic;
         if let Some((&ty::Ref(_, _, from_mt), &ty::Ref(_, _, to_mt))) =
-            get_transmute_from_to(cx, expr).map(|(ty1, ty2)| (&ty1.kind, &ty2.kind))
+            get_transmute_from_to(cx, expr).map(|(ty1, ty2)| (ty1.kind(), ty2.kind()))
         {
             if to_mt == hir::Mutability::Mut && from_mt == hir::Mutability::Not {
                 let msg = "mutating transmuted &mut T from &T may cause undefined behavior, \
@@ -930,6 +1204,7 @@ impl<'tcx> LateLintPass<'tcx> for MutableTransmutes {
 }
 
 declare_lint! {
+    /// The `unstable_features` is deprecated and should no longer be used.
     UNSTABLE_FEATURES,
     Allow,
     "enabling unstable features (deprecated. do not use)"
@@ -955,6 +1230,32 @@ impl<'tcx> LateLintPass<'tcx> for UnstableFeatures {
 }
 
 declare_lint! {
+    /// The `unreachable_pub` lint triggers for `pub` items not reachable from
+    /// the crate root.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// #![deny(unreachable_pub)]
+    /// mod foo {
+    ///     pub mod bar {
+    ///
+    ///     }
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// A bare `pub` visibility may be misleading if the item is not actually
+    /// publicly exported from the crate. The `pub(crate)` visibility is
+    /// recommended to be used instead, which more clearly expresses the intent
+    /// that the item is only visible within its own crate.
+    ///
+    /// This lint is "allow" by default because it will trigger for a large
+    /// amount existing Rust code, and has some false-positives. Eventually it
+    /// is desired for this to become warn-by-default.
     pub UNREACHABLE_PUB,
     Allow,
     "`pub` items not reachable from crate root"
@@ -1034,6 +1335,21 @@ impl<'tcx> LateLintPass<'tcx> for UnreachablePub {
 }
 
 declare_lint! {
+    /// The `type_alias_bounds` lint detects bounds in type aliases.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// type SendVec<T: Send> = Vec<T>;
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// The trait bounds in a type alias are currently ignored, and should not
+    /// be included to avoid confusion. This was previously allowed
+    /// unintentionally; this may become a hard error in the future.
     TYPE_ALIAS_BOUNDS,
     Warn,
     "bounds in type aliases are not enforced"
@@ -1171,21 +1487,19 @@ declare_lint_pass!(
     UnusedBrokenConst => []
 );
 
-fn check_const(cx: &LateContext<'_>, body_id: hir::BodyId) {
-    let def_id = cx.tcx.hir().body_owner_def_id(body_id).to_def_id();
-    // trigger the query once for all constants since that will already report the errors
-    // FIXME: Use ensure here
-    let _ = cx.tcx.const_eval_poly(def_id);
-}
-
 impl<'tcx> LateLintPass<'tcx> for UnusedBrokenConst {
     fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
         match it.kind {
             hir::ItemKind::Const(_, body_id) => {
-                check_const(cx, body_id);
+                let def_id = cx.tcx.hir().body_owner_def_id(body_id).to_def_id();
+                // trigger the query once for all constants since that will already report the errors
+                // FIXME: Use ensure here
+                let _ = cx.tcx.const_eval_poly(def_id);
             }
             hir::ItemKind::Static(_, _, body_id) => {
-                check_const(cx, body_id);
+                let def_id = cx.tcx.hir().body_owner_def_id(body_id).to_def_id();
+                // FIXME: Use ensure here
+                let _ = cx.tcx.eval_static_initializer(def_id);
             }
             _ => {}
         }
@@ -1193,6 +1507,35 @@ impl<'tcx> LateLintPass<'tcx> for UnusedBrokenConst {
 }
 
 declare_lint! {
+    /// The `trivial_bounds` lint detects trait bounds that don't depend on
+    /// any type parameters.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// #![feature(trivial_bounds)]
+    /// pub struct A where i32: Copy;
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Usually you would not write a trait bound that you know is always
+    /// true, or never true. However, when using macros, the macro may not
+    /// know whether or not the constraint would hold or not at the time when
+    /// generating the code. Currently, the compiler does not alert you if the
+    /// constraint is always true, and generates an error if it is never true.
+    /// The `trivial_bounds` feature changes this to be a warning in both
+    /// cases, giving macros more freedom and flexibility to generate code,
+    /// while still providing a signal when writing non-macro code that
+    /// something is amiss.
+    ///
+    /// See [RFC 2056] for more details. This feature is currently only
+    /// available on the nightly channel, see [tracking issue #48214].
+    ///
+    /// [RFC 2056]: https://github.com/rust-lang/rfcs/blob/master/text/2056-allow-trivial-where-clause-constraints.md
+    /// [tracking issue #48214]: https://github.com/rust-lang/rust/issues/48214
     TRIVIAL_BOUNDS,
     Warn,
     "these bounds don't depend on an type parameters"
@@ -1227,7 +1570,8 @@ impl<'tcx> LateLintPass<'tcx> for TrivialConstraints {
                     ClosureKind(..) |
                     Subtype(..) |
                     ConstEvaluatable(..) |
-                    ConstEquate(..) => continue,
+                    ConstEquate(..) |
+                    TypeWellFormedFromEnv(..) => continue,
                 };
                 if predicate.is_global() {
                     cx.struct_span_lint(TRIVIAL_BOUNDS, span, |lint| {
@@ -1268,6 +1612,29 @@ declare_lint_pass!(
 );
 
 declare_lint! {
+    /// The `ellipsis_inclusive_range_patterns` lint detects the [`...` range
+    /// pattern], which is deprecated.
+    ///
+    /// [`...` range pattern]: https://doc.rust-lang.org/reference/patterns.html#range-patterns
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// let x = 123;
+    /// match x {
+    ///     0...100 => {}
+    ///     _ => {}
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// The `...` range pattern syntax was changed to `..=` to avoid potential
+    /// confusion with the [`..` range expression]. Use the new form instead.
+    ///
+    /// [`..` range expression]: https://doc.rust-lang.org/reference/expressions/range-expr.html
     pub ELLIPSIS_INCLUSIVE_RANGE_PATTERNS,
     Warn,
     "`...` range patterns are deprecated"
@@ -1354,6 +1721,38 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns {
 }
 
 declare_lint! {
+    /// The `unnameable_test_items` lint detects [`#[test]`][test] functions
+    /// that are not able to be run by the test harness because they are in a
+    /// position where they are not nameable.
+    ///
+    /// [test]: https://doc.rust-lang.org/reference/attributes/testing.html#the-test-attribute
+    ///
+    /// ### Example
+    ///
+    /// ```rust,test
+    /// fn main() {
+    ///     #[test]
+    ///     fn foo() {
+    ///         // This test will not fail because it does not run.
+    ///         assert_eq!(1, 2);
+    ///     }
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// In order for the test harness to run a test, the test function must be
+    /// located in a position where it can be accessed from the crate root.
+    /// This generally means it must be defined in a module, and not anywhere
+    /// else such as inside another function. The compiler previously allowed
+    /// this without an error, so a lint was added as an alert that a test is
+    /// not being used. Whether or not this should be allowed has not yet been
+    /// decided, see [RFC 2471] and [issue #36629].
+    ///
+    /// [RFC 2471]: https://github.com/rust-lang/rfcs/pull/2471#issuecomment-397414443
+    /// [issue #36629]: https://github.com/rust-lang/rust/issues/36629
     UNNAMEABLE_TEST_ITEMS,
     Warn,
     "detects an item that cannot be named being marked as `#[test_case]`",
@@ -1399,6 +1798,41 @@ impl<'tcx> LateLintPass<'tcx> for UnnameableTestItems {
 }
 
 declare_lint! {
+    /// The `keyword_idents` lint detects edition keywords being used as an
+    /// identifier.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,edition2015,compile_fail
+    /// #![deny(keyword_idents)]
+    /// // edition 2015
+    /// fn dyn() {}
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Rust [editions] allow the language to evolve without breaking
+    /// backwards compatibility. This lint catches code that uses new keywords
+    /// that are added to the language that are used as identifiers (such as a
+    /// variable name, function name, etc.). If you switch the compiler to a
+    /// new edition without updating the code, then it will fail to compile if
+    /// you are using a new keyword as an identifier.
+    ///
+    /// You can manually change the identifiers to a non-keyword, or use a
+    /// [raw identifier], for example `r#dyn`, to transition to a new edition.
+    ///
+    /// This lint solves the problem automatically. It is "allow" by default
+    /// because the code is perfectly valid in older editions. The [`cargo
+    /// fix`] tool with the `--edition` flag will switch this lint to "warn"
+    /// and automatically apply the suggested fix from the compiler (which is
+    /// to use a raw identifier). This provides a completely automated way to
+    /// update old code for a new edition.
+    ///
+    /// [editions]: https://doc.rust-lang.org/edition-guide/
+    /// [raw identifier]: https://doc.rust-lang.org/reference/identifiers.html
+    /// [`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html
     pub KEYWORD_IDENTS,
     Allow,
     "detects edition keywords being used as an identifier",
@@ -1563,9 +1997,9 @@ impl ExplicitOutlivesRequirements {
             .filter_map(|(i, bound)| {
                 if let hir::GenericBound::Outlives(lifetime) = bound {
                     let is_inferred = match tcx.named_region(lifetime.hir_id) {
-                        Some(Region::Static) if infer_static => inferred_outlives
-                            .iter()
-                            .any(|r| if let ty::ReStatic = r { true } else { false }),
+                        Some(Region::Static) if infer_static => {
+                            inferred_outlives.iter().any(|r| matches!(r, ty::ReStatic))
+                        }
                         Some(Region::EarlyBound(index, ..)) => inferred_outlives.iter().any(|r| {
                             if let ty::ReEarlyBound(ebr) = r { ebr.index == index } else { false }
                         }),
@@ -1657,9 +2091,10 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
             let mut lint_spans = Vec::new();
 
             for param in hir_generics.params {
-                let has_lifetime_bounds = param.bounds.iter().any(|bound| {
-                    if let hir::GenericBound::Outlives(_) = bound { true } else { false }
-                });
+                let has_lifetime_bounds = param
+                    .bounds
+                    .iter()
+                    .any(|bound| matches!(bound, hir::GenericBound::Outlives(_)));
                 if !has_lifetime_bounds {
                     continue;
                 }
@@ -1800,6 +2235,26 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
 }
 
 declare_lint! {
+    /// The `incomplete_features` lint detects unstable features enabled with
+    /// the [`feature` attribute] that may function improperly in some or all
+    /// cases.
+    ///
+    /// [`feature` attribute]: https://doc.rust-lang.org/nightly/unstable-book/
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// #![feature(generic_associated_types)]
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Although it is encouraged for people to experiment with unstable
+    /// features, some of them are known to be incomplete or faulty. This lint
+    /// is a signal that the feature has not yet been finished, and you may
+    /// experience problems with it.
     pub INCOMPLETE_FEATURES,
     Warn,
     "incomplete features that may function improperly in some or all cases"
@@ -1840,6 +2295,36 @@ impl EarlyLintPass for IncompleteFeatures {
 }
 
 declare_lint! {
+    /// The `invalid_value` lint detects creating a value that is not valid,
+    /// such as a NULL reference.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,no_run
+    /// # #![allow(unused)]
+    /// unsafe {
+    ///     let x: &'static i32 = std::mem::zeroed();
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// In some situations the compiler can detect that the code is creating
+    /// an invalid value, which should be avoided.
+    ///
+    /// In particular, this lint will check for improper use of
+    /// [`mem::zeroed`], [`mem::uninitialized`], [`mem::transmute`], and
+    /// [`MaybeUninit::assume_init`] that can cause [undefined behavior]. The
+    /// lint should provide extra information to indicate what the problem is
+    /// and a possible solution.
+    ///
+    /// [`mem::zeroed`]: https://doc.rust-lang.org/std/mem/fn.zeroed.html
+    /// [`mem::uninitialized`]: https://doc.rust-lang.org/std/mem/fn.uninitialized.html
+    /// [`mem::transmute`]: https://doc.rust-lang.org/std/mem/fn.transmute.html
+    /// [`MaybeUninit::assume_init`]: https://doc.rust-lang.org/std/mem/union.MaybeUninit.html#method.assume_init
+    /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
     pub INVALID_VALUE,
     Warn,
     "an invalid value is being created (such as a NULL reference)"
@@ -1878,13 +2363,6 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
 
         /// Determine if this expression is a "dangerous initialization".
         fn is_dangerous_init(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<InitKind> {
-            // `transmute` is inside an anonymous module (the `extern` block?);
-            // `Invalid` represents the empty string and matches that.
-            // FIXME(#66075): use diagnostic items.  Somehow, that does not seem to work
-            // on intrinsics right now.
-            const TRANSMUTE_PATH: &[Symbol] =
-                &[sym::core, sym::intrinsics, kw::Invalid, sym::transmute];
-
             if let hir::ExprKind::Call(ref path_expr, ref args) = expr.kind {
                 // Find calls to `mem::{uninitialized,zeroed}` methods.
                 if let hir::ExprKind::Path(ref qpath) = path_expr.kind {
@@ -1894,7 +2372,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
                         return Some(InitKind::Zeroed);
                     } else if cx.tcx.is_diagnostic_item(sym::mem_uninitialized, def_id) {
                         return Some(InitKind::Uninit);
-                    } else if cx.match_def_path(def_id, TRANSMUTE_PATH) {
+                    } else if cx.tcx.is_diagnostic_item(sym::transmute, def_id) {
                         if is_zero(&args[0]) {
                             return Some(InitKind::Zeroed);
                         }
@@ -1939,13 +2417,13 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
             init: InitKind,
         ) -> Option<InitError> {
             use rustc_middle::ty::TyKind::*;
-            match ty.kind {
+            match ty.kind() {
                 // Primitive types that don't like 0 as a value.
                 Ref(..) => Some(("references must be non-null".to_string(), None)),
                 Adt(..) if ty.is_box() => Some(("`Box` must be non-null".to_string(), None)),
                 FnPtr(..) => Some(("function pointers must be non-null".to_string(), None)),
                 Never => Some(("the `!` type has no valid value".to_string(), None)),
-                RawPtr(tm) if matches!(tm.ty.kind, Dynamic(..)) =>
+                RawPtr(tm) if matches!(tm.ty.kind(), Dynamic(..)) =>
                 // raw ptr to dyn Trait
                 {
                     Some(("the vtable of a wide raw pointer must be non-null".to_string(), None))
@@ -2040,7 +2518,9 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
             // using zeroed or uninitialized memory.
             // We are extremely conservative with what we warn about.
             let conjured_ty = cx.typeck_results().expr_ty(expr);
-            if let Some((msg, span)) = ty_find_init_error(cx.tcx, conjured_ty, init) {
+            if let Some((msg, span)) =
+                with_no_trimmed_paths(|| ty_find_init_error(cx.tcx, conjured_ty, init))
+            {
                 cx.struct_span_lint(INVALID_VALUE, expr.span, |lint| {
                     let mut err = lint.build(&format!(
                         "the type `{}` does not permit {}",
@@ -2069,6 +2549,40 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
 }
 
 declare_lint! {
+    /// The `clashing_extern_declarations` lint detects when an `extern fn`
+    /// has been declared with the same name but different types.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// mod m {
+    ///     extern "C" {
+    ///         fn foo();
+    ///     }
+    /// }
+    ///
+    /// extern "C" {
+    ///     fn foo(_: u32);
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Because two symbols of the same name cannot be resolved to two
+    /// different functions at link time, and one function cannot possibly
+    /// have two types, a clashing extern declaration is almost certainly a
+    /// mistake. Check to make sure that the `extern` definitions are correct
+    /// and equivalent, and possibly consider unifying them in one location.
+    ///
+    /// This lint does not run between crates because a project may have
+    /// dependencies which both rely on the same extern function, but declare
+    /// it in a different (but valid) way. For example, they may both declare
+    /// an opaque type for one or more of the arguments (which would end up
+    /// distinct types), or use types that are valid conversions in the
+    /// language the `extern fn` is defined in. In these cases, the compiler
+    /// can't say that the clashing declaration is incorrect.
     pub CLASHING_EXTERN_DECLARATIONS,
     Warn,
     "detects when an extern fn has been declared with the same name but different types"
@@ -2170,7 +2684,7 @@ impl ClashingExternDeclarations {
             let non_transparent_ty = |ty: Ty<'tcx>| -> Ty<'tcx> {
                 let mut ty = ty;
                 loop {
-                    if let ty::Adt(def, substs) = ty.kind {
+                    if let ty::Adt(def, substs) = *ty.kind() {
                         let is_transparent = def.subst(tcx, substs).repr.transparent();
                         let is_non_null = crate::types::nonnull_optimization_guaranteed(tcx, &def);
                         debug!(
@@ -2180,8 +2694,7 @@ impl ClashingExternDeclarations {
                         if is_transparent && !is_non_null {
                             debug_assert!(def.variants.len() == 1);
                             let v = &def.variants[VariantIdx::new(0)];
-                            ty = v
-                                .transparent_newtype_field(tcx)
+                            ty = transparent_newtype_field(tcx, v)
                                 .expect(
                                     "single-variant transparent structure with zero-sized field",
                                 )
@@ -2209,8 +2722,8 @@ impl ClashingExternDeclarations {
             } else {
                 // Do a full, depth-first comparison between the two.
                 use rustc_middle::ty::TyKind::*;
-                let a_kind = &a.kind;
-                let b_kind = &b.kind;
+                let a_kind = a.kind();
+                let b_kind = b.kind();
 
                 let compare_layouts = |a, b| -> Result<bool, LayoutError<'tcx>> {
                     debug!("compare_layouts({:?}, {:?})", a, b);
@@ -2332,7 +2845,7 @@ impl ClashingExternDeclarations {
                             if is_primitive_or_pointer(other_kind) =>
                         {
                             let (primitive, adt) =
-                                if is_primitive_or_pointer(&a.kind) { (a, b) } else { (b, a) };
+                                if is_primitive_or_pointer(a.kind()) { (a, b) } else { (b, a) };
                             if let Some(ty) = crate::types::repr_nullable_ptr(cx, adt, ckind) {
                                 ty == primitive
                             } else {
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index a6784ffffcd..0265fc323b3 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -31,6 +31,7 @@ use rustc_middle::lint::LintDiagnosticBuilder;
 use rustc_middle::middle::privacy::AccessLevels;
 use rustc_middle::middle::stability;
 use rustc_middle::ty::layout::{LayoutError, TyAndLayout};
+use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{self, print::Printer, subst::GenericArg, Ty, TyCtxt};
 use rustc_session::lint::{add_elided_lifetime_in_path_suggestion, BuiltinLintDiagnostics};
 use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintBuffer, LintId};
@@ -719,6 +720,10 @@ impl<'tcx> LateContext<'tcx> {
     /// Anonymous scopes such as `extern` imports are matched with `kw::Invalid`;
     /// inherent `impl` blocks are matched with the name of the type.
     ///
+    /// Instead of using this method, it is often preferable to instead use
+    /// `rustc_diagnostic_item` or a `lang_item`. This is less prone to errors
+    /// as paths get invalidated if the target definition moves.
+    ///
     /// # Examples
     ///
     /// ```rust,ignore (no context or def id available)
@@ -789,16 +794,18 @@ impl<'tcx> LateContext<'tcx> {
                 trait_ref: Option<ty::TraitRef<'tcx>>,
             ) -> Result<Self::Path, Self::Error> {
                 if trait_ref.is_none() {
-                    if let ty::Adt(def, substs) = self_ty.kind {
+                    if let ty::Adt(def, substs) = self_ty.kind() {
                         return self.print_def_path(def.did, substs);
                     }
                 }
 
                 // This shouldn't ever be needed, but just in case:
-                Ok(vec![match trait_ref {
-                    Some(trait_ref) => Symbol::intern(&format!("{:?}", trait_ref)),
-                    None => Symbol::intern(&format!("<{}>", self_ty)),
-                }])
+                with_no_trimmed_paths(|| {
+                    Ok(vec![match trait_ref {
+                        Some(trait_ref) => Symbol::intern(&format!("{:?}", trait_ref)),
+                        None => Symbol::intern(&format!("<{}>", self_ty)),
+                    }])
+                })
             }
 
             fn path_append_impl(
@@ -812,12 +819,16 @@ impl<'tcx> LateContext<'tcx> {
 
                 // This shouldn't ever be needed, but just in case:
                 path.push(match trait_ref {
-                    Some(trait_ref) => Symbol::intern(&format!(
-                        "<impl {} for {}>",
-                        trait_ref.print_only_trait_path(),
-                        self_ty
-                    )),
-                    None => Symbol::intern(&format!("<impl {}>", self_ty)),
+                    Some(trait_ref) => with_no_trimmed_paths(|| {
+                        Symbol::intern(&format!(
+                            "<impl {} for {}>",
+                            trait_ref.print_only_trait_path(),
+                            self_ty
+                        ))
+                    }),
+                    None => {
+                        with_no_trimmed_paths(|| Symbol::intern(&format!("<impl {}>", self_ty)))
+                    }
                 });
 
                 Ok(path)
diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs
index 100e555f299..c2d98b8e4ad 100644
--- a/compiler/rustc_lint/src/internal.rs
+++ b/compiler/rustc_lint/src/internal.rs
@@ -5,7 +5,9 @@ use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}
 use rustc_ast::{Item, ItemKind};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::Applicability;
+use rustc_hir::def::Res;
 use rustc_hir::{GenericArg, HirId, MutTy, Mutability, Path, PathSegment, QPath, Ty, TyKind};
+use rustc_middle::ty;
 use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
 use rustc_span::hygiene::{ExpnKind, MacroKind};
 use rustc_span::symbol::{sym, Ident, Symbol};
@@ -177,11 +179,31 @@ fn lint_ty_kind_usage(cx: &LateContext<'_>, segment: &PathSegment<'_>) -> bool {
 fn is_ty_or_ty_ctxt(cx: &LateContext<'_>, ty: &Ty<'_>) -> Option<String> {
     if let TyKind::Path(qpath) = &ty.kind {
         if let QPath::Resolved(_, path) = qpath {
-            let did = path.res.opt_def_id()?;
-            if cx.tcx.is_diagnostic_item(sym::Ty, did) {
-                return Some(format!("Ty{}", gen_args(path.segments.last().unwrap())));
-            } else if cx.tcx.is_diagnostic_item(sym::TyCtxt, did) {
-                return Some(format!("TyCtxt{}", gen_args(path.segments.last().unwrap())));
+            match path.res {
+                Res::Def(_, did) => {
+                    if cx.tcx.is_diagnostic_item(sym::Ty, did) {
+                        return Some(format!("Ty{}", gen_args(path.segments.last().unwrap())));
+                    } else if cx.tcx.is_diagnostic_item(sym::TyCtxt, did) {
+                        return Some(format!("TyCtxt{}", gen_args(path.segments.last().unwrap())));
+                    }
+                }
+                // Only lint on `&Ty` and `&TyCtxt` if it is used outside of a trait.
+                Res::SelfTy(None, Some((did, _))) => {
+                    if let ty::Adt(adt, substs) = cx.tcx.type_of(did).kind() {
+                        if cx.tcx.is_diagnostic_item(sym::Ty, adt.did) {
+                            // NOTE: This path is currently unreachable as `Ty<'tcx>` is
+                            // defined as a type alias meaning that `impl<'tcx> Ty<'tcx>`
+                            // is not actually allowed.
+                            //
+                            // I(@lcnr) still kept this branch in so we don't miss this
+                            // if we ever change it in the future.
+                            return Some(format!("Ty<{}>", substs[0]));
+                        } else if cx.tcx.is_diagnostic_item(sym::TyCtxt, adt.did) {
+                            return Some(format!("TyCtxt<{}>", substs[0]));
+                        }
+                    }
+                }
+                _ => (),
             }
         }
     }
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index 0a14b16e274..7f7472d9283 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -25,8 +25,9 @@
 //!
 //! This API is completely unstable and subject to change.
 
-#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
+#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![cfg_attr(test, feature(test))]
+#![feature(array_windows)]
 #![feature(bool_to_option)]
 #![feature(box_syntax)]
 #![feature(crate_visibility_modifier)]
diff --git a/compiler/rustc_lint/src/non_ascii_idents.rs b/compiler/rustc_lint/src/non_ascii_idents.rs
index 2f0b2a8d680..a1c7e47e749 100644
--- a/compiler/rustc_lint/src/non_ascii_idents.rs
+++ b/compiler/rustc_lint/src/non_ascii_idents.rs
@@ -4,6 +4,32 @@ use rustc_data_structures::fx::FxHashMap;
 use rustc_span::symbol::Symbol;
 
 declare_lint! {
+    /// The `non_ascii_idents` lint detects non-ASCII identifiers.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// # #![allow(unused)]
+    /// #![feature(non_ascii_idents)]
+    /// #![deny(non_ascii_idents)]
+    /// fn main() {
+    ///     let föö = 1;
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Currently on stable Rust, identifiers must contain ASCII characters.
+    /// The [`non_ascii_idents`] nightly-only feature allows identifiers to
+    /// contain non-ASCII characters. This lint allows projects that wish to
+    /// retain the limit of only using ASCII characters to switch this lint to
+    /// "forbid" (for example to ease collaboration or for security reasons).
+    /// See [RFC 2457] for more details.
+    ///
+    /// [`non_ascii_idents`]: https://doc.rust-lang.org/nightly/unstable-book/language-features/non-ascii-idents.html
+    /// [RFC 2457]: https://github.com/rust-lang/rfcs/blob/master/text/2457-non-ascii-idents.md
     pub NON_ASCII_IDENTS,
     Allow,
     "detects non-ASCII identifiers",
@@ -11,6 +37,37 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `uncommon_codepoints` lint detects uncommon Unicode codepoints in
+    /// identifiers.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// # #![allow(unused)]
+    /// #![feature(non_ascii_idents)]
+    /// const µ: f64 = 0.000001;
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// With the [`non_ascii_idents`] nightly-only feature enabled,
+    /// identifiers are allowed to use non-ASCII characters. This lint warns
+    /// about using characters which are not commonly used, and may cause
+    /// visual confusion.
+    ///
+    /// This lint is triggered by identifiers that contain a codepoint that is
+    /// not part of the set of "Allowed" codepoints as described by [Unicode®
+    /// Technical Standard #39 Unicode Security Mechanisms Section 3.1 General
+    /// Security Profile for Identifiers][TR39Allowed].
+    ///
+    /// Note that the set of uncommon codepoints may change over time. Beware
+    /// that if you "forbid" this lint that existing code may fail in the
+    /// future.
+    ///
+    /// [`non_ascii_idents`]: https://doc.rust-lang.org/nightly/unstable-book/language-features/non-ascii-idents.html
+    /// [TR39Allowed]: https://www.unicode.org/reports/tr39/#General_Security_Profile
     pub UNCOMMON_CODEPOINTS,
     Warn,
     "detects uncommon Unicode codepoints in identifiers",
@@ -18,6 +75,43 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `confusable_idents` lint detects visually confusable pairs between
+    /// identifiers.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// #![feature(non_ascii_idents)]
+    ///
+    /// // Latin Capital Letter E With Caron
+    /// pub const Ě: i32 = 1;
+    /// // Latin Capital Letter E With Breve
+    /// pub const Ĕ: i32 = 2;
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// With the [`non_ascii_idents`] nightly-only feature enabled,
+    /// identifiers are allowed to use non-ASCII characters. This lint warns
+    /// when different identifiers may appear visually similar, which can
+    /// cause confusion.
+    ///
+    /// The confusable detection algorithm is based on [Unicode® Technical
+    /// Standard #39 Unicode Security Mechanisms Section 4 Confusable
+    /// Detection][TR39Confusable]. For every distinct identifier X execute
+    /// the function `skeleton(X)`. If there exist two distinct identifiers X
+    /// and Y in the same crate where `skeleton(X) = skeleton(Y)` report it.
+    /// The compiler uses the same mechanism to check if an identifier is too
+    /// similar to a keyword.
+    ///
+    /// Note that the set of confusable characters may change over time.
+    /// Beware that if you "forbid" this lint that existing code may fail in
+    /// the future.
+    ///
+    /// [`non_ascii_idents`]: https://doc.rust-lang.org/nightly/unstable-book/language-features/non-ascii-idents.html
+    /// [TR39Confusable]: https://www.unicode.org/reports/tr39/#Confusable_Detection
     pub CONFUSABLE_IDENTS,
     Warn,
     "detects visually confusable pairs between identifiers",
@@ -25,6 +119,41 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `mixed_script_confusables` lint detects visually confusable
+    /// characters in identifiers between different [scripts].
+    ///
+    /// [scripts]: https://en.wikipedia.org/wiki/Script_(Unicode)
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// #![feature(non_ascii_idents)]
+    ///
+    /// // The Japanese katakana character エ can be confused with the Han character 工.
+    /// const エ: &'static str = "アイウ";
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// With the [`non_ascii_idents`] nightly-only feature enabled,
+    /// identifiers are allowed to use non-ASCII characters. This lint warns
+    /// when characters between different scripts may appear visually similar,
+    /// which can cause confusion.
+    ///
+    /// If the crate contains other identifiers in the same script that have
+    /// non-confusable characters, then this lint will *not* be issued. For
+    /// example, if the example given above has another identifier with
+    /// katakana characters (such as `let カタカナ = 123;`), then this indicates
+    /// that you are intentionally using katakana, and it will not warn about
+    /// it.
+    ///
+    /// Note that the set of confusable characters may change over time.
+    /// Beware that if you "forbid" this lint that existing code may fail in
+    /// the future.
+    ///
+    /// [`non_ascii_idents`]: https://doc.rust-lang.org/nightly/unstable-book/language-features/non-ascii-idents.html
     pub MIXED_SCRIPT_CONFUSABLES,
     Warn,
     "detects Unicode scripts whose mixed script confusables codepoints are solely used",
@@ -212,7 +341,8 @@ impl EarlyLintPass for NonAsciiIdents {
                         }
                     }
 
-                    ch_list.sort();
+                    // We sort primitive chars here and can use unstable sort
+                    ch_list.sort_unstable();
                     ch_list.dedup();
                     lint_reports.insert((sp, ch_list), augment_script_set);
                 }
diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs
index f23e8c5e208..b3125f55d4d 100644
--- a/compiler/rustc_lint/src/nonstandard_style.rs
+++ b/compiler/rustc_lint/src/nonstandard_style.rs
@@ -31,6 +31,24 @@ pub fn method_context(cx: &LateContext<'_>, id: hir::HirId) -> MethodLateContext
 }
 
 declare_lint! {
+    /// The `non_camel_case_types` lint detects types, variants, traits and
+    /// type parameters that don't have camel case names.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// struct my_struct;
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// The preferred style for these identifiers is to use "camel case", such
+    /// as `MyStruct`, where the first letter should not be lowercase, and
+    /// should not use underscores between letters. Underscores are allowed at
+    /// the beginning and end of the identifier, as well as between
+    /// non-letters (such as `X86_64`).
     pub NON_CAMEL_CASE_TYPES,
     Warn,
     "types, variants, traits and type parameters should have camel case names"
@@ -52,9 +70,9 @@ fn is_camel_case(name: &str) -> bool {
     // ones (some scripts don't have a concept of upper/lowercase)
     !name.chars().next().unwrap().is_lowercase()
         && !name.contains("__")
-        && !name.chars().collect::<Vec<_>>().windows(2).any(|pair| {
+        && !name.chars().collect::<Vec<_>>().array_windows().any(|&[fst, snd]| {
             // contains a capitalisable character followed by, or preceded by, an underscore
-            char_has_case(pair[0]) && pair[1] == '_' || char_has_case(pair[1]) && pair[0] == '_'
+            char_has_case(fst) && snd == '_' || char_has_case(snd) && fst == '_'
         })
 }
 
@@ -161,6 +179,22 @@ impl EarlyLintPass for NonCamelCaseTypes {
 }
 
 declare_lint! {
+    /// The `non_snake_case` lint detects variables, methods, functions,
+    /// lifetime parameters and modules that don't have snake case names.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// let MY_VALUE = 5;
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// The preferred style for these identifiers is to use "snake case",
+    /// where all the characters are in lowercase, with words separated with a
+    /// single underscore, such as `my_value`.
     pub NON_SNAKE_CASE,
     Warn,
     "variables, methods, functions, lifetime parameters and modules should have snake case names"
@@ -379,6 +413,21 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase {
 }
 
 declare_lint! {
+    /// The `non_upper_case_globals` lint detects static items that don't have
+    /// uppercase identifiers.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// static max_points: i32 = 5;
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// The preferred style is for static item names to use all uppercase
+    /// letters such as `MAX_POINTS`.
     pub NON_UPPER_CASE_GLOBALS,
     Warn,
     "static constants should have uppercase identifiers"
diff --git a/compiler/rustc_lint/src/redundant_semicolon.rs b/compiler/rustc_lint/src/redundant_semicolon.rs
index d4aa4968f25..a31deb87ff0 100644
--- a/compiler/rustc_lint/src/redundant_semicolon.rs
+++ b/compiler/rustc_lint/src/redundant_semicolon.rs
@@ -4,6 +4,21 @@ use rustc_errors::Applicability;
 use rustc_span::Span;
 
 declare_lint! {
+    /// The `redundant_semicolons` lint detects unnecessary trailing
+    /// semicolons.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// let _ = 123;;
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Extra semicolons are not needed, and may be removed to avoid confusion
+    /// and visual clutter.
     pub REDUNDANT_SEMICOLONS,
     Warn,
     "detects unnecessary trailing semicolons"
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index 35c462c24c8..6aa28d04ae1 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -23,18 +23,82 @@ use std::cmp;
 use tracing::debug;
 
 declare_lint! {
+    /// The `unused_comparisons` lint detects comparisons made useless by
+    /// limits of the types involved.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// fn foo(x: u8) {
+    ///     x >= 0;
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// A useless comparison may indicate a mistake, and should be fixed or
+    /// removed.
     UNUSED_COMPARISONS,
     Warn,
     "comparisons made useless by limits of the types involved"
 }
 
 declare_lint! {
+    /// The `overflowing_literals` lint detects literal out of range for its
+    /// type.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// let x: u8 = 1000;
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// It is usually a mistake to use a literal that overflows the type where
+    /// it is used. Either use a literal that is within range, or change the
+    /// type to be within the range of the literal.
     OVERFLOWING_LITERALS,
     Deny,
     "literal out of range for its type"
 }
 
 declare_lint! {
+    /// The `variant_size_differences` lint detects enums with widely varying
+    /// variant sizes.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// #![deny(variant_size_differences)]
+    /// enum En {
+    ///     V0(u8),
+    ///     VBig([u8; 1024]),
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// It can be a mistake to add a variant to an enum that is much larger
+    /// than the other variants, bloating the overall size required for all
+    /// variants. This can impact performance and memory usage. This is
+    /// triggered if one variant is more than 3 times larger than the
+    /// second-largest variant.
+    ///
+    /// Consider placing the large variant's contents on the heap (for example
+    /// via [`Box`]) to keep the overall size of the enum itself down.
+    ///
+    /// This lint is "allow" by default because it can be noisy, and may not be
+    /// an actual problem. Decisions about this should be guided with
+    /// profiling and benchmarking.
+    ///
+    /// [`Box`]: https://doc.rust-lang.org/std/boxed/index.html
     VARIANT_SIZE_DIFFERENCES,
     Allow,
     "detects enums with widely varying variant sizes"
@@ -217,7 +281,7 @@ fn get_type_suggestion(t: Ty<'_>, val: u128, negative: bool) -> Option<&'static
             }
         }
     }
-    match t.kind {
+    match t.kind() {
         ty::Int(i) => find_fit!(i, val, negative,
                       I8 => [U8] => [I16, I32, I64, I128],
                       I16 => [U16] => [I32, I64, I128],
@@ -303,7 +367,7 @@ fn lint_uint_literal<'tcx>(
         if let Node::Expr(par_e) = cx.tcx.hir().get(parent_id) {
             match par_e.kind {
                 hir::ExprKind::Cast(..) => {
-                    if let ty::Char = cx.typeck_results().expr_ty(par_e).kind {
+                    if let ty::Char = cx.typeck_results().expr_ty(par_e).kind() {
                         cx.struct_span_lint(OVERFLOWING_LITERALS, par_e.span, |lint| {
                             lint.build("only `u8` can be cast into `char`")
                                 .span_suggestion(
@@ -354,7 +418,7 @@ fn lint_literal<'tcx>(
     e: &'tcx hir::Expr<'tcx>,
     lit: &hir::Lit,
 ) {
-    match cx.typeck_results().node_type(e.hir_id).kind {
+    match *cx.typeck_results().node_type(e.hir_id).kind() {
         ty::Int(t) => {
             match lit.node {
                 ast::LitKind::Int(v, ast::LitIntType::Signed(_) | ast::LitIntType::Unsuffixed) => {
@@ -450,7 +514,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits {
             // Normalize the binop so that the literal is always on the RHS in
             // the comparison
             let norm_binop = if swap { rev_binop(binop) } else { binop };
-            match cx.typeck_results().node_type(expr.hir_id).kind {
+            match *cx.typeck_results().node_type(expr.hir_id).kind() {
                 ty::Int(int_ty) => {
                     let (min, max) = int_ty_range(int_ty);
                     let lit_val: i128 = match lit.kind {
@@ -495,6 +559,27 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits {
 }
 
 declare_lint! {
+    /// The `improper_ctypes` lint detects incorrect use of types in foreign
+    /// modules.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// extern "C" {
+    ///     static STATIC: String;
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// The compiler has several checks to verify that types used in `extern`
+    /// blocks are safe and follow certain rules to ensure proper
+    /// compatibility with the foreign interfaces. This lint is issued when it
+    /// detects a probable mistake in a definition. The lint usually should
+    /// provide a description of the issue, along with possibly a hint on how
+    /// to resolve it.
     IMPROPER_CTYPES,
     Warn,
     "proper use of libc types in foreign modules"
@@ -503,6 +588,27 @@ declare_lint! {
 declare_lint_pass!(ImproperCTypesDeclarations => [IMPROPER_CTYPES]);
 
 declare_lint! {
+    /// The `improper_ctypes_definitions` lint detects incorrect use of
+    /// [`extern` function] definitions.
+    ///
+    /// [`extern` function]: https://doc.rust-lang.org/reference/items/functions.html#extern-function-qualifier
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// # #![allow(unused)]
+    /// pub extern "C" fn str_type(p: &str) { }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// There are many parameter and return types that may be specified in an
+    /// `extern` function that are not compatible with the given ABI. This
+    /// lint is an alert that these types should not be used. The lint usually
+    /// should provide a description of the issue, along with possibly a hint
+    /// on how to resolve it.
     IMPROPER_CTYPES_DEFINITIONS,
     Warn,
     "proper use of libc types in foreign item definitions"
@@ -533,10 +639,30 @@ crate fn nonnull_optimization_guaranteed<'tcx>(tcx: TyCtxt<'tcx>, def: &ty::AdtD
         .any(|a| tcx.sess.check_name(a, sym::rustc_nonnull_optimization_guaranteed))
 }
 
+/// `repr(transparent)` structs can have a single non-ZST field, this function returns that
+/// field.
+pub fn transparent_newtype_field<'a, 'tcx>(
+    tcx: TyCtxt<'tcx>,
+    variant: &'a ty::VariantDef,
+) -> Option<&'a ty::FieldDef> {
+    let param_env = tcx.param_env(variant.def_id);
+    for field in &variant.fields {
+        let field_ty = tcx.type_of(field.did);
+        let is_zst =
+            tcx.layout_of(param_env.and(field_ty)).map(|layout| layout.is_zst()).unwrap_or(false);
+
+        if !is_zst {
+            return Some(field);
+        }
+    }
+
+    None
+}
+
 /// Is type known to be non-null?
 crate fn ty_is_known_nonnull<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, mode: CItemKind) -> bool {
     let tcx = cx.tcx;
-    match ty.kind {
+    match ty.kind() {
         ty::FnPtr(_) => true,
         ty::Ref(..) => true,
         ty::Adt(def, _) if def.is_box() && matches!(mode, CItemKind::Definition) => true,
@@ -548,7 +674,7 @@ crate fn ty_is_known_nonnull<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, mode: C
             }
 
             for variant in &def.variants {
-                if let Some(field) = variant.transparent_newtype_field(tcx) {
+                if let Some(field) = transparent_newtype_field(cx.tcx, variant) {
                     if ty_is_known_nonnull(cx, field.ty(tcx, substs), mode) {
                         return true;
                     }
@@ -565,11 +691,11 @@ crate fn ty_is_known_nonnull<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, mode: C
 /// If the type passed in was not scalar, returns None.
 fn get_nullable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
     let tcx = cx.tcx;
-    Some(match ty.kind {
+    Some(match *ty.kind() {
         ty::Adt(field_def, field_substs) => {
             let inner_field_ty = {
                 let first_non_zst_ty =
-                    field_def.variants.iter().filter_map(|v| v.transparent_newtype_field(tcx));
+                    field_def.variants.iter().filter_map(|v| transparent_newtype_field(cx.tcx, v));
                 debug_assert_eq!(
                     first_non_zst_ty.clone().count(),
                     1,
@@ -607,7 +733,7 @@ fn get_nullable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'t
 }
 
 /// Check if this enum can be safely exported based on the "nullable pointer optimization". If it
-/// can, return the the type that `ty` can be safely converted to, otherwise return `None`.
+/// can, return the type that `ty` can be safely converted to, otherwise return `None`.
 /// Currently restricted to function pointers, boxes, references, `core::num::NonZero*`,
 /// `core::ptr::NonNull`, and `#[repr(transparent)]` newtypes.
 /// FIXME: This duplicates code in codegen.
@@ -617,7 +743,7 @@ crate fn repr_nullable_ptr<'tcx>(
     ckind: CItemKind,
 ) -> Option<Ty<'tcx>> {
     debug!("is_repr_nullable_ptr(cx, ty = {:?})", ty);
-    if let ty::Adt(ty_def, substs) = ty.kind {
+    if let ty::Adt(ty_def, substs) = ty.kind() {
         if ty_def.variants.len() != 2 {
             return None;
         }
@@ -667,7 +793,7 @@ crate fn repr_nullable_ptr<'tcx>(
 impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
     /// Check if the type is array and emit an unsafe type lint.
     fn check_for_array_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool {
-        if let ty::Array(..) = ty.kind {
+        if let ty::Array(..) = ty.kind() {
             self.emit_ffi_unsafe_type_lint(
                 ty,
                 sp,
@@ -710,7 +836,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
         if def.repr.transparent() {
             // Can assume that only one field is not a ZST, so only check
             // that field's type for FFI-safety.
-            if let Some(field) = variant.transparent_newtype_field(self.cx.tcx) {
+            if let Some(field) = transparent_newtype_field(self.cx.tcx, variant) {
                 self.check_field_type_for_ffi(cache, field, substs)
             } else {
                 bug!("malformed transparent type");
@@ -755,7 +881,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
             return FfiSafe;
         }
 
-        match ty.kind {
+        match ty.kind() {
             ty::Adt(def, _) if def.is_box() && matches!(self.mode, CItemKind::Definition) => {
                 FfiSafe
             }
@@ -994,7 +1120,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
                 diag.help(help);
             }
             diag.note(note);
-            if let ty::Adt(def, _) = ty.kind {
+            if let ty::Adt(def, _) = ty.kind() {
                 if let Some(sp) = self.cx.tcx.hir().span_if_local(def.did) {
                     diag.span_note(sp, "the type is defined here");
                 }
@@ -1011,7 +1137,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
 
         impl<'a, 'tcx> ty::fold::TypeVisitor<'tcx> for ProhibitOpaqueTypes<'a, 'tcx> {
             fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool {
-                match ty.kind {
+                match ty.kind() {
                     ty::Opaque(..) => {
                         self.ty = Some(ty);
                         true
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index c793e81ebe3..1e8c30071e7 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -20,6 +20,29 @@ use rustc_span::{BytePos, Span, DUMMY_SP};
 use tracing::debug;
 
 declare_lint! {
+    /// The `unused_must_use` lint detects unused result of a type flagged as
+    /// `#[must_use]`.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// fn returns_result() -> Result<(), ()> {
+    ///     Ok(())
+    /// }
+    ///
+    /// fn main() {
+    ///     returns_result();
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// The `#[must_use]` attribute is an indicator that it is a mistake to
+    /// ignore the value. See [the reference] for more details.
+    ///
+    /// [the reference]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute
     pub UNUSED_MUST_USE,
     Warn,
     "unused result of a type flagged as `#[must_use]`",
@@ -27,6 +50,39 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `unused_results` lint checks for the unused result of an
+    /// expression in a statement.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// #![deny(unused_results)]
+    /// fn foo<T>() -> T { panic!() }
+    ///
+    /// fn main() {
+    ///     foo::<usize>();
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Ignoring the return value of a function may indicate a mistake. In
+    /// cases were it is almost certain that the result should be used, it is
+    /// recommended to annotate the function with the [`must_use` attribute].
+    /// Failure to use such a return value will trigger the [`unused_must_use`
+    /// lint] which is warn-by-default. The `unused_results` lint is
+    /// essentially the same, but triggers for *all* return values.
+    ///
+    /// This lint is "allow" by default because it can be noisy, and may not be
+    /// an actual problem. For example, calling the `remove` method of a `Vec`
+    /// or `HashMap` returns the previous value, which you may not care about.
+    /// Using this lint would require explicitly ignoring or discarding such
+    /// values.
+    ///
+    /// [`must_use` attribute]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute
+    /// [`unused_must_use` lint]: warn-by-default.html#unused-must-use
     pub UNUSED_RESULTS,
     Allow,
     "unused result of an expression in a statement"
@@ -135,7 +191,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
 
             let plural_suffix = pluralize!(plural_len);
 
-            match ty.kind {
+            match *ty.kind() {
                 ty::Adt(..) if ty.is_box() => {
                     let boxed_ty = ty.boxed_ty();
                     let descr_pre = &format!("{}boxed ", descr_pre);
@@ -265,6 +321,21 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
 }
 
 declare_lint! {
+    /// The `path_statements` lint detects path statements with no effect.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// let x = 42;
+    ///
+    /// x;
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// It is usually a mistake to have a statement that has no effect.
     pub PATH_STATEMENTS,
     Warn,
     "path statements with no effect"
@@ -635,6 +706,21 @@ trait UnusedDelimLint {
 }
 
 declare_lint! {
+    /// The `unused_parens` lint detects `if`, `match`, `while` and `return`
+    /// with parentheses; they do not need them.
+    ///
+    /// ### Examples
+    ///
+    /// ```rust
+    /// if(true) {}
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// The parenthesis are not needed, and should be removed. This is the
+    /// preferred style for writing these expressions.
     pub(super) UNUSED_PARENS,
     Warn,
     "`if`, `match`, `while` and `return` do not need parentheses"
@@ -808,6 +894,23 @@ impl EarlyLintPass for UnusedParens {
 }
 
 declare_lint! {
+    /// The `unused_braces` lint detects unnecessary braces around an
+    /// expression.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// if { true } {
+    ///     // ...
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// The braces are not needed, and should be removed. This is the
+    /// preferred style for writing these expressions.
     pub(super) UNUSED_BRACES,
     Warn,
     "unnecessary braces around an expression"
@@ -929,6 +1032,30 @@ impl EarlyLintPass for UnusedBraces {
 }
 
 declare_lint! {
+    /// The `unused_import_braces` lint catches unnecessary braces around an
+    /// imported item.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// #![deny(unused_import_braces)]
+    /// use test::{A};
+    ///
+    /// pub mod test {
+    ///     pub struct A;
+    /// }
+    /// # fn main() {}
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// If there is only a single item, then remove the braces (`use test::A;`
+    /// for example).
+    ///
+    /// This lint is "allow" by default because it is only enforcing a
+    /// stylistic choice.
     UNUSED_IMPORT_BRACES,
     Allow,
     "unnecessary braces around an imported item"
@@ -978,6 +1105,25 @@ impl EarlyLintPass for UnusedImportBraces {
 }
 
 declare_lint! {
+    /// The `unused_allocation` lint detects unnecessary allocations that can
+    /// be eliminated.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// #![feature(box_syntax)]
+    /// fn main() {
+    ///     let a = (box [1,2,3]).len();
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// When a `box` expression is immediately coerced to a reference, then
+    /// the allocation is unnecessary, and a reference (using `&` or `&mut`)
+    /// should be used instead to avoid the allocation.
     pub(super) UNUSED_ALLOCATION,
     Warn,
     "detects unnecessary allocations that can be eliminated"
diff --git a/compiler/rustc_llvm/Cargo.toml b/compiler/rustc_llvm/Cargo.toml
new file mode 100644
index 00000000000..e29af053289
--- /dev/null
+++ b/compiler/rustc_llvm/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+authors = ["The Rust Project Developers"]
+name = "rustc_llvm"
+version = "0.0.0"
+edition = "2018"
+
+[features]
+static-libstdcpp = []
+emscripten = []
+
+[dependencies]
+libc = "0.2.73"
+
+[build-dependencies]
+build_helper = { path = "../../src/build_helper" }
+cc = "1.0.60"
diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs
new file mode 100644
index 00000000000..7f1e5cf336a
--- /dev/null
+++ b/compiler/rustc_llvm/build.rs
@@ -0,0 +1,322 @@
+use std::env;
+use std::path::{Path, PathBuf};
+use std::process::Command;
+
+use build_helper::{output, tracked_env_var_os};
+
+fn detect_llvm_link() -> (&'static str, &'static str) {
+    // Force the link mode we want, preferring static by default, but
+    // possibly overridden by `configure --enable-llvm-link-shared`.
+    if tracked_env_var_os("LLVM_LINK_SHARED").is_some() {
+        ("dylib", "--link-shared")
+    } else {
+        ("static", "--link-static")
+    }
+}
+
+fn main() {
+    if tracked_env_var_os("RUST_CHECK").is_some() {
+        // If we're just running `check`, there's no need for LLVM to be built.
+        return;
+    }
+
+    build_helper::restore_library_path();
+
+    let target = env::var("TARGET").expect("TARGET was not set");
+    let llvm_config =
+        tracked_env_var_os("LLVM_CONFIG").map(|x| Some(PathBuf::from(x))).unwrap_or_else(|| {
+            if let Some(dir) = tracked_env_var_os("CARGO_TARGET_DIR").map(PathBuf::from) {
+                let to_test = dir
+                    .parent()
+                    .unwrap()
+                    .parent()
+                    .unwrap()
+                    .join(&target)
+                    .join("llvm/bin/llvm-config");
+                if Command::new(&to_test).output().is_ok() {
+                    return Some(to_test);
+                }
+            }
+            None
+        });
+
+    if let Some(llvm_config) = &llvm_config {
+        println!("cargo:rerun-if-changed={}", llvm_config.display());
+    }
+    let llvm_config = llvm_config.unwrap_or_else(|| PathBuf::from("llvm-config"));
+
+    // Test whether we're cross-compiling LLVM. This is a pretty rare case
+    // currently where we're producing an LLVM for a different platform than
+    // what this build script is currently running on.
+    //
+    // In that case, there's no guarantee that we can actually run the target,
+    // so the build system works around this by giving us the LLVM_CONFIG for
+    // the host platform. This only really works if the host LLVM and target
+    // LLVM are compiled the same way, but for us that's typically the case.
+    //
+    // We *want* detect this cross compiling situation by asking llvm-config
+    // what its host-target is. If that's not the TARGET, then we're cross
+    // compiling. Unfortunately `llvm-config` seems either be buggy, or we're
+    // misconfiguring it, because the `i686-pc-windows-gnu` build of LLVM will
+    // report itself with a `--host-target` of `x86_64-pc-windows-gnu`. This
+    // tricks us into thinking we're doing a cross build when we aren't, so
+    // havoc ensues.
+    //
+    // In any case, if we're cross compiling, this generally just means that we
+    // can't trust all the output of llvm-config because it might be targeted
+    // for the host rather than the target. As a result a bunch of blocks below
+    // are gated on `if !is_crossed`
+    let target = env::var("TARGET").expect("TARGET was not set");
+    let host = env::var("HOST").expect("HOST was not set");
+    let is_crossed = target != host;
+
+    let mut optional_components = vec![
+        "x86",
+        "arm",
+        "aarch64",
+        "amdgpu",
+        "avr",
+        "mips",
+        "powerpc",
+        "systemz",
+        "jsbackend",
+        "webassembly",
+        "msp430",
+        "sparc",
+        "nvptx",
+        "hexagon",
+    ];
+
+    let mut version_cmd = Command::new(&llvm_config);
+    version_cmd.arg("--version");
+    let version_output = output(&mut version_cmd);
+    let mut parts = version_output.split('.').take(2).filter_map(|s| s.parse::<u32>().ok());
+    let (major, _minor) = if let (Some(major), Some(minor)) = (parts.next(), parts.next()) {
+        (major, minor)
+    } else {
+        (6, 0)
+    };
+
+    if major > 6 {
+        optional_components.push("riscv");
+    }
+
+    let required_components = &[
+        "ipo",
+        "bitreader",
+        "bitwriter",
+        "linker",
+        "asmparser",
+        "lto",
+        "coverage",
+        "instrumentation",
+    ];
+
+    let components = output(Command::new(&llvm_config).arg("--components"));
+    let mut components = components.split_whitespace().collect::<Vec<_>>();
+    components.retain(|c| optional_components.contains(c) || required_components.contains(c));
+
+    for component in required_components {
+        if !components.contains(component) {
+            panic!("require llvm component {} but wasn't found", component);
+        }
+    }
+
+    for component in components.iter() {
+        println!("cargo:rustc-cfg=llvm_component=\"{}\"", component);
+    }
+
+    if major >= 9 {
+        println!("cargo:rustc-cfg=llvm_has_msp430_asm_parser");
+    }
+
+    // Link in our own LLVM shims, compiled with the same flags as LLVM
+    let mut cmd = Command::new(&llvm_config);
+    cmd.arg("--cxxflags");
+    let cxxflags = output(&mut cmd);
+    let mut cfg = cc::Build::new();
+    cfg.warnings(false);
+    for flag in cxxflags.split_whitespace() {
+        // Ignore flags like `-m64` when we're doing a cross build
+        if is_crossed && flag.starts_with("-m") {
+            continue;
+        }
+
+        if flag.starts_with("-flto") {
+            continue;
+        }
+
+        // -Wdate-time is not supported by the netbsd cross compiler
+        if is_crossed && target.contains("netbsd") && flag.contains("date-time") {
+            continue;
+        }
+
+        // Include path contains host directory, replace it with target
+        if is_crossed && flag.starts_with("-I") {
+            cfg.flag(&flag.replace(&host, &target));
+            continue;
+        }
+
+        cfg.flag(flag);
+    }
+
+    for component in &components {
+        let mut flag = String::from("LLVM_COMPONENT_");
+        flag.push_str(&component.to_uppercase());
+        cfg.define(&flag, None);
+    }
+
+    if tracked_env_var_os("LLVM_RUSTLLVM").is_some() {
+        cfg.define("LLVM_RUSTLLVM", None);
+    }
+
+    if tracked_env_var_os("LLVM_NDEBUG").is_some() {
+        cfg.define("NDEBUG", None);
+        cfg.debug(false);
+    }
+
+    build_helper::rerun_if_changed_anything_in_dir(Path::new("llvm-wrapper"));
+    cfg.file("llvm-wrapper/PassWrapper.cpp")
+        .file("llvm-wrapper/RustWrapper.cpp")
+        .file("llvm-wrapper/ArchiveWrapper.cpp")
+        .file("llvm-wrapper/CoverageMappingWrapper.cpp")
+        .file("llvm-wrapper/Linker.cpp")
+        .cpp(true)
+        .cpp_link_stdlib(None) // we handle this below
+        .compile("llvm-wrapper");
+
+    let (llvm_kind, llvm_link_arg) = detect_llvm_link();
+
+    // Link in all LLVM libraries, if we're using the "wrong" llvm-config then
+    // we don't pick up system libs because unfortunately they're for the host
+    // of llvm-config, not the target that we're attempting to link.
+    let mut cmd = Command::new(&llvm_config);
+    cmd.arg(llvm_link_arg).arg("--libs");
+
+    if !is_crossed {
+        cmd.arg("--system-libs");
+    } else if target.contains("windows-gnu") {
+        println!("cargo:rustc-link-lib=shell32");
+        println!("cargo:rustc-link-lib=uuid");
+    } else if target.contains("netbsd") || target.contains("haiku") {
+        println!("cargo:rustc-link-lib=z");
+    }
+    cmd.args(&components);
+
+    for lib in output(&mut cmd).split_whitespace() {
+        let name = if lib.starts_with("-l") {
+            &lib[2..]
+        } else if lib.starts_with('-') {
+            &lib[1..]
+        } else if Path::new(lib).exists() {
+            // On MSVC llvm-config will print the full name to libraries, but
+            // we're only interested in the name part
+            let name = Path::new(lib).file_name().unwrap().to_str().unwrap();
+            name.trim_end_matches(".lib")
+        } else if lib.ends_with(".lib") {
+            // Some MSVC libraries just come up with `.lib` tacked on, so chop
+            // that off
+            lib.trim_end_matches(".lib")
+        } else {
+            continue;
+        };
+
+        // Don't need or want this library, but LLVM's CMake build system
+        // doesn't provide a way to disable it, so filter it here even though we
+        // may or may not have built it. We don't reference anything from this
+        // library and it otherwise may just pull in extra dependencies on
+        // libedit which we don't want
+        if name == "LLVMLineEditor" {
+            continue;
+        }
+
+        let kind = if name.starts_with("LLVM") { llvm_kind } else { "dylib" };
+        println!("cargo:rustc-link-lib={}={}", kind, name);
+    }
+
+    // LLVM ldflags
+    //
+    // If we're a cross-compile of LLVM then unfortunately we can't trust these
+    // ldflags (largely where all the LLVM libs are located). Currently just
+    // hack around this by replacing the host triple with the target and pray
+    // that those -L directories are the same!
+    let mut cmd = Command::new(&llvm_config);
+    cmd.arg(llvm_link_arg).arg("--ldflags");
+    for lib in output(&mut cmd).split_whitespace() {
+        if is_crossed {
+            if lib.starts_with("-LIBPATH:") {
+                println!("cargo:rustc-link-search=native={}", lib[9..].replace(&host, &target));
+            } else if lib.starts_with("-L") {
+                println!("cargo:rustc-link-search=native={}", lib[2..].replace(&host, &target));
+            }
+        } else if lib.starts_with("-LIBPATH:") {
+            println!("cargo:rustc-link-search=native={}", &lib[9..]);
+        } else if lib.starts_with("-l") {
+            println!("cargo:rustc-link-lib={}", &lib[2..]);
+        } else if lib.starts_with("-L") {
+            println!("cargo:rustc-link-search=native={}", &lib[2..]);
+        }
+    }
+
+    // Some LLVM linker flags (-L and -l) may be needed even when linking
+    // rustc_llvm, for example when using static libc++, we may need to
+    // manually specify the library search path and -ldl -lpthread as link
+    // dependencies.
+    let llvm_linker_flags = tracked_env_var_os("LLVM_LINKER_FLAGS");
+    if let Some(s) = llvm_linker_flags {
+        for lib in s.into_string().unwrap().split_whitespace() {
+            if lib.starts_with("-l") {
+                println!("cargo:rustc-link-lib={}", &lib[2..]);
+            } else if lib.starts_with("-L") {
+                println!("cargo:rustc-link-search=native={}", &lib[2..]);
+            }
+        }
+    }
+
+    let llvm_static_stdcpp = tracked_env_var_os("LLVM_STATIC_STDCPP");
+    let llvm_use_libcxx = tracked_env_var_os("LLVM_USE_LIBCXX");
+
+    let stdcppname = if target.contains("openbsd") {
+        if target.contains("sparc64") { "estdc++" } else { "c++" }
+    } else if target.contains("freebsd") {
+        "c++"
+    } else if target.contains("darwin") {
+        "c++"
+    } else if target.contains("netbsd") && llvm_static_stdcpp.is_some() {
+        // NetBSD uses a separate library when relocation is required
+        "stdc++_pic"
+    } else if llvm_use_libcxx.is_some() {
+        "c++"
+    } else {
+        "stdc++"
+    };
+
+    // RISC-V requires libatomic for sub-word atomic operations
+    if target.starts_with("riscv") {
+        println!("cargo:rustc-link-lib=atomic");
+    }
+
+    // C++ runtime library
+    if !target.contains("msvc") {
+        if let Some(s) = llvm_static_stdcpp {
+            assert!(!cxxflags.contains("stdlib=libc++"));
+            let path = PathBuf::from(s);
+            println!("cargo:rustc-link-search=native={}", path.parent().unwrap().display());
+            if target.contains("windows") {
+                println!("cargo:rustc-link-lib=static-nobundle={}", stdcppname);
+            } else {
+                println!("cargo:rustc-link-lib=static={}", stdcppname);
+            }
+        } else if cxxflags.contains("stdlib=libc++") {
+            println!("cargo:rustc-link-lib=c++");
+        } else {
+            println!("cargo:rustc-link-lib={}", stdcppname);
+        }
+    }
+
+    // Libstdc++ depends on pthread which Rust doesn't link on MinGW
+    // since nothing else requires it.
+    if target.contains("windows-gnu") {
+        println!("cargo:rustc-link-lib=static-nobundle=pthread");
+    }
+}
diff --git a/compiler/rustc_llvm/llvm-wrapper/.editorconfig b/compiler/rustc_llvm/llvm-wrapper/.editorconfig
new file mode 100644
index 00000000000..865cd45f708
--- /dev/null
+++ b/compiler/rustc_llvm/llvm-wrapper/.editorconfig
@@ -0,0 +1,6 @@
+[*.{h,cpp}]
+end_of_line = lf
+insert_final_newline = true
+charset = utf-8
+indent_style = space
+indent_size = 2
diff --git a/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp
new file mode 100644
index 00000000000..2797fe8df4a
--- /dev/null
+++ b/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp
@@ -0,0 +1,226 @@
+#include "LLVMWrapper.h"
+
+#include "llvm/Object/Archive.h"
+#include "llvm/Object/ArchiveWriter.h"
+#include "llvm/Support/Path.h"
+
+using namespace llvm;
+using namespace llvm::object;
+
+struct RustArchiveMember {
+  const char *Filename;
+  const char *Name;
+  Archive::Child Child;
+
+  RustArchiveMember()
+      : Filename(nullptr), Name(nullptr),
+        Child(nullptr, nullptr, nullptr)
+  {
+  }
+  ~RustArchiveMember() {}
+};
+
+struct RustArchiveIterator {
+  bool First;
+  Archive::child_iterator Cur;
+  Archive::child_iterator End;
+  std::unique_ptr<Error> Err;
+
+  RustArchiveIterator(Archive::child_iterator Cur, Archive::child_iterator End,
+      std::unique_ptr<Error> Err)
+    : First(true),
+      Cur(Cur),
+      End(End),
+      Err(std::move(Err)) {}
+};
+
+enum class LLVMRustArchiveKind {
+  GNU,
+  BSD,
+  DARWIN,
+  COFF,
+};
+
+static Archive::Kind fromRust(LLVMRustArchiveKind Kind) {
+  switch (Kind) {
+  case LLVMRustArchiveKind::GNU:
+    return Archive::K_GNU;
+  case LLVMRustArchiveKind::BSD:
+    return Archive::K_BSD;
+  case LLVMRustArchiveKind::DARWIN:
+    return Archive::K_DARWIN;
+  case LLVMRustArchiveKind::COFF:
+    return Archive::K_COFF;
+  default:
+    report_fatal_error("Bad ArchiveKind.");
+  }
+}
+
+typedef OwningBinary<Archive> *LLVMRustArchiveRef;
+typedef RustArchiveMember *LLVMRustArchiveMemberRef;
+typedef Archive::Child *LLVMRustArchiveChildRef;
+typedef Archive::Child const *LLVMRustArchiveChildConstRef;
+typedef RustArchiveIterator *LLVMRustArchiveIteratorRef;
+
+extern "C" LLVMRustArchiveRef LLVMRustOpenArchive(char *Path) {
+  ErrorOr<std::unique_ptr<MemoryBuffer>> BufOr =
+      MemoryBuffer::getFile(Path, -1, false);
+  if (!BufOr) {
+    LLVMRustSetLastError(BufOr.getError().message().c_str());
+    return nullptr;
+  }
+
+  Expected<std::unique_ptr<Archive>> ArchiveOr =
+      Archive::create(BufOr.get()->getMemBufferRef());
+
+  if (!ArchiveOr) {
+    LLVMRustSetLastError(toString(ArchiveOr.takeError()).c_str());
+    return nullptr;
+  }
+
+  OwningBinary<Archive> *Ret = new OwningBinary<Archive>(
+      std::move(ArchiveOr.get()), std::move(BufOr.get()));
+
+  return Ret;
+}
+
+extern "C" void LLVMRustDestroyArchive(LLVMRustArchiveRef RustArchive) {
+  delete RustArchive;
+}
+
+extern "C" LLVMRustArchiveIteratorRef
+LLVMRustArchiveIteratorNew(LLVMRustArchiveRef RustArchive) {
+  Archive *Archive = RustArchive->getBinary();
+#if LLVM_VERSION_GE(10, 0)
+  std::unique_ptr<Error> Err = std::make_unique<Error>(Error::success());
+#else
+  std::unique_ptr<Error> Err = llvm::make_unique<Error>(Error::success());
+#endif
+  auto Cur = Archive->child_begin(*Err);
+  if (*Err) {
+    LLVMRustSetLastError(toString(std::move(*Err)).c_str());
+    return nullptr;
+  }
+  auto End = Archive->child_end();
+  return new RustArchiveIterator(Cur, End, std::move(Err));
+}
+
+extern "C" LLVMRustArchiveChildConstRef
+LLVMRustArchiveIteratorNext(LLVMRustArchiveIteratorRef RAI) {
+  if (RAI->Cur == RAI->End)
+    return nullptr;
+
+  // Advancing the iterator validates the next child, and this can
+  // uncover an error. LLVM requires that we check all Errors,
+  // so we only advance the iterator if we actually need to fetch
+  // the next child.
+  // This means we must not advance the iterator in the *first* call,
+  // but instead advance it *before* fetching the child in all later calls.
+  if (!RAI->First) {
+    ++RAI->Cur;
+    if (*RAI->Err) {
+      LLVMRustSetLastError(toString(std::move(*RAI->Err)).c_str());
+      return nullptr;
+    }
+  } else {
+    RAI->First = false;
+  }
+
+  if (RAI->Cur == RAI->End)
+    return nullptr;
+
+  const Archive::Child &Child = *RAI->Cur.operator->();
+  Archive::Child *Ret = new Archive::Child(Child);
+
+  return Ret;
+}
+
+extern "C" void LLVMRustArchiveChildFree(LLVMRustArchiveChildRef Child) {
+  delete Child;
+}
+
+extern "C" void LLVMRustArchiveIteratorFree(LLVMRustArchiveIteratorRef RAI) {
+  delete RAI;
+}
+
+extern "C" const char *
+LLVMRustArchiveChildName(LLVMRustArchiveChildConstRef Child, size_t *Size) {
+  Expected<StringRef> NameOrErr = Child->getName();
+  if (!NameOrErr) {
+    // rustc_codegen_llvm currently doesn't use this error string, but it might be
+    // useful in the future, and in the mean time this tells LLVM that the
+    // error was not ignored and that it shouldn't abort the process.
+    LLVMRustSetLastError(toString(NameOrErr.takeError()).c_str());
+    return nullptr;
+  }
+  StringRef Name = NameOrErr.get();
+  *Size = Name.size();
+  return Name.data();
+}
+
+extern "C" const char *LLVMRustArchiveChildData(LLVMRustArchiveChildRef Child,
+                                                size_t *Size) {
+  StringRef Buf;
+  Expected<StringRef> BufOrErr = Child->getBuffer();
+  if (!BufOrErr) {
+    LLVMRustSetLastError(toString(BufOrErr.takeError()).c_str());
+    return nullptr;
+  }
+  Buf = BufOrErr.get();
+  *Size = Buf.size();
+  return Buf.data();
+}
+
+extern "C" LLVMRustArchiveMemberRef
+LLVMRustArchiveMemberNew(char *Filename, char *Name,
+                         LLVMRustArchiveChildRef Child) {
+  RustArchiveMember *Member = new RustArchiveMember;
+  Member->Filename = Filename;
+  Member->Name = Name;
+  if (Child)
+    Member->Child = *Child;
+  return Member;
+}
+
+extern "C" void LLVMRustArchiveMemberFree(LLVMRustArchiveMemberRef Member) {
+  delete Member;
+}
+
+extern "C" LLVMRustResult
+LLVMRustWriteArchive(char *Dst, size_t NumMembers,
+                     const LLVMRustArchiveMemberRef *NewMembers,
+                     bool WriteSymbtab, LLVMRustArchiveKind RustKind) {
+
+  std::vector<NewArchiveMember> Members;
+  auto Kind = fromRust(RustKind);
+
+  for (size_t I = 0; I < NumMembers; I++) {
+    auto Member = NewMembers[I];
+    assert(Member->Name);
+    if (Member->Filename) {
+      Expected<NewArchiveMember> MOrErr =
+          NewArchiveMember::getFile(Member->Filename, true);
+      if (!MOrErr) {
+        LLVMRustSetLastError(toString(MOrErr.takeError()).c_str());
+        return LLVMRustResult::Failure;
+      }
+      MOrErr->MemberName = sys::path::filename(MOrErr->MemberName);
+      Members.push_back(std::move(*MOrErr));
+    } else {
+      Expected<NewArchiveMember> MOrErr =
+          NewArchiveMember::getOldMember(Member->Child, true);
+      if (!MOrErr) {
+        LLVMRustSetLastError(toString(MOrErr.takeError()).c_str());
+        return LLVMRustResult::Failure;
+      }
+      Members.push_back(std::move(*MOrErr));
+    }
+  }
+
+  auto Result = writeArchive(Dst, Members, WriteSymbtab, Kind, true, false);
+  if (!Result)
+    return LLVMRustResult::Success;
+  LLVMRustSetLastError(toString(std::move(Result)).c_str());
+
+  return LLVMRustResult::Failure;
+}
diff --git a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp
new file mode 100644
index 00000000000..2b1143a4ecf
--- /dev/null
+++ b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp
@@ -0,0 +1,70 @@
+#include "LLVMWrapper.h"
+#include "llvm/ProfileData/Coverage/CoverageMapping.h"
+#include "llvm/ProfileData/Coverage/CoverageMappingWriter.h"
+#include "llvm/ProfileData/InstrProf.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/Support/LEB128.h"
+
+#include <iostream>
+
+using namespace llvm;
+
+extern "C" void LLVMRustCoverageWriteFilenamesSectionToBuffer(
+    const char* const Filenames[],
+    size_t FilenamesLen,
+    RustStringRef BufferOut) {
+  // LLVM 11's CoverageFilenamesSectionWriter uses its new `Version4` format,
+  // so we're manually writing the `Version3` format ourselves.
+  RawRustStringOstream OS(BufferOut);
+  encodeULEB128(FilenamesLen, OS);
+  for (size_t i = 0; i < FilenamesLen; i++) {
+    StringRef Filename(Filenames[i]);
+    encodeULEB128(Filename.size(), OS);
+    OS << Filename;
+  }
+}
+
+extern "C" void LLVMRustCoverageWriteMappingToBuffer(
+    const unsigned *VirtualFileMappingIDs,
+    unsigned NumVirtualFileMappingIDs,
+    const coverage::CounterExpression *Expressions,
+    unsigned NumExpressions,
+    coverage::CounterMappingRegion *MappingRegions,
+    unsigned NumMappingRegions,
+    RustStringRef BufferOut) {
+  auto CoverageMappingWriter = coverage::CoverageMappingWriter(
+      makeArrayRef(VirtualFileMappingIDs, NumVirtualFileMappingIDs),
+      makeArrayRef(Expressions, NumExpressions),
+      makeMutableArrayRef(MappingRegions, NumMappingRegions));
+  RawRustStringOstream OS(BufferOut);
+  CoverageMappingWriter.write(OS);
+}
+
+extern "C" LLVMValueRef LLVMRustCoverageCreatePGOFuncNameVar(LLVMValueRef F, const char *FuncName) {
+  StringRef FuncNameRef(FuncName);
+  return wrap(createPGOFuncNameVar(*cast<Function>(unwrap(F)), FuncNameRef));
+}
+
+extern "C" uint64_t LLVMRustCoverageComputeHash(const char *Name) {
+  StringRef NameRef(Name);
+  return IndexedInstrProf::ComputeHash(NameRef);
+}
+
+extern "C" void LLVMRustCoverageWriteSectionNameToString(LLVMModuleRef M,
+                                                         RustStringRef Str) {
+  Triple TargetTriple(unwrap(M)->getTargetTriple());
+  auto name = getInstrProfSectionName(IPSK_covmap,
+                                      TargetTriple.getObjectFormat());
+  RawRustStringOstream OS(Str);
+  OS << name;
+}
+
+extern "C" void LLVMRustCoverageWriteMappingVarNameToString(RustStringRef Str) {
+  auto name = getCoverageMappingVarName();
+  RawRustStringOstream OS(Str);
+  OS << name;
+}
+
+extern "C" uint32_t LLVMRustCoverageMappingVersion() {
+  return coverage::CovMapVersion::Version3;
+}
diff --git a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h
new file mode 100644
index 00000000000..57b8664d3b6
--- /dev/null
+++ b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h
@@ -0,0 +1,115 @@
+#include "llvm-c/BitReader.h"
+#include "llvm-c/Core.h"
+#include "llvm-c/Object.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Analysis/Lint.h"
+#include "llvm/Analysis/Passes.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/InlineAsm.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/DynamicLibrary.h"
+#include "llvm/Support/FormattedStream.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/Memory.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/Timer.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Target/TargetMachine.h"
+#include "llvm/Target/TargetOptions.h"
+#include "llvm/Transforms/IPO.h"
+#include "llvm/Transforms/Instrumentation.h"
+#include "llvm/Transforms/Scalar.h"
+#include "llvm/Transforms/Vectorize.h"
+
+#define LLVM_VERSION_GE(major, minor)                                          \
+  (LLVM_VERSION_MAJOR > (major) ||                                             \
+   LLVM_VERSION_MAJOR == (major) && LLVM_VERSION_MINOR >= (minor))
+
+#define LLVM_VERSION_EQ(major, minor)                                          \
+  (LLVM_VERSION_MAJOR == (major) && LLVM_VERSION_MINOR == (minor))
+
+#define LLVM_VERSION_LE(major, minor)                                          \
+  (LLVM_VERSION_MAJOR < (major) ||                                             \
+   LLVM_VERSION_MAJOR == (major) && LLVM_VERSION_MINOR <= (minor))
+
+#define LLVM_VERSION_LT(major, minor) (!LLVM_VERSION_GE((major), (minor)))
+
+#include "llvm/IR/LegacyPassManager.h"
+
+#include "llvm/Bitcode/BitcodeReader.h"
+#include "llvm/Bitcode/BitcodeWriter.h"
+
+#include "llvm/IR/DIBuilder.h"
+#include "llvm/IR/DebugInfo.h"
+#include "llvm/IR/IRPrintingPasses.h"
+#include "llvm/Linker/Linker.h"
+
+extern "C" void LLVMRustSetLastError(const char *);
+
+enum class LLVMRustResult { Success, Failure };
+
+enum LLVMRustAttribute {
+  AlwaysInline = 0,
+  ByVal = 1,
+  Cold = 2,
+  InlineHint = 3,
+  MinSize = 4,
+  Naked = 5,
+  NoAlias = 6,
+  NoCapture = 7,
+  NoInline = 8,
+  NonNull = 9,
+  NoRedZone = 10,
+  NoReturn = 11,
+  NoUnwind = 12,
+  OptimizeForSize = 13,
+  ReadOnly = 14,
+  SExt = 15,
+  StructRet = 16,
+  UWTable = 17,
+  ZExt = 18,
+  InReg = 19,
+  SanitizeThread = 20,
+  SanitizeAddress = 21,
+  SanitizeMemory = 22,
+  NonLazyBind = 23,
+  OptimizeNone = 24,
+  ReturnsTwice = 25,
+  ReadNone = 26,
+  InaccessibleMemOnly = 27,
+};
+
+typedef struct OpaqueRustString *RustStringRef;
+typedef struct LLVMOpaqueTwine *LLVMTwineRef;
+typedef struct LLVMOpaqueSMDiagnostic *LLVMSMDiagnosticRef;
+
+extern "C" void LLVMRustStringWriteImpl(RustStringRef Str, const char *Ptr,
+                                        size_t Size);
+
+class RawRustStringOstream : public llvm::raw_ostream {
+  RustStringRef Str;
+  uint64_t Pos;
+
+  void write_impl(const char *Ptr, size_t Size) override {
+    LLVMRustStringWriteImpl(Str, Ptr, Size);
+    Pos += Size;
+  }
+
+  uint64_t current_pos() const override { return Pos; }
+
+public:
+  explicit RawRustStringOstream(RustStringRef Str) : Str(Str), Pos(0) {}
+
+  ~RawRustStringOstream() {
+    // LLVM requires this.
+    flush();
+  }
+};
diff --git a/compiler/rustc_llvm/llvm-wrapper/Linker.cpp b/compiler/rustc_llvm/llvm-wrapper/Linker.cpp
new file mode 100644
index 00000000000..8766e96f086
--- /dev/null
+++ b/compiler/rustc_llvm/llvm-wrapper/Linker.cpp
@@ -0,0 +1,48 @@
+#include "llvm/Linker/Linker.h"
+
+#include "LLVMWrapper.h"
+
+using namespace llvm;
+
+struct RustLinker {
+  Linker L;
+  LLVMContext &Ctx;
+
+  RustLinker(Module &M) :
+    L(M),
+    Ctx(M.getContext())
+  {}
+};
+
+extern "C" RustLinker*
+LLVMRustLinkerNew(LLVMModuleRef DstRef) {
+  Module *Dst = unwrap(DstRef);
+
+  return new RustLinker(*Dst);
+}
+
+extern "C" void
+LLVMRustLinkerFree(RustLinker *L) {
+  delete L;
+}
+
+extern "C" bool
+LLVMRustLinkerAdd(RustLinker *L, char *BC, size_t Len) {
+  std::unique_ptr<MemoryBuffer> Buf =
+      MemoryBuffer::getMemBufferCopy(StringRef(BC, Len));
+
+  Expected<std::unique_ptr<Module>> SrcOrError =
+      llvm::getLazyBitcodeModule(Buf->getMemBufferRef(), L->Ctx);
+  if (!SrcOrError) {
+    LLVMRustSetLastError(toString(SrcOrError.takeError()).c_str());
+    return false;
+  }
+
+  auto Src = std::move(*SrcOrError);
+
+  if (L->L.linkInModule(std::move(Src))) {
+    LLVMRustSetLastError("");
+    return false;
+  }
+  return true;
+}
diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
new file mode 100644
index 00000000000..7b1c3f9ba2c
--- /dev/null
+++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
@@ -0,0 +1,1655 @@
+#include <stdio.h>
+
+#include <vector>
+#include <set>
+
+#include "LLVMWrapper.h"
+
+#include "llvm/Analysis/TargetLibraryInfo.h"
+#include "llvm/Analysis/TargetTransformInfo.h"
+#include "llvm/CodeGen/TargetSubtargetInfo.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/IR/AutoUpgrade.h"
+#include "llvm/IR/AssemblyAnnotationWriter.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/Verifier.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Object/IRObjectFile.h"
+#include "llvm/Passes/PassBuilder.h"
+#if LLVM_VERSION_GE(9, 0)
+#include "llvm/Passes/StandardInstrumentations.h"
+#endif
+#include "llvm/Support/CBindingWrapping.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Target/TargetMachine.h"
+#include "llvm/Transforms/IPO/PassManagerBuilder.h"
+#include "llvm/Transforms/IPO/AlwaysInliner.h"
+#include "llvm/Transforms/IPO/FunctionImport.h"
+#include "llvm/Transforms/Utils/FunctionImportUtils.h"
+#include "llvm/LTO/LTO.h"
+#include "llvm-c/Transforms/PassManagerBuilder.h"
+
+#include "llvm/Transforms/Instrumentation.h"
+#if LLVM_VERSION_GE(9, 0)
+#include "llvm/Transforms/Instrumentation/AddressSanitizer.h"
+#include "llvm/Support/TimeProfiler.h"
+#endif
+#include "llvm/Transforms/Instrumentation/ThreadSanitizer.h"
+#include "llvm/Transforms/Instrumentation/MemorySanitizer.h"
+#if LLVM_VERSION_GE(9, 0)
+#include "llvm/Transforms/Utils/CanonicalizeAliases.h"
+#endif
+#include "llvm/Transforms/Utils/NameAnonGlobals.h"
+
+using namespace llvm;
+
+typedef struct LLVMOpaquePass *LLVMPassRef;
+typedef struct LLVMOpaqueTargetMachine *LLVMTargetMachineRef;
+
+DEFINE_STDCXX_CONVERSION_FUNCTIONS(Pass, LLVMPassRef)
+DEFINE_STDCXX_CONVERSION_FUNCTIONS(TargetMachine, LLVMTargetMachineRef)
+#if LLVM_VERSION_LT(11, 0)
+DEFINE_STDCXX_CONVERSION_FUNCTIONS(PassManagerBuilder,
+                                   LLVMPassManagerBuilderRef)
+#endif
+
+extern "C" void LLVMInitializePasses() {
+  PassRegistry &Registry = *PassRegistry::getPassRegistry();
+  initializeCore(Registry);
+  initializeCodeGen(Registry);
+  initializeScalarOpts(Registry);
+  initializeVectorization(Registry);
+  initializeIPO(Registry);
+  initializeAnalysis(Registry);
+  initializeTransformUtils(Registry);
+  initializeInstCombine(Registry);
+  initializeInstrumentation(Registry);
+  initializeTarget(Registry);
+}
+
+extern "C" void LLVMTimeTraceProfilerInitialize() {
+#if LLVM_VERSION_GE(10, 0)
+  timeTraceProfilerInitialize(
+      /* TimeTraceGranularity */ 0,
+      /* ProcName */ "rustc");
+#elif LLVM_VERSION_GE(9, 0)
+  timeTraceProfilerInitialize();
+#endif
+}
+
+extern "C" void LLVMTimeTraceProfilerFinish(const char* FileName) {
+#if LLVM_VERSION_GE(9, 0)
+  StringRef FN(FileName);
+  std::error_code EC;
+  raw_fd_ostream OS(FN, EC, sys::fs::CD_CreateAlways);
+
+  timeTraceProfilerWrite(OS);
+  timeTraceProfilerCleanup();
+#endif
+}
+
+enum class LLVMRustPassKind {
+  Other,
+  Function,
+  Module,
+};
+
+static LLVMRustPassKind toRust(PassKind Kind) {
+  switch (Kind) {
+  case PT_Function:
+    return LLVMRustPassKind::Function;
+  case PT_Module:
+    return LLVMRustPassKind::Module;
+  default:
+    return LLVMRustPassKind::Other;
+  }
+}
+
+extern "C" LLVMPassRef LLVMRustFindAndCreatePass(const char *PassName) {
+  StringRef SR(PassName);
+  PassRegistry *PR = PassRegistry::getPassRegistry();
+
+  const PassInfo *PI = PR->getPassInfo(SR);
+  if (PI) {
+    return wrap(PI->createPass());
+  }
+  return nullptr;
+}
+
+extern "C" LLVMPassRef LLVMRustCreateAddressSanitizerFunctionPass(bool Recover) {
+  const bool CompileKernel = false;
+  const bool UseAfterScope = true;
+
+  return wrap(createAddressSanitizerFunctionPass(CompileKernel, Recover, UseAfterScope));
+}
+
+extern "C" LLVMPassRef LLVMRustCreateModuleAddressSanitizerPass(bool Recover) {
+  const bool CompileKernel = false;
+
+#if LLVM_VERSION_GE(9, 0)
+  return wrap(createModuleAddressSanitizerLegacyPassPass(CompileKernel, Recover));
+#else
+  return wrap(createAddressSanitizerModulePass(CompileKernel, Recover));
+#endif
+}
+
+extern "C" LLVMPassRef LLVMRustCreateMemorySanitizerPass(int TrackOrigins, bool Recover) {
+#if LLVM_VERSION_GE(9, 0)
+  const bool CompileKernel = false;
+
+  return wrap(createMemorySanitizerLegacyPassPass(
+      MemorySanitizerOptions{TrackOrigins, Recover, CompileKernel}));
+#else
+  return wrap(createMemorySanitizerLegacyPassPass(TrackOrigins, Recover));
+#endif
+}
+
+extern "C" LLVMPassRef LLVMRustCreateThreadSanitizerPass() {
+  return wrap(createThreadSanitizerLegacyPassPass());
+}
+
+extern "C" LLVMRustPassKind LLVMRustPassKind(LLVMPassRef RustPass) {
+  assert(RustPass);
+  Pass *Pass = unwrap(RustPass);
+  return toRust(Pass->getPassKind());
+}
+
+extern "C" void LLVMRustAddPass(LLVMPassManagerRef PMR, LLVMPassRef RustPass) {
+  assert(RustPass);
+  Pass *Pass = unwrap(RustPass);
+  PassManagerBase *PMB = unwrap(PMR);
+  PMB->add(Pass);
+}
+
+extern "C"
+void LLVMRustPassManagerBuilderPopulateThinLTOPassManager(
+  LLVMPassManagerBuilderRef PMBR,
+  LLVMPassManagerRef PMR
+) {
+  unwrap(PMBR)->populateThinLTOPassManager(*unwrap(PMR));
+}
+
+extern "C"
+void LLVMRustAddLastExtensionPasses(
+    LLVMPassManagerBuilderRef PMBR, LLVMPassRef *Passes, size_t NumPasses) {
+  auto AddExtensionPasses = [Passes, NumPasses](
+      const PassManagerBuilder &Builder, PassManagerBase &PM) {
+    for (size_t I = 0; I < NumPasses; I++) {
+      PM.add(unwrap(Passes[I]));
+    }
+  };
+  // Add the passes to both of the pre-finalization extension points,
+  // so they are run for optimized and non-optimized builds.
+  unwrap(PMBR)->addExtension(PassManagerBuilder::EP_OptimizerLast,
+                             AddExtensionPasses);
+  unwrap(PMBR)->addExtension(PassManagerBuilder::EP_EnabledOnOptLevel0,
+                             AddExtensionPasses);
+}
+
+#ifdef LLVM_COMPONENT_X86
+#define SUBTARGET_X86 SUBTARGET(X86)
+#else
+#define SUBTARGET_X86
+#endif
+
+#ifdef LLVM_COMPONENT_ARM
+#define SUBTARGET_ARM SUBTARGET(ARM)
+#else
+#define SUBTARGET_ARM
+#endif
+
+#ifdef LLVM_COMPONENT_AARCH64
+#define SUBTARGET_AARCH64 SUBTARGET(AArch64)
+#else
+#define SUBTARGET_AARCH64
+#endif
+
+#ifdef LLVM_COMPONENT_AVR
+#define SUBTARGET_AVR SUBTARGET(AVR)
+#else
+#define SUBTARGET_AVR
+#endif
+
+#ifdef LLVM_COMPONENT_MIPS
+#define SUBTARGET_MIPS SUBTARGET(Mips)
+#else
+#define SUBTARGET_MIPS
+#endif
+
+#ifdef LLVM_COMPONENT_POWERPC
+#define SUBTARGET_PPC SUBTARGET(PPC)
+#else
+#define SUBTARGET_PPC
+#endif
+
+#ifdef LLVM_COMPONENT_SYSTEMZ
+#define SUBTARGET_SYSTEMZ SUBTARGET(SystemZ)
+#else
+#define SUBTARGET_SYSTEMZ
+#endif
+
+#ifdef LLVM_COMPONENT_MSP430
+#define SUBTARGET_MSP430 SUBTARGET(MSP430)
+#else
+#define SUBTARGET_MSP430
+#endif
+
+#ifdef LLVM_COMPONENT_RISCV
+#define SUBTARGET_RISCV SUBTARGET(RISCV)
+#else
+#define SUBTARGET_RISCV
+#endif
+
+#ifdef LLVM_COMPONENT_SPARC
+#define SUBTARGET_SPARC SUBTARGET(Sparc)
+#else
+#define SUBTARGET_SPARC
+#endif
+
+#ifdef LLVM_COMPONENT_HEXAGON
+#define SUBTARGET_HEXAGON SUBTARGET(Hexagon)
+#else
+#define SUBTARGET_HEXAGON
+#endif
+
+#define GEN_SUBTARGETS                                                         \
+  SUBTARGET_X86                                                                \
+  SUBTARGET_ARM                                                                \
+  SUBTARGET_AARCH64                                                            \
+  SUBTARGET_AVR                                                                \
+  SUBTARGET_MIPS                                                               \
+  SUBTARGET_PPC                                                                \
+  SUBTARGET_SYSTEMZ                                                            \
+  SUBTARGET_MSP430                                                             \
+  SUBTARGET_SPARC                                                              \
+  SUBTARGET_HEXAGON                                                            \
+  SUBTARGET_RISCV                                                              \
+
+#define SUBTARGET(x)                                                           \
+  namespace llvm {                                                             \
+  extern const SubtargetFeatureKV x##FeatureKV[];                              \
+  extern const SubtargetFeatureKV x##SubTypeKV[];                              \
+  }
+
+GEN_SUBTARGETS
+#undef SUBTARGET
+
+extern "C" bool LLVMRustHasFeature(LLVMTargetMachineRef TM,
+                                   const char *Feature) {
+  TargetMachine *Target = unwrap(TM);
+  const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo();
+  return MCInfo->checkFeatures(std::string("+") + Feature);
+}
+
+enum class LLVMRustCodeModel {
+  Tiny,
+  Small,
+  Kernel,
+  Medium,
+  Large,
+  None,
+};
+
+static Optional<CodeModel::Model> fromRust(LLVMRustCodeModel Model) {
+  switch (Model) {
+  case LLVMRustCodeModel::Tiny:
+    return CodeModel::Tiny;
+  case LLVMRustCodeModel::Small:
+    return CodeModel::Small;
+  case LLVMRustCodeModel::Kernel:
+    return CodeModel::Kernel;
+  case LLVMRustCodeModel::Medium:
+    return CodeModel::Medium;
+  case LLVMRustCodeModel::Large:
+    return CodeModel::Large;
+  case LLVMRustCodeModel::None:
+    return None;
+  default:
+    report_fatal_error("Bad CodeModel.");
+  }
+}
+
+enum class LLVMRustCodeGenOptLevel {
+  None,
+  Less,
+  Default,
+  Aggressive,
+};
+
+static CodeGenOpt::Level fromRust(LLVMRustCodeGenOptLevel Level) {
+  switch (Level) {
+  case LLVMRustCodeGenOptLevel::None:
+    return CodeGenOpt::None;
+  case LLVMRustCodeGenOptLevel::Less:
+    return CodeGenOpt::Less;
+  case LLVMRustCodeGenOptLevel::Default:
+    return CodeGenOpt::Default;
+  case LLVMRustCodeGenOptLevel::Aggressive:
+    return CodeGenOpt::Aggressive;
+  default:
+    report_fatal_error("Bad CodeGenOptLevel.");
+  }
+}
+
+enum class LLVMRustPassBuilderOptLevel {
+  O0,
+  O1,
+  O2,
+  O3,
+  Os,
+  Oz,
+};
+
+static PassBuilder::OptimizationLevel fromRust(LLVMRustPassBuilderOptLevel Level) {
+  switch (Level) {
+  case LLVMRustPassBuilderOptLevel::O0:
+    return PassBuilder::OptimizationLevel::O0;
+  case LLVMRustPassBuilderOptLevel::O1:
+    return PassBuilder::OptimizationLevel::O1;
+  case LLVMRustPassBuilderOptLevel::O2:
+    return PassBuilder::OptimizationLevel::O2;
+  case LLVMRustPassBuilderOptLevel::O3:
+    return PassBuilder::OptimizationLevel::O3;
+  case LLVMRustPassBuilderOptLevel::Os:
+    return PassBuilder::OptimizationLevel::Os;
+  case LLVMRustPassBuilderOptLevel::Oz:
+    return PassBuilder::OptimizationLevel::Oz;
+  default:
+    report_fatal_error("Bad PassBuilderOptLevel.");
+  }
+}
+
+enum class LLVMRustRelocModel {
+  Static,
+  PIC,
+  DynamicNoPic,
+  ROPI,
+  RWPI,
+  ROPIRWPI,
+};
+
+static Reloc::Model fromRust(LLVMRustRelocModel RustReloc) {
+  switch (RustReloc) {
+  case LLVMRustRelocModel::Static:
+    return Reloc::Static;
+  case LLVMRustRelocModel::PIC:
+    return Reloc::PIC_;
+  case LLVMRustRelocModel::DynamicNoPic:
+    return Reloc::DynamicNoPIC;
+  case LLVMRustRelocModel::ROPI:
+    return Reloc::ROPI;
+  case LLVMRustRelocModel::RWPI:
+    return Reloc::RWPI;
+  case LLVMRustRelocModel::ROPIRWPI:
+    return Reloc::ROPI_RWPI;
+  }
+  report_fatal_error("Bad RelocModel.");
+}
+
+#ifdef LLVM_RUSTLLVM
+/// getLongestEntryLength - Return the length of the longest entry in the table.
+template<typename KV>
+static size_t getLongestEntryLength(ArrayRef<KV> Table) {
+  size_t MaxLen = 0;
+  for (auto &I : Table)
+    MaxLen = std::max(MaxLen, std::strlen(I.Key));
+  return MaxLen;
+}
+
+extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM) {
+  const TargetMachine *Target = unwrap(TM);
+  const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo();
+  const Triple::ArchType HostArch = Triple(sys::getProcessTriple()).getArch();
+  const Triple::ArchType TargetArch = Target->getTargetTriple().getArch();
+  const ArrayRef<SubtargetSubTypeKV> CPUTable = MCInfo->getCPUTable();
+  unsigned MaxCPULen = getLongestEntryLength(CPUTable);
+
+  printf("Available CPUs for this target:\n");
+  if (HostArch == TargetArch) {
+    const StringRef HostCPU = sys::getHostCPUName();
+    printf("    %-*s - Select the CPU of the current host (currently %.*s).\n",
+      MaxCPULen, "native", (int)HostCPU.size(), HostCPU.data());
+  }
+  for (auto &CPU : CPUTable)
+    printf("    %-*s\n", MaxCPULen, CPU.Key);
+  printf("\n");
+}
+
+extern "C" void LLVMRustPrintTargetFeatures(LLVMTargetMachineRef TM) {
+  const TargetMachine *Target = unwrap(TM);
+  const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo();
+  const ArrayRef<SubtargetFeatureKV> FeatTable = MCInfo->getFeatureTable();
+  unsigned MaxFeatLen = getLongestEntryLength(FeatTable);
+
+  printf("Available features for this target:\n");
+  for (auto &Feature : FeatTable)
+    printf("    %-*s - %s.\n", MaxFeatLen, Feature.Key, Feature.Desc);
+  printf("\nRust-specific features:\n");
+  printf("    %-*s - %s.\n",
+    MaxFeatLen,
+    "crt-static",
+    "Enables libraries with C Run-time Libraries(CRT) to be statically linked"
+  );
+  printf("\n");
+
+  printf("Use +feature to enable a feature, or -feature to disable it.\n"
+         "For example, rustc -C -target-cpu=mycpu -C "
+         "target-feature=+feature1,-feature2\n\n");
+}
+
+#else
+
+extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef) {
+  printf("Target CPU help is not supported by this LLVM version.\n\n");
+}
+
+extern "C" void LLVMRustPrintTargetFeatures(LLVMTargetMachineRef) {
+  printf("Target features help is not supported by this LLVM version.\n\n");
+}
+#endif
+
+extern "C" const char* LLVMRustGetHostCPUName(size_t *len) {
+  StringRef Name = sys::getHostCPUName();
+  *len = Name.size();
+  return Name.data();
+}
+
+extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
+    const char *TripleStr, const char *CPU, const char *Feature,
+    const char *ABIStr, LLVMRustCodeModel RustCM, LLVMRustRelocModel RustReloc,
+    LLVMRustCodeGenOptLevel RustOptLevel, bool UseSoftFloat,
+    bool FunctionSections,
+    bool DataSections,
+    bool TrapUnreachable,
+    bool Singlethread,
+    bool AsmComments,
+    bool EmitStackSizeSection,
+    bool RelaxELFRelocations,
+    bool UseInitArray) {
+
+  auto OptLevel = fromRust(RustOptLevel);
+  auto RM = fromRust(RustReloc);
+  auto CM = fromRust(RustCM);
+
+  std::string Error;
+  Triple Trip(Triple::normalize(TripleStr));
+  const llvm::Target *TheTarget =
+      TargetRegistry::lookupTarget(Trip.getTriple(), Error);
+  if (TheTarget == nullptr) {
+    LLVMRustSetLastError(Error.c_str());
+    return nullptr;
+  }
+
+  TargetOptions Options;
+
+  Options.FloatABIType = FloatABI::Default;
+  if (UseSoftFloat) {
+    Options.FloatABIType = FloatABI::Soft;
+  }
+  Options.DataSections = DataSections;
+  Options.FunctionSections = FunctionSections;
+  Options.MCOptions.AsmVerbose = AsmComments;
+  Options.MCOptions.PreserveAsmComments = AsmComments;
+  Options.MCOptions.ABIName = ABIStr;
+  Options.RelaxELFRelocations = RelaxELFRelocations;
+  Options.UseInitArray = UseInitArray;
+
+  if (TrapUnreachable) {
+    // Tell LLVM to codegen `unreachable` into an explicit trap instruction.
+    // This limits the extent of possible undefined behavior in some cases, as
+    // it prevents control flow from "falling through" into whatever code
+    // happens to be laid out next in memory.
+    Options.TrapUnreachable = true;
+  }
+
+  if (Singlethread) {
+    Options.ThreadModel = ThreadModel::Single;
+  }
+
+  Options.EmitStackSizeSection = EmitStackSizeSection;
+
+  TargetMachine *TM = TheTarget->createTargetMachine(
+      Trip.getTriple(), CPU, Feature, Options, RM, CM, OptLevel);
+  return wrap(TM);
+}
+
+extern "C" void LLVMRustDisposeTargetMachine(LLVMTargetMachineRef TM) {
+  delete unwrap(TM);
+}
+
+extern "C" void LLVMRustConfigurePassManagerBuilder(
+    LLVMPassManagerBuilderRef PMBR, LLVMRustCodeGenOptLevel OptLevel,
+    bool MergeFunctions, bool SLPVectorize, bool LoopVectorize, bool PrepareForThinLTO,
+    const char* PGOGenPath, const char* PGOUsePath) {
+  unwrap(PMBR)->MergeFunctions = MergeFunctions;
+  unwrap(PMBR)->SLPVectorize = SLPVectorize;
+  unwrap(PMBR)->OptLevel = fromRust(OptLevel);
+  unwrap(PMBR)->LoopVectorize = LoopVectorize;
+  unwrap(PMBR)->PrepareForThinLTO = PrepareForThinLTO;
+
+  if (PGOGenPath) {
+    assert(!PGOUsePath);
+    unwrap(PMBR)->EnablePGOInstrGen = true;
+    unwrap(PMBR)->PGOInstrGen = PGOGenPath;
+  }
+  if (PGOUsePath) {
+    assert(!PGOGenPath);
+    unwrap(PMBR)->PGOInstrUse = PGOUsePath;
+  }
+}
+
+// Unfortunately, the LLVM C API doesn't provide a way to set the `LibraryInfo`
+// field of a PassManagerBuilder, we expose our own method of doing so.
+extern "C" void LLVMRustAddBuilderLibraryInfo(LLVMPassManagerBuilderRef PMBR,
+                                              LLVMModuleRef M,
+                                              bool DisableSimplifyLibCalls) {
+  Triple TargetTriple(unwrap(M)->getTargetTriple());
+  TargetLibraryInfoImpl *TLI = new TargetLibraryInfoImpl(TargetTriple);
+  if (DisableSimplifyLibCalls)
+    TLI->disableAllFunctions();
+  unwrap(PMBR)->LibraryInfo = TLI;
+}
+
+// Unfortunately, the LLVM C API doesn't provide a way to create the
+// TargetLibraryInfo pass, so we use this method to do so.
+extern "C" void LLVMRustAddLibraryInfo(LLVMPassManagerRef PMR, LLVMModuleRef M,
+                                       bool DisableSimplifyLibCalls) {
+  Triple TargetTriple(unwrap(M)->getTargetTriple());
+  TargetLibraryInfoImpl TLII(TargetTriple);
+  if (DisableSimplifyLibCalls)
+    TLII.disableAllFunctions();
+  unwrap(PMR)->add(new TargetLibraryInfoWrapperPass(TLII));
+}
+
+// Unfortunately, the LLVM C API doesn't provide an easy way of iterating over
+// all the functions in a module, so we do that manually here. You'll find
+// similar code in clang's BackendUtil.cpp file.
+extern "C" void LLVMRustRunFunctionPassManager(LLVMPassManagerRef PMR,
+                                               LLVMModuleRef M) {
+  llvm::legacy::FunctionPassManager *P =
+      unwrap<llvm::legacy::FunctionPassManager>(PMR);
+  P->doInitialization();
+
+  // Upgrade all calls to old intrinsics first.
+  for (Module::iterator I = unwrap(M)->begin(), E = unwrap(M)->end(); I != E;)
+    UpgradeCallsToIntrinsic(&*I++); // must be post-increment, as we remove
+
+  for (Module::iterator I = unwrap(M)->begin(), E = unwrap(M)->end(); I != E;
+       ++I)
+    if (!I->isDeclaration())
+      P->run(*I);
+
+  P->doFinalization();
+}
+
+extern "C" void LLVMRustSetLLVMOptions(int Argc, char **Argv) {
+  // Initializing the command-line options more than once is not allowed. So,
+  // check if they've already been initialized.  (This could happen if we're
+  // being called from rustpkg, for example). If the arguments change, then
+  // that's just kinda unfortunate.
+  static bool Initialized = false;
+  if (Initialized)
+    return;
+  Initialized = true;
+  cl::ParseCommandLineOptions(Argc, Argv);
+}
+
+enum class LLVMRustFileType {
+  AssemblyFile,
+  ObjectFile,
+};
+
+#if LLVM_VERSION_GE(10, 0)
+static CodeGenFileType fromRust(LLVMRustFileType Type) {
+  switch (Type) {
+  case LLVMRustFileType::AssemblyFile:
+    return CGFT_AssemblyFile;
+  case LLVMRustFileType::ObjectFile:
+    return CGFT_ObjectFile;
+  default:
+    report_fatal_error("Bad FileType.");
+  }
+}
+#else
+static TargetMachine::CodeGenFileType fromRust(LLVMRustFileType Type) {
+  switch (Type) {
+  case LLVMRustFileType::AssemblyFile:
+    return TargetMachine::CGFT_AssemblyFile;
+  case LLVMRustFileType::ObjectFile:
+    return TargetMachine::CGFT_ObjectFile;
+  default:
+    report_fatal_error("Bad FileType.");
+  }
+}
+#endif
+
+extern "C" LLVMRustResult
+LLVMRustWriteOutputFile(LLVMTargetMachineRef Target, LLVMPassManagerRef PMR,
+                        LLVMModuleRef M, const char *Path,
+                        LLVMRustFileType RustFileType) {
+  llvm::legacy::PassManager *PM = unwrap<llvm::legacy::PassManager>(PMR);
+  auto FileType = fromRust(RustFileType);
+
+  std::string ErrorInfo;
+  std::error_code EC;
+  raw_fd_ostream OS(Path, EC, sys::fs::F_None);
+  if (EC)
+    ErrorInfo = EC.message();
+  if (ErrorInfo != "") {
+    LLVMRustSetLastError(ErrorInfo.c_str());
+    return LLVMRustResult::Failure;
+  }
+
+  buffer_ostream BOS(OS);
+  unwrap(Target)->addPassesToEmitFile(*PM, BOS, nullptr, FileType, false);
+  PM->run(*unwrap(M));
+
+  // Apparently `addPassesToEmitFile` adds a pointer to our on-the-stack output
+  // stream (OS), so the only real safe place to delete this is here? Don't we
+  // wish this was written in Rust?
+  LLVMDisposePassManager(PMR);
+  return LLVMRustResult::Success;
+}
+
+extern "C" typedef void (*LLVMRustSelfProfileBeforePassCallback)(void*, // LlvmSelfProfiler
+                                                      const char*,      // pass name
+                                                      const char*);     // IR name
+extern "C" typedef void (*LLVMRustSelfProfileAfterPassCallback)(void*); // LlvmSelfProfiler
+
+#if LLVM_VERSION_GE(9, 0)
+
+std::string LLVMRustwrappedIrGetName(const llvm::Any &WrappedIr) {
+  if (any_isa<const Module *>(WrappedIr))
+    return any_cast<const Module *>(WrappedIr)->getName().str();
+  if (any_isa<const Function *>(WrappedIr))
+    return any_cast<const Function *>(WrappedIr)->getName().str();
+  if (any_isa<const Loop *>(WrappedIr))
+    return any_cast<const Loop *>(WrappedIr)->getName().str();
+  if (any_isa<const LazyCallGraph::SCC *>(WrappedIr))
+    return any_cast<const LazyCallGraph::SCC *>(WrappedIr)->getName();
+  return "<UNKNOWN>";
+}
+
+
+void LLVMSelfProfileInitializeCallbacks(
+    PassInstrumentationCallbacks& PIC, void* LlvmSelfProfiler,
+    LLVMRustSelfProfileBeforePassCallback BeforePassCallback,
+    LLVMRustSelfProfileAfterPassCallback AfterPassCallback) {
+  PIC.registerBeforePassCallback([LlvmSelfProfiler, BeforePassCallback](
+                                     StringRef Pass, llvm::Any Ir) {
+    std::string PassName = Pass.str();
+    std::string IrName = LLVMRustwrappedIrGetName(Ir);
+    BeforePassCallback(LlvmSelfProfiler, PassName.c_str(), IrName.c_str());
+    return true;
+  });
+
+  PIC.registerAfterPassCallback(
+      [LlvmSelfProfiler, AfterPassCallback](StringRef Pass, llvm::Any Ir) {
+        AfterPassCallback(LlvmSelfProfiler);
+      });
+
+  PIC.registerAfterPassInvalidatedCallback(
+      [LlvmSelfProfiler, AfterPassCallback](StringRef Pass) {
+        AfterPassCallback(LlvmSelfProfiler);
+      });
+
+  PIC.registerBeforeAnalysisCallback([LlvmSelfProfiler, BeforePassCallback](
+                                         StringRef Pass, llvm::Any Ir) {
+    std::string PassName = Pass.str();
+    std::string IrName = LLVMRustwrappedIrGetName(Ir);
+    BeforePassCallback(LlvmSelfProfiler, PassName.c_str(), IrName.c_str());
+  });
+
+  PIC.registerAfterAnalysisCallback(
+      [LlvmSelfProfiler, AfterPassCallback](StringRef Pass, llvm::Any Ir) {
+        AfterPassCallback(LlvmSelfProfiler);
+      });
+}
+#endif
+
+enum class LLVMRustOptStage {
+  PreLinkNoLTO,
+  PreLinkThinLTO,
+  PreLinkFatLTO,
+  ThinLTO,
+  FatLTO,
+};
+
+struct LLVMRustSanitizerOptions {
+  bool SanitizeAddress;
+  bool SanitizeAddressRecover;
+  bool SanitizeMemory;
+  bool SanitizeMemoryRecover;
+  int  SanitizeMemoryTrackOrigins;
+  bool SanitizeThread;
+};
+
+extern "C" void
+LLVMRustOptimizeWithNewPassManager(
+    LLVMModuleRef ModuleRef,
+    LLVMTargetMachineRef TMRef,
+    LLVMRustPassBuilderOptLevel OptLevelRust,
+    LLVMRustOptStage OptStage,
+    bool NoPrepopulatePasses, bool VerifyIR, bool UseThinLTOBuffers,
+    bool MergeFunctions, bool UnrollLoops, bool SLPVectorize, bool LoopVectorize,
+    bool DisableSimplifyLibCalls, bool EmitLifetimeMarkers,
+    LLVMRustSanitizerOptions *SanitizerOptions,
+    const char *PGOGenPath, const char *PGOUsePath,
+    void* LlvmSelfProfiler,
+    LLVMRustSelfProfileBeforePassCallback BeforePassCallback,
+    LLVMRustSelfProfileAfterPassCallback AfterPassCallback) {
+#if LLVM_VERSION_GE(9, 0)
+  Module *TheModule = unwrap(ModuleRef);
+  TargetMachine *TM = unwrap(TMRef);
+  PassBuilder::OptimizationLevel OptLevel = fromRust(OptLevelRust);
+
+  // FIXME: MergeFunctions is not supported by NewPM yet.
+  (void) MergeFunctions;
+
+  PipelineTuningOptions PTO;
+  PTO.LoopUnrolling = UnrollLoops;
+  PTO.LoopInterleaving = UnrollLoops;
+  PTO.LoopVectorization = LoopVectorize;
+  PTO.SLPVectorization = SLPVectorize;
+
+  PassInstrumentationCallbacks PIC;
+  StandardInstrumentations SI;
+  SI.registerCallbacks(PIC);
+
+  if (LlvmSelfProfiler){
+    LLVMSelfProfileInitializeCallbacks(PIC,LlvmSelfProfiler,BeforePassCallback,AfterPassCallback);
+  }
+
+  Optional<PGOOptions> PGOOpt;
+  if (PGOGenPath) {
+    assert(!PGOUsePath);
+    PGOOpt = PGOOptions(PGOGenPath, "", "", PGOOptions::IRInstr);
+  } else if (PGOUsePath) {
+    assert(!PGOGenPath);
+    PGOOpt = PGOOptions(PGOUsePath, "", "", PGOOptions::IRUse);
+  }
+
+  PassBuilder PB(TM, PTO, PGOOpt, &PIC);
+
+  // FIXME: We may want to expose this as an option.
+  bool DebugPassManager = false;
+  LoopAnalysisManager LAM(DebugPassManager);
+  FunctionAnalysisManager FAM(DebugPassManager);
+  CGSCCAnalysisManager CGAM(DebugPassManager);
+  ModuleAnalysisManager MAM(DebugPassManager);
+
+  FAM.registerPass([&] { return PB.buildDefaultAAPipeline(); });
+
+  Triple TargetTriple(TheModule->getTargetTriple());
+  std::unique_ptr<TargetLibraryInfoImpl> TLII(new TargetLibraryInfoImpl(TargetTriple));
+  if (DisableSimplifyLibCalls)
+    TLII->disableAllFunctions();
+  FAM.registerPass([&] { return TargetLibraryAnalysis(*TLII); });
+
+  PB.registerModuleAnalyses(MAM);
+  PB.registerCGSCCAnalyses(CGAM);
+  PB.registerFunctionAnalyses(FAM);
+  PB.registerLoopAnalyses(LAM);
+  PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
+
+  // We manually collect pipeline callbacks so we can apply them at O0, where the
+  // PassBuilder does not create a pipeline.
+  std::vector<std::function<void(ModulePassManager &)>> PipelineStartEPCallbacks;
+#if LLVM_VERSION_GE(11, 0)
+  std::vector<std::function<void(ModulePassManager &, PassBuilder::OptimizationLevel)>>
+      OptimizerLastEPCallbacks;
+#else
+  std::vector<std::function<void(FunctionPassManager &, PassBuilder::OptimizationLevel)>>
+      OptimizerLastEPCallbacks;
+#endif
+
+  if (VerifyIR) {
+    PipelineStartEPCallbacks.push_back([VerifyIR](ModulePassManager &MPM) {
+        MPM.addPass(VerifierPass());
+    });
+  }
+
+  if (SanitizerOptions) {
+    if (SanitizerOptions->SanitizeMemory) {
+      MemorySanitizerOptions Options(
+          SanitizerOptions->SanitizeMemoryTrackOrigins,
+          SanitizerOptions->SanitizeMemoryRecover,
+          /*CompileKernel=*/false);
+#if LLVM_VERSION_GE(11, 0)
+      OptimizerLastEPCallbacks.push_back(
+        [Options](ModulePassManager &MPM, PassBuilder::OptimizationLevel Level) {
+          MPM.addPass(MemorySanitizerPass(Options));
+          MPM.addPass(createModuleToFunctionPassAdaptor(MemorySanitizerPass(Options)));
+        }
+      );
+#else
+#if LLVM_VERSION_GE(10, 0)
+      PipelineStartEPCallbacks.push_back([Options](ModulePassManager &MPM) {
+        MPM.addPass(MemorySanitizerPass(Options));
+      });
+#endif
+      OptimizerLastEPCallbacks.push_back(
+        [Options](FunctionPassManager &FPM, PassBuilder::OptimizationLevel Level) {
+          FPM.addPass(MemorySanitizerPass(Options));
+        }
+      );
+#endif
+    }
+
+    if (SanitizerOptions->SanitizeThread) {
+#if LLVM_VERSION_GE(11, 0)
+      OptimizerLastEPCallbacks.push_back(
+        [](ModulePassManager &MPM, PassBuilder::OptimizationLevel Level) {
+          MPM.addPass(ThreadSanitizerPass());
+          MPM.addPass(createModuleToFunctionPassAdaptor(ThreadSanitizerPass()));
+        }
+      );
+#else
+#if LLVM_VERSION_GE(10, 0)
+      PipelineStartEPCallbacks.push_back([](ModulePassManager &MPM) {
+        MPM.addPass(ThreadSanitizerPass());
+      });
+#endif
+      OptimizerLastEPCallbacks.push_back(
+        [](FunctionPassManager &FPM, PassBuilder::OptimizationLevel Level) {
+          FPM.addPass(ThreadSanitizerPass());
+        }
+      );
+#endif
+    }
+
+    if (SanitizerOptions->SanitizeAddress) {
+#if LLVM_VERSION_GE(11, 0)
+      OptimizerLastEPCallbacks.push_back(
+        [SanitizerOptions](ModulePassManager &MPM, PassBuilder::OptimizationLevel Level) {
+          MPM.addPass(RequireAnalysisPass<ASanGlobalsMetadataAnalysis, Module>());
+          MPM.addPass(ModuleAddressSanitizerPass(
+              /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover));
+          MPM.addPass(createModuleToFunctionPassAdaptor(AddressSanitizerPass(
+              /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover,
+              /*UseAfterScope=*/true)));
+        }
+      );
+#else
+      PipelineStartEPCallbacks.push_back([&](ModulePassManager &MPM) {
+        MPM.addPass(RequireAnalysisPass<ASanGlobalsMetadataAnalysis, Module>());
+      });
+      OptimizerLastEPCallbacks.push_back(
+        [SanitizerOptions](FunctionPassManager &FPM, PassBuilder::OptimizationLevel Level) {
+          FPM.addPass(AddressSanitizerPass(
+              /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover,
+              /*UseAfterScope=*/true));
+        }
+      );
+      PipelineStartEPCallbacks.push_back(
+        [SanitizerOptions](ModulePassManager &MPM) {
+          MPM.addPass(ModuleAddressSanitizerPass(
+              /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover));
+        }
+      );
+#endif
+    }
+  }
+
+  ModulePassManager MPM(DebugPassManager);
+  if (!NoPrepopulatePasses) {
+    if (OptLevel == PassBuilder::OptimizationLevel::O0) {
+      for (const auto &C : PipelineStartEPCallbacks)
+        C(MPM);
+
+#if LLVM_VERSION_GE(11, 0)
+      for (const auto &C : OptimizerLastEPCallbacks)
+        C(MPM, OptLevel);
+#else
+      if (!OptimizerLastEPCallbacks.empty()) {
+        FunctionPassManager FPM(DebugPassManager);
+        for (const auto &C : OptimizerLastEPCallbacks)
+          C(FPM, OptLevel);
+        MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
+      }
+#endif
+
+      MPM.addPass(AlwaysInlinerPass(EmitLifetimeMarkers));
+
+#if LLVM_VERSION_GE(10, 0)
+      if (PGOOpt) {
+        PB.addPGOInstrPassesForO0(
+            MPM, DebugPassManager, PGOOpt->Action == PGOOptions::IRInstr,
+            /*IsCS=*/false, PGOOpt->ProfileFile, PGOOpt->ProfileRemappingFile);
+      }
+#endif
+    } else {
+      for (const auto &C : PipelineStartEPCallbacks)
+        PB.registerPipelineStartEPCallback(C);
+      if (OptStage != LLVMRustOptStage::PreLinkThinLTO) {
+        for (const auto &C : OptimizerLastEPCallbacks)
+          PB.registerOptimizerLastEPCallback(C);
+      }
+
+      switch (OptStage) {
+      case LLVMRustOptStage::PreLinkNoLTO:
+        MPM = PB.buildPerModuleDefaultPipeline(OptLevel, DebugPassManager);
+        break;
+      case LLVMRustOptStage::PreLinkThinLTO:
+        MPM = PB.buildThinLTOPreLinkDefaultPipeline(OptLevel, DebugPassManager);
+#if LLVM_VERSION_GE(11, 0)
+        for (const auto &C : OptimizerLastEPCallbacks)
+          C(MPM, OptLevel);
+#else
+        if (!OptimizerLastEPCallbacks.empty()) {
+          FunctionPassManager FPM(DebugPassManager);
+          for (const auto &C : OptimizerLastEPCallbacks)
+            C(FPM, OptLevel);
+          MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
+        }
+#endif
+        break;
+      case LLVMRustOptStage::PreLinkFatLTO:
+        MPM = PB.buildLTOPreLinkDefaultPipeline(OptLevel, DebugPassManager);
+        break;
+      case LLVMRustOptStage::ThinLTO:
+        // FIXME: Does it make sense to pass the ModuleSummaryIndex?
+        // It only seems to be needed for C++ specific optimizations.
+        MPM = PB.buildThinLTODefaultPipeline(OptLevel, DebugPassManager, nullptr);
+        break;
+      case LLVMRustOptStage::FatLTO:
+        MPM = PB.buildLTODefaultPipeline(OptLevel, DebugPassManager, nullptr);
+        break;
+      }
+    }
+  }
+
+  if (UseThinLTOBuffers) {
+    MPM.addPass(CanonicalizeAliasesPass());
+    MPM.addPass(NameAnonGlobalPass());
+  }
+
+  // Upgrade all calls to old intrinsics first.
+  for (Module::iterator I = TheModule->begin(), E = TheModule->end(); I != E;)
+    UpgradeCallsToIntrinsic(&*I++); // must be post-increment, as we remove
+
+  MPM.run(*TheModule, MAM);
+#else
+  // The new pass manager has been available for a long time,
+  // but we don't bother supporting it on old LLVM versions.
+  report_fatal_error("New pass manager only supported since LLVM 9");
+#endif
+}
+
+// Callback to demangle function name
+// Parameters:
+// * name to be demangled
+// * name len
+// * output buffer
+// * output buffer len
+// Returns len of demangled string, or 0 if demangle failed.
+typedef size_t (*DemangleFn)(const char*, size_t, char*, size_t);
+
+
+namespace {
+
+class RustAssemblyAnnotationWriter : public AssemblyAnnotationWriter {
+  DemangleFn Demangle;
+  std::vector<char> Buf;
+
+public:
+  RustAssemblyAnnotationWriter(DemangleFn Demangle) : Demangle(Demangle) {}
+
+  // Return empty string if demangle failed
+  // or if name does not need to be demangled
+  StringRef CallDemangle(StringRef name) {
+    if (!Demangle) {
+      return StringRef();
+    }
+
+    if (Buf.size() < name.size() * 2) {
+      // Semangled name usually shorter than mangled,
+      // but allocate twice as much memory just in case
+      Buf.resize(name.size() * 2);
+    }
+
+    auto R = Demangle(name.data(), name.size(), Buf.data(), Buf.size());
+    if (!R) {
+      // Demangle failed.
+      return StringRef();
+    }
+
+    auto Demangled = StringRef(Buf.data(), R);
+    if (Demangled == name) {
+      // Do not print anything if demangled name is equal to mangled.
+      return StringRef();
+    }
+
+    return Demangled;
+  }
+
+  void emitFunctionAnnot(const Function *F,
+                         formatted_raw_ostream &OS) override {
+    StringRef Demangled = CallDemangle(F->getName());
+    if (Demangled.empty()) {
+        return;
+    }
+
+    OS << "; " << Demangled << "\n";
+  }
+
+  void emitInstructionAnnot(const Instruction *I,
+                            formatted_raw_ostream &OS) override {
+    const char *Name;
+    const Value *Value;
+    if (const CallInst *CI = dyn_cast<CallInst>(I)) {
+      Name = "call";
+      Value = CI->getCalledOperand();
+    } else if (const InvokeInst* II = dyn_cast<InvokeInst>(I)) {
+      Name = "invoke";
+      Value = II->getCalledOperand();
+    } else {
+      // Could demangle more operations, e. g.
+      // `store %place, @function`.
+      return;
+    }
+
+    if (!Value->hasName()) {
+      return;
+    }
+
+    StringRef Demangled = CallDemangle(Value->getName());
+    if (Demangled.empty()) {
+      return;
+    }
+
+    OS << "; " << Name << " " << Demangled << "\n";
+  }
+};
+
+} // namespace
+
+extern "C" LLVMRustResult
+LLVMRustPrintModule(LLVMModuleRef M, const char *Path, DemangleFn Demangle) {
+  std::string ErrorInfo;
+  std::error_code EC;
+  raw_fd_ostream OS(Path, EC, sys::fs::F_None);
+  if (EC)
+    ErrorInfo = EC.message();
+  if (ErrorInfo != "") {
+    LLVMRustSetLastError(ErrorInfo.c_str());
+    return LLVMRustResult::Failure;
+  }
+
+  RustAssemblyAnnotationWriter AAW(Demangle);
+  formatted_raw_ostream FOS(OS);
+  unwrap(M)->print(FOS, &AAW);
+
+  return LLVMRustResult::Success;
+}
+
+extern "C" void LLVMRustPrintPasses() {
+  LLVMInitializePasses();
+  struct MyListener : PassRegistrationListener {
+    void passEnumerate(const PassInfo *Info) {
+      StringRef PassArg = Info->getPassArgument();
+      StringRef PassName = Info->getPassName();
+      if (!PassArg.empty()) {
+        // These unsigned->signed casts could theoretically overflow, but
+        // realistically never will (and even if, the result is implementation
+        // defined rather plain UB).
+        printf("%15.*s - %.*s\n", (int)PassArg.size(), PassArg.data(),
+               (int)PassName.size(), PassName.data());
+      }
+    }
+  } Listener;
+
+  PassRegistry *PR = PassRegistry::getPassRegistry();
+  PR->enumerateWith(&Listener);
+}
+
+extern "C" void LLVMRustAddAlwaysInlinePass(LLVMPassManagerBuilderRef PMBR,
+                                            bool AddLifetimes) {
+  unwrap(PMBR)->Inliner = llvm::createAlwaysInlinerLegacyPass(AddLifetimes);
+}
+
+extern "C" void LLVMRustRunRestrictionPass(LLVMModuleRef M, char **Symbols,
+                                           size_t Len) {
+  llvm::legacy::PassManager passes;
+
+  auto PreserveFunctions = [=](const GlobalValue &GV) {
+    for (size_t I = 0; I < Len; I++) {
+      if (GV.getName() == Symbols[I]) {
+        return true;
+      }
+    }
+    return false;
+  };
+
+  passes.add(llvm::createInternalizePass(PreserveFunctions));
+
+  passes.run(*unwrap(M));
+}
+
+extern "C" void LLVMRustMarkAllFunctionsNounwind(LLVMModuleRef M) {
+  for (Module::iterator GV = unwrap(M)->begin(), E = unwrap(M)->end(); GV != E;
+       ++GV) {
+    GV->setDoesNotThrow();
+    Function *F = dyn_cast<Function>(GV);
+    if (F == nullptr)
+      continue;
+
+    for (Function::iterator B = F->begin(), BE = F->end(); B != BE; ++B) {
+      for (BasicBlock::iterator I = B->begin(), IE = B->end(); I != IE; ++I) {
+        if (isa<InvokeInst>(I)) {
+          InvokeInst *CI = cast<InvokeInst>(I);
+          CI->setDoesNotThrow();
+        }
+      }
+    }
+  }
+}
+
+extern "C" void
+LLVMRustSetDataLayoutFromTargetMachine(LLVMModuleRef Module,
+                                       LLVMTargetMachineRef TMR) {
+  TargetMachine *Target = unwrap(TMR);
+  unwrap(Module)->setDataLayout(Target->createDataLayout());
+}
+
+extern "C" void LLVMRustSetModulePICLevel(LLVMModuleRef M) {
+  unwrap(M)->setPICLevel(PICLevel::Level::BigPIC);
+}
+
+extern "C" void LLVMRustSetModulePIELevel(LLVMModuleRef M) {
+  unwrap(M)->setPIELevel(PIELevel::Level::Large);
+}
+
+// Here you'll find an implementation of ThinLTO as used by the Rust compiler
+// right now. This ThinLTO support is only enabled on "recent ish" versions of
+// LLVM, and otherwise it's just blanket rejected from other compilers.
+//
+// Most of this implementation is straight copied from LLVM. At the time of
+// this writing it wasn't *quite* suitable to reuse more code from upstream
+// for our purposes, but we should strive to upstream this support once it's
+// ready to go! I figure we may want a bit of testing locally first before
+// sending this upstream to LLVM. I hear though they're quite eager to receive
+// feedback like this!
+//
+// If you're reading this code and wondering "what in the world" or you're
+// working "good lord by LLVM upgrade is *still* failing due to these bindings"
+// then fear not! (ok maybe fear a little). All code here is mostly based
+// on `lib/LTO/ThinLTOCodeGenerator.cpp` in LLVM.
+//
+// You'll find that the general layout here roughly corresponds to the `run`
+// method in that file as well as `ProcessThinLTOModule`. Functions are
+// specifically commented below as well, but if you're updating this code
+// or otherwise trying to understand it, the LLVM source will be useful in
+// interpreting the mysteries within.
+//
+// Otherwise I'll apologize in advance, it probably requires a relatively
+// significant investment on your part to "truly understand" what's going on
+// here. Not saying I do myself, but it took me awhile staring at LLVM's source
+// and various online resources about ThinLTO to make heads or tails of all
+// this.
+
+// This is a shared data structure which *must* be threadsafe to share
+// read-only amongst threads. This also corresponds basically to the arguments
+// of the `ProcessThinLTOModule` function in the LLVM source.
+struct LLVMRustThinLTOData {
+  // The combined index that is the global analysis over all modules we're
+  // performing ThinLTO for. This is mostly managed by LLVM.
+  ModuleSummaryIndex Index;
+
+  // All modules we may look at, stored as in-memory serialized versions. This
+  // is later used when inlining to ensure we can extract any module to inline
+  // from.
+  StringMap<MemoryBufferRef> ModuleMap;
+
+  // A set that we manage of everything we *don't* want internalized. Note that
+  // this includes all transitive references right now as well, but it may not
+  // always!
+  DenseSet<GlobalValue::GUID> GUIDPreservedSymbols;
+
+  // Not 100% sure what these are, but they impact what's internalized and
+  // what's inlined across modules, I believe.
+  StringMap<FunctionImporter::ImportMapTy> ImportLists;
+  StringMap<FunctionImporter::ExportSetTy> ExportLists;
+  StringMap<GVSummaryMapTy> ModuleToDefinedGVSummaries;
+
+  LLVMRustThinLTOData() : Index(/* HaveGVs = */ false) {}
+};
+
+// Just an argument to the `LLVMRustCreateThinLTOData` function below.
+struct LLVMRustThinLTOModule {
+  const char *identifier;
+  const char *data;
+  size_t len;
+};
+
+// This is copied from `lib/LTO/ThinLTOCodeGenerator.cpp`, not sure what it
+// does.
+static const GlobalValueSummary *
+getFirstDefinitionForLinker(const GlobalValueSummaryList &GVSummaryList) {
+  auto StrongDefForLinker = llvm::find_if(
+      GVSummaryList, [](const std::unique_ptr<GlobalValueSummary> &Summary) {
+        auto Linkage = Summary->linkage();
+        return !GlobalValue::isAvailableExternallyLinkage(Linkage) &&
+               !GlobalValue::isWeakForLinker(Linkage);
+      });
+  if (StrongDefForLinker != GVSummaryList.end())
+    return StrongDefForLinker->get();
+
+  auto FirstDefForLinker = llvm::find_if(
+      GVSummaryList, [](const std::unique_ptr<GlobalValueSummary> &Summary) {
+        auto Linkage = Summary->linkage();
+        return !GlobalValue::isAvailableExternallyLinkage(Linkage);
+      });
+  if (FirstDefForLinker == GVSummaryList.end())
+    return nullptr;
+  return FirstDefForLinker->get();
+}
+
+// The main entry point for creating the global ThinLTO analysis. The structure
+// here is basically the same as before threads are spawned in the `run`
+// function of `lib/LTO/ThinLTOCodeGenerator.cpp`.
+extern "C" LLVMRustThinLTOData*
+LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules,
+                          int num_modules,
+                          const char **preserved_symbols,
+                          int num_symbols) {
+#if LLVM_VERSION_GE(10, 0)
+  auto Ret = std::make_unique<LLVMRustThinLTOData>();
+#else
+  auto Ret = llvm::make_unique<LLVMRustThinLTOData>();
+#endif
+
+  // Load each module's summary and merge it into one combined index
+  for (int i = 0; i < num_modules; i++) {
+    auto module = &modules[i];
+    StringRef buffer(module->data, module->len);
+    MemoryBufferRef mem_buffer(buffer, module->identifier);
+
+    Ret->ModuleMap[module->identifier] = mem_buffer;
+
+    if (Error Err = readModuleSummaryIndex(mem_buffer, Ret->Index, i)) {
+      LLVMRustSetLastError(toString(std::move(Err)).c_str());
+      return nullptr;
+    }
+  }
+
+  // Collect for each module the list of function it defines (GUID -> Summary)
+  Ret->Index.collectDefinedGVSummariesPerModule(Ret->ModuleToDefinedGVSummaries);
+
+  // Convert the preserved symbols set from string to GUID, this is then needed
+  // for internalization.
+  for (int i = 0; i < num_symbols; i++) {
+    auto GUID = GlobalValue::getGUID(preserved_symbols[i]);
+    Ret->GUIDPreservedSymbols.insert(GUID);
+  }
+
+  // Collect the import/export lists for all modules from the call-graph in the
+  // combined index
+  //
+  // This is copied from `lib/LTO/ThinLTOCodeGenerator.cpp`
+  auto deadIsPrevailing = [&](GlobalValue::GUID G) {
+    return PrevailingType::Unknown;
+  };
+  // We don't have a complete picture in our use of ThinLTO, just our immediate
+  // crate, so we need `ImportEnabled = false` to limit internalization.
+  // Otherwise, we sometimes lose `static` values -- see #60184.
+  computeDeadSymbolsWithConstProp(Ret->Index, Ret->GUIDPreservedSymbols,
+                                  deadIsPrevailing, /* ImportEnabled = */ false);
+  ComputeCrossModuleImport(
+    Ret->Index,
+    Ret->ModuleToDefinedGVSummaries,
+    Ret->ImportLists,
+    Ret->ExportLists
+  );
+
+  // Resolve LinkOnce/Weak symbols, this has to be computed early be cause it
+  // impacts the caching.
+  //
+  // This is copied from `lib/LTO/ThinLTOCodeGenerator.cpp` with some of this
+  // being lifted from `lib/LTO/LTO.cpp` as well
+  StringMap<std::map<GlobalValue::GUID, GlobalValue::LinkageTypes>> ResolvedODR;
+  DenseMap<GlobalValue::GUID, const GlobalValueSummary *> PrevailingCopy;
+  for (auto &I : Ret->Index) {
+    if (I.second.SummaryList.size() > 1)
+      PrevailingCopy[I.first] = getFirstDefinitionForLinker(I.second.SummaryList);
+  }
+  auto isPrevailing = [&](GlobalValue::GUID GUID, const GlobalValueSummary *S) {
+    const auto &Prevailing = PrevailingCopy.find(GUID);
+    if (Prevailing == PrevailingCopy.end())
+      return true;
+    return Prevailing->second == S;
+  };
+  auto recordNewLinkage = [&](StringRef ModuleIdentifier,
+                              GlobalValue::GUID GUID,
+                              GlobalValue::LinkageTypes NewLinkage) {
+    ResolvedODR[ModuleIdentifier][GUID] = NewLinkage;
+  };
+#if LLVM_VERSION_GE(9, 0)
+  thinLTOResolvePrevailingInIndex(Ret->Index, isPrevailing, recordNewLinkage,
+                                  Ret->GUIDPreservedSymbols);
+#else
+  thinLTOResolvePrevailingInIndex(Ret->Index, isPrevailing, recordNewLinkage);
+#endif
+
+  // Here we calculate an `ExportedGUIDs` set for use in the `isExported`
+  // callback below. This callback below will dictate the linkage for all
+  // summaries in the index, and we basically just only want to ensure that dead
+  // symbols are internalized. Otherwise everything that's already external
+  // linkage will stay as external, and internal will stay as internal.
+  std::set<GlobalValue::GUID> ExportedGUIDs;
+  for (auto &List : Ret->Index) {
+    for (auto &GVS: List.second.SummaryList) {
+      if (GlobalValue::isLocalLinkage(GVS->linkage()))
+        continue;
+      auto GUID = GVS->getOriginalName();
+      if (GVS->flags().Live)
+        ExportedGUIDs.insert(GUID);
+    }
+  }
+#if LLVM_VERSION_GE(10, 0)
+  auto isExported = [&](StringRef ModuleIdentifier, ValueInfo VI) {
+    const auto &ExportList = Ret->ExportLists.find(ModuleIdentifier);
+    return (ExportList != Ret->ExportLists.end() &&
+      ExportList->second.count(VI)) ||
+      ExportedGUIDs.count(VI.getGUID());
+  };
+  thinLTOInternalizeAndPromoteInIndex(Ret->Index, isExported, isPrevailing);
+#else
+  auto isExported = [&](StringRef ModuleIdentifier, GlobalValue::GUID GUID) {
+    const auto &ExportList = Ret->ExportLists.find(ModuleIdentifier);
+    return (ExportList != Ret->ExportLists.end() &&
+      ExportList->second.count(GUID)) ||
+      ExportedGUIDs.count(GUID);
+  };
+  thinLTOInternalizeAndPromoteInIndex(Ret->Index, isExported);
+#endif
+
+  return Ret.release();
+}
+
+extern "C" void
+LLVMRustFreeThinLTOData(LLVMRustThinLTOData *Data) {
+  delete Data;
+}
+
+// Below are the various passes that happen *per module* when doing ThinLTO.
+//
+// In other words, these are the functions that are all run concurrently
+// with one another, one per module. The passes here correspond to the analysis
+// passes in `lib/LTO/ThinLTOCodeGenerator.cpp`, currently found in the
+// `ProcessThinLTOModule` function. Here they're split up into separate steps
+// so rustc can save off the intermediate bytecode between each step.
+
+#if LLVM_VERSION_GE(11, 0)
+static bool
+clearDSOLocalOnDeclarations(Module &Mod, TargetMachine &TM) {
+  // When linking an ELF shared object, dso_local should be dropped. We
+  // conservatively do this for -fpic.
+  bool ClearDSOLocalOnDeclarations =
+      TM.getTargetTriple().isOSBinFormatELF() &&
+      TM.getRelocationModel() != Reloc::Static &&
+      Mod.getPIELevel() == PIELevel::Default;
+  return ClearDSOLocalOnDeclarations;
+}
+#endif
+
+extern "C" bool
+LLVMRustPrepareThinLTORename(const LLVMRustThinLTOData *Data, LLVMModuleRef M,
+                             LLVMTargetMachineRef TM) {
+  Module &Mod = *unwrap(M);
+  TargetMachine &Target = *unwrap(TM);
+
+#if LLVM_VERSION_GE(11, 0)
+  bool ClearDSOLocal = clearDSOLocalOnDeclarations(Mod, Target);
+  bool error = renameModuleForThinLTO(Mod, Data->Index, ClearDSOLocal);
+#else
+  bool error = renameModuleForThinLTO(Mod, Data->Index);
+#endif
+
+  if (error) {
+    LLVMRustSetLastError("renameModuleForThinLTO failed");
+    return false;
+  }
+  return true;
+}
+
+extern "C" bool
+LLVMRustPrepareThinLTOResolveWeak(const LLVMRustThinLTOData *Data, LLVMModuleRef M) {
+  Module &Mod = *unwrap(M);
+  const auto &DefinedGlobals = Data->ModuleToDefinedGVSummaries.lookup(Mod.getModuleIdentifier());
+  thinLTOResolvePrevailingInModule(Mod, DefinedGlobals);
+  return true;
+}
+
+extern "C" bool
+LLVMRustPrepareThinLTOInternalize(const LLVMRustThinLTOData *Data, LLVMModuleRef M) {
+  Module &Mod = *unwrap(M);
+  const auto &DefinedGlobals = Data->ModuleToDefinedGVSummaries.lookup(Mod.getModuleIdentifier());
+  thinLTOInternalizeModule(Mod, DefinedGlobals);
+  return true;
+}
+
+extern "C" bool
+LLVMRustPrepareThinLTOImport(const LLVMRustThinLTOData *Data, LLVMModuleRef M,
+                             LLVMTargetMachineRef TM) {
+  Module &Mod = *unwrap(M);
+  TargetMachine &Target = *unwrap(TM);
+
+  const auto &ImportList = Data->ImportLists.lookup(Mod.getModuleIdentifier());
+  auto Loader = [&](StringRef Identifier) {
+    const auto &Memory = Data->ModuleMap.lookup(Identifier);
+    auto &Context = Mod.getContext();
+    auto MOrErr = getLazyBitcodeModule(Memory, Context, true, true);
+
+    if (!MOrErr)
+      return MOrErr;
+
+    // The rest of this closure is a workaround for
+    // https://bugs.llvm.org/show_bug.cgi?id=38184 where during ThinLTO imports
+    // we accidentally import wasm custom sections into different modules,
+    // duplicating them by in the final output artifact.
+    //
+    // The issue is worked around here by manually removing the
+    // `wasm.custom_sections` named metadata node from any imported module. This
+    // we know isn't used by any optimization pass so there's no need for it to
+    // be imported.
+    //
+    // Note that the metadata is currently lazily loaded, so we materialize it
+    // here before looking up if there's metadata inside. The `FunctionImporter`
+    // will immediately materialize metadata anyway after an import, so this
+    // shouldn't be a perf hit.
+    if (Error Err = (*MOrErr)->materializeMetadata()) {
+      Expected<std::unique_ptr<Module>> Ret(std::move(Err));
+      return Ret;
+    }
+
+    auto *WasmCustomSections = (*MOrErr)->getNamedMetadata("wasm.custom_sections");
+    if (WasmCustomSections)
+      WasmCustomSections->eraseFromParent();
+
+    return MOrErr;
+  };
+#if LLVM_VERSION_GE(11, 0)
+  bool ClearDSOLocal = clearDSOLocalOnDeclarations(Mod, Target);
+  FunctionImporter Importer(Data->Index, Loader, ClearDSOLocal);
+#else
+  FunctionImporter Importer(Data->Index, Loader);
+#endif
+  Expected<bool> Result = Importer.importFunctions(Mod, ImportList);
+  if (!Result) {
+    LLVMRustSetLastError(toString(Result.takeError()).c_str());
+    return false;
+  }
+  return true;
+}
+
+extern "C" typedef void (*LLVMRustModuleNameCallback)(void*, // payload
+                                                      const char*, // importing module name
+                                                      const char*); // imported module name
+
+// Calls `module_name_callback` for each module import done by ThinLTO.
+// The callback is provided with regular null-terminated C strings.
+extern "C" void
+LLVMRustGetThinLTOModuleImports(const LLVMRustThinLTOData *data,
+                                LLVMRustModuleNameCallback module_name_callback,
+                                void* callback_payload) {
+  for (const auto& importing_module : data->ImportLists) {
+    const std::string importing_module_id = importing_module.getKey().str();
+    const auto& imports = importing_module.getValue();
+    for (const auto& imported_module : imports) {
+      const std::string imported_module_id = imported_module.getKey().str();
+      module_name_callback(callback_payload,
+                           importing_module_id.c_str(),
+                           imported_module_id.c_str());
+    }
+  }
+}
+
+// This struct and various functions are sort of a hack right now, but the
+// problem is that we've got in-memory LLVM modules after we generate and
+// optimize all codegen-units for one compilation in rustc. To be compatible
+// with the LTO support above we need to serialize the modules plus their
+// ThinLTO summary into memory.
+//
+// This structure is basically an owned version of a serialize module, with
+// a ThinLTO summary attached.
+struct LLVMRustThinLTOBuffer {
+  std::string data;
+};
+
+extern "C" LLVMRustThinLTOBuffer*
+LLVMRustThinLTOBufferCreate(LLVMModuleRef M) {
+#if LLVM_VERSION_GE(10, 0)
+  auto Ret = std::make_unique<LLVMRustThinLTOBuffer>();
+#else
+  auto Ret = llvm::make_unique<LLVMRustThinLTOBuffer>();
+#endif
+  {
+    raw_string_ostream OS(Ret->data);
+    {
+      legacy::PassManager PM;
+      PM.add(createWriteThinLTOBitcodePass(OS));
+      PM.run(*unwrap(M));
+    }
+  }
+  return Ret.release();
+}
+
+extern "C" void
+LLVMRustThinLTOBufferFree(LLVMRustThinLTOBuffer *Buffer) {
+  delete Buffer;
+}
+
+extern "C" const void*
+LLVMRustThinLTOBufferPtr(const LLVMRustThinLTOBuffer *Buffer) {
+  return Buffer->data.data();
+}
+
+extern "C" size_t
+LLVMRustThinLTOBufferLen(const LLVMRustThinLTOBuffer *Buffer) {
+  return Buffer->data.length();
+}
+
+// This is what we used to parse upstream bitcode for actual ThinLTO
+// processing.  We'll call this once per module optimized through ThinLTO, and
+// it'll be called concurrently on many threads.
+extern "C" LLVMModuleRef
+LLVMRustParseBitcodeForLTO(LLVMContextRef Context,
+                           const char *data,
+                           size_t len,
+                           const char *identifier) {
+  StringRef Data(data, len);
+  MemoryBufferRef Buffer(Data, identifier);
+  unwrap(Context)->enableDebugTypeODRUniquing();
+  Expected<std::unique_ptr<Module>> SrcOrError =
+      parseBitcodeFile(Buffer, *unwrap(Context));
+  if (!SrcOrError) {
+    LLVMRustSetLastError(toString(SrcOrError.takeError()).c_str());
+    return nullptr;
+  }
+  return wrap(std::move(*SrcOrError).release());
+}
+
+// Find the bitcode section in the object file data and return it as a slice.
+// Fail if the bitcode section is present but empty.
+//
+// On success, the return value is the pointer to the start of the slice and
+// `out_len` is filled with the (non-zero) length. On failure, the return value
+// is `nullptr` and `out_len` is set to zero.
+extern "C" const char*
+LLVMRustGetBitcodeSliceFromObjectData(const char *data,
+                                      size_t len,
+                                      size_t *out_len) {
+  *out_len = 0;
+
+  StringRef Data(data, len);
+  MemoryBufferRef Buffer(Data, ""); // The id is unused.
+
+  Expected<MemoryBufferRef> BitcodeOrError =
+    object::IRObjectFile::findBitcodeInMemBuffer(Buffer);
+  if (!BitcodeOrError) {
+    LLVMRustSetLastError(toString(BitcodeOrError.takeError()).c_str());
+    return nullptr;
+  }
+
+  *out_len = BitcodeOrError->getBufferSize();
+  return BitcodeOrError->getBufferStart();
+}
+
+// Rewrite all `DICompileUnit` pointers to the `DICompileUnit` specified. See
+// the comment in `back/lto.rs` for why this exists.
+extern "C" void
+LLVMRustThinLTOGetDICompileUnit(LLVMModuleRef Mod,
+                                DICompileUnit **A,
+                                DICompileUnit **B) {
+  Module *M = unwrap(Mod);
+  DICompileUnit **Cur = A;
+  DICompileUnit **Next = B;
+  for (DICompileUnit *CU : M->debug_compile_units()) {
+    *Cur = CU;
+    Cur = Next;
+    Next = nullptr;
+    if (Cur == nullptr)
+      break;
+  }
+}
+
+// Rewrite all `DICompileUnit` pointers to the `DICompileUnit` specified. See
+// the comment in `back/lto.rs` for why this exists.
+extern "C" void
+LLVMRustThinLTOPatchDICompileUnit(LLVMModuleRef Mod, DICompileUnit *Unit) {
+  Module *M = unwrap(Mod);
+
+  // If the original source module didn't have a `DICompileUnit` then try to
+  // merge all the existing compile units. If there aren't actually any though
+  // then there's not much for us to do so return.
+  if (Unit == nullptr) {
+    for (DICompileUnit *CU : M->debug_compile_units()) {
+      Unit = CU;
+      break;
+    }
+    if (Unit == nullptr)
+      return;
+  }
+
+  // Use LLVM's built-in `DebugInfoFinder` to find a bunch of debuginfo and
+  // process it recursively. Note that we used to specifically iterate over
+  // instructions to ensure we feed everything into it, but `processModule`
+  // started doing this the same way in LLVM 7 (commit d769eb36ab2b8).
+  DebugInfoFinder Finder;
+  Finder.processModule(*M);
+
+  // After we've found all our debuginfo, rewrite all subprograms to point to
+  // the same `DICompileUnit`.
+  for (auto &F : Finder.subprograms()) {
+    F->replaceUnit(Unit);
+  }
+
+  // Erase any other references to other `DICompileUnit` instances, the verifier
+  // will later ensure that we don't actually have any other stale references to
+  // worry about.
+  auto *MD = M->getNamedMetadata("llvm.dbg.cu");
+  MD->clearOperands();
+  MD->addOperand(Unit);
+}
diff --git a/compiler/rustc_llvm/llvm-wrapper/README b/compiler/rustc_llvm/llvm-wrapper/README
new file mode 100644
index 00000000000..e1c6dd07d2b
--- /dev/null
+++ b/compiler/rustc_llvm/llvm-wrapper/README
@@ -0,0 +1,16 @@
+This directory currently contains some LLVM support code. This will generally
+be sent upstream to LLVM in time; for now it lives here.
+
+NOTE: the LLVM C++ ABI is subject to between-version breakage and must *never*
+be exposed to Rust. To allow for easy auditing of that, all Rust-exposed types
+must be typedef-ed as "LLVMXyz", or "LLVMRustXyz" if they were defined here.
+
+Functions that return a failure status and leave the error in
+the LLVM last error should return an LLVMRustResult rather than an
+int or anything to avoid confusion.
+
+When translating enums, add a single `Other` variant as the first
+one to allow for new variants to be added. It should abort when used
+as an input.
+
+All other types must not be typedef-ed as such.
diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
new file mode 100644
index 00000000000..e85a9b76380
--- /dev/null
+++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
@@ -0,0 +1,1721 @@
+#include "LLVMWrapper.h"
+#include "llvm/IR/DebugInfoMetadata.h"
+#include "llvm/IR/DiagnosticInfo.h"
+#include "llvm/IR/DiagnosticPrinter.h"
+#include "llvm/IR/GlobalVariable.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Intrinsics.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Bitcode/BitcodeWriterPass.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/ADT/Optional.h"
+
+#include <iostream>
+
+//===----------------------------------------------------------------------===
+//
+// This file defines alternate interfaces to core functions that are more
+// readily callable by Rust's FFI.
+//
+//===----------------------------------------------------------------------===
+
+using namespace llvm;
+using namespace llvm::sys;
+using namespace llvm::object;
+
+// LLVMAtomicOrdering is already an enum - don't create another
+// one.
+static AtomicOrdering fromRust(LLVMAtomicOrdering Ordering) {
+  switch (Ordering) {
+  case LLVMAtomicOrderingNotAtomic:
+    return AtomicOrdering::NotAtomic;
+  case LLVMAtomicOrderingUnordered:
+    return AtomicOrdering::Unordered;
+  case LLVMAtomicOrderingMonotonic:
+    return AtomicOrdering::Monotonic;
+  case LLVMAtomicOrderingAcquire:
+    return AtomicOrdering::Acquire;
+  case LLVMAtomicOrderingRelease:
+    return AtomicOrdering::Release;
+  case LLVMAtomicOrderingAcquireRelease:
+    return AtomicOrdering::AcquireRelease;
+  case LLVMAtomicOrderingSequentiallyConsistent:
+    return AtomicOrdering::SequentiallyConsistent;
+  }
+
+  report_fatal_error("Invalid LLVMAtomicOrdering value!");
+}
+
+static LLVM_THREAD_LOCAL char *LastError;
+
+// Custom error handler for fatal LLVM errors.
+//
+// Notably it exits the process with code 101, unlike LLVM's default of 1.
+static void FatalErrorHandler(void *UserData,
+                              const std::string& Reason,
+                              bool GenCrashDiag) {
+  // Do the same thing that the default error handler does.
+  std::cerr << "LLVM ERROR: " << Reason << std::endl;
+
+  // Since this error handler exits the process, we have to run any cleanup that
+  // LLVM would run after handling the error. This might change with an LLVM
+  // upgrade.
+  sys::RunInterruptHandlers();
+
+  exit(101);
+}
+
+extern "C" void LLVMRustInstallFatalErrorHandler() {
+  install_fatal_error_handler(FatalErrorHandler);
+}
+
+extern "C" LLVMMemoryBufferRef
+LLVMRustCreateMemoryBufferWithContentsOfFile(const char *Path) {
+  ErrorOr<std::unique_ptr<MemoryBuffer>> BufOr =
+      MemoryBuffer::getFile(Path, -1, false);
+  if (!BufOr) {
+    LLVMRustSetLastError(BufOr.getError().message().c_str());
+    return nullptr;
+  }
+  return wrap(BufOr.get().release());
+}
+
+extern "C" char *LLVMRustGetLastError(void) {
+  char *Ret = LastError;
+  LastError = nullptr;
+  return Ret;
+}
+
+extern "C" unsigned int LLVMRustGetInstructionCount(LLVMModuleRef M) {
+  return unwrap(M)->getInstructionCount();
+}
+
+extern "C" void LLVMRustSetLastError(const char *Err) {
+  free((void *)LastError);
+  LastError = strdup(Err);
+}
+
+extern "C" LLVMContextRef LLVMRustContextCreate(bool shouldDiscardNames) {
+  auto ctx = new LLVMContext();
+  ctx->setDiscardValueNames(shouldDiscardNames);
+  return wrap(ctx);
+}
+
+extern "C" void LLVMRustSetNormalizedTarget(LLVMModuleRef M,
+                                            const char *Triple) {
+  unwrap(M)->setTargetTriple(Triple::normalize(Triple));
+}
+
+extern "C" void LLVMRustPrintPassTimings() {
+  raw_fd_ostream OS(2, false); // stderr.
+  TimerGroup::printAll(OS);
+}
+
+extern "C" LLVMValueRef LLVMRustGetNamedValue(LLVMModuleRef M, const char *Name,
+                                              size_t NameLen) {
+  return wrap(unwrap(M)->getNamedValue(StringRef(Name, NameLen)));
+}
+
+extern "C" LLVMValueRef LLVMRustGetOrInsertFunction(LLVMModuleRef M,
+                                                    const char *Name,
+                                                    size_t NameLen,
+                                                    LLVMTypeRef FunctionTy) {
+  return wrap(unwrap(M)
+                  ->getOrInsertFunction(StringRef(Name, NameLen),
+                                        unwrap<FunctionType>(FunctionTy))
+#if LLVM_VERSION_GE(9, 0)
+                  .getCallee()
+#endif
+  );
+}
+
+extern "C" LLVMValueRef
+LLVMRustGetOrInsertGlobal(LLVMModuleRef M, const char *Name, size_t NameLen, LLVMTypeRef Ty) {
+  StringRef NameRef(Name, NameLen);
+  return wrap(unwrap(M)->getOrInsertGlobal(NameRef, unwrap(Ty)));
+}
+
+extern "C" LLVMValueRef
+LLVMRustInsertPrivateGlobal(LLVMModuleRef M, LLVMTypeRef Ty) {
+  return wrap(new GlobalVariable(*unwrap(M),
+                                 unwrap(Ty),
+                                 false,
+                                 GlobalValue::PrivateLinkage,
+                                 nullptr));
+}
+
+extern "C" LLVMTypeRef LLVMRustMetadataTypeInContext(LLVMContextRef C) {
+  return wrap(Type::getMetadataTy(*unwrap(C)));
+}
+
+static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) {
+  switch (Kind) {
+  case AlwaysInline:
+    return Attribute::AlwaysInline;
+  case ByVal:
+    return Attribute::ByVal;
+  case Cold:
+    return Attribute::Cold;
+  case InlineHint:
+    return Attribute::InlineHint;
+  case MinSize:
+    return Attribute::MinSize;
+  case Naked:
+    return Attribute::Naked;
+  case NoAlias:
+    return Attribute::NoAlias;
+  case NoCapture:
+    return Attribute::NoCapture;
+  case NoInline:
+    return Attribute::NoInline;
+  case NonNull:
+    return Attribute::NonNull;
+  case NoRedZone:
+    return Attribute::NoRedZone;
+  case NoReturn:
+    return Attribute::NoReturn;
+  case NoUnwind:
+    return Attribute::NoUnwind;
+  case OptimizeForSize:
+    return Attribute::OptimizeForSize;
+  case ReadOnly:
+    return Attribute::ReadOnly;
+  case SExt:
+    return Attribute::SExt;
+  case StructRet:
+    return Attribute::StructRet;
+  case UWTable:
+    return Attribute::UWTable;
+  case ZExt:
+    return Attribute::ZExt;
+  case InReg:
+    return Attribute::InReg;
+  case SanitizeThread:
+    return Attribute::SanitizeThread;
+  case SanitizeAddress:
+    return Attribute::SanitizeAddress;
+  case SanitizeMemory:
+    return Attribute::SanitizeMemory;
+  case NonLazyBind:
+    return Attribute::NonLazyBind;
+  case OptimizeNone:
+    return Attribute::OptimizeNone;
+  case ReturnsTwice:
+    return Attribute::ReturnsTwice;
+  case ReadNone:
+    return Attribute::ReadNone;
+  case InaccessibleMemOnly:
+    return Attribute::InaccessibleMemOnly;
+  }
+  report_fatal_error("bad AttributeKind");
+}
+
+extern "C" void LLVMRustAddCallSiteAttribute(LLVMValueRef Instr, unsigned Index,
+                                             LLVMRustAttribute RustAttr) {
+  CallBase *Call = unwrap<CallBase>(Instr);
+  Attribute Attr = Attribute::get(Call->getContext(), fromRust(RustAttr));
+  Call->addAttribute(Index, Attr);
+}
+
+extern "C" void LLVMRustAddAlignmentCallSiteAttr(LLVMValueRef Instr,
+                                                 unsigned Index,
+                                                 uint32_t Bytes) {
+  CallBase *Call = unwrap<CallBase>(Instr);
+  AttrBuilder B;
+  B.addAlignmentAttr(Bytes);
+  Call->setAttributes(Call->getAttributes().addAttributes(
+      Call->getContext(), Index, B));
+}
+
+extern "C" void LLVMRustAddDereferenceableCallSiteAttr(LLVMValueRef Instr,
+                                                       unsigned Index,
+                                                       uint64_t Bytes) {
+  CallBase *Call = unwrap<CallBase>(Instr);
+  AttrBuilder B;
+  B.addDereferenceableAttr(Bytes);
+  Call->setAttributes(Call->getAttributes().addAttributes(
+      Call->getContext(), Index, B));
+}
+
+extern "C" void LLVMRustAddDereferenceableOrNullCallSiteAttr(LLVMValueRef Instr,
+                                                             unsigned Index,
+                                                             uint64_t Bytes) {
+  CallBase *Call = unwrap<CallBase>(Instr);
+  AttrBuilder B;
+  B.addDereferenceableOrNullAttr(Bytes);
+  Call->setAttributes(Call->getAttributes().addAttributes(
+      Call->getContext(), Index, B));
+}
+
+extern "C" void LLVMRustAddByValCallSiteAttr(LLVMValueRef Instr, unsigned Index,
+                                             LLVMTypeRef Ty) {
+  CallBase *Call = unwrap<CallBase>(Instr);
+#if LLVM_VERSION_GE(9, 0)
+  Attribute Attr = Attribute::getWithByValType(Call->getContext(), unwrap(Ty));
+#else
+  Attribute Attr = Attribute::get(Call->getContext(), Attribute::ByVal);
+#endif
+  Call->addAttribute(Index, Attr);
+}
+
+extern "C" void LLVMRustAddFunctionAttribute(LLVMValueRef Fn, unsigned Index,
+                                             LLVMRustAttribute RustAttr) {
+  Function *A = unwrap<Function>(Fn);
+  Attribute Attr = Attribute::get(A->getContext(), fromRust(RustAttr));
+  AttrBuilder B(Attr);
+  A->addAttributes(Index, B);
+}
+
+extern "C" void LLVMRustAddAlignmentAttr(LLVMValueRef Fn,
+                                         unsigned Index,
+                                         uint32_t Bytes) {
+  Function *A = unwrap<Function>(Fn);
+  AttrBuilder B;
+  B.addAlignmentAttr(Bytes);
+  A->addAttributes(Index, B);
+}
+
+extern "C" void LLVMRustAddDereferenceableAttr(LLVMValueRef Fn, unsigned Index,
+                                               uint64_t Bytes) {
+  Function *A = unwrap<Function>(Fn);
+  AttrBuilder B;
+  B.addDereferenceableAttr(Bytes);
+  A->addAttributes(Index, B);
+}
+
+extern "C" void LLVMRustAddDereferenceableOrNullAttr(LLVMValueRef Fn,
+                                                     unsigned Index,
+                                                     uint64_t Bytes) {
+  Function *A = unwrap<Function>(Fn);
+  AttrBuilder B;
+  B.addDereferenceableOrNullAttr(Bytes);
+  A->addAttributes(Index, B);
+}
+
+extern "C" void LLVMRustAddByValAttr(LLVMValueRef Fn, unsigned Index,
+                                     LLVMTypeRef Ty) {
+  Function *F = unwrap<Function>(Fn);
+#if LLVM_VERSION_GE(9, 0)
+  Attribute Attr = Attribute::getWithByValType(F->getContext(), unwrap(Ty));
+#else
+  Attribute Attr = Attribute::get(F->getContext(), Attribute::ByVal);
+#endif
+  F->addAttribute(Index, Attr);
+}
+
+extern "C" void LLVMRustAddFunctionAttrStringValue(LLVMValueRef Fn,
+                                                   unsigned Index,
+                                                   const char *Name,
+                                                   const char *Value) {
+  Function *F = unwrap<Function>(Fn);
+  AttrBuilder B;
+  B.addAttribute(Name, Value);
+  F->addAttributes(Index, B);
+}
+
+extern "C" void LLVMRustRemoveFunctionAttributes(LLVMValueRef Fn,
+                                                 unsigned Index,
+                                                 LLVMRustAttribute RustAttr) {
+  Function *F = unwrap<Function>(Fn);
+  Attribute Attr = Attribute::get(F->getContext(), fromRust(RustAttr));
+  AttrBuilder B(Attr);
+  auto PAL = F->getAttributes();
+  auto PALNew = PAL.removeAttributes(F->getContext(), Index, B);
+  F->setAttributes(PALNew);
+}
+
+// enable fpmath flag UnsafeAlgebra
+extern "C" void LLVMRustSetHasUnsafeAlgebra(LLVMValueRef V) {
+  if (auto I = dyn_cast<Instruction>(unwrap<Value>(V))) {
+    I->setFast(true);
+  }
+}
+
+extern "C" LLVMValueRef
+LLVMRustBuildAtomicLoad(LLVMBuilderRef B, LLVMValueRef Source, const char *Name,
+                        LLVMAtomicOrdering Order) {
+  Value *Ptr = unwrap(Source);
+  Type *Ty = Ptr->getType()->getPointerElementType();
+  LoadInst *LI = unwrap(B)->CreateLoad(Ty, Ptr, Name);
+  LI->setAtomic(fromRust(Order));
+  return wrap(LI);
+}
+
+extern "C" LLVMValueRef LLVMRustBuildAtomicStore(LLVMBuilderRef B,
+                                                 LLVMValueRef V,
+                                                 LLVMValueRef Target,
+                                                 LLVMAtomicOrdering Order) {
+  StoreInst *SI = unwrap(B)->CreateStore(unwrap(V), unwrap(Target));
+  SI->setAtomic(fromRust(Order));
+  return wrap(SI);
+}
+
+// FIXME: Use the C-API LLVMBuildAtomicCmpXchg and LLVMSetWeak
+// once we raise our minimum support to LLVM 10.
+extern "C" LLVMValueRef
+LLVMRustBuildAtomicCmpXchg(LLVMBuilderRef B, LLVMValueRef Target,
+                           LLVMValueRef Old, LLVMValueRef Source,
+                           LLVMAtomicOrdering Order,
+                           LLVMAtomicOrdering FailureOrder, LLVMBool Weak) {
+  AtomicCmpXchgInst *ACXI = unwrap(B)->CreateAtomicCmpXchg(
+      unwrap(Target), unwrap(Old), unwrap(Source), fromRust(Order),
+      fromRust(FailureOrder));
+  ACXI->setWeak(Weak);
+  return wrap(ACXI);
+}
+
+enum class LLVMRustSynchronizationScope {
+  SingleThread,
+  CrossThread,
+};
+
+static SyncScope::ID fromRust(LLVMRustSynchronizationScope Scope) {
+  switch (Scope) {
+  case LLVMRustSynchronizationScope::SingleThread:
+    return SyncScope::SingleThread;
+  case LLVMRustSynchronizationScope::CrossThread:
+    return SyncScope::System;
+  default:
+    report_fatal_error("bad SynchronizationScope.");
+  }
+}
+
+extern "C" LLVMValueRef
+LLVMRustBuildAtomicFence(LLVMBuilderRef B, LLVMAtomicOrdering Order,
+                         LLVMRustSynchronizationScope Scope) {
+  return wrap(unwrap(B)->CreateFence(fromRust(Order), fromRust(Scope)));
+}
+
+enum class LLVMRustAsmDialect {
+  Att,
+  Intel,
+};
+
+static InlineAsm::AsmDialect fromRust(LLVMRustAsmDialect Dialect) {
+  switch (Dialect) {
+  case LLVMRustAsmDialect::Att:
+    return InlineAsm::AD_ATT;
+  case LLVMRustAsmDialect::Intel:
+    return InlineAsm::AD_Intel;
+  default:
+    report_fatal_error("bad AsmDialect.");
+  }
+}
+
+extern "C" LLVMValueRef
+LLVMRustInlineAsm(LLVMTypeRef Ty, char *AsmString, size_t AsmStringLen,
+                  char *Constraints, size_t ConstraintsLen,
+                  LLVMBool HasSideEffects, LLVMBool IsAlignStack,
+                  LLVMRustAsmDialect Dialect) {
+  return wrap(InlineAsm::get(unwrap<FunctionType>(Ty),
+                             StringRef(AsmString, AsmStringLen),
+                             StringRef(Constraints, ConstraintsLen),
+                             HasSideEffects, IsAlignStack, fromRust(Dialect)));
+}
+
+extern "C" bool LLVMRustInlineAsmVerify(LLVMTypeRef Ty, char *Constraints,
+                                        size_t ConstraintsLen) {
+  return InlineAsm::Verify(unwrap<FunctionType>(Ty),
+                           StringRef(Constraints, ConstraintsLen));
+}
+
+extern "C" void LLVMRustAppendModuleInlineAsm(LLVMModuleRef M, const char *Asm,
+                                              size_t AsmLen) {
+  unwrap(M)->appendModuleInlineAsm(StringRef(Asm, AsmLen));
+}
+
+typedef DIBuilder *LLVMRustDIBuilderRef;
+
+template <typename DIT> DIT *unwrapDIPtr(LLVMMetadataRef Ref) {
+  return (DIT *)(Ref ? unwrap<MDNode>(Ref) : nullptr);
+}
+
+#define DIDescriptor DIScope
+#define DIArray DINodeArray
+#define unwrapDI unwrapDIPtr
+
+// These values **must** match debuginfo::DIFlags! They also *happen*
+// to match LLVM, but that isn't required as we do giant sets of
+// matching below. The value shouldn't be directly passed to LLVM.
+enum class LLVMRustDIFlags : uint32_t {
+  FlagZero = 0,
+  FlagPrivate = 1,
+  FlagProtected = 2,
+  FlagPublic = 3,
+  FlagFwdDecl = (1 << 2),
+  FlagAppleBlock = (1 << 3),
+  FlagBlockByrefStruct = (1 << 4),
+  FlagVirtual = (1 << 5),
+  FlagArtificial = (1 << 6),
+  FlagExplicit = (1 << 7),
+  FlagPrototyped = (1 << 8),
+  FlagObjcClassComplete = (1 << 9),
+  FlagObjectPointer = (1 << 10),
+  FlagVector = (1 << 11),
+  FlagStaticMember = (1 << 12),
+  FlagLValueReference = (1 << 13),
+  FlagRValueReference = (1 << 14),
+  FlagExternalTypeRef = (1 << 15),
+  FlagIntroducedVirtual = (1 << 18),
+  FlagBitField = (1 << 19),
+  FlagNoReturn = (1 << 20),
+  // Do not add values that are not supported by the minimum LLVM
+  // version we support! see llvm/include/llvm/IR/DebugInfoFlags.def
+};
+
+inline LLVMRustDIFlags operator&(LLVMRustDIFlags A, LLVMRustDIFlags B) {
+  return static_cast<LLVMRustDIFlags>(static_cast<uint32_t>(A) &
+                                      static_cast<uint32_t>(B));
+}
+
+inline LLVMRustDIFlags operator|(LLVMRustDIFlags A, LLVMRustDIFlags B) {
+  return static_cast<LLVMRustDIFlags>(static_cast<uint32_t>(A) |
+                                      static_cast<uint32_t>(B));
+}
+
+inline LLVMRustDIFlags &operator|=(LLVMRustDIFlags &A, LLVMRustDIFlags B) {
+  return A = A | B;
+}
+
+inline bool isSet(LLVMRustDIFlags F) { return F != LLVMRustDIFlags::FlagZero; }
+
+inline LLVMRustDIFlags visibility(LLVMRustDIFlags F) {
+  return static_cast<LLVMRustDIFlags>(static_cast<uint32_t>(F) & 0x3);
+}
+
+static DINode::DIFlags fromRust(LLVMRustDIFlags Flags) {
+  DINode::DIFlags Result = DINode::DIFlags::FlagZero;
+
+  switch (visibility(Flags)) {
+  case LLVMRustDIFlags::FlagPrivate:
+    Result |= DINode::DIFlags::FlagPrivate;
+    break;
+  case LLVMRustDIFlags::FlagProtected:
+    Result |= DINode::DIFlags::FlagProtected;
+    break;
+  case LLVMRustDIFlags::FlagPublic:
+    Result |= DINode::DIFlags::FlagPublic;
+    break;
+  default:
+    // The rest are handled below
+    break;
+  }
+
+  if (isSet(Flags & LLVMRustDIFlags::FlagFwdDecl)) {
+    Result |= DINode::DIFlags::FlagFwdDecl;
+  }
+  if (isSet(Flags & LLVMRustDIFlags::FlagAppleBlock)) {
+    Result |= DINode::DIFlags::FlagAppleBlock;
+  }
+#if LLVM_VERSION_LT(10, 0)
+  if (isSet(Flags & LLVMRustDIFlags::FlagBlockByrefStruct)) {
+    Result |= DINode::DIFlags::FlagBlockByrefStruct;
+  }
+#endif
+  if (isSet(Flags & LLVMRustDIFlags::FlagVirtual)) {
+    Result |= DINode::DIFlags::FlagVirtual;
+  }
+  if (isSet(Flags & LLVMRustDIFlags::FlagArtificial)) {
+    Result |= DINode::DIFlags::FlagArtificial;
+  }
+  if (isSet(Flags & LLVMRustDIFlags::FlagExplicit)) {
+    Result |= DINode::DIFlags::FlagExplicit;
+  }
+  if (isSet(Flags & LLVMRustDIFlags::FlagPrototyped)) {
+    Result |= DINode::DIFlags::FlagPrototyped;
+  }
+  if (isSet(Flags & LLVMRustDIFlags::FlagObjcClassComplete)) {
+    Result |= DINode::DIFlags::FlagObjcClassComplete;
+  }
+  if (isSet(Flags & LLVMRustDIFlags::FlagObjectPointer)) {
+    Result |= DINode::DIFlags::FlagObjectPointer;
+  }
+  if (isSet(Flags & LLVMRustDIFlags::FlagVector)) {
+    Result |= DINode::DIFlags::FlagVector;
+  }
+  if (isSet(Flags & LLVMRustDIFlags::FlagStaticMember)) {
+    Result |= DINode::DIFlags::FlagStaticMember;
+  }
+  if (isSet(Flags & LLVMRustDIFlags::FlagLValueReference)) {
+    Result |= DINode::DIFlags::FlagLValueReference;
+  }
+  if (isSet(Flags & LLVMRustDIFlags::FlagRValueReference)) {
+    Result |= DINode::DIFlags::FlagRValueReference;
+  }
+  if (isSet(Flags & LLVMRustDIFlags::FlagIntroducedVirtual)) {
+    Result |= DINode::DIFlags::FlagIntroducedVirtual;
+  }
+  if (isSet(Flags & LLVMRustDIFlags::FlagBitField)) {
+    Result |= DINode::DIFlags::FlagBitField;
+  }
+  if (isSet(Flags & LLVMRustDIFlags::FlagNoReturn)) {
+    Result |= DINode::DIFlags::FlagNoReturn;
+  }
+
+  return Result;
+}
+
+// These values **must** match debuginfo::DISPFlags! They also *happen*
+// to match LLVM, but that isn't required as we do giant sets of
+// matching below. The value shouldn't be directly passed to LLVM.
+enum class LLVMRustDISPFlags : uint32_t {
+  SPFlagZero = 0,
+  SPFlagVirtual = 1,
+  SPFlagPureVirtual = 2,
+  SPFlagLocalToUnit = (1 << 2),
+  SPFlagDefinition = (1 << 3),
+  SPFlagOptimized = (1 << 4),
+  SPFlagMainSubprogram = (1 << 5),
+  // Do not add values that are not supported by the minimum LLVM
+  // version we support! see llvm/include/llvm/IR/DebugInfoFlags.def
+  // (In LLVM < 8, createFunction supported these as separate bool arguments.)
+};
+
+inline LLVMRustDISPFlags operator&(LLVMRustDISPFlags A, LLVMRustDISPFlags B) {
+  return static_cast<LLVMRustDISPFlags>(static_cast<uint32_t>(A) &
+                                      static_cast<uint32_t>(B));
+}
+
+inline LLVMRustDISPFlags operator|(LLVMRustDISPFlags A, LLVMRustDISPFlags B) {
+  return static_cast<LLVMRustDISPFlags>(static_cast<uint32_t>(A) |
+                                      static_cast<uint32_t>(B));
+}
+
+inline LLVMRustDISPFlags &operator|=(LLVMRustDISPFlags &A, LLVMRustDISPFlags B) {
+  return A = A | B;
+}
+
+inline bool isSet(LLVMRustDISPFlags F) { return F != LLVMRustDISPFlags::SPFlagZero; }
+
+inline LLVMRustDISPFlags virtuality(LLVMRustDISPFlags F) {
+  return static_cast<LLVMRustDISPFlags>(static_cast<uint32_t>(F) & 0x3);
+}
+
+static DISubprogram::DISPFlags fromRust(LLVMRustDISPFlags SPFlags) {
+  DISubprogram::DISPFlags Result = DISubprogram::DISPFlags::SPFlagZero;
+
+  switch (virtuality(SPFlags)) {
+  case LLVMRustDISPFlags::SPFlagVirtual:
+    Result |= DISubprogram::DISPFlags::SPFlagVirtual;
+    break;
+  case LLVMRustDISPFlags::SPFlagPureVirtual:
+    Result |= DISubprogram::DISPFlags::SPFlagPureVirtual;
+    break;
+  default:
+    // The rest are handled below
+    break;
+  }
+
+  if (isSet(SPFlags & LLVMRustDISPFlags::SPFlagLocalToUnit)) {
+    Result |= DISubprogram::DISPFlags::SPFlagLocalToUnit;
+  }
+  if (isSet(SPFlags & LLVMRustDISPFlags::SPFlagDefinition)) {
+    Result |= DISubprogram::DISPFlags::SPFlagDefinition;
+  }
+  if (isSet(SPFlags & LLVMRustDISPFlags::SPFlagOptimized)) {
+    Result |= DISubprogram::DISPFlags::SPFlagOptimized;
+  }
+#if LLVM_VERSION_GE(9, 0)
+  if (isSet(SPFlags & LLVMRustDISPFlags::SPFlagMainSubprogram)) {
+    Result |= DISubprogram::DISPFlags::SPFlagMainSubprogram;
+  }
+#endif
+
+  return Result;
+}
+
+enum class LLVMRustDebugEmissionKind {
+  NoDebug,
+  FullDebug,
+  LineTablesOnly,
+};
+
+static DICompileUnit::DebugEmissionKind fromRust(LLVMRustDebugEmissionKind Kind) {
+  switch (Kind) {
+  case LLVMRustDebugEmissionKind::NoDebug:
+    return DICompileUnit::DebugEmissionKind::NoDebug;
+  case LLVMRustDebugEmissionKind::FullDebug:
+    return DICompileUnit::DebugEmissionKind::FullDebug;
+  case LLVMRustDebugEmissionKind::LineTablesOnly:
+    return DICompileUnit::DebugEmissionKind::LineTablesOnly;
+  default:
+    report_fatal_error("bad DebugEmissionKind.");
+  }
+}
+
+enum class LLVMRustChecksumKind {
+  None,
+  MD5,
+  SHA1,
+};
+
+static Optional<DIFile::ChecksumKind> fromRust(LLVMRustChecksumKind Kind) {
+  switch (Kind) {
+  case LLVMRustChecksumKind::None:
+    return None;
+  case LLVMRustChecksumKind::MD5:
+    return DIFile::ChecksumKind::CSK_MD5;
+  case LLVMRustChecksumKind::SHA1:
+    return DIFile::ChecksumKind::CSK_SHA1;
+  default:
+    report_fatal_error("bad ChecksumKind.");
+  }
+}
+
+extern "C" uint32_t LLVMRustDebugMetadataVersion() {
+  return DEBUG_METADATA_VERSION;
+}
+
+extern "C" uint32_t LLVMRustVersionMinor() { return LLVM_VERSION_MINOR; }
+
+extern "C" uint32_t LLVMRustVersionMajor() { return LLVM_VERSION_MAJOR; }
+
+extern "C" void LLVMRustAddModuleFlag(LLVMModuleRef M, const char *Name,
+                                      uint32_t Value) {
+  unwrap(M)->addModuleFlag(Module::Warning, Name, Value);
+}
+
+extern "C" LLVMValueRef LLVMRustMetadataAsValue(LLVMContextRef C, LLVMMetadataRef MD) {
+  return wrap(MetadataAsValue::get(*unwrap(C), unwrap(MD)));
+}
+
+extern "C" LLVMRustDIBuilderRef LLVMRustDIBuilderCreate(LLVMModuleRef M) {
+  return new DIBuilder(*unwrap(M));
+}
+
+extern "C" void LLVMRustDIBuilderDispose(LLVMRustDIBuilderRef Builder) {
+  delete Builder;
+}
+
+extern "C" void LLVMRustDIBuilderFinalize(LLVMRustDIBuilderRef Builder) {
+  Builder->finalize();
+}
+
+extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateCompileUnit(
+    LLVMRustDIBuilderRef Builder, unsigned Lang, LLVMMetadataRef FileRef,
+    const char *Producer, size_t ProducerLen, bool isOptimized,
+    const char *Flags, unsigned RuntimeVer,
+    const char *SplitName, size_t SplitNameLen,
+    LLVMRustDebugEmissionKind Kind) {
+  auto *File = unwrapDI<DIFile>(FileRef);
+
+  return wrap(Builder->createCompileUnit(Lang, File, StringRef(Producer, ProducerLen),
+                                         isOptimized, Flags, RuntimeVer,
+                                         StringRef(SplitName, SplitNameLen),
+                                         fromRust(Kind)));
+}
+
+extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFile(
+    LLVMRustDIBuilderRef Builder,
+    const char *Filename, size_t FilenameLen,
+    const char *Directory, size_t DirectoryLen, LLVMRustChecksumKind CSKind,
+    const char *Checksum, size_t ChecksumLen) {
+  Optional<DIFile::ChecksumKind> llvmCSKind = fromRust(CSKind);
+  Optional<DIFile::ChecksumInfo<StringRef>> CSInfo{};
+  if (llvmCSKind)
+    CSInfo.emplace(*llvmCSKind, StringRef{Checksum, ChecksumLen});
+  return wrap(Builder->createFile(StringRef(Filename, FilenameLen),
+                                  StringRef(Directory, DirectoryLen),
+                                  CSInfo));
+}
+
+extern "C" LLVMMetadataRef
+LLVMRustDIBuilderCreateSubroutineType(LLVMRustDIBuilderRef Builder,
+                                      LLVMMetadataRef ParameterTypes) {
+  return wrap(Builder->createSubroutineType(
+      DITypeRefArray(unwrap<MDTuple>(ParameterTypes))));
+}
+
+extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFunction(
+    LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope,
+    const char *Name, size_t NameLen,
+    const char *LinkageName, size_t LinkageNameLen,
+    LLVMMetadataRef File, unsigned LineNo,
+    LLVMMetadataRef Ty, unsigned ScopeLine, LLVMRustDIFlags Flags,
+    LLVMRustDISPFlags SPFlags, LLVMValueRef Fn, LLVMMetadataRef TParam,
+    LLVMMetadataRef Decl) {
+  DITemplateParameterArray TParams =
+      DITemplateParameterArray(unwrap<MDTuple>(TParam));
+  DISubprogram::DISPFlags llvmSPFlags = fromRust(SPFlags);
+  DINode::DIFlags llvmFlags = fromRust(Flags);
+#if LLVM_VERSION_LT(9, 0)
+  if (isSet(SPFlags & LLVMRustDISPFlags::SPFlagMainSubprogram))
+    llvmFlags |= DINode::DIFlags::FlagMainSubprogram;
+#endif
+  DISubprogram *Sub = Builder->createFunction(
+      unwrapDI<DIScope>(Scope),
+      StringRef(Name, NameLen),
+      StringRef(LinkageName, LinkageNameLen),
+      unwrapDI<DIFile>(File), LineNo,
+      unwrapDI<DISubroutineType>(Ty), ScopeLine, llvmFlags,
+      llvmSPFlags, TParams, unwrapDIPtr<DISubprogram>(Decl));
+  unwrap<Function>(Fn)->setSubprogram(Sub);
+  return wrap(Sub);
+}
+
+extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateBasicType(
+    LLVMRustDIBuilderRef Builder, const char *Name, size_t NameLen,
+    uint64_t SizeInBits, unsigned Encoding) {
+  return wrap(Builder->createBasicType(StringRef(Name, NameLen), SizeInBits, Encoding));
+}
+
+extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateTypedef(
+    LLVMRustDIBuilderRef Builder, LLVMMetadataRef Type, const char *Name, size_t NameLen,
+    LLVMMetadataRef File, unsigned LineNo, LLVMMetadataRef Scope) {
+  return wrap(Builder->createTypedef(
+    unwrap<DIType>(Type), StringRef(Name, NameLen), unwrap<DIFile>(File),
+    LineNo, unwrap<DIScope>(Scope)));
+}
+
+extern "C" LLVMMetadataRef LLVMRustDIBuilderCreatePointerType(
+    LLVMRustDIBuilderRef Builder, LLVMMetadataRef PointeeTy,
+    uint64_t SizeInBits, uint32_t AlignInBits, unsigned AddressSpace,
+    const char *Name, size_t NameLen) {
+  return wrap(Builder->createPointerType(unwrapDI<DIType>(PointeeTy),
+                                         SizeInBits, AlignInBits,
+                                         AddressSpace,
+                                         StringRef(Name, NameLen)));
+}
+
+extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStructType(
+    LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope,
+    const char *Name, size_t NameLen,
+    LLVMMetadataRef File, unsigned LineNumber, uint64_t SizeInBits,
+    uint32_t AlignInBits, LLVMRustDIFlags Flags,
+    LLVMMetadataRef DerivedFrom, LLVMMetadataRef Elements,
+    unsigned RunTimeLang, LLVMMetadataRef VTableHolder,
+    const char *UniqueId, size_t UniqueIdLen) {
+  return wrap(Builder->createStructType(
+      unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen),
+      unwrapDI<DIFile>(File), LineNumber,
+      SizeInBits, AlignInBits, fromRust(Flags), unwrapDI<DIType>(DerivedFrom),
+      DINodeArray(unwrapDI<MDTuple>(Elements)), RunTimeLang,
+      unwrapDI<DIType>(VTableHolder), StringRef(UniqueId, UniqueIdLen)));
+}
+
+extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantPart(
+    LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope,
+    const char *Name, size_t NameLen,
+    LLVMMetadataRef File, unsigned LineNumber, uint64_t SizeInBits,
+    uint32_t AlignInBits, LLVMRustDIFlags Flags, LLVMMetadataRef Discriminator,
+    LLVMMetadataRef Elements, const char *UniqueId, size_t UniqueIdLen) {
+  return wrap(Builder->createVariantPart(
+      unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen),
+      unwrapDI<DIFile>(File), LineNumber,
+      SizeInBits, AlignInBits, fromRust(Flags), unwrapDI<DIDerivedType>(Discriminator),
+      DINodeArray(unwrapDI<MDTuple>(Elements)), StringRef(UniqueId, UniqueIdLen)));
+}
+
+extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateMemberType(
+    LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope,
+    const char *Name, size_t NameLen,
+    LLVMMetadataRef File, unsigned LineNo, uint64_t SizeInBits,
+    uint32_t AlignInBits, uint64_t OffsetInBits, LLVMRustDIFlags Flags,
+    LLVMMetadataRef Ty) {
+  return wrap(Builder->createMemberType(unwrapDI<DIDescriptor>(Scope),
+                                        StringRef(Name, NameLen),
+                                        unwrapDI<DIFile>(File), LineNo,
+                                        SizeInBits, AlignInBits, OffsetInBits,
+                                        fromRust(Flags), unwrapDI<DIType>(Ty)));
+}
+
+extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantMemberType(
+    LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope,
+    const char *Name, size_t NameLen, LLVMMetadataRef File, unsigned LineNo,
+    uint64_t SizeInBits, uint32_t AlignInBits, uint64_t OffsetInBits, LLVMValueRef Discriminant,
+    LLVMRustDIFlags Flags, LLVMMetadataRef Ty) {
+  llvm::ConstantInt* D = nullptr;
+  if (Discriminant) {
+    D = unwrap<llvm::ConstantInt>(Discriminant);
+  }
+  return wrap(Builder->createVariantMemberType(unwrapDI<DIDescriptor>(Scope),
+                                               StringRef(Name, NameLen),
+                                               unwrapDI<DIFile>(File), LineNo,
+                                               SizeInBits, AlignInBits, OffsetInBits, D,
+                                               fromRust(Flags), unwrapDI<DIType>(Ty)));
+}
+
+extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateLexicalBlock(
+    LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope,
+    LLVMMetadataRef File, unsigned Line, unsigned Col) {
+  return wrap(Builder->createLexicalBlock(unwrapDI<DIDescriptor>(Scope),
+                                          unwrapDI<DIFile>(File), Line, Col));
+}
+
+extern "C" LLVMMetadataRef
+LLVMRustDIBuilderCreateLexicalBlockFile(LLVMRustDIBuilderRef Builder,
+                                        LLVMMetadataRef Scope,
+                                        LLVMMetadataRef File) {
+  return wrap(Builder->createLexicalBlockFile(unwrapDI<DIDescriptor>(Scope),
+                                              unwrapDI<DIFile>(File)));
+}
+
+extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStaticVariable(
+    LLVMRustDIBuilderRef Builder, LLVMMetadataRef Context,
+    const char *Name, size_t NameLen,
+    const char *LinkageName, size_t LinkageNameLen,
+    LLVMMetadataRef File, unsigned LineNo,
+    LLVMMetadataRef Ty, bool IsLocalToUnit, LLVMValueRef V,
+    LLVMMetadataRef Decl = nullptr, uint32_t AlignInBits = 0) {
+  llvm::GlobalVariable *InitVal = cast<llvm::GlobalVariable>(unwrap(V));
+
+  llvm::DIExpression *InitExpr = nullptr;
+  if (llvm::ConstantInt *IntVal = llvm::dyn_cast<llvm::ConstantInt>(InitVal)) {
+    InitExpr = Builder->createConstantValueExpression(
+        IntVal->getValue().getSExtValue());
+  } else if (llvm::ConstantFP *FPVal =
+                 llvm::dyn_cast<llvm::ConstantFP>(InitVal)) {
+    InitExpr = Builder->createConstantValueExpression(
+        FPVal->getValueAPF().bitcastToAPInt().getZExtValue());
+  }
+
+  llvm::DIGlobalVariableExpression *VarExpr = Builder->createGlobalVariableExpression(
+      unwrapDI<DIDescriptor>(Context), StringRef(Name, NameLen),
+      StringRef(LinkageName, LinkageNameLen),
+      unwrapDI<DIFile>(File), LineNo, unwrapDI<DIType>(Ty), IsLocalToUnit,
+#if LLVM_VERSION_GE(10, 0)
+      /* isDefined */ true,
+#endif
+      InitExpr, unwrapDIPtr<MDNode>(Decl),
+      /* templateParams */ nullptr,
+      AlignInBits);
+
+  InitVal->setMetadata("dbg", VarExpr);
+
+  return wrap(VarExpr);
+}
+
+extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariable(
+    LLVMRustDIBuilderRef Builder, unsigned Tag, LLVMMetadataRef Scope,
+    const char *Name, size_t NameLen,
+    LLVMMetadataRef File, unsigned LineNo,
+    LLVMMetadataRef Ty, bool AlwaysPreserve, LLVMRustDIFlags Flags,
+    unsigned ArgNo, uint32_t AlignInBits) {
+  if (Tag == 0x100) { // DW_TAG_auto_variable
+    return wrap(Builder->createAutoVariable(
+        unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen),
+        unwrapDI<DIFile>(File), LineNo,
+        unwrapDI<DIType>(Ty), AlwaysPreserve, fromRust(Flags), AlignInBits));
+  } else {
+    return wrap(Builder->createParameterVariable(
+        unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), ArgNo,
+        unwrapDI<DIFile>(File), LineNo,
+        unwrapDI<DIType>(Ty), AlwaysPreserve, fromRust(Flags)));
+  }
+}
+
+extern "C" LLVMMetadataRef
+LLVMRustDIBuilderCreateArrayType(LLVMRustDIBuilderRef Builder, uint64_t Size,
+                                 uint32_t AlignInBits, LLVMMetadataRef Ty,
+                                 LLVMMetadataRef Subscripts) {
+  return wrap(
+      Builder->createArrayType(Size, AlignInBits, unwrapDI<DIType>(Ty),
+                               DINodeArray(unwrapDI<MDTuple>(Subscripts))));
+}
+
+extern "C" LLVMMetadataRef
+LLVMRustDIBuilderGetOrCreateSubrange(LLVMRustDIBuilderRef Builder, int64_t Lo,
+                                     int64_t Count) {
+  return wrap(Builder->getOrCreateSubrange(Lo, Count));
+}
+
+extern "C" LLVMMetadataRef
+LLVMRustDIBuilderGetOrCreateArray(LLVMRustDIBuilderRef Builder,
+                                  LLVMMetadataRef *Ptr, unsigned Count) {
+  Metadata **DataValue = unwrap(Ptr);
+  return wrap(
+      Builder->getOrCreateArray(ArrayRef<Metadata *>(DataValue, Count)).get());
+}
+
+extern "C" LLVMValueRef LLVMRustDIBuilderInsertDeclareAtEnd(
+    LLVMRustDIBuilderRef Builder, LLVMValueRef V, LLVMMetadataRef VarInfo,
+    int64_t *AddrOps, unsigned AddrOpsCount, LLVMValueRef DL,
+    LLVMBasicBlockRef InsertAtEnd) {
+  return wrap(Builder->insertDeclare(
+      unwrap(V), unwrap<DILocalVariable>(VarInfo),
+      Builder->createExpression(llvm::ArrayRef<int64_t>(AddrOps, AddrOpsCount)),
+      DebugLoc(cast<MDNode>(unwrap<MetadataAsValue>(DL)->getMetadata())),
+      unwrap(InsertAtEnd)));
+}
+
+extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateEnumerator(
+    LLVMRustDIBuilderRef Builder, const char *Name, size_t NameLen,
+    int64_t Value, bool IsUnsigned) {
+  return wrap(Builder->createEnumerator(StringRef(Name, NameLen), Value, IsUnsigned));
+}
+
+extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateEnumerationType(
+    LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope,
+    const char *Name, size_t NameLen,
+    LLVMMetadataRef File, unsigned LineNumber, uint64_t SizeInBits,
+    uint32_t AlignInBits, LLVMMetadataRef Elements,
+    LLVMMetadataRef ClassTy, bool IsScoped) {
+  return wrap(Builder->createEnumerationType(
+      unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen),
+      unwrapDI<DIFile>(File), LineNumber,
+      SizeInBits, AlignInBits, DINodeArray(unwrapDI<MDTuple>(Elements)),
+      unwrapDI<DIType>(ClassTy), "", IsScoped));
+}
+
+extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateUnionType(
+    LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope,
+    const char *Name, size_t NameLen,
+    LLVMMetadataRef File, unsigned LineNumber, uint64_t SizeInBits,
+    uint32_t AlignInBits, LLVMRustDIFlags Flags, LLVMMetadataRef Elements,
+    unsigned RunTimeLang, const char *UniqueId, size_t UniqueIdLen) {
+  return wrap(Builder->createUnionType(
+      unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), unwrapDI<DIFile>(File),
+      LineNumber, SizeInBits, AlignInBits, fromRust(Flags),
+      DINodeArray(unwrapDI<MDTuple>(Elements)), RunTimeLang,
+      StringRef(UniqueId, UniqueIdLen)));
+}
+
+extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateTemplateTypeParameter(
+    LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope,
+    const char *Name, size_t NameLen, LLVMMetadataRef Ty) {
+#if LLVM_VERSION_GE(11, 0)
+  bool IsDefault = false; // FIXME: should we ever set this true?
+  return wrap(Builder->createTemplateTypeParameter(
+      unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), unwrapDI<DIType>(Ty), IsDefault));
+#else
+  return wrap(Builder->createTemplateTypeParameter(
+      unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), unwrapDI<DIType>(Ty)));
+#endif
+}
+
+extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateNameSpace(
+    LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope,
+    const char *Name, size_t NameLen, bool ExportSymbols) {
+  return wrap(Builder->createNameSpace(
+      unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), ExportSymbols
+  ));
+}
+
+extern "C" void
+LLVMRustDICompositeTypeReplaceArrays(LLVMRustDIBuilderRef Builder,
+                                     LLVMMetadataRef CompositeTy,
+                                     LLVMMetadataRef Elements,
+                                     LLVMMetadataRef Params) {
+  DICompositeType *Tmp = unwrapDI<DICompositeType>(CompositeTy);
+  Builder->replaceArrays(Tmp, DINodeArray(unwrap<MDTuple>(Elements)),
+                         DINodeArray(unwrap<MDTuple>(Params)));
+}
+
+extern "C" LLVMValueRef
+LLVMRustDIBuilderCreateDebugLocation(LLVMContextRef ContextRef, unsigned Line,
+                                     unsigned Column, LLVMMetadataRef Scope,
+                                     LLVMMetadataRef InlinedAt) {
+  LLVMContext &Context = *unwrap(ContextRef);
+
+  DebugLoc debug_loc = DebugLoc::get(Line, Column, unwrapDIPtr<MDNode>(Scope),
+                                     unwrapDIPtr<MDNode>(InlinedAt));
+
+  return wrap(MetadataAsValue::get(Context, debug_loc.getAsMDNode()));
+}
+
+extern "C" int64_t LLVMRustDIBuilderCreateOpDeref() {
+  return dwarf::DW_OP_deref;
+}
+
+extern "C" int64_t LLVMRustDIBuilderCreateOpPlusUconst() {
+  return dwarf::DW_OP_plus_uconst;
+}
+
+extern "C" void LLVMRustWriteTypeToString(LLVMTypeRef Ty, RustStringRef Str) {
+  RawRustStringOstream OS(Str);
+  unwrap<llvm::Type>(Ty)->print(OS);
+}
+
+extern "C" void LLVMRustWriteValueToString(LLVMValueRef V,
+                                           RustStringRef Str) {
+  RawRustStringOstream OS(Str);
+  if (!V) {
+    OS << "(null)";
+  } else {
+    OS << "(";
+    unwrap<llvm::Value>(V)->getType()->print(OS);
+    OS << ":";
+    unwrap<llvm::Value>(V)->print(OS);
+    OS << ")";
+  }
+}
+
+// Note that the two following functions look quite similar to the
+// LLVMGetSectionName function. Sadly, it appears that this function only
+// returns a char* pointer, which isn't guaranteed to be null-terminated. The
+// function provided by LLVM doesn't return the length, so we've created our own
+// function which returns the length as well as the data pointer.
+//
+// For an example of this not returning a null terminated string, see
+// lib/Object/COFFObjectFile.cpp in the getSectionName function. One of the
+// branches explicitly creates a StringRef without a null terminator, and then
+// that's returned.
+
+inline section_iterator *unwrap(LLVMSectionIteratorRef SI) {
+  return reinterpret_cast<section_iterator *>(SI);
+}
+
+extern "C" size_t LLVMRustGetSectionName(LLVMSectionIteratorRef SI,
+                                         const char **Ptr) {
+#if LLVM_VERSION_GE(10, 0)
+  auto NameOrErr = (*unwrap(SI))->getName();
+  if (!NameOrErr)
+    report_fatal_error(NameOrErr.takeError());
+  *Ptr = NameOrErr->data();
+  return NameOrErr->size();
+#else
+  StringRef Ret;
+  if (std::error_code EC = (*unwrap(SI))->getName(Ret))
+    report_fatal_error(EC.message());
+  *Ptr = Ret.data();
+  return Ret.size();
+#endif
+}
+
+// LLVMArrayType function does not support 64-bit ElementCount
+extern "C" LLVMTypeRef LLVMRustArrayType(LLVMTypeRef ElementTy,
+                                         uint64_t ElementCount) {
+  return wrap(ArrayType::get(unwrap(ElementTy), ElementCount));
+}
+
+DEFINE_SIMPLE_CONVERSION_FUNCTIONS(Twine, LLVMTwineRef)
+
+extern "C" void LLVMRustWriteTwineToString(LLVMTwineRef T, RustStringRef Str) {
+  RawRustStringOstream OS(Str);
+  unwrap(T)->print(OS);
+}
+
+extern "C" void LLVMRustUnpackOptimizationDiagnostic(
+    LLVMDiagnosticInfoRef DI, RustStringRef PassNameOut,
+    LLVMValueRef *FunctionOut, unsigned* Line, unsigned* Column,
+    RustStringRef FilenameOut, RustStringRef MessageOut) {
+  // Undefined to call this not on an optimization diagnostic!
+  llvm::DiagnosticInfoOptimizationBase *Opt =
+      static_cast<llvm::DiagnosticInfoOptimizationBase *>(unwrap(DI));
+
+  RawRustStringOstream PassNameOS(PassNameOut);
+  PassNameOS << Opt->getPassName();
+  *FunctionOut = wrap(&Opt->getFunction());
+
+  RawRustStringOstream FilenameOS(FilenameOut);
+  DiagnosticLocation loc = Opt->getLocation();
+  if (loc.isValid()) {
+    *Line = loc.getLine();
+    *Column = loc.getColumn();
+    FilenameOS << loc.getAbsolutePath();
+  }
+
+  RawRustStringOstream MessageOS(MessageOut);
+  MessageOS << Opt->getMsg();
+}
+
+enum class LLVMRustDiagnosticLevel {
+    Error,
+    Warning,
+    Note,
+    Remark,
+};
+
+extern "C" void
+LLVMRustUnpackInlineAsmDiagnostic(LLVMDiagnosticInfoRef DI,
+                                  LLVMRustDiagnosticLevel *LevelOut,
+                                  unsigned *CookieOut,
+                                  LLVMTwineRef *MessageOut,
+                                  LLVMValueRef *InstructionOut) {
+  // Undefined to call this not on an inline assembly diagnostic!
+  llvm::DiagnosticInfoInlineAsm *IA =
+      static_cast<llvm::DiagnosticInfoInlineAsm *>(unwrap(DI));
+
+  *CookieOut = IA->getLocCookie();
+  *MessageOut = wrap(&IA->getMsgStr());
+  *InstructionOut = wrap(IA->getInstruction());
+
+  switch (IA->getSeverity()) {
+    case DS_Error:
+      *LevelOut = LLVMRustDiagnosticLevel::Error;
+      break;
+    case DS_Warning:
+      *LevelOut = LLVMRustDiagnosticLevel::Warning;
+      break;
+    case DS_Note:
+      *LevelOut = LLVMRustDiagnosticLevel::Note;
+      break;
+    case DS_Remark:
+      *LevelOut = LLVMRustDiagnosticLevel::Remark;
+      break;
+    default:
+      report_fatal_error("Invalid LLVMRustDiagnosticLevel value!");
+  }
+}
+
+extern "C" void LLVMRustWriteDiagnosticInfoToString(LLVMDiagnosticInfoRef DI,
+                                                    RustStringRef Str) {
+  RawRustStringOstream OS(Str);
+  DiagnosticPrinterRawOStream DP(OS);
+  unwrap(DI)->print(DP);
+}
+
+enum class LLVMRustDiagnosticKind {
+  Other,
+  InlineAsm,
+  StackSize,
+  DebugMetadataVersion,
+  SampleProfile,
+  OptimizationRemark,
+  OptimizationRemarkMissed,
+  OptimizationRemarkAnalysis,
+  OptimizationRemarkAnalysisFPCommute,
+  OptimizationRemarkAnalysisAliasing,
+  OptimizationRemarkOther,
+  OptimizationFailure,
+  PGOProfile,
+  Linker,
+};
+
+static LLVMRustDiagnosticKind toRust(DiagnosticKind Kind) {
+  switch (Kind) {
+  case DK_InlineAsm:
+    return LLVMRustDiagnosticKind::InlineAsm;
+  case DK_StackSize:
+    return LLVMRustDiagnosticKind::StackSize;
+  case DK_DebugMetadataVersion:
+    return LLVMRustDiagnosticKind::DebugMetadataVersion;
+  case DK_SampleProfile:
+    return LLVMRustDiagnosticKind::SampleProfile;
+  case DK_OptimizationRemark:
+    return LLVMRustDiagnosticKind::OptimizationRemark;
+  case DK_OptimizationRemarkMissed:
+    return LLVMRustDiagnosticKind::OptimizationRemarkMissed;
+  case DK_OptimizationRemarkAnalysis:
+    return LLVMRustDiagnosticKind::OptimizationRemarkAnalysis;
+  case DK_OptimizationRemarkAnalysisFPCommute:
+    return LLVMRustDiagnosticKind::OptimizationRemarkAnalysisFPCommute;
+  case DK_OptimizationRemarkAnalysisAliasing:
+    return LLVMRustDiagnosticKind::OptimizationRemarkAnalysisAliasing;
+  case DK_PGOProfile:
+    return LLVMRustDiagnosticKind::PGOProfile;
+  case DK_Linker:
+    return LLVMRustDiagnosticKind::Linker;
+  default:
+    return (Kind >= DK_FirstRemark && Kind <= DK_LastRemark)
+               ? LLVMRustDiagnosticKind::OptimizationRemarkOther
+               : LLVMRustDiagnosticKind::Other;
+  }
+}
+
+extern "C" LLVMRustDiagnosticKind
+LLVMRustGetDiagInfoKind(LLVMDiagnosticInfoRef DI) {
+  return toRust((DiagnosticKind)unwrap(DI)->getKind());
+}
+
+// This is kept distinct from LLVMGetTypeKind, because when
+// a new type kind is added, the Rust-side enum must be
+// updated or UB will result.
+extern "C" LLVMTypeKind LLVMRustGetTypeKind(LLVMTypeRef Ty) {
+  switch (unwrap(Ty)->getTypeID()) {
+  case Type::VoidTyID:
+    return LLVMVoidTypeKind;
+  case Type::HalfTyID:
+    return LLVMHalfTypeKind;
+  case Type::FloatTyID:
+    return LLVMFloatTypeKind;
+  case Type::DoubleTyID:
+    return LLVMDoubleTypeKind;
+  case Type::X86_FP80TyID:
+    return LLVMX86_FP80TypeKind;
+  case Type::FP128TyID:
+    return LLVMFP128TypeKind;
+  case Type::PPC_FP128TyID:
+    return LLVMPPC_FP128TypeKind;
+  case Type::LabelTyID:
+    return LLVMLabelTypeKind;
+  case Type::MetadataTyID:
+    return LLVMMetadataTypeKind;
+  case Type::IntegerTyID:
+    return LLVMIntegerTypeKind;
+  case Type::FunctionTyID:
+    return LLVMFunctionTypeKind;
+  case Type::StructTyID:
+    return LLVMStructTypeKind;
+  case Type::ArrayTyID:
+    return LLVMArrayTypeKind;
+  case Type::PointerTyID:
+    return LLVMPointerTypeKind;
+#if LLVM_VERSION_GE(11, 0)
+  case Type::FixedVectorTyID:
+    return LLVMVectorTypeKind;
+#else
+  case Type::VectorTyID:
+    return LLVMVectorTypeKind;
+#endif
+  case Type::X86_MMXTyID:
+    return LLVMX86_MMXTypeKind;
+  case Type::TokenTyID:
+    return LLVMTokenTypeKind;
+#if LLVM_VERSION_GE(11, 0)
+  case Type::ScalableVectorTyID:
+    return LLVMScalableVectorTypeKind;
+  case Type::BFloatTyID:
+    return LLVMBFloatTypeKind;
+#endif
+  }
+  report_fatal_error("Unhandled TypeID.");
+}
+
+DEFINE_SIMPLE_CONVERSION_FUNCTIONS(SMDiagnostic, LLVMSMDiagnosticRef)
+
+extern "C" void LLVMRustSetInlineAsmDiagnosticHandler(
+    LLVMContextRef C, LLVMContext::InlineAsmDiagHandlerTy H, void *CX) {
+  unwrap(C)->setInlineAsmDiagnosticHandler(H, CX);
+}
+
+extern "C" bool LLVMRustUnpackSMDiagnostic(LLVMSMDiagnosticRef DRef,
+                                           RustStringRef MessageOut,
+                                           RustStringRef BufferOut,
+                                           LLVMRustDiagnosticLevel* LevelOut,
+                                           unsigned* LocOut,
+                                           unsigned* RangesOut,
+                                           size_t* NumRanges) {
+  SMDiagnostic& D = *unwrap(DRef);
+  RawRustStringOstream MessageOS(MessageOut);
+  MessageOS << D.getMessage();
+
+  switch (D.getKind()) {
+    case SourceMgr::DK_Error:
+      *LevelOut = LLVMRustDiagnosticLevel::Error;
+      break;
+    case SourceMgr::DK_Warning:
+      *LevelOut = LLVMRustDiagnosticLevel::Warning;
+      break;
+    case SourceMgr::DK_Note:
+      *LevelOut = LLVMRustDiagnosticLevel::Note;
+      break;
+    case SourceMgr::DK_Remark:
+      *LevelOut = LLVMRustDiagnosticLevel::Remark;
+      break;
+    default:
+      report_fatal_error("Invalid LLVMRustDiagnosticLevel value!");
+  }
+
+  if (D.getLoc() == SMLoc())
+    return false;
+
+  const SourceMgr &LSM = *D.getSourceMgr();
+  const MemoryBuffer *LBuf = LSM.getMemoryBuffer(LSM.FindBufferContainingLoc(D.getLoc()));
+  LLVMRustStringWriteImpl(BufferOut, LBuf->getBufferStart(), LBuf->getBufferSize());
+
+  *LocOut = D.getLoc().getPointer() - LBuf->getBufferStart();
+
+  *NumRanges = std::min(*NumRanges, D.getRanges().size());
+  size_t LineStart = *LocOut - (size_t)D.getColumnNo();
+  for (size_t i = 0; i < *NumRanges; i++) {
+    RangesOut[i * 2] = LineStart + D.getRanges()[i].first;
+    RangesOut[i * 2 + 1] = LineStart + D.getRanges()[i].second;
+  }
+
+  return true;
+}
+
+extern "C" LLVMValueRef LLVMRustBuildCleanupPad(LLVMBuilderRef B,
+                                                LLVMValueRef ParentPad,
+                                                unsigned ArgCount,
+                                                LLVMValueRef *LLArgs,
+                                                const char *Name) {
+  Value **Args = unwrap(LLArgs);
+  if (ParentPad == nullptr) {
+    Type *Ty = Type::getTokenTy(unwrap(B)->getContext());
+    ParentPad = wrap(Constant::getNullValue(Ty));
+  }
+  return wrap(unwrap(B)->CreateCleanupPad(
+      unwrap(ParentPad), ArrayRef<Value *>(Args, ArgCount), Name));
+}
+
+extern "C" LLVMValueRef LLVMRustBuildCleanupRet(LLVMBuilderRef B,
+                                                LLVMValueRef CleanupPad,
+                                                LLVMBasicBlockRef UnwindBB) {
+  CleanupPadInst *Inst = cast<CleanupPadInst>(unwrap(CleanupPad));
+  return wrap(unwrap(B)->CreateCleanupRet(Inst, unwrap(UnwindBB)));
+}
+
+extern "C" LLVMValueRef
+LLVMRustBuildCatchPad(LLVMBuilderRef B, LLVMValueRef ParentPad,
+                      unsigned ArgCount, LLVMValueRef *LLArgs, const char *Name) {
+  Value **Args = unwrap(LLArgs);
+  return wrap(unwrap(B)->CreateCatchPad(
+      unwrap(ParentPad), ArrayRef<Value *>(Args, ArgCount), Name));
+}
+
+extern "C" LLVMValueRef LLVMRustBuildCatchRet(LLVMBuilderRef B,
+                                              LLVMValueRef Pad,
+                                              LLVMBasicBlockRef BB) {
+  return wrap(unwrap(B)->CreateCatchRet(cast<CatchPadInst>(unwrap(Pad)),
+                                              unwrap(BB)));
+}
+
+extern "C" LLVMValueRef LLVMRustBuildCatchSwitch(LLVMBuilderRef B,
+                                                 LLVMValueRef ParentPad,
+                                                 LLVMBasicBlockRef BB,
+                                                 unsigned NumHandlers,
+                                                 const char *Name) {
+  if (ParentPad == nullptr) {
+    Type *Ty = Type::getTokenTy(unwrap(B)->getContext());
+    ParentPad = wrap(Constant::getNullValue(Ty));
+  }
+  return wrap(unwrap(B)->CreateCatchSwitch(unwrap(ParentPad), unwrap(BB),
+                                                 NumHandlers, Name));
+}
+
+extern "C" void LLVMRustAddHandler(LLVMValueRef CatchSwitchRef,
+                                   LLVMBasicBlockRef Handler) {
+  Value *CatchSwitch = unwrap(CatchSwitchRef);
+  cast<CatchSwitchInst>(CatchSwitch)->addHandler(unwrap(Handler));
+}
+
+extern "C" OperandBundleDef *LLVMRustBuildOperandBundleDef(const char *Name,
+                                                           LLVMValueRef *Inputs,
+                                                           unsigned NumInputs) {
+  return new OperandBundleDef(Name, makeArrayRef(unwrap(Inputs), NumInputs));
+}
+
+extern "C" void LLVMRustFreeOperandBundleDef(OperandBundleDef *Bundle) {
+  delete Bundle;
+}
+
+extern "C" LLVMValueRef LLVMRustBuildCall(LLVMBuilderRef B, LLVMValueRef Fn,
+                                          LLVMValueRef *Args, unsigned NumArgs,
+                                          OperandBundleDef *Bundle) {
+  Value *Callee = unwrap(Fn);
+  FunctionType *FTy = cast<FunctionType>(Callee->getType()->getPointerElementType());
+  unsigned Len = Bundle ? 1 : 0;
+  ArrayRef<OperandBundleDef> Bundles = makeArrayRef(Bundle, Len);
+  return wrap(unwrap(B)->CreateCall(
+      FTy, Callee, makeArrayRef(unwrap(Args), NumArgs), Bundles));
+}
+
+extern "C" LLVMValueRef LLVMRustGetInstrProfIncrementIntrinsic(LLVMModuleRef M) {
+  return wrap(llvm::Intrinsic::getDeclaration(unwrap(M),
+              (llvm::Intrinsic::ID)llvm::Intrinsic::instrprof_increment));
+}
+
+extern "C" LLVMValueRef LLVMRustBuildMemCpy(LLVMBuilderRef B,
+                                            LLVMValueRef Dst, unsigned DstAlign,
+                                            LLVMValueRef Src, unsigned SrcAlign,
+                                            LLVMValueRef Size, bool IsVolatile) {
+#if LLVM_VERSION_GE(10, 0)
+  return wrap(unwrap(B)->CreateMemCpy(
+      unwrap(Dst), MaybeAlign(DstAlign),
+      unwrap(Src), MaybeAlign(SrcAlign),
+      unwrap(Size), IsVolatile));
+#else
+  return wrap(unwrap(B)->CreateMemCpy(
+      unwrap(Dst), DstAlign,
+      unwrap(Src), SrcAlign,
+      unwrap(Size), IsVolatile));
+#endif
+}
+
+extern "C" LLVMValueRef LLVMRustBuildMemMove(LLVMBuilderRef B,
+                                             LLVMValueRef Dst, unsigned DstAlign,
+                                             LLVMValueRef Src, unsigned SrcAlign,
+                                             LLVMValueRef Size, bool IsVolatile) {
+#if LLVM_VERSION_GE(10, 0)
+  return wrap(unwrap(B)->CreateMemMove(
+      unwrap(Dst), MaybeAlign(DstAlign),
+      unwrap(Src), MaybeAlign(SrcAlign),
+      unwrap(Size), IsVolatile));
+#else
+  return wrap(unwrap(B)->CreateMemMove(
+      unwrap(Dst), DstAlign,
+      unwrap(Src), SrcAlign,
+      unwrap(Size), IsVolatile));
+#endif
+}
+
+extern "C" LLVMValueRef LLVMRustBuildMemSet(LLVMBuilderRef B,
+                                            LLVMValueRef Dst, unsigned DstAlign,
+                                            LLVMValueRef Val,
+                                            LLVMValueRef Size, bool IsVolatile) {
+#if LLVM_VERSION_GE(10, 0)
+  return wrap(unwrap(B)->CreateMemSet(
+      unwrap(Dst), unwrap(Val), unwrap(Size), MaybeAlign(DstAlign), IsVolatile));
+#else
+  return wrap(unwrap(B)->CreateMemSet(
+      unwrap(Dst), unwrap(Val), unwrap(Size), DstAlign, IsVolatile));
+#endif
+}
+
+extern "C" LLVMValueRef
+LLVMRustBuildInvoke(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args,
+                    unsigned NumArgs, LLVMBasicBlockRef Then,
+                    LLVMBasicBlockRef Catch, OperandBundleDef *Bundle,
+                    const char *Name) {
+  Value *Callee = unwrap(Fn);
+  FunctionType *FTy = cast<FunctionType>(Callee->getType()->getPointerElementType());
+  unsigned Len = Bundle ? 1 : 0;
+  ArrayRef<OperandBundleDef> Bundles = makeArrayRef(Bundle, Len);
+  return wrap(unwrap(B)->CreateInvoke(FTy, Callee, unwrap(Then), unwrap(Catch),
+                                      makeArrayRef(unwrap(Args), NumArgs),
+                                      Bundles, Name));
+}
+
+extern "C" void LLVMRustPositionBuilderAtStart(LLVMBuilderRef B,
+                                               LLVMBasicBlockRef BB) {
+  auto Point = unwrap(BB)->getFirstInsertionPt();
+  unwrap(B)->SetInsertPoint(unwrap(BB), Point);
+}
+
+extern "C" void LLVMRustSetComdat(LLVMModuleRef M, LLVMValueRef V,
+                                  const char *Name, size_t NameLen) {
+  Triple TargetTriple(unwrap(M)->getTargetTriple());
+  GlobalObject *GV = unwrap<GlobalObject>(V);
+  if (!TargetTriple.isOSBinFormatMachO()) {
+    StringRef NameRef(Name, NameLen);
+    GV->setComdat(unwrap(M)->getOrInsertComdat(NameRef));
+  }
+}
+
+extern "C" void LLVMRustUnsetComdat(LLVMValueRef V) {
+  GlobalObject *GV = unwrap<GlobalObject>(V);
+  GV->setComdat(nullptr);
+}
+
+enum class LLVMRustLinkage {
+  ExternalLinkage = 0,
+  AvailableExternallyLinkage = 1,
+  LinkOnceAnyLinkage = 2,
+  LinkOnceODRLinkage = 3,
+  WeakAnyLinkage = 4,
+  WeakODRLinkage = 5,
+  AppendingLinkage = 6,
+  InternalLinkage = 7,
+  PrivateLinkage = 8,
+  ExternalWeakLinkage = 9,
+  CommonLinkage = 10,
+};
+
+static LLVMRustLinkage toRust(LLVMLinkage Linkage) {
+  switch (Linkage) {
+  case LLVMExternalLinkage:
+    return LLVMRustLinkage::ExternalLinkage;
+  case LLVMAvailableExternallyLinkage:
+    return LLVMRustLinkage::AvailableExternallyLinkage;
+  case LLVMLinkOnceAnyLinkage:
+    return LLVMRustLinkage::LinkOnceAnyLinkage;
+  case LLVMLinkOnceODRLinkage:
+    return LLVMRustLinkage::LinkOnceODRLinkage;
+  case LLVMWeakAnyLinkage:
+    return LLVMRustLinkage::WeakAnyLinkage;
+  case LLVMWeakODRLinkage:
+    return LLVMRustLinkage::WeakODRLinkage;
+  case LLVMAppendingLinkage:
+    return LLVMRustLinkage::AppendingLinkage;
+  case LLVMInternalLinkage:
+    return LLVMRustLinkage::InternalLinkage;
+  case LLVMPrivateLinkage:
+    return LLVMRustLinkage::PrivateLinkage;
+  case LLVMExternalWeakLinkage:
+    return LLVMRustLinkage::ExternalWeakLinkage;
+  case LLVMCommonLinkage:
+    return LLVMRustLinkage::CommonLinkage;
+  default:
+    report_fatal_error("Invalid LLVMRustLinkage value!");
+  }
+}
+
+static LLVMLinkage fromRust(LLVMRustLinkage Linkage) {
+  switch (Linkage) {
+  case LLVMRustLinkage::ExternalLinkage:
+    return LLVMExternalLinkage;
+  case LLVMRustLinkage::AvailableExternallyLinkage:
+    return LLVMAvailableExternallyLinkage;
+  case LLVMRustLinkage::LinkOnceAnyLinkage:
+    return LLVMLinkOnceAnyLinkage;
+  case LLVMRustLinkage::LinkOnceODRLinkage:
+    return LLVMLinkOnceODRLinkage;
+  case LLVMRustLinkage::WeakAnyLinkage:
+    return LLVMWeakAnyLinkage;
+  case LLVMRustLinkage::WeakODRLinkage:
+    return LLVMWeakODRLinkage;
+  case LLVMRustLinkage::AppendingLinkage:
+    return LLVMAppendingLinkage;
+  case LLVMRustLinkage::InternalLinkage:
+    return LLVMInternalLinkage;
+  case LLVMRustLinkage::PrivateLinkage:
+    return LLVMPrivateLinkage;
+  case LLVMRustLinkage::ExternalWeakLinkage:
+    return LLVMExternalWeakLinkage;
+  case LLVMRustLinkage::CommonLinkage:
+    return LLVMCommonLinkage;
+  }
+  report_fatal_error("Invalid LLVMRustLinkage value!");
+}
+
+extern "C" LLVMRustLinkage LLVMRustGetLinkage(LLVMValueRef V) {
+  return toRust(LLVMGetLinkage(V));
+}
+
+extern "C" void LLVMRustSetLinkage(LLVMValueRef V,
+                                   LLVMRustLinkage RustLinkage) {
+  LLVMSetLinkage(V, fromRust(RustLinkage));
+}
+
+// Returns true if both high and low were successfully set. Fails in case constant wasn’t any of
+// the common sizes (1, 8, 16, 32, 64, 128 bits)
+extern "C" bool LLVMRustConstInt128Get(LLVMValueRef CV, bool sext, uint64_t *high, uint64_t *low)
+{
+    auto C = unwrap<llvm::ConstantInt>(CV);
+    if (C->getBitWidth() > 128) { return false; }
+    APInt AP;
+    if (sext) {
+        AP = C->getValue().sextOrSelf(128);
+    } else {
+        AP = C->getValue().zextOrSelf(128);
+    }
+    *low = AP.getLoBits(64).getZExtValue();
+    *high = AP.getHiBits(64).getZExtValue();
+    return true;
+}
+
+enum class LLVMRustVisibility {
+  Default = 0,
+  Hidden = 1,
+  Protected = 2,
+};
+
+static LLVMRustVisibility toRust(LLVMVisibility Vis) {
+  switch (Vis) {
+  case LLVMDefaultVisibility:
+    return LLVMRustVisibility::Default;
+  case LLVMHiddenVisibility:
+    return LLVMRustVisibility::Hidden;
+  case LLVMProtectedVisibility:
+    return LLVMRustVisibility::Protected;
+  }
+  report_fatal_error("Invalid LLVMRustVisibility value!");
+}
+
+static LLVMVisibility fromRust(LLVMRustVisibility Vis) {
+  switch (Vis) {
+  case LLVMRustVisibility::Default:
+    return LLVMDefaultVisibility;
+  case LLVMRustVisibility::Hidden:
+    return LLVMHiddenVisibility;
+  case LLVMRustVisibility::Protected:
+    return LLVMProtectedVisibility;
+  }
+  report_fatal_error("Invalid LLVMRustVisibility value!");
+}
+
+extern "C" LLVMRustVisibility LLVMRustGetVisibility(LLVMValueRef V) {
+  return toRust(LLVMGetVisibility(V));
+}
+
+// Oh hey, a binding that makes sense for once? (because LLVM’s own do not)
+extern "C" LLVMValueRef LLVMRustBuildIntCast(LLVMBuilderRef B, LLVMValueRef Val,
+                                             LLVMTypeRef DestTy, bool isSigned) {
+  return wrap(unwrap(B)->CreateIntCast(unwrap(Val), unwrap(DestTy), isSigned, ""));
+}
+
+extern "C" void LLVMRustSetVisibility(LLVMValueRef V,
+                                      LLVMRustVisibility RustVisibility) {
+  LLVMSetVisibility(V, fromRust(RustVisibility));
+}
+
+struct LLVMRustModuleBuffer {
+  std::string data;
+};
+
+extern "C" LLVMRustModuleBuffer*
+LLVMRustModuleBufferCreate(LLVMModuleRef M) {
+#if LLVM_VERSION_GE(10, 0)
+  auto Ret = std::make_unique<LLVMRustModuleBuffer>();
+#else
+  auto Ret = llvm::make_unique<LLVMRustModuleBuffer>();
+#endif
+  {
+    raw_string_ostream OS(Ret->data);
+    {
+      legacy::PassManager PM;
+      PM.add(createBitcodeWriterPass(OS));
+      PM.run(*unwrap(M));
+    }
+  }
+  return Ret.release();
+}
+
+extern "C" void
+LLVMRustModuleBufferFree(LLVMRustModuleBuffer *Buffer) {
+  delete Buffer;
+}
+
+extern "C" const void*
+LLVMRustModuleBufferPtr(const LLVMRustModuleBuffer *Buffer) {
+  return Buffer->data.data();
+}
+
+extern "C" size_t
+LLVMRustModuleBufferLen(const LLVMRustModuleBuffer *Buffer) {
+  return Buffer->data.length();
+}
+
+extern "C" uint64_t
+LLVMRustModuleCost(LLVMModuleRef M) {
+  auto f = unwrap(M)->functions();
+  return std::distance(std::begin(f), std::end(f));
+}
+
+// Vector reductions:
+extern "C" LLVMValueRef
+LLVMRustBuildVectorReduceFAdd(LLVMBuilderRef B, LLVMValueRef Acc, LLVMValueRef Src) {
+    return wrap(unwrap(B)->CreateFAddReduce(unwrap(Acc),unwrap(Src)));
+}
+extern "C" LLVMValueRef
+LLVMRustBuildVectorReduceFMul(LLVMBuilderRef B, LLVMValueRef Acc, LLVMValueRef Src) {
+    return wrap(unwrap(B)->CreateFMulReduce(unwrap(Acc),unwrap(Src)));
+}
+extern "C" LLVMValueRef
+LLVMRustBuildVectorReduceAdd(LLVMBuilderRef B, LLVMValueRef Src) {
+    return wrap(unwrap(B)->CreateAddReduce(unwrap(Src)));
+}
+extern "C" LLVMValueRef
+LLVMRustBuildVectorReduceMul(LLVMBuilderRef B, LLVMValueRef Src) {
+    return wrap(unwrap(B)->CreateMulReduce(unwrap(Src)));
+}
+extern "C" LLVMValueRef
+LLVMRustBuildVectorReduceAnd(LLVMBuilderRef B, LLVMValueRef Src) {
+    return wrap(unwrap(B)->CreateAndReduce(unwrap(Src)));
+}
+extern "C" LLVMValueRef
+LLVMRustBuildVectorReduceOr(LLVMBuilderRef B, LLVMValueRef Src) {
+    return wrap(unwrap(B)->CreateOrReduce(unwrap(Src)));
+}
+extern "C" LLVMValueRef
+LLVMRustBuildVectorReduceXor(LLVMBuilderRef B, LLVMValueRef Src) {
+    return wrap(unwrap(B)->CreateXorReduce(unwrap(Src)));
+}
+extern "C" LLVMValueRef
+LLVMRustBuildVectorReduceMin(LLVMBuilderRef B, LLVMValueRef Src, bool IsSigned) {
+    return wrap(unwrap(B)->CreateIntMinReduce(unwrap(Src), IsSigned));
+}
+extern "C" LLVMValueRef
+LLVMRustBuildVectorReduceMax(LLVMBuilderRef B, LLVMValueRef Src, bool IsSigned) {
+    return wrap(unwrap(B)->CreateIntMaxReduce(unwrap(Src), IsSigned));
+}
+extern "C" LLVMValueRef
+LLVMRustBuildVectorReduceFMin(LLVMBuilderRef B, LLVMValueRef Src, bool NoNaN) {
+   return wrap(unwrap(B)->CreateFPMinReduce(unwrap(Src), NoNaN));
+}
+extern "C" LLVMValueRef
+LLVMRustBuildVectorReduceFMax(LLVMBuilderRef B, LLVMValueRef Src, bool NoNaN) {
+  return wrap(unwrap(B)->CreateFPMaxReduce(unwrap(Src), NoNaN));
+}
+
+extern "C" LLVMValueRef
+LLVMRustBuildMinNum(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRef RHS) {
+    return wrap(unwrap(B)->CreateMinNum(unwrap(LHS),unwrap(RHS)));
+}
+extern "C" LLVMValueRef
+LLVMRustBuildMaxNum(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRef RHS) {
+    return wrap(unwrap(B)->CreateMaxNum(unwrap(LHS),unwrap(RHS)));
+}
diff --git a/compiler/rustc_llvm/src/lib.rs b/compiler/rustc_llvm/src/lib.rs
new file mode 100644
index 00000000000..a7a10b91b4e
--- /dev/null
+++ b/compiler/rustc_llvm/src/lib.rs
@@ -0,0 +1,173 @@
+#![feature(nll)]
+#![feature(static_nobundle)]
+#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
+
+// NOTE: This crate only exists to allow linking on mingw targets.
+
+use libc::{c_char, size_t};
+use std::cell::RefCell;
+use std::slice;
+
+#[repr(C)]
+pub struct RustString {
+    pub bytes: RefCell<Vec<u8>>,
+}
+
+impl RustString {
+    pub fn len(&self) -> usize {
+        self.bytes.borrow().len()
+    }
+}
+
+/// Appending to a Rust string -- used by RawRustStringOstream.
+#[no_mangle]
+#[allow(improper_ctypes_definitions)]
+pub unsafe extern "C" fn LLVMRustStringWriteImpl(
+    sr: &RustString,
+    ptr: *const c_char,
+    size: size_t,
+) {
+    let slice = slice::from_raw_parts(ptr as *const u8, size as usize);
+
+    sr.bytes.borrow_mut().extend_from_slice(slice);
+}
+
+/// Initialize targets enabled by the build script via `cfg(llvm_component = "...")`.
+/// N.B., this function can't be moved to `rustc_codegen_llvm` because of the `cfg`s.
+pub fn initialize_available_targets() {
+    macro_rules! init_target(
+        ($cfg:meta, $($method:ident),*) => { {
+            #[cfg($cfg)]
+            fn init() {
+                extern {
+                    $(fn $method();)*
+                }
+                unsafe {
+                    $($method();)*
+                }
+            }
+            #[cfg(not($cfg))]
+            fn init() { }
+            init();
+        } }
+    );
+    init_target!(
+        llvm_component = "x86",
+        LLVMInitializeX86TargetInfo,
+        LLVMInitializeX86Target,
+        LLVMInitializeX86TargetMC,
+        LLVMInitializeX86AsmPrinter,
+        LLVMInitializeX86AsmParser
+    );
+    init_target!(
+        llvm_component = "arm",
+        LLVMInitializeARMTargetInfo,
+        LLVMInitializeARMTarget,
+        LLVMInitializeARMTargetMC,
+        LLVMInitializeARMAsmPrinter,
+        LLVMInitializeARMAsmParser
+    );
+    init_target!(
+        llvm_component = "aarch64",
+        LLVMInitializeAArch64TargetInfo,
+        LLVMInitializeAArch64Target,
+        LLVMInitializeAArch64TargetMC,
+        LLVMInitializeAArch64AsmPrinter,
+        LLVMInitializeAArch64AsmParser
+    );
+    init_target!(
+        llvm_component = "amdgpu",
+        LLVMInitializeAMDGPUTargetInfo,
+        LLVMInitializeAMDGPUTarget,
+        LLVMInitializeAMDGPUTargetMC,
+        LLVMInitializeAMDGPUAsmPrinter,
+        LLVMInitializeAMDGPUAsmParser
+    );
+    init_target!(
+        llvm_component = "avr",
+        LLVMInitializeAVRTargetInfo,
+        LLVMInitializeAVRTarget,
+        LLVMInitializeAVRTargetMC,
+        LLVMInitializeAVRAsmPrinter,
+        LLVMInitializeAVRAsmParser
+    );
+    init_target!(
+        llvm_component = "mips",
+        LLVMInitializeMipsTargetInfo,
+        LLVMInitializeMipsTarget,
+        LLVMInitializeMipsTargetMC,
+        LLVMInitializeMipsAsmPrinter,
+        LLVMInitializeMipsAsmParser
+    );
+    init_target!(
+        llvm_component = "powerpc",
+        LLVMInitializePowerPCTargetInfo,
+        LLVMInitializePowerPCTarget,
+        LLVMInitializePowerPCTargetMC,
+        LLVMInitializePowerPCAsmPrinter,
+        LLVMInitializePowerPCAsmParser
+    );
+    init_target!(
+        llvm_component = "systemz",
+        LLVMInitializeSystemZTargetInfo,
+        LLVMInitializeSystemZTarget,
+        LLVMInitializeSystemZTargetMC,
+        LLVMInitializeSystemZAsmPrinter,
+        LLVMInitializeSystemZAsmParser
+    );
+    init_target!(
+        llvm_component = "jsbackend",
+        LLVMInitializeJSBackendTargetInfo,
+        LLVMInitializeJSBackendTarget,
+        LLVMInitializeJSBackendTargetMC
+    );
+    init_target!(
+        llvm_component = "msp430",
+        LLVMInitializeMSP430TargetInfo,
+        LLVMInitializeMSP430Target,
+        LLVMInitializeMSP430TargetMC,
+        LLVMInitializeMSP430AsmPrinter
+    );
+    init_target!(
+        all(llvm_component = "msp430", llvm_has_msp430_asm_parser),
+        LLVMInitializeMSP430AsmParser
+    );
+    init_target!(
+        llvm_component = "riscv",
+        LLVMInitializeRISCVTargetInfo,
+        LLVMInitializeRISCVTarget,
+        LLVMInitializeRISCVTargetMC,
+        LLVMInitializeRISCVAsmPrinter,
+        LLVMInitializeRISCVAsmParser
+    );
+    init_target!(
+        llvm_component = "sparc",
+        LLVMInitializeSparcTargetInfo,
+        LLVMInitializeSparcTarget,
+        LLVMInitializeSparcTargetMC,
+        LLVMInitializeSparcAsmPrinter,
+        LLVMInitializeSparcAsmParser
+    );
+    init_target!(
+        llvm_component = "nvptx",
+        LLVMInitializeNVPTXTargetInfo,
+        LLVMInitializeNVPTXTarget,
+        LLVMInitializeNVPTXTargetMC,
+        LLVMInitializeNVPTXAsmPrinter
+    );
+    init_target!(
+        llvm_component = "hexagon",
+        LLVMInitializeHexagonTargetInfo,
+        LLVMInitializeHexagonTarget,
+        LLVMInitializeHexagonTargetMC,
+        LLVMInitializeHexagonAsmPrinter,
+        LLVMInitializeHexagonAsmParser
+    );
+    init_target!(
+        llvm_component = "webassembly",
+        LLVMInitializeWebAssemblyTargetInfo,
+        LLVMInitializeWebAssemblyTarget,
+        LLVMInitializeWebAssemblyTargetMC,
+        LLVMInitializeWebAssemblyAsmPrinter
+    );
+}
diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs
index 7fb3b0e7ea6..5c28839c9b7 100644
--- a/compiler/rustc_macros/src/lib.rs
+++ b/compiler/rustc_macros/src/lib.rs
@@ -1,3 +1,4 @@
+#![feature(proc_macro_diagnostic)]
 #![allow(rustc::default_hash_types)]
 #![recursion_limit = "128"]
 
@@ -9,6 +10,7 @@ mod hash_stable;
 mod lift;
 mod query;
 mod serialize;
+mod session_diagnostic;
 mod symbols;
 mod type_foldable;
 
@@ -36,3 +38,14 @@ decl_derive!([MetadataDecodable] => serialize::meta_decodable_derive);
 decl_derive!([MetadataEncodable] => serialize::meta_encodable_derive);
 decl_derive!([TypeFoldable, attributes(type_foldable)] => type_foldable::type_foldable_derive);
 decl_derive!([Lift, attributes(lift)] => lift::lift_derive);
+decl_derive!(
+    [SessionDiagnostic, attributes(
+        message,
+        lint,
+        error,
+        label,
+        suggestion,
+        suggestion_short,
+        suggestion_hidden,
+        suggestion_verbose)] => session_diagnostic::session_diagnostic_derive
+);
diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs
index c17d5311e8f..204e8e800cd 100644
--- a/compiler/rustc_macros/src/query.rs
+++ b/compiler/rustc_macros/src/query.rs
@@ -5,8 +5,8 @@ use syn::parse::{Parse, ParseStream, Result};
 use syn::punctuated::Punctuated;
 use syn::spanned::Spanned;
 use syn::{
-    braced, parenthesized, parse_macro_input, Attribute, Block, Error, Expr, Ident, ReturnType,
-    Token, Type,
+    braced, parenthesized, parse_macro_input, AttrStyle, Attribute, Block, Error, Expr, Ident,
+    ReturnType, Token, Type,
 };
 
 #[allow(non_camel_case_types)]
@@ -128,17 +128,25 @@ impl Parse for QueryModifier {
 }
 
 /// Ensures only doc comment attributes are used
-fn check_attributes(attrs: Vec<Attribute>) -> Result<()> {
-    for attr in attrs {
+fn check_attributes(attrs: Vec<Attribute>) -> Result<Vec<Attribute>> {
+    let inner = |attr: Attribute| {
         if !attr.path.is_ident("doc") {
-            return Err(Error::new(attr.span(), "attributes not supported on queries"));
+            Err(Error::new(attr.span(), "attributes not supported on queries"))
+        } else if attr.style != AttrStyle::Outer {
+            Err(Error::new(
+                attr.span(),
+                "attributes must be outer attributes (`///`), not inner attributes",
+            ))
+        } else {
+            Ok(attr)
         }
-    }
-    Ok(())
+    };
+    attrs.into_iter().map(inner).collect()
 }
 
 /// A compiler query. `query ... { ... }`
 struct Query {
+    doc_comments: Vec<Attribute>,
     modifiers: List<QueryModifier>,
     name: Ident,
     key: IdentOrWild,
@@ -148,7 +156,7 @@ struct Query {
 
 impl Parse for Query {
     fn parse(input: ParseStream<'_>) -> Result<Self> {
-        check_attributes(input.call(Attribute::parse_outer)?)?;
+        let doc_comments = check_attributes(input.call(Attribute::parse_outer)?)?;
 
         // Parse the query declaration. Like `query type_of(key: DefId) -> Ty<'tcx>`
         input.parse::<kw::query>()?;
@@ -165,7 +173,7 @@ impl Parse for Query {
         braced!(content in input);
         let modifiers = content.parse()?;
 
-        Ok(Query { modifiers, name, key, arg, result })
+        Ok(Query { doc_comments, modifiers, name, key, arg, result })
     }
 }
 
@@ -392,7 +400,7 @@ fn add_query_description_impl(
             #tcx: TyCtxt<'tcx>,
             #key: #arg,
         ) -> Cow<'static, str> {
-            format!(#desc).into()
+            ::rustc_middle::ty::print::with_no_trimmed_paths(|| format!(#desc).into())
         }
     };
 
@@ -476,9 +484,10 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
             };
 
             let attribute_stream = quote! {#(#attributes),*};
-
+            let doc_comments = query.doc_comments.iter();
             // Add the query to the group
             group_stream.extend(quote! {
+                #(#doc_comments)*
                 [#attribute_stream] fn #name: #name(#arg) #result,
             });
 
diff --git a/compiler/rustc_macros/src/session_diagnostic.rs b/compiler/rustc_macros/src/session_diagnostic.rs
new file mode 100644
index 00000000000..610b9155cfc
--- /dev/null
+++ b/compiler/rustc_macros/src/session_diagnostic.rs
@@ -0,0 +1,666 @@
+#![deny(unused_must_use)]
+use proc_macro::Diagnostic;
+use quote::{format_ident, quote};
+use syn::spanned::Spanned;
+
+use std::collections::{BTreeSet, HashMap};
+
+/// Implements #[derive(SessionDiagnostic)], which allows for errors to be specified as a struct, independent
+/// from the actual diagnostics emitting code.
+/// ```ignore (pseudo-rust)
+/// # extern crate rustc_errors;
+/// # use rustc_errors::Applicability;
+/// # extern crate rustc_span;
+/// # use rustc_span::{symbol::Ident, Span};
+/// # extern crate rust_middle;
+/// # use rustc_middle::ty::Ty;
+/// #[derive(SessionDiagnostic)]
+/// #[code = "E0505"]
+/// #[error = "cannot move out of {name} because it is borrowed"]
+/// pub struct MoveOutOfBorrowError<'tcx> {
+///     pub name: Ident,
+///     pub ty: Ty<'tcx>,
+///     #[label = "cannot move out of borrow"]
+///     pub span: Span,
+///     #[label = "`{ty}` first borrowed here"]
+///     pub other_span: Span,
+///     #[suggestion(message = "consider cloning here", code = "{name}.clone()")]
+///     pub opt_sugg: Option<(Span, Applicability)>
+/// }
+/// ```
+/// Then, later, to emit the error:
+///
+/// ```ignore (pseudo-rust)
+/// sess.emit_err(MoveOutOfBorrowError {
+///     expected,
+///     actual,
+///     span,
+///     other_span,
+///     opt_sugg: Some(suggestion, Applicability::MachineApplicable),
+/// });
+/// ```
+pub fn session_diagnostic_derive(s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
+    // Names for the diagnostic we build and the session we build it from.
+    let diag = format_ident!("diag");
+    let sess = format_ident!("sess");
+
+    SessionDiagnosticDerive::new(diag, sess, s).into_tokens()
+}
+
+// Checks whether the type name of `ty` matches `name`.
+//
+// Given some struct at a::b::c::Foo, this will return true for c::Foo, b::c::Foo, or
+// a::b::c::Foo. This reasonably allows qualified names to be used in the macro.
+fn type_matches_path(ty: &syn::Type, name: &[&str]) -> bool {
+    if let syn::Type::Path(ty) = ty {
+        ty.path
+            .segments
+            .iter()
+            .map(|s| s.ident.to_string())
+            .rev()
+            .zip(name.iter().rev())
+            .all(|(x, y)| &x.as_str() == y)
+    } else {
+        false
+    }
+}
+
+/// The central struct for constructing the as_error method from an annotated struct.
+struct SessionDiagnosticDerive<'a> {
+    structure: synstructure::Structure<'a>,
+    builder: SessionDiagnosticDeriveBuilder<'a>,
+}
+
+impl std::convert::From<syn::Error> for SessionDiagnosticDeriveError {
+    fn from(e: syn::Error) -> Self {
+        SessionDiagnosticDeriveError::SynError(e)
+    }
+}
+
+/// Equivalent to rustc:errors::diagnostic::DiagnosticId, except stores the quoted expression to
+/// initialise the code with.
+enum DiagnosticId {
+    Error(proc_macro2::TokenStream),
+    Lint(proc_macro2::TokenStream),
+}
+
+#[derive(Debug)]
+enum SessionDiagnosticDeriveError {
+    SynError(syn::Error),
+    ErrorHandled,
+}
+
+impl SessionDiagnosticDeriveError {
+    fn to_compile_error(self) -> proc_macro2::TokenStream {
+        match self {
+            SessionDiagnosticDeriveError::SynError(e) => e.to_compile_error(),
+            SessionDiagnosticDeriveError::ErrorHandled => {
+                // Return ! to avoid having to create a blank DiagnosticBuilder to return when an
+                // error has already been emitted to the compiler.
+                quote! {
+                    unreachable!()
+                }
+            }
+        }
+    }
+}
+
+fn span_err(span: impl proc_macro::MultiSpan, msg: &str) -> proc_macro::Diagnostic {
+    Diagnostic::spanned(span, proc_macro::Level::Error, msg)
+}
+
+/// For methods that return a Result<_, SessionDiagnosticDeriveError>: emit a diagnostic on
+/// span $span with msg $msg (and, optionally, perform additional decoration using the FnOnce
+/// passed in `diag`). Then, return Err(ErrorHandled).
+macro_rules! throw_span_err {
+    ($span:expr, $msg:expr) => {{ throw_span_err!($span, $msg, |diag| diag) }};
+    ($span:expr, $msg:expr, $f:expr) => {{
+        return Err(_throw_span_err($span, $msg, $f));
+    }};
+}
+
+/// When possible, prefer using throw_span_err! over using this function directly. This only exists
+/// as a function to constrain `f` to an impl FnOnce.
+fn _throw_span_err(
+    span: impl proc_macro::MultiSpan,
+    msg: &str,
+    f: impl FnOnce(proc_macro::Diagnostic) -> proc_macro::Diagnostic,
+) -> SessionDiagnosticDeriveError {
+    let diag = span_err(span, msg);
+    f(diag).emit();
+    SessionDiagnosticDeriveError::ErrorHandled
+}
+
+impl<'a> SessionDiagnosticDerive<'a> {
+    fn new(diag: syn::Ident, sess: syn::Ident, structure: synstructure::Structure<'a>) -> Self {
+        // Build the mapping of field names to fields. This allows attributes to peek values from
+        // other fields.
+        let mut fields_map = HashMap::new();
+
+        // Convenience bindings.
+        let ast = structure.ast();
+
+        if let syn::Data::Struct(syn::DataStruct { fields, .. }) = &ast.data {
+            for field in fields.iter() {
+                if let Some(ident) = &field.ident {
+                    fields_map.insert(ident.to_string(), field);
+                }
+            }
+        }
+
+        Self {
+            builder: SessionDiagnosticDeriveBuilder { diag, sess, fields: fields_map, kind: None },
+            structure,
+        }
+    }
+    fn into_tokens(self) -> proc_macro2::TokenStream {
+        let SessionDiagnosticDerive { structure, mut builder } = self;
+
+        let ast = structure.ast();
+        let attrs = &ast.attrs;
+
+        let implementation = {
+            if let syn::Data::Struct(..) = ast.data {
+                let preamble = {
+                    let preamble = attrs.iter().map(|attr| {
+                        builder
+                            .generate_structure_code(attr)
+                            .unwrap_or_else(|v| v.to_compile_error())
+                    });
+                    quote! {
+                        #(#preamble)*;
+                    }
+                };
+
+                let body = structure.each(|field_binding| {
+                    let field = field_binding.ast();
+                    let result = field.attrs.iter().map(|attr| {
+                        builder
+                            .generate_field_code(
+                                attr,
+                                FieldInfo {
+                                    vis: &field.vis,
+                                    binding: field_binding,
+                                    ty: &field.ty,
+                                    span: &field.span(),
+                                },
+                            )
+                            .unwrap_or_else(|v| v.to_compile_error())
+                    });
+                    return quote! {
+                        #(#result);*
+                    };
+                });
+                // Finally, putting it altogether.
+                match builder.kind {
+                    None => {
+                        span_err(ast.span().unwrap(), "`code` not specified")
+                        .help("use the [code = \"...\"] attribute to set this diagnostic's error code ")
+                        .emit();
+                        SessionDiagnosticDeriveError::ErrorHandled.to_compile_error()
+                    }
+                    Some((kind, _)) => match kind {
+                        DiagnosticId::Lint(_lint) => todo!(),
+                        DiagnosticId::Error(code) => {
+                            let (diag, sess) = (&builder.diag, &builder.sess);
+                            quote! {
+                                let mut #diag = #sess.struct_err_with_code("", rustc_errors::DiagnosticId::Error(#code));
+                                #preamble
+                                match self {
+                                    #body
+                                }
+                                #diag
+                            }
+                        }
+                    },
+                }
+            } else {
+                span_err(
+                    ast.span().unwrap(),
+                    "`#[derive(SessionDiagnostic)]` can only be used on structs",
+                )
+                .emit();
+                SessionDiagnosticDeriveError::ErrorHandled.to_compile_error()
+            }
+        };
+
+        let sess = &builder.sess;
+        structure.gen_impl(quote! {
+            gen impl<'__session_diagnostic_sess> rustc_session::SessionDiagnostic<'__session_diagnostic_sess>
+                    for @Self
+            {
+                fn into_diagnostic(
+                    self,
+                    #sess: &'__session_diagnostic_sess rustc_session::Session
+                ) -> rustc_errors::DiagnosticBuilder<'__session_diagnostic_sess> {
+                    #implementation
+                }
+            }
+        })
+    }
+}
+
+/// Field information passed to the builder. Deliberately omits attrs to discourage the generate_*
+/// methods from walking the attributes themselves.
+struct FieldInfo<'a> {
+    vis: &'a syn::Visibility,
+    binding: &'a synstructure::BindingInfo<'a>,
+    ty: &'a syn::Type,
+    span: &'a proc_macro2::Span,
+}
+
+/// Tracks persistent information required for building up the individual calls to diagnostic
+/// methods for the final generated method. This is a separate struct to SessionDerive only to be
+/// able to destructure and split self.builder and the self.structure up to avoid a double mut
+/// borrow later on.
+struct SessionDiagnosticDeriveBuilder<'a> {
+    /// Name of the session parameter that's passed in to the as_error method.
+    sess: syn::Ident,
+
+    /// Store a map of field name to its corresponding field. This is built on construction of the
+    /// derive builder.
+    fields: HashMap<String, &'a syn::Field>,
+
+    /// The identifier to use for the generated DiagnosticBuilder instance.
+    diag: syn::Ident,
+
+    /// Whether this is a lint or an error. This dictates how the diag will be initialised. Span
+    /// stores at what Span the kind was first set at (for error reporting purposes, if the kind
+    /// was multiply specified).
+    kind: Option<(DiagnosticId, proc_macro2::Span)>,
+}
+
+impl<'a> SessionDiagnosticDeriveBuilder<'a> {
+    fn generate_structure_code(
+        &mut self,
+        attr: &syn::Attribute,
+    ) -> Result<proc_macro2::TokenStream, SessionDiagnosticDeriveError> {
+        Ok(match attr.parse_meta()? {
+            syn::Meta::NameValue(syn::MetaNameValue { lit: syn::Lit::Str(s), .. }) => {
+                let formatted_str = self.build_format(&s.value(), attr.span());
+                let name = attr.path.segments.last().unwrap().ident.to_string();
+                let name = name.as_str();
+                match name {
+                    "message" => {
+                        let diag = &self.diag;
+                        quote! {
+                            #diag.set_primary_message(#formatted_str);
+                        }
+                    }
+                    attr @ "error" | attr @ "lint" => {
+                        self.set_kind_once(
+                            if attr == "error" {
+                                DiagnosticId::Error(formatted_str)
+                            } else if attr == "lint" {
+                                DiagnosticId::Lint(formatted_str)
+                            } else {
+                                unreachable!()
+                            },
+                            s.span(),
+                        )?;
+                        // This attribute is only allowed to be applied once, and the attribute
+                        // will be set in the initialisation code.
+                        quote! {}
+                    }
+                    other => throw_span_err!(
+                        attr.span().unwrap(),
+                        &format!(
+                            "`#[{} = ...]` is not a valid SessionDiagnostic struct attribute",
+                            other
+                        )
+                    ),
+                }
+            }
+            _ => todo!("unhandled meta kind"),
+        })
+    }
+
+    #[must_use]
+    fn set_kind_once(
+        &mut self,
+        kind: DiagnosticId,
+        span: proc_macro2::Span,
+    ) -> Result<(), SessionDiagnosticDeriveError> {
+        if self.kind.is_none() {
+            self.kind = Some((kind, span));
+            Ok(())
+        } else {
+            let kind_str = |kind: &DiagnosticId| match kind {
+                DiagnosticId::Lint(..) => "lint",
+                DiagnosticId::Error(..) => "error",
+            };
+
+            let existing_kind = kind_str(&self.kind.as_ref().unwrap().0);
+            let this_kind = kind_str(&kind);
+
+            let msg = if this_kind == existing_kind {
+                format!("`{}` specified multiple times", existing_kind)
+            } else {
+                format!("`{}` specified when `{}` was already specified", this_kind, existing_kind)
+            };
+            throw_span_err!(span.unwrap(), &msg);
+        }
+    }
+
+    fn generate_field_code(
+        &mut self,
+        attr: &syn::Attribute,
+        info: FieldInfo<'_>,
+    ) -> Result<proc_macro2::TokenStream, SessionDiagnosticDeriveError> {
+        let field_binding = &info.binding.binding;
+
+        let option_ty = option_inner_ty(&info.ty);
+
+        let generated_code = self.generate_non_option_field_code(
+            attr,
+            FieldInfo {
+                vis: info.vis,
+                binding: info.binding,
+                ty: option_ty.unwrap_or(&info.ty),
+                span: info.span,
+            },
+        )?;
+        Ok(if option_ty.is_none() {
+            quote! { #generated_code }
+        } else {
+            quote! {
+                if let Some(#field_binding) = #field_binding {
+                    #generated_code
+                }
+            }
+        })
+    }
+
+    fn generate_non_option_field_code(
+        &mut self,
+        attr: &syn::Attribute,
+        info: FieldInfo<'_>,
+    ) -> Result<proc_macro2::TokenStream, SessionDiagnosticDeriveError> {
+        let diag = &self.diag;
+        let field_binding = &info.binding.binding;
+        let name = attr.path.segments.last().unwrap().ident.to_string();
+        let name = name.as_str();
+        // At this point, we need to dispatch based on the attribute key + the
+        // type.
+        let meta = attr.parse_meta()?;
+        Ok(match meta {
+            syn::Meta::NameValue(syn::MetaNameValue { lit: syn::Lit::Str(s), .. }) => {
+                let formatted_str = self.build_format(&s.value(), attr.span());
+                match name {
+                    "message" => {
+                        if type_matches_path(&info.ty, &["rustc_span", "Span"]) {
+                            quote! {
+                                #diag.set_span(*#field_binding);
+                                #diag.set_primary_message(#formatted_str);
+                            }
+                        } else {
+                            throw_span_err!(
+                                attr.span().unwrap(),
+                                "the `#[message = \"...\"]` attribute can only be applied to fields of type Span"
+                            );
+                        }
+                    }
+                    "label" => {
+                        if type_matches_path(&info.ty, &["rustc_span", "Span"]) {
+                            quote! {
+                                #diag.span_label(*#field_binding, #formatted_str);
+                            }
+                        } else {
+                            throw_span_err!(
+                                attr.span().unwrap(),
+                                "The `#[label = ...]` attribute can only be applied to fields of type Span"
+                            );
+                        }
+                    }
+                    other => throw_span_err!(
+                        attr.span().unwrap(),
+                        &format!(
+                            "`#[{} = ...]` is not a valid SessionDiagnostic field attribute",
+                            other
+                        )
+                    ),
+                }
+            }
+            syn::Meta::List(list) => {
+                match list.path.segments.iter().last().unwrap().ident.to_string().as_str() {
+                    suggestion_kind @ "suggestion"
+                    | suggestion_kind @ "suggestion_short"
+                    | suggestion_kind @ "suggestion_hidden"
+                    | suggestion_kind @ "suggestion_verbose" => {
+                        // For suggest, we need to ensure we are running on a (Span,
+                        // Applicability) pair.
+                        let (span, applicability) = (|| match &info.ty {
+                            ty @ syn::Type::Path(..)
+                                if type_matches_path(ty, &["rustc_span", "Span"]) =>
+                            {
+                                let binding = &info.binding.binding;
+                                Ok((
+                                    quote!(*#binding),
+                                    quote!(rustc_errors::Applicability::Unspecified),
+                                ))
+                            }
+                            syn::Type::Tuple(tup) => {
+                                let mut span_idx = None;
+                                let mut applicability_idx = None;
+                                for (idx, elem) in tup.elems.iter().enumerate() {
+                                    if type_matches_path(elem, &["rustc_span", "Span"]) {
+                                        if span_idx.is_none() {
+                                            span_idx = Some(syn::Index::from(idx));
+                                        } else {
+                                            throw_span_err!(
+                                                info.span.clone().unwrap(),
+                                                "type of field annotated with `#[suggestion(...)]` contains more than one Span"
+                                            );
+                                        }
+                                    } else if type_matches_path(
+                                        elem,
+                                        &["rustc_errors", "Applicability"],
+                                    ) {
+                                        if applicability_idx.is_none() {
+                                            applicability_idx = Some(syn::Index::from(idx));
+                                        } else {
+                                            throw_span_err!(
+                                                info.span.clone().unwrap(),
+                                                "type of field annotated with `#[suggestion(...)]` contains more than one Applicability"
+                                            );
+                                        }
+                                    }
+                                }
+                                if let Some(span_idx) = span_idx {
+                                    let binding = &info.binding.binding;
+                                    let span = quote!(#binding.#span_idx);
+                                    let applicability = applicability_idx
+                                        .map(
+                                            |applicability_idx| quote!(#binding.#applicability_idx),
+                                        )
+                                        .unwrap_or(quote!(
+                                            rustc_errors::Applicability::Unspecified
+                                        ));
+                                    return Ok((span, applicability));
+                                }
+                                throw_span_err!(
+                                    info.span.clone().unwrap(),
+                                    "wrong types for suggestion",
+                                    |diag| {
+                                        diag.help("#[suggestion(...)] on a tuple field must be applied to fields of type (Span, Applicability)")
+                                    }
+                                );
+                            }
+                            _ => throw_span_err!(
+                                info.span.clone().unwrap(),
+                                "wrong field type for suggestion",
+                                |diag| {
+                                    diag.help("#[suggestion(...)] should be applied to fields of type Span or (Span, Applicability)")
+                                }
+                            ),
+                        })()?;
+                        // Now read the key-value pairs.
+                        let mut msg = None;
+                        let mut code = None;
+
+                        for arg in list.nested.iter() {
+                            if let syn::NestedMeta::Meta(syn::Meta::NameValue(arg_name_value)) = arg
+                            {
+                                if let syn::MetaNameValue { lit: syn::Lit::Str(s), .. } =
+                                    arg_name_value
+                                {
+                                    let name = arg_name_value
+                                        .path
+                                        .segments
+                                        .last()
+                                        .unwrap()
+                                        .ident
+                                        .to_string();
+                                    let name = name.as_str();
+                                    let formatted_str = self.build_format(&s.value(), arg.span());
+                                    match name {
+                                        "message" => {
+                                            msg = Some(formatted_str);
+                                        }
+                                        "code" => {
+                                            code = Some(formatted_str);
+                                        }
+                                        other => throw_span_err!(
+                                            arg.span().unwrap(),
+                                            &format!(
+                                                "`{}` is not a valid key for `#[suggestion(...)]`",
+                                                other
+                                            )
+                                        ),
+                                    }
+                                }
+                            }
+                        }
+                        let msg = if let Some(msg) = msg {
+                            quote!(#msg.as_str())
+                        } else {
+                            throw_span_err!(
+                                list.span().unwrap(),
+                                "missing suggestion message",
+                                |diag| {
+                                    diag.help("provide a suggestion message using #[suggestion(message = \"...\")]")
+                                }
+                            );
+                        };
+                        let code = code.unwrap_or_else(|| quote! { String::new() });
+                        // Now build it out:
+                        let suggestion_method = format_ident!("span_{}", suggestion_kind);
+                        quote! {
+                            #diag.#suggestion_method(#span, #msg, #code, #applicability);
+                        }
+                    }
+                    other => throw_span_err!(
+                        list.span().unwrap(),
+                        &format!("invalid annotation list `#[{}(...)]`", other)
+                    ),
+                }
+            }
+            _ => panic!("unhandled meta kind"),
+        })
+    }
+
+    /// In the strings in the attributes supplied to this macro, we want callers to be able to
+    /// reference fields in the format string. Take this, for example:
+    /// ```ignore (not-usage-example)
+    /// struct Point {
+    ///     #[error = "Expected a point greater than ({x}, {y})"]
+    ///     x: i32,
+    ///     y: i32,
+    /// }
+    /// ```
+    /// We want to automatically pick up that {x} refers `self.x` and {y} refers to `self.y`, then
+    /// generate this call to format!:
+    /// ```ignore (not-usage-example)
+    /// format!("Expected a point greater than ({x}, {y})", x = self.x, y = self.y)
+    /// ```
+    /// This function builds the entire call to format!.
+    fn build_format(&self, input: &String, span: proc_macro2::Span) -> proc_macro2::TokenStream {
+        // This set is used later to generate the final format string. To keep builds reproducible,
+        // the iteration order needs to be deterministic, hence why we use a BTreeSet here instead
+        // of a HashSet.
+        let mut referenced_fields: BTreeSet<String> = BTreeSet::new();
+
+        // At this point, we can start parsing the format string.
+        let mut it = input.chars().peekable();
+        // Once the start of a format string has been found, process the format string and spit out
+        // the referenced fields. Leaves `it` sitting on the closing brace of the format string, so the
+        // next call to `it.next()` retrieves the next character.
+        while let Some(c) = it.next() {
+            if c == '{' && *it.peek().unwrap_or(&'\0') != '{' {
+                #[must_use]
+                let mut eat_argument = || -> Option<String> {
+                    let mut result = String::new();
+                    // Format specifiers look like
+                    // format   := '{' [ argument ] [ ':' format_spec ] '}' .
+                    // Therefore, we only need to eat until ':' or '}' to find the argument.
+                    while let Some(c) = it.next() {
+                        result.push(c);
+                        let next = *it.peek().unwrap_or(&'\0');
+                        if next == '}' {
+                            break;
+                        } else if next == ':' {
+                            // Eat the ':' character.
+                            assert_eq!(it.next().unwrap(), ':');
+                            break;
+                        }
+                    }
+                    // Eat until (and including) the matching '}'
+                    while it.next()? != '}' {
+                        continue;
+                    }
+                    Some(result)
+                };
+
+                if let Some(referenced_field) = eat_argument() {
+                    referenced_fields.insert(referenced_field);
+                }
+            }
+        }
+        // At this point, `referenced_fields` contains a set of the unique fields that were
+        // referenced in the format string. Generate the corresponding "x = self.x" format
+        // string parameters:
+        let args = referenced_fields.into_iter().map(|field: String| {
+            let field_ident = format_ident!("{}", field);
+            let value = if self.fields.contains_key(&field) {
+                quote! {
+                    &self.#field_ident
+                }
+            } else {
+                // This field doesn't exist. Emit a diagnostic.
+                Diagnostic::spanned(
+                    span.unwrap(),
+                    proc_macro::Level::Error,
+                    format!("`{}` doesn't refer to a field on this type", field),
+                )
+                .emit();
+                quote! {
+                    "{#field}"
+                }
+            };
+            quote! {
+                #field_ident = #value
+            }
+        });
+        quote! {
+            format!(#input #(,#args)*)
+        }
+    }
+}
+
+/// If `ty` is an Option, returns Some(inner type). Else, returns None.
+fn option_inner_ty(ty: &syn::Type) -> Option<&syn::Type> {
+    if type_matches_path(ty, &["std", "option", "Option"]) {
+        if let syn::Type::Path(ty_path) = ty {
+            let path = &ty_path.path;
+            let ty = path.segments.iter().last().unwrap();
+            if let syn::PathArguments::AngleBracketed(bracketed) = &ty.arguments {
+                if bracketed.args.len() == 1 {
+                    if let syn::GenericArgument::Type(ty) = &bracketed.args[0] {
+                        return Some(ty);
+                    }
+                }
+            }
+        }
+    }
+    None
+}
diff --git a/compiler/rustc_metadata/Cargo.toml b/compiler/rustc_metadata/Cargo.toml
index 4b144f94ea7..f1975e78801 100644
--- a/compiler/rustc_metadata/Cargo.toml
+++ b/compiler/rustc_metadata/Cargo.toml
@@ -17,6 +17,7 @@ rustc_middle = { path = "../rustc_middle" }
 rustc_attr = { path = "../rustc_attr" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
+rustc_feature = { path = "../rustc_feature" }
 rustc_hir = { path = "../rustc_hir" }
 rustc_hir_pretty = { path = "../rustc_hir_pretty" }
 rustc_target = { path = "../rustc_target" }
diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs
index 85490f5f6e9..77766be7397 100644
--- a/compiler/rustc_metadata/src/lib.rs
+++ b/compiler/rustc_metadata/src/lib.rs
@@ -1,4 +1,4 @@
-#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
+#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![feature(bool_to_option)]
 #![feature(core_intrinsics)]
 #![feature(crate_visibility_modifier)]
diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs
index 3976475cb06..e76c2cb356f 100644
--- a/compiler/rustc_metadata/src/native_libs.rs
+++ b/compiler/rustc_metadata/src/native_libs.rs
@@ -170,7 +170,7 @@ impl Collector<'tcx> {
             feature_err(
                 &self.tcx.sess.parse_sess,
                 sym::static_nobundle,
-                span.unwrap_or_else(|| rustc_span::DUMMY_SP),
+                span.unwrap_or(rustc_span::DUMMY_SP),
                 "kind=\"static-nobundle\" is unstable",
             )
             .emit();
@@ -179,7 +179,7 @@ impl Collector<'tcx> {
             feature_err(
                 &self.tcx.sess.parse_sess,
                 sym::raw_dylib,
-                span.unwrap_or_else(|| rustc_span::DUMMY_SP),
+                span.unwrap_or(rustc_span::DUMMY_SP),
                 "kind=\"raw-dylib\" is unstable",
             )
             .emit();
diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs
index 43d76e9fdb4..72d54a26b01 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder.rs
@@ -11,6 +11,7 @@ use rustc_data_structures::fingerprint::{Fingerprint, FingerprintDecoder};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::svh::Svh;
 use rustc_data_structures::sync::{AtomicCell, Lock, LockGuard, Lrc, OnceCell};
+use rustc_errors::ErrorReported;
 use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind};
 use rustc_expand::proc_macro::{AttrProcMacro, BangProcMacro, ProcMacroDerive};
 use rustc_hir as hir;
@@ -562,6 +563,12 @@ impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for Span {
     }
 }
 
+impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for &'tcx [mir::abstract_const::Node<'tcx>] {
+    fn decode(d: &mut DecodeContext<'a, 'tcx>) -> Result<Self, String> {
+        ty::codec::RefDecodable::decode(d)
+    }
+}
+
 impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for &'tcx [(ty::Predicate<'tcx>, Span)] {
     fn decode(d: &mut DecodeContext<'a, 'tcx>) -> Result<Self, String> {
         ty::codec::RefDecodable::decode(d)
@@ -1191,6 +1198,19 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
             .decode((self, tcx))
     }
 
+    fn get_mir_abstract_const(
+        &self,
+        tcx: TyCtxt<'tcx>,
+        id: DefIndex,
+    ) -> Result<Option<&'tcx [mir::abstract_const::Node<'tcx>]>, ErrorReported> {
+        self.root
+            .tables
+            .mir_abstract_consts
+            .get(self, id)
+            .filter(|_| !self.is_proc_macro(id))
+            .map_or(Ok(None), |v| Ok(Some(v.decode((self, tcx)))))
+    }
+
     fn get_unused_generic_params(&self, id: DefIndex) -> FiniteBitSet<u32> {
         self.root
             .tables
diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
index 36ff65fc5eb..d4f577a7d1b 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
@@ -18,7 +18,7 @@ use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_session::utils::NativeLibKind;
 use rustc_session::{CrateDisambiguator, Session};
-use rustc_span::source_map::{self, Span, Spanned};
+use rustc_span::source_map::{Span, Spanned};
 use rustc_span::symbol::Symbol;
 
 use rustc_data_structures::sync::Lrc;
@@ -112,6 +112,7 @@ provide! { <'tcx> tcx, def_id, other, cdata,
     }
     optimized_mir => { tcx.arena.alloc(cdata.get_optimized_mir(tcx, def_id.index)) }
     promoted_mir => { tcx.arena.alloc(cdata.get_promoted_mir(tcx, def_id.index)) }
+    mir_abstract_const => { cdata.get_mir_abstract_const(tcx, def_id.index) }
     unused_generic_params => { cdata.get_unused_generic_params(def_id.index) }
     mir_const_qualif => { cdata.mir_const_qualif(def_id.index) }
     fn_sig => { cdata.fn_sig(def_id.index, tcx) }
@@ -421,7 +422,11 @@ impl CStore {
                 span,
                 attrs: attrs.to_vec(),
                 kind: ast::ItemKind::MacroDef(data.get_macro(id.index, sess)),
-                vis: source_map::respan(span.shrink_to_lo(), ast::VisibilityKind::Inherited),
+                vis: ast::Visibility {
+                    span: span.shrink_to_lo(),
+                    kind: ast::VisibilityKind::Inherited,
+                    tokens: None,
+                },
                 tokens: None,
             },
             data.root.edition,
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index 509ef1caf1a..556cf419920 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -40,6 +40,7 @@ use tracing::{debug, trace};
 pub(super) struct EncodeContext<'a, 'tcx> {
     opaque: opaque::Encoder,
     tcx: TyCtxt<'tcx>,
+    feat: &'tcx rustc_feature::Features,
 
     tables: TableBuilders<'tcx>,
 
@@ -162,7 +163,7 @@ impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for ExpnId {
 
 impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for Span {
     fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) -> opaque::EncodeResult {
-        if self.is_dummy() {
+        if *self == rustc_span::DUMMY_SP {
             return TAG_INVALID_SPAN.encode(s);
         }
 
@@ -320,6 +321,12 @@ impl<'a, 'tcx> TyEncoder<'tcx> for EncodeContext<'a, 'tcx> {
     }
 }
 
+impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for &'tcx [mir::abstract_const::Node<'tcx>] {
+    fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) -> opaque::EncodeResult {
+        (**self).encode(s)
+    }
+}
+
 impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for &'tcx [(ty::Predicate<'tcx>, Span)] {
     fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) -> opaque::EncodeResult {
         (**self).encode(s)
@@ -1108,6 +1115,11 @@ impl EncodeContext<'a, 'tcx> {
             if !unused.is_empty() {
                 record!(self.tables.unused_generic_params[def_id.to_def_id()] <- unused);
             }
+
+            let abstract_const = self.tcx.mir_abstract_const(def_id);
+            if let Ok(Some(abstract_const)) = abstract_const {
+                record!(self.tables.mir_abstract_consts[def_id.to_def_id()] <- abstract_const);
+            }
         }
     }
 
@@ -1132,15 +1144,25 @@ impl EncodeContext<'a, 'tcx> {
 
     fn encode_stability(&mut self, def_id: DefId) {
         debug!("EncodeContext::encode_stability({:?})", def_id);
-        if let Some(stab) = self.tcx.lookup_stability(def_id) {
-            record!(self.tables.stability[def_id] <- stab)
+
+        // The query lookup can take a measurable amount of time in crates with many items. Check if
+        // the stability attributes are even enabled before using their queries.
+        if self.feat.staged_api || self.tcx.sess.opts.debugging_opts.force_unstable_if_unmarked {
+            if let Some(stab) = self.tcx.lookup_stability(def_id) {
+                record!(self.tables.stability[def_id] <- stab)
+            }
         }
     }
 
     fn encode_const_stability(&mut self, def_id: DefId) {
         debug!("EncodeContext::encode_const_stability({:?})", def_id);
-        if let Some(stab) = self.tcx.lookup_const_stability(def_id) {
-            record!(self.tables.const_stability[def_id] <- stab)
+
+        // The query lookup can take a measurable amount of time in crates with many items. Check if
+        // the stability attributes are even enabled before using their queries.
+        if self.feat.staged_api || self.tcx.sess.opts.debugging_opts.force_unstable_if_unmarked {
+            if let Some(stab) = self.tcx.lookup_const_stability(def_id) {
+                record!(self.tables.const_stability[def_id] <- stab)
+            }
         }
     }
 
@@ -1278,7 +1300,7 @@ impl EncodeContext<'a, 'tcx> {
         });
         record!(self.tables.visibility[def_id] <-
             ty::Visibility::from_hir(&item.vis, item.hir_id, tcx));
-        record!(self.tables.span[def_id] <- item.span);
+        record!(self.tables.span[def_id] <- self.tcx.def_span(def_id));
         record!(self.tables.attributes[def_id] <- item.attrs);
         // FIXME(eddyb) there should be a nicer way to do this.
         match item.kind {
@@ -1418,7 +1440,7 @@ impl EncodeContext<'a, 'tcx> {
         let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
         let ty = self.tcx.typeck(def_id).node_type(hir_id);
 
-        record!(self.tables.kind[def_id.to_def_id()] <- match ty.kind {
+        record!(self.tables.kind[def_id.to_def_id()] <- match ty.kind() {
             ty::Generator(..) => {
                 let data = self.tcx.generator_kind(def_id).unwrap();
                 EntryKind::Generator(data)
@@ -1432,7 +1454,7 @@ impl EncodeContext<'a, 'tcx> {
         record!(self.tables.span[def_id.to_def_id()] <- self.tcx.def_span(def_id));
         record!(self.tables.attributes[def_id.to_def_id()] <- &self.tcx.get_attrs(def_id.to_def_id())[..]);
         self.encode_item_type(def_id.to_def_id());
-        if let ty::Closure(def_id, substs) = ty.kind {
+        if let ty::Closure(def_id, substs) = *ty.kind() {
             record!(self.tables.fn_sig[def_id] <- substs.as_closure().sig());
         }
         self.encode_generics(def_id.to_def_id());
@@ -1979,6 +2001,7 @@ fn encode_metadata_impl(tcx: TyCtxt<'_>) -> EncodedMetadata {
     let mut ecx = EncodeContext {
         opaque: encoder,
         tcx,
+        feat: tcx.features(),
         tables: Default::default(),
         lazy_state: LazyState::NoNode,
         type_shorthands: Default::default(),
diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs
index 1ba5962d119..ba540c94411 100644
--- a/compiler/rustc_metadata/src/rmeta/mod.rs
+++ b/compiler/rustc_metadata/src/rmeta/mod.rs
@@ -284,6 +284,7 @@ define_tables! {
     super_predicates: Table<DefIndex, Lazy!(ty::GenericPredicates<'tcx>)>,
     mir: Table<DefIndex, Lazy!(mir::Body<'tcx>)>,
     promoted_mir: Table<DefIndex, Lazy!(IndexVec<mir::Promoted, mir::Body<'tcx>>)>,
+    mir_abstract_consts: Table<DefIndex, Lazy!(&'tcx [mir::abstract_const::Node<'tcx>])>,
     unused_generic_params: Table<DefIndex, Lazy<FiniteBitSet<u32>>>,
     // `def_keys` and `def_path_hashes` represent a lazy version of a
     // `DefPathTable`. This allows us to avoid deserializing an entire
diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml
index 5a82cbf2997..5136e2743cd 100644
--- a/compiler/rustc_middle/Cargo.toml
+++ b/compiler/rustc_middle/Cargo.toml
@@ -26,8 +26,7 @@ rustc_index = { path = "../rustc_index" }
 rustc_serialize = { path = "../rustc_serialize" }
 rustc_ast = { path = "../rustc_ast" }
 rustc_span = { path = "../rustc_span" }
-byteorder = { version = "1.3" }
-chalk-ir = "0.14.0"
+chalk-ir = "0.28.0"
 smallvec = { version = "1.0", features = ["union", "may_dangle"] }
 measureme = "0.7.1"
 rustc_session = { path = "../rustc_session" }
diff --git a/compiler/rustc_middle/src/infer/unify_key.rs b/compiler/rustc_middle/src/infer/unify_key.rs
index 2580ac6bebd..a60a17befef 100644
--- a/compiler/rustc_middle/src/infer/unify_key.rs
+++ b/compiler/rustc_middle/src/infer/unify_key.rs
@@ -124,6 +124,7 @@ pub struct ConstVariableOrigin {
 pub enum ConstVariableOriginKind {
     MiscVariable,
     ConstInference,
+    // FIXME(const_generics): Consider storing the `DefId` of the param here.
     ConstParameterDefinition(Symbol),
     SubstitutionPlaceholder,
 }
diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs
index 1b2dea8a378..fa885ce2e7c 100644
--- a/compiler/rustc_middle/src/lib.rs
+++ b/compiler/rustc_middle/src/lib.rs
@@ -22,7 +22,8 @@
 //!
 //! This API is completely unstable and subject to change.
 
-#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
+#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
+#![feature(array_windows)]
 #![feature(backtrace)]
 #![feature(bool_to_option)]
 #![feature(box_patterns)]
@@ -30,25 +31,21 @@
 #![feature(cmp_min_max_by)]
 #![feature(const_fn)]
 #![feature(const_panic)]
-#![feature(const_fn_transmute)]
 #![feature(core_intrinsics)]
 #![feature(discriminant_kind)]
-#![feature(drain_filter)]
 #![feature(never_type)]
-#![feature(exhaustive_patterns)]
 #![feature(extern_types)]
 #![feature(nll)]
+#![feature(once_cell)]
 #![feature(option_expect_none)]
 #![feature(or_patterns)]
 #![feature(min_specialization)]
 #![feature(trusted_len)]
-#![feature(stmt_expr_attributes)]
 #![feature(test)]
 #![feature(in_band_lifetimes)]
 #![feature(crate_visibility_modifier)]
 #![feature(associated_type_bounds)]
 #![feature(rustc_attrs)]
-#![feature(hash_raw_entry)]
 #![feature(int_error_matching)]
 #![recursion_limit = "512"]
 
diff --git a/compiler/rustc_middle/src/middle/cstore.rs b/compiler/rustc_middle/src/middle/cstore.rs
index 1af1d581817..f3d7c8506ab 100644
--- a/compiler/rustc_middle/src/middle/cstore.rs
+++ b/compiler/rustc_middle/src/middle/cstore.rs
@@ -69,7 +69,7 @@ pub enum LibSource {
 
 impl LibSource {
     pub fn is_some(&self) -> bool {
-        if let LibSource::Some(_) = *self { true } else { false }
+        matches!(self, LibSource::Some(_))
     }
 
     pub fn option(&self) -> Option<PathBuf> {
diff --git a/compiler/rustc_middle/src/middle/lang_items.rs b/compiler/rustc_middle/src/middle/lang_items.rs
index 7194a035e89..cc9706f2d86 100644
--- a/compiler/rustc_middle/src/middle/lang_items.rs
+++ b/compiler/rustc_middle/src/middle/lang_items.rs
@@ -17,7 +17,7 @@ use rustc_target::spec::PanicStrategy;
 impl<'tcx> TyCtxt<'tcx> {
     /// Returns the `DefId` for a given `LangItem`.
     /// If not found, fatally aborts compilation.
-    pub fn require_lang_item(&self, lang_item: LangItem, span: Option<Span>) -> DefId {
+    pub fn require_lang_item(self, lang_item: LangItem, span: Option<Span>) -> DefId {
         self.lang_items().require(lang_item).unwrap_or_else(|msg| {
             if let Some(span) = span {
                 self.sess.span_fatal(span, &msg)
@@ -27,7 +27,7 @@ impl<'tcx> TyCtxt<'tcx> {
         })
     }
 
-    pub fn fn_trait_kind_from_lang_item(&self, id: DefId) -> Option<ty::ClosureKind> {
+    pub fn fn_trait_kind_from_lang_item(self, id: DefId) -> Option<ty::ClosureKind> {
         let items = self.lang_items();
         match Some(id) {
             x if x == items.fn_trait() => Some(ty::ClosureKind::Fn),
@@ -37,7 +37,7 @@ impl<'tcx> TyCtxt<'tcx> {
         }
     }
 
-    pub fn is_weak_lang_item(&self, item_def_id: DefId) -> bool {
+    pub fn is_weak_lang_item(self, item_def_id: DefId) -> bool {
         self.lang_items().is_weak_lang_item(item_def_id)
     }
 }
diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs
index b32eebbb11e..27658d50d45 100644
--- a/compiler/rustc_middle/src/middle/stability.rs
+++ b/compiler/rustc_middle/src/middle/stability.rs
@@ -13,6 +13,7 @@ use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX};
 use rustc_hir::{self, HirId};
+use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_session::lint::builtin::{DEPRECATED, DEPRECATED_IN_FUTURE, SOFT_UNSTABLE};
 use rustc_session::lint::{BuiltinLintDiagnostics, Lint, LintBuffer};
 use rustc_session::parse::feature_err_issue;
@@ -308,7 +309,7 @@ impl<'tcx> TyCtxt<'tcx> {
                 // #[rustc_deprecated] however wants to emit down the whole
                 // hierarchy.
                 if !skip || depr_entry.attr.is_since_rustc_version {
-                    let path = &self.def_path_str(def_id);
+                    let path = &with_no_trimmed_paths(|| self.def_path_str(def_id));
                     let kind = self.def_kind(def_id).descr(def_id);
                     let (message, lint) = deprecation_message(&depr_entry.attr, kind, path);
                     late_report_deprecation(
diff --git a/compiler/rustc_middle/src/mir/abstract_const.rs b/compiler/rustc_middle/src/mir/abstract_const.rs
new file mode 100644
index 00000000000..b85f1e6e5de
--- /dev/null
+++ b/compiler/rustc_middle/src/mir/abstract_const.rs
@@ -0,0 +1,20 @@
+//! A subset of a mir body used for const evaluatability checking.
+use crate::mir;
+use crate::ty;
+
+rustc_index::newtype_index! {
+    /// An index into an `AbstractConst`.
+    pub struct NodeId {
+        derive [HashStable]
+        DEBUG_FORMAT = "n{}",
+    }
+}
+
+/// A node of an `AbstractConst`.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
+pub enum Node<'tcx> {
+    Leaf(&'tcx ty::Const<'tcx>),
+    Binop(mir::BinOp, NodeId, NodeId),
+    UnaryOp(mir::UnOp, NodeId),
+    FunctionCall(NodeId, &'tcx [NodeId]),
+}
diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs
index 505939d56ed..ee1ea816e01 100644
--- a/compiler/rustc_middle/src/mir/interpret/allocation.rs
+++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs
@@ -345,10 +345,8 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra<Tag>> Allocation<Tag, Extra> {
 
     /// Reads a *non-ZST* scalar.
     ///
-    /// ZSTs can't be read for two reasons:
-    /// * byte-order cannot work with zero-element buffers;
-    /// * in order to obtain a `Pointer`, we need to check for ZSTness anyway due to integer
-    ///   pointers being valid for ZSTs.
+    /// ZSTs can't be read because in order to obtain a `Pointer`, we need to check
+    /// for ZSTness anyway due to integer pointers being valid for ZSTs.
     ///
     /// It is the caller's responsibility to check bounds and alignment beforehand.
     /// Most likely, you want to call `InterpCx::read_scalar` instead of this method.
@@ -397,10 +395,8 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra<Tag>> Allocation<Tag, Extra> {
 
     /// Writes a *non-ZST* scalar.
     ///
-    /// ZSTs can't be read for two reasons:
-    /// * byte-order cannot work with zero-element buffers;
-    /// * in order to obtain a `Pointer`, we need to check for ZSTness anyway due to integer
-    ///   pointers being valid for ZSTs.
+    /// ZSTs can't be read because in order to obtain a `Pointer`, we need to check
+    /// for ZSTness anyway due to integer pointers being valid for ZSTs.
     ///
     /// It is the caller's responsibility to check bounds and alignment beforehand.
     /// Most likely, you want to call `InterpCx::write_scalar` instead of this method.
diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs
index 059925088ce..d41e5680602 100644
--- a/compiler/rustc_middle/src/mir/interpret/error.rs
+++ b/compiler/rustc_middle/src/mir/interpret/error.rs
@@ -1,4 +1,4 @@
-use super::{AllocId, Pointer, RawConst, Scalar};
+use super::{AllocId, ConstAlloc, Pointer, Scalar};
 
 use crate::mir::interpret::ConstValue;
 use crate::ty::{layout, query::TyCtxtAt, tls, FnSig, Ty};
@@ -23,12 +23,18 @@ pub enum ErrorHandled {
     TooGeneric,
 }
 
+impl From<ErrorReported> for ErrorHandled {
+    fn from(err: ErrorReported) -> ErrorHandled {
+        ErrorHandled::Reported(err)
+    }
+}
+
 CloneTypeFoldableAndLiftImpls! {
     ErrorHandled,
 }
 
-pub type ConstEvalRawResult<'tcx> = Result<RawConst<'tcx>, ErrorHandled>;
-pub type ConstEvalResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
+pub type EvalToAllocationRawResult<'tcx> = Result<ConstAlloc<'tcx>, ErrorHandled>;
+pub type EvalToConstValueResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
 
 pub fn struct_error<'tcx>(tcx: TyCtxtAt<'tcx>, msg: &str) -> DiagnosticBuilder<'tcx> {
     struct_span_err!(tcx.sess, tcx.span, E0080, "{}", msg)
diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs
index 0dc3d6e344a..20363625e42 100644
--- a/compiler/rustc_middle/src/mir/interpret/mod.rs
+++ b/compiler/rustc_middle/src/mir/interpret/mod.rs
@@ -98,16 +98,17 @@ mod value;
 use std::convert::TryFrom;
 use std::fmt;
 use std::io;
+use std::io::{Read, Write};
 use std::num::NonZeroU32;
 use std::sync::atomic::{AtomicU32, Ordering};
 
-use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt};
 use rustc_ast::LitKind;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sync::{HashMapExt, Lock};
 use rustc_data_structures::tiny_list::TinyList;
 use rustc_hir::def_id::DefId;
 use rustc_macros::HashStable;
+use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_serialize::{Decodable, Encodable};
 use rustc_target::abi::{Endian, Size};
 
@@ -117,12 +118,12 @@ use crate::ty::subst::GenericArgKind;
 use crate::ty::{self, Instance, Ty, TyCtxt};
 
 pub use self::error::{
-    struct_error, CheckInAllocMsg, ConstEvalRawResult, ConstEvalResult, ErrorHandled, InterpError,
-    InterpErrorInfo, InterpResult, InvalidProgramInfo, MachineStopType, ResourceExhaustionInfo,
-    UndefinedBehaviorInfo, UninitBytesAccess, UnsupportedOpInfo,
+    struct_error, CheckInAllocMsg, ErrorHandled, EvalToAllocationRawResult, EvalToConstValueResult,
+    InterpError, InterpErrorInfo, InterpResult, InvalidProgramInfo, MachineStopType,
+    ResourceExhaustionInfo, UndefinedBehaviorInfo, UninitBytesAccess, UnsupportedOpInfo,
 };
 
-pub use self::value::{get_slice_bytes, ConstValue, RawConst, Scalar, ScalarMaybeUninit};
+pub use self::value::{get_slice_bytes, ConstAlloc, ConstValue, Scalar, ScalarMaybeUninit};
 
 pub use self::allocation::{Allocation, AllocationExtra, InitMask, Relocations};
 
@@ -145,7 +146,7 @@ pub struct GlobalId<'tcx> {
 
 impl GlobalId<'tcx> {
     pub fn display(self, tcx: TyCtxt<'tcx>) -> String {
-        let instance_name = tcx.def_path_str(self.instance.def.def_id());
+        let instance_name = with_no_trimmed_paths(|| tcx.def_path_str(self.instance.def.def_id()));
         if let Some(promoted) = self.promoted {
             format!("{}::{:?}", instance_name, promoted)
         } else {
@@ -446,14 +447,14 @@ impl<'tcx> TyCtxt<'tcx> {
     ///
     /// Make sure to call `set_alloc_id_memory` or `set_alloc_id_same_memory` before returning such
     /// an `AllocId` from a query.
-    pub fn reserve_alloc_id(&self) -> AllocId {
+    pub fn reserve_alloc_id(self) -> AllocId {
         self.alloc_map.lock().reserve()
     }
 
     /// Reserves a new ID *if* this allocation has not been dedup-reserved before.
     /// Should only be used for function pointers and statics, we don't want
     /// to dedup IDs for "real" memory!
-    fn reserve_and_set_dedup(&self, alloc: GlobalAlloc<'tcx>) -> AllocId {
+    fn reserve_and_set_dedup(self, alloc: GlobalAlloc<'tcx>) -> AllocId {
         let mut alloc_map = self.alloc_map.lock();
         match alloc {
             GlobalAlloc::Function(..) | GlobalAlloc::Static(..) => {}
@@ -471,13 +472,13 @@ impl<'tcx> TyCtxt<'tcx> {
 
     /// Generates an `AllocId` for a static or return a cached one in case this function has been
     /// called on the same static before.
-    pub fn create_static_alloc(&self, static_id: DefId) -> AllocId {
+    pub fn create_static_alloc(self, static_id: DefId) -> AllocId {
         self.reserve_and_set_dedup(GlobalAlloc::Static(static_id))
     }
 
     /// Generates an `AllocId` for a function.  Depending on the function type,
     /// this might get deduplicated or assigned a new ID each time.
-    pub fn create_fn_alloc(&self, instance: Instance<'tcx>) -> AllocId {
+    pub fn create_fn_alloc(self, instance: Instance<'tcx>) -> AllocId {
         // Functions cannot be identified by pointers, as asm-equal functions can get deduplicated
         // by the linker (we set the "unnamed_addr" attribute for LLVM) and functions can be
         // duplicated across crates.
@@ -506,7 +507,7 @@ impl<'tcx> TyCtxt<'tcx> {
     /// Statics with identical content will still point to the same `Allocation`, i.e.,
     /// their data will be deduplicated through `Allocation` interning -- but they
     /// are different places in memory and as such need different IDs.
-    pub fn create_memory_alloc(&self, mem: &'tcx Allocation) -> AllocId {
+    pub fn create_memory_alloc(self, mem: &'tcx Allocation) -> AllocId {
         let id = self.reserve_alloc_id();
         self.set_alloc_id_memory(id, mem);
         id
@@ -518,7 +519,7 @@ impl<'tcx> TyCtxt<'tcx> {
     /// This function exists to allow const eval to detect the difference between evaluation-
     /// local dangling pointers and allocations in constants/statics.
     #[inline]
-    pub fn get_global_alloc(&self, id: AllocId) -> Option<GlobalAlloc<'tcx>> {
+    pub fn get_global_alloc(self, id: AllocId) -> Option<GlobalAlloc<'tcx>> {
         self.alloc_map.lock().alloc_map.get(&id).cloned()
     }
 
@@ -528,7 +529,7 @@ impl<'tcx> TyCtxt<'tcx> {
     /// constants (as all constants must pass interning and validation that check for dangling
     /// ids), this function is frequently used throughout rustc, but should not be used within
     /// the miri engine.
-    pub fn global_alloc(&self, id: AllocId) -> GlobalAlloc<'tcx> {
+    pub fn global_alloc(self, id: AllocId) -> GlobalAlloc<'tcx> {
         match self.get_global_alloc(id) {
             Some(alloc) => alloc,
             None => bug!("could not find allocation for {}", id),
@@ -537,7 +538,7 @@ impl<'tcx> TyCtxt<'tcx> {
 
     /// Freezes an `AllocId` created with `reserve` by pointing it at an `Allocation`. Trying to
     /// call this function twice, even with the same `Allocation` will ICE the compiler.
-    pub fn set_alloc_id_memory(&self, id: AllocId, mem: &'tcx Allocation) {
+    pub fn set_alloc_id_memory(self, id: AllocId, mem: &'tcx Allocation) {
         if let Some(old) = self.alloc_map.lock().alloc_map.insert(id, GlobalAlloc::Memory(mem)) {
             bug!("tried to set allocation ID {}, but it was already existing as {:#?}", id, old);
         }
@@ -545,7 +546,7 @@ impl<'tcx> TyCtxt<'tcx> {
 
     /// Freezes an `AllocId` created with `reserve` by pointing it at an `Allocation`. May be called
     /// twice for the same `(AllocId, Allocation)` pair.
-    fn set_alloc_id_same_memory(&self, id: AllocId, mem: &'tcx Allocation) {
+    fn set_alloc_id_same_memory(self, id: AllocId, mem: &'tcx Allocation) {
         self.alloc_map.lock().alloc_map.insert_same(id, GlobalAlloc::Memory(mem));
     }
 }
@@ -560,19 +561,33 @@ pub fn write_target_uint(
     mut target: &mut [u8],
     data: u128,
 ) -> Result<(), io::Error> {
-    let len = target.len();
+    // This u128 holds an "any-size uint" (since smaller uints can fits in it)
+    // So we do not write all bytes of the u128, just the "payload".
     match endianness {
-        Endian::Little => target.write_uint128::<LittleEndian>(data, len),
-        Endian::Big => target.write_uint128::<BigEndian>(data, len),
-    }
+        Endian::Little => target.write(&data.to_le_bytes())?,
+        Endian::Big => target.write(&data.to_be_bytes()[16 - target.len()..])?,
+    };
+    debug_assert!(target.len() == 0); // We should have filled the target buffer.
+    Ok(())
 }
 
 #[inline]
 pub fn read_target_uint(endianness: Endian, mut source: &[u8]) -> Result<u128, io::Error> {
-    match endianness {
-        Endian::Little => source.read_uint128::<LittleEndian>(source.len()),
-        Endian::Big => source.read_uint128::<BigEndian>(source.len()),
-    }
+    // This u128 holds an "any-size uint" (since smaller uints can fits in it)
+    let mut buf = [0u8; std::mem::size_of::<u128>()];
+    // So we do not read exactly 16 bytes into the u128, just the "payload".
+    let uint = match endianness {
+        Endian::Little => {
+            source.read(&mut buf)?;
+            Ok(u128::from_le_bytes(buf))
+        }
+        Endian::Big => {
+            source.read(&mut buf[16 - source.len()..])?;
+            Ok(u128::from_be_bytes(buf))
+        }
+    };
+    debug_assert!(source.len() == 0); // We should have consumed the source buffer.
+    uint
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs
index dcc1f8b1a4b..f366681bc75 100644
--- a/compiler/rustc_middle/src/mir/interpret/queries.rs
+++ b/compiler/rustc_middle/src/mir/interpret/queries.rs
@@ -1,4 +1,4 @@
-use super::{ConstEvalResult, ErrorHandled, GlobalId};
+use super::{ErrorHandled, EvalToConstValueResult, GlobalId};
 
 use crate::mir;
 use crate::ty::subst::{InternalSubsts, SubstsRef};
@@ -10,7 +10,7 @@ impl<'tcx> TyCtxt<'tcx> {
     /// Evaluates a constant without providing any substitutions. This is useful to evaluate consts
     /// that can't take any generic arguments like statics, const items or enum discriminants. If a
     /// generic parameter is used within the constant `ErrorHandled::ToGeneric` will be returned.
-    pub fn const_eval_poly(self, def_id: DefId) -> ConstEvalResult<'tcx> {
+    pub fn const_eval_poly(self, def_id: DefId) -> EvalToConstValueResult<'tcx> {
         // In some situations def_id will have substitutions within scope, but they aren't allowed
         // to be used. So we can't use `Instance::mono`, instead we feed unresolved substitutions
         // into `const_eval` which will return `ErrorHandled::ToGeneric` if any of them are
@@ -38,7 +38,7 @@ impl<'tcx> TyCtxt<'tcx> {
         substs: SubstsRef<'tcx>,
         promoted: Option<mir::Promoted>,
         span: Option<Span>,
-    ) -> ConstEvalResult<'tcx> {
+    ) -> EvalToConstValueResult<'tcx> {
         match ty::Instance::resolve_opt_const_arg(self, param_env, def, substs) {
             Ok(Some(instance)) => {
                 let cid = GlobalId { instance, promoted };
@@ -54,7 +54,7 @@ impl<'tcx> TyCtxt<'tcx> {
         param_env: ty::ParamEnv<'tcx>,
         instance: ty::Instance<'tcx>,
         span: Option<Span>,
-    ) -> ConstEvalResult<'tcx> {
+    ) -> EvalToConstValueResult<'tcx> {
         self.const_eval_global_id(param_env, GlobalId { instance, promoted: None }, span)
     }
 
@@ -64,14 +64,14 @@ impl<'tcx> TyCtxt<'tcx> {
         param_env: ty::ParamEnv<'tcx>,
         cid: GlobalId<'tcx>,
         span: Option<Span>,
-    ) -> ConstEvalResult<'tcx> {
+    ) -> EvalToConstValueResult<'tcx> {
         // Const-eval shouldn't depend on lifetimes at all, so we can erase them, which should
         // improve caching of queries.
         let inputs = self.erase_regions(&param_env.and(cid));
         if let Some(span) = span {
-            self.at(span).const_eval_validated(inputs)
+            self.at(span).eval_to_const_value_raw(inputs)
         } else {
-            self.const_eval_validated(inputs)
+            self.eval_to_const_value_raw(inputs)
         }
     }
 
@@ -94,7 +94,7 @@ impl<'tcx> TyCtxt<'tcx> {
         param_env: ty::ParamEnv<'tcx>,
     ) -> Result<&'tcx mir::Allocation, ErrorHandled> {
         trace!("eval_to_allocation: Need to compute {:?}", gid);
-        let raw_const = self.const_eval_raw(param_env.and(gid))?;
+        let raw_const = self.eval_to_allocation_raw(param_env.and(gid))?;
         Ok(self.global_alloc(raw_const.alloc_id).unwrap_memory())
     }
 }
diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs
index 7d6ff3eb5c1..206f01c2498 100644
--- a/compiler/rustc_middle/src/mir/interpret/value.rs
+++ b/compiler/rustc_middle/src/mir/interpret/value.rs
@@ -12,9 +12,9 @@ use crate::ty::{ParamEnv, Ty, TyCtxt};
 
 use super::{sign_extend, truncate, AllocId, Allocation, InterpResult, Pointer, PointerArithmetic};
 
-/// Represents the result of a raw const operation, pre-validation.
-#[derive(Clone, HashStable)]
-pub struct RawConst<'tcx> {
+/// Represents the result of const evaluation via the `eval_to_allocation` query.
+#[derive(Clone, HashStable, TyEncodable, TyDecodable)]
+pub struct ConstAlloc<'tcx> {
     // the value lives here, at offset 0, and that allocation definitely is a `AllocKind::Memory`
     // (so you can use `AllocMap::unwrap_memory`).
     pub alloc_id: AllocId,
@@ -578,6 +578,9 @@ pub enum ScalarMaybeUninit<Tag = ()> {
     Uninit,
 }
 
+#[cfg(target_arch = "x86_64")]
+static_assert_size!(ScalarMaybeUninit, 24);
+
 impl<Tag> From<Scalar<Tag>> for ScalarMaybeUninit<Tag> {
     #[inline(always)]
     fn from(s: Scalar<Tag>) -> Self {
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index 785a7f0c51a..8ff75bf392e 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -40,6 +40,7 @@ use std::{iter, mem, option};
 use self::predecessors::{PredecessorCache, Predecessors};
 pub use self::query::*;
 
+pub mod abstract_const;
 pub mod coverage;
 pub mod interpret;
 pub mod mono;
@@ -186,6 +187,23 @@ pub struct Body<'tcx> {
     /// FIXME(oli-obk): rewrite the promoted during promotion to eliminate the cell components.
     pub ignore_interior_mut_in_const_validation: bool,
 
+    /// Does this body use generic parameters. This is used for the `ConstEvaluatable` check.
+    ///
+    /// Note that this does not actually mean that this body is not computable right now.
+    /// The repeat count in the following example is polymorphic, but can still be evaluated
+    /// without knowing anything about the type parameter `T`.
+    ///
+    /// ```rust
+    /// fn test<T>() {
+    ///     let _ = [0; std::mem::size_of::<*mut T>()];
+    /// }
+    /// ```
+    ///
+    /// **WARNING**: Do not change this flags after the MIR was originally created, even if an optimization
+    /// removed the last mention of all generic params. We do not want to rely on optimizations and
+    /// potentially allow things like `[u8; std::mem::size_of::<T>() * 0]` due to this.
+    pub is_polymorphic: bool,
+
     predecessor_cache: PredecessorCache,
 }
 
@@ -208,7 +226,7 @@ impl<'tcx> Body<'tcx> {
             local_decls.len()
         );
 
-        Body {
+        let mut body = Body {
             phase: MirPhase::Build,
             basic_blocks,
             source_scopes,
@@ -224,8 +242,11 @@ impl<'tcx> Body<'tcx> {
             span,
             required_consts: Vec::new(),
             ignore_interior_mut_in_const_validation: false,
+            is_polymorphic: false,
             predecessor_cache: PredecessorCache::new(),
-        }
+        };
+        body.is_polymorphic = body.has_param_types_or_consts();
+        body
     }
 
     /// Returns a partially initialized MIR body containing only a list of basic blocks.
@@ -234,7 +255,7 @@ impl<'tcx> Body<'tcx> {
     /// is only useful for testing but cannot be `#[cfg(test)]` because it is used in a different
     /// crate.
     pub fn new_cfg_only(basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>) -> Self {
-        Body {
+        let mut body = Body {
             phase: MirPhase::Build,
             basic_blocks,
             source_scopes: IndexVec::new(),
@@ -250,8 +271,11 @@ impl<'tcx> Body<'tcx> {
             generator_kind: None,
             var_debug_info: Vec::new(),
             ignore_interior_mut_in_const_validation: false,
+            is_polymorphic: false,
             predecessor_cache: PredecessorCache::new(),
-        }
+        };
+        body.is_polymorphic = body.has_param_types_or_consts();
+        body
     }
 
     #[inline]
@@ -899,6 +923,8 @@ pub enum LocalInfo<'tcx> {
     User(ClearCrossCrate<BindingForm<'tcx>>),
     /// A temporary created that references the static with the given `DefId`.
     StaticRef { def_id: DefId, is_thread_local: bool },
+    /// A temporary created that references the const with the given `DefId`
+    ConstRef { def_id: DefId },
 }
 
 impl<'tcx> LocalDecl<'tcx> {
@@ -1051,6 +1077,25 @@ pub struct VarDebugInfo<'tcx> {
 // BasicBlock
 
 rustc_index::newtype_index! {
+    /// A node in the MIR [control-flow graph][CFG].
+    ///
+    /// There are no branches (e.g., `if`s, function calls, etc.) within a basic block, which makes
+    /// it easier to do [data-flow analyses] and optimizations. Instead, branches are represented
+    /// as an edge in a graph between basic blocks.
+    ///
+    /// Basic blocks consist of a series of [statements][Statement], ending with a
+    /// [terminator][Terminator]. Basic blocks can have multiple predecessors and successors,
+    /// however there is a MIR pass ([`CriticalCallEdges`]) that removes *critical edges*, which
+    /// are edges that go from a multi-successor node to a multi-predecessor node. This pass is
+    /// needed because some analyses require that there are no critical edges in the CFG.
+    ///
+    /// Read more about basic blocks in the [rustc-dev-guide][guide-mir].
+    ///
+    /// [CFG]: https://rustc-dev-guide.rust-lang.org/appendix/background.html#cfg
+    /// [data-flow analyses]:
+    ///     https://rustc-dev-guide.rust-lang.org/appendix/background.html#what-is-a-dataflow-analysis
+    /// [`CriticalCallEdges`]: ../../rustc_mir/transform/add_call_guards/enum.AddCallGuards.html#variant.CriticalCallEdges
+    /// [guide-mir]: https://rustc-dev-guide.rust-lang.org/mir/
     pub struct BasicBlock {
         derive [HashStable]
         DEBUG_FORMAT = "bb{}",
@@ -1067,6 +1112,7 @@ impl BasicBlock {
 ///////////////////////////////////////////////////////////////////////////
 // BasicBlockData and Terminator
 
+/// See [`BasicBlock`] for documentation on what basic blocks are at a high level.
 #[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable)]
 pub struct BasicBlockData<'tcx> {
     /// List of statements in this block.
@@ -1530,7 +1576,24 @@ impl Debug for Statement<'_> {
             AscribeUserType(box (ref place, ref c_ty), ref variance) => {
                 write!(fmt, "AscribeUserType({:?}, {:?}, {:?})", place, variance, c_ty)
             }
-            Coverage(box ref coverage) => write!(fmt, "{:?}", coverage),
+            Coverage(box ref coverage) => {
+                let rgn = &coverage.code_region;
+                match coverage.kind {
+                    CoverageKind::Counter { id, .. } => {
+                        write!(fmt, "Coverage::Counter({:?}) for {:?}", id.index(), rgn)
+                    }
+                    CoverageKind::Expression { id, lhs, op, rhs } => write!(
+                        fmt,
+                        "Coverage::Expression({:?}) = {} {} {} for {:?}",
+                        id.index(),
+                        lhs.index(),
+                        if op == coverage::Op::Add { "+" } else { "-" },
+                        rhs.index(),
+                        rgn
+                    ),
+                    CoverageKind::Unreachable => write!(fmt, "Coverage::Unreachable for {:?}", rgn),
+                }
+            }
             Nop => write!(fmt, "nop"),
         }
     }
@@ -1937,6 +2000,15 @@ impl<'tcx> Operand<'tcx> {
             Operand::Constant(_) => None,
         }
     }
+
+    /// Returns the `Constant` that is the target of this `Operand`, or `None` if this `Operand` is a
+    /// place.
+    pub fn constant(&self) -> Option<&Constant<'tcx>> {
+        match self {
+            Operand::Constant(x) => Some(&**x),
+            Operand::Copy(_) | Operand::Move(_) => None,
+        }
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////
@@ -2234,8 +2306,8 @@ impl<'tcx> Debug for Rvalue<'tcx> {
 /// Constants
 ///
 /// Two constants are equal if they are the same constant. Note that
-/// this does not necessarily mean that they are "==" in Rust -- in
-/// particular one must be wary of `NaN`!
+/// this does not necessarily mean that they are `==` in Rust. In
+/// particular, one must be wary of `NaN`!
 
 #[derive(Clone, Copy, PartialEq, TyEncodable, TyDecodable, HashStable)]
 pub struct Constant<'tcx> {
@@ -2465,7 +2537,7 @@ impl<'tcx> Debug for Constant<'tcx> {
 
 impl<'tcx> Display for Constant<'tcx> {
     fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
-        match self.literal.ty.kind {
+        match self.literal.ty.kind() {
             ty::FnDef(..) => {}
             _ => write!(fmt, "const ")?,
         }
diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs
index 0d5f6619df5..79e2c5aac23 100644
--- a/compiler/rustc_middle/src/mir/mono.rs
+++ b/compiler/rustc_middle/src/mir/mono.rs
@@ -1,6 +1,5 @@
 use crate::dep_graph::{DepConstructor, DepNode, WorkProduct, WorkProductId};
 use crate::ich::{NodeIdHashingMode, StableHashingContext};
-use crate::ty::print::obsolete::DefPathBasedNames;
 use crate::ty::{subst::InternalSubsts, Instance, InstanceDef, SymbolName, TyCtxt};
 use rustc_attr::InlineAttr;
 use rustc_data_structures::base_n;
@@ -86,7 +85,7 @@ impl<'tcx> MonoItem<'tcx> {
             .debugging_opts
             .inline_in_all_cgus
             .unwrap_or_else(|| tcx.sess.opts.optimize != OptLevel::No)
-            && tcx.sess.opts.cg.link_dead_code != Some(true);
+            && !tcx.sess.link_dead_code();
 
         match *self {
             MonoItem::Fn(ref instance) => {
@@ -171,30 +170,6 @@ impl<'tcx> MonoItem<'tcx> {
         !tcx.subst_and_check_impossible_predicates((def_id, &substs))
     }
 
-    pub fn to_string(&self, tcx: TyCtxt<'tcx>, debug: bool) -> String {
-        return match *self {
-            MonoItem::Fn(instance) => to_string_internal(tcx, "fn ", instance, debug),
-            MonoItem::Static(def_id) => {
-                let instance = Instance::new(def_id, tcx.intern_substs(&[]));
-                to_string_internal(tcx, "static ", instance, debug)
-            }
-            MonoItem::GlobalAsm(..) => "global_asm".to_string(),
-        };
-
-        fn to_string_internal<'tcx>(
-            tcx: TyCtxt<'tcx>,
-            prefix: &str,
-            instance: Instance<'tcx>,
-            debug: bool,
-        ) -> String {
-            let mut result = String::with_capacity(32);
-            result.push_str(prefix);
-            let printer = DefPathBasedNames::new(tcx, false, false);
-            printer.push_instance_as_string(instance, &mut result, debug);
-            result
-        }
-    }
-
     pub fn local_span(&self, tcx: TyCtxt<'tcx>) -> Option<Span> {
         match *self {
             MonoItem::Fn(Instance { def, .. }) => {
@@ -229,6 +204,18 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for MonoItem<'tcx> {
     }
 }
 
+impl<'tcx> fmt::Display for MonoItem<'tcx> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match *self {
+            MonoItem::Fn(instance) => write!(f, "fn {}", instance),
+            MonoItem::Static(def_id) => {
+                write!(f, "static {}", Instance::new(def_id, InternalSubsts::empty()))
+            }
+            MonoItem::GlobalAsm(..) => write!(f, "global_asm"),
+        }
+    }
+}
+
 pub struct CodegenUnit<'tcx> {
     /// A name for this CGU. Incremental compilation requires that
     /// name be unique amongst **all** crates. Therefore, it should
diff --git a/compiler/rustc_middle/src/mir/predecessors.rs b/compiler/rustc_middle/src/mir/predecessors.rs
index b16a1d53fff..a8b74883355 100644
--- a/compiler/rustc_middle/src/mir/predecessors.rs
+++ b/compiler/rustc_middle/src/mir/predecessors.rs
@@ -33,7 +33,7 @@ impl PredecessorCache {
         self.cache = OnceCell::new();
     }
 
-    /// Returns the the predecessor graph for this MIR.
+    /// Returns the predecessor graph for this MIR.
     #[inline]
     pub(super) fn compute(
         &self,
diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs
index efcd41e5c18..b9e4f6fb12e 100644
--- a/compiler/rustc_middle/src/mir/tcx.rs
+++ b/compiler/rustc_middle/src/mir/tcx.rs
@@ -33,7 +33,7 @@ impl<'tcx> PlaceTy<'tcx> {
     ///
     /// Note that the resulting type has not been normalized.
     pub fn field_ty(self, tcx: TyCtxt<'tcx>, f: &Field) -> Ty<'tcx> {
-        let answer = match self.ty.kind {
+        let answer = match self.ty.kind() {
             ty::Adt(adt_def, substs) => {
                 let variant_def = match self.variant_index {
                     None => adt_def.non_enum_variant(),
@@ -90,7 +90,7 @@ impl<'tcx> PlaceTy<'tcx> {
                 PlaceTy::from_ty(self.ty.builtin_index().unwrap())
             }
             ProjectionElem::Subslice { from, to, from_end } => {
-                PlaceTy::from_ty(match self.ty.kind {
+                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, size) if from_end => {
diff --git a/compiler/rustc_middle/src/mir/terminator/mod.rs b/compiler/rustc_middle/src/mir/terminator/mod.rs
index fcfd648c2b7..8909f02270c 100644
--- a/compiler/rustc_middle/src/mir/terminator/mod.rs
+++ b/compiler/rustc_middle/src/mir/terminator/mod.rs
@@ -96,6 +96,8 @@ pub enum TerminatorKind<'tcx> {
     ///   P <- V
     /// }
     /// ```
+    ///
+    /// Note that DropAndReplace is eliminated as part of the `ElaborateDrops` pass.
     DropAndReplace {
         place: Place<'tcx>,
         value: Operand<'tcx>,
diff --git a/compiler/rustc_middle/src/mir/type_foldable.rs b/compiler/rustc_middle/src/mir/type_foldable.rs
index 6bb6abe0289..ad2eae0298c 100644
--- a/compiler/rustc_middle/src/mir/type_foldable.rs
+++ b/compiler/rustc_middle/src/mir/type_foldable.rs
@@ -175,7 +175,7 @@ impl<'tcx> TypeFoldable<'tcx> for Rvalue<'tcx> {
         use crate::mir::Rvalue::*;
         match *self {
             Use(ref op) => Use(op.fold_with(folder)),
-            Repeat(ref op, len) => Repeat(op.fold_with(folder), len),
+            Repeat(ref op, len) => Repeat(op.fold_with(folder), len.fold_with(folder)),
             ThreadLocalRef(did) => ThreadLocalRef(did.fold_with(folder)),
             Ref(region, bk, ref place) => {
                 Ref(region.fold_with(folder), bk, place.fold_with(folder))
diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs
index 6515ae31b46..a008bd5f75f 100644
--- a/compiler/rustc_middle/src/mir/visit.rs
+++ b/compiler/rustc_middle/src/mir/visit.rs
@@ -1150,8 +1150,6 @@ pub enum NonUseContext {
     StorageDead,
     /// User type annotation assertions for NLL.
     AscribeUserTy,
-    /// Coverage code region and counter metadata.
-    Coverage,
     /// The data of an user variable, for debug info.
     VarDebugInfo,
 }
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index e05752f08f6..9c047cbfaef 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -42,48 +42,48 @@ rustc_queries! {
     }
 
     Other {
-        // Represents crate as a whole (as distinct from the top-level crate module).
-        // If you call `hir_crate` (e.g., indirectly by calling `tcx.hir().krate()`),
-        // we will have to assume that any change means that you need to be recompiled.
-        // This is because the `hir_crate` query gives you access to all other items.
-        // To avoid this fate, do not call `tcx.hir().krate()`; instead,
-        // prefer wrappers like `tcx.visit_all_items_in_krate()`.
+        /// Represents crate as a whole (as distinct from the top-level crate module).
+        /// If you call `hir_crate` (e.g., indirectly by calling `tcx.hir().krate()`),
+        /// we will have to assume that any change means that you need to be recompiled.
+        /// This is because the `hir_crate` query gives you access to all other items.
+        /// To avoid this fate, do not call `tcx.hir().krate()`; instead,
+        /// prefer wrappers like `tcx.visit_all_items_in_krate()`.
         query hir_crate(key: CrateNum) -> &'tcx Crate<'tcx> {
             eval_always
             no_hash
             desc { "get the crate HIR" }
         }
 
-        // The indexed HIR. This can be conveniently accessed by `tcx.hir()`.
-        // Avoid calling this query directly.
+        /// The indexed HIR. This can be conveniently accessed by `tcx.hir()`.
+        /// Avoid calling this query directly.
         query index_hir(_: CrateNum) -> &'tcx map::IndexedHir<'tcx> {
             eval_always
             no_hash
             desc { "index HIR" }
         }
 
-        // The items in a module.
-        //
-        // This can be conveniently accessed by `tcx.hir().visit_item_likes_in_module`.
-        // Avoid calling this query directly.
+        /// The items in a module.
+        ///
+        /// This can be conveniently accessed by `tcx.hir().visit_item_likes_in_module`.
+        /// Avoid calling this query directly.
         query hir_module_items(key: LocalDefId) -> &'tcx hir::ModuleItems {
             eval_always
             desc { |tcx| "HIR module items in `{}`", tcx.def_path_str(key.to_def_id()) }
         }
 
-        // Gives access to the HIR node for the HIR owner `key`.
-        //
-        // This can be conveniently accessed by methods on `tcx.hir()`.
-        // Avoid calling this query directly.
+        /// Gives access to the HIR node for the HIR owner `key`.
+        ///
+        /// This can be conveniently accessed by methods on `tcx.hir()`.
+        /// Avoid calling this query directly.
         query hir_owner(key: LocalDefId) -> Option<&'tcx crate::hir::Owner<'tcx>> {
             eval_always
             desc { |tcx| "HIR owner of `{}`", tcx.def_path_str(key.to_def_id()) }
         }
 
-        // Gives access to the HIR nodes and bodies inside the HIR owner `key`.
-        //
-        // This can be conveniently accessed by methods on `tcx.hir()`.
-        // Avoid calling this query directly.
+        /// Gives access to the HIR nodes and bodies inside the HIR owner `key`.
+        ///
+        /// This can be conveniently accessed by methods on `tcx.hir()`.
+        /// Avoid calling this query directly.
         query hir_owner_nodes(key: LocalDefId) -> Option<&'tcx crate::hir::OwnerNodes<'tcx>> {
             eval_always
             desc { |tcx| "HIR owner items in `{}`", tcx.def_path_str(key.to_def_id()) }
@@ -244,6 +244,35 @@ rustc_queries! {
             no_hash
         }
 
+        /// Try to build an abstract representation of the given constant.
+        query mir_abstract_const(
+            key: DefId
+        ) -> Result<Option<&'tcx [mir::abstract_const::Node<'tcx>]>, ErrorReported> {
+            desc {
+                |tcx| "building an abstract representation for {}", tcx.def_path_str(key),
+            }
+        }
+        /// Try to build an abstract representation of the given constant.
+        query mir_abstract_const_of_const_arg(
+            key: (LocalDefId, DefId)
+        ) -> Result<Option<&'tcx [mir::abstract_const::Node<'tcx>]>, ErrorReported> {
+            desc {
+                |tcx|
+                "building an abstract representation for the const argument {}",
+                tcx.def_path_str(key.0.to_def_id()),
+            }
+        }
+
+        query try_unify_abstract_consts(key: (
+            (ty::WithOptConstParam<DefId>, SubstsRef<'tcx>),
+            (ty::WithOptConstParam<DefId>, SubstsRef<'tcx>)
+        )) -> bool {
+            desc {
+                |tcx| "trying to unify the generic constants {} and {}",
+                tcx.def_path_str(key.0.0.did), tcx.def_path_str(key.1.0.did)
+            }
+        }
+
         query mir_drops_elaborated_and_const_checked(
             key: ty::WithOptConstParam<LocalDefId>
         ) -> &'tcx Steal<mir::Body<'tcx>> {
@@ -305,9 +334,9 @@ rustc_queries! {
     }
 
     TypeChecking {
-        // Erases regions from `ty` to yield a new type.
-        // Normally you would just use `tcx.erase_regions(&value)`,
-        // however, which uses this query as a kind of cache.
+        /// Erases regions from `ty` to yield a new type.
+        /// Normally you would just use `tcx.erase_regions(&value)`,
+        /// however, which uses this query as a kind of cache.
         query erase_regions_ty(ty: Ty<'tcx>) -> Ty<'tcx> {
             // This query is not expected to have input -- as a result, it
             // is not a good candidates for "replay" because it is essentially a
@@ -678,38 +707,31 @@ rustc_queries! {
     }
 
     Other {
-        /// Evaluates a constant without running sanity checks.
+        /// Evaluates a constant and returns the computed allocation.
         ///
-        /// **Do not use this** outside const eval. Const eval uses this to break query cycles
-        /// during validation. Please add a comment to every use site explaining why using
-        /// `const_eval_validated` isn't sufficient. The returned constant also isn't in a suitable
-        /// form to be used outside of const eval.
-        query const_eval_raw(key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>)
-            -> ConstEvalRawResult<'tcx> {
+        /// **Do not use this** directly, use the `tcx.eval_static_initializer` wrapper.
+        query eval_to_allocation_raw(key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>)
+            -> EvalToAllocationRawResult<'tcx> {
             desc { |tcx|
-                "const-evaluating `{}`",
+                "const-evaluating + checking `{}`",
                 key.value.display(tcx)
             }
+            cache_on_disk_if { true }
         }
 
-        /// Results of evaluating const items or constants embedded in
-        /// other items (such as enum variant explicit discriminants).
-        ///
-        /// In contrast to `const_eval_raw` this performs some validation on the constant, and
-        /// returns a proper constant that is usable by the rest of the compiler.
+        /// Evaluates const items or anonymous constants
+        /// (such as enum variant explicit discriminants or array lengths)
+        /// into a representation suitable for the type system and const generics.
         ///
         /// **Do not use this** directly, use one of the following wrappers: `tcx.const_eval_poly`,
         /// `tcx.const_eval_resolve`, `tcx.const_eval_instance`, or `tcx.const_eval_global_id`.
-        query const_eval_validated(key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>)
-            -> ConstEvalResult<'tcx> {
+        query eval_to_const_value_raw(key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>)
+            -> EvalToConstValueResult<'tcx> {
             desc { |tcx|
-                "const-evaluating + checking `{}`",
+                "simplifying constant for the type system `{}`",
                 key.value.display(tcx)
             }
-            cache_on_disk_if(_, opt_result) {
-                // Only store results without errors
-                opt_result.map_or(true, |r| r.is_ok())
-            }
+            cache_on_disk_if { true }
         }
 
         /// Destructure a constant ADT or array into its variant index and its
@@ -1255,6 +1277,11 @@ rustc_queries! {
             storage(ArenaCacheSelector<'tcx>)
             desc { "calculating the visible parent map" }
         }
+        query trimmed_def_paths(_: CrateNum)
+            -> FxHashMap<DefId, Symbol> {
+            storage(ArenaCacheSelector<'tcx>)
+            desc { "calculating trimmed def paths" }
+        }
         query missing_extern_crate_item(_: CrateNum) -> bool {
             eval_always
             desc { "seeing if we're missing an `extern crate` item for this crate" }
@@ -1394,7 +1421,7 @@ rustc_queries! {
         }
 
         query evaluate_goal(
-            goal: traits::ChalkCanonicalGoal<'tcx>
+            goal: traits::CanonicalChalkEnvironmentAndGoal<'tcx>
         ) -> Result<
             &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>,
             NoSolution
@@ -1509,7 +1536,7 @@ rustc_queries! {
             desc { "looking up supported target features" }
         }
 
-        // Get an estimate of the size of an InstanceDef based on its MIR for CGU partitioning.
+        /// Get an estimate of the size of an InstanceDef based on its MIR for CGU partitioning.
         query instance_def_size_estimate(def: ty::InstanceDef<'tcx>)
             -> usize {
             desc { |tcx| "estimating size for `{}`", tcx.def_path_str(def.def_id()) }
diff --git a/compiler/rustc_middle/src/traits/chalk.rs b/compiler/rustc_middle/src/traits/chalk.rs
index 405af8cb240..d8507d08c1b 100644
--- a/compiler/rustc_middle/src/traits/chalk.rs
+++ b/compiler/rustc_middle/src/traits/chalk.rs
@@ -6,14 +6,11 @@
 //! interned Chalk types.
 
 use rustc_middle::mir::interpret::ConstValue;
-use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
-use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
+use rustc_middle::ty::{self, AdtDef, TyCtxt};
 
 use rustc_hir::def_id::DefId;
 use rustc_target::spec::abi::Abi;
 
-use smallvec::SmallVec;
-
 use std::cmp::Ordering;
 use std::fmt;
 use std::hash::{Hash, Hasher};
@@ -75,6 +72,7 @@ impl<'tcx> chalk_ir::interner::Interner for RustInterner<'tcx> {
     type InternedQuantifiedWhereClauses = Vec<chalk_ir::QuantifiedWhereClause<Self>>;
     type InternedVariableKinds = Vec<chalk_ir::VariableKind<Self>>;
     type InternedCanonicalVarKinds = Vec<chalk_ir::CanonicalVarKind<Self>>;
+    type InternedConstraints = Vec<chalk_ir::InEnvironment<chalk_ir::Constraint<Self>>>;
     type DefId = DefId;
     type InternedAdtId = &'tcx AdtDef;
     type Identifier = ();
@@ -108,8 +106,42 @@ impl<'tcx> chalk_ir::interner::Interner for RustInterner<'tcx> {
         application_ty: &chalk_ir::ApplicationTy<Self>,
         fmt: &mut fmt::Formatter<'_>,
     ) -> Option<fmt::Result> {
-        let chalk_ir::ApplicationTy { name, substitution } = application_ty;
-        Some(write!(fmt, "{:?}{:?}", name, chalk_ir::debug::Angle(substitution.interned())))
+        match application_ty.name {
+            chalk_ir::TypeName::Ref(mutbl) => {
+                let data = application_ty.substitution.interned();
+                match (&**data[0].interned(), &**data[1].interned()) {
+                    (
+                        chalk_ir::GenericArgData::Lifetime(lifetime),
+                        chalk_ir::GenericArgData::Ty(ty),
+                    ) => Some(match mutbl {
+                        chalk_ir::Mutability::Not => write!(fmt, "(&{:?} {:?})", lifetime, ty),
+                        chalk_ir::Mutability::Mut => write!(fmt, "(&{:?} mut {:?})", lifetime, ty),
+                    }),
+                    _ => unreachable!(),
+                }
+            }
+            chalk_ir::TypeName::Array => {
+                let data = application_ty.substitution.interned();
+                match (&**data[0].interned(), &**data[1].interned()) {
+                    (chalk_ir::GenericArgData::Ty(ty), chalk_ir::GenericArgData::Const(len)) => {
+                        Some(write!(fmt, "[{:?}; {:?}]", ty, len))
+                    }
+                    _ => unreachable!(),
+                }
+            }
+            chalk_ir::TypeName::Slice => {
+                let data = application_ty.substitution.interned();
+                let ty = match &**data[0].interned() {
+                    chalk_ir::GenericArgData::Ty(t) => t,
+                    _ => unreachable!(),
+                };
+                Some(write!(fmt, "[{:?}]", ty))
+            }
+            _ => {
+                let chalk_ir::ApplicationTy { name, substitution } = application_ty;
+                Some(write!(fmt, "{:?}{:?}", name, chalk_ir::debug::Angle(substitution.interned())))
+            }
+        }
     }
 
     fn debug_substitution(
@@ -321,37 +353,30 @@ impl<'tcx> chalk_ir::interner::Interner for RustInterner<'tcx> {
     ) -> &'a [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<'a>(
+        &self,
+        constraints: &'a Self::InternedConstraints,
+    ) -> &'a [chalk_ir::InEnvironment<chalk_ir::Constraint<Self>>] {
+        constraints
+    }
 }
 
 impl<'tcx> chalk_ir::interner::HasInterner for RustInterner<'tcx> {
     type Interner = Self;
 }
 
-#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable, TypeFoldable)]
-pub enum ChalkEnvironmentClause<'tcx> {
-    /// A normal rust `ty::Predicate` in the environment.
-    Predicate(ty::Predicate<'tcx>),
-    /// A special clause in the environment that gets lowered to
-    /// `chalk_ir::FromEnv::Ty`.
-    TypeFromEnv(Ty<'tcx>),
-}
-
-impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List<ChalkEnvironmentClause<'tcx>> {
-    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
-        let v = self.iter().map(|t| t.fold_with(folder)).collect::<SmallVec<[_; 8]>>();
-        folder.tcx().intern_chalk_environment_clause_list(&v)
-    }
-
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        self.iter().any(|t| t.visit_with(visitor))
-    }
-}
-/// We have to elaborate the environment of a chalk goal *before*
-/// canonicalization. This type wraps the predicate and the elaborated
-/// environment.
+/// A chalk environment and goal.
 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable, TypeFoldable)]
 pub struct ChalkEnvironmentAndGoal<'tcx> {
-    pub environment: &'tcx ty::List<ChalkEnvironmentClause<'tcx>>,
+    pub environment: &'tcx ty::List<ty::Predicate<'tcx>>,
     pub goal: ty::Predicate<'tcx>,
 }
 
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index f86403fa502..a554c80cdaa 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -26,14 +26,12 @@ use std::rc::Rc;
 
 pub use self::select::{EvaluationCache, EvaluationResult, OverflowError, SelectionCache};
 
-pub type ChalkCanonicalGoal<'tcx> = Canonical<'tcx, ChalkEnvironmentAndGoal<'tcx>>;
+pub type CanonicalChalkEnvironmentAndGoal<'tcx> = Canonical<'tcx, ChalkEnvironmentAndGoal<'tcx>>;
 
 pub use self::ImplSource::*;
 pub use self::ObligationCauseCode::*;
 
-pub use self::chalk::{
-    ChalkEnvironmentAndGoal, ChalkEnvironmentClause, RustInterner as ChalkRustInterner,
-};
+pub use self::chalk::{ChalkEnvironmentAndGoal, RustInterner as ChalkRustInterner};
 
 /// Depending on the stage of compilation, we want projection to be
 /// more or less conservative.
@@ -350,13 +348,16 @@ pub struct MatchExpressionArmCause<'tcx> {
     pub prior_arms: Vec<Span>,
     pub last_ty: Ty<'tcx>,
     pub scrut_hir_id: hir::HirId,
+    pub opt_suggest_box_span: Option<Span>,
 }
 
 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
 pub struct IfExpressionCause {
     pub then: Span,
+    pub else_sp: Span,
     pub outer: Option<Span>,
     pub semicolon: Option<Span>,
+    pub opt_suggest_box_span: Option<Span>,
 }
 
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Lift)]
diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs
index 4b7663e9ade..f9cadb3bb2d 100644
--- a/compiler/rustc_middle/src/traits/query.rs
+++ b/compiler/rustc_middle/src/traits/query.rs
@@ -190,74 +190,6 @@ impl<'tcx> FromIterator<DtorckConstraint<'tcx>> for DtorckConstraint<'tcx> {
     }
 }
 
-/// This returns true if the type `ty` is "trivial" for
-/// dropck-outlives -- that is, if it doesn't require any types to
-/// outlive. This is similar but not *quite* the same as the
-/// `needs_drop` test in the compiler already -- that is, for every
-/// type T for which this function return true, needs-drop would
-/// return `false`. But the reverse does not hold: in particular,
-/// `needs_drop` returns false for `PhantomData`, but it is not
-/// trivial for dropck-outlives.
-///
-/// Note also that `needs_drop` requires a "global" type (i.e., one
-/// with erased regions), but this function does not.
-pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
-    match ty.kind {
-        // None of these types have a destructor and hence they do not
-        // require anything in particular to outlive the dtor's
-        // execution.
-        ty::Infer(ty::FreshIntTy(_))
-        | ty::Infer(ty::FreshFloatTy(_))
-        | ty::Bool
-        | ty::Int(_)
-        | ty::Uint(_)
-        | ty::Float(_)
-        | ty::Never
-        | ty::FnDef(..)
-        | ty::FnPtr(_)
-        | ty::Char
-        | ty::GeneratorWitness(..)
-        | ty::RawPtr(_)
-        | ty::Ref(..)
-        | ty::Str
-        | ty::Foreign(..)
-        | ty::Error(_) => true,
-
-        // [T; N] and [T] have same properties as T.
-        ty::Array(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, ty),
-
-        // (T1..Tn) and closures have same properties as T1..Tn --
-        // check if *any* of those are trivial.
-        ty::Tuple(ref tys) => tys.iter().all(|t| trivial_dropck_outlives(tcx, t.expect_ty())),
-        ty::Closure(_, ref substs) => {
-            substs.as_closure().upvar_tys().all(|t| trivial_dropck_outlives(tcx, t))
-        }
-
-        ty::Adt(def, _) => {
-            if Some(def.did) == tcx.lang_items().manually_drop() {
-                // `ManuallyDrop` never has a dtor.
-                true
-            } else {
-                // Other types might. Moreover, PhantomData doesn't
-                // have a dtor, but it is considered to own its
-                // content, so it is non-trivial. Unions can have `impl Drop`,
-                // and hence are non-trivial as well.
-                false
-            }
-        }
-
-        // The following *might* require a destructor: needs deeper inspection.
-        ty::Dynamic(..)
-        | ty::Projection(..)
-        | ty::Param(_)
-        | ty::Opaque(..)
-        | ty::Placeholder(..)
-        | ty::Infer(_)
-        | ty::Bound(..)
-        | ty::Generator(..) => false,
-    }
-}
-
 #[derive(Debug, HashStable)]
 pub struct CandidateStep<'tcx> {
     pub self_ty: Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>,
diff --git a/compiler/rustc_middle/src/ty/_match.rs b/compiler/rustc_middle/src/ty/_match.rs
index 4693a2f66fb..27bccc0bcaf 100644
--- a/compiler/rustc_middle/src/ty/_match.rs
+++ b/compiler/rustc_middle/src/ty/_match.rs
@@ -67,7 +67,7 @@ impl TypeRelation<'tcx> for Match<'tcx> {
             return Ok(a);
         }
 
-        match (&a.kind, &b.kind) {
+        match (a.kind(), b.kind()) {
             (
                 _,
                 &ty::Infer(ty::FreshTy(_))
diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs
index 6a9bb8d6c28..46ef5ff7dd8 100644
--- a/compiler/rustc_middle/src/ty/adjustment.rs
+++ b/compiler/rustc_middle/src/ty/adjustment.rs
@@ -4,6 +4,7 @@ use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_hir::lang_items::LangItem;
 use rustc_macros::HashStable;
+use rustc_span::Span;
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)]
 pub enum PointerCast {
@@ -113,6 +114,9 @@ pub enum Adjust<'tcx> {
 pub struct OverloadedDeref<'tcx> {
     pub region: ty::Region<'tcx>,
     pub mutbl: hir::Mutability,
+    /// The `Span` associated with the field access or method call
+    /// that triggered this overloaded deref.
+    pub span: Span,
 }
 
 impl<'tcx> OverloadedDeref<'tcx> {
diff --git a/compiler/rustc_middle/src/ty/cast.rs b/compiler/rustc_middle/src/ty/cast.rs
index 79a3008c364..b47d9c50e1d 100644
--- a/compiler/rustc_middle/src/ty/cast.rs
+++ b/compiler/rustc_middle/src/ty/cast.rs
@@ -50,7 +50,7 @@ impl<'tcx> CastTy<'tcx> {
     /// Returns `Some` for integral/pointer casts.
     /// casts like unsizing casts will return `None`
     pub fn from_ty(t: Ty<'tcx>) -> Option<CastTy<'tcx>> {
-        match t.kind {
+        match *t.kind() {
             ty::Bool => Some(CastTy::Int(IntTy::Bool)),
             ty::Char => Some(CastTy::Int(IntTy::Char)),
             ty::Int(_) => Some(CastTy::Int(IntTy::I)),
diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs
index 291648869fb..8ea34f9161a 100644
--- a/compiler/rustc_middle/src/ty/codec.rs
+++ b/compiler/rustc_middle/src/ty/codec.rs
@@ -36,8 +36,10 @@ pub trait EncodableWithShorthand<'tcx, E: TyEncoder<'tcx>>: Copy + Eq + Hash {
 #[allow(rustc::usage_of_ty_tykind)]
 impl<'tcx, E: TyEncoder<'tcx>> EncodableWithShorthand<'tcx, E> for Ty<'tcx> {
     type Variant = ty::TyKind<'tcx>;
+
+    #[inline]
     fn variant(&self) -> &Self::Variant {
-        &self.kind
+        self.kind()
     }
 }
 
@@ -355,6 +357,26 @@ impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [(ty::Predicate<'tcx>,
     }
 }
 
+impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [mir::abstract_const::Node<'tcx>] {
+    fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> {
+        Ok(decoder.tcx().arena.alloc_from_iter(
+            (0..decoder.read_usize()?)
+                .map(|_| Decodable::decode(decoder))
+                .collect::<Result<Vec<_>, _>>()?,
+        ))
+    }
+}
+
+impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [mir::abstract_const::NodeId] {
+    fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> {
+        Ok(decoder.tcx().arena.alloc_from_iter(
+            (0..decoder.read_usize()?)
+                .map(|_| Decodable::decode(decoder))
+                .collect::<Result<Vec<_>, _>>()?,
+        ))
+    }
+}
+
 impl_decodable_via_ref! {
     &'tcx ty::TypeckResults<'tcx>,
     &'tcx ty::List<Ty<'tcx>>,
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 18ae744cb1e..ccc8ffd9a9c 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -34,6 +34,7 @@ use rustc_data_structures::stable_hasher::{
     hash_stable_hashmap, HashStable, StableHasher, StableVec,
 };
 use rustc_data_structures::sync::{self, Lock, Lrc, WorkerLocal};
+use rustc_data_structures::unhash::UnhashMap;
 use rustc_errors::ErrorReported;
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
@@ -65,8 +66,8 @@ use std::mem;
 use std::ops::{Bound, Deref};
 use std::sync::Arc;
 
-/// A type that is not publicly constructable. This prevents people from making `TyKind::Error`
-/// except through `tcx.err*()`, which are in this module.
+/// A type that is not publicly constructable. This prevents people from making [`TyKind::Error`]s
+/// except through the error-reporting functions on a [`tcx`][TyCtxt].
 #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
 #[derive(TyEncodable, TyDecodable, HashStable)]
 pub struct DelaySpanBugEmitted(());
@@ -90,8 +91,6 @@ pub struct CtxtInterners<'tcx> {
     projs: InternedSet<'tcx, List<ProjectionKind>>,
     place_elems: InternedSet<'tcx, List<PlaceElem<'tcx>>>,
     const_: InternedSet<'tcx, Const<'tcx>>,
-
-    chalk_environment_clause_list: InternedSet<'tcx, List<traits::ChalkEnvironmentClause<'tcx>>>,
 }
 
 impl<'tcx> CtxtInterners<'tcx> {
@@ -109,7 +108,6 @@ impl<'tcx> CtxtInterners<'tcx> {
             projs: Default::default(),
             place_elems: Default::default(),
             const_: Default::default(),
-            chalk_environment_clause_list: Default::default(),
         }
     }
 
@@ -758,10 +756,10 @@ impl CanonicalUserType<'tcx> {
 
                 user_substs.substs.iter().zip(BoundVar::new(0)..).all(|(kind, cvar)| {
                     match kind.unpack() {
-                        GenericArgKind::Type(ty) => match ty.kind {
+                        GenericArgKind::Type(ty) => match ty.kind() {
                             ty::Bound(debruijn, b) => {
                                 // We only allow a `ty::INNERMOST` index in substitutions.
-                                assert_eq!(debruijn, ty::INNERMOST);
+                                assert_eq!(*debruijn, ty::INNERMOST);
                                 cvar == b.var
                             }
                             _ => false,
@@ -935,7 +933,7 @@ pub struct GlobalCtxt<'tcx> {
 
     /// A map from `DefPathHash` -> `DefId`. Includes `DefId`s from the local crate
     /// as well as all upstream crates. Only populated in incremental mode.
-    pub def_path_hash_to_def_id: Option<FxHashMap<DefPathHash, DefId>>,
+    pub def_path_hash_to_def_id: Option<UnhashMap<DefPathHash, DefId>>,
 
     pub queries: query::Queries<'tcx>,
 
@@ -943,7 +941,7 @@ pub struct GlobalCtxt<'tcx> {
     maybe_unused_extern_crates: Vec<(LocalDefId, Span)>,
     /// A map of glob use to a set of names it actually imports. Currently only
     /// used in save-analysis.
-    glob_map: FxHashMap<LocalDefId, FxHashSet<Symbol>>,
+    pub(crate) glob_map: FxHashMap<LocalDefId, FxHashSet<Symbol>>,
     /// Extern prelude entries. The value is `true` if the entry was introduced
     /// via `extern crate` item and not `--extern` option or compiler built-in.
     pub extern_prelude: FxHashMap<Symbol, bool>,
@@ -1104,7 +1102,7 @@ impl<'tcx> TyCtxt<'tcx> {
         let def_path_hash_to_def_id = if s.opts.build_dep_graph() {
             let capacity = definitions.def_path_table().num_def_ids()
                 + crates.iter().map(|cnum| cstore.num_def_ids(*cnum)).sum::<usize>();
-            let mut map = FxHashMap::with_capacity_and_hasher(capacity, Default::default());
+            let mut map = UnhashMap::with_capacity_and_hasher(capacity, Default::default());
 
             map.extend(definitions.def_path_table().all_def_path_hashes_and_def_ids(LOCAL_CRATE));
             for cnum in &crates {
@@ -1181,7 +1179,7 @@ impl<'tcx> TyCtxt<'tcx> {
         self.mk_const(ty::Const { val: ty::ConstKind::Error(DelaySpanBugEmitted(())), ty })
     }
 
-    pub fn consider_optimizing<T: Fn() -> String>(&self, msg: T) -> bool {
+    pub fn consider_optimizing<T: Fn() -> String>(self, msg: T) -> bool {
         let cname = self.crate_name(LOCAL_CRATE).as_str();
         self.sess.consider_optimizing(&cname, msg)
     }
@@ -1405,7 +1403,7 @@ impl<'tcx> TyCtxt<'tcx> {
     }
 
     // Returns the `DefId` and the `BoundRegion` corresponding to the given region.
-    pub fn is_suitable_region(&self, region: Region<'tcx>) -> Option<FreeRegionInfo> {
+    pub fn is_suitable_region(self, region: Region<'tcx>) -> Option<FreeRegionInfo> {
         let (suitable_region_binding_scope, bound_region) = match *region {
             ty::ReFree(ref free_region) => {
                 (free_region.scope.expect_local(), free_region.bound_region)
@@ -1435,7 +1433,7 @@ impl<'tcx> TyCtxt<'tcx> {
 
     /// Given a `DefId` for an `fn`, return all the `dyn` and `impl` traits in its return type.
     pub fn return_type_impl_or_dyn_traits(
-        &self,
+        self,
         scope_def_id: LocalDefId,
     ) -> Vec<&'tcx hir::Ty<'tcx>> {
         let hir_id = self.hir().local_def_id_to_hir_id(scope_def_id);
@@ -1481,7 +1479,7 @@ impl<'tcx> TyCtxt<'tcx> {
         v.0
     }
 
-    pub fn return_type_impl_trait(&self, scope_def_id: LocalDefId) -> Option<(Ty<'tcx>, Span)> {
+    pub fn return_type_impl_trait(self, scope_def_id: LocalDefId) -> Option<(Ty<'tcx>, Span)> {
         // HACK: `type_of_def_id()` will fail on these (#55796), so return `None`.
         let hir_id = self.hir().local_def_id_to_hir_id(scope_def_id);
         match self.hir().get(hir_id) {
@@ -1497,9 +1495,9 @@ impl<'tcx> TyCtxt<'tcx> {
         }
 
         let ret_ty = self.type_of(scope_def_id);
-        match ret_ty.kind {
+        match ret_ty.kind() {
             ty::FnDef(_, _) => {
-                let sig = ret_ty.fn_sig(*self);
+                let sig = ret_ty.fn_sig(self);
                 let output = self.erase_late_bound_regions(&sig.output());
                 if output.is_impl_trait() {
                     let fn_decl = self.hir().fn_decl_by_hir_id(hir_id).unwrap();
@@ -1513,7 +1511,7 @@ impl<'tcx> TyCtxt<'tcx> {
     }
 
     // Checks if the bound region is in Impl Item.
-    pub fn is_bound_region_in_impl_item(&self, suitable_region_binding_scope: LocalDefId) -> bool {
+    pub fn is_bound_region_in_impl_item(self, suitable_region_binding_scope: LocalDefId) -> bool {
         let container_id =
             self.associated_item(suitable_region_binding_scope.to_def_id()).container.id();
         if self.impl_trait_ref(container_id).is_some() {
@@ -1530,21 +1528,21 @@ impl<'tcx> TyCtxt<'tcx> {
 
     /// Determines whether identifiers in the assembly have strict naming rules.
     /// Currently, only NVPTX* targets need it.
-    pub fn has_strict_asm_symbol_naming(&self) -> bool {
+    pub fn has_strict_asm_symbol_naming(self) -> bool {
         self.sess.target.target.arch.contains("nvptx")
     }
 
     /// Returns `&'static core::panic::Location<'static>`.
-    pub fn caller_location_ty(&self) -> Ty<'tcx> {
+    pub fn caller_location_ty(self) -> Ty<'tcx> {
         self.mk_imm_ref(
             self.lifetimes.re_static,
             self.type_of(self.require_lang_item(LangItem::PanicLocation, None))
-                .subst(*self, self.mk_substs([self.lifetimes.re_static.into()].iter())),
+                .subst(self, self.mk_substs([self.lifetimes.re_static.into()].iter())),
         )
     }
 
     /// Returns a displayable description and article for the given `def_id` (e.g. `("a", "struct")`).
-    pub fn article_and_description(&self, def_id: DefId) -> (&'static str, &'static str) {
+    pub fn article_and_description(self, def_id: DefId) -> (&'static str, &'static str) {
         match self.def_kind(def_id) {
             DefKind::Generator => match self.generator_kind(def_id).unwrap() {
                 rustc_hir::GeneratorKind::Async(..) => ("an", "async closure"),
@@ -1821,15 +1819,15 @@ macro_rules! sty_debug_print {
                 let shards = tcx.interners.type_.lock_shards();
                 let types = shards.iter().flat_map(|shard| shard.keys());
                 for &Interned(t) in types {
-                    let variant = match t.kind {
+                    let variant = match t.kind() {
                         ty::Bool | ty::Char | ty::Int(..) | ty::Uint(..) |
                             ty::Float(..) | ty::Str | ty::Never => continue,
                         ty::Error(_) => /* unimportant */ continue,
                         $(ty::$variant(..) => &mut $variant,)*
                     };
-                    let lt = t.flags.intersects(ty::TypeFlags::HAS_RE_INFER);
-                    let ty = t.flags.intersects(ty::TypeFlags::HAS_TY_INFER);
-                    let ct = t.flags.intersects(ty::TypeFlags::HAS_CT_INFER);
+                    let lt = t.flags().intersects(ty::TypeFlags::HAS_RE_INFER);
+                    let ty = t.flags().intersects(ty::TypeFlags::HAS_TY_INFER);
+                    let ct = t.flags().intersects(ty::TypeFlags::HAS_CT_INFER);
 
                     variant.total += 1;
                     total.total += 1;
@@ -1930,7 +1928,7 @@ impl<'tcx, T: 'tcx + ?Sized> IntoPointer for Interned<'tcx, T> {
 // N.B., an `Interned<Ty>` compares and hashes as a `TyKind`.
 impl<'tcx> PartialEq for Interned<'tcx, TyS<'tcx>> {
     fn eq(&self, other: &Interned<'tcx, TyS<'tcx>>) -> bool {
-        self.0.kind == other.0.kind
+        self.0.kind() == other.0.kind()
     }
 }
 
@@ -1938,14 +1936,14 @@ impl<'tcx> Eq for Interned<'tcx, TyS<'tcx>> {}
 
 impl<'tcx> Hash for Interned<'tcx, TyS<'tcx>> {
     fn hash<H: Hasher>(&self, s: &mut H) {
-        self.0.kind.hash(s)
+        self.0.kind().hash(s)
     }
 }
 
 #[allow(rustc::usage_of_ty_tykind)]
 impl<'tcx> Borrow<TyKind<'tcx>> for Interned<'tcx, TyS<'tcx>> {
     fn borrow<'a>(&'a self) -> &'a TyKind<'tcx> {
-        &self.0.kind
+        &self.0.kind()
     }
 }
 // N.B., an `Interned<PredicateInner>` compares and hashes as a `PredicateKind`.
@@ -2040,7 +2038,7 @@ direct_interners! {
 }
 
 macro_rules! slice_interners {
-    ($($field:ident: $method:ident($ty:ty)),+) => (
+    ($($field:ident: $method:ident($ty:ty)),+ $(,)?) => (
         $(impl<'tcx> TyCtxt<'tcx> {
             pub fn $method(self, v: &[$ty]) -> &'tcx List<$ty> {
                 self.interners.$field.intern_ref(v, || {
@@ -2059,8 +2057,6 @@ slice_interners!(
     predicates: _intern_predicates(Predicate<'tcx>),
     projs: _intern_projs(ProjectionKind),
     place_elems: _intern_place_elems(PlaceElem<'tcx>),
-    chalk_environment_clause_list:
-        _intern_chalk_environment_clause_list(traits::ChalkEnvironmentClause<'tcx>)
 );
 
 impl<'tcx> TyCtxt<'tcx> {
@@ -2085,7 +2081,7 @@ impl<'tcx> TyCtxt<'tcx> {
         unsafety: hir::Unsafety,
     ) -> PolyFnSig<'tcx> {
         sig.map_bound(|s| {
-            let params_iter = match s.inputs()[0].kind {
+            let params_iter = match s.inputs()[0].kind() {
                 ty::Tuple(params) => params.into_iter().map(|k| k.expect_ty()),
                 _ => bug!(),
             };
@@ -2423,7 +2419,7 @@ impl<'tcx> TyCtxt<'tcx> {
         eps: &[ExistentialPredicate<'tcx>],
     ) -> &'tcx List<ExistentialPredicate<'tcx>> {
         assert!(!eps.is_empty());
-        assert!(eps.windows(2).all(|w| w[0].stable_cmp(self, &w[1]) != Ordering::Greater));
+        assert!(eps.array_windows().all(|[a, b]| a.stable_cmp(self, b) != Ordering::Greater));
         self._intern_existential_predicates(eps)
     }
 
@@ -2459,13 +2455,6 @@ impl<'tcx> TyCtxt<'tcx> {
         if ts.is_empty() { List::empty() } else { self._intern_canonical_var_infos(ts) }
     }
 
-    pub fn intern_chalk_environment_clause_list(
-        self,
-        ts: &[traits::ChalkEnvironmentClause<'tcx>],
-    ) -> &'tcx List<traits::ChalkEnvironmentClause<'tcx>> {
-        if ts.is_empty() { List::empty() } else { self._intern_chalk_environment_clause_list(ts) }
-    }
-
     pub fn mk_fn_sig<I>(
         self,
         inputs: I,
@@ -2523,18 +2512,6 @@ impl<'tcx> TyCtxt<'tcx> {
         self.mk_substs(iter::once(self_ty.into()).chain(rest.iter().cloned()))
     }
 
-    pub fn mk_chalk_environment_clause_list<
-        I: InternAs<
-            [traits::ChalkEnvironmentClause<'tcx>],
-            &'tcx List<traits::ChalkEnvironmentClause<'tcx>>,
-        >,
-    >(
-        self,
-        iter: I,
-    ) -> I::Output {
-        iter.intern_with(|xs| self.intern_chalk_environment_clause_list(xs))
-    }
-
     /// Walks upwards from `id` to find a node which might change lint levels with attributes.
     /// It stops at `bound` and just returns it if reached.
     pub fn maybe_lint_level_root_bounded(self, mut id: HirId, bound: HirId) -> HirId {
diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs
index b22727bdd75..715319747e3 100644
--- a/compiler/rustc_middle/src/ty/diagnostics.rs
+++ b/compiler/rustc_middle/src/ty/diagnostics.rs
@@ -11,7 +11,7 @@ use rustc_hir::{QPath, TyKind, WhereBoundPredicate, WherePredicate};
 impl<'tcx> TyS<'tcx> {
     /// Similar to `TyS::is_primitive`, but also considers inferred numeric values to be primitive.
     pub fn is_primitive_ty(&self) -> bool {
-        match self.kind {
+        match self.kind() {
             Bool
             | Char
             | Str
@@ -31,7 +31,7 @@ impl<'tcx> TyS<'tcx> {
     /// Whether the type is succinctly representable as a type instead of just referred to with a
     /// description in error messages. This is used in the main error message.
     pub fn is_simple_ty(&self) -> bool {
-        match self.kind {
+        match self.kind() {
             Bool
             | Char
             | Str
@@ -55,7 +55,7 @@ impl<'tcx> TyS<'tcx> {
     /// `is_simple_ty` includes, it also accepts ADTs with no type arguments and references to
     /// ADTs with no type arguments.
     pub fn is_simple_text(&self) -> bool {
-        match self.kind {
+        match self.kind() {
             Adt(_, substs) => substs.types().next().is_none(),
             Ref(_, ty, _) => ty.is_simple_text(),
             _ => self.is_simple_ty(),
@@ -64,7 +64,7 @@ impl<'tcx> TyS<'tcx> {
 
     /// Whether the type can be safely suggested during error recovery.
     pub fn is_suggestable(&self) -> bool {
-        match self.kind {
+        match self.kind() {
             Opaque(..) | FnDef(..) | FnPtr(..) | Dynamic(..) | Closure(..) | Infer(..)
             | Projection(..) => false,
             _ => true,
@@ -202,33 +202,59 @@ pub fn suggest_constraining_type_param(
         //    Suggestion:
         //      fn foo<T>(t: T) where T: Foo, T: Bar {... }
         //                                          - insert: `, T: Zar`
+        //
+        // Additionally, there may be no `where` clause whatsoever in the case that this was
+        // reached because the generic parameter has a default:
+        //
+        //    Message:
+        //      trait Foo<T=()> {... }
+        //             - help: consider further restricting this type parameter with `where T: Zar`
+        //
+        //    Suggestion:
+        //      trait Foo<T=()> where T: Zar {... }
+        //                     - insert: `where T: Zar`
 
-        let mut param_spans = Vec::new();
+        if matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
+            && generics.where_clause.predicates.len() == 0
+        {
+            // Suggest a bound, but there is no existing `where` clause *and* the type param has a
+            // default (`<T=Foo>`), so we suggest adding `where T: Bar`.
+            err.span_suggestion_verbose(
+                generics.where_clause.tail_span_for_suggestion(),
+                &msg_restrict_type_further,
+                format!(" where {}: {}", param_name, constraint),
+                Applicability::MachineApplicable,
+            );
+        } else {
+            let mut param_spans = Vec::new();
 
-        for predicate in generics.where_clause.predicates {
-            if let WherePredicate::BoundPredicate(WhereBoundPredicate {
-                span, bounded_ty, ..
-            }) = predicate
-            {
-                if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind {
-                    if let Some(segment) = path.segments.first() {
-                        if segment.ident.to_string() == param_name {
-                            param_spans.push(span);
+            for predicate in generics.where_clause.predicates {
+                if let WherePredicate::BoundPredicate(WhereBoundPredicate {
+                    span,
+                    bounded_ty,
+                    ..
+                }) = predicate
+                {
+                    if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind {
+                        if let Some(segment) = path.segments.first() {
+                            if segment.ident.to_string() == param_name {
+                                param_spans.push(span);
+                            }
                         }
                     }
                 }
             }
-        }
 
-        match &param_spans[..] {
-            &[&param_span] => suggest_restrict(param_span.shrink_to_hi()),
-            _ => {
-                err.span_suggestion_verbose(
-                    generics.where_clause.tail_span_for_suggestion(),
-                    &msg_restrict_type_further,
-                    format!(", {}: {}", param_name, constraint),
-                    Applicability::MachineApplicable,
-                );
+            match &param_spans[..] {
+                &[&param_span] => suggest_restrict(param_span.shrink_to_hi()),
+                _ => {
+                    err.span_suggestion_verbose(
+                        generics.where_clause.tail_span_for_suggestion(),
+                        &msg_restrict_type_further,
+                        format!(", {}: {}", param_name, constraint),
+                        Applicability::MachineApplicable,
+                    );
+                }
             }
         }
 
diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs
index 1963881626e..342a37cadba 100644
--- a/compiler/rustc_middle/src/ty/error.rs
+++ b/compiler/rustc_middle/src/ty/error.rs
@@ -56,6 +56,7 @@ pub enum TypeError<'tcx> {
     /// created a cycle (because it appears somewhere within that
     /// type).
     CyclicTy(Ty<'tcx>),
+    CyclicConst(&'tcx ty::Const<'tcx>),
     ProjectionMismatched(ExpectedFound<DefId>),
     ExistentialMismatch(ExpectedFound<&'tcx ty::List<ty::ExistentialPredicate<'tcx>>>),
     ObjectUnsafeCoercion(DefId),
@@ -100,6 +101,7 @@ impl<'tcx> fmt::Display for TypeError<'tcx> {
 
         match *self {
             CyclicTy(_) => write!(f, "cyclic type of infinite size"),
+            CyclicConst(_) => write!(f, "encountered a self-referencing constant"),
             Mismatch => write!(f, "types differ"),
             UnsafetyMismatch(values) => {
                 write!(f, "expected {} fn, found {} fn", values.expected, values.found)
@@ -195,9 +197,9 @@ impl<'tcx> TypeError<'tcx> {
     pub fn must_include_note(&self) -> bool {
         use self::TypeError::*;
         match self {
-            CyclicTy(_) | UnsafetyMismatch(_) | Mismatch | AbiMismatch(_) | FixedArraySize(_)
-            | Sorts(_) | IntMismatch(_) | FloatMismatch(_) | VariadicMismatch(_)
-            | TargetFeatureCast(_) => false,
+            CyclicTy(_) | CyclicConst(_) | UnsafetyMismatch(_) | Mismatch | AbiMismatch(_)
+            | FixedArraySize(_) | Sorts(_) | IntMismatch(_) | FloatMismatch(_)
+            | VariadicMismatch(_) | TargetFeatureCast(_) => false,
 
             Mutability
             | TupleSize(_)
@@ -218,7 +220,7 @@ impl<'tcx> TypeError<'tcx> {
 
 impl<'tcx> ty::TyS<'tcx> {
     pub fn sort_string(&self, tcx: TyCtxt<'_>) -> Cow<'static, str> {
-        match self.kind {
+        match *self.kind() {
             ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str | ty::Never => {
                 format!("`{}`", self).into()
             }
@@ -282,7 +284,7 @@ impl<'tcx> ty::TyS<'tcx> {
     }
 
     pub fn prefix_string(&self) -> Cow<'static, str> {
-        match self.kind {
+        match *self.kind() {
             ty::Infer(_)
             | ty::Error(_)
             | ty::Bool
@@ -351,7 +353,7 @@ impl<'tcx> TyCtxt<'tcx> {
                         );
                     }
                 }
-                match (&values.expected.kind, &values.found.kind) {
+                match (values.expected.kind(), values.found.kind()) {
                     (ty::Float(_), ty::Infer(ty::IntVar(_))) => {
                         if let Ok(
                             // Issue #53280
@@ -512,7 +514,10 @@ impl<T> Trait<T> for X {
                 }
                 debug!(
                     "note_and_explain_type_err expected={:?} ({:?}) found={:?} ({:?})",
-                    values.expected, values.expected.kind, values.found, values.found.kind,
+                    values.expected,
+                    values.expected.kind(),
+                    values.found,
+                    values.found.kind(),
                 );
             }
             CyclicTy(ty) => {
@@ -543,7 +548,7 @@ impl<T> Trait<T> for X {
     }
 
     fn suggest_constraint(
-        &self,
+        self,
         db: &mut DiagnosticBuilder<'_>,
         msg: &str,
         body_owner_def_id: DefId,
@@ -551,14 +556,14 @@ impl<T> Trait<T> for X {
         ty: Ty<'tcx>,
     ) -> bool {
         let assoc = self.associated_item(proj_ty.item_def_id);
-        let trait_ref = proj_ty.trait_ref(*self);
+        let trait_ref = proj_ty.trait_ref(self);
         if let Some(item) = self.hir().get_if_local(body_owner_def_id) {
             if let Some(hir_generics) = item.generics() {
                 // Get the `DefId` for the type parameter corresponding to `A` in `<A as T>::Foo`.
                 // This will also work for `impl Trait`.
-                let def_id = if let ty::Param(param_ty) = proj_ty.self_ty().kind {
+                let def_id = if let ty::Param(param_ty) = proj_ty.self_ty().kind() {
                     let generics = self.generics_of(body_owner_def_id);
-                    generics.type_param(&param_ty, *self).def_id
+                    generics.type_param(param_ty, self).def_id
                 } else {
                     return false;
                 };
@@ -626,7 +631,7 @@ impl<T> Trait<T> for X {
     ///    and the `impl`, we provide a generic `help` to constrain the assoc type or call an assoc
     ///    fn that returns the type.
     fn expected_projection(
-        &self,
+        self,
         db: &mut DiagnosticBuilder<'_>,
         proj_ty: &ty::ProjectionTy<'tcx>,
         values: &ExpectedFound<Ty<'tcx>>,
@@ -680,7 +685,7 @@ impl<T> Trait<T> for X {
             }
         }
 
-        if let ty::Opaque(def_id, _) = proj_ty.self_ty().kind {
+        if let ty::Opaque(def_id, _) = *proj_ty.self_ty().kind() {
             // When the expected `impl Trait` is not defined in the current item, it will come from
             // a return type. This can occur when dealing with `TryStream` (#71035).
             if self.constrain_associated_type_structured_suggestion(
@@ -731,7 +736,7 @@ fn foo(&self) -> Self::T { String::new() }
     }
 
     fn point_at_methods_that_satisfy_associated_type(
-        &self,
+        self,
         db: &mut DiagnosticBuilder<'_>,
         assoc_container_id: DefId,
         current_method_ident: Option<Symbol>,
@@ -750,7 +755,7 @@ fn foo(&self) -> Self::T { String::new() }
             })
             .filter_map(|(_, item)| {
                 let method = self.fn_sig(item.def_id);
-                match method.output().skip_binder().kind {
+                match *method.output().skip_binder().kind() {
                     ty::Projection(ty::ProjectionTy { item_def_id, .. })
                         if item_def_id == proj_ty_item_def_id =>
                     {
@@ -786,7 +791,7 @@ fn foo(&self) -> Self::T { String::new() }
     }
 
     fn point_at_associated_type(
-        &self,
+        self,
         db: &mut DiagnosticBuilder<'_>,
         body_owner_def_id: DefId,
         found: Ty<'tcx>,
@@ -829,14 +834,11 @@ fn foo(&self) -> Self::T { String::new() }
                 kind: hir::ItemKind::Impl { items, .. }, ..
             })) => {
                 for item in &items[..] {
-                    match item.kind {
-                        hir::AssocItemKind::Type => {
-                            if self.type_of(self.hir().local_def_id(item.id.hir_id)) == found {
-                                db.span_label(item.span, "expected this associated type");
-                                return true;
-                            }
+                    if let hir::AssocItemKind::Type = item.kind {
+                        if self.type_of(self.hir().local_def_id(item.id.hir_id)) == found {
+                            db.span_label(item.span, "expected this associated type");
+                            return true;
                         }
-                        _ => {}
                     }
                 }
             }
@@ -848,7 +850,7 @@ fn foo(&self) -> Self::T { String::new() }
     /// Given a slice of `hir::GenericBound`s, if any of them corresponds to the `trait_ref`
     /// requirement, provide a strucuted suggestion to constrain it to a given type `ty`.
     fn constrain_generic_bound_associated_type_structured_suggestion(
-        &self,
+        self,
         db: &mut DiagnosticBuilder<'_>,
         trait_ref: &ty::TraitRef<'tcx>,
         bounds: hir::GenericBounds<'_>,
@@ -872,7 +874,7 @@ fn foo(&self) -> Self::T { String::new() }
     /// Given a span corresponding to a bound, provide a structured suggestion to set an
     /// associated type to a given type `ty`.
     fn constrain_associated_type_structured_suggestion(
-        &self,
+        self,
         db: &mut DiagnosticBuilder<'_>,
         span: Span,
         assoc: &ty::AssocItem,
diff --git a/compiler/rustc_middle/src/ty/fast_reject.rs b/compiler/rustc_middle/src/ty/fast_reject.rs
index 1bee2d60f75..860f91db2bf 100644
--- a/compiler/rustc_middle/src/ty/fast_reject.rs
+++ b/compiler/rustc_middle/src/ty/fast_reject.rs
@@ -60,7 +60,7 @@ pub fn simplify_type(
     ty: Ty<'_>,
     can_simplify_params: bool,
 ) -> Option<SimplifiedType> {
-    match ty.kind {
+    match *ty.kind() {
         ty::Bool => Some(BoolSimplifiedType),
         ty::Char => Some(CharSimplifiedType),
         ty::Int(int_type) => Some(IntSimplifiedType(int_type)),
diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs
index 27f50c240db..c9a4022330a 100644
--- a/compiler/rustc_middle/src/ty/flags.rs
+++ b/compiler/rustc_middle/src/ty/flags.rs
@@ -249,11 +249,14 @@ impl FlagComputation {
                 self.add_const(expected);
                 self.add_const(found);
             }
+            ty::PredicateAtom::TypeWellFormedFromEnv(ty) => {
+                self.add_ty(ty);
+            }
         }
     }
 
     fn add_ty(&mut self, ty: Ty<'_>) {
-        self.add_flags(ty.flags);
+        self.add_flags(ty.flags());
         self.add_exclusive_binder(ty.outer_exclusive_binder);
     }
 
diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs
index 492f8ce9ef1..84134bedef0 100644
--- a/compiler/rustc_middle/src/ty/fold.rs
+++ b/compiler/rustc_middle/src/ty/fold.rs
@@ -352,7 +352,7 @@ impl<'tcx> TyCtxt<'tcx> {
 
             fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool {
                 // We're only interested in types involving regions
-                if ty.flags.intersects(TypeFlags::HAS_FREE_REGIONS) {
+                if ty.flags().intersects(TypeFlags::HAS_FREE_REGIONS) {
                     ty.super_visit_with(self)
                 } else {
                     false // keep visiting
@@ -471,7 +471,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for BoundVarReplacer<'a, 'tcx> {
     }
 
     fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
-        match t.kind {
+        match *t.kind() {
             ty::Bound(debruijn, bound_ty) => {
                 if debruijn == self.current_index {
                     let fld_t = &mut self.fld_t;
@@ -623,7 +623,7 @@ impl<'tcx> TyCtxt<'tcx> {
     /// Replaces any late-bound regions bound in `value` with
     /// free variants attached to `all_outlive_scope`.
     pub fn liberate_late_bound_regions<T>(
-        &self,
+        self,
         all_outlive_scope: DefId,
         value: &ty::Binder<T>,
     ) -> T
@@ -644,7 +644,7 @@ impl<'tcx> TyCtxt<'tcx> {
     /// variables and equate `value` with something else, those
     /// variables will also be equated.
     pub fn collect_constrained_late_bound_regions<T>(
-        &self,
+        self,
         value: &Binder<T>,
     ) -> FxHashSet<ty::BoundRegion>
     where
@@ -655,7 +655,7 @@ impl<'tcx> TyCtxt<'tcx> {
 
     /// Returns a set of all late-bound regions that appear in `value` anywhere.
     pub fn collect_referenced_late_bound_regions<T>(
-        &self,
+        self,
         value: &Binder<T>,
     ) -> FxHashSet<ty::BoundRegion>
     where
@@ -665,7 +665,7 @@ impl<'tcx> TyCtxt<'tcx> {
     }
 
     fn collect_late_bound_regions<T>(
-        &self,
+        self,
         value: &Binder<T>,
         just_constraint: bool,
     ) -> FxHashSet<ty::BoundRegion>
@@ -771,7 +771,7 @@ impl TypeFolder<'tcx> for Shifter<'tcx> {
     }
 
     fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
-        match ty.kind {
+        match *ty.kind() {
             ty::Bound(debruijn, bound_ty) => {
                 if self.amount == 0 || debruijn < self.current_index {
                     ty
@@ -922,8 +922,13 @@ struct HasTypeFlagsVisitor {
 
 impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor {
     fn visit_ty(&mut self, t: Ty<'_>) -> bool {
-        debug!("HasTypeFlagsVisitor: t={:?} t.flags={:?} self.flags={:?}", t, t.flags, self.flags);
-        t.flags.intersects(self.flags)
+        debug!(
+            "HasTypeFlagsVisitor: t={:?} t.flags={:?} self.flags={:?}",
+            t,
+            t.flags(),
+            self.flags
+        );
+        t.flags().intersects(self.flags)
     }
 
     fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool {
@@ -987,7 +992,7 @@ impl<'tcx> TypeVisitor<'tcx> for LateBoundRegionsCollector {
         // ignore the inputs to a projection, as they may not appear
         // in the normalized form
         if self.just_constrained {
-            if let ty::Projection(..) | ty::Opaque(..) = t.kind {
+            if let ty::Projection(..) | ty::Opaque(..) = t.kind() {
                 return false;
             }
         }
diff --git a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs
index d1b5eed921b..2c1179c21fb 100644
--- a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs
+++ b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs
@@ -196,7 +196,7 @@ impl<'tcx> FieldDef {
 impl<'tcx> TyS<'tcx> {
     /// Calculates the forest of `DefId`s from which this type is visibly uninhabited.
     fn uninhabited_from(&self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> DefIdForest {
-        match self.kind {
+        match *self.kind() {
             Adt(def, substs) => {
                 ensure_sufficient_stack(|| def.uninhabited_from(tcx, substs, param_env))
             }
diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs
index 8e08fe4b87b..a6b62097d5b 100644
--- a/compiler/rustc_middle/src/ty/instance.rs
+++ b/compiler/rustc_middle/src/ty/instance.rs
@@ -62,10 +62,6 @@ pub enum InstanceDef<'tcx> {
     /// `<fn() as FnTrait>::call_*` (generated `FnTrait` implementation for `fn()` pointers).
     ///
     /// `DefId` is `FnTrait::call_*`.
-    ///
-    /// NB: the (`fn` pointer) type must currently be monomorphic to avoid double substitution
-    /// problems with the MIR shim bodies. `Instance::resolve` enforces this.
-    // FIXME(#69925) support polymorphic MIR shim bodies properly instead.
     FnPtrShim(DefId, Ty<'tcx>),
 
     /// Dynamic dispatch to `<dyn Trait as Trait>::fn`.
@@ -87,10 +83,6 @@ pub enum InstanceDef<'tcx> {
     /// The `DefId` is for `core::ptr::drop_in_place`.
     /// The `Option<Ty<'tcx>>` is either `Some(T)`, or `None` for empty drop
     /// glue.
-    ///
-    /// NB: the type must currently be monomorphic to avoid double substitution
-    /// problems with the MIR shim bodies. `Instance::resolve` enforces this.
-    // FIXME(#69925) support polymorphic MIR shim bodies properly instead.
     DropGlue(DefId, Option<Ty<'tcx>>),
 
     /// Compiler-generated `<T as Clone>::clone` implementation.
@@ -99,10 +91,6 @@ pub enum InstanceDef<'tcx> {
     /// Additionally, arrays, tuples, and closures get a `Clone` shim even if they aren't `Copy`.
     ///
     /// The `DefId` is for `Clone::clone`, the `Ty` is the type `T` with the builtin `Clone` impl.
-    ///
-    /// NB: the type must currently be monomorphic to avoid double substitution
-    /// problems with the MIR shim bodies. `Instance::resolve` enforces this.
-    // FIXME(#69925) support polymorphic MIR shim bodies properly instead.
     CloneShim(DefId, Ty<'tcx>),
 }
 
@@ -243,6 +231,27 @@ impl<'tcx> InstanceDef<'tcx> {
             _ => false,
         }
     }
+
+    /// Returns `true` when the MIR body associated with this instance should be monomorphized
+    /// by its users (e.g. codegen or miri) by substituting the `substs` from `Instance` (see
+    /// `Instance::substs_for_mir_body`).
+    ///
+    /// Otherwise, returns `false` only for some kinds of shims where the construction of the MIR
+    /// body should perform necessary substitutions.
+    pub fn has_polymorphic_mir_body(&self) -> bool {
+        match *self {
+            InstanceDef::CloneShim(..)
+            | InstanceDef::FnPtrShim(..)
+            | InstanceDef::DropGlue(_, Some(_)) => false,
+            InstanceDef::ClosureOnceShim { .. }
+            | InstanceDef::DropGlue(..)
+            | InstanceDef::Item(_)
+            | InstanceDef::Intrinsic(..)
+            | InstanceDef::ReifyShim(..)
+            | InstanceDef::Virtual(..)
+            | InstanceDef::VtableShim(..) => true,
+        }
+    }
 }
 
 impl<'tcx> fmt::Display for Instance<'tcx> {
@@ -260,10 +269,11 @@ impl<'tcx> fmt::Display for Instance<'tcx> {
             InstanceDef::ReifyShim(_) => write!(f, " - shim(reify)"),
             InstanceDef::Intrinsic(_) => write!(f, " - intrinsic"),
             InstanceDef::Virtual(_, num) => write!(f, " - virtual#{}", num),
-            InstanceDef::FnPtrShim(_, ty) => write!(f, " - shim({:?})", ty),
+            InstanceDef::FnPtrShim(_, ty) => write!(f, " - shim({})", ty),
             InstanceDef::ClosureOnceShim { .. } => write!(f, " - shim"),
-            InstanceDef::DropGlue(_, ty) => write!(f, " - shim({:?})", ty),
-            InstanceDef::CloneShim(_, ty) => write!(f, " - shim({:?})", ty),
+            InstanceDef::DropGlue(_, None) => write!(f, " - shim(None)"),
+            InstanceDef::DropGlue(_, Some(ty)) => write!(f, " - shim(Some({}))", ty),
+            InstanceDef::CloneShim(_, ty) => write!(f, " - shim({})", ty),
         }
     }
 }
@@ -439,30 +449,18 @@ impl<'tcx> Instance<'tcx> {
         Instance { def, substs }
     }
 
-    /// FIXME(#69925) Depending on the kind of `InstanceDef`, the MIR body associated with an
+    /// Depending on the kind of `InstanceDef`, the MIR body associated with an
     /// instance is expressed in terms of the generic parameters of `self.def_id()`, and in other
     /// cases the MIR body is expressed in terms of the types found in the substitution array.
     /// In the former case, we want to substitute those generic types and replace them with the
     /// values from the substs when monomorphizing the function body. But in the latter case, we
     /// don't want to do that substitution, since it has already been done effectively.
     ///
-    /// This function returns `Some(substs)` in the former case and None otherwise -- i.e., if
+    /// This function returns `Some(substs)` in the former case and `None` otherwise -- i.e., if
     /// this function returns `None`, then the MIR body does not require substitution during
-    /// monomorphization.
+    /// codegen.
     pub fn substs_for_mir_body(&self) -> Option<SubstsRef<'tcx>> {
-        match self.def {
-            InstanceDef::CloneShim(..)
-            | InstanceDef::DropGlue(_, Some(_)) => None,
-            InstanceDef::ClosureOnceShim { .. }
-            | InstanceDef::DropGlue(..)
-            // FIXME(#69925): `FnPtrShim` should be in the other branch.
-            | InstanceDef::FnPtrShim(..)
-            | InstanceDef::Item(_)
-            | InstanceDef::Intrinsic(..)
-            | InstanceDef::ReifyShim(..)
-            | InstanceDef::Virtual(..)
-            | InstanceDef::VtableShim(..) => Some(self.substs),
-        }
+        if self.def.has_polymorphic_mir_body() { Some(self.substs) } else { None }
     }
 
     /// Returns a new `Instance` where generic parameters in `instance.substs` are replaced by
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index 3ec08423472..0fda1473f64 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -174,9 +174,9 @@ pub enum LayoutError<'tcx> {
 impl<'tcx> fmt::Display for LayoutError<'tcx> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match *self {
-            LayoutError::Unknown(ty) => write!(f, "the type `{:?}` has an unknown layout", ty),
+            LayoutError::Unknown(ty) => write!(f, "the type `{}` has an unknown layout", ty),
             LayoutError::SizeOverflow(ty) => {
-                write!(f, "the type `{:?}` is too big for the current architecture", ty)
+                write!(f, "the type `{}` is too big for the current architecture", ty)
             }
         }
     }
@@ -477,7 +477,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
         };
         debug_assert!(!ty.has_infer_types_or_consts());
 
-        Ok(match ty.kind {
+        Ok(match *ty.kind() {
             // Basic scalars.
             ty::Bool => tcx.intern_layout(Layout::scalar(
                 self,
@@ -522,7 +522,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
                 }
 
                 let unsized_part = tcx.struct_tail_erasing_lifetimes(pointee, param_env);
-                let metadata = match unsized_part.kind {
+                let metadata = match unsized_part.kind() {
                     ty::Foreign(..) => {
                         return Ok(tcx.intern_layout(Layout::scalar(self, data_ptr)));
                     }
@@ -1241,11 +1241,11 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
                 tcx.layout_raw(param_env.and(normalized))?
             }
 
-            ty::Bound(..) | ty::Placeholder(..) | ty::GeneratorWitness(..) | ty::Infer(_) => {
+            ty::Placeholder(..) | ty::GeneratorWitness(..) | ty::Infer(_) => {
                 bug!("Layout::compute: unexpected type `{}`", ty)
             }
 
-            ty::Param(_) | ty::Error(_) => {
+            ty::Bound(..) | ty::Param(_) | ty::Error(_) => {
                 return Err(LayoutError::Unknown(ty));
             }
         })
@@ -1606,7 +1606,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
             );
         };
 
-        let adt_def = match layout.ty.kind {
+        let adt_def = match *layout.ty.kind() {
             ty::Adt(ref adt_def, _) => {
                 debug!("print-type-size t: `{:?}` process adt", layout.ty);
                 adt_def
@@ -1749,11 +1749,11 @@ impl<'tcx> SizeSkeleton<'tcx> {
             Err(err) => err,
         };
 
-        match ty.kind {
+        match *ty.kind() {
             ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => {
                 let non_zero = !ty.is_unsafe_ptr();
                 let tail = tcx.struct_tail_erasing_lifetimes(pointee, param_env);
-                match tail.kind {
+                match tail.kind() {
                     ty::Param(_) | ty::Projection(_) => {
                         debug_assert!(tail.has_param_types_or_consts());
                         Ok(SizeSkeleton::Pointer { non_zero, tail: tcx.erase_regions(&tail) })
@@ -2000,7 +2000,7 @@ where
                     assert_eq!(original_layout.variants, Variants::Single { index });
                 }
 
-                let fields = match this.ty.kind {
+                let fields = match this.ty.kind() {
                     ty::Adt(def, _) if def.variants.is_empty() =>
                         bug!("for_variant called on zero-variant enum"),
                     ty::Adt(def, _) => def.variants[variant_index].fields.len(),
@@ -2038,7 +2038,7 @@ where
             }))
         };
 
-        cx.layout_of(match this.ty.kind {
+        cx.layout_of(match *this.ty.kind() {
             ty::Bool
             | ty::Char
             | ty::Int(_)
@@ -2074,7 +2074,7 @@ where
                     ));
                 }
 
-                match tcx.struct_tail_erasing_lifetimes(pointee, cx.param_env()).kind {
+                match tcx.struct_tail_erasing_lifetimes(pointee, cx.param_env()).kind() {
                     ty::Slice(_) | ty::Str => tcx.types.usize,
                     ty::Dynamic(_, _) => {
                         tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_array(tcx.types.usize, 3))
@@ -2152,7 +2152,7 @@ where
             if ty.is_fn() { cx.data_layout().instruction_address_space } else { AddressSpace::DATA }
         };
 
-        let pointee_info = match this.ty.kind {
+        let pointee_info = match *this.ty.kind() {
             ty::RawPtr(mt) if offset.bytes() == 0 => {
                 cx.layout_of(mt.ty).to_result().ok().map(|layout| PointeeInfo {
                     size: layout.size,
@@ -2268,7 +2268,7 @@ where
 
                 // FIXME(eddyb) This should be for `ptr::Unique<T>`, not `Box<T>`.
                 if let Some(ref mut pointee) = result {
-                    if let ty::Adt(def, _) = this.ty.kind {
+                    if let ty::Adt(def, _) = this.ty.kind() {
                         if def.is_box() && offset.bytes() == 0 {
                             pointee.safe = Some(PointerKind::UniqueOwned);
                         }
@@ -2281,7 +2281,9 @@ where
 
         debug!(
             "pointee_info_at (offset={:?}, type kind: {:?}) => {:?}",
-            offset, this.ty.kind, pointee_info
+            offset,
+            this.ty.kind(),
+            pointee_info
         );
 
         pointee_info
@@ -2308,14 +2310,14 @@ impl<'tcx> ty::Instance<'tcx> {
     fn fn_sig_for_fn_abi(&self, tcx: TyCtxt<'tcx>) -> ty::PolyFnSig<'tcx> {
         // FIXME(davidtwco,eddyb): A `ParamEnv` should be passed through to this function.
         let ty = self.ty(tcx, ty::ParamEnv::reveal_all());
-        match ty.kind {
+        match *ty.kind() {
             ty::FnDef(..) => {
                 // HACK(davidtwco,eddyb): This is a workaround for polymorphization considering
                 // parameters unused if they show up in the signature, but not in the `mir::Body`
                 // (i.e. due to being inside a projection that got normalized, see
                 // `src/test/ui/polymorphization/normalized_sig_types.rs`), and codegen not keeping
                 // track of a polymorphization `ParamEnv` to allow normalizing later.
-                let mut sig = match ty.kind {
+                let mut sig = match *ty.kind() {
                     ty::FnDef(def_id, substs) => tcx
                         .normalize_erasing_regions(tcx.param_env(def_id), tcx.fn_sig(def_id))
                         .subst(tcx, substs),
@@ -2578,7 +2580,7 @@ where
             assert!(!sig.c_variadic && extra_args.is_empty());
 
             if let Some(input) = sig.inputs().last() {
-                if let ty::Tuple(tupled_arguments) = input.kind {
+                if let ty::Tuple(tupled_arguments) = input.kind() {
                     inputs = &sig.inputs()[0..sig.inputs().len() - 1];
                     tupled_arguments.iter().map(|k| k.expect_ty()).collect()
                 } else {
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index b6300a40b0d..637ef4c17eb 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -580,8 +580,12 @@ bitflags! {
 
 #[allow(rustc::usage_of_ty_tykind)]
 pub struct TyS<'tcx> {
-    pub kind: TyKind<'tcx>,
-    pub flags: TypeFlags,
+    /// This field shouldn't be used directly and may be removed in the future.
+    /// Use `TyS::kind()` instead.
+    kind: TyKind<'tcx>,
+    /// This field shouldn't be used directly and may be removed in the future.
+    /// Use `TyS::flags()` instead.
+    flags: TypeFlags,
 
     /// This is a kind of confusing thing: it stores the smallest
     /// binder such that
@@ -609,13 +613,13 @@ static_assert_size!(TyS<'_>, 32);
 
 impl<'tcx> Ord for TyS<'tcx> {
     fn cmp(&self, other: &TyS<'tcx>) -> Ordering {
-        self.kind.cmp(&other.kind)
+        self.kind().cmp(other.kind())
     }
 }
 
 impl<'tcx> PartialOrd for TyS<'tcx> {
     fn partial_cmp(&self, other: &TyS<'tcx>) -> Option<Ordering> {
-        Some(self.kind.cmp(&other.kind))
+        Some(self.kind().cmp(other.kind()))
     }
 }
 
@@ -1151,6 +1155,11 @@ pub enum PredicateAtom<'tcx> {
 
     /// Constants must be equal. The first component is the const that is expected.
     ConstEquate(&'tcx Const<'tcx>, &'tcx Const<'tcx>),
+
+    /// Represents a type found in the environment that we can use for implied bounds.
+    ///
+    /// Only used for Chalk.
+    TypeWellFormedFromEnv(Ty<'tcx>),
 }
 
 impl<'tcx> PredicateAtom<'tcx> {
@@ -1446,7 +1455,8 @@ impl<'tcx> Predicate<'tcx> {
             | PredicateAtom::ClosureKind(..)
             | PredicateAtom::TypeOutlives(..)
             | PredicateAtom::ConstEvaluatable(..)
-            | PredicateAtom::ConstEquate(..) => None,
+            | PredicateAtom::ConstEquate(..)
+            | PredicateAtom::TypeWellFormedFromEnv(..) => None,
         }
     }
 
@@ -1461,7 +1471,8 @@ impl<'tcx> Predicate<'tcx> {
             | PredicateAtom::ObjectSafe(..)
             | PredicateAtom::ClosureKind(..)
             | PredicateAtom::ConstEvaluatable(..)
-            | PredicateAtom::ConstEquate(..) => None,
+            | PredicateAtom::ConstEquate(..)
+            | PredicateAtom::TypeWellFormedFromEnv(..) => None,
         }
     }
 }
@@ -1735,10 +1746,8 @@ pub struct ParamEnv<'tcx> {
     /// Note: This is packed, use the reveal() method to access it.
     packed: CopyTaggedPtr<&'tcx List<Predicate<'tcx>>, traits::Reveal, true>,
 
-    /// If this `ParamEnv` comes from a call to `tcx.param_env(def_id)`,
-    /// register that `def_id` (useful for transitioning to the chalk trait
-    /// solver).
-    pub def_id: Option<DefId>,
+    /// FIXME: This field is not used, but removing it causes a performance degradation. See #76913.
+    unused_field: Option<DefId>,
 }
 
 unsafe impl rustc_data_structures::tagged_ptr::Tag for traits::Reveal {
@@ -1763,7 +1772,6 @@ impl<'tcx> fmt::Debug for ParamEnv<'tcx> {
         f.debug_struct("ParamEnv")
             .field("caller_bounds", &self.caller_bounds())
             .field("reveal", &self.reveal())
-            .field("def_id", &self.def_id)
             .finish()
     }
 }
@@ -1772,23 +1780,16 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for ParamEnv<'tcx> {
     fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
         self.caller_bounds().hash_stable(hcx, hasher);
         self.reveal().hash_stable(hcx, hasher);
-        self.def_id.hash_stable(hcx, hasher);
     }
 }
 
 impl<'tcx> TypeFoldable<'tcx> for ParamEnv<'tcx> {
     fn super_fold_with<F: ty::fold::TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
-        ParamEnv::new(
-            self.caller_bounds().fold_with(folder),
-            self.reveal().fold_with(folder),
-            self.def_id.fold_with(folder),
-        )
+        ParamEnv::new(self.caller_bounds().fold_with(folder), self.reveal().fold_with(folder))
     }
 
     fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        self.caller_bounds().visit_with(visitor)
-            || self.reveal().visit_with(visitor)
-            || self.def_id.visit_with(visitor)
+        self.caller_bounds().visit_with(visitor) || self.reveal().visit_with(visitor)
     }
 }
 
@@ -1799,7 +1800,7 @@ impl<'tcx> ParamEnv<'tcx> {
     /// type-checking.
     #[inline]
     pub fn empty() -> Self {
-        Self::new(List::empty(), Reveal::UserFacing, None)
+        Self::new(List::empty(), Reveal::UserFacing)
     }
 
     #[inline]
@@ -1821,17 +1822,13 @@ impl<'tcx> ParamEnv<'tcx> {
     /// or invoke `param_env.with_reveal_all()`.
     #[inline]
     pub fn reveal_all() -> Self {
-        Self::new(List::empty(), Reveal::All, None)
+        Self::new(List::empty(), Reveal::All)
     }
 
     /// Construct a trait environment with the given set of predicates.
     #[inline]
-    pub fn new(
-        caller_bounds: &'tcx List<Predicate<'tcx>>,
-        reveal: Reveal,
-        def_id: Option<DefId>,
-    ) -> Self {
-        ty::ParamEnv { packed: CopyTaggedPtr::new(caller_bounds, reveal), def_id }
+    pub fn new(caller_bounds: &'tcx List<Predicate<'tcx>>, reveal: Reveal) -> Self {
+        ty::ParamEnv { packed: CopyTaggedPtr::new(caller_bounds, reveal), unused_field: None }
     }
 
     pub fn with_user_facing(mut self) -> Self {
@@ -1853,12 +1850,12 @@ impl<'tcx> ParamEnv<'tcx> {
             return self;
         }
 
-        ParamEnv::new(tcx.normalize_opaque_types(self.caller_bounds()), Reveal::All, self.def_id)
+        ParamEnv::new(tcx.normalize_opaque_types(self.caller_bounds()), Reveal::All)
     }
 
     /// Returns this same environment but with no caller bounds.
     pub fn without_caller_bounds(self) -> Self {
-        Self::new(List::empty(), self.reveal(), self.def_id)
+        Self::new(List::empty(), self.reveal())
     }
 
     /// Creates a suitable environment in which to perform trait
@@ -2005,7 +2002,7 @@ pub struct VariantDef {
     flags: VariantFlags,
 }
 
-impl<'tcx> VariantDef {
+impl VariantDef {
     /// Creates a new `VariantDef`.
     ///
     /// `variant_did` is the `DefId` that identifies the enum variant (if this `VariantDef`
@@ -2071,19 +2068,6 @@ impl<'tcx> VariantDef {
     pub fn is_recovered(&self) -> bool {
         self.flags.intersects(VariantFlags::IS_RECOVERED)
     }
-
-    /// `repr(transparent)` structs can have a single non-ZST field, this function returns that
-    /// field.
-    pub fn transparent_newtype_field(&self, tcx: TyCtxt<'tcx>) -> Option<&FieldDef> {
-        for field in &self.fields {
-            let field_ty = field.ty(tcx, InternalSubsts::identity_for_item(tcx, self.def_id));
-            if !field_ty.is_zst(tcx, self.def_id) {
-                return Some(field);
-            }
-        }
-
-        None
-    }
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)]
@@ -3101,6 +3085,7 @@ pub fn provide(providers: &mut ty::query::Providers) {
     erase_regions::provide(providers);
     layout::provide(providers);
     util::provide(providers);
+    print::provide(providers);
     super::util::bug::provide(providers);
     *providers = ty::query::Providers {
         trait_impls_of: trait_def::trait_impls_of_provider,
diff --git a/compiler/rustc_middle/src/ty/outlives.rs b/compiler/rustc_middle/src/ty/outlives.rs
index 1a8693b8df7..ca992d36e95 100644
--- a/compiler/rustc_middle/src/ty/outlives.rs
+++ b/compiler/rustc_middle/src/ty/outlives.rs
@@ -4,6 +4,7 @@
 
 use crate::ty::subst::{GenericArg, GenericArgKind};
 use crate::ty::{self, Ty, TyCtxt, TypeFoldable};
+use rustc_data_structures::mini_set::MiniSet;
 use smallvec::SmallVec;
 
 #[derive(Debug)]
@@ -50,18 +51,24 @@ impl<'tcx> TyCtxt<'tcx> {
     /// Push onto `out` all the things that must outlive `'a` for the condition
     /// `ty0: 'a` to hold. Note that `ty0` must be a **fully resolved type**.
     pub fn push_outlives_components(self, ty0: Ty<'tcx>, out: &mut SmallVec<[Component<'tcx>; 4]>) {
-        compute_components(self, ty0, out);
+        let mut visited = MiniSet::new();
+        compute_components(self, ty0, out, &mut visited);
         debug!("components({:?}) = {:?}", ty0, out);
     }
 }
 
-fn compute_components(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, out: &mut SmallVec<[Component<'tcx>; 4]>) {
+fn compute_components(
+    tcx: TyCtxt<'tcx>,
+    ty: Ty<'tcx>,
+    out: &mut SmallVec<[Component<'tcx>; 4]>,
+    visited: &mut MiniSet<GenericArg<'tcx>>,
+) {
     // Descend through the types, looking for the various "base"
     // components and collecting them into `out`. This is not written
     // with `collect()` because of the need to sometimes skip subtrees
     // in the `subtys` iterator (e.g., when encountering a
     // projection).
-    match ty.kind {
+    match *ty.kind() {
             ty::FnDef(_, substs) => {
                 // HACK(eddyb) ignore lifetimes found shallowly in `substs`.
                 // This is inconsistent with `ty::Adt` (including all substs)
@@ -73,11 +80,11 @@ fn compute_components(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, out: &mut SmallVec<[Compo
                 for child in substs {
                     match child.unpack() {
                         GenericArgKind::Type(ty) => {
-                            compute_components(tcx, ty, out);
+                            compute_components(tcx, ty, out, visited);
                         }
                         GenericArgKind::Lifetime(_) => {}
                         GenericArgKind::Const(_) => {
-                            compute_components_recursive(tcx, child, out);
+                            compute_components_recursive(tcx, child, out, visited);
                         }
                     }
                 }
@@ -85,19 +92,19 @@ fn compute_components(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, out: &mut SmallVec<[Compo
 
             ty::Array(element, _) => {
                 // Don't look into the len const as it doesn't affect regions
-                compute_components(tcx, element, out);
+                compute_components(tcx, element, out, visited);
             }
 
             ty::Closure(_, ref substs) => {
                 for upvar_ty in substs.as_closure().upvar_tys() {
-                    compute_components(tcx, upvar_ty, out);
+                    compute_components(tcx, upvar_ty, out, visited);
                 }
             }
 
             ty::Generator(_, ref substs, _) => {
                 // Same as the closure case
                 for upvar_ty in substs.as_generator().upvar_tys() {
-                    compute_components(tcx, upvar_ty, out);
+                    compute_components(tcx, upvar_ty, out, visited);
                 }
 
                 // We ignore regions in the generator interior as we don't
@@ -135,7 +142,8 @@ fn compute_components(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, out: &mut SmallVec<[Compo
                     // OutlivesProjectionComponents.  Continue walking
                     // through and constrain Pi.
                     let mut subcomponents = smallvec![];
-                    compute_components_recursive(tcx, ty.into(), &mut subcomponents);
+                    let mut subvisited = MiniSet::new();
+                    compute_components_recursive(tcx, ty.into(), &mut subcomponents, &mut subvisited);
                     out.push(Component::EscapingProjection(subcomponents.into_iter().collect()));
                 }
             }
@@ -177,7 +185,7 @@ fn compute_components(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, out: &mut SmallVec<[Compo
                 // the "bound regions list".  In our representation, no such
                 // list is maintained explicitly, because bound regions
                 // themselves can be readily identified.
-                compute_components_recursive(tcx, ty.into(), out);
+                compute_components_recursive(tcx, ty.into(), out, visited);
             }
         }
 }
@@ -186,11 +194,12 @@ fn compute_components_recursive(
     tcx: TyCtxt<'tcx>,
     parent: GenericArg<'tcx>,
     out: &mut SmallVec<[Component<'tcx>; 4]>,
+    visited: &mut MiniSet<GenericArg<'tcx>>,
 ) {
-    for child in parent.walk_shallow() {
+    for child in parent.walk_shallow(visited) {
         match child.unpack() {
             GenericArgKind::Type(ty) => {
-                compute_components(tcx, ty, out);
+                compute_components(tcx, ty, out, visited);
             }
             GenericArgKind::Lifetime(lt) => {
                 // Ignore late-bound regions.
@@ -199,7 +208,7 @@ fn compute_components_recursive(
                 }
             }
             GenericArgKind::Const(_) => {
-                compute_components_recursive(tcx, child, out);
+                compute_components_recursive(tcx, child, out, visited);
             }
         }
     }
diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs
index 6c8f23c139f..225ea2399fb 100644
--- a/compiler/rustc_middle/src/ty/print/mod.rs
+++ b/compiler/rustc_middle/src/ty/print/mod.rs
@@ -2,6 +2,7 @@ use crate::ty::subst::{GenericArg, Subst};
 use crate::ty::{self, DefIdTree, Ty, TyCtxt};
 
 use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::mini_set::MiniSet;
 use rustc_hir::def_id::{CrateNum, DefId};
 use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
 
@@ -9,8 +10,6 @@ use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
 mod pretty;
 pub use self::pretty::*;
 
-pub mod obsolete;
-
 // FIXME(eddyb) false positive, the lifetime parameters are used with `P:  Printer<...>`.
 #[allow(unused_lifetimes)]
 pub trait Print<'tcx, P> {
@@ -265,21 +264,33 @@ pub trait Printer<'tcx>: Sized {
 /// function tries to find a "characteristic `DefId`" for a
 /// type. It's just a heuristic so it makes some questionable
 /// decisions and we may want to adjust it later.
-pub fn characteristic_def_id_of_type(ty: Ty<'_>) -> Option<DefId> {
-    match ty.kind {
+///
+/// Visited set is needed to avoid full iteration over
+/// deeply nested tuples that have no DefId.
+fn characteristic_def_id_of_type_cached<'a>(
+    ty: Ty<'a>,
+    visited: &mut MiniSet<Ty<'a>>,
+) -> Option<DefId> {
+    match *ty.kind() {
         ty::Adt(adt_def, _) => Some(adt_def.did),
 
         ty::Dynamic(data, ..) => data.principal_def_id(),
 
-        ty::Array(subty, _) | ty::Slice(subty) => characteristic_def_id_of_type(subty),
+        ty::Array(subty, _) | ty::Slice(subty) => {
+            characteristic_def_id_of_type_cached(subty, visited)
+        }
 
-        ty::RawPtr(mt) => characteristic_def_id_of_type(mt.ty),
+        ty::RawPtr(mt) => characteristic_def_id_of_type_cached(mt.ty, visited),
 
-        ty::Ref(_, ty, _) => characteristic_def_id_of_type(ty),
+        ty::Ref(_, ty, _) => characteristic_def_id_of_type_cached(ty, visited),
 
-        ty::Tuple(ref tys) => {
-            tys.iter().find_map(|ty| characteristic_def_id_of_type(ty.expect_ty()))
-        }
+        ty::Tuple(ref tys) => tys.iter().find_map(|ty| {
+            let ty = ty.expect_ty();
+            if visited.insert(ty) {
+                return characteristic_def_id_of_type_cached(ty, visited);
+            }
+            return None;
+        }),
 
         ty::FnDef(def_id, _)
         | ty::Closure(def_id, _)
@@ -304,6 +315,9 @@ pub fn characteristic_def_id_of_type(ty: Ty<'_>) -> Option<DefId> {
         | ty::Float(_) => None,
     }
 }
+pub fn characteristic_def_id_of_type(ty: Ty<'_>) -> Option<DefId> {
+    characteristic_def_id_of_type_cached(ty, &mut MiniSet::new())
+}
 
 impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for ty::RegionKind {
     type Output = P::Region;
diff --git a/compiler/rustc_middle/src/ty/print/obsolete.rs b/compiler/rustc_middle/src/ty/print/obsolete.rs
deleted file mode 100644
index 2ea7cd2a6dc..00000000000
--- a/compiler/rustc_middle/src/ty/print/obsolete.rs
+++ /dev/null
@@ -1,251 +0,0 @@
-//! Allows for producing a unique string key for a mono item.
-//! These keys are used by the handwritten auto-tests, so they need to be
-//! predictable and human-readable.
-//!
-//! Note: A lot of this could looks very similar to what's already in `ty::print`.
-//! FIXME(eddyb) implement a custom `PrettyPrinter` for this.
-
-use crate::bug;
-use crate::ty::subst::SubstsRef;
-use crate::ty::{self, Const, Instance, Ty, TyCtxt};
-use rustc_hir as hir;
-use rustc_hir::def_id::DefId;
-use std::fmt::Write;
-use std::iter;
-
-/// Same as `unique_type_name()` but with the result pushed onto the given
-/// `output` parameter.
-pub struct DefPathBasedNames<'tcx> {
-    tcx: TyCtxt<'tcx>,
-    omit_disambiguators: bool,
-    omit_local_crate_name: bool,
-}
-
-impl DefPathBasedNames<'tcx> {
-    pub fn new(tcx: TyCtxt<'tcx>, omit_disambiguators: bool, omit_local_crate_name: bool) -> Self {
-        DefPathBasedNames { tcx, omit_disambiguators, omit_local_crate_name }
-    }
-
-    // Pushes the type name of the specified type to the provided string.
-    // If `debug` is true, printing normally unprintable types is allowed
-    // (e.g. `ty::GeneratorWitness`). This parameter should only be set when
-    // this method is being used for logging purposes (e.g. with `debug!` or `info!`)
-    // When being used for codegen purposes, `debug` should be set to `false`
-    // in order to catch unexpected types that should never end up in a type name.
-    pub fn push_type_name(&self, t: Ty<'tcx>, output: &mut String, debug: bool) {
-        match t.kind {
-            ty::Bool => output.push_str("bool"),
-            ty::Char => output.push_str("char"),
-            ty::Str => output.push_str("str"),
-            ty::Never => output.push_str("!"),
-            ty::Int(ty) => output.push_str(ty.name_str()),
-            ty::Uint(ty) => output.push_str(ty.name_str()),
-            ty::Float(ty) => output.push_str(ty.name_str()),
-            ty::Adt(adt_def, substs) => {
-                self.push_def_path(adt_def.did, output);
-                self.push_generic_params(substs, iter::empty(), output, debug);
-            }
-            ty::Tuple(component_types) => {
-                output.push('(');
-                for component_type in component_types {
-                    self.push_type_name(component_type.expect_ty(), output, debug);
-                    output.push_str(", ");
-                }
-                if !component_types.is_empty() {
-                    output.pop();
-                    output.pop();
-                }
-                output.push(')');
-            }
-            ty::RawPtr(ty::TypeAndMut { ty: inner_type, mutbl }) => {
-                output.push('*');
-                match mutbl {
-                    hir::Mutability::Not => output.push_str("const "),
-                    hir::Mutability::Mut => output.push_str("mut "),
-                }
-
-                self.push_type_name(inner_type, output, debug);
-            }
-            ty::Ref(_, inner_type, mutbl) => {
-                output.push('&');
-                output.push_str(mutbl.prefix_str());
-
-                self.push_type_name(inner_type, output, debug);
-            }
-            ty::Array(inner_type, len) => {
-                output.push('[');
-                self.push_type_name(inner_type, output, debug);
-                let len = len.eval_usize(self.tcx, ty::ParamEnv::reveal_all());
-                write!(output, "; {}", len).unwrap();
-                output.push(']');
-            }
-            ty::Slice(inner_type) => {
-                output.push('[');
-                self.push_type_name(inner_type, output, debug);
-                output.push(']');
-            }
-            ty::Dynamic(ref trait_data, ..) => {
-                if let Some(principal) = trait_data.principal() {
-                    self.push_def_path(principal.def_id(), output);
-                    self.push_generic_params(
-                        principal.skip_binder().substs,
-                        trait_data.projection_bounds(),
-                        output,
-                        debug,
-                    );
-                } else {
-                    output.push_str("dyn '_");
-                }
-            }
-            ty::Foreign(did) => self.push_def_path(did, output),
-            ty::FnDef(..) | ty::FnPtr(_) => {
-                let sig = t.fn_sig(self.tcx);
-                output.push_str(sig.unsafety().prefix_str());
-
-                let abi = sig.abi();
-                if abi != ::rustc_target::spec::abi::Abi::Rust {
-                    output.push_str("extern \"");
-                    output.push_str(abi.name());
-                    output.push_str("\" ");
-                }
-
-                output.push_str("fn(");
-
-                let sig =
-                    self.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig);
-
-                if !sig.inputs().is_empty() {
-                    for &parameter_type in sig.inputs() {
-                        self.push_type_name(parameter_type, output, debug);
-                        output.push_str(", ");
-                    }
-                    output.pop();
-                    output.pop();
-                }
-
-                if sig.c_variadic {
-                    if !sig.inputs().is_empty() {
-                        output.push_str(", ...");
-                    } else {
-                        output.push_str("...");
-                    }
-                }
-
-                output.push(')');
-
-                if !sig.output().is_unit() {
-                    output.push_str(" -> ");
-                    self.push_type_name(sig.output(), output, debug);
-                }
-            }
-            ty::Generator(def_id, substs, _) | ty::Closure(def_id, substs) => {
-                self.push_def_path(def_id, output);
-                let generics = self.tcx.generics_of(self.tcx.closure_base_def_id(def_id));
-                let substs = substs.truncate_to(self.tcx, generics);
-                self.push_generic_params(substs, iter::empty(), output, debug);
-            }
-            ty::Param(_) => {
-                output.push_str(&t.to_string());
-            }
-            ty::Error(_)
-            | ty::Bound(..)
-            | ty::Infer(_)
-            | ty::Placeholder(..)
-            | ty::Projection(..)
-            | ty::GeneratorWitness(_)
-            | ty::Opaque(..) => {
-                if debug {
-                    output.push_str(&format!("`{:?}`", t));
-                } else {
-                    bug!(
-                        "DefPathBasedNames: trying to create type name for unexpected type: {:?}",
-                        t,
-                    );
-                }
-            }
-        }
-    }
-
-    // Pushes the the name of the specified const to the provided string.
-    // If `debug` is true, the unprintable types of constants will be printed with `fmt::Debug`
-    // (see `push_type_name` for more details).
-    pub fn push_const_name(&self, ct: &Const<'tcx>, output: &mut String, debug: bool) {
-        write!(output, "{}", ct).unwrap();
-        output.push_str(": ");
-        self.push_type_name(ct.ty, output, debug);
-    }
-
-    pub fn push_def_path(&self, def_id: DefId, output: &mut String) {
-        let def_path = self.tcx.def_path(def_id);
-
-        // some_crate::
-        if !(self.omit_local_crate_name && def_id.is_local()) {
-            output.push_str(&self.tcx.crate_name(def_path.krate).as_str());
-            output.push_str("::");
-        }
-
-        // foo::bar::ItemName::
-        for part in self.tcx.def_path(def_id).data {
-            if self.omit_disambiguators {
-                write!(output, "{}::", part.data.as_symbol()).unwrap();
-            } else {
-                write!(output, "{}[{}]::", part.data.as_symbol(), part.disambiguator).unwrap();
-            }
-        }
-
-        // remove final "::"
-        output.pop();
-        output.pop();
-    }
-
-    fn push_generic_params<I>(
-        &self,
-        substs: SubstsRef<'tcx>,
-        projections: I,
-        output: &mut String,
-        debug: bool,
-    ) where
-        I: Iterator<Item = ty::PolyExistentialProjection<'tcx>>,
-    {
-        let mut projections = projections.peekable();
-        if substs.non_erasable_generics().next().is_none() && projections.peek().is_none() {
-            return;
-        }
-
-        output.push('<');
-
-        for type_parameter in substs.types() {
-            self.push_type_name(type_parameter, output, debug);
-            output.push_str(", ");
-        }
-
-        for projection in projections {
-            let projection = projection.skip_binder();
-            let name = &self.tcx.associated_item(projection.item_def_id).ident.as_str();
-            output.push_str(name);
-            output.push_str("=");
-            self.push_type_name(projection.ty, output, debug);
-            output.push_str(", ");
-        }
-
-        for const_parameter in substs.consts() {
-            self.push_const_name(const_parameter, output, debug);
-            output.push_str(", ");
-        }
-
-        output.pop();
-        output.pop();
-
-        output.push('>');
-    }
-
-    pub fn push_instance_as_string(
-        &self,
-        instance: Instance<'tcx>,
-        output: &mut String,
-        debug: bool,
-    ) {
-        self.push_def_path(instance.def_id(), output);
-        self.push_generic_params(instance.substs, iter::empty(), output, debug);
-    }
-}
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index 999a1d52a26..c9cc9bfc9fa 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -7,10 +7,13 @@ use rustc_apfloat::ieee::{Double, Single};
 use rustc_apfloat::Float;
 use rustc_ast as ast;
 use rustc_attr::{SignedInt, UnsignedInt};
+use rustc_data_structures::fx::FxHashMap;
 use rustc_hir as hir;
-use rustc_hir::def::{CtorKind, DefKind, Namespace};
-use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
+use rustc_hir::def::{self, CtorKind, DefKind, Namespace};
+use rustc_hir::def_id::{CrateNum, DefId, DefIdSet, CRATE_DEF_INDEX, LOCAL_CRATE};
 use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
+use rustc_hir::ItemKind;
+use rustc_session::config::TrimmedDefPaths;
 use rustc_span::symbol::{kw, Ident, Symbol};
 use rustc_target::abi::{Integer, Size};
 use rustc_target::spec::abi::Abi;
@@ -52,6 +55,7 @@ macro_rules! define_scoped_cx {
 thread_local! {
     static FORCE_IMPL_FILENAME_LINE: Cell<bool> = Cell::new(false);
     static SHOULD_PREFIX_WITH_CRATE: Cell<bool> = Cell::new(false);
+    static NO_TRIMMED_PATH: Cell<bool> = Cell::new(false);
     static NO_QUERIES: Cell<bool> = Cell::new(false);
 }
 
@@ -94,6 +98,18 @@ pub fn with_crate_prefix<F: FnOnce() -> R, R>(f: F) -> R {
     })
 }
 
+/// Prevent path trimming if it is turned on. Path trimming affects `Display` impl
+/// of various rustc types, for example `std::vec::Vec` would be trimmed to `Vec`,
+/// if no other `Vec` is found.
+pub fn with_no_trimmed_paths<F: FnOnce() -> R, R>(f: F) -> R {
+    NO_TRIMMED_PATH.with(|flag| {
+        let old = flag.replace(true);
+        let result = f();
+        flag.set(old);
+        result
+    })
+}
+
 /// The "region highlights" are used to control region printing during
 /// specific error messages. When a "region highlight" is enabled, it
 /// gives an alternate way to print specific regions. For now, we
@@ -243,6 +259,28 @@ pub trait PrettyPrinter<'tcx>:
         self.try_print_visible_def_path_recur(def_id, &mut callers)
     }
 
+    /// Try to see if this path can be trimmed to a unique symbol name.
+    fn try_print_trimmed_def_path(
+        mut self,
+        def_id: DefId,
+    ) -> Result<(Self::Path, bool), Self::Error> {
+        if !self.tcx().sess.opts.debugging_opts.trim_diagnostic_paths
+            || matches!(self.tcx().sess.opts.trimmed_def_paths, TrimmedDefPaths::Never)
+            || NO_TRIMMED_PATH.with(|flag| flag.get())
+            || SHOULD_PREFIX_WITH_CRATE.with(|flag| flag.get())
+        {
+            return Ok((self, false));
+        }
+
+        match self.tcx().trimmed_def_paths(LOCAL_CRATE).get(&def_id) {
+            None => Ok((self, false)),
+            Some(symbol) => {
+                self.write_str(&symbol.as_str())?;
+                Ok((self, true))
+            }
+        }
+    }
+
     /// Does the work of `try_print_visible_def_path`, building the
     /// full definition path recursively before attempting to
     /// post-process it into the valid and visible version that
@@ -419,7 +457,7 @@ pub trait PrettyPrinter<'tcx>:
             // Inherent impls. Try to print `Foo::bar` for an inherent
             // impl on `Foo`, but fallback to `<Foo>::bar` if self-type is
             // anything other than a simple path.
-            match self_ty.kind {
+            match self_ty.kind() {
                 ty::Adt(..)
                 | ty::Foreign(_)
                 | ty::Bool
@@ -470,7 +508,7 @@ pub trait PrettyPrinter<'tcx>:
     fn pretty_print_type(mut self, ty: Ty<'tcx>) -> Result<Self::Type, Self::Error> {
         define_scoped_cx!(self);
 
-        match ty.kind {
+        match *ty.kind() {
             ty::Bool => p!(write("bool")),
             ty::Char => p!(write("char")),
             ty::Int(t) => p!(write("{}", t.name_str())),
@@ -759,7 +797,7 @@ pub trait PrettyPrinter<'tcx>:
             // Special-case `Fn(...) -> ...` and resugar it.
             let fn_trait_kind = self.tcx().fn_trait_kind_from_lang_item(principal.def_id);
             if !self.tcx().sess.verbose() && fn_trait_kind.is_some() {
-                if let ty::Tuple(ref args) = principal.substs.type_at(0).kind {
+                if let ty::Tuple(ref args) = principal.substs.type_at(0).kind() {
                     let mut projections = predicates.projection_bounds();
                     if let (Some(proj), None) = (projections.next(), projections.next()) {
                         let tys: Vec<_> = args.iter().map(|k| k.expect_ty()).collect();
@@ -938,7 +976,7 @@ pub trait PrettyPrinter<'tcx>:
     ) -> Result<Self::Const, Self::Error> {
         define_scoped_cx!(self);
 
-        match (scalar, &ty.kind) {
+        match (scalar, &ty.kind()) {
             // Byte strings (&[u8; N])
             (
                 Scalar::Ptr(ptr),
@@ -1098,7 +1136,7 @@ pub trait PrettyPrinter<'tcx>:
 
         let u8_type = self.tcx().types.u8;
 
-        match (ct, &ty.kind) {
+        match (ct, ty.kind()) {
             // Byte/string slices, printed as (byte) string literals.
             (
                 ConstValue::Slice { data, start, end },
@@ -1151,7 +1189,7 @@ pub trait PrettyPrinter<'tcx>:
                 );
                 let fields = contents.fields.iter().copied();
 
-                match ty.kind {
+                match *ty.kind() {
                     ty::Array(..) => {
                         p!(write("["), comma_sep(fields), write("]"));
                     }
@@ -1226,6 +1264,7 @@ pub struct FmtPrinterData<'a, 'tcx, F> {
     used_region_names: FxHashSet<Symbol>,
     region_index: usize,
     binder_depth: usize,
+    printed_type_count: usize,
 
     pub region_highlight_mode: RegionHighlightMode,
 
@@ -1256,6 +1295,7 @@ impl<F> FmtPrinter<'a, 'tcx, F> {
             used_region_names: Default::default(),
             region_index: 0,
             binder_depth: 0,
+            printed_type_count: 0,
             region_highlight_mode: RegionHighlightMode::default(),
             name_resolver: None,
         }))
@@ -1324,6 +1364,11 @@ impl<F: fmt::Write> Printer<'tcx> for FmtPrinter<'_, 'tcx, F> {
         define_scoped_cx!(self);
 
         if substs.is_empty() {
+            match self.try_print_trimmed_def_path(def_id)? {
+                (cx, true) => return Ok(cx),
+                (cx, false) => self = cx,
+            }
+
             match self.try_print_visible_def_path(def_id)? {
                 (cx, true) => return Ok(cx),
                 (cx, false) => self = cx,
@@ -1368,8 +1413,14 @@ impl<F: fmt::Write> Printer<'tcx> for FmtPrinter<'_, 'tcx, F> {
         self.pretty_print_region(region)
     }
 
-    fn print_type(self, ty: Ty<'tcx>) -> Result<Self::Type, Self::Error> {
-        self.pretty_print_type(ty)
+    fn print_type(mut self, ty: Ty<'tcx>) -> Result<Self::Type, Self::Error> {
+        if self.tcx.sess.type_length_limit().value_within_limit(self.printed_type_count) {
+            self.printed_type_count += 1;
+            self.pretty_print_type(ty)
+        } else {
+            write!(self, "...")?;
+            Ok(self)
+        }
     }
 
     fn print_dyn_existential(
@@ -2053,6 +2104,11 @@ define_print_and_forward_display! {
                 print(c2),
                 write("`"))
             }
+            ty::PredicateAtom::TypeWellFormedFromEnv(ty) => {
+                p!(write("the type `"),
+                print(ty),
+                write("` is found in the environment"))
+            }
         }
     }
 
@@ -2064,3 +2120,127 @@ define_print_and_forward_display! {
         }
     }
 }
+
+fn for_each_def(tcx: TyCtxt<'_>, mut collect_fn: impl for<'b> FnMut(&'b Ident, Namespace, DefId)) {
+    // Iterate all local crate items no matter where they are defined.
+    let hir = tcx.hir();
+    for item in hir.krate().items.values() {
+        if item.ident.name.as_str().is_empty() || matches!(item.kind, ItemKind::Use(_, _)) {
+            continue;
+        }
+
+        if let Some(local_def_id) = hir.definitions().opt_hir_id_to_local_def_id(item.hir_id) {
+            let def_id = local_def_id.to_def_id();
+            let ns = tcx.def_kind(def_id).ns().unwrap_or(Namespace::TypeNS);
+            collect_fn(&item.ident, ns, def_id);
+        }
+    }
+
+    // Now take care of extern crate items.
+    let queue = &mut Vec::new();
+    let mut seen_defs: DefIdSet = Default::default();
+
+    for &cnum in tcx.crates().iter() {
+        let def_id = DefId { krate: cnum, index: CRATE_DEF_INDEX };
+
+        // Ignore crates that are not direct dependencies.
+        match tcx.extern_crate(def_id) {
+            None => continue,
+            Some(extern_crate) => {
+                if !extern_crate.is_direct() {
+                    continue;
+                }
+            }
+        }
+
+        queue.push(def_id);
+    }
+
+    // Iterate external crate defs but be mindful about visibility
+    while let Some(def) = queue.pop() {
+        for child in tcx.item_children(def).iter() {
+            if child.vis != ty::Visibility::Public {
+                continue;
+            }
+
+            match child.res {
+                def::Res::Def(DefKind::AssocTy, _) => {}
+                def::Res::Def(defkind, def_id) => {
+                    if let Some(ns) = defkind.ns() {
+                        collect_fn(&child.ident, ns, def_id);
+                    }
+
+                    if seen_defs.insert(def_id) {
+                        queue.push(def_id);
+                    }
+                }
+                _ => {}
+            }
+        }
+    }
+}
+
+/// The purpose of this function is to collect public symbols names that are unique across all
+/// crates in the build. Later, when printing about types we can use those names instead of the
+/// full exported path to them.
+///
+/// So essentially, if a symbol name can only be imported from one place for a type, and as
+/// long as it was not glob-imported anywhere in the current crate, we can trim its printed
+/// path and print only the name.
+///
+/// This has wide implications on error messages with types, for example, shortening
+/// `std::vec::Vec` to just `Vec`, as long as there is no other `Vec` importable anywhere.
+///
+/// The implementation uses similar import discovery logic to that of 'use' suggestions.
+fn trimmed_def_paths(tcx: TyCtxt<'_>, crate_num: CrateNum) -> FxHashMap<DefId, Symbol> {
+    assert_eq!(crate_num, LOCAL_CRATE);
+
+    let mut map = FxHashMap::default();
+
+    if let TrimmedDefPaths::GoodPath = tcx.sess.opts.trimmed_def_paths {
+        // For good paths causing this bug, the `rustc_middle::ty::print::with_no_trimmed_paths`
+        // wrapper can be used to suppress this query, in exchange for full paths being formatted.
+        tcx.sess.delay_good_path_bug("trimmed_def_paths constructed");
+    }
+
+    let unique_symbols_rev: &mut FxHashMap<(Namespace, Symbol), Option<DefId>> =
+        &mut FxHashMap::default();
+
+    for symbol_set in tcx.glob_map.values() {
+        for symbol in symbol_set {
+            unique_symbols_rev.insert((Namespace::TypeNS, *symbol), None);
+            unique_symbols_rev.insert((Namespace::ValueNS, *symbol), None);
+            unique_symbols_rev.insert((Namespace::MacroNS, *symbol), None);
+        }
+    }
+
+    for_each_def(tcx, |ident, ns, def_id| {
+        use std::collections::hash_map::Entry::{Occupied, Vacant};
+
+        match unique_symbols_rev.entry((ns, ident.name)) {
+            Occupied(mut v) => match v.get() {
+                None => {}
+                Some(existing) => {
+                    if *existing != def_id {
+                        v.insert(None);
+                    }
+                }
+            },
+            Vacant(v) => {
+                v.insert(Some(def_id));
+            }
+        }
+    });
+
+    for ((_, symbol), opt_def_id) in unique_symbols_rev.drain() {
+        if let Some(def_id) = opt_def_id {
+            map.insert(def_id, symbol);
+        }
+    }
+
+    map
+}
+
+pub fn provide(providers: &mut ty::query::Providers) {
+    *providers = ty::query::Providers { trimmed_def_paths, ..*providers };
+}
diff --git a/compiler/rustc_middle/src/ty/query/keys.rs b/compiler/rustc_middle/src/ty/query/keys.rs
index 3f7a20bba2b..a005990264c 100644
--- a/compiler/rustc_middle/src/ty/query/keys.rs
+++ b/compiler/rustc_middle/src/ty/query/keys.rs
@@ -193,6 +193,22 @@ impl<'tcx> Key for (DefId, SubstsRef<'tcx>) {
     }
 }
 
+impl<'tcx> Key
+    for (
+        (ty::WithOptConstParam<DefId>, SubstsRef<'tcx>),
+        (ty::WithOptConstParam<DefId>, SubstsRef<'tcx>),
+    )
+{
+    type CacheSelector = DefaultCacheSelector;
+
+    fn query_crate(&self) -> CrateNum {
+        (self.0).0.did.krate
+    }
+    fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
+        (self.0).0.did.default_span(tcx)
+    }
+}
+
 impl<'tcx> Key for (LocalDefId, DefId, SubstsRef<'tcx>) {
     type CacheSelector = DefaultCacheSelector;
 
diff --git a/compiler/rustc_middle/src/ty/query/mod.rs b/compiler/rustc_middle/src/ty/query/mod.rs
index ee9b203b151..d3a7412ef14 100644
--- a/compiler/rustc_middle/src/ty/query/mod.rs
+++ b/compiler/rustc_middle/src/ty/query/mod.rs
@@ -14,7 +14,7 @@ use crate::middle::resolve_lifetime::{ObjectLifetimeDefault, Region, ResolveLife
 use crate::middle::stability::{self, DeprecationEntry};
 use crate::mir;
 use crate::mir::interpret::GlobalId;
-use crate::mir::interpret::{ConstEvalRawResult, ConstEvalResult, ConstValue};
+use crate::mir::interpret::{ConstValue, EvalToAllocationRawResult, EvalToConstValueResult};
 use crate::mir::interpret::{LitToConstError, LitToConstInput};
 use crate::mir::mono::CodegenUnit;
 use crate::traits::query::{
diff --git a/compiler/rustc_middle/src/ty/query/on_disk_cache.rs b/compiler/rustc_middle/src/ty/query/on_disk_cache.rs
index dcfb8d31430..b0c48a860eb 100644
--- a/compiler/rustc_middle/src/ty/query/on_disk_cache.rs
+++ b/compiler/rustc_middle/src/ty/query/on_disk_cache.rs
@@ -760,6 +760,12 @@ impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>>
     }
 }
 
+impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for &'tcx [mir::abstract_const::Node<'tcx>] {
+    fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Result<Self, String> {
+        RefDecodable::decode(d)
+    }
+}
+
 impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for &'tcx [(ty::Predicate<'tcx>, Span)] {
     fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Result<Self, String> {
         RefDecodable::decode(d)
diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs
index ae2820b460f..c4df0bba726 100644
--- a/compiler/rustc_middle/src/ty/relate.rs
+++ b/compiler/rustc_middle/src/ty/relate.rs
@@ -325,7 +325,7 @@ pub fn super_relate_tys<R: TypeRelation<'tcx>>(
 ) -> RelateResult<'tcx, Ty<'tcx>> {
     let tcx = relation.tcx();
     debug!("super_relate_tys: a={:?} b={:?}", a, b);
-    match (&a.kind, &b.kind) {
+    match (a.kind(), b.kind()) {
         (&ty::Infer(_), _) | (_, &ty::Infer(_)) => {
             // The caller should handle these cases!
             bug!("var types encountered in super_relate_tys")
@@ -516,7 +516,7 @@ pub fn super_relate_consts<R: TypeRelation<'tcx>>(
                 (ConstValue::Scalar(a_val), ConstValue::Scalar(b_val)) if a.ty == b.ty => {
                     if a_val == b_val {
                         Ok(ConstValue::Scalar(a_val))
-                    } else if let ty::FnPtr(_) = a.ty.kind {
+                    } else if let ty::FnPtr(_) = a.ty.kind() {
                         let a_instance = tcx.global_alloc(a_val.assert_ptr().alloc_id).unwrap_fn();
                         let b_instance = tcx.global_alloc(b_val.assert_ptr().alloc_id).unwrap_fn();
                         if a_instance == b_instance {
@@ -540,7 +540,7 @@ pub fn super_relate_consts<R: TypeRelation<'tcx>>(
                 }
 
                 (ConstValue::ByRef { .. }, ConstValue::ByRef { .. }) => {
-                    match a.ty.kind {
+                    match a.ty.kind() {
                         ty::Array(..) | ty::Adt(..) | ty::Tuple(..) => {
                             let a_destructured = tcx.destructure_const(relation.param_env().and(a));
                             let b_destructured = tcx.destructure_const(relation.param_env().and(b));
@@ -576,7 +576,20 @@ pub fn super_relate_consts<R: TypeRelation<'tcx>>(
             new_val.map(ty::ConstKind::Value)
         }
 
-        // FIXME(const_generics): this is wrong, as it is a projection
+        (
+            ty::ConstKind::Unevaluated(a_def, a_substs, None),
+            ty::ConstKind::Unevaluated(b_def, b_substs, None),
+        ) if tcx.features().const_evaluatable_checked => {
+            if tcx.try_unify_abstract_consts(((a_def, a_substs), (b_def, b_substs))) {
+                Ok(a.val)
+            } else {
+                Err(TypeError::ConstMismatch(expected_found(relation, a, b)))
+            }
+        }
+
+        // While this is slightly incorrect, it shouldn't matter for `min_const_generics`
+        // and is the better alternative to waiting until `const_evaluatable_checked` can
+        // be stabilized.
         (
             ty::ConstKind::Unevaluated(a_def, a_substs, a_promoted),
             ty::ConstKind::Unevaluated(b_def, b_substs, b_promoted),
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index 605e3545dea..597ceac9386 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -5,7 +5,7 @@
 use crate::mir::interpret;
 use crate::mir::ProjectionKind;
 use crate::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
-use crate::ty::print::{FmtPrinter, Printer};
+use crate::ty::print::{with_no_trimmed_paths, FmtPrinter, Printer};
 use crate::ty::{self, InferConst, Lift, Ty, TyCtxt};
 use rustc_hir as hir;
 use rustc_hir::def::Namespace;
@@ -20,7 +20,9 @@ use std::sync::Arc;
 impl fmt::Debug for ty::TraitDef {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         ty::tls::with(|tcx| {
-            FmtPrinter::new(tcx, f, Namespace::TypeNS).print_def_path(self.def_id, &[])?;
+            with_no_trimmed_paths(|| {
+                FmtPrinter::new(tcx, f, Namespace::TypeNS).print_def_path(self.def_id, &[])
+            })?;
             Ok(())
         })
     }
@@ -29,7 +31,9 @@ impl fmt::Debug for ty::TraitDef {
 impl fmt::Debug for ty::AdtDef {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         ty::tls::with(|tcx| {
-            FmtPrinter::new(tcx, f, Namespace::TypeNS).print_def_path(self.did, &[])?;
+            with_no_trimmed_paths(|| {
+                FmtPrinter::new(tcx, f, Namespace::TypeNS).print_def_path(self.did, &[])
+            })?;
             Ok(())
         })
     }
@@ -50,7 +54,7 @@ impl fmt::Debug for ty::UpvarBorrow<'tcx> {
 
 impl fmt::Debug for ty::ExistentialTraitRef<'tcx> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fmt::Display::fmt(self, f)
+        with_no_trimmed_paths(|| fmt::Display::fmt(self, f))
     }
 }
 
@@ -183,13 +187,13 @@ impl fmt::Debug for ty::FloatVarValue {
 
 impl fmt::Debug for ty::TraitRef<'tcx> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fmt::Display::fmt(self, f)
+        with_no_trimmed_paths(|| fmt::Display::fmt(self, f))
     }
 }
 
 impl fmt::Debug for Ty<'tcx> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fmt::Display::fmt(self, f)
+        with_no_trimmed_paths(|| fmt::Display::fmt(self, f))
     }
 }
 
@@ -256,6 +260,9 @@ impl fmt::Debug for ty::PredicateAtom<'tcx> {
                 write!(f, "ConstEvaluatable({:?}, {:?})", def_id, substs)
             }
             ty::PredicateAtom::ConstEquate(c1, c2) => write!(f, "ConstEquate({:?}, {:?})", c1, c2),
+            ty::PredicateAtom::TypeWellFormedFromEnv(ty) => {
+                write!(f, "TypeWellFormedFromEnv({:?})", ty)
+            }
         }
     }
 }
@@ -532,6 +539,9 @@ impl<'a, 'tcx> Lift<'tcx> for ty::PredicateAtom<'a> {
             ty::PredicateAtom::ConstEquate(c1, c2) => {
                 tcx.lift(&(c1, c2)).map(|(c1, c2)| ty::PredicateAtom::ConstEquate(c1, c2))
             }
+            ty::PredicateAtom::TypeWellFormedFromEnv(ty) => {
+                tcx.lift(&ty).map(ty::PredicateAtom::TypeWellFormedFromEnv)
+            }
         }
     }
 }
@@ -547,7 +557,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::ParamEnv<'a> {
     type Lifted = ty::ParamEnv<'tcx>;
     fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
         tcx.lift(&self.caller_bounds())
-            .map(|caller_bounds| ty::ParamEnv::new(caller_bounds, self.reveal(), self.def_id))
+            .map(|caller_bounds| ty::ParamEnv::new(caller_bounds, self.reveal()))
     }
 }
 
@@ -602,8 +612,11 @@ impl<'a, 'tcx> Lift<'tcx> for ty::adjustment::Adjust<'a> {
 impl<'a, 'tcx> Lift<'tcx> for ty::adjustment::OverloadedDeref<'a> {
     type Lifted = ty::adjustment::OverloadedDeref<'tcx>;
     fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
-        tcx.lift(&self.region)
-            .map(|region| ty::adjustment::OverloadedDeref { region, mutbl: self.mutbl })
+        tcx.lift(&self.region).map(|region| ty::adjustment::OverloadedDeref {
+            region,
+            mutbl: self.mutbl,
+            span: self.span,
+        })
     }
 }
 
@@ -676,6 +689,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> {
             Traits(x) => Traits(x),
             VariadicMismatch(x) => VariadicMismatch(x),
             CyclicTy(t) => return tcx.lift(&t).map(|t| CyclicTy(t)),
+            CyclicConst(ct) => return tcx.lift(&ct).map(|ct| CyclicConst(ct)),
             ProjectionMismatched(x) => ProjectionMismatched(x),
             Sorts(ref x) => return tcx.lift(x).map(Sorts),
             ExistentialMismatch(ref x) => return tcx.lift(x).map(ExistentialMismatch),
@@ -919,25 +933,25 @@ impl<'tcx> TypeFoldable<'tcx> for interpret::GlobalId<'tcx> {
 
 impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> {
     fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
-        let kind = match self.kind {
+        let kind = match self.kind() {
             ty::RawPtr(tm) => ty::RawPtr(tm.fold_with(folder)),
             ty::Array(typ, sz) => ty::Array(typ.fold_with(folder), sz.fold_with(folder)),
             ty::Slice(typ) => ty::Slice(typ.fold_with(folder)),
-            ty::Adt(tid, substs) => ty::Adt(tid, substs.fold_with(folder)),
+            ty::Adt(tid, substs) => ty::Adt(*tid, substs.fold_with(folder)),
             ty::Dynamic(ref trait_ty, ref region) => {
                 ty::Dynamic(trait_ty.fold_with(folder), region.fold_with(folder))
             }
             ty::Tuple(ts) => ty::Tuple(ts.fold_with(folder)),
-            ty::FnDef(def_id, substs) => ty::FnDef(def_id, substs.fold_with(folder)),
+            ty::FnDef(def_id, substs) => ty::FnDef(*def_id, substs.fold_with(folder)),
             ty::FnPtr(f) => ty::FnPtr(f.fold_with(folder)),
-            ty::Ref(ref r, ty, mutbl) => ty::Ref(r.fold_with(folder), ty.fold_with(folder), mutbl),
+            ty::Ref(ref r, ty, mutbl) => ty::Ref(r.fold_with(folder), ty.fold_with(folder), *mutbl),
             ty::Generator(did, substs, movability) => {
-                ty::Generator(did, substs.fold_with(folder), movability)
+                ty::Generator(*did, substs.fold_with(folder), *movability)
             }
             ty::GeneratorWitness(types) => ty::GeneratorWitness(types.fold_with(folder)),
-            ty::Closure(did, substs) => ty::Closure(did, substs.fold_with(folder)),
+            ty::Closure(did, substs) => ty::Closure(*did, substs.fold_with(folder)),
             ty::Projection(ref data) => ty::Projection(data.fold_with(folder)),
-            ty::Opaque(did, substs) => ty::Opaque(did, substs.fold_with(folder)),
+            ty::Opaque(did, substs) => ty::Opaque(*did, substs.fold_with(folder)),
 
             ty::Bool
             | ty::Char
@@ -954,7 +968,7 @@ impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> {
             | ty::Foreign(..) => return self,
         };
 
-        if self.kind == kind { self } else { folder.tcx().mk_ty(kind) }
+        if *self.kind() == kind { self } else { folder.tcx().mk_ty(kind) }
     }
 
     fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
@@ -962,7 +976,7 @@ impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> {
     }
 
     fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        match self.kind {
+        match self.kind() {
             ty::RawPtr(ref tm) => tm.visit_with(visitor),
             ty::Array(typ, sz) => typ.visit_with(visitor) || sz.visit_with(visitor),
             ty::Slice(typ) => typ.visit_with(visitor),
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index c1f354c7a15..724ec101b23 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -367,7 +367,8 @@ impl<'tcx> ClosureSubsts<'tcx> {
     /// Used primarily by `ty::print::pretty` to be able to handle closure
     /// types that haven't had their synthetic types substituted in.
     pub fn is_valid(self) -> bool {
-        self.substs.len() >= 3 && matches!(self.split().tupled_upvars_ty.expect_ty().kind, Tuple(_))
+        self.substs.len() >= 3
+            && matches!(self.split().tupled_upvars_ty.expect_ty().kind(), Tuple(_))
     }
 
     /// Returns the substitutions of the closure's parent.
@@ -414,9 +415,9 @@ impl<'tcx> ClosureSubsts<'tcx> {
     /// Extracts the signature from the closure.
     pub fn sig(self) -> ty::PolyFnSig<'tcx> {
         let ty = self.sig_as_fn_ptr_ty();
-        match ty.kind {
-            ty::FnPtr(sig) => sig,
-            _ => bug!("closure_sig_as_fn_ptr_ty is not a fn-ptr: {:?}", ty.kind),
+        match ty.kind() {
+            ty::FnPtr(sig) => *sig,
+            _ => bug!("closure_sig_as_fn_ptr_ty is not a fn-ptr: {:?}", ty.kind()),
         }
     }
 }
@@ -484,7 +485,8 @@ impl<'tcx> GeneratorSubsts<'tcx> {
     /// Used primarily by `ty::print::pretty` to be able to handle generator
     /// types that haven't had their synthetic types substituted in.
     pub fn is_valid(self) -> bool {
-        self.substs.len() >= 5 && matches!(self.split().tupled_upvars_ty.expect_ty().kind, Tuple(_))
+        self.substs.len() >= 5
+            && matches!(self.split().tupled_upvars_ty.expect_ty().kind(), Tuple(_))
     }
 
     /// Returns the substitutions of the generator's parent.
@@ -1231,13 +1233,13 @@ rustc_index::newtype_index! {
     /// particular, imagine a type like this:
     ///
     ///     for<'a> fn(for<'b> fn(&'b isize, &'a isize), &'a char)
-    ///     ^          ^            |        |         |
-    ///     |          |            |        |         |
-    ///     |          +------------+ 0      |         |
-    ///     |                                |         |
-    ///     +--------------------------------+ 1       |
-    ///     |                                          |
-    ///     +------------------------------------------+ 0
+    ///     ^          ^            |          |           |
+    ///     |          |            |          |           |
+    ///     |          +------------+ 0        |           |
+    ///     |                                  |           |
+    ///     +----------------------------------+ 1         |
+    ///     |                                              |
+    ///     +----------------------------------------------+ 0
     ///
     /// In this type, there are two binders (the outer fn and the inner
     /// fn). We need to be able to determine, for any given region, which
@@ -1741,9 +1743,19 @@ impl RegionKind {
 
 /// Type utilities
 impl<'tcx> TyS<'tcx> {
+    #[inline(always)]
+    pub fn kind(&self) -> &TyKind<'tcx> {
+        &self.kind
+    }
+
+    #[inline(always)]
+    pub fn flags(&self) -> TypeFlags {
+        self.flags
+    }
+
     #[inline]
     pub fn is_unit(&self) -> bool {
-        match self.kind {
+        match self.kind() {
             Tuple(ref tys) => tys.is_empty(),
             _ => false,
         }
@@ -1751,7 +1763,7 @@ impl<'tcx> TyS<'tcx> {
 
     #[inline]
     pub fn is_never(&self) -> bool {
-        match self.kind {
+        match self.kind() {
             Never => true,
             _ => false,
         }
@@ -1766,7 +1778,7 @@ impl<'tcx> TyS<'tcx> {
     pub fn conservative_is_privately_uninhabited(&self, tcx: TyCtxt<'tcx>) -> bool {
         // FIXME(varkor): we can make this less conversative by substituting concrete
         // type arguments.
-        match self.kind {
+        match self.kind() {
             ty::Never => true,
             ty::Adt(def, _) if def.is_union() => {
                 // For now, `union`s are never considered uninhabited.
@@ -1806,12 +1818,28 @@ impl<'tcx> TyS<'tcx> {
 
     #[inline]
     pub fn is_primitive(&self) -> bool {
-        self.kind.is_primitive()
+        self.kind().is_primitive()
+    }
+
+    #[inline]
+    pub fn is_adt(&self) -> bool {
+        match self.kind() {
+            Adt(..) => true,
+            _ => false,
+        }
+    }
+
+    #[inline]
+    pub fn is_ref(&self) -> bool {
+        match self.kind() {
+            Ref(..) => true,
+            _ => false,
+        }
     }
 
     #[inline]
     pub fn is_ty_var(&self) -> bool {
-        match self.kind {
+        match self.kind() {
             Infer(TyVar(_)) => true,
             _ => false,
         }
@@ -1819,7 +1847,7 @@ impl<'tcx> TyS<'tcx> {
 
     #[inline]
     pub fn is_ty_infer(&self) -> bool {
-        match self.kind {
+        match self.kind() {
             Infer(_) => true,
             _ => false,
         }
@@ -1827,23 +1855,23 @@ impl<'tcx> TyS<'tcx> {
 
     #[inline]
     pub fn is_phantom_data(&self) -> bool {
-        if let Adt(def, _) = self.kind { def.is_phantom_data() } else { false }
+        if let Adt(def, _) = self.kind() { def.is_phantom_data() } else { false }
     }
 
     #[inline]
     pub fn is_bool(&self) -> bool {
-        self.kind == Bool
+        *self.kind() == Bool
     }
 
     /// Returns `true` if this type is a `str`.
     #[inline]
     pub fn is_str(&self) -> bool {
-        self.kind == Str
+        *self.kind() == Str
     }
 
     #[inline]
     pub fn is_param(&self, index: u32) -> bool {
-        match self.kind {
+        match self.kind() {
             ty::Param(ref data) => data.index == index,
             _ => false,
         }
@@ -1851,8 +1879,8 @@ impl<'tcx> TyS<'tcx> {
 
     #[inline]
     pub fn is_slice(&self) -> bool {
-        match self.kind {
-            RawPtr(TypeAndMut { ty, .. }) | Ref(_, ty, _) => match ty.kind {
+        match self.kind() {
+            RawPtr(TypeAndMut { ty, .. }) | Ref(_, ty, _) => match ty.kind() {
                 Slice(_) | Str => true,
                 _ => false,
             },
@@ -1861,15 +1889,23 @@ impl<'tcx> TyS<'tcx> {
     }
 
     #[inline]
+    pub fn is_array(&self) -> bool {
+        match self.kind() {
+            Array(..) => true,
+            _ => false,
+        }
+    }
+
+    #[inline]
     pub fn is_simd(&self) -> bool {
-        match self.kind {
+        match self.kind() {
             Adt(def, _) => def.repr.simd(),
             _ => false,
         }
     }
 
     pub fn sequence_element_type(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
-        match self.kind {
+        match self.kind() {
             Array(ty, _) | Slice(ty) => ty,
             Str => tcx.mk_mach_uint(ast::UintTy::U8),
             _ => bug!("`sequence_element_type` called on non-sequence value: {}", self),
@@ -1877,7 +1913,7 @@ impl<'tcx> TyS<'tcx> {
     }
 
     pub fn simd_type(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
-        match self.kind {
+        match self.kind() {
             Adt(def, substs) => def.non_enum_variant().fields[0].ty(tcx, substs),
             _ => bug!("`simd_type` called on invalid type"),
         }
@@ -1886,14 +1922,14 @@ impl<'tcx> TyS<'tcx> {
     pub fn simd_size(&self, _tcx: TyCtxt<'tcx>) -> u64 {
         // Parameter currently unused, but probably needed in the future to
         // allow `#[repr(simd)] struct Simd<T, const N: usize>([T; N]);`.
-        match self.kind {
+        match self.kind() {
             Adt(def, _) => def.non_enum_variant().fields.len() as u64,
             _ => bug!("`simd_size` called on invalid type"),
         }
     }
 
     pub fn simd_size_and_type(&self, tcx: TyCtxt<'tcx>) -> (u64, Ty<'tcx>) {
-        match self.kind {
+        match self.kind() {
             Adt(def, substs) => {
                 let variant = def.non_enum_variant();
                 (variant.fields.len() as u64, variant.fields[0].ty(tcx, substs))
@@ -1904,7 +1940,7 @@ impl<'tcx> TyS<'tcx> {
 
     #[inline]
     pub fn is_region_ptr(&self) -> bool {
-        match self.kind {
+        match self.kind() {
             Ref(..) => true,
             _ => false,
         }
@@ -1912,7 +1948,7 @@ impl<'tcx> TyS<'tcx> {
 
     #[inline]
     pub fn is_mutable_ptr(&self) -> bool {
-        match self.kind {
+        match self.kind() {
             RawPtr(TypeAndMut { mutbl: hir::Mutability::Mut, .. })
             | Ref(_, _, hir::Mutability::Mut) => true,
             _ => false,
@@ -1921,7 +1957,7 @@ impl<'tcx> TyS<'tcx> {
 
     #[inline]
     pub fn is_unsafe_ptr(&self) -> bool {
-        match self.kind {
+        match self.kind() {
             RawPtr(_) => true,
             _ => false,
         }
@@ -1935,7 +1971,7 @@ impl<'tcx> TyS<'tcx> {
 
     #[inline]
     pub fn is_box(&self) -> bool {
-        match self.kind {
+        match self.kind() {
             Adt(def, _) => def.is_box(),
             _ => false,
         }
@@ -1943,7 +1979,7 @@ impl<'tcx> TyS<'tcx> {
 
     /// Panics if called on any type other than `Box<T>`.
     pub fn boxed_ty(&self) -> Ty<'tcx> {
-        match self.kind {
+        match self.kind() {
             Adt(def, substs) if def.is_box() => substs.type_at(0),
             _ => bug!("`boxed_ty` is called on non-box type {:?}", self),
         }
@@ -1954,7 +1990,7 @@ impl<'tcx> TyS<'tcx> {
     /// contents are abstract to rustc.)
     #[inline]
     pub fn is_scalar(&self) -> bool {
-        match self.kind {
+        match self.kind() {
             Bool
             | Char
             | Int(_)
@@ -1971,7 +2007,7 @@ impl<'tcx> TyS<'tcx> {
     /// Returns `true` if this type is a floating point type.
     #[inline]
     pub fn is_floating_point(&self) -> bool {
-        match self.kind {
+        match self.kind() {
             Float(_) | Infer(FloatVar(_)) => true,
             _ => false,
         }
@@ -1979,7 +2015,7 @@ impl<'tcx> TyS<'tcx> {
 
     #[inline]
     pub fn is_trait(&self) -> bool {
-        match self.kind {
+        match self.kind() {
             Dynamic(..) => true,
             _ => false,
         }
@@ -1987,7 +2023,7 @@ impl<'tcx> TyS<'tcx> {
 
     #[inline]
     pub fn is_enum(&self) -> bool {
-        match self.kind {
+        match self.kind() {
             Adt(adt_def, _) => adt_def.is_enum(),
             _ => false,
         }
@@ -1995,7 +2031,7 @@ impl<'tcx> TyS<'tcx> {
 
     #[inline]
     pub fn is_closure(&self) -> bool {
-        match self.kind {
+        match self.kind() {
             Closure(..) => true,
             _ => false,
         }
@@ -2003,7 +2039,7 @@ impl<'tcx> TyS<'tcx> {
 
     #[inline]
     pub fn is_generator(&self) -> bool {
-        match self.kind {
+        match self.kind() {
             Generator(..) => true,
             _ => false,
         }
@@ -2011,7 +2047,7 @@ impl<'tcx> TyS<'tcx> {
 
     #[inline]
     pub fn is_integral(&self) -> bool {
-        match self.kind {
+        match self.kind() {
             Infer(IntVar(_)) | Int(_) | Uint(_) => true,
             _ => false,
         }
@@ -2019,7 +2055,7 @@ impl<'tcx> TyS<'tcx> {
 
     #[inline]
     pub fn is_fresh_ty(&self) -> bool {
-        match self.kind {
+        match self.kind() {
             Infer(FreshTy(_)) => true,
             _ => false,
         }
@@ -2027,7 +2063,7 @@ impl<'tcx> TyS<'tcx> {
 
     #[inline]
     pub fn is_fresh(&self) -> bool {
-        match self.kind {
+        match self.kind() {
             Infer(FreshTy(_)) => true,
             Infer(FreshIntTy(_)) => true,
             Infer(FreshFloatTy(_)) => true,
@@ -2037,7 +2073,7 @@ impl<'tcx> TyS<'tcx> {
 
     #[inline]
     pub fn is_char(&self) -> bool {
-        match self.kind {
+        match self.kind() {
             Char => true,
             _ => false,
         }
@@ -2050,7 +2086,7 @@ impl<'tcx> TyS<'tcx> {
 
     #[inline]
     pub fn is_signed(&self) -> bool {
-        match self.kind {
+        match self.kind() {
             Int(_) => true,
             _ => false,
         }
@@ -2058,7 +2094,7 @@ impl<'tcx> TyS<'tcx> {
 
     #[inline]
     pub fn is_ptr_sized_integral(&self) -> bool {
-        match self.kind {
+        match self.kind() {
             Int(ast::IntTy::Isize) | Uint(ast::UintTy::Usize) => true,
             _ => false,
         }
@@ -2066,7 +2102,7 @@ impl<'tcx> TyS<'tcx> {
 
     #[inline]
     pub fn is_machine(&self) -> bool {
-        match self.kind {
+        match self.kind() {
             Int(..) | Uint(..) | Float(..) => true,
             _ => false,
         }
@@ -2074,7 +2110,7 @@ impl<'tcx> TyS<'tcx> {
 
     #[inline]
     pub fn has_concrete_skeleton(&self) -> bool {
-        match self.kind {
+        match self.kind() {
             Param(_) | Infer(_) | Error(_) => false,
             _ => true,
         }
@@ -2085,28 +2121,28 @@ impl<'tcx> TyS<'tcx> {
     /// The parameter `explicit` indicates if this is an *explicit* dereference.
     /// Some types -- notably unsafe ptrs -- can only be dereferenced explicitly.
     pub fn builtin_deref(&self, explicit: bool) -> Option<TypeAndMut<'tcx>> {
-        match self.kind {
+        match self.kind() {
             Adt(def, _) if def.is_box() => {
                 Some(TypeAndMut { ty: self.boxed_ty(), mutbl: hir::Mutability::Not })
             }
-            Ref(_, ty, mutbl) => Some(TypeAndMut { ty, mutbl }),
-            RawPtr(mt) if explicit => Some(mt),
+            Ref(_, ty, mutbl) => Some(TypeAndMut { ty, mutbl: *mutbl }),
+            RawPtr(mt) if explicit => Some(*mt),
             _ => None,
         }
     }
 
     /// Returns the type of `ty[i]`.
     pub fn builtin_index(&self) -> Option<Ty<'tcx>> {
-        match self.kind {
+        match self.kind() {
             Array(ty, _) | Slice(ty) => Some(ty),
             _ => None,
         }
     }
 
     pub fn fn_sig(&self, tcx: TyCtxt<'tcx>) -> PolyFnSig<'tcx> {
-        match self.kind {
-            FnDef(def_id, substs) => tcx.fn_sig(def_id).subst(tcx, substs),
-            FnPtr(f) => f,
+        match self.kind() {
+            FnDef(def_id, substs) => tcx.fn_sig(*def_id).subst(tcx, substs),
+            FnPtr(f) => *f,
             Error(_) => {
                 // ignore errors (#54954)
                 ty::Binder::dummy(FnSig::fake())
@@ -2120,7 +2156,7 @@ impl<'tcx> TyS<'tcx> {
 
     #[inline]
     pub fn is_fn(&self) -> bool {
-        match self.kind {
+        match self.kind() {
             FnDef(..) | FnPtr(_) => true,
             _ => false,
         }
@@ -2128,7 +2164,7 @@ impl<'tcx> TyS<'tcx> {
 
     #[inline]
     pub fn is_fn_ptr(&self) -> bool {
-        match self.kind {
+        match self.kind() {
             FnPtr(_) => true,
             _ => false,
         }
@@ -2136,7 +2172,7 @@ impl<'tcx> TyS<'tcx> {
 
     #[inline]
     pub fn is_impl_trait(&self) -> bool {
-        match self.kind {
+        match self.kind() {
             Opaque(..) => true,
             _ => false,
         }
@@ -2144,7 +2180,7 @@ impl<'tcx> TyS<'tcx> {
 
     #[inline]
     pub fn ty_adt_def(&self) -> Option<&'tcx AdtDef> {
-        match self.kind {
+        match self.kind() {
             Adt(adt, _) => Some(adt),
             _ => None,
         }
@@ -2153,7 +2189,7 @@ impl<'tcx> TyS<'tcx> {
     /// Iterates over tuple fields.
     /// Panics when called on anything but a tuple.
     pub fn tuple_fields(&self) -> impl DoubleEndedIterator<Item = Ty<'tcx>> {
-        match self.kind {
+        match self.kind() {
             Tuple(substs) => substs.iter().map(|field| field.expect_ty()),
             _ => bug!("tuple_fields called on non-tuple"),
         }
@@ -2164,10 +2200,10 @@ impl<'tcx> TyS<'tcx> {
     // FIXME: This requires the optimized MIR in the case of generators.
     #[inline]
     pub fn variant_range(&self, tcx: TyCtxt<'tcx>) -> Option<Range<VariantIdx>> {
-        match self.kind {
+        match self.kind() {
             TyKind::Adt(adt, _) => Some(adt.variant_range()),
             TyKind::Generator(def_id, substs, _) => {
-                Some(substs.as_generator().variant_range(def_id, tcx))
+                Some(substs.as_generator().variant_range(*def_id, tcx))
             }
             _ => None,
         }
@@ -2183,7 +2219,7 @@ impl<'tcx> TyS<'tcx> {
         tcx: TyCtxt<'tcx>,
         variant_index: VariantIdx,
     ) -> Option<Discr<'tcx>> {
-        match self.kind {
+        match self.kind() {
             TyKind::Adt(adt, _) if adt.variants.is_empty() => {
                 bug!("discriminant_for_variant called on zero variant enum");
             }
@@ -2191,7 +2227,7 @@ impl<'tcx> TyS<'tcx> {
                 Some(adt.discriminant_for_variant(tcx, variant_index))
             }
             TyKind::Generator(def_id, substs, _) => {
-                Some(substs.as_generator().discriminant_for_variant(def_id, tcx, variant_index))
+                Some(substs.as_generator().discriminant_for_variant(*def_id, tcx, variant_index))
             }
             _ => None,
         }
@@ -2199,7 +2235,7 @@ impl<'tcx> TyS<'tcx> {
 
     /// Returns the type of the discriminant of this type.
     pub fn discriminant_ty(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
-        match self.kind {
+        match self.kind() {
             ty::Adt(adt, _) if adt.is_enum() => adt.repr.discr_type().to_ty(tcx),
             ty::Generator(_, substs, _) => substs.as_generator().discr_ty(tcx),
             _ => {
@@ -2222,7 +2258,7 @@ impl<'tcx> TyS<'tcx> {
     /// inferred. Once upvar inference (in `src/librustc_typeck/check/upvar.rs`)
     /// is complete, that type variable will be unified.
     pub fn to_opt_closure_kind(&self) -> Option<ty::ClosureKind> {
-        match self.kind {
+        match self.kind() {
             Int(int_ty) => match int_ty {
                 ast::IntTy::I8 => Some(ty::ClosureKind::Fn),
                 ast::IntTy::I16 => Some(ty::ClosureKind::FnMut),
@@ -2244,8 +2280,14 @@ impl<'tcx> TyS<'tcx> {
     ///
     /// Returning true means the type is known to be sized. Returning
     /// `false` means nothing -- could be sized, might not be.
+    ///
+    /// Note that we could never rely on the fact that a type such as `[_]` is
+    /// trivially `!Sized` because we could be in a type environment with a
+    /// bound such as `[_]: Copy`. A function with such a bound obviously never
+    /// can be called, but that doesn't mean it shouldn't typecheck. This is why
+    /// this method doesn't return `Option<bool>`.
     pub fn is_trivially_sized(&self, tcx: TyCtxt<'tcx>) -> bool {
-        match self.kind {
+        match self.kind() {
             ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
             | ty::Uint(_)
             | ty::Int(_)
@@ -2280,9 +2322,4 @@ impl<'tcx> TyS<'tcx> {
             }
         }
     }
-
-    /// Is this a zero-sized type?
-    pub fn is_zst(&'tcx self, tcx: TyCtxt<'tcx>, did: DefId) -> bool {
-        tcx.layout_of(tcx.param_env(did).and(self)).map(|layout| layout.is_zst()).unwrap_or(false)
-    }
 }
diff --git a/compiler/rustc_middle/src/ty/subst.rs b/compiler/rustc_middle/src/ty/subst.rs
index acd58ab7f96..1bd3bcb6a4d 100644
--- a/compiler/rustc_middle/src/ty/subst.rs
+++ b/compiler/rustc_middle/src/ty/subst.rs
@@ -486,7 +486,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> {
             return t;
         }
 
-        match t.kind {
+        match *t.kind() {
             ty::Param(p) => self.ty_for_param(p, t),
             _ => t.super_fold_with(self),
         }
diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs
index 86fe3ac3751..9d5b558234b 100644
--- a/compiler/rustc_middle/src/ty/trait_def.rs
+++ b/compiler/rustc_middle/src/ty/trait_def.rs
@@ -167,7 +167,7 @@ impl<'tcx> TyCtxt<'tcx> {
         }
     }
 
-    /// Returns a vector containing all impls
+    /// Returns an iterator containing all impls
     pub fn all_impls(self, def_id: DefId) -> impl Iterator<Item = DefId> + 'tcx {
         let TraitImpls { blanket_impls, non_blanket_impls } = self.trait_impls_of(def_id);
 
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index 63d4dcca080..4127b6535bc 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -33,7 +33,7 @@ pub struct Discr<'tcx> {
 
 impl<'tcx> fmt::Display for Discr<'tcx> {
     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self.ty.kind {
+        match *self.ty.kind() {
             ty::Int(ity) => {
                 let size = ty::tls::with(|tcx| Integer::from_attr(&tcx, SignedInt(ity)).size());
                 let x = self.val;
@@ -59,7 +59,7 @@ fn unsigned_max(size: Size) -> u128 {
 }
 
 fn int_size_and_signed<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> (Size, bool) {
-    let (int, signed) = match ty.kind {
+    let (int, signed) = match *ty.kind() {
         Int(ity) => (Integer::from_attr(&tcx, SignedInt(ity)), true),
         Uint(uty) => (Integer::from_attr(&tcx, UnsignedInt(uty)), false),
         _ => bug!("non integer discriminant"),
@@ -170,14 +170,12 @@ impl<'tcx> TyCtxt<'tcx> {
         });
         hasher.finish()
     }
-}
 
-impl<'tcx> TyCtxt<'tcx> {
     pub fn has_error_field(self, ty: Ty<'tcx>) -> bool {
-        if let ty::Adt(def, substs) = ty.kind {
+        if let ty::Adt(def, substs) = *ty.kind() {
             for field in def.all_fields() {
                 let field_ty = field.ty(self, substs);
-                if let Error(_) = field_ty.kind {
+                if let Error(_) = field_ty.kind() {
                     return true;
                 }
             }
@@ -225,7 +223,7 @@ impl<'tcx> TyCtxt<'tcx> {
         normalize: impl Fn(Ty<'tcx>) -> Ty<'tcx>,
     ) -> Ty<'tcx> {
         loop {
-            match ty.kind {
+            match *ty.kind() {
                 ty::Adt(def, substs) => {
                     if !def.is_struct() {
                         break;
@@ -298,7 +296,7 @@ impl<'tcx> TyCtxt<'tcx> {
     ) -> (Ty<'tcx>, Ty<'tcx>) {
         let (mut a, mut b) = (source, target);
         loop {
-            match (&a.kind, &b.kind) {
+            match (&a.kind(), &b.kind()) {
                 (&Adt(a_def, a_substs), &Adt(b_def, b_substs))
                     if a_def == b_def && a_def.is_struct() =>
                 {
@@ -401,12 +399,12 @@ impl<'tcx> TyCtxt<'tcx> {
         // <P1, P2, P0>, and then look up which of the impl substs refer to
         // parameters marked as pure.
 
-        let impl_substs = match self.type_of(impl_def_id).kind {
+        let impl_substs = match *self.type_of(impl_def_id).kind() {
             ty::Adt(def_, substs) if def_ == def => substs,
             _ => bug!(),
         };
 
-        let item_substs = match self.type_of(def.did).kind {
+        let item_substs = match *self.type_of(def.did).kind() {
             ty::Adt(def_, substs) if def_ == def => substs,
             _ => bug!(),
         };
@@ -526,22 +524,22 @@ impl<'tcx> TyCtxt<'tcx> {
     }
 
     /// Returns `true` if the node pointed to by `def_id` is a `static` item.
-    pub fn is_static(&self, def_id: DefId) -> bool {
+    pub fn is_static(self, def_id: DefId) -> bool {
         self.static_mutability(def_id).is_some()
     }
 
     /// Returns `true` if this is a `static` item with the `#[thread_local]` attribute.
-    pub fn is_thread_local_static(&self, def_id: DefId) -> bool {
+    pub fn is_thread_local_static(self, def_id: DefId) -> bool {
         self.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::THREAD_LOCAL)
     }
 
     /// Returns `true` if the node pointed to by `def_id` is a mutable `static` item.
-    pub fn is_mutable_static(&self, def_id: DefId) -> bool {
+    pub fn is_mutable_static(self, def_id: DefId) -> bool {
         self.static_mutability(def_id) == Some(hir::Mutability::Mut)
     }
 
     /// Get the type of the pointer to the static that we use in MIR.
-    pub fn static_ptr_ty(&self, def_id: DefId) -> Ty<'tcx> {
+    pub fn static_ptr_ty(self, def_id: DefId) -> Ty<'tcx> {
         // Make sure that any constants in the static's type are evaluated.
         let static_ty = self.normalize_erasing_regions(ty::ParamEnv::empty(), self.type_of(def_id));
 
@@ -640,7 +638,7 @@ impl<'tcx> ty::TyS<'tcx> {
     /// Returns the maximum value for the given numeric type (including `char`s)
     /// or returns `None` if the type is not numeric.
     pub fn numeric_max_val(&'tcx self, tcx: TyCtxt<'tcx>) -> Option<&'tcx ty::Const<'tcx>> {
-        let val = match self.kind {
+        let val = match self.kind() {
             ty::Int(_) | ty::Uint(_) => {
                 let (size, signed) = int_size_and_signed(tcx, self);
                 let val = if signed { signed_max(size) as u128 } else { unsigned_max(size) };
@@ -659,7 +657,7 @@ impl<'tcx> ty::TyS<'tcx> {
     /// Returns the minimum value for the given numeric type (including `char`s)
     /// or returns `None` if the type is not numeric.
     pub fn numeric_min_val(&'tcx self, tcx: TyCtxt<'tcx>) -> Option<&'tcx ty::Const<'tcx>> {
-        let val = match self.kind {
+        let val = match self.kind() {
             ty::Int(_) | ty::Uint(_) => {
                 let (size, signed) = int_size_and_signed(tcx, self);
                 let val = if signed { truncate(signed_min(size) as u128, size) } else { 0 };
@@ -717,7 +715,7 @@ impl<'tcx> ty::TyS<'tcx> {
     /// Returning true means the type is known to be `Freeze`. Returning
     /// `false` means nothing -- could be `Freeze`, might not be.
     fn is_trivially_freeze(&self) -> bool {
-        match self.kind {
+        match self.kind() {
             ty::Int(_)
             | ty::Uint(_)
             | ty::Float(_)
@@ -793,7 +791,7 @@ impl<'tcx> ty::TyS<'tcx> {
     /// down, you will need to use a type visitor.
     #[inline]
     pub fn is_structural_eq_shallow(&'tcx self, tcx: TyCtxt<'tcx>) -> bool {
-        match self.kind {
+        match self.kind() {
             // Look for an impl of both `PartialStructuralEq` and `StructuralEq`.
             Adt(..) => tcx.has_structural_eq_impls(self),
 
@@ -828,7 +826,7 @@ impl<'tcx> ty::TyS<'tcx> {
     }
 
     pub fn same_type(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
-        match (&a.kind, &b.kind) {
+        match (&a.kind(), &b.kind()) {
             (&Adt(did_a, substs_a), &Adt(did_b, substs_b)) => {
                 if did_a != did_b {
                     return false;
@@ -860,7 +858,7 @@ impl<'tcx> ty::TyS<'tcx> {
             representable_cache: &mut FxHashMap<Ty<'tcx>, Representability>,
             ty: Ty<'tcx>,
         ) -> Representability {
-            match ty.kind {
+            match ty.kind() {
                 Tuple(..) => {
                     // Find non representable
                     fold_repr(ty.tuple_fields().map(|ty| {
@@ -909,7 +907,7 @@ impl<'tcx> ty::TyS<'tcx> {
         }
 
         fn same_struct_or_enum<'tcx>(ty: Ty<'tcx>, def: &'tcx ty::AdtDef) -> bool {
-            match ty.kind {
+            match *ty.kind() {
                 Adt(ty_def, _) => ty_def == def,
                 _ => false,
             }
@@ -947,7 +945,7 @@ impl<'tcx> ty::TyS<'tcx> {
             representable_cache: &mut FxHashMap<Ty<'tcx>, Representability>,
             ty: Ty<'tcx>,
         ) -> Representability {
-            match ty.kind {
+            match ty.kind() {
                 Adt(def, _) => {
                     {
                         // Iterate through stack of previously seen types.
@@ -962,7 +960,7 @@ impl<'tcx> ty::TyS<'tcx> {
                         // struct Bar<T> { x: Bar<Foo> }
 
                         if let Some(&seen_type) = iter.next() {
-                            if same_struct_or_enum(seen_type, def) {
+                            if same_struct_or_enum(seen_type, *def) {
                                 debug!("SelfRecursive: {:?} contains {:?}", seen_type, ty);
                                 return Representability::SelfRecursive(vec![sp]);
                             }
@@ -1024,7 +1022,7 @@ impl<'tcx> ty::TyS<'tcx> {
     /// - `&'a *const &'b u8 -> *const &'b u8`
     pub fn peel_refs(&'tcx self) -> Ty<'tcx> {
         let mut ty = self;
-        while let Ref(_, inner_ty, _) = ty.kind {
+        while let Ref(_, inner_ty, _) = ty.kind() {
             ty = inner_ty;
         }
         ty
@@ -1070,7 +1068,7 @@ impl<'tcx> ExplicitSelf<'tcx> {
     {
         use self::ExplicitSelf::*;
 
-        match self_arg_ty.kind {
+        match *self_arg_ty.kind() {
             _ if is_self_ty(self_arg_ty) => ByValue,
             ty::Ref(region, ty, mutbl) if is_self_ty(ty) => ByReference(region, mutbl),
             ty::RawPtr(ty::TypeAndMut { ty, mutbl }) if is_self_ty(ty) => ByRawPointer(mutbl),
@@ -1087,7 +1085,7 @@ pub fn needs_drop_components(
     ty: Ty<'tcx>,
     target_layout: &TargetDataLayout,
 ) -> Result<SmallVec<[Ty<'tcx>; 2]>, AlwaysRequiresDrop> {
-    match ty.kind {
+    match ty.kind() {
         ty::Infer(ty::FreshIntTy(_))
         | ty::Infer(ty::FreshFloatTy(_))
         | ty::Bool
diff --git a/compiler/rustc_middle/src/ty/walk.rs b/compiler/rustc_middle/src/ty/walk.rs
index 82c649b8f54..80ade7dda4c 100644
--- a/compiler/rustc_middle/src/ty/walk.rs
+++ b/compiler/rustc_middle/src/ty/walk.rs
@@ -3,6 +3,7 @@
 
 use crate::ty;
 use crate::ty::subst::{GenericArg, GenericArgKind};
+use rustc_data_structures::mini_set::MiniSet;
 use smallvec::{self, SmallVec};
 
 // The TypeWalker's stack is hot enough that it's worth going to some effort to
@@ -12,11 +13,20 @@ type TypeWalkerStack<'tcx> = SmallVec<[GenericArg<'tcx>; 8]>;
 pub struct TypeWalker<'tcx> {
     stack: TypeWalkerStack<'tcx>,
     last_subtree: usize,
+    visited: MiniSet<GenericArg<'tcx>>,
 }
 
+/// An iterator for walking the type tree.
+///
+/// It's very easy to produce a deeply
+/// nested type tree with a lot of
+/// identical subtrees. In order to work efficiently
+/// in this situation walker only visits each type once.
+/// It maintains a set of visited types and
+/// skips any types that are already there.
 impl<'tcx> TypeWalker<'tcx> {
-    pub fn new(root: GenericArg<'tcx>) -> TypeWalker<'tcx> {
-        TypeWalker { stack: smallvec![root], last_subtree: 1 }
+    pub fn new(root: GenericArg<'tcx>) -> Self {
+        Self { stack: smallvec![root], last_subtree: 1, visited: MiniSet::new() }
     }
 
     /// Skips the subtree corresponding to the last type
@@ -41,11 +51,15 @@ impl<'tcx> Iterator for TypeWalker<'tcx> {
 
     fn next(&mut self) -> Option<GenericArg<'tcx>> {
         debug!("next(): stack={:?}", self.stack);
-        let next = self.stack.pop()?;
-        self.last_subtree = self.stack.len();
-        push_inner(&mut self.stack, next);
-        debug!("next: stack={:?}", self.stack);
-        Some(next)
+        loop {
+            let next = self.stack.pop()?;
+            self.last_subtree = self.stack.len();
+            if self.visited.insert(next) {
+                push_inner(&mut self.stack, next);
+                debug!("next: stack={:?}", self.stack);
+                return Some(next);
+            }
+        }
     }
 }
 
@@ -55,7 +69,7 @@ impl GenericArg<'tcx> {
     /// that appear in `self`, it does not descend into the fields of
     /// structs or variants. For example:
     ///
-    /// ```notrust
+    /// ```text
     /// isize => { isize }
     /// Foo<Bar<isize>> => { Foo<Bar<isize>>, Bar<isize>, isize }
     /// [isize] => { [isize], isize }
@@ -67,9 +81,17 @@ impl GenericArg<'tcx> {
     /// Iterator that walks the immediate children of `self`. Hence
     /// `Foo<Bar<i32>, u32>` yields the sequence `[Bar<i32>, u32]`
     /// (but not `i32`, like `walk`).
-    pub fn walk_shallow(self) -> impl Iterator<Item = GenericArg<'tcx>> {
+    ///
+    /// Iterator only walks items once.
+    /// It accepts visited set, updates it with all visited types
+    /// and skips any types that are already there.
+    pub fn walk_shallow(
+        self,
+        visited: &mut MiniSet<GenericArg<'tcx>>,
+    ) -> impl Iterator<Item = GenericArg<'tcx>> {
         let mut stack = SmallVec::new();
         push_inner(&mut stack, self);
+        stack.retain(|a| visited.insert(*a));
         stack.into_iter()
     }
 }
@@ -80,7 +102,7 @@ impl<'tcx> super::TyS<'tcx> {
     /// that appear in `self`, it does not descend into the fields of
     /// structs or variants. For example:
     ///
-    /// ```notrust
+    /// ```text
     /// isize => { isize }
     /// Foo<Bar<isize>> => { Foo<Bar<isize>>, Bar<isize>, isize }
     /// [isize] => { [isize], isize }
@@ -98,7 +120,7 @@ impl<'tcx> super::TyS<'tcx> {
 // types as they are written).
 fn push_inner<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent: GenericArg<'tcx>) {
     match parent.unpack() {
-        GenericArgKind::Type(parent_ty) => match parent_ty.kind {
+        GenericArgKind::Type(parent_ty) => match *parent_ty.kind() {
             ty::Bool
             | ty::Char
             | ty::Int(_)
diff --git a/compiler/rustc_mir/Cargo.toml b/compiler/rustc_mir/Cargo.toml
index 6b0412ece7a..a6d22243d6d 100644
--- a/compiler/rustc_mir/Cargo.toml
+++ b/compiler/rustc_mir/Cargo.toml
@@ -10,10 +10,11 @@ doctest = false
 [dependencies]
 either = "1.5.0"
 rustc_graphviz = { path = "../rustc_graphviz" }
-itertools = "0.8"
+itertools = "0.9"
 tracing = "0.1"
 log_settings = "0.1.1"
 polonius-engine = "0.12.0"
+regex = "1"
 rustc_middle = { path = "../rustc_middle" }
 rustc_attr = { path = "../rustc_attr" }
 rustc_data_structures = { path = "../rustc_data_structures" }
diff --git a/compiler/rustc_mir/src/borrow_check/def_use.rs b/compiler/rustc_mir/src/borrow_check/def_use.rs
index 6574e584406..689ec249a2f 100644
--- a/compiler/rustc_mir/src/borrow_check/def_use.rs
+++ b/compiler/rustc_mir/src/borrow_check/def_use.rs
@@ -72,8 +72,7 @@ pub fn categorize(context: PlaceContext) -> Option<DefUse> {
         PlaceContext::MutatingUse(MutatingUseContext::Drop) =>
             Some(DefUse::Drop),
 
-        // Coverage and debug info are neither def nor use.
-        PlaceContext::NonUse(NonUseContext::Coverage) |
+        // Debug info is neither def nor use.
         PlaceContext::NonUse(NonUseContext::VarDebugInfo) => None,
     }
 }
diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs
index 9076dbccb52..11122b195c0 100644
--- a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs
@@ -66,7 +66,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         let span = use_spans.args_or_use();
 
         let move_site_vec = self.get_moved_indexes(location, mpi);
-        debug!("report_use_of_moved_or_uninitialized: move_site_vec={:?}", move_site_vec);
+        debug!(
+            "report_use_of_moved_or_uninitialized: move_site_vec={:?} use_spans={:?}",
+            move_site_vec, use_spans
+        );
         let move_out_indices: Vec<_> =
             move_site_vec.iter().map(|move_site| move_site.moi).collect();
 
@@ -229,6 +232,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                                     );
                                 }
                             }
+                            // Deref::deref takes &self, which cannot cause a move
+                            FnSelfUseKind::DerefCoercion { .. } => unreachable!(),
                         }
                     } else {
                         err.span_label(
@@ -291,7 +296,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             let ty =
                 Place::ty_from(used_place.local, used_place.projection, self.body, self.infcx.tcx)
                     .ty;
-            let needs_note = match ty.kind {
+            let needs_note = match ty.kind() {
                 ty::Closure(id, _) => {
                     let tables = self.infcx.tcx.typeck(id.expect_local());
                     let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(id.expect_local());
@@ -306,7 +311,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             let ty = place.ty(self.body, self.infcx.tcx).ty;
 
             if is_loop_move {
-                if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind {
+                if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() {
                     // We have a `&mut` ref, we need to reborrow on each iteration (#62112).
                     err.span_suggestion_verbose(
                         span.shrink_to_lo(),
@@ -329,7 +334,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                     Some(ref name) => format!("`{}`", name),
                     None => "value".to_owned(),
                 };
-                if let ty::Param(param_ty) = ty.kind {
+                if let ty::Param(param_ty) = ty.kind() {
                     let tcx = self.infcx.tcx;
                     let generics = tcx.generics_of(self.mir_def_id);
                     let param = generics.type_param(&param_ty, tcx);
@@ -355,6 +360,20 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 self.note_type_does_not_implement_copy(&mut err, &note_msg, ty, span, partial_str);
             }
 
+            if let UseSpans::FnSelfUse {
+                kind: FnSelfUseKind::DerefCoercion { deref_target, deref_target_ty },
+                ..
+            } = use_spans
+            {
+                err.note(&format!(
+                    "{} occurs due to deref coercion to `{}`",
+                    desired_action.as_noun(),
+                    deref_target_ty
+                ));
+
+                err.span_note(deref_target, "deref defined here");
+            }
+
             if let Some((_, mut old_err)) =
                 self.move_error_reported.insert(move_out_indices, (used_place, err))
             {
@@ -945,7 +964,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         name: &str,
         borrow: &BorrowData<'tcx>,
         drop_span: Span,
-        borrow_spans: UseSpans,
+        borrow_spans: UseSpans<'tcx>,
         explanation: BorrowExplanation,
     ) -> DiagnosticBuilder<'cx> {
         debug!(
@@ -997,7 +1016,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                         .opt_name(fn_hir_id)
                         .map(|name| format!("function `{}`", name))
                         .unwrap_or_else(|| {
-                            match &self.infcx.tcx.typeck(self.mir_def_id).node_type(fn_hir_id).kind
+                            match &self
+                                .infcx
+                                .tcx
+                                .typeck(self.mir_def_id)
+                                .node_type(fn_hir_id)
+                                .kind()
                             {
                                 ty::Closure(..) => "enclosing closure",
                                 ty::Generator(..) => "enclosing generator",
@@ -1141,7 +1165,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         location: Location,
         borrow: &BorrowData<'tcx>,
         drop_span: Span,
-        borrow_spans: UseSpans,
+        borrow_spans: UseSpans<'tcx>,
         proper_span: Span,
         explanation: BorrowExplanation,
     ) -> DiagnosticBuilder<'cx> {
@@ -1269,7 +1293,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
     fn report_escaping_closure_capture(
         &mut self,
-        use_span: UseSpans,
+        use_span: UseSpans<'tcx>,
         var_span: Span,
         fr_name: &RegionName,
         category: ConstraintCategory,
@@ -1625,7 +1649,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                     },
                     ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => {
                         let base_ty = Place::ty_from(place.local, proj_base, self.body, tcx).ty;
-                        match base_ty.kind {
+                        match base_ty.kind() {
                             ty::Adt(def, _) if def.has_dtor(tcx) => {
                                 // Report the outermost adt with a destructor
                                 match base_access {
@@ -1689,7 +1713,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 None
             } else {
                 let ty = self.infcx.tcx.type_of(self.mir_def_id);
-                match ty.kind {
+                match ty.kind() {
                     ty::FnDef(_, _) | ty::FnPtr(_) => self.annotate_fn_sig(
                         self.mir_def_id.to_def_id(),
                         self.infcx.tcx.fn_sig(self.mir_def_id),
@@ -1924,13 +1948,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         // 3. The return type is not a reference. In this case, we don't highlight
         //    anything.
         let return_ty = sig.output();
-        match return_ty.skip_binder().kind {
+        match return_ty.skip_binder().kind() {
             ty::Ref(return_region, _, _) if return_region.has_name() && !is_closure => {
                 // This is case 1 from above, return type is a named reference so we need to
                 // search for relevant arguments.
                 let mut arguments = Vec::new();
                 for (index, argument) in sig.inputs().skip_binder().iter().enumerate() {
-                    if let ty::Ref(argument_region, _, _) = argument.kind {
+                    if let ty::Ref(argument_region, _, _) = argument.kind() {
                         if argument_region == return_region {
                             // Need to use the `rustc_middle::ty` types to compare against the
                             // `return_region`. Then use the `rustc_hir` type to get only
@@ -1976,9 +2000,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
                 // Closure arguments are wrapped in a tuple, so we need to get the first
                 // from that.
-                if let ty::Tuple(elems) = argument_ty.kind {
+                if let ty::Tuple(elems) = argument_ty.kind() {
                     let argument_ty = elems.first()?.expect_ty();
-                    if let ty::Ref(_, _, _) = argument_ty.kind {
+                    if let ty::Ref(_, _, _) = argument_ty.kind() {
                         return Some(AnnotatedBorrowFnSignature::Closure {
                             argument_ty,
                             argument_span,
@@ -1998,7 +2022,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 let return_ty = sig.output().skip_binder();
 
                 // We expect the first argument to be a reference.
-                match argument_ty.kind {
+                match argument_ty.kind() {
                     ty::Ref(_, _, _) => {}
                     _ => return None,
                 }
diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs
index b591b938b5a..eccb6168229 100644
--- a/compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs
+++ b/compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs
@@ -102,7 +102,7 @@ impl BorrowExplanation {
                 should_note_order,
             } => {
                 let local_decl = &body.local_decls[dropped_local];
-                let (dtor_desc, type_desc) = match local_decl.ty.kind {
+                let (dtor_desc, type_desc) = match local_decl.ty.kind() {
                     // If type is an ADT that implements Drop, then
                     // simplify output by reporting just the ADT name.
                     ty::Adt(adt, _substs) if adt.has_dtor(tcx) && !adt.is_box() => {
@@ -501,7 +501,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
     fn later_use_kind(
         &self,
         borrow: &BorrowData<'tcx>,
-        use_spans: UseSpans,
+        use_spans: UseSpans<'tcx>,
         location: Location,
     ) -> (LaterUseKind, Span) {
         match use_spans {
@@ -626,7 +626,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                                         if from == target {
                                             debug!("was_captured_by_trait_object: ty={:?}", ty);
                                             // Check the type for a trait object.
-                                            return match ty.kind {
+                                            return match ty.kind() {
                                                 // `&dyn Trait`
                                                 ty::Ref(_, ty, _) if ty.is_trait() => true,
                                                 // `Box<dyn Trait>`
diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs
index dfaa75d9f23..4256f6e39d5 100644
--- a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs
+++ b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs
@@ -11,7 +11,7 @@ use rustc_middle::mir::{
     PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind,
 };
 use rustc_middle::ty::print::Print;
-use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt};
+use rustc_middle::ty::{self, DefIdTree, Instance, Ty, TyCtxt};
 use rustc_span::{
     hygiene::{DesugaringKind, ForLoopLoc},
     symbol::sym,
@@ -81,43 +81,41 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         let terminator = self.body[location.block].terminator();
         debug!("add_moved_or_invoked_closure_note: terminator={:?}", terminator);
         if let TerminatorKind::Call {
-            func:
-                Operand::Constant(box Constant {
-                    literal: ty::Const { ty: &ty::TyS { kind: ty::FnDef(id, _), .. }, .. },
-                    ..
-                }),
+            func: Operand::Constant(box Constant { literal: ty::Const { ty: const_ty, .. }, .. }),
             args,
             ..
         } = &terminator.kind
         {
-            debug!("add_moved_or_invoked_closure_note: id={:?}", id);
-            if self.infcx.tcx.parent(id) == self.infcx.tcx.lang_items().fn_once_trait() {
-                let closure = match args.first() {
-                    Some(Operand::Copy(ref place)) | Some(Operand::Move(ref place))
-                        if target == place.local_or_deref_local() =>
-                    {
-                        place.local_or_deref_local().unwrap()
-                    }
-                    _ => return,
-                };
+            if let ty::FnDef(id, _) = *const_ty.kind() {
+                debug!("add_moved_or_invoked_closure_note: id={:?}", id);
+                if self.infcx.tcx.parent(id) == self.infcx.tcx.lang_items().fn_once_trait() {
+                    let closure = match args.first() {
+                        Some(Operand::Copy(ref place)) | Some(Operand::Move(ref place))
+                            if target == place.local_or_deref_local() =>
+                        {
+                            place.local_or_deref_local().unwrap()
+                        }
+                        _ => return,
+                    };
 
-                debug!("add_moved_or_invoked_closure_note: closure={:?}", closure);
-                if let ty::Closure(did, _) = self.body.local_decls[closure].ty.kind {
-                    let did = did.expect_local();
-                    let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did);
+                    debug!("add_moved_or_invoked_closure_note: closure={:?}", closure);
+                    if let ty::Closure(did, _) = self.body.local_decls[closure].ty.kind() {
+                        let did = did.expect_local();
+                        let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did);
 
-                    if let Some((span, name)) =
-                        self.infcx.tcx.typeck(did).closure_kind_origins().get(hir_id)
-                    {
-                        diag.span_note(
-                            *span,
-                            &format!(
-                                "closure cannot be invoked more than once because it moves the \
-                                 variable `{}` out of its environment",
-                                name,
-                            ),
-                        );
-                        return;
+                        if let Some((span, name)) =
+                            self.infcx.tcx.typeck(did).closure_kind_origins().get(hir_id)
+                        {
+                            diag.span_note(
+                                *span,
+                                &format!(
+                                    "closure cannot be invoked more than once because it moves the \
+                                    variable `{}` out of its environment",
+                                    name,
+                                ),
+                            );
+                            return;
+                        }
                     }
                 }
             }
@@ -125,7 +123,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
         // Check if we are just moving a closure after it has been invoked.
         if let Some(target) = target {
-            if let ty::Closure(did, _) = self.body.local_decls[target].ty.kind {
+            if let ty::Closure(did, _) = self.body.local_decls[target].ty.kind() {
                 let did = did.expect_local();
                 let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did);
 
@@ -152,8 +150,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             Some(mut descr) => {
                 // Surround descr with `backticks`.
                 descr.reserve(2);
-                descr.insert_str(0, "`");
-                descr.push_str("`");
+                descr.insert(0, '`');
+                descr.push('`');
                 descr
             }
             None => "value".to_string(),
@@ -224,7 +222,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                             if self.upvars[var_index].by_ref {
                                 buf.push_str(&name);
                             } else {
-                                buf.push_str(&format!("*{}", &name));
+                                buf.push('*');
+                                buf.push_str(&name);
                             }
                         } else {
                             if autoderef {
@@ -236,7 +235,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                                     &including_downcast,
                                 )?;
                             } else {
-                                buf.push_str(&"*");
+                                buf.push('*');
                                 self.append_place_to_string(
                                     PlaceRef { local, projection: proj_base },
                                     buf,
@@ -274,7 +273,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                                 autoderef,
                                 &including_downcast,
                             )?;
-                            buf.push_str(&format!(".{}", field_name));
+                            buf.push('.');
+                            buf.push_str(&field_name);
                         }
                     }
                     ProjectionElem::Index(index) => {
@@ -286,11 +286,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                             autoderef,
                             &including_downcast,
                         )?;
-                        buf.push_str("[");
+                        buf.push('[');
                         if self.append_local_to_string(*index, buf).is_err() {
-                            buf.push_str("_");
+                            buf.push('_');
                         }
-                        buf.push_str("]");
+                        buf.push(']');
                     }
                     ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
                         autoderef = true;
@@ -303,7 +303,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                             autoderef,
                             &including_downcast,
                         )?;
-                        buf.push_str(&"[..]");
+                        buf.push_str("[..]");
                     }
                 };
             }
@@ -365,7 +365,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             // If the type is a box, the field is described from the boxed type
             self.describe_field_from_ty(&ty.boxed_ty(), field, variant_index)
         } else {
-            match ty.kind {
+            match *ty.kind() {
                 ty::Adt(def, _) => {
                     let variant = if let Some(idx) = variant_index {
                         assert!(def.is_enum());
@@ -496,7 +496,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         // We need to add synthesized lifetimes where appropriate. We do
         // this by hooking into the pretty printer and telling it to label the
         // lifetimes without names with the value `'0`.
-        match ty.kind {
+        match ty.kind() {
             ty::Ref(
                 ty::RegionKind::ReLateBound(_, br)
                 | ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }),
@@ -516,7 +516,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         let mut s = String::new();
         let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, &mut s, Namespace::TypeNS);
 
-        let region = match ty.kind {
+        let region = match ty.kind() {
             ty::Ref(region, _, _) => {
                 match region {
                     ty::RegionKind::ReLateBound(_, br)
@@ -538,7 +538,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
 /// The span(s) associated to a use of a place.
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
-pub(super) enum UseSpans {
+pub(super) enum UseSpans<'tcx> {
     /// The access is caused by capturing a variable for a closure.
     ClosureUse {
         /// This is true if the captured variable was from a generator.
@@ -558,7 +558,7 @@ pub(super) enum UseSpans {
         fn_call_span: Span,
         /// The definition span of the method being called
         fn_span: Span,
-        kind: FnSelfUseKind,
+        kind: FnSelfUseKind<'tcx>,
     },
     /// This access is caused by a `match` or `if let` pattern.
     PatUse(Span),
@@ -567,22 +567,32 @@ pub(super) enum UseSpans {
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
-pub(super) enum FnSelfUseKind {
+pub(super) enum FnSelfUseKind<'tcx> {
     /// A normal method call of the form `receiver.foo(a, b, c)`
     Normal { self_arg: Ident, implicit_into_iter: bool },
     /// A call to `FnOnce::call_once`, desugared from `my_closure(a, b, c)`
     FnOnceCall,
     /// A call to an operator trait, desuraged from operator syntax (e.g. `a << b`)
     Operator { self_arg: Ident },
+    DerefCoercion {
+        /// The `Span` of the `Target` associated type
+        /// in the `Deref` impl we are using.
+        deref_target: Span,
+        /// The type `T::Deref` we are dereferencing to
+        deref_target_ty: Ty<'tcx>,
+    },
 }
 
-impl UseSpans {
+impl UseSpans<'_> {
     pub(super) fn args_or_use(self) -> Span {
         match self {
             UseSpans::ClosureUse { args_span: span, .. }
             | UseSpans::PatUse(span)
-            | UseSpans::FnSelfUse { var_span: span, .. }
             | UseSpans::OtherUse(span) => span,
+            UseSpans::FnSelfUse {
+                fn_call_span, kind: FnSelfUseKind::DerefCoercion { .. }, ..
+            } => fn_call_span,
+            UseSpans::FnSelfUse { var_span, .. } => var_span,
         }
     }
 
@@ -590,8 +600,11 @@ impl UseSpans {
         match self {
             UseSpans::ClosureUse { var_span: span, .. }
             | UseSpans::PatUse(span)
-            | UseSpans::FnSelfUse { var_span: span, .. }
             | UseSpans::OtherUse(span) => span,
+            UseSpans::FnSelfUse {
+                fn_call_span, kind: FnSelfUseKind::DerefCoercion { .. }, ..
+            } => fn_call_span,
+            UseSpans::FnSelfUse { var_span, .. } => var_span,
         }
     }
 
@@ -650,7 +663,7 @@ impl UseSpans {
                     " in closure".to_string()
                 }
             }
-            _ => "".to_string(),
+            _ => String::new(),
         }
     }
 
@@ -680,7 +693,7 @@ impl BorrowedContentSource<'tcx> {
             BorrowedContentSource::DerefRawPointer => "a raw pointer".to_string(),
             BorrowedContentSource::DerefSharedRef => "a shared reference".to_string(),
             BorrowedContentSource::DerefMutableRef => "a mutable reference".to_string(),
-            BorrowedContentSource::OverloadedDeref(ty) => match ty.kind {
+            BorrowedContentSource::OverloadedDeref(ty) => match ty.kind() {
                 ty::Adt(def, _) if tcx.is_diagnostic_item(sym::Rc, def.did) => {
                     "an `Rc`".to_string()
                 }
@@ -712,7 +725,7 @@ impl BorrowedContentSource<'tcx> {
             BorrowedContentSource::DerefMutableRef => {
                 bug!("describe_for_immutable_place: DerefMutableRef isn't immutable")
             }
-            BorrowedContentSource::OverloadedDeref(ty) => match ty.kind {
+            BorrowedContentSource::OverloadedDeref(ty) => match ty.kind() {
                 ty::Adt(def, _) if tcx.is_diagnostic_item(sym::Rc, def.did) => {
                     "an `Rc`".to_string()
                 }
@@ -726,7 +739,7 @@ impl BorrowedContentSource<'tcx> {
     }
 
     fn from_call(func: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Option<Self> {
-        match func.kind {
+        match *func.kind() {
             ty::FnDef(def_id, substs) => {
                 let trait_id = tcx.trait_of_item(def_id)?;
 
@@ -754,7 +767,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         &self,
         moved_place: PlaceRef<'tcx>, // Could also be an upvar.
         location: Location,
-    ) -> UseSpans {
+    ) -> UseSpans<'tcx> {
         use self::UseSpans::*;
 
         let stmt = match self.body[location.block].statements.get(location.statement_index) {
@@ -806,68 +819,79 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         debug!("move_spans: target_temp = {:?}", target_temp);
 
         if let Some(Terminator {
-            kind: TerminatorKind::Call { func, args, fn_span, from_hir_call, .. },
-            ..
+            kind: TerminatorKind::Call { fn_span, from_hir_call, .. }, ..
         }) = &self.body[location.block].terminator
         {
-            let mut method_did = None;
-            if let Operand::Constant(box Constant { literal: ty::Const { ty, .. }, .. }) = func {
-                if let ty::FnDef(def_id, _) = ty.kind {
-                    debug!("move_spans: fn = {:?}", def_id);
-                    if let Some(ty::AssocItem { fn_has_self_parameter, .. }) =
-                        self.infcx.tcx.opt_associated_item(def_id)
-                    {
-                        if *fn_has_self_parameter {
-                            method_did = Some(def_id);
-                        }
-                    }
-                }
-            }
+            let (method_did, method_substs) = if let Some(info) =
+                crate::util::find_self_call(self.infcx.tcx, &self.body, target_temp, location.block)
+            {
+                info
+            } else {
+                return normal_ret;
+            };
 
             let tcx = self.infcx.tcx;
-            let method_did = if let Some(did) = method_did { did } else { return normal_ret };
-
-            if let [Operand::Move(self_place), ..] = **args {
-                if self_place.as_local() == Some(target_temp) {
-                    let parent = tcx.parent(method_did);
-                    let is_fn_once = parent == tcx.lang_items().fn_once_trait();
-                    let is_operator = !from_hir_call
-                        && parent.map_or(false, |p| {
-                            tcx.lang_items().group(LangItemGroup::Op).contains(&p)
-                        });
-                    let fn_call_span = *fn_span;
-
-                    let self_arg = tcx.fn_arg_names(method_did)[0];
-
-                    let kind = if is_fn_once {
-                        FnSelfUseKind::FnOnceCall
-                    } else if is_operator {
-                        FnSelfUseKind::Operator { self_arg }
-                    } else {
-                        debug!(
-                            "move_spans: method_did={:?}, fn_call_span={:?}",
-                            method_did, fn_call_span
-                        );
-                        let implicit_into_iter = matches!(
-                            fn_call_span.desugaring_kind(),
-                            Some(DesugaringKind::ForLoop(ForLoopLoc::IntoIter))
-                        );
-                        FnSelfUseKind::Normal { self_arg, implicit_into_iter }
-                    };
-
-                    return FnSelfUse {
-                        var_span: stmt.source_info.span,
-                        fn_call_span,
-                        fn_span: self
-                            .infcx
-                            .tcx
-                            .sess
-                            .source_map()
-                            .guess_head_span(self.infcx.tcx.def_span(method_did)),
-                        kind,
-                    };
+            let parent = tcx.parent(method_did);
+            let is_fn_once = parent == tcx.lang_items().fn_once_trait();
+            let is_operator = !from_hir_call
+                && parent.map_or(false, |p| tcx.lang_items().group(LangItemGroup::Op).contains(&p));
+            let is_deref = !from_hir_call && tcx.is_diagnostic_item(sym::deref_method, method_did);
+            let fn_call_span = *fn_span;
+
+            let self_arg = tcx.fn_arg_names(method_did)[0];
+
+            debug!(
+                "terminator = {:?} from_hir_call={:?}",
+                self.body[location.block].terminator, from_hir_call
+            );
+
+            // Check for a 'special' use of 'self' -
+            // an FnOnce call, an operator (e.g. `<<`), or a
+            // deref coercion.
+            let kind = if is_fn_once {
+                Some(FnSelfUseKind::FnOnceCall)
+            } else if is_operator {
+                Some(FnSelfUseKind::Operator { self_arg })
+            } else if is_deref {
+                let deref_target =
+                    tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
+                        Instance::resolve(tcx, self.param_env, deref_target, method_substs)
+                            .transpose()
+                    });
+                if let Some(Ok(instance)) = deref_target {
+                    let deref_target_ty = instance.ty(tcx, self.param_env);
+                    Some(FnSelfUseKind::DerefCoercion {
+                        deref_target: tcx.def_span(instance.def_id()),
+                        deref_target_ty,
+                    })
+                } else {
+                    None
                 }
-            }
+            } else {
+                None
+            };
+
+            let kind = kind.unwrap_or_else(|| {
+                // This isn't a 'special' use of `self`
+                debug!("move_spans: method_did={:?}, fn_call_span={:?}", method_did, fn_call_span);
+                let implicit_into_iter = matches!(
+                    fn_call_span.desugaring_kind(),
+                    Some(DesugaringKind::ForLoop(ForLoopLoc::IntoIter))
+                );
+                FnSelfUseKind::Normal { self_arg, implicit_into_iter }
+            });
+
+            return FnSelfUse {
+                var_span: stmt.source_info.span,
+                fn_call_span,
+                fn_span: self
+                    .infcx
+                    .tcx
+                    .sess
+                    .source_map()
+                    .guess_head_span(self.infcx.tcx.def_span(method_did)),
+                kind,
+            };
         }
         normal_ret
     }
@@ -876,7 +900,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
     /// and its usage of the local assigned at `location`.
     /// This is done by searching in statements succeeding `location`
     /// and originating from `maybe_closure_span`.
-    pub(super) fn borrow_spans(&self, use_span: Span, location: Location) -> UseSpans {
+    pub(super) fn borrow_spans(&self, use_span: Span, location: Location) -> UseSpans<'tcx> {
         use self::UseSpans::*;
         debug!("borrow_spans: use_span={:?} location={:?}", use_span, location);
 
@@ -980,7 +1004,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
     /// Helper to retrieve span(s) of given borrow from the current MIR
     /// representation
-    pub(super) fn retrieve_borrow_spans(&self, borrow: &BorrowData<'_>) -> UseSpans {
+    pub(super) fn retrieve_borrow_spans(&self, borrow: &BorrowData<'_>) -> UseSpans<'tcx> {
         let span = self.body.source_info(borrow.reserve_location).span;
         self.borrow_spans(span, borrow.reserve_location)
     }
diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs
index 1c8da212f10..629e9be9ddd 100644
--- a/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs
+++ b/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs
@@ -47,7 +47,7 @@ enum GroupedMoveError<'tcx> {
     // Everything that isn't from pattern matching.
     OtherIllegalMove {
         original_path: Place<'tcx>,
-        use_spans: UseSpans,
+        use_spans: UseSpans<'tcx>,
         kind: IllegalMoveOriginKind<'tcx>,
     },
 }
@@ -222,7 +222,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         let (mut err, err_span) = {
             let (span, use_spans, original_path, kind): (
                 Span,
-                Option<UseSpans>,
+                Option<UseSpans<'tcx>>,
                 Place<'tcx>,
                 &IllegalMoveOriginKind<'_>,
             ) = match error {
@@ -291,7 +291,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         move_place: Place<'tcx>,
         deref_target_place: Place<'tcx>,
         span: Span,
-        use_spans: Option<UseSpans>,
+        use_spans: Option<UseSpans<'tcx>>,
     ) -> DiagnosticBuilder<'a> {
         // Inspect the type of the content behind the
         // borrow to provide feedback about why this
@@ -326,7 +326,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         }
 
         debug!("report: ty={:?}", ty);
-        let mut err = match ty.kind {
+        let mut err = match ty.kind() {
             ty::Array(..) | ty::Slice(..) => {
                 self.cannot_move_out_of_interior_noncopy(span, ty, None)
             }
@@ -385,7 +385,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
             }
         };
         if let Ok(snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(span) {
-            let def_id = match move_place.ty(self.body, self.infcx.tcx).ty.kind {
+            let def_id = match *move_place.ty(self.body, self.infcx.tcx).ty.kind() {
                 ty::Adt(self_def, _) => self_def.did,
                 ty::Foreign(def_id)
                 | ty::FnDef(def_id, _)
@@ -492,8 +492,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
             {
                 if let Ok(pat_snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(pat_span)
                 {
-                    if pat_snippet.starts_with('&') {
-                        let pat_snippet = pat_snippet[1..].trim_start();
+                    if let Some(stripped) = pat_snippet.strip_prefix('&') {
+                        let pat_snippet = stripped.trim_start();
                         let (suggestion, to_remove) = if pat_snippet.starts_with("mut")
                             && pat_snippet["mut".len()..].starts_with(rustc_lexer::is_whitespace)
                         {
diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs
index d26436ff1de..d4cdf02104a 100644
--- a/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs
+++ b/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs
@@ -230,7 +230,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                                 // Otherwise, check if the name is the self kewyord - in which case
                                 // we have an explicit self. Do the same thing in this case and check
                                 // for a `self: &mut Self` to suggest removing the `&mut`.
-                                if let ty::Ref(_, _, hir::Mutability::Mut) = local_decl.ty.kind {
+                                if let ty::Ref(_, _, hir::Mutability::Mut) = local_decl.ty.kind() {
                                     true
                                 } else {
                                     false
@@ -509,7 +509,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
             let def_id = hir.local_def_id(item_id);
             let tables = self.infcx.tcx.typeck(def_id);
             if let Some(ty::FnDef(def_id, _)) =
-                tables.node_type_opt(func.hir_id).as_ref().map(|ty| &ty.kind)
+                tables.node_type_opt(func.hir_id).as_ref().map(|ty| ty.kind())
             {
                 let arg = match hir.get_if_local(*def_id) {
                     Some(
@@ -631,9 +631,8 @@ fn suggest_ampmut<'tcx>(
                 let lt_name = &src[1..ws_pos];
                 let ty = &src[ws_pos..];
                 return (assignment_rhs_span, format!("&{} mut {}", lt_name, ty));
-            } else if src.starts_with('&') {
-                let borrowed_expr = &src[1..];
-                return (assignment_rhs_span, format!("&mut {}", borrowed_expr));
+            } else if let Some(stripped) = src.strip_prefix('&') {
+                return (assignment_rhs_span, format!("&mut {}", stripped));
             }
         }
     }
@@ -687,8 +686,8 @@ fn annotate_struct_field(
     field: &mir::Field,
 ) -> Option<(Span, String)> {
     // Expect our local to be a reference to a struct of some kind.
-    if let ty::Ref(_, ty, _) = ty.kind {
-        if let ty::Adt(def, _) = ty.kind {
+    if let ty::Ref(_, ty, _) = ty.kind() {
+        if let ty::Adt(def, _) = ty.kind() {
             let field = def.all_fields().nth(field.index())?;
             // Use the HIR types to construct the diagnostic message.
             let hir_id = tcx.hir().local_def_id_to_hir_id(field.did.as_local()?);
diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/outlives_suggestion.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/outlives_suggestion.rs
index a775fa59c1b..7505e6e2dd1 100644
--- a/compiler/rustc_mir/src/borrow_check/diagnostics/outlives_suggestion.rs
+++ b/compiler/rustc_mir/src/borrow_check/diagnostics/outlives_suggestion.rs
@@ -115,9 +115,10 @@ impl OutlivesSuggestionBuilder {
             //    should just replace 'a with 'static.
             // 3) Suggest unifying 'a with 'b if we have both 'a: 'b and 'b: 'a
 
-            if outlived.iter().any(|(_, outlived_name)| {
-                if let RegionNameSource::Static = outlived_name.source { true } else { false }
-            }) {
+            if outlived
+                .iter()
+                .any(|(_, outlived_name)| matches!(outlived_name.source, RegionNameSource::Static))
+            {
                 suggested.push(SuggestedConstraint::Static(fr_name));
             } else {
                 // We want to isolate out all lifetimes that should be unified and print out
diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/region_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/region_errors.rs
index a0d99ac33c0..eb1f70099fc 100644
--- a/compiler/rustc_mir/src/borrow_check/diagnostics/region_errors.rs
+++ b/compiler/rustc_mir/src/borrow_check/diagnostics/region_errors.rs
@@ -364,13 +364,13 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
             .struct_span_err(*span, "captured variable cannot escape `FnMut` closure body");
 
         let mut output_ty = self.regioncx.universal_regions().unnormalized_output_ty;
-        if let ty::Opaque(def_id, _) = output_ty.kind {
+        if let ty::Opaque(def_id, _) = *output_ty.kind() {
             output_ty = self.infcx.tcx.type_of(def_id)
         };
 
         debug!("report_fnmut_error: output_ty={:?}", output_ty);
 
-        let message = match output_ty.kind {
+        let message = match output_ty.kind() {
             ty::Closure(_, _) => {
                 "returns a closure that contains a reference to a captured variable, which then \
                  escapes the closure body"
@@ -387,7 +387,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         if let ReturnConstraint::ClosureUpvar(upvar) = kind {
             let def_id = match self.regioncx.universal_regions().defining_ty {
                 DefiningTy::Closure(def_id, _) => def_id,
-                ty @ _ => bug!("unexpected DefiningTy {:?}", ty),
+                ty => bug!("unexpected DefiningTy {:?}", ty),
             };
 
             let upvar_def_span = self.infcx.tcx.hir().span(upvar);
@@ -571,13 +571,13 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         if let (Some(f), Some(ty::RegionKind::ReStatic)) =
             (self.to_error_region(fr), self.to_error_region(outlived_fr))
         {
-            if let Some((&ty::TyS { kind: ty::Opaque(did, substs), .. }, _)) = self
+            if let Some(&ty::Opaque(did, substs)) = self
                 .infcx
                 .tcx
                 .is_suitable_region(f)
                 .map(|r| r.def_id)
-                .map(|id| self.infcx.tcx.return_type_impl_trait(id))
-                .unwrap_or(None)
+                .and_then(|id| self.infcx.tcx.return_type_impl_trait(id))
+                .map(|(ty, _)| ty.kind())
             {
                 // Check whether or not the impl trait return type is intended to capture
                 // data with the static lifetime.
diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/region_name.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/region_name.rs
index 2603b1e048d..da7bc1564c0 100644
--- a/compiler/rustc_mir/src/borrow_check/diagnostics/region_name.rs
+++ b/compiler/rustc_mir/src/borrow_check/diagnostics/region_name.rs
@@ -441,7 +441,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
         let search_stack: &mut Vec<(Ty<'tcx>, &hir::Ty<'_>)> = &mut vec![(ty, hir_ty)];
 
         while let Some((ty, hir_ty)) = search_stack.pop() {
-            match (&ty.kind, &hir_ty.kind) {
+            match (&ty.kind(), &hir_ty.kind) {
                 // Check if the `ty` is `&'X ..` where `'X`
                 // is the region we are looking for -- if so, and we have a `&T`
                 // on the RHS, then we want to highlight the `&` like so:
diff --git a/compiler/rustc_mir/src/borrow_check/member_constraints.rs b/compiler/rustc_mir/src/borrow_check/member_constraints.rs
index d4baa5d809a..baaf6f27ee8 100644
--- a/compiler/rustc_mir/src/borrow_check/member_constraints.rs
+++ b/compiler/rustc_mir/src/borrow_check/member_constraints.rs
@@ -71,7 +71,7 @@ impl<'tcx> MemberConstraintSet<'tcx, ty::RegionVid> {
     /// Pushes a member constraint into the set.
     ///
     /// The input member constraint `m_c` is in the form produced by
-    /// the the `rustc_middle::infer` code.
+    /// the `rustc_middle::infer` code.
     ///
     /// The `to_region_vid` callback fn is used to convert the regions
     /// within into `RegionVid` format -- it typically consults the
diff --git a/compiler/rustc_mir/src/borrow_check/mod.rs b/compiler/rustc_mir/src/borrow_check/mod.rs
index 86908eaabd1..e4237482f47 100644
--- a/compiler/rustc_mir/src/borrow_check/mod.rs
+++ b/compiler/rustc_mir/src/borrow_check/mod.rs
@@ -17,7 +17,7 @@ use rustc_middle::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind
 use rustc_middle::mir::{Field, ProjectionElem, Promoted, Rvalue, Statement, StatementKind};
 use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind};
 use rustc_middle::ty::query::Providers;
-use rustc_middle::ty::{self, InstanceDef, RegionVid, TyCtxt};
+use rustc_middle::ty::{self, InstanceDef, ParamEnv, RegionVid, TyCtxt};
 use rustc_session::lint::builtin::{MUTABLE_BORROW_RESERVATION_CONFLICT, UNUSED_MUT};
 use rustc_span::{Span, Symbol, DUMMY_SP};
 
@@ -205,6 +205,7 @@ fn do_mir_borrowck<'a, 'tcx>(
 
     let mut flow_inits = MaybeInitializedPlaces::new(tcx, &body, &mdpe)
         .into_engine(tcx, &body, def.did.to_def_id())
+        .pass_name("borrowck")
         .iterate_to_fixpoint()
         .into_results_cursor(&body);
 
@@ -264,12 +265,15 @@ fn do_mir_borrowck<'a, 'tcx>(
 
     let flow_borrows = Borrows::new(tcx, &body, regioncx.clone(), &borrow_set)
         .into_engine(tcx, &body, def.did.to_def_id())
+        .pass_name("borrowck")
         .iterate_to_fixpoint();
     let flow_uninits = MaybeUninitializedPlaces::new(tcx, &body, &mdpe)
         .into_engine(tcx, &body, def.did.to_def_id())
+        .pass_name("borrowck")
         .iterate_to_fixpoint();
     let flow_ever_inits = EverInitializedPlaces::new(tcx, &body, &mdpe)
         .into_engine(tcx, &body, def.did.to_def_id())
+        .pass_name("borrowck")
         .iterate_to_fixpoint();
 
     let movable_generator = match tcx.hir().get(id) {
@@ -287,6 +291,7 @@ fn do_mir_borrowck<'a, 'tcx>(
         if let Err((move_data, move_errors)) = move_data_results {
             let mut promoted_mbcx = MirBorrowckCtxt {
                 infcx,
+                param_env,
                 body: promoted_body,
                 mir_def_id: def.did,
                 move_data: &move_data,
@@ -320,6 +325,7 @@ fn do_mir_borrowck<'a, 'tcx>(
 
     let mut mbcx = MirBorrowckCtxt {
         infcx,
+        param_env,
         body,
         mir_def_id: def.did,
         move_data: &mdpe.move_data,
@@ -473,6 +479,7 @@ fn do_mir_borrowck<'a, 'tcx>(
 
 crate struct MirBorrowckCtxt<'cx, 'tcx> {
     crate infcx: &'cx InferCtxt<'cx, 'tcx>,
+    param_env: ParamEnv<'tcx>,
     body: &'cx Body<'tcx>,
     mir_def_id: LocalDefId,
     move_data: &'cx MoveData<'tcx>,
@@ -1758,7 +1765,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         {
             let place_ty =
                 Place::ty_from(place_span.0.local, base_proj, self.body(), self.infcx.tcx);
-            if let ty::Array(..) = place_ty.ty.kind {
+            if let ty::Array(..) = place_ty.ty.kind() {
                 let array_place = PlaceRef { local: place_span.0.local, projection: base_proj };
                 self.check_if_subslice_element_is_moved(
                     location,
@@ -1876,7 +1883,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                     // be already initialized
                     let tcx = self.infcx.tcx;
                     let base_ty = Place::ty_from(place.local, proj_base, self.body(), tcx).ty;
-                    match base_ty.kind {
+                    match base_ty.kind() {
                         ty::Adt(def, _) if def.has_dtor(tcx) => {
                             self.check_if_path_or_subpath_is_moved(
                                 location, InitializationRequiringAction::Assignment,
@@ -1979,7 +1986,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 // of the union - we should error in that case.
                 let tcx = this.infcx.tcx;
                 if let ty::Adt(def, _) =
-                    Place::ty_from(base.local, base.projection, this.body(), tcx).ty.kind
+                    Place::ty_from(base.local, base.projection, this.body(), tcx).ty.kind()
                 {
                     if def.is_union() {
                         if this.move_data.path_map[mpi].iter().any(|moi| {
@@ -2206,7 +2213,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                             Place::ty_from(place.local, proj_base, self.body(), self.infcx.tcx).ty;
 
                         // Check the kind of deref to decide
-                        match base_ty.kind {
+                        match base_ty.kind() {
                             ty::Ref(_, _, mutbl) => {
                                 match mutbl {
                                     // Shared borrowed data is never mutable
diff --git a/compiler/rustc_mir/src/borrow_check/place_ext.rs b/compiler/rustc_mir/src/borrow_check/place_ext.rs
index cadf1ebf1b7..52fac3e53ee 100644
--- a/compiler/rustc_mir/src/borrow_check/place_ext.rs
+++ b/compiler/rustc_mir/src/borrow_check/place_ext.rs
@@ -49,7 +49,7 @@ impl<'tcx> PlaceExt<'tcx> for Place<'tcx> {
 
             if elem == ProjectionElem::Deref {
                 let ty = Place::ty_from(self.local, proj_base, body, tcx).ty;
-                match ty.kind {
+                match ty.kind() {
                     ty::Ref(_, _, hir::Mutability::Not) if i == 0 => {
                         // For references to thread-local statics, we do need
                         // to track the borrow.
diff --git a/compiler/rustc_mir/src/borrow_check/places_conflict.rs b/compiler/rustc_mir/src/borrow_check/places_conflict.rs
index 246e4826e0e..02c7b7dc200 100644
--- a/compiler/rustc_mir/src/borrow_check/places_conflict.rs
+++ b/compiler/rustc_mir/src/borrow_check/places_conflict.rs
@@ -210,7 +210,7 @@ fn place_components_conflict<'tcx>(
             let proj_base = &borrow_place.projection[..access_place.projection.len() + i];
             let base_ty = Place::ty_from(borrow_local, proj_base, body, tcx).ty;
 
-            match (elem, &base_ty.kind, access) {
+            match (elem, &base_ty.kind(), access) {
                 (_, _, Shallow(Some(ArtificialField::ArrayLength)))
                 | (_, _, Shallow(Some(ArtificialField::ShallowBorrow))) => {
                     // The array length is like  additional fields on the
@@ -330,7 +330,7 @@ fn place_projection_conflict<'tcx>(
                 Overlap::EqualOrDisjoint
             } else {
                 let ty = Place::ty_from(pi1_local, pi1_proj_base, body, tcx).ty;
-                match ty.kind {
+                match ty.kind() {
                     ty::Adt(def, _) if def.is_union() => {
                         // Different fields of a union, we are basically stuck.
                         debug!("place_element_conflict: STUCK-UNION");
diff --git a/compiler/rustc_mir/src/borrow_check/prefixes.rs b/compiler/rustc_mir/src/borrow_check/prefixes.rs
index a2475e0ff29..5bfe02ff3b0 100644
--- a/compiler/rustc_mir/src/borrow_check/prefixes.rs
+++ b/compiler/rustc_mir/src/borrow_check/prefixes.rs
@@ -121,7 +121,7 @@ impl<'cx, 'tcx> Iterator for Prefixes<'cx, 'tcx> {
                     // reference.
 
                     let ty = Place::ty_from(cursor.local, proj_base, self.body, self.tcx).ty;
-                    match ty.kind {
+                    match ty.kind() {
                         ty::RawPtr(_) | ty::Ref(_ /*rgn*/, _ /*ty*/, hir::Mutability::Not) => {
                             // don't continue traversing over derefs of raw pointers or shared
                             // borrows.
diff --git a/compiler/rustc_mir/src/borrow_check/region_infer/values.rs b/compiler/rustc_mir/src/borrow_check/region_infer/values.rs
index 8a5a600cfdd..f247d07e1f0 100644
--- a/compiler/rustc_mir/src/borrow_check/region_infer/values.rs
+++ b/compiler/rustc_mir/src/borrow_check/region_infer/values.rs
@@ -417,7 +417,7 @@ crate fn location_set_str(
 
 fn region_value_str(elements: impl IntoIterator<Item = RegionElement>) -> String {
     let mut result = String::new();
-    result.push_str("{");
+    result.push('{');
 
     // Set to Some(l1, l2) when we have observed all the locations
     // from l1..=l2 (inclusive) but not yet printed them. This
@@ -478,7 +478,7 @@ fn region_value_str(elements: impl IntoIterator<Item = RegionElement>) -> String
         push_location_range(&mut result, location1, location2);
     }
 
-    result.push_str("}");
+    result.push('}');
 
     return result;
 
diff --git a/compiler/rustc_mir/src/borrow_check/type_check/mod.rs b/compiler/rustc_mir/src/borrow_check/type_check/mod.rs
index 69c4f633770..3ace14610e2 100644
--- a/compiler/rustc_mir/src/borrow_check/type_check/mod.rs
+++ b/compiler/rustc_mir/src/borrow_check/type_check/mod.rs
@@ -386,7 +386,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
                 }
             }
 
-            if let ty::FnDef(def_id, substs) = constant.literal.ty.kind {
+            if let ty::FnDef(def_id, substs) = *constant.literal.ty.kind() {
                 let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, substs);
                 self.cx.normalize_and_prove_instantiated_predicates(
                     instantiated_predicates,
@@ -412,7 +412,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
                     // If we have a binding of the form `let ref x: T = ..`
                     // then remove the outermost reference so we can check the
                     // type annotation for the remaining type.
-                    if let ty::Ref(_, rty, _) = local_decl.ty.kind {
+                    if let ty::Ref(_, rty, _) = local_decl.ty.kind() {
                         rty
                     } else {
                         bug!("{:?} with ref binding has wrong type {}", local, local_decl.ty);
@@ -646,7 +646,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
                 }))
             }
             ProjectionElem::Subslice { from, to, from_end } => {
-                PlaceTy::from_ty(match base_ty.kind {
+                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)
@@ -658,7 +658,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
                     _ => span_mirbug_and_err!(self, place, "slice of non-array {:?}", base_ty),
                 })
             }
-            ProjectionElem::Downcast(maybe_name, index) => match base_ty.kind {
+            ProjectionElem::Downcast(maybe_name, index) => match base_ty.kind() {
                 ty::Adt(adt_def, _substs) if adt_def.is_enum() => {
                     if index.as_usize() >= adt_def.variants.len() {
                         PlaceTy::from_ty(span_mirbug_and_err!(
@@ -738,7 +738,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
         let tcx = self.tcx();
 
         let (variant, substs) = match base_ty {
-            PlaceTy { ty, variant_index: Some(variant_index) } => match ty.kind {
+            PlaceTy { ty, variant_index: Some(variant_index) } => match *ty.kind() {
                 ty::Adt(adt_def, substs) => (&adt_def.variants[variant_index], substs),
                 ty::Generator(def_id, substs, _) => {
                     let mut variants = substs.as_generator().state_tys(def_id, tcx);
@@ -757,7 +757,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
                 }
                 _ => bug!("can't have downcast of non-adt non-generator type"),
             },
-            PlaceTy { ty, variant_index: None } => match ty.kind {
+            PlaceTy { ty, variant_index: None } => match *ty.kind() {
                 ty::Adt(adt_def, substs) if !adt_def.is_enum() => {
                     (&adt_def.variants[VariantIdx::new(0)], substs)
                 }
@@ -1140,7 +1140,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         category: ConstraintCategory,
     ) -> Fallible<()> {
         if let Err(terr) = self.sub_types(sub, sup, locations, category) {
-            if let ty::Opaque(..) = sup.kind {
+            if let ty::Opaque(..) = sup.kind() {
                 // When you have `let x: impl Foo = ...` in a closure,
                 // the resulting inferend values are stored with the
                 // def-id of the base function.
@@ -1283,8 +1283,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
 
                     for (&opaque_def_id, opaque_decl) in &opaque_type_map {
                         let resolved_ty = infcx.resolve_vars_if_possible(&opaque_decl.concrete_ty);
-                        let concrete_is_opaque = if let ty::Opaque(def_id, _) = resolved_ty.kind {
-                            def_id == opaque_def_id
+                        let concrete_is_opaque = if let ty::Opaque(def_id, _) = resolved_ty.kind() {
+                            *def_id == opaque_def_id
                         } else {
                             false
                         };
@@ -1486,7 +1486,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
             }
             StatementKind::SetDiscriminant { ref place, variant_index } => {
                 let place_type = place.ty(body, tcx).ty;
-                let adt = match place_type.kind {
+                let adt = match place_type.kind() {
                     ty::Adt(adt, _) if adt.is_enum() => adt,
                     _ => {
                         span_bug!(
@@ -1602,7 +1602,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
             TerminatorKind::Call { ref func, ref args, ref destination, from_hir_call, .. } => {
                 let func_ty = func.ty(body, tcx);
                 debug!("check_terminator: call, func_ty={:?}", func_ty);
-                let sig = match func_ty.kind {
+                let sig = match func_ty.kind() {
                     ty::FnDef(..) | ty::FnPtr(_) => func_ty.fn_sig(tcx),
                     _ => {
                         span_mirbug!(self, term, "call to non-function {:?}", func_ty);
@@ -2093,7 +2093,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                     }
 
                     CastKind::Pointer(PointerCast::ClosureFnPointer(unsafety)) => {
-                        let sig = match op.ty(body, tcx).kind {
+                        let sig = match op.ty(body, tcx).kind() {
                             ty::Closure(_, substs) => substs.as_closure().sig(),
                             _ => bug!(),
                         };
@@ -2161,7 +2161,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                     }
 
                     CastKind::Pointer(PointerCast::MutToConstPointer) => {
-                        let ty_from = match op.ty(body, tcx).kind {
+                        let ty_from = match op.ty(body, tcx).kind() {
                             ty::RawPtr(ty::TypeAndMut {
                                 ty: ty_from,
                                 mutbl: hir::Mutability::Mut,
@@ -2176,7 +2176,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                                 return;
                             }
                         };
-                        let ty_to = match ty.kind {
+                        let ty_to = match ty.kind() {
                             ty::RawPtr(ty::TypeAndMut {
                                 ty: ty_to,
                                 mutbl: hir::Mutability::Not,
@@ -2211,11 +2211,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                     CastKind::Pointer(PointerCast::ArrayToPointer) => {
                         let ty_from = op.ty(body, tcx);
 
-                        let opt_ty_elem = match ty_from.kind {
+                        let opt_ty_elem = match ty_from.kind() {
                             ty::RawPtr(ty::TypeAndMut {
                                 mutbl: hir::Mutability::Not,
                                 ty: array_ty,
-                            }) => match array_ty.kind {
+                            }) => match array_ty.kind() {
                                 ty::Array(ty_elem, _) => Some(ty_elem),
                                 _ => None,
                             },
@@ -2235,7 +2235,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                             }
                         };
 
-                        let ty_to = match ty.kind {
+                        let ty_to = match ty.kind() {
                             ty::RawPtr(ty::TypeAndMut {
                                 mutbl: hir::Mutability::Not,
                                 ty: ty_to,
@@ -2301,7 +2301,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                 right,
             ) => {
                 let ty_left = left.ty(body, tcx);
-                match ty_left.kind {
+                match ty_left.kind() {
                     // Types with regions are comparable if they have a common super-type.
                     ty::RawPtr(_) | ty::FnPtr(_) => {
                         let ty_right = right.ty(body, tcx);
@@ -2512,7 +2512,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                     let base_ty = Place::ty_from(borrowed_place.local, proj_base, body, tcx).ty;
 
                     debug!("add_reborrow_constraint - base_ty = {:?}", base_ty);
-                    match base_ty.kind {
+                    match base_ty.kind() {
                         ty::Ref(ref_region, _, mutbl) => {
                             constraints.outlives_constraints.push(OutlivesConstraint {
                                 sup: ref_region.to_region_vid(),
diff --git a/compiler/rustc_mir/src/borrow_check/universal_regions.rs b/compiler/rustc_mir/src/borrow_check/universal_regions.rs
index 9dfc67bcf67..4742113b1a5 100644
--- a/compiler/rustc_mir/src/borrow_check/universal_regions.rs
+++ b/compiler/rustc_mir/src/borrow_check/universal_regions.rs
@@ -524,7 +524,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
                 let defining_ty =
                     self.infcx.replace_free_regions_with_nll_infer_vars(FR, &defining_ty);
 
-                match defining_ty.kind {
+                match *defining_ty.kind() {
                     ty::Closure(def_id, substs) => DefiningTy::Closure(def_id, substs),
                     ty::Generator(def_id, substs, movability) => {
                         DefiningTy::Generator(def_id, substs, movability)
@@ -603,7 +603,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
                     // flattens this tuple.
                     let (&output, tuplized_inputs) = inputs_and_output.split_last().unwrap();
                     assert_eq!(tuplized_inputs.len(), 1, "multiple closure inputs");
-                    let inputs = match tuplized_inputs[0].kind {
+                    let inputs = match tuplized_inputs[0].kind() {
                         ty::Tuple(inputs) => inputs,
                         _ => bug!("closure inputs not a tuple: {:?}", tuplized_inputs[0]),
                     };
diff --git a/compiler/rustc_mir/src/const_eval/eval_queries.rs b/compiler/rustc_mir/src/const_eval/eval_queries.rs
index 291b42c12d7..a0ee7fdc072 100644
--- a/compiler/rustc_mir/src/const_eval/eval_queries.rs
+++ b/compiler/rustc_mir/src/const_eval/eval_queries.rs
@@ -1,8 +1,8 @@
 use super::{CompileTimeEvalContext, CompileTimeInterpreter, ConstEvalErr, MemoryExtra};
 use crate::interpret::eval_nullary_intrinsic;
 use crate::interpret::{
-    intern_const_alloc_recursive, Allocation, ConstValue, GlobalId, Immediate, InternKind,
-    InterpCx, InterpResult, MPlaceTy, MemoryKind, OpTy, RawConst, RefTracking, Scalar,
+    intern_const_alloc_recursive, Allocation, ConstAlloc, ConstValue, GlobalId, Immediate,
+    InternKind, InterpCx, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking, Scalar,
     ScalarMaybeUninit, StackPopCleanup,
 };
 
@@ -10,6 +10,7 @@ use rustc_hir::def::DefKind;
 use rustc_middle::mir;
 use rustc_middle::mir::interpret::ErrorHandled;
 use rustc_middle::traits::Reveal;
+use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{self, subst::Subst, TyCtxt};
 use rustc_span::source_map::Span;
 use rustc_target::abi::{Abi, LayoutOf};
@@ -33,7 +34,8 @@ fn eval_body_using_ecx<'mir, 'tcx>(
     assert!(!layout.is_unsized());
     let ret = ecx.allocate(layout, MemoryKind::Stack);
 
-    let name = ty::tls::with(|tcx| tcx.def_path_str(cid.instance.def_id()));
+    let name =
+        with_no_trimmed_paths(|| ty::tls::with(|tcx| tcx.def_path_str(cid.instance.def_id())));
     let prom = cid.promoted.map_or(String::new(), |p| format!("::promoted[{:?}]", p));
     trace!("eval_body_using_ecx: pushing stack frame for global: {}{}", name, prom);
 
@@ -102,6 +104,8 @@ pub(super) fn mk_eval_cx<'mir, 'tcx>(
     )
 }
 
+/// This function converts an interpreter value into a constant that is meant for use in the
+/// type system.
 pub(super) fn op_to_const<'tcx>(
     ecx: &CompileTimeEvalContext<'_, 'tcx>,
     op: OpTy<'tcx>,
@@ -115,8 +119,8 @@ pub(super) fn op_to_const<'tcx>(
     // `Undef` situation.
     let try_as_immediate = match op.layout.abi {
         Abi::Scalar(..) => true,
-        Abi::ScalarPair(..) => match op.layout.ty.kind {
-            ty::Ref(_, inner, _) => match inner.kind {
+        Abi::ScalarPair(..) => match op.layout.ty.kind() {
+            ty::Ref(_, inner, _) => match *inner.kind() {
                 ty::Slice(elem) => elem == ecx.tcx.types.u8,
                 ty::Str => true,
                 _ => false,
@@ -180,63 +184,37 @@ pub(super) fn op_to_const<'tcx>(
     }
 }
 
-fn validate_and_turn_into_const<'tcx>(
+fn turn_into_const_value<'tcx>(
     tcx: TyCtxt<'tcx>,
-    constant: RawConst<'tcx>,
+    constant: ConstAlloc<'tcx>,
     key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>,
-) -> ::rustc_middle::mir::interpret::ConstEvalResult<'tcx> {
+) -> ConstValue<'tcx> {
     let cid = key.value;
     let def_id = cid.instance.def.def_id();
     let is_static = tcx.is_static(def_id);
     let ecx = mk_eval_cx(tcx, tcx.def_span(key.value.instance.def_id()), key.param_env, is_static);
-    let val = (|| {
-        let mplace = ecx.raw_const_to_mplace(constant)?;
 
-        // FIXME do not validate promoteds until a decision on
-        // https://github.com/rust-lang/rust/issues/67465 is made
-        if cid.promoted.is_none() {
-            let mut ref_tracking = RefTracking::new(mplace);
-            while let Some((mplace, path)) = ref_tracking.todo.pop() {
-                ecx.const_validate_operand(
-                    mplace.into(),
-                    path,
-                    &mut ref_tracking,
-                    /*may_ref_to_static*/ ecx.memory.extra.can_access_statics,
-                )?;
-            }
-        }
-        // Now that we validated, turn this into a proper constant.
-        // Statics/promoteds are always `ByRef`, for the rest `op_to_const` decides
-        // whether they become immediates.
-        if is_static || cid.promoted.is_some() {
-            let ptr = mplace.ptr.assert_ptr();
-            Ok(ConstValue::ByRef {
-                alloc: ecx.tcx.global_alloc(ptr.alloc_id).unwrap_memory(),
-                offset: ptr.offset,
-            })
-        } else {
-            Ok(op_to_const(&ecx, mplace.into()))
-        }
-    })();
-
-    val.map_err(|error| {
-        let err = ConstEvalErr::new(&ecx, error, None);
-        err.struct_error(ecx.tcx, "it is undefined behavior to use this value", |mut diag| {
-            diag.note(note_on_undefined_behavior_error());
-            diag.emit();
-        })
-    })
+    let mplace = ecx.raw_const_to_mplace(constant).expect(
+        "can only fail if layout computation failed, \
+        which should have given a good error before ever invoking this function",
+    );
+    assert!(
+        !is_static || cid.promoted.is_some(),
+        "the `eval_to_const_value_raw` query should not be used for statics, use `eval_to_allocation` instead"
+    );
+    // Turn this into a proper constant.
+    op_to_const(&ecx, mplace.into())
 }
 
-pub fn const_eval_validated_provider<'tcx>(
+pub fn eval_to_const_value_raw_provider<'tcx>(
     tcx: TyCtxt<'tcx>,
     key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>,
-) -> ::rustc_middle::mir::interpret::ConstEvalResult<'tcx> {
+) -> ::rustc_middle::mir::interpret::EvalToConstValueResult<'tcx> {
     // see comment in const_eval_raw_provider for what we're doing here
     if key.param_env.reveal() == Reveal::All {
         let mut key = key;
         key.param_env = key.param_env.with_user_facing();
-        match tcx.const_eval_validated(key) {
+        match tcx.eval_to_const_value_raw(key) {
             // try again with reveal all as requested
             Err(ErrorHandled::TooGeneric) => {}
             // deduplicate calls
@@ -248,7 +226,7 @@ pub fn const_eval_validated_provider<'tcx>(
     // Catch such calls and evaluate them instead of trying to load a constant's MIR.
     if let ty::InstanceDef::Intrinsic(def_id) = key.value.instance.def {
         let ty = key.value.instance.ty(tcx, key.param_env);
-        let substs = match ty.kind {
+        let substs = match ty.kind() {
             ty::FnDef(_, substs) => substs,
             _ => bug!("intrinsic with type {:?}", ty),
         };
@@ -259,13 +237,13 @@ pub fn const_eval_validated_provider<'tcx>(
         });
     }
 
-    tcx.const_eval_raw(key).and_then(|val| validate_and_turn_into_const(tcx, val, key))
+    tcx.eval_to_allocation_raw(key).map(|val| turn_into_const_value(tcx, val, key))
 }
 
-pub fn const_eval_raw_provider<'tcx>(
+pub fn eval_to_allocation_raw_provider<'tcx>(
     tcx: TyCtxt<'tcx>,
     key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>,
-) -> ::rustc_middle::mir::interpret::ConstEvalRawResult<'tcx> {
+) -> ::rustc_middle::mir::interpret::EvalToAllocationRawResult<'tcx> {
     // Because the constant is computed twice (once per value of `Reveal`), we are at risk of
     // reporting the same error twice here. To resolve this, we check whether we can evaluate the
     // constant in the more restrictive `Reveal::UserFacing`, which most likely already was
@@ -277,7 +255,7 @@ pub fn const_eval_raw_provider<'tcx>(
     if key.param_env.reveal() == Reveal::All {
         let mut key = key;
         key.param_env = key.param_env.with_user_facing();
-        match tcx.const_eval_raw(key) {
+        match tcx.eval_to_allocation_raw(key) {
             // try again with reveal all as requested
             Err(ErrorHandled::TooGeneric) => {}
             // deduplicate calls
@@ -290,7 +268,7 @@ pub fn const_eval_raw_provider<'tcx>(
         // The next two lines concatenated contain some discussion:
         // https://rust-lang.zulipchat.com/#narrow/stream/146212-t-compiler.2Fconst-eval/
         // subject/anon_const_instance_printing/near/135980032
-        let instance = key.value.instance.to_string();
+        let instance = with_no_trimmed_paths(|| key.value.instance.to_string());
         trace!("const eval: {:?} ({})", key, instance);
     }
 
@@ -316,9 +294,8 @@ pub fn const_eval_raw_provider<'tcx>(
     );
 
     let res = ecx.load_mir(cid.instance.def, cid.promoted);
-    res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, &body))
-        .map(|place| RawConst { alloc_id: place.ptr.assert_ptr().alloc_id, ty: place.layout.ty })
-        .map_err(|error| {
+    match res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, &body)) {
+        Err(error) => {
             let err = ConstEvalErr::new(&ecx, error, None);
             // errors in statics are always emitted as fatal errors
             if is_static {
@@ -340,7 +317,7 @@ pub fn const_eval_raw_provider<'tcx>(
                     );
                 }
 
-                v
+                Err(v)
             } else if let Some(def) = def.as_local() {
                 // constant defined in this crate, we can figure out a lint level!
                 match tcx.def_kind(def.did.to_def_id()) {
@@ -354,12 +331,12 @@ pub fn const_eval_raw_provider<'tcx>(
                     // compatibility hazard
                     DefKind::Const | DefKind::AssocConst => {
                         let hir_id = tcx.hir().local_def_id_to_hir_id(def.did);
-                        err.report_as_lint(
+                        Err(err.report_as_lint(
                             tcx.at(tcx.def_span(def.did)),
                             "any use of this value will cause an error",
                             hir_id,
                             Some(err.span),
-                        )
+                        ))
                     }
                     // promoting runtime code is only allowed to error if it references broken
                     // constants any other kind of error will be reported to the user as a
@@ -368,31 +345,65 @@ pub fn const_eval_raw_provider<'tcx>(
                         if let Some(p) = cid.promoted {
                             let span = tcx.promoted_mir_of_opt_const_arg(def.to_global())[p].span;
                             if let err_inval!(ReferencedConstant) = err.error {
-                                err.report_as_error(
+                                Err(err.report_as_error(
                                     tcx.at(span),
                                     "evaluation of constant expression failed",
-                                )
+                                ))
                             } else {
-                                err.report_as_lint(
+                                Err(err.report_as_lint(
                                     tcx.at(span),
                                     "reaching this expression at runtime will panic or abort",
                                     tcx.hir().local_def_id_to_hir_id(def.did),
                                     Some(err.span),
-                                )
+                                ))
                             }
                         // anything else (array lengths, enum initializers, constant patterns) are
                         // reported as hard errors
                         } else {
-                            err.report_as_error(
+                            Err(err.report_as_error(
                                 ecx.tcx.at(ecx.cur_span()),
                                 "evaluation of constant value failed",
-                            )
+                            ))
                         }
                     }
                 }
             } else {
                 // use of broken constant from other crate
-                err.report_as_error(ecx.tcx.at(ecx.cur_span()), "could not evaluate constant")
+                Err(err.report_as_error(ecx.tcx.at(ecx.cur_span()), "could not evaluate constant"))
             }
-        })
+        }
+        Ok(mplace) => {
+            // Since evaluation had no errors, valiate the resulting constant:
+            let validation = try {
+                // FIXME do not validate promoteds until a decision on
+                // https://github.com/rust-lang/rust/issues/67465 is made
+                if cid.promoted.is_none() {
+                    let mut ref_tracking = RefTracking::new(mplace);
+                    while let Some((mplace, path)) = ref_tracking.todo.pop() {
+                        ecx.const_validate_operand(
+                            mplace.into(),
+                            path,
+                            &mut ref_tracking,
+                            /*may_ref_to_static*/ ecx.memory.extra.can_access_statics,
+                        )?;
+                    }
+                }
+            };
+            if let Err(error) = validation {
+                // Validation failed, report an error
+                let err = ConstEvalErr::new(&ecx, error, None);
+                Err(err.struct_error(
+                    ecx.tcx,
+                    "it is undefined behavior to use this value",
+                    |mut diag| {
+                        diag.note(note_on_undefined_behavior_error());
+                        diag.emit();
+                    },
+                ))
+            } else {
+                // Convert to raw constant
+                Ok(ConstAlloc { alloc_id: mplace.ptr.assert_ptr().alloc_id, ty: mplace.layout.ty })
+            }
+        }
+    }
 }
diff --git a/compiler/rustc_mir/src/const_eval/machine.rs b/compiler/rustc_mir/src/const_eval/machine.rs
index b0357c508a3..73ca7e0d471 100644
--- a/compiler/rustc_mir/src/const_eval/machine.rs
+++ b/compiler/rustc_mir/src/const_eval/machine.rs
@@ -11,7 +11,7 @@ use rustc_ast::Mutability;
 use rustc_hir::def_id::DefId;
 use rustc_middle::mir::AssertMessage;
 use rustc_session::Limit;
-use rustc_span::symbol::Symbol;
+use rustc_span::symbol::{sym, Symbol};
 
 use crate::interpret::{
     self, compile_time_machine, AllocId, Allocation, Frame, GlobalId, ImmTy, InterpCx,
@@ -51,7 +51,7 @@ impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> {
 
         let gid = GlobalId { instance, promoted: None };
 
-        let place = self.const_eval_raw(gid)?;
+        let place = self.eval_to_allocation(gid)?;
 
         self.copy_op(place.into(), dest)?;
 
@@ -176,6 +176,38 @@ impl interpret::MayLeak for ! {
     }
 }
 
+impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> {
+    fn guaranteed_eq(&mut self, a: Scalar, b: Scalar) -> bool {
+        match (a, b) {
+            // Comparisons between integers are always known.
+            (Scalar::Raw { .. }, Scalar::Raw { .. }) => a == b,
+            // Equality with integers can never be known for sure.
+            (Scalar::Raw { .. }, Scalar::Ptr(_)) | (Scalar::Ptr(_), Scalar::Raw { .. }) => false,
+            // FIXME: return `true` for when both sides are the same pointer, *except* that
+            // some things (like functions and vtables) do not have stable addresses
+            // so we need to be careful around them (see e.g. #73722).
+            (Scalar::Ptr(_), Scalar::Ptr(_)) => false,
+        }
+    }
+
+    fn guaranteed_ne(&mut self, a: Scalar, b: Scalar) -> bool {
+        match (a, b) {
+            // Comparisons between integers are always known.
+            (Scalar::Raw { .. }, Scalar::Raw { .. }) => a != b,
+            // Comparisons of abstract pointers with null pointers are known if the pointer
+            // is in bounds, because if they are in bounds, the pointer can't be null.
+            (Scalar::Raw { data: 0, .. }, Scalar::Ptr(ptr))
+            | (Scalar::Ptr(ptr), Scalar::Raw { data: 0, .. }) => !self.memory.ptr_may_be_null(ptr),
+            // Inequality with integers other than null can never be known for sure.
+            (Scalar::Raw { .. }, Scalar::Ptr(_)) | (Scalar::Ptr(_), Scalar::Raw { .. }) => false,
+            // FIXME: return `true` for at least some comparisons where we can reliably
+            // determine the result of runtime inequality tests at compile-time.
+            // Examples include comparison of addresses in different static items.
+            (Scalar::Ptr(_), Scalar::Ptr(_)) => false,
+        }
+    }
+}
+
 impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, 'tcx> {
     compile_time_machine!(<'mir, 'tcx>);
 
@@ -234,12 +266,45 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
         ret: Option<(PlaceTy<'tcx>, mir::BasicBlock)>,
         _unwind: Option<mir::BasicBlock>,
     ) -> InterpResult<'tcx> {
+        // Shared intrinsics.
         if ecx.emulate_intrinsic(instance, args, ret)? {
             return Ok(());
         }
-        // An intrinsic that we do not support
         let intrinsic_name = ecx.tcx.item_name(instance.def_id());
-        Err(ConstEvalErrKind::NeedsRfc(format!("calling intrinsic `{}`", intrinsic_name)).into())
+
+        // CTFE-specific intrinsics.
+        let (dest, ret) = match ret {
+            None => {
+                return Err(ConstEvalErrKind::NeedsRfc(format!(
+                    "calling intrinsic `{}`",
+                    intrinsic_name
+                ))
+                .into());
+            }
+            Some(p) => p,
+        };
+        match intrinsic_name {
+            sym::ptr_guaranteed_eq | sym::ptr_guaranteed_ne => {
+                let a = ecx.read_immediate(args[0])?.to_scalar()?;
+                let b = ecx.read_immediate(args[1])?.to_scalar()?;
+                let cmp = if intrinsic_name == sym::ptr_guaranteed_eq {
+                    ecx.guaranteed_eq(a, b)
+                } else {
+                    ecx.guaranteed_ne(a, b)
+                };
+                ecx.write_scalar(Scalar::from_bool(cmp), dest)?;
+            }
+            _ => {
+                return Err(ConstEvalErrKind::NeedsRfc(format!(
+                    "calling intrinsic `{}`",
+                    intrinsic_name
+                ))
+                .into());
+            }
+        }
+
+        ecx.go_to_block(ret);
+        Ok(())
     }
 
     fn assert_panic(
diff --git a/compiler/rustc_mir/src/const_eval/mod.rs b/compiler/rustc_mir/src/const_eval/mod.rs
index e7eeb4b4de4..c93feb5096b 100644
--- a/compiler/rustc_mir/src/const_eval/mod.rs
+++ b/compiler/rustc_mir/src/const_eval/mod.rs
@@ -44,7 +44,7 @@ pub(crate) fn destructure_const<'tcx>(
     let op = ecx.const_to_op(val, None).unwrap();
 
     // 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 val.ty.kind() {
         ty::Array(_, len) => (usize::try_from(len.eval_usize(tcx, param_env)).unwrap(), None, op),
         ty::Adt(def, _) if def.variants.is_empty() => {
             return mir::DestructuredConst { variant: None, fields: tcx.arena.alloc_slice(&[]) };
diff --git a/compiler/rustc_mir/src/dataflow/drop_flag_effects.rs b/compiler/rustc_mir/src/dataflow/drop_flag_effects.rs
index 707e136678e..d1d507e54ef 100644
--- a/compiler/rustc_mir/src/dataflow/drop_flag_effects.rs
+++ b/compiler/rustc_mir/src/dataflow/drop_flag_effects.rs
@@ -53,7 +53,7 @@ fn place_contents_drop_state_cannot_differ<'tcx>(
     place: mir::Place<'tcx>,
 ) -> bool {
     let ty = place.ty(body, tcx).ty;
-    match ty.kind {
+    match ty.kind() {
         ty::Array(..) => {
             debug!(
                 "place_contents_drop_state_cannot_differ place: {:?} ty: {:?} => false",
diff --git a/compiler/rustc_mir/src/dataflow/framework/cursor.rs b/compiler/rustc_mir/src/dataflow/framework/cursor.rs
index 4f5930dc3f5..4942bed656c 100644
--- a/compiler/rustc_mir/src/dataflow/framework/cursor.rs
+++ b/compiler/rustc_mir/src/dataflow/framework/cursor.rs
@@ -4,6 +4,7 @@ use std::borrow::Borrow;
 use std::cmp::Ordering;
 
 use rustc_index::bit_set::BitSet;
+use rustc_index::vec::Idx;
 use rustc_middle::mir::{self, BasicBlock, Location};
 
 use super::{Analysis, Direction, Effect, EffectIndex, Results};
@@ -26,7 +27,7 @@ where
 {
     body: &'mir mir::Body<'tcx>,
     results: R,
-    state: BitSet<A::Idx>,
+    state: A::Domain,
 
     pos: CursorPosition,
 
@@ -46,17 +47,16 @@ where
 {
     /// Returns a new cursor that can inspect `results`.
     pub fn new(body: &'mir mir::Body<'tcx>, results: R) -> Self {
-        let bits_per_block = results.borrow().entry_set_for_block(mir::START_BLOCK).domain_size();
-
+        let bottom_value = results.borrow().analysis.bottom_value(body);
         ResultsCursor {
             body,
             results,
 
-            // Initialize to an empty `BitSet` and set `state_needs_reset` to tell the cursor that
+            // Initialize to the `bottom_value` and set `state_needs_reset` to tell the cursor that
             // it needs to reset to block entry before the first seek. The cursor position is
             // immaterial.
             state_needs_reset: true,
-            state: BitSet::new_empty(bits_per_block),
+            state: bottom_value,
             pos: CursorPosition::block_entry(mir::START_BLOCK),
 
             #[cfg(debug_assertions)]
@@ -68,23 +68,21 @@ where
         self.body
     }
 
-    /// Returns the `Analysis` used to generate the underlying results.
+    /// Returns the underlying `Results`.
+    pub fn results(&self) -> &Results<'tcx, A> {
+        &self.results.borrow()
+    }
+
+    /// Returns the `Analysis` used to generate the underlying `Results`.
     pub fn analysis(&self) -> &A {
         &self.results.borrow().analysis
     }
 
     /// Returns the dataflow state at the current location.
-    pub fn get(&self) -> &BitSet<A::Idx> {
+    pub fn get(&self) -> &A::Domain {
         &self.state
     }
 
-    /// Returns `true` if the dataflow state at the current location contains the given element.
-    ///
-    /// Shorthand for `self.get().contains(elem)`
-    pub fn contains(&self, elem: A::Idx) -> bool {
-        self.state.contains(elem)
-    }
-
     /// Resets the cursor to hold the entry set for the given basic block.
     ///
     /// For forward dataflow analyses, this is the dataflow state prior to the first statement.
@@ -94,7 +92,7 @@ where
         #[cfg(debug_assertions)]
         assert!(self.reachable_blocks.contains(block));
 
-        self.state.overwrite(&self.results.borrow().entry_set_for_block(block));
+        self.state.clone_from(&self.results.borrow().entry_set_for_block(block));
         self.pos = CursorPosition::block_entry(block);
         self.state_needs_reset = false;
     }
@@ -202,12 +200,23 @@ where
     ///
     /// This can be used, e.g., to apply the call return effect directly to the cursor without
     /// creating an extra copy of the dataflow state.
-    pub fn apply_custom_effect(&mut self, f: impl FnOnce(&A, &mut BitSet<A::Idx>)) {
+    pub fn apply_custom_effect(&mut self, f: impl FnOnce(&A, &mut A::Domain)) {
         f(&self.results.borrow().analysis, &mut self.state);
         self.state_needs_reset = true;
     }
 }
 
+impl<'mir, 'tcx, A, R, T> ResultsCursor<'mir, 'tcx, A, R>
+where
+    A: Analysis<'tcx, Domain = BitSet<T>>,
+    T: Idx,
+    R: Borrow<Results<'tcx, A>>,
+{
+    pub fn contains(&self, elem: T) -> bool {
+        self.get().contains(elem)
+    }
+}
+
 #[derive(Clone, Copy, Debug)]
 struct CursorPosition {
     block: BasicBlock,
diff --git a/compiler/rustc_mir/src/dataflow/framework/direction.rs b/compiler/rustc_mir/src/dataflow/framework/direction.rs
index 4512ae96c08..76c48100371 100644
--- a/compiler/rustc_mir/src/dataflow/framework/direction.rs
+++ b/compiler/rustc_mir/src/dataflow/framework/direction.rs
@@ -18,7 +18,7 @@ pub trait Direction {
     /// `effects.start()` must precede or equal `effects.end()` in this direction.
     fn apply_effects_in_range<A>(
         analysis: &A,
-        state: &mut BitSet<A::Idx>,
+        state: &mut A::Domain,
         block: BasicBlock,
         block_data: &mir::BasicBlockData<'tcx>,
         effects: RangeInclusive<EffectIndex>,
@@ -27,7 +27,7 @@ pub trait Direction {
 
     fn apply_effects_in_block<A>(
         analysis: &A,
-        state: &mut BitSet<A::Idx>,
+        state: &mut A::Domain,
         block: BasicBlock,
         block_data: &mir::BasicBlockData<'tcx>,
     ) where
@@ -55,9 +55,9 @@ pub trait Direction {
         tcx: TyCtxt<'tcx>,
         body: &mir::Body<'tcx>,
         dead_unwinds: Option<&BitSet<BasicBlock>>,
-        exit_state: &mut BitSet<A::Idx>,
+        exit_state: &mut A::Domain,
         block: (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
-        propagate: impl FnMut(BasicBlock, &BitSet<A::Idx>),
+        propagate: impl FnMut(BasicBlock, &A::Domain),
     ) where
         A: Analysis<'tcx>;
 }
@@ -72,7 +72,7 @@ impl Direction for Backward {
 
     fn apply_effects_in_block<A>(
         analysis: &A,
-        state: &mut BitSet<A::Idx>,
+        state: &mut A::Domain,
         block: BasicBlock,
         block_data: &mir::BasicBlockData<'tcx>,
     ) where
@@ -112,7 +112,7 @@ impl Direction for Backward {
 
     fn apply_effects_in_range<A>(
         analysis: &A,
-        state: &mut BitSet<A::Idx>,
+        state: &mut A::Domain,
         block: BasicBlock,
         block_data: &mir::BasicBlockData<'tcx>,
         effects: RangeInclusive<EffectIndex>,
@@ -224,9 +224,9 @@ impl Direction for Backward {
         _tcx: TyCtxt<'tcx>,
         body: &mir::Body<'tcx>,
         dead_unwinds: Option<&BitSet<BasicBlock>>,
-        exit_state: &mut BitSet<A::Idx>,
+        exit_state: &mut A::Domain,
         (bb, _bb_data): (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
-        mut propagate: impl FnMut(BasicBlock, &BitSet<A::Idx>),
+        mut propagate: impl FnMut(BasicBlock, &A::Domain),
     ) where
         A: Analysis<'tcx>,
     {
@@ -281,7 +281,7 @@ impl Direction for Forward {
 
     fn apply_effects_in_block<A>(
         analysis: &A,
-        state: &mut BitSet<A::Idx>,
+        state: &mut A::Domain,
         block: BasicBlock,
         block_data: &mir::BasicBlockData<'tcx>,
     ) where
@@ -321,7 +321,7 @@ impl Direction for Forward {
 
     fn apply_effects_in_range<A>(
         analysis: &A,
-        state: &mut BitSet<A::Idx>,
+        state: &mut A::Domain,
         block: BasicBlock,
         block_data: &mir::BasicBlockData<'tcx>,
         effects: RangeInclusive<EffectIndex>,
@@ -428,9 +428,9 @@ impl Direction for Forward {
         tcx: TyCtxt<'tcx>,
         body: &mir::Body<'tcx>,
         dead_unwinds: Option<&BitSet<BasicBlock>>,
-        exit_state: &mut BitSet<A::Idx>,
+        exit_state: &mut A::Domain,
         (bb, bb_data): (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
-        mut propagate: impl FnMut(BasicBlock, &BitSet<A::Idx>),
+        mut propagate: impl FnMut(BasicBlock, &A::Domain),
     ) where
         A: Analysis<'tcx>,
     {
@@ -499,7 +499,7 @@ impl Direction for Forward {
                         // MIR building adds discriminants to the `values` array in the same order as they
                         // are yielded by `AdtDef::discriminants`. We rely on this to match each
                         // discriminant in `values` to its corresponding variant in linear time.
-                        let mut tmp = BitSet::new_empty(exit_state.domain_size());
+                        let mut tmp = analysis.bottom_value(body);
                         let mut discriminants = enum_def.discriminants(tcx);
                         for (value, target) in values.iter().zip(targets.iter().copied()) {
                             let (variant_idx, _) =
@@ -508,7 +508,7 @@ impl Direction for Forward {
                                          from that of `SwitchInt::values`",
                                 );
 
-                            tmp.overwrite(exit_state);
+                            tmp.clone_from(exit_state);
                             analysis.apply_discriminant_switch_effect(
                                 &mut tmp,
                                 bb,
@@ -559,7 +559,7 @@ fn switch_on_enum_discriminant(
         Some(mir::StatementKind::Assign(box (lhs, mir::Rvalue::Discriminant(discriminated))))
             if *lhs == switch_on =>
         {
-            match &discriminated.ty(body, tcx).ty.kind {
+            match &discriminated.ty(body, tcx).ty.kind() {
                 ty::Adt(def, _) => Some((*discriminated, def)),
 
                 // `Rvalue::Discriminant` is also used to get the active yield point for a
diff --git a/compiler/rustc_mir/src/dataflow/framework/engine.rs b/compiler/rustc_mir/src/dataflow/framework/engine.rs
index b703852b1de..f39c78f503d 100644
--- a/compiler/rustc_mir/src/dataflow/framework/engine.rs
+++ b/compiler/rustc_mir/src/dataflow/framework/engine.rs
@@ -1,5 +1,6 @@
 //! A solver for dataflow problems.
 
+use std::borrow::BorrowMut;
 use std::ffi::OsString;
 use std::fs;
 use std::path::PathBuf;
@@ -9,14 +10,16 @@ use rustc_data_structures::work_queue::WorkQueue;
 use rustc_graphviz as dot;
 use rustc_hir::def_id::DefId;
 use rustc_index::bit_set::BitSet;
-use rustc_index::vec::IndexVec;
+use rustc_index::vec::{Idx, IndexVec};
 use rustc_middle::mir::{self, traversal, BasicBlock};
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_span::symbol::{sym, Symbol};
 
+use super::fmt::DebugWithContext;
 use super::graphviz;
 use super::{
-    visit_results, Analysis, Direction, GenKillAnalysis, GenKillSet, ResultsCursor, ResultsVisitor,
+    visit_results, Analysis, Direction, GenKill, GenKillAnalysis, GenKillSet, JoinSemiLattice,
+    ResultsCursor, ResultsVisitor,
 };
 use crate::util::pretty::dump_enabled;
 
@@ -26,7 +29,7 @@ where
     A: Analysis<'tcx>,
 {
     pub analysis: A,
-    pub(super) entry_sets: IndexVec<BasicBlock, BitSet<A::Idx>>,
+    pub(super) entry_sets: IndexVec<BasicBlock, A::Domain>,
 }
 
 impl<A> Results<'tcx, A>
@@ -39,7 +42,7 @@ where
     }
 
     /// Gets the dataflow state for the given block.
-    pub fn entry_set_for_block(&self, block: BasicBlock) -> &BitSet<A::Idx> {
+    pub fn entry_set_for_block(&self, block: BasicBlock) -> &A::Domain {
         &self.entry_sets[block]
     }
 
@@ -47,7 +50,7 @@ where
         &self,
         body: &'mir mir::Body<'tcx>,
         blocks: impl IntoIterator<Item = BasicBlock>,
-        vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = BitSet<A::Idx>>,
+        vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>,
     ) {
         visit_results(body, blocks, self, vis)
     }
@@ -55,7 +58,7 @@ where
     pub fn visit_reachable_with(
         &self,
         body: &'mir mir::Body<'tcx>,
-        vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = BitSet<A::Idx>>,
+        vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>,
     ) {
         let blocks = mir::traversal::reachable(body);
         visit_results(body, blocks.map(|(bb, _)| bb), self, vis)
@@ -64,7 +67,7 @@ where
     pub fn visit_in_rpo_with(
         &self,
         body: &'mir mir::Body<'tcx>,
-        vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = BitSet<A::Idx>>,
+        vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>,
     ) {
         let blocks = mir::traversal::reverse_postorder(body);
         visit_results(body, blocks.map(|(bb, _)| bb), self, vis)
@@ -76,21 +79,28 @@ pub struct Engine<'a, 'tcx, A>
 where
     A: Analysis<'tcx>,
 {
-    bits_per_block: usize,
     tcx: TyCtxt<'tcx>,
     body: &'a mir::Body<'tcx>,
     def_id: DefId,
     dead_unwinds: Option<&'a BitSet<BasicBlock>>,
-    entry_sets: IndexVec<BasicBlock, BitSet<A::Idx>>,
+    entry_sets: IndexVec<BasicBlock, A::Domain>,
+    pass_name: Option<&'static str>,
     analysis: A,
 
     /// Cached, cumulative transfer functions for each block.
-    trans_for_block: Option<IndexVec<BasicBlock, GenKillSet<A::Idx>>>,
+    //
+    // FIXME(ecstaticmorse): This boxed `Fn` trait object is invoked inside a tight loop for
+    // gen/kill problems on cyclic CFGs. This is not ideal, but it doesn't seem to degrade
+    // performance in practice. I've tried a few ways to avoid this, but they have downsides. See
+    // the message for the commit that added this FIXME for more information.
+    apply_trans_for_block: Option<Box<dyn Fn(BasicBlock, &mut A::Domain)>>,
 }
 
-impl<A> Engine<'a, 'tcx, A>
+impl<A, D, T> Engine<'a, 'tcx, A>
 where
-    A: GenKillAnalysis<'tcx>,
+    A: GenKillAnalysis<'tcx, Idx = T, Domain = D>,
+    D: Clone + JoinSemiLattice + GenKill<T> + BorrowMut<BitSet<T>>,
+    T: Idx,
 {
     /// Creates a new `Engine` to solve a gen-kill dataflow problem.
     pub fn new_gen_kill(
@@ -109,22 +119,26 @@ where
 
         // Otherwise, compute and store the cumulative transfer function for each block.
 
-        let bits_per_block = analysis.bits_per_block(body);
-        let mut trans_for_block =
-            IndexVec::from_elem(GenKillSet::identity(bits_per_block), body.basic_blocks());
+        let identity = GenKillSet::identity(analysis.bottom_value(body).borrow().domain_size());
+        let mut trans_for_block = IndexVec::from_elem(identity, body.basic_blocks());
 
         for (block, block_data) in body.basic_blocks().iter_enumerated() {
             let trans = &mut trans_for_block[block];
             A::Direction::gen_kill_effects_in_block(&analysis, trans, block, block_data);
         }
 
-        Self::new(tcx, body, def_id, analysis, Some(trans_for_block))
+        let apply_trans = Box::new(move |bb: BasicBlock, state: &mut A::Domain| {
+            trans_for_block[bb].apply(state.borrow_mut());
+        });
+
+        Self::new(tcx, body, def_id, analysis, Some(apply_trans as Box<_>))
     }
 }
 
-impl<A> Engine<'a, 'tcx, A>
+impl<A, D> Engine<'a, 'tcx, A>
 where
-    A: Analysis<'tcx>,
+    A: Analysis<'tcx, Domain = D>,
+    D: Clone + JoinSemiLattice,
 {
     /// Creates a new `Engine` to solve a dataflow problem with an arbitrary transfer
     /// function.
@@ -145,32 +159,25 @@ where
         body: &'a mir::Body<'tcx>,
         def_id: DefId,
         analysis: A,
-        trans_for_block: Option<IndexVec<BasicBlock, GenKillSet<A::Idx>>>,
+        apply_trans_for_block: Option<Box<dyn Fn(BasicBlock, &mut A::Domain)>>,
     ) -> Self {
-        let bits_per_block = analysis.bits_per_block(body);
-
-        let bottom_value_set = if A::BOTTOM_VALUE {
-            BitSet::new_filled(bits_per_block)
-        } else {
-            BitSet::new_empty(bits_per_block)
-        };
-
-        let mut entry_sets = IndexVec::from_elem(bottom_value_set.clone(), body.basic_blocks());
+        let bottom_value = analysis.bottom_value(body);
+        let mut entry_sets = IndexVec::from_elem(bottom_value.clone(), body.basic_blocks());
         analysis.initialize_start_block(body, &mut entry_sets[mir::START_BLOCK]);
 
-        if A::Direction::is_backward() && entry_sets[mir::START_BLOCK] != bottom_value_set {
+        if A::Direction::is_backward() && entry_sets[mir::START_BLOCK] != bottom_value {
             bug!("`initialize_start_block` is not yet supported for backward dataflow analyses");
         }
 
         Engine {
             analysis,
-            bits_per_block,
             tcx,
             body,
             def_id,
             dead_unwinds: None,
+            pass_name: None,
             entry_sets,
-            trans_for_block,
+            apply_trans_for_block,
         }
     }
 
@@ -184,17 +191,29 @@ where
         self
     }
 
+    /// Adds an identifier to the graphviz output for this particular run of a dataflow analysis.
+    ///
+    /// Some analyses are run multiple times in the compilation pipeline. Give them a `pass_name`
+    /// to differentiate them. Otherwise, only the results for the latest run will be saved.
+    pub fn pass_name(mut self, name: &'static str) -> Self {
+        self.pass_name = Some(name);
+        self
+    }
+
     /// Computes the fixpoint for this dataflow problem and returns it.
-    pub fn iterate_to_fixpoint(self) -> Results<'tcx, A> {
+    pub fn iterate_to_fixpoint(self) -> Results<'tcx, A>
+    where
+        A::Domain: DebugWithContext<A>,
+    {
         let Engine {
             analysis,
-            bits_per_block,
             body,
             dead_unwinds,
             def_id,
             mut entry_sets,
             tcx,
-            trans_for_block,
+            apply_trans_for_block,
+            pass_name,
             ..
         } = self;
 
@@ -213,14 +232,14 @@ where
             }
         }
 
-        let mut state = BitSet::new_empty(bits_per_block);
+        let mut state = analysis.bottom_value(body);
         while let Some(bb) = dirty_queue.pop() {
             let bb_data = &body[bb];
 
             // Apply the block transfer function, using the cached one if it exists.
-            state.overwrite(&entry_sets[bb]);
-            match &trans_for_block {
-                Some(trans_for_block) => trans_for_block[bb].apply(&mut state),
+            state.clone_from(&entry_sets[bb]);
+            match &apply_trans_for_block {
+                Some(apply) => apply(bb, &mut state),
                 None => A::Direction::apply_effects_in_block(&analysis, &mut state, bb, bb_data),
             }
 
@@ -231,8 +250,8 @@ where
                 dead_unwinds,
                 &mut state,
                 (bb, bb_data),
-                |target: BasicBlock, state: &BitSet<A::Idx>| {
-                    let set_changed = analysis.join(&mut entry_sets[target], state);
+                |target: BasicBlock, state: &A::Domain| {
+                    let set_changed = entry_sets[target].join(state);
                     if set_changed {
                         dirty_queue.insert(target);
                     }
@@ -242,7 +261,7 @@ where
 
         let results = Results { analysis, entry_sets };
 
-        let res = write_graphviz_results(tcx, def_id, &body, &results, trans_for_block);
+        let res = write_graphviz_results(tcx, def_id, &body, &results, pass_name);
         if let Err(e) = res {
             warn!("Failed to write graphviz dataflow results: {}", e);
         }
@@ -260,10 +279,11 @@ fn write_graphviz_results<A>(
     def_id: DefId,
     body: &mir::Body<'tcx>,
     results: &Results<'tcx, A>,
-    block_transfer_functions: Option<IndexVec<BasicBlock, GenKillSet<A::Idx>>>,
+    pass_name: Option<&'static str>,
 ) -> std::io::Result<()>
 where
     A: Analysis<'tcx>,
+    A::Domain: DebugWithContext<A>,
 {
     let attrs = match RustcMirAttrs::parse(tcx, def_id) {
         Ok(attrs) => attrs,
@@ -278,39 +298,38 @@ where
         None if tcx.sess.opts.debugging_opts.dump_mir_dataflow
             && dump_enabled(tcx, A::NAME, def_id) =>
         {
+            // FIXME: Use some variant of `pretty::dump_path` for this
             let mut path = PathBuf::from(&tcx.sess.opts.debugging_opts.dump_mir_dir);
 
+            let crate_name = tcx.crate_name(def_id.krate);
             let item_name = ty::print::with_forced_impl_filename_line(|| {
                 tcx.def_path(def_id).to_filename_friendly_no_crate()
             });
-            path.push(format!("rustc.{}.{}.dot", item_name, A::NAME));
+
+            let pass_name = pass_name.map(|s| format!(".{}", s)).unwrap_or_default();
+
+            path.push(format!("{}.{}.{}{}.dot", crate_name, item_name, A::NAME, pass_name));
             path
         }
 
         None => return Ok(()),
     };
 
-    let bits_per_block = results.analysis.bits_per_block(body);
-
-    let mut formatter: Box<dyn graphviz::StateFormatter<'tcx, _>> = match attrs.formatter {
-        Some(sym::two_phase) => Box::new(graphviz::TwoPhaseDiff::new(bits_per_block)),
-        Some(sym::gen_kill) => {
-            if let Some(trans_for_block) = block_transfer_functions {
-                Box::new(graphviz::BlockTransferFunc::new(body, trans_for_block))
-            } else {
-                Box::new(graphviz::SimpleDiff::new(body, &results))
-            }
-        }
-
-        // Default to the `SimpleDiff` output style.
-        _ => Box::new(graphviz::SimpleDiff::new(body, &results)),
+    let style = match attrs.formatter {
+        Some(sym::two_phase) => graphviz::OutputStyle::BeforeAndAfter,
+        _ => graphviz::OutputStyle::AfterOnly,
     };
 
     debug!("printing dataflow results for {:?} to {}", def_id, path.display());
     let mut buf = Vec::new();
 
-    let graphviz = graphviz::Formatter::new(body, def_id, results, &mut *formatter);
-    dot::render_opts(&graphviz, &mut buf, &[dot::RenderOption::Monospace])?;
+    let graphviz = graphviz::Formatter::new(body, def_id, results, style);
+    let mut render_opts =
+        vec![dot::RenderOption::Fontname(tcx.sess.opts.debugging_opts.graphviz_font.clone())];
+    if tcx.sess.opts.debugging_opts.graphviz_dark_mode {
+        render_opts.push(dot::RenderOption::DarkTheme);
+    }
+    dot::render_opts(&graphviz, &mut buf, &render_opts)?;
 
     if let Some(parent) = path.parent() {
         fs::create_dir_all(parent)?;
diff --git a/compiler/rustc_mir/src/dataflow/framework/fmt.rs b/compiler/rustc_mir/src/dataflow/framework/fmt.rs
new file mode 100644
index 00000000000..0140a750544
--- /dev/null
+++ b/compiler/rustc_mir/src/dataflow/framework/fmt.rs
@@ -0,0 +1,172 @@
+//! Custom formatting traits used when outputting Graphviz diagrams with the results of a dataflow
+//! analysis.
+
+use rustc_index::bit_set::{BitSet, HybridBitSet};
+use rustc_index::vec::Idx;
+use std::fmt;
+
+/// An extension to `fmt::Debug` for data that can be better printed with some auxiliary data `C`.
+pub trait DebugWithContext<C>: Eq + fmt::Debug {
+    fn fmt_with(&self, _ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        fmt::Debug::fmt(self, f)
+    }
+
+    /// Print the difference between `self` and `old`.
+    ///
+    /// This should print nothing if `self == old`.
+    ///
+    /// `+` and `-` are typically used to indicate differences. However, these characters are
+    /// fairly common and may be needed to print a types representation. If using them to indicate
+    /// a diff, prefix them with the "Unit Separator"  control character (␟  U+001F).
+    fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        if self == old {
+            return Ok(());
+        }
+
+        write!(f, "\u{001f}+")?;
+        self.fmt_with(ctxt, f)?;
+
+        if f.alternate() {
+            write!(f, "\n")?;
+        } else {
+            write!(f, "\t")?;
+        }
+
+        write!(f, "\u{001f}-")?;
+        self.fmt_with(ctxt, f)
+    }
+}
+
+/// Implements `fmt::Debug` by deferring to `<T as DebugWithContext<C>>::fmt_with`.
+pub struct DebugWithAdapter<'a, T, C> {
+    pub this: T,
+    pub ctxt: &'a C,
+}
+
+impl<T, C> fmt::Debug for DebugWithAdapter<'_, T, C>
+where
+    T: DebugWithContext<C>,
+{
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.this.fmt_with(self.ctxt, f)
+    }
+}
+
+/// Implements `fmt::Debug` by deferring to `<T as DebugWithContext<C>>::fmt_diff_with`.
+pub struct DebugDiffWithAdapter<'a, T, C> {
+    pub new: T,
+    pub old: T,
+    pub ctxt: &'a C,
+}
+
+impl<T, C> fmt::Debug for DebugDiffWithAdapter<'_, T, C>
+where
+    T: DebugWithContext<C>,
+{
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.new.fmt_diff_with(&self.old, self.ctxt, f)
+    }
+}
+
+// Impls
+
+impl<T, C> DebugWithContext<C> for BitSet<T>
+where
+    T: Idx + DebugWithContext<C>,
+{
+    fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_set().entries(self.iter().map(|i| DebugWithAdapter { this: i, ctxt })).finish()
+    }
+
+    fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let size = self.domain_size();
+        assert_eq!(size, old.domain_size());
+
+        let mut set_in_self = HybridBitSet::new_empty(size);
+        let mut cleared_in_self = HybridBitSet::new_empty(size);
+
+        for i in (0..size).map(T::new) {
+            match (self.contains(i), old.contains(i)) {
+                (true, false) => set_in_self.insert(i),
+                (false, true) => cleared_in_self.insert(i),
+                _ => continue,
+            };
+        }
+
+        let mut first = true;
+        for idx in set_in_self.iter() {
+            let delim = if first {
+                "\u{001f}+"
+            } else if f.alternate() {
+                "\n\u{001f}+"
+            } else {
+                ", "
+            };
+
+            write!(f, "{}", delim)?;
+            idx.fmt_with(ctxt, f)?;
+            first = false;
+        }
+
+        if !f.alternate() {
+            first = true;
+            if !set_in_self.is_empty() && !cleared_in_self.is_empty() {
+                write!(f, "\t")?;
+            }
+        }
+
+        for idx in cleared_in_self.iter() {
+            let delim = if first {
+                "\u{001f}-"
+            } else if f.alternate() {
+                "\n\u{001f}-"
+            } else {
+                ", "
+            };
+
+            write!(f, "{}", delim)?;
+            idx.fmt_with(ctxt, f)?;
+            first = false;
+        }
+
+        Ok(())
+    }
+}
+
+impl<T, C> DebugWithContext<C> for &'_ T
+where
+    T: DebugWithContext<C>,
+{
+    fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        (*self).fmt_with(ctxt, f)
+    }
+
+    fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        (*self).fmt_diff_with(*old, ctxt, f)
+    }
+}
+
+impl<C> DebugWithContext<C> for rustc_middle::mir::Local {}
+impl<C> DebugWithContext<C> for crate::dataflow::move_paths::InitIndex {}
+
+impl<'tcx, C> DebugWithContext<C> for crate::dataflow::move_paths::MovePathIndex
+where
+    C: crate::dataflow::move_paths::HasMoveData<'tcx>,
+{
+    fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "{}", ctxt.move_data().move_paths[*self])
+    }
+}
+
+impl<T, C> DebugWithContext<C> for crate::dataflow::lattice::Dual<T>
+where
+    T: DebugWithContext<C>,
+{
+    fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        (self.0).fmt_with(ctxt, f)
+    }
+
+    fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        (self.0).fmt_diff_with(&old.0, ctxt, f)
+    }
+}
diff --git a/compiler/rustc_mir/src/dataflow/framework/graphviz.rs b/compiler/rustc_mir/src/dataflow/framework/graphviz.rs
index 896616a2175..5d4c4251961 100644
--- a/compiler/rustc_mir/src/dataflow/framework/graphviz.rs
+++ b/compiler/rustc_mir/src/dataflow/framework/graphviz.rs
@@ -1,26 +1,41 @@
 //! A helpful diagram for debugging dataflow problems.
 
-use std::cell::RefCell;
+use std::borrow::Cow;
+use std::lazy::SyncOnceCell;
 use std::{io, ops, str};
 
+use regex::Regex;
 use rustc_graphviz as dot;
 use rustc_hir::def_id::DefId;
-use rustc_index::bit_set::{BitSet, HybridBitSet};
-use rustc_index::vec::{Idx, IndexVec};
 use rustc_middle::mir::{self, BasicBlock, Body, Location};
 
-use super::{Analysis, Direction, GenKillSet, Results, ResultsRefCursor};
+use super::fmt::{DebugDiffWithAdapter, DebugWithAdapter, DebugWithContext};
+use super::{Analysis, Direction, Results, ResultsRefCursor, ResultsVisitor};
 use crate::util::graphviz_safe_def_name;
 
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum OutputStyle {
+    AfterOnly,
+    BeforeAndAfter,
+}
+
+impl OutputStyle {
+    fn num_state_columns(&self) -> usize {
+        match self {
+            Self::AfterOnly => 1,
+            Self::BeforeAndAfter => 2,
+        }
+    }
+}
+
 pub struct Formatter<'a, 'tcx, A>
 where
     A: Analysis<'tcx>,
 {
     body: &'a Body<'tcx>,
     def_id: DefId,
-
-    // This must be behind a `RefCell` because `dot::Labeller` takes `&self`.
-    block_formatter: RefCell<BlockFormatter<'a, 'tcx, A>>,
+    results: &'a Results<'tcx, A>,
+    style: OutputStyle,
 }
 
 impl<A> Formatter<'a, 'tcx, A>
@@ -31,15 +46,9 @@ where
         body: &'a Body<'tcx>,
         def_id: DefId,
         results: &'a Results<'tcx, A>,
-        state_formatter: &'a mut dyn StateFormatter<'tcx, A>,
+        style: OutputStyle,
     ) -> Self {
-        let block_formatter = BlockFormatter {
-            bg: Background::Light,
-            results: ResultsRefCursor::new(body, results),
-            state_formatter,
-        };
-
-        Formatter { body, def_id, block_formatter: RefCell::new(block_formatter) }
+        Formatter { body, def_id, results, style }
     }
 }
 
@@ -62,6 +71,7 @@ fn dataflow_successors(body: &Body<'tcx>, bb: BasicBlock) -> Vec<CfgEdge> {
 impl<A> dot::Labeller<'_> for Formatter<'a, 'tcx, A>
 where
     A: Analysis<'tcx>,
+    A::Domain: DebugWithContext<A>,
 {
     type Node = BasicBlock;
     type Edge = CfgEdge;
@@ -77,7 +87,13 @@ where
 
     fn node_label(&self, block: &Self::Node) -> dot::LabelText<'_> {
         let mut label = Vec::new();
-        self.block_formatter.borrow_mut().write_node_label(&mut label, self.body, *block).unwrap();
+        let mut fmt = BlockFormatter {
+            results: ResultsRefCursor::new(self.body, self.results),
+            style: self.style,
+            bg: Background::Light,
+        };
+
+        fmt.write_node_label(&mut label, self.body, *block).unwrap();
         dot::LabelText::html(String::from_utf8(label).unwrap())
     }
 
@@ -126,19 +142,16 @@ where
 {
     results: ResultsRefCursor<'a, 'a, 'tcx, A>,
     bg: Background,
-    state_formatter: &'a mut dyn StateFormatter<'tcx, A>,
+    style: OutputStyle,
 }
 
 impl<A> BlockFormatter<'a, 'tcx, A>
 where
     A: Analysis<'tcx>,
+    A::Domain: DebugWithContext<A>,
 {
     const HEADER_COLOR: &'static str = "#a0a0a0";
 
-    fn num_state_columns(&self) -> usize {
-        std::cmp::max(1, self.state_formatter.column_names().len())
-    }
-
     fn toggle_background(&mut self) -> Background {
         let bg = self.bg;
         self.bg = !bg;
@@ -187,40 +200,30 @@ where
         write!(w, r#"<table{fmt}>"#, fmt = table_fmt)?;
 
         // A + B: Block header
-        if self.state_formatter.column_names().is_empty() {
-            self.write_block_header_simple(w, block)?;
-        } else {
-            self.write_block_header_with_state_columns(w, block)?;
+        match self.style {
+            OutputStyle::AfterOnly => self.write_block_header_simple(w, block)?,
+            OutputStyle::BeforeAndAfter => {
+                self.write_block_header_with_state_columns(w, block, &["BEFORE", "AFTER"])?
+            }
         }
 
         // C: State at start of block
         self.bg = Background::Light;
         self.results.seek_to_block_start(block);
-        let block_entry_state = self.results.get().clone();
-
+        let block_start_state = self.results.get().clone();
         self.write_row_with_full_state(w, "", "(on start)")?;
 
-        // D: Statement transfer functions
-        for (i, statement) in body[block].statements.iter().enumerate() {
-            let location = Location { block, statement_index: i };
-            let statement_str = format!("{:?}", statement);
-            self.write_row_for_location(w, &i.to_string(), &statement_str, location)?;
-        }
-
-        // E: Terminator transfer function
-        let terminator = body[block].terminator();
-        let terminator_loc = body.terminator_loc(block);
-        let mut terminator_str = String::new();
-        terminator.kind.fmt_head(&mut terminator_str).unwrap();
-
-        self.write_row_for_location(w, "T", &terminator_str, terminator_loc)?;
+        // D + E: Statement and terminator transfer functions
+        self.write_statements_and_terminator(w, body, block)?;
 
         // F: State at end of block
 
+        let terminator = body[block].terminator();
+
         // Write the full dataflow state immediately after the terminator if it differs from the
         // state at block entry.
         self.results.seek_to_block_end(block);
-        if self.results.get() != &block_entry_state || A::Direction::is_backward() {
+        if self.results.get() != &block_start_state || A::Direction::is_backward() {
             let after_terminator_name = match terminator.kind {
                 mir::TerminatorKind::Call { destination: Some(_), .. } => "(on unwind)",
                 _ => "(on end)",
@@ -229,8 +232,11 @@ where
             self.write_row_with_full_state(w, "", after_terminator_name)?;
         }
 
-        // Write any changes caused by terminator-specific effects
-        let num_state_columns = self.num_state_columns();
+        // Write any changes caused by terminator-specific effects.
+        //
+        // FIXME: These should really be printed as part of each outgoing edge rather than the node
+        // for the basic block itself. That way, we could display terminator-specific effects for
+        // backward dataflow analyses as well as effects for `SwitchInt` terminators.
         match terminator.kind {
             mir::TerminatorKind::Call {
                 destination: Some((return_place, _)),
@@ -239,44 +245,43 @@ where
                 ..
             } => {
                 self.write_row(w, "", "(on successful return)", |this, w, fmt| {
-                    write!(
-                        w,
-                        r#"<td balign="left" colspan="{colspan}" {fmt} align="left">"#,
-                        colspan = num_state_columns,
-                        fmt = fmt,
-                    )?;
-
                     let state_on_unwind = this.results.get().clone();
                     this.results.apply_custom_effect(|analysis, state| {
                         analysis.apply_call_return_effect(state, block, func, args, return_place);
                     });
 
-                    write_diff(w, this.results.analysis(), &state_on_unwind, this.results.get())?;
-                    write!(w, "</td>")
+                    write!(
+                        w,
+                        r#"<td balign="left" colspan="{colspan}" {fmt} align="left">{diff}</td>"#,
+                        colspan = this.style.num_state_columns(),
+                        fmt = fmt,
+                        diff = diff_pretty(
+                            this.results.get(),
+                            &state_on_unwind,
+                            this.results.analysis()
+                        ),
+                    )
                 })?;
             }
 
             mir::TerminatorKind::Yield { resume, resume_arg, .. } => {
                 self.write_row(w, "", "(on yield resume)", |this, w, fmt| {
-                    write!(
-                        w,
-                        r#"<td balign="left" colspan="{colspan}" {fmt} align="left">"#,
-                        colspan = num_state_columns,
-                        fmt = fmt,
-                    )?;
-
                     let state_on_generator_drop = this.results.get().clone();
                     this.results.apply_custom_effect(|analysis, state| {
                         analysis.apply_yield_resume_effect(state, resume, resume_arg);
                     });
 
-                    write_diff(
+                    write!(
                         w,
-                        this.results.analysis(),
-                        &state_on_generator_drop,
-                        this.results.get(),
-                    )?;
-                    write!(w, "</td>")
+                        r#"<td balign="left" colspan="{colspan}" {fmt} align="left">{diff}</td>"#,
+                        colspan = this.style.num_state_columns(),
+                        fmt = fmt,
+                        diff = diff_pretty(
+                            this.results.get(),
+                            &state_on_generator_drop,
+                            this.results.analysis()
+                        ),
+                    )
                 })?;
             }
 
@@ -322,6 +327,7 @@ where
         &mut self,
         w: &mut impl io::Write,
         block: BasicBlock,
+        state_column_names: &[&str],
     ) -> io::Result<()> {
         //   +------------------------------------+-------------+
         // A |                bb4                 |    STATE    |
@@ -330,8 +336,6 @@ where
         //   +-+----------------------------------+------+------+
         //   | |              ...                 |      |      |
 
-        let state_column_names = self.state_formatter.column_names();
-
         // A
         write!(
             w,
@@ -357,6 +361,56 @@ where
         write!(w, "</tr>")
     }
 
+    fn write_statements_and_terminator(
+        &mut self,
+        w: &mut impl io::Write,
+        body: &'a Body<'tcx>,
+        block: BasicBlock,
+    ) -> io::Result<()> {
+        let diffs = StateDiffCollector::run(body, block, self.results.results(), self.style);
+
+        let mut befores = diffs.before.map(|v| v.into_iter());
+        let mut afters = diffs.after.into_iter();
+
+        let next_in_dataflow_order = |it: &mut std::vec::IntoIter<_>| {
+            if A::Direction::is_forward() { it.next().unwrap() } else { it.next_back().unwrap() }
+        };
+
+        for (i, statement) in body[block].statements.iter().enumerate() {
+            let statement_str = format!("{:?}", statement);
+            let index_str = format!("{}", i);
+
+            let after = next_in_dataflow_order(&mut afters);
+            let before = befores.as_mut().map(next_in_dataflow_order);
+
+            self.write_row(w, &index_str, &statement_str, |_this, w, fmt| {
+                if let Some(before) = before {
+                    write!(w, r#"<td {fmt} align="left">{diff}</td>"#, fmt = fmt, diff = before)?;
+                }
+
+                write!(w, r#"<td {fmt} align="left">{diff}</td>"#, fmt = fmt, diff = after)
+            })?;
+        }
+
+        let after = next_in_dataflow_order(&mut afters);
+        let before = befores.as_mut().map(next_in_dataflow_order);
+
+        assert!(afters.is_empty());
+        assert!(befores.as_ref().map_or(true, ExactSizeIterator::is_empty));
+
+        let terminator = body[block].terminator();
+        let mut terminator_str = String::new();
+        terminator.kind.fmt_head(&mut terminator_str).unwrap();
+
+        self.write_row(w, "T", &terminator_str, |_this, w, fmt| {
+            if let Some(before) = before {
+                write!(w, r#"<td {fmt} align="left">{diff}</td>"#, fmt = fmt, diff = before)?;
+            }
+
+            write!(w, r#"<td {fmt} align="left">{diff}</td>"#, fmt = fmt, diff = after)
+        })
+    }
+
     /// Write a row with the given index and MIR, using the function argument to fill in the
     /// "STATE" column(s).
     fn write_row<W: io::Write>(
@@ -397,319 +451,176 @@ where
             let state = this.results.get();
             let analysis = this.results.analysis();
 
+            // FIXME: The full state vector can be quite long. It would be nice to split on commas
+            // and use some text wrapping algorithm.
             write!(
                 w,
-                r#"<td colspan="{colspan}" {fmt} align="left">{{"#,
-                colspan = this.num_state_columns(),
+                r#"<td colspan="{colspan}" {fmt} align="left">{state}</td>"#,
+                colspan = this.style.num_state_columns(),
                 fmt = fmt,
-            )?;
-            pretty_print_state_elems(w, analysis, state.iter(), ", ", LIMIT_30_ALIGN_1)?;
-            write!(w, "}}</td>")
-        })
-    }
-
-    fn write_row_for_location(
-        &mut self,
-        w: &mut impl io::Write,
-        i: &str,
-        mir: &str,
-        location: Location,
-    ) -> io::Result<()> {
-        self.write_row(w, i, mir, |this, w, fmt| {
-            this.state_formatter.write_state_for_location(w, fmt, &mut this.results, location)
+                state = format!("{:?}", DebugWithAdapter { this: state, ctxt: analysis }),
+            )
         })
     }
 }
 
-/// Controls what gets printed under the `STATE` header.
-pub trait StateFormatter<'tcx, A>
+struct StateDiffCollector<'a, 'tcx, A>
 where
     A: Analysis<'tcx>,
 {
-    /// The columns that will get printed under `STATE`.
-    fn column_names(&self) -> &[&str];
-
-    fn write_state_for_location(
-        &mut self,
-        w: &mut dyn io::Write,
-        fmt: &str,
-        results: &mut ResultsRefCursor<'_, '_, 'tcx, A>,
-        location: Location,
-    ) -> io::Result<()>;
+    analysis: &'a A,
+    prev_state: A::Domain,
+    before: Option<Vec<String>>,
+    after: Vec<String>,
 }
 
-/// Prints a single column containing the state vector immediately *after* each statement.
-pub struct SimpleDiff<'a, 'tcx, A>
+impl<A> StateDiffCollector<'a, 'tcx, A>
 where
     A: Analysis<'tcx>,
+    A::Domain: DebugWithContext<A>,
 {
-    prev_state: ResultsRefCursor<'a, 'a, 'tcx, A>,
-}
+    fn run(
+        body: &'a mir::Body<'tcx>,
+        block: BasicBlock,
+        results: &'a Results<'tcx, A>,
+        style: OutputStyle,
+    ) -> Self {
+        let mut collector = StateDiffCollector {
+            analysis: &results.analysis,
+            prev_state: results.analysis.bottom_value(body),
+            after: vec![],
+            before: (style == OutputStyle::BeforeAndAfter).then_some(vec![]),
+        };
 
-impl<A> SimpleDiff<'a, 'tcx, A>
-where
-    A: Analysis<'tcx>,
-{
-    pub fn new(body: &'a Body<'tcx>, results: &'a Results<'tcx, A>) -> Self {
-        SimpleDiff { prev_state: ResultsRefCursor::new(body, results) }
+        results.visit_with(body, std::iter::once(block), &mut collector);
+        collector
     }
 }
 
-impl<A> StateFormatter<'tcx, A> for SimpleDiff<'_, 'tcx, A>
+impl<A> ResultsVisitor<'a, 'tcx> for StateDiffCollector<'a, 'tcx, A>
 where
     A: Analysis<'tcx>,
+    A::Domain: DebugWithContext<A>,
 {
-    fn column_names(&self) -> &[&str] {
-        &[]
-    }
+    type FlowState = A::Domain;
 
-    fn write_state_for_location(
+    fn visit_block_start(
         &mut self,
-        mut w: &mut dyn io::Write,
-        fmt: &str,
-        results: &mut ResultsRefCursor<'_, '_, 'tcx, A>,
-        location: Location,
-    ) -> io::Result<()> {
+        state: &Self::FlowState,
+        _block_data: &'mir mir::BasicBlockData<'tcx>,
+        _block: BasicBlock,
+    ) {
         if A::Direction::is_forward() {
-            if location.statement_index == 0 {
-                self.prev_state.seek_to_block_start(location.block);
-            } else {
-                self.prev_state.seek_after_primary_effect(Location {
-                    statement_index: location.statement_index - 1,
-                    ..location
-                });
-            }
-        } else {
-            if location == results.body().terminator_loc(location.block) {
-                self.prev_state.seek_to_block_end(location.block);
-            } else {
-                self.prev_state.seek_after_primary_effect(location.successor_within_block());
-            }
+            self.prev_state.clone_from(state);
         }
-
-        write!(w, r#"<td {fmt} balign="left" align="left">"#, fmt = fmt)?;
-        results.seek_after_primary_effect(location);
-        let curr_state = results.get();
-        write_diff(&mut w, results.analysis(), self.prev_state.get(), curr_state)?;
-        write!(w, "</td>")
     }
-}
 
-/// Prints two state columns, one containing only the "before" effect of each statement and one
-/// containing the full effect.
-pub struct TwoPhaseDiff<T: Idx> {
-    prev_state: BitSet<T>,
-    prev_loc: Location,
-}
+    fn visit_block_end(
+        &mut self,
+        state: &Self::FlowState,
+        _block_data: &'mir mir::BasicBlockData<'tcx>,
+        _block: BasicBlock,
+    ) {
+        if A::Direction::is_backward() {
+            self.prev_state.clone_from(state);
+        }
+    }
 
-impl<T: Idx> TwoPhaseDiff<T> {
-    pub fn new(bits_per_block: usize) -> Self {
-        TwoPhaseDiff { prev_state: BitSet::new_empty(bits_per_block), prev_loc: Location::START }
+    fn visit_statement_before_primary_effect(
+        &mut self,
+        state: &Self::FlowState,
+        _statement: &'mir mir::Statement<'tcx>,
+        _location: Location,
+    ) {
+        if let Some(before) = self.before.as_mut() {
+            before.push(diff_pretty(state, &self.prev_state, self.analysis));
+            self.prev_state.clone_from(state)
+        }
     }
-}
 
-impl<A> StateFormatter<'tcx, A> for TwoPhaseDiff<A::Idx>
-where
-    A: Analysis<'tcx>,
-{
-    fn column_names(&self) -> &[&str] {
-        &["BEFORE", " AFTER"]
+    fn visit_statement_after_primary_effect(
+        &mut self,
+        state: &Self::FlowState,
+        _statement: &'mir mir::Statement<'tcx>,
+        _location: Location,
+    ) {
+        self.after.push(diff_pretty(state, &self.prev_state, self.analysis));
+        self.prev_state.clone_from(state)
     }
 
-    fn write_state_for_location(
+    fn visit_terminator_before_primary_effect(
         &mut self,
-        mut w: &mut dyn io::Write,
-        fmt: &str,
-        results: &mut ResultsRefCursor<'_, '_, 'tcx, A>,
-        location: Location,
-    ) -> io::Result<()> {
-        if location.statement_index == 0 {
-            results.seek_to_block_entry(location.block);
-            self.prev_state.overwrite(results.get());
-        } else {
-            // Ensure that we are visiting statements in order, so `prev_state` is correct.
-            assert_eq!(self.prev_loc.successor_within_block(), location);
+        state: &Self::FlowState,
+        _terminator: &'mir mir::Terminator<'tcx>,
+        _location: Location,
+    ) {
+        if let Some(before) = self.before.as_mut() {
+            before.push(diff_pretty(state, &self.prev_state, self.analysis));
+            self.prev_state.clone_from(state)
         }
-
-        self.prev_loc = location;
-
-        // Before
-
-        write!(w, r#"<td {fmt} align="left">"#, fmt = fmt)?;
-        results.seek_before_primary_effect(location);
-        let curr_state = results.get();
-        write_diff(&mut w, results.analysis(), &self.prev_state, curr_state)?;
-        self.prev_state.overwrite(curr_state);
-        write!(w, "</td>")?;
-
-        // After
-
-        write!(w, r#"<td {fmt} align="left">"#, fmt = fmt)?;
-        results.seek_after_primary_effect(location);
-        let curr_state = results.get();
-        write_diff(&mut w, results.analysis(), &self.prev_state, curr_state)?;
-        self.prev_state.overwrite(curr_state);
-        write!(w, "</td>")
     }
-}
 
-/// Prints the gen/kill set for the entire block.
-pub struct BlockTransferFunc<'a, 'tcx, T: Idx> {
-    body: &'a mir::Body<'tcx>,
-    trans_for_block: IndexVec<BasicBlock, GenKillSet<T>>,
+    fn visit_terminator_after_primary_effect(
+        &mut self,
+        state: &Self::FlowState,
+        _terminator: &'mir mir::Terminator<'tcx>,
+        _location: Location,
+    ) {
+        self.after.push(diff_pretty(state, &self.prev_state, self.analysis));
+        self.prev_state.clone_from(state)
+    }
 }
 
-impl<T: Idx> BlockTransferFunc<'mir, 'tcx, T> {
-    pub fn new(
-        body: &'mir mir::Body<'tcx>,
-        trans_for_block: IndexVec<BasicBlock, GenKillSet<T>>,
-    ) -> Self {
-        BlockTransferFunc { body, trans_for_block }
-    }
+macro_rules! regex {
+    ($re:literal $(,)?) => {{
+        static RE: SyncOnceCell<regex::Regex> = SyncOnceCell::new();
+        RE.get_or_init(|| Regex::new($re).unwrap())
+    }};
 }
 
-impl<A> StateFormatter<'tcx, A> for BlockTransferFunc<'mir, 'tcx, A::Idx>
+fn diff_pretty<T, C>(new: T, old: T, ctxt: &C) -> String
 where
-    A: Analysis<'tcx>,
+    T: DebugWithContext<C>,
 {
-    fn column_names(&self) -> &[&str] {
-        &["GEN", "KILL"]
+    if new == old {
+        return String::new();
     }
 
-    fn write_state_for_location(
-        &mut self,
-        mut w: &mut dyn io::Write,
-        fmt: &str,
-        results: &mut ResultsRefCursor<'_, '_, 'tcx, A>,
-        location: Location,
-    ) -> io::Result<()> {
-        // Only print a single row.
-        if location.statement_index != 0 {
-            return Ok(());
-        }
+    let re = regex!("\t?\u{001f}([+-])");
 
-        let block_trans = &self.trans_for_block[location.block];
-        let rowspan = self.body.basic_blocks()[location.block].statements.len();
+    let raw_diff = format!("{:#?}", DebugDiffWithAdapter { new, old, ctxt });
 
-        for set in &[&block_trans.gen, &block_trans.kill] {
-            write!(
-                w,
-                r#"<td {fmt} rowspan="{rowspan}" balign="left" align="left">"#,
-                fmt = fmt,
-                rowspan = rowspan
-            )?;
+    // Replace newlines in the `Debug` output with `<br/>`
+    let raw_diff = raw_diff.replace('\n', r#"<br align="left"/>"#);
 
-            pretty_print_state_elems(&mut w, results.analysis(), set.iter(), BR_LEFT, None)?;
-            write!(w, "</td>")?;
+    let mut inside_font_tag = false;
+    let html_diff = re.replace_all(&raw_diff, |captures: &regex::Captures<'_>| {
+        let mut ret = String::new();
+        if inside_font_tag {
+            ret.push_str(r#"</font>"#);
         }
 
-        Ok(())
-    }
-}
-
-/// Writes two lines, one containing the added bits and one the removed bits.
-fn write_diff<A: Analysis<'tcx>>(
-    w: &mut impl io::Write,
-    analysis: &A,
-    from: &BitSet<A::Idx>,
-    to: &BitSet<A::Idx>,
-) -> io::Result<()> {
-    assert_eq!(from.domain_size(), to.domain_size());
-    let len = from.domain_size();
-
-    let mut set = HybridBitSet::new_empty(len);
-    let mut clear = HybridBitSet::new_empty(len);
-
-    // FIXME: Implement a lazy iterator over the symmetric difference of two bitsets.
-    for i in (0..len).map(A::Idx::new) {
-        match (from.contains(i), to.contains(i)) {
-            (false, true) => set.insert(i),
-            (true, false) => clear.insert(i),
-            _ => continue,
+        let tag = match &captures[1] {
+            "+" => r#"<font color="darkgreen">+"#,
+            "-" => r#"<font color="red">-"#,
+            _ => unreachable!(),
         };
-    }
-
-    if !set.is_empty() {
-        write!(w, r#"<font color="darkgreen">+"#)?;
-        pretty_print_state_elems(w, analysis, set.iter(), ", ", LIMIT_30_ALIGN_1)?;
-        write!(w, r#"</font>"#)?;
-    }
-
-    if !set.is_empty() && !clear.is_empty() {
-        write!(w, "{}", BR_LEFT)?;
-    }
-
-    if !clear.is_empty() {
-        write!(w, r#"<font color="red">-"#)?;
-        pretty_print_state_elems(w, analysis, clear.iter(), ", ", LIMIT_30_ALIGN_1)?;
-        write!(w, r#"</font>"#)?;
-    }
-
-    Ok(())
-}
 
-const BR_LEFT: &str = r#"<br align="left"/>"#;
-const BR_LEFT_SPACE: &str = r#"<br align="left"/> "#;
+        inside_font_tag = true;
+        ret.push_str(tag);
+        ret
+    });
 
-/// Line break policy that breaks at 40 characters and starts the next line with a single space.
-const LIMIT_30_ALIGN_1: Option<LineBreak> = Some(LineBreak { sequence: BR_LEFT_SPACE, limit: 30 });
-
-struct LineBreak {
-    sequence: &'static str,
-    limit: usize,
-}
-
-/// Formats each `elem` using the pretty printer provided by `analysis` into a list with the given
-/// separator (`sep`).
-///
-/// Optionally, it will break lines using the given character sequence (usually `<br/>`) and
-/// character limit.
-fn pretty_print_state_elems<A>(
-    w: &mut impl io::Write,
-    analysis: &A,
-    elems: impl Iterator<Item = A::Idx>,
-    sep: &str,
-    line_break: Option<LineBreak>,
-) -> io::Result<bool>
-where
-    A: Analysis<'tcx>,
-{
-    let sep_width = sep.chars().count();
-
-    let mut buf = Vec::new();
-
-    let mut first = true;
-    let mut curr_line_width = 0;
-    let mut line_break_inserted = false;
-
-    for idx in elems {
-        buf.clear();
-        analysis.pretty_print_idx(&mut buf, idx)?;
-        let idx_str =
-            str::from_utf8(&buf).expect("Output of `pretty_print_idx` must be valid UTF-8");
-        let escaped = dot::escape_html(idx_str);
-        let escaped_width = escaped.chars().count();
-
-        if first {
-            first = false;
-        } else {
-            write!(w, "{}", sep)?;
-            curr_line_width += sep_width;
-
-            if let Some(line_break) = &line_break {
-                if curr_line_width + sep_width + escaped_width > line_break.limit {
-                    write!(w, "{}", line_break.sequence)?;
-                    line_break_inserted = true;
-                    curr_line_width = 0;
-                }
-            }
-        }
+    let mut html_diff = match html_diff {
+        Cow::Borrowed(_) => return raw_diff,
+        Cow::Owned(s) => s,
+    };
 
-        write!(w, "{}", escaped)?;
-        curr_line_width += escaped_width;
+    if inside_font_tag {
+        html_diff.push_str("</font>");
     }
 
-    Ok(line_break_inserted)
+    html_diff
 }
 
 /// The background color used for zebra-striping the table.
diff --git a/compiler/rustc_mir/src/dataflow/framework/lattice.rs b/compiler/rustc_mir/src/dataflow/framework/lattice.rs
new file mode 100644
index 00000000000..e7ef9267db5
--- /dev/null
+++ b/compiler/rustc_mir/src/dataflow/framework/lattice.rs
@@ -0,0 +1,230 @@
+//! Traits used to represent [lattices] for use as the domain of a dataflow analysis.
+//!
+//! # Overview
+//!
+//! The most common lattice is a powerset of some set `S`, ordered by [set inclusion]. The [Hasse
+//! diagram] for the powerset of a set with two elements (`X` and `Y`) is shown below. Note that
+//! distinct elements at the same height in a Hasse diagram (e.g. `{X}` and `{Y}`) are
+//! *incomparable*, not equal.
+//!
+//! ```text
+//!      {X, Y}    <- top
+//!       /  \
+//!    {X}    {Y}
+//!       \  /
+//!        {}      <- bottom
+//!
+//! ```
+//!
+//! The defining characteristic of a lattice—the one that differentiates it from a [partially
+//! ordered set][poset]—is the existence of a *unique* least upper and greatest lower bound for
+//! every pair of elements. The lattice join operator (`∨`) returns the least upper bound, and the
+//! lattice meet operator (`∧`) returns the greatest lower bound. Types that implement one operator
+//! but not the other are known as semilattices. Dataflow analysis only uses the join operator and
+//! will work with any join-semilattice, but both should be specified when possible.
+//!
+//! ## `PartialOrd`
+//!
+//! Given that they represent partially ordered sets, you may be surprised that [`JoinSemiLattice`]
+//! and [`MeetSemiLattice`] do not have [`PartialOrd`][std::cmp::PartialOrd] as a supertrait. This
+//! is because most standard library types use lexicographic ordering instead of set inclusion for
+//! their `PartialOrd` impl. Since we do not actually need to compare lattice elements to run a
+//! dataflow analysis, there's no need for a newtype wrapper with a custom `PartialOrd` impl. The
+//! only benefit would be the ability to check that the least upper (or greatest lower) bound
+//! returned by the lattice join (or meet) operator was in fact greater (or lower) than the inputs.
+//!
+//! [lattices]: https://en.wikipedia.org/wiki/Lattice_(order)
+//! [set inclusion]: https://en.wikipedia.org/wiki/Subset
+//! [Hasse diagram]: https://en.wikipedia.org/wiki/Hasse_diagram
+//! [poset]: https://en.wikipedia.org/wiki/Partially_ordered_set
+
+use rustc_index::bit_set::BitSet;
+use rustc_index::vec::{Idx, IndexVec};
+
+/// A [partially ordered set][poset] that has a [least upper bound][lub] for any pair of elements
+/// in the set.
+///
+/// [lub]: https://en.wikipedia.org/wiki/Infimum_and_supremum
+/// [poset]: https://en.wikipedia.org/wiki/Partially_ordered_set
+pub trait JoinSemiLattice: Eq {
+    /// Computes the least upper bound of two elements, storing the result in `self` and returning
+    /// `true` if `self` has changed.
+    ///
+    /// The lattice join operator is abbreviated as `∨`.
+    fn join(&mut self, other: &Self) -> bool;
+}
+
+/// A [partially ordered set][poset] that has a [greatest lower bound][glb] for any pair of
+/// elements in the set.
+///
+/// Dataflow analyses only require that their domains implement [`JoinSemiLattice`], not
+/// `MeetSemiLattice`. However, types that will be used as dataflow domains should implement both
+/// so that they can be used with [`Dual`].
+///
+/// [glb]: https://en.wikipedia.org/wiki/Infimum_and_supremum
+/// [poset]: https://en.wikipedia.org/wiki/Partially_ordered_set
+pub trait MeetSemiLattice: Eq {
+    /// Computes the greatest lower bound of two elements, storing the result in `self` and
+    /// returning `true` if `self` has changed.
+    ///
+    /// The lattice meet operator is abbreviated as `∧`.
+    fn meet(&mut self, other: &Self) -> bool;
+}
+
+/// A `bool` is a "two-point" lattice with `true` as the top element and `false` as the bottom:
+///
+/// ```text
+///      true
+///        |
+///      false
+/// ```
+impl JoinSemiLattice for bool {
+    fn join(&mut self, other: &Self) -> bool {
+        if let (false, true) = (*self, *other) {
+            *self = true;
+            return true;
+        }
+
+        false
+    }
+}
+
+impl MeetSemiLattice for bool {
+    fn meet(&mut self, other: &Self) -> bool {
+        if let (true, false) = (*self, *other) {
+            *self = false;
+            return true;
+        }
+
+        false
+    }
+}
+
+/// A tuple (or list) of lattices is itself a lattice whose least upper bound is the concatenation
+/// of the least upper bounds of each element of the tuple (or list).
+///
+/// In other words:
+///     (A₀, A₁, ..., Aₙ) ∨ (B₀, B₁, ..., Bₙ) = (A₀∨B₀, A₁∨B₁, ..., Aₙ∨Bₙ)
+impl<I: Idx, T: JoinSemiLattice> JoinSemiLattice for IndexVec<I, T> {
+    fn join(&mut self, other: &Self) -> bool {
+        assert_eq!(self.len(), other.len());
+
+        let mut changed = false;
+        for (a, b) in self.iter_mut().zip(other.iter()) {
+            changed |= a.join(b);
+        }
+        changed
+    }
+}
+
+impl<I: Idx, T: MeetSemiLattice> MeetSemiLattice for IndexVec<I, T> {
+    fn meet(&mut self, other: &Self) -> bool {
+        assert_eq!(self.len(), other.len());
+
+        let mut changed = false;
+        for (a, b) in self.iter_mut().zip(other.iter()) {
+            changed |= a.meet(b);
+        }
+        changed
+    }
+}
+
+/// A `BitSet` represents the lattice formed by the powerset of all possible values of
+/// the index type `T` ordered by inclusion. Equivalently, it is a tuple of "two-point" lattices,
+/// one for each possible value of `T`.
+impl<T: Idx> JoinSemiLattice for BitSet<T> {
+    fn join(&mut self, other: &Self) -> bool {
+        self.union(other)
+    }
+}
+
+impl<T: Idx> MeetSemiLattice for BitSet<T> {
+    fn meet(&mut self, other: &Self) -> bool {
+        self.intersect(other)
+    }
+}
+
+/// The counterpart of a given semilattice `T` using the [inverse order].
+///
+/// The dual of a join-semilattice is a meet-semilattice and vice versa. For example, the dual of a
+/// powerset has the empty set as its top element and the full set as its bottom element and uses
+/// set *intersection* as its join operator.
+///
+/// [inverse order]: https://en.wikipedia.org/wiki/Duality_(order_theory)
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub struct Dual<T>(pub T);
+
+impl<T> std::borrow::Borrow<T> for Dual<T> {
+    fn borrow(&self) -> &T {
+        &self.0
+    }
+}
+
+impl<T> std::borrow::BorrowMut<T> for Dual<T> {
+    fn borrow_mut(&mut self) -> &mut T {
+        &mut self.0
+    }
+}
+
+impl<T: MeetSemiLattice> JoinSemiLattice for Dual<T> {
+    fn join(&mut self, other: &Self) -> bool {
+        self.0.meet(&other.0)
+    }
+}
+
+impl<T: JoinSemiLattice> MeetSemiLattice for Dual<T> {
+    fn meet(&mut self, other: &Self) -> bool {
+        self.0.join(&other.0)
+    }
+}
+
+/// Extends a type `T` with top and bottom elements to make it a partially ordered set in which no
+/// value of `T` is comparable with any other. A flat set has the following [Hasse diagram]:
+///
+/// ```text
+///         top
+///       / /  \ \
+/// all possible values of `T`
+///       \ \  / /
+///        bottom
+/// ```
+///
+/// [Hasse diagram]: https://en.wikipedia.org/wiki/Hasse_diagram
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum FlatSet<T> {
+    Bottom,
+    Elem(T),
+    Top,
+}
+
+impl<T: Clone + Eq> JoinSemiLattice for FlatSet<T> {
+    fn join(&mut self, other: &Self) -> bool {
+        let result = match (&*self, other) {
+            (Self::Top, _) | (_, Self::Bottom) => return false,
+            (Self::Elem(a), Self::Elem(b)) if a == b => return false,
+
+            (Self::Bottom, Self::Elem(x)) => Self::Elem(x.clone()),
+
+            _ => Self::Top,
+        };
+
+        *self = result;
+        true
+    }
+}
+
+impl<T: Clone + Eq> MeetSemiLattice for FlatSet<T> {
+    fn meet(&mut self, other: &Self) -> bool {
+        let result = match (&*self, other) {
+            (Self::Bottom, _) | (_, Self::Top) => return false,
+            (Self::Elem(ref a), Self::Elem(ref b)) if a == b => return false,
+
+            (Self::Top, Self::Elem(ref x)) => Self::Elem(x.clone()),
+
+            _ => Self::Bottom,
+        };
+
+        *self = result;
+        true
+    }
+}
diff --git a/compiler/rustc_mir/src/dataflow/framework/mod.rs b/compiler/rustc_mir/src/dataflow/framework/mod.rs
index a21bbacb467..eefa1395a62 100644
--- a/compiler/rustc_mir/src/dataflow/framework/mod.rs
+++ b/compiler/rustc_mir/src/dataflow/framework/mod.rs
@@ -30,8 +30,8 @@
 //!
 //! [gen-kill]: https://en.wikipedia.org/wiki/Data-flow_analysis#Bit_vector_problems
 
+use std::borrow::BorrowMut;
 use std::cmp::Ordering;
-use std::io;
 
 use rustc_hir::def_id::DefId;
 use rustc_index::bit_set::{BitSet, HybridBitSet};
@@ -43,69 +43,27 @@ use rustc_target::abi::VariantIdx;
 mod cursor;
 mod direction;
 mod engine;
+pub mod fmt;
 mod graphviz;
+pub mod lattice;
 mod visitor;
 
 pub use self::cursor::{ResultsCursor, ResultsRefCursor};
 pub use self::direction::{Backward, Direction, Forward};
 pub use self::engine::{Engine, Results};
+pub use self::lattice::{JoinSemiLattice, MeetSemiLattice};
 pub use self::visitor::{visit_results, ResultsVisitor};
 pub use self::visitor::{BorrowckFlowState, BorrowckResults};
 
-/// Parameterization for the precise form of data flow that is used.
-///
-/// `BottomValue` determines whether the initial entry set for each basic block is empty or full.
-/// This also determines the semantics of the lattice `join` operator used to merge dataflow
-/// results, since dataflow works by starting at the bottom and moving monotonically to a fixed
-/// point.
-///
-/// This means, for propagation across the graph, that you either want to start at all-zeroes and
-/// then use Union as your merge when propagating, or you start at all-ones and then use Intersect
-/// as your merge when propagating.
-pub trait BottomValue {
-    /// Specifies the initial value for each bit in the entry set for each basic block.
-    const BOTTOM_VALUE: bool;
-
-    /// Merges `in_set` into `inout_set`, returning `true` if `inout_set` changed.
-    ///
-    /// It is almost certainly wrong to override this, since it automatically applies
-    /// * `inout_set & in_set` if `BOTTOM_VALUE == true`
-    /// * `inout_set | in_set` if `BOTTOM_VALUE == false`
-    ///
-    /// This means that if a bit is not `BOTTOM_VALUE`, it is propagated into all target blocks.
-    /// For clarity, the above statement again from a different perspective:
-    /// A bit in the block's entry set is `!BOTTOM_VALUE` if *any* predecessor block's bit value is
-    /// `!BOTTOM_VALUE`.
-    ///
-    /// There are situations where you want the opposite behaviour: propagate only if *all*
-    /// predecessor blocks's value is `!BOTTOM_VALUE`.
-    /// E.g. if you want to know whether a bit is *definitely* set at a specific location. This
-    /// means that all code paths leading to the location must have set the bit, instead of any
-    /// code path leading there.
-    ///
-    /// If you want this kind of "definitely set" analysis, you need to
-    /// 1. Invert `BOTTOM_VALUE`
-    /// 2. Reset the `entry_set` in `start_block_effect` to `!BOTTOM_VALUE`
-    /// 3. Override `join` to do the opposite from what it's doing now.
-    #[inline]
-    fn join<T: Idx>(&self, inout_set: &mut BitSet<T>, in_set: &BitSet<T>) -> bool {
-        if !Self::BOTTOM_VALUE { inout_set.union(in_set) } else { inout_set.intersect(in_set) }
-    }
-}
-
 /// Define the domain of a dataflow problem.
 ///
-/// This trait specifies the lattice on which this analysis operates. For now, this must be a
-/// powerset of values of type `Idx`. The elements of this lattice are represented with a `BitSet`
-/// and referred to as the state vector.
-///
-/// This trait also defines the initial value for the dataflow state upon entry to the
-/// `START_BLOCK`, as well as some names used to refer to this analysis when debugging.
-pub trait AnalysisDomain<'tcx>: BottomValue {
-    /// The type of the elements in the state vector.
-    type Idx: Idx;
+/// This trait specifies the lattice on which this analysis operates (the domain) as well as its
+/// initial value at the entry point of each basic block.
+pub trait AnalysisDomain<'tcx> {
+    /// The type that holds the dataflow state at any given point in the program.
+    type Domain: Clone + JoinSemiLattice;
 
-    /// The direction of this analyis. Either `Forward` or `Backward`.
+    /// The direction of this analysis. Either `Forward` or `Backward`.
     type Direction: Direction = Forward;
 
     /// A descriptive name for this analysis. Used only for debugging.
@@ -114,11 +72,10 @@ pub trait AnalysisDomain<'tcx>: BottomValue {
     /// suitable as part of a filename.
     const NAME: &'static str;
 
-    /// The size of the state vector.
-    fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize;
+    /// The initial value of the dataflow state upon entry to each basic block.
+    fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain;
 
-    /// Mutates the entry set of the `START_BLOCK` to contain the initial state for dataflow
-    /// analysis.
+    /// Mutates the initial value of the dataflow state upon entry to the `START_BLOCK`.
     ///
     /// For backward analyses, initial state besides the bottom value is not yet supported. Trying
     /// to mutate the initial state will result in a panic.
@@ -126,20 +83,30 @@ pub trait AnalysisDomain<'tcx>: BottomValue {
     // FIXME: For backward dataflow analyses, the initial state should be applied to every basic
     // block where control flow could exit the MIR body (e.g., those terminated with `return` or
     // `resume`). It's not obvious how to handle `yield` points in generators, however.
-    fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>);
-
-    /// Prints an element in the state vector for debugging.
-    fn pretty_print_idx(&self, w: &mut impl io::Write, idx: Self::Idx) -> io::Result<()> {
-        write!(w, "{:?}", idx)
-    }
+    fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain);
 }
 
 /// A dataflow problem with an arbitrarily complex transfer function.
+///
+/// # Convergence
+///
+/// When implementing this trait directly (not via [`GenKillAnalysis`]), it's possible to choose a
+/// transfer function such that the analysis does not reach fixpoint. To guarantee convergence,
+/// your transfer functions must maintain the following invariant:
+///
+/// > If the dataflow state **before** some point in the program changes to be greater
+/// than the prior state **before** that point, the dataflow state **after** that point must
+/// also change to be greater than the prior state **after** that point.
+///
+/// This invariant guarantees that the dataflow state at a given point in the program increases
+/// monotonically until fixpoint is reached. Note that this monotonicity requirement only applies
+/// to the same point in the program at different points in time. The dataflow state at a given
+/// point in the program may or may not be greater than the state at any preceding point.
 pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
     /// Updates the current dataflow state with the effect of evaluating a statement.
     fn apply_statement_effect(
         &self,
-        state: &mut BitSet<Self::Idx>,
+        state: &mut Self::Domain,
         statement: &mir::Statement<'tcx>,
         location: Location,
     );
@@ -152,7 +119,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
     /// analyses should not implement this without implementing `apply_statement_effect`.
     fn apply_before_statement_effect(
         &self,
-        _state: &mut BitSet<Self::Idx>,
+        _state: &mut Self::Domain,
         _statement: &mir::Statement<'tcx>,
         _location: Location,
     ) {
@@ -166,7 +133,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
     /// initialized here.
     fn apply_terminator_effect(
         &self,
-        state: &mut BitSet<Self::Idx>,
+        state: &mut Self::Domain,
         terminator: &mir::Terminator<'tcx>,
         location: Location,
     );
@@ -179,7 +146,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
     /// analyses should not implement this without implementing `apply_terminator_effect`.
     fn apply_before_terminator_effect(
         &self,
-        _state: &mut BitSet<Self::Idx>,
+        _state: &mut Self::Domain,
         _terminator: &mir::Terminator<'tcx>,
         _location: Location,
     ) {
@@ -192,7 +159,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
     /// edges.
     fn apply_call_return_effect(
         &self,
-        state: &mut BitSet<Self::Idx>,
+        state: &mut Self::Domain,
         block: BasicBlock,
         func: &mir::Operand<'tcx>,
         args: &[mir::Operand<'tcx>],
@@ -207,7 +174,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
     /// By default, no effects happen.
     fn apply_yield_resume_effect(
         &self,
-        _state: &mut BitSet<Self::Idx>,
+        _state: &mut Self::Domain,
         _resume_block: BasicBlock,
         _resume_place: mir::Place<'tcx>,
     ) {
@@ -222,7 +189,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
     /// FIXME: This class of effects is not supported for backward dataflow analyses.
     fn apply_discriminant_switch_effect(
         &self,
-        _state: &mut BitSet<Self::Idx>,
+        _state: &mut Self::Domain,
         _block: BasicBlock,
         _enum_place: mir::Place<'tcx>,
         _adt: &ty::AdtDef,
@@ -264,6 +231,8 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
 ///
 /// `Analysis` is automatically implemented for all implementers of `GenKillAnalysis`.
 pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> {
+    type Idx: Idx;
+
     /// See `Analysis::apply_statement_effect`.
     fn statement_effect(
         &self,
@@ -332,10 +301,11 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> {
 impl<A> Analysis<'tcx> for A
 where
     A: GenKillAnalysis<'tcx>,
+    A::Domain: GenKill<A::Idx> + BorrowMut<BitSet<A::Idx>>,
 {
     fn apply_statement_effect(
         &self,
-        state: &mut BitSet<Self::Idx>,
+        state: &mut A::Domain,
         statement: &mir::Statement<'tcx>,
         location: Location,
     ) {
@@ -344,7 +314,7 @@ where
 
     fn apply_before_statement_effect(
         &self,
-        state: &mut BitSet<Self::Idx>,
+        state: &mut A::Domain,
         statement: &mir::Statement<'tcx>,
         location: Location,
     ) {
@@ -353,7 +323,7 @@ where
 
     fn apply_terminator_effect(
         &self,
-        state: &mut BitSet<Self::Idx>,
+        state: &mut A::Domain,
         terminator: &mir::Terminator<'tcx>,
         location: Location,
     ) {
@@ -362,7 +332,7 @@ where
 
     fn apply_before_terminator_effect(
         &self,
-        state: &mut BitSet<Self::Idx>,
+        state: &mut A::Domain,
         terminator: &mir::Terminator<'tcx>,
         location: Location,
     ) {
@@ -371,7 +341,7 @@ where
 
     fn apply_call_return_effect(
         &self,
-        state: &mut BitSet<Self::Idx>,
+        state: &mut A::Domain,
         block: BasicBlock,
         func: &mir::Operand<'tcx>,
         args: &[mir::Operand<'tcx>],
@@ -382,7 +352,7 @@ where
 
     fn apply_yield_resume_effect(
         &self,
-        state: &mut BitSet<Self::Idx>,
+        state: &mut A::Domain,
         resume_block: BasicBlock,
         resume_place: mir::Place<'tcx>,
     ) {
@@ -391,7 +361,7 @@ where
 
     fn apply_discriminant_switch_effect(
         &self,
-        state: &mut BitSet<Self::Idx>,
+        state: &mut A::Domain,
         block: BasicBlock,
         enum_place: mir::Place<'tcx>,
         adt: &ty::AdtDef,
@@ -450,7 +420,7 @@ pub trait GenKill<T> {
 /// applied multiple times efficiently. When there are multiple calls to `gen` and/or `kill` for
 /// the same element, the most recent one takes precedence.
 #[derive(Clone)]
-pub struct GenKillSet<T: Idx> {
+pub struct GenKillSet<T> {
     gen: HybridBitSet<T>,
     kill: HybridBitSet<T>,
 }
@@ -464,7 +434,6 @@ impl<T: Idx> GenKillSet<T> {
         }
     }
 
-    /// Applies this transfer function to the given state vector.
     pub fn apply(&self, state: &mut BitSet<T>) {
         state.union(&self.gen);
         state.subtract(&self.kill);
@@ -493,6 +462,16 @@ impl<T: Idx> GenKill<T> for BitSet<T> {
     }
 }
 
+impl<T: Idx> GenKill<T> for lattice::Dual<BitSet<T>> {
+    fn gen(&mut self, elem: T) {
+        self.0.insert(elem);
+    }
+
+    fn kill(&mut self, elem: T) {
+        self.0.remove(elem);
+    }
+}
+
 // NOTE: DO NOT CHANGE VARIANT ORDER. The derived `Ord` impls rely on the current order.
 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
 pub enum Effect {
diff --git a/compiler/rustc_mir/src/dataflow/framework/tests.rs b/compiler/rustc_mir/src/dataflow/framework/tests.rs
index 9349f5133a5..a5989121679 100644
--- a/compiler/rustc_mir/src/dataflow/framework/tests.rs
+++ b/compiler/rustc_mir/src/dataflow/framework/tests.rs
@@ -9,7 +9,6 @@ use rustc_middle::ty;
 use rustc_span::DUMMY_SP;
 
 use super::*;
-use crate::dataflow::BottomValue;
 
 /// Creates a `mir::Body` with a few disconnected basic blocks.
 ///
@@ -92,13 +91,13 @@ impl<D: Direction> MockAnalysis<'tcx, D> {
     /// The entry set for each `BasicBlock` is the ID of that block offset by a fixed amount to
     /// avoid colliding with the statement/terminator effects.
     fn mock_entry_set(&self, bb: BasicBlock) -> BitSet<usize> {
-        let mut ret = BitSet::new_empty(self.bits_per_block(self.body));
+        let mut ret = self.bottom_value(self.body);
         ret.insert(Self::BASIC_BLOCK_OFFSET + bb.index());
         ret
     }
 
     fn mock_entry_sets(&self) -> IndexVec<BasicBlock, BitSet<usize>> {
-        let empty = BitSet::new_empty(self.bits_per_block(self.body));
+        let empty = self.bottom_value(self.body);
         let mut ret = IndexVec::from_elem(empty, &self.body.basic_blocks());
 
         for (bb, _) in self.body.basic_blocks().iter_enumerated() {
@@ -130,7 +129,7 @@ impl<D: Direction> MockAnalysis<'tcx, D> {
     /// would be `[102, 0, 1, 2, 3, 4]`.
     fn expected_state_at_target(&self, target: SeekTarget) -> BitSet<usize> {
         let block = target.block();
-        let mut ret = BitSet::new_empty(self.bits_per_block(self.body));
+        let mut ret = self.bottom_value(self.body);
         ret.insert(Self::BASIC_BLOCK_OFFSET + block.index());
 
         let target = match target {
@@ -161,21 +160,17 @@ impl<D: Direction> MockAnalysis<'tcx, D> {
     }
 }
 
-impl<D: Direction> BottomValue for MockAnalysis<'tcx, D> {
-    const BOTTOM_VALUE: bool = false;
-}
-
 impl<D: Direction> AnalysisDomain<'tcx> for MockAnalysis<'tcx, D> {
-    type Idx = usize;
+    type Domain = BitSet<usize>;
     type Direction = D;
 
     const NAME: &'static str = "mock";
 
-    fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize {
-        Self::BASIC_BLOCK_OFFSET + body.basic_blocks().len()
+    fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
+        BitSet::new_empty(Self::BASIC_BLOCK_OFFSET + body.basic_blocks().len())
     }
 
-    fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut BitSet<Self::Idx>) {
+    fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) {
         unimplemented!("This is never called since `MockAnalysis` is never iterated to fixpoint");
     }
 }
@@ -183,7 +178,7 @@ impl<D: Direction> AnalysisDomain<'tcx> for MockAnalysis<'tcx, D> {
 impl<D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> {
     fn apply_statement_effect(
         &self,
-        state: &mut BitSet<Self::Idx>,
+        state: &mut Self::Domain,
         _statement: &mir::Statement<'tcx>,
         location: Location,
     ) {
@@ -193,7 +188,7 @@ impl<D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> {
 
     fn apply_before_statement_effect(
         &self,
-        state: &mut BitSet<Self::Idx>,
+        state: &mut Self::Domain,
         _statement: &mir::Statement<'tcx>,
         location: Location,
     ) {
@@ -203,7 +198,7 @@ impl<D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> {
 
     fn apply_terminator_effect(
         &self,
-        state: &mut BitSet<Self::Idx>,
+        state: &mut Self::Domain,
         _terminator: &mir::Terminator<'tcx>,
         location: Location,
     ) {
@@ -213,7 +208,7 @@ impl<D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> {
 
     fn apply_before_terminator_effect(
         &self,
-        state: &mut BitSet<Self::Idx>,
+        state: &mut Self::Domain,
         _terminator: &mir::Terminator<'tcx>,
         location: Location,
     ) {
@@ -223,7 +218,7 @@ impl<D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> {
 
     fn apply_call_return_effect(
         &self,
-        _state: &mut BitSet<Self::Idx>,
+        _state: &mut Self::Domain,
         _block: BasicBlock,
         _func: &mir::Operand<'tcx>,
         _args: &[mir::Operand<'tcx>],
diff --git a/compiler/rustc_mir/src/dataflow/framework/visitor.rs b/compiler/rustc_mir/src/dataflow/framework/visitor.rs
index 257f3cb9a6d..82eb734ed06 100644
--- a/compiler/rustc_mir/src/dataflow/framework/visitor.rs
+++ b/compiler/rustc_mir/src/dataflow/framework/visitor.rs
@@ -1,4 +1,3 @@
-use rustc_index::bit_set::BitSet;
 use rustc_middle::mir::{self, BasicBlock, Location};
 
 use super::{Analysis, Direction, Results};
@@ -139,16 +138,16 @@ impl<'tcx, A> ResultsVisitable<'tcx> for Results<'tcx, A>
 where
     A: Analysis<'tcx>,
 {
-    type FlowState = BitSet<A::Idx>;
+    type FlowState = A::Domain;
 
     type Direction = A::Direction;
 
     fn new_flow_state(&self, body: &mir::Body<'tcx>) -> Self::FlowState {
-        BitSet::new_empty(self.analysis.bits_per_block(body))
+        self.analysis.bottom_value(body)
     }
 
     fn reset_to_block_entry(&self, state: &mut Self::FlowState, block: BasicBlock) {
-        state.overwrite(&self.entry_set_for_block(block));
+        state.clone_from(&self.entry_set_for_block(block));
     }
 
     fn reconstruct_before_statement_effect(
@@ -217,11 +216,11 @@ macro_rules! impl_visitable {
             $( $A: Analysis<'tcx, Direction = D>, )*
         {
             type Direction = D;
-            type FlowState = $T<$( BitSet<$A::Idx> ),*>;
+            type FlowState = $T<$( $A::Domain ),*>;
 
             fn new_flow_state(&self, body: &mir::Body<'tcx>) -> Self::FlowState {
                 $T {
-                    $( $field: BitSet::new_empty(self.$field.analysis.bits_per_block(body)) ),*
+                    $( $field: self.$field.analysis.bottom_value(body) ),*
                 }
             }
 
@@ -230,7 +229,7 @@ macro_rules! impl_visitable {
                 state: &mut Self::FlowState,
                 block: BasicBlock,
             ) {
-                $( state.$field.overwrite(&self.$field.entry_set_for_block(block)); )*
+                $( state.$field.clone_from(&self.$field.entry_set_for_block(block)); )*
             }
 
             fn reconstruct_before_statement_effect(
diff --git a/compiler/rustc_mir/src/dataflow/impls/borrowed_locals.rs b/compiler/rustc_mir/src/dataflow/impls/borrowed_locals.rs
index a3fc51cad65..65e04ed6831 100644
--- a/compiler/rustc_mir/src/dataflow/impls/borrowed_locals.rs
+++ b/compiler/rustc_mir/src/dataflow/impls/borrowed_locals.rs
@@ -82,15 +82,15 @@ impl<K> AnalysisDomain<'tcx> for MaybeBorrowedLocals<K>
 where
     K: BorrowAnalysisKind<'tcx>,
 {
-    type Idx = Local;
-
+    type Domain = BitSet<Local>;
     const NAME: &'static str = K::ANALYSIS_NAME;
 
-    fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize {
-        body.local_decls().len()
+    fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
+        // bottom = unborrowed
+        BitSet::new_empty(body.local_decls().len())
     }
 
-    fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut BitSet<Self::Idx>) {
+    fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) {
         // No locals are aliased on function entry
     }
 }
@@ -99,6 +99,8 @@ impl<K> GenKillAnalysis<'tcx> for MaybeBorrowedLocals<K>
 where
     K: BorrowAnalysisKind<'tcx>,
 {
+    type Idx = Local;
+
     fn statement_effect(
         &self,
         trans: &mut impl GenKill<Self::Idx>,
@@ -128,11 +130,6 @@ where
     }
 }
 
-impl<K> BottomValue for MaybeBorrowedLocals<K> {
-    // bottom = unborrowed
-    const BOTTOM_VALUE: bool = false;
-}
-
 /// A `Visitor` that defines the transfer function for `MaybeBorrowedLocals`.
 struct TransferFunction<'a, T, K> {
     trans: &'a mut T,
diff --git a/compiler/rustc_mir/src/dataflow/impls/borrows.rs b/compiler/rustc_mir/src/dataflow/impls/borrows.rs
index aeb7ffe3e3b..0be13b6ba81 100644
--- a/compiler/rustc_mir/src/dataflow/impls/borrows.rs
+++ b/compiler/rustc_mir/src/dataflow/impls/borrows.rs
@@ -8,9 +8,9 @@ use rustc_index::bit_set::BitSet;
 use crate::borrow_check::{
     places_conflict, BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext, ToRegionVid,
 };
-use crate::dataflow::BottomValue;
-use crate::dataflow::{self, GenKill};
+use crate::dataflow::{self, fmt::DebugWithContext, GenKill};
 
+use std::fmt;
 use std::rc::Rc;
 
 rustc_index::newtype_index! {
@@ -227,25 +227,24 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
 }
 
 impl<'tcx> dataflow::AnalysisDomain<'tcx> for Borrows<'_, 'tcx> {
-    type Idx = BorrowIndex;
+    type Domain = BitSet<BorrowIndex>;
 
     const NAME: &'static str = "borrows";
 
-    fn bits_per_block(&self, _: &mir::Body<'tcx>) -> usize {
-        self.borrow_set.len() * 2
+    fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
+        // bottom = nothing is reserved or activated yet;
+        BitSet::new_empty(self.borrow_set.len() * 2)
     }
 
-    fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut BitSet<Self::Idx>) {
+    fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) {
         // no borrows of code region_scopes have been taken prior to
         // function execution, so this method has no effect.
     }
-
-    fn pretty_print_idx(&self, w: &mut impl std::io::Write, idx: Self::Idx) -> std::io::Result<()> {
-        write!(w, "{:?}", self.location(idx))
-    }
 }
 
 impl<'tcx> dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
+    type Idx = BorrowIndex;
+
     fn before_statement_effect(
         &self,
         trans: &mut impl GenKill<Self::Idx>,
@@ -344,7 +343,8 @@ impl<'tcx> dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
     }
 }
 
-impl<'a, 'tcx> BottomValue for Borrows<'a, 'tcx> {
-    /// bottom = nothing is reserved or activated yet;
-    const BOTTOM_VALUE: bool = false;
+impl DebugWithContext<Borrows<'_, '_>> for BorrowIndex {
+    fn fmt_with(&self, ctxt: &Borrows<'_, '_>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "{:?}", ctxt.location(*self))
+    }
 }
diff --git a/compiler/rustc_mir/src/dataflow/impls/init_locals.rs b/compiler/rustc_mir/src/dataflow/impls/init_locals.rs
index 0e7cd1bb0e4..bb7292cd033 100644
--- a/compiler/rustc_mir/src/dataflow/impls/init_locals.rs
+++ b/compiler/rustc_mir/src/dataflow/impls/init_locals.rs
@@ -2,7 +2,7 @@
 //!
 //! A local will be maybe initialized if *any* projections of that local might be initialized.
 
-use crate::dataflow::{self, BottomValue, GenKill};
+use crate::dataflow::{self, GenKill};
 
 use rustc_index::bit_set::BitSet;
 use rustc_middle::mir::visit::{PlaceContext, Visitor};
@@ -10,21 +10,17 @@ use rustc_middle::mir::{self, BasicBlock, Local, Location};
 
 pub struct MaybeInitializedLocals;
 
-impl BottomValue for MaybeInitializedLocals {
-    /// bottom = uninit
-    const BOTTOM_VALUE: bool = false;
-}
-
 impl dataflow::AnalysisDomain<'tcx> for MaybeInitializedLocals {
-    type Idx = Local;
+    type Domain = BitSet<Local>;
 
     const NAME: &'static str = "maybe_init_locals";
 
-    fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize {
-        body.local_decls.len()
+    fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
+        // bottom = uninit
+        BitSet::new_empty(body.local_decls.len())
     }
 
-    fn initialize_start_block(&self, body: &mir::Body<'tcx>, entry_set: &mut BitSet<Self::Idx>) {
+    fn initialize_start_block(&self, body: &mir::Body<'tcx>, entry_set: &mut Self::Domain) {
         // Function arguments are initialized to begin with.
         for arg in body.args_iter() {
             entry_set.insert(arg);
@@ -33,6 +29,8 @@ impl dataflow::AnalysisDomain<'tcx> for MaybeInitializedLocals {
 }
 
 impl dataflow::GenKillAnalysis<'tcx> for MaybeInitializedLocals {
+    type Idx = Local;
+
     fn statement_effect(
         &self,
         trans: &mut impl GenKill<Self::Idx>,
@@ -99,7 +97,6 @@ where
             PlaceContext::NonUse(
                 NonUseContext::StorageLive
                 | NonUseContext::AscribeUserTy
-                | NonUseContext::Coverage
                 | NonUseContext::VarDebugInfo,
             )
             | PlaceContext::NonMutatingUse(
diff --git a/compiler/rustc_mir/src/dataflow/impls/liveness.rs b/compiler/rustc_mir/src/dataflow/impls/liveness.rs
index 784b0bd9293..b0da28156d1 100644
--- a/compiler/rustc_mir/src/dataflow/impls/liveness.rs
+++ b/compiler/rustc_mir/src/dataflow/impls/liveness.rs
@@ -2,7 +2,7 @@ use rustc_index::bit_set::BitSet;
 use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
 use rustc_middle::mir::{self, Local, Location};
 
-use crate::dataflow::{AnalysisDomain, Backward, BottomValue, GenKill, GenKillAnalysis};
+use crate::dataflow::{AnalysisDomain, Backward, GenKill, GenKillAnalysis};
 
 /// A [live-variable dataflow analysis][liveness].
 ///
@@ -22,27 +22,25 @@ impl MaybeLiveLocals {
     }
 }
 
-impl BottomValue for MaybeLiveLocals {
-    // bottom = not live
-    const BOTTOM_VALUE: bool = false;
-}
-
 impl AnalysisDomain<'tcx> for MaybeLiveLocals {
-    type Idx = Local;
+    type Domain = BitSet<Local>;
     type Direction = Backward;
 
     const NAME: &'static str = "liveness";
 
-    fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize {
-        body.local_decls.len()
+    fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
+        // bottom = not live
+        BitSet::new_empty(body.local_decls.len())
     }
 
-    fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut BitSet<Self::Idx>) {
+    fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) {
         // No variables are live until we observe a use
     }
 }
 
 impl GenKillAnalysis<'tcx> for MaybeLiveLocals {
+    type Idx = Local;
+
     fn statement_effect(
         &self,
         trans: &mut impl GenKill<Self::Idx>,
diff --git a/compiler/rustc_mir/src/dataflow/impls/mod.rs b/compiler/rustc_mir/src/dataflow/impls/mod.rs
index 8975faec487..1769feaf7a5 100644
--- a/compiler/rustc_mir/src/dataflow/impls/mod.rs
+++ b/compiler/rustc_mir/src/dataflow/impls/mod.rs
@@ -13,7 +13,7 @@ use super::MoveDataParamEnv;
 use crate::util::elaborate_drops::DropFlagState;
 
 use super::move_paths::{HasMoveData, InitIndex, InitKind, MoveData, MovePathIndex};
-use super::{AnalysisDomain, BottomValue, GenKill, GenKillAnalysis};
+use super::{lattice, AnalysisDomain, GenKill, GenKillAnalysis};
 
 use super::drop_flag_effects_for_function_entry;
 use super::drop_flag_effects_for_location;
@@ -204,7 +204,7 @@ impl<'a, 'tcx> HasMoveData<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> {
 
 /// `EverInitializedPlaces` tracks all places that might have ever been
 /// initialized upon reaching a particular point in the control flow
-/// for a function, without an intervening `Storage Dead`.
+/// for a function, without an intervening `StorageDead`.
 ///
 /// This dataflow is used to determine if an immutable local variable may
 /// be assigned to.
@@ -290,27 +290,25 @@ impl<'a, 'tcx> DefinitelyInitializedPlaces<'a, 'tcx> {
 }
 
 impl<'tcx> AnalysisDomain<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
-    type Idx = MovePathIndex;
-
+    type Domain = BitSet<MovePathIndex>;
     const NAME: &'static str = "maybe_init";
 
-    fn bits_per_block(&self, _: &mir::Body<'tcx>) -> usize {
-        self.move_data().move_paths.len()
+    fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
+        // bottom = uninitialized
+        BitSet::new_empty(self.move_data().move_paths.len())
     }
 
-    fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>) {
+    fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) {
         drop_flag_effects_for_function_entry(self.tcx, self.body, self.mdpe, |path, s| {
             assert!(s == DropFlagState::Present);
             state.insert(path);
         });
     }
-
-    fn pretty_print_idx(&self, w: &mut impl std::io::Write, mpi: Self::Idx) -> std::io::Result<()> {
-        write!(w, "{}", self.move_data().move_paths[mpi])
-    }
 }
 
 impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
+    type Idx = MovePathIndex;
+
     fn statement_effect(
         &self,
         trans: &mut impl GenKill<Self::Idx>,
@@ -376,18 +374,18 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
 }
 
 impl<'tcx> AnalysisDomain<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
-    type Idx = MovePathIndex;
+    type Domain = BitSet<MovePathIndex>;
 
     const NAME: &'static str = "maybe_uninit";
 
-    fn bits_per_block(&self, _: &mir::Body<'tcx>) -> usize {
-        self.move_data().move_paths.len()
+    fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
+        // bottom = initialized (start_block_effect counters this at outset)
+        BitSet::new_empty(self.move_data().move_paths.len())
     }
 
     // sets on_entry bits for Arg places
-    fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>) {
+    fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) {
         // set all bits to 1 (uninit) before gathering counterevidence
-        assert!(self.bits_per_block(body) == state.domain_size());
         state.insert_all();
 
         drop_flag_effects_for_function_entry(self.tcx, self.body, self.mdpe, |path, s| {
@@ -395,13 +393,11 @@ impl<'tcx> AnalysisDomain<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
             state.remove(path);
         });
     }
-
-    fn pretty_print_idx(&self, w: &mut impl std::io::Write, mpi: Self::Idx) -> std::io::Result<()> {
-        write!(w, "{}", self.move_data().move_paths[mpi])
-    }
 }
 
 impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
+    type Idx = MovePathIndex;
+
     fn statement_effect(
         &self,
         trans: &mut impl GenKill<Self::Idx>,
@@ -471,30 +467,30 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
 }
 
 impl<'a, 'tcx> AnalysisDomain<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> {
-    type Idx = MovePathIndex;
+    /// Use set intersection as the join operator.
+    type Domain = lattice::Dual<BitSet<MovePathIndex>>;
 
     const NAME: &'static str = "definite_init";
 
-    fn bits_per_block(&self, _: &mir::Body<'tcx>) -> usize {
-        self.move_data().move_paths.len()
+    fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
+        // bottom = initialized (start_block_effect counters this at outset)
+        lattice::Dual(BitSet::new_filled(self.move_data().move_paths.len()))
     }
 
     // sets on_entry bits for Arg places
-    fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>) {
-        state.clear();
+    fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) {
+        state.0.clear();
 
         drop_flag_effects_for_function_entry(self.tcx, self.body, self.mdpe, |path, s| {
             assert!(s == DropFlagState::Present);
-            state.insert(path);
+            state.0.insert(path);
         });
     }
-
-    fn pretty_print_idx(&self, w: &mut impl std::io::Write, mpi: Self::Idx) -> std::io::Result<()> {
-        write!(w, "{}", self.move_data().move_paths[mpi])
-    }
 }
 
 impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> {
+    type Idx = MovePathIndex;
+
     fn statement_effect(
         &self,
         trans: &mut impl GenKill<Self::Idx>,
@@ -540,15 +536,16 @@ impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> {
 }
 
 impl<'tcx> AnalysisDomain<'tcx> for EverInitializedPlaces<'_, 'tcx> {
-    type Idx = InitIndex;
+    type Domain = BitSet<InitIndex>;
 
     const NAME: &'static str = "ever_init";
 
-    fn bits_per_block(&self, _: &mir::Body<'tcx>) -> usize {
-        self.move_data().inits.len()
+    fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
+        // bottom = no initialized variables by default
+        BitSet::new_empty(self.move_data().inits.len())
     }
 
-    fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>) {
+    fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) {
         for arg_init in 0..body.arg_count {
             state.insert(InitIndex::new(arg_init));
         }
@@ -556,6 +553,8 @@ impl<'tcx> AnalysisDomain<'tcx> for EverInitializedPlaces<'_, 'tcx> {
 }
 
 impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
+    type Idx = InitIndex;
+
     fn statement_effect(
         &self,
         trans: &mut impl GenKill<Self::Idx>,
@@ -625,23 +624,3 @@ impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
         }
     }
 }
-
-impl<'a, 'tcx> BottomValue for MaybeInitializedPlaces<'a, 'tcx> {
-    /// bottom = uninitialized
-    const BOTTOM_VALUE: bool = false;
-}
-
-impl<'a, 'tcx> BottomValue for MaybeUninitializedPlaces<'a, 'tcx> {
-    /// bottom = initialized (start_block_effect counters this at outset)
-    const BOTTOM_VALUE: bool = false;
-}
-
-impl<'a, 'tcx> BottomValue for DefinitelyInitializedPlaces<'a, 'tcx> {
-    /// bottom = initialized (start_block_effect counters this at outset)
-    const BOTTOM_VALUE: bool = true;
-}
-
-impl<'a, 'tcx> BottomValue for EverInitializedPlaces<'a, 'tcx> {
-    /// bottom = no initialized variables by default
-    const BOTTOM_VALUE: bool = false;
-}
diff --git a/compiler/rustc_mir/src/dataflow/impls/storage_liveness.rs b/compiler/rustc_mir/src/dataflow/impls/storage_liveness.rs
index 21623e3cad5..9250cd40847 100644
--- a/compiler/rustc_mir/src/dataflow/impls/storage_liveness.rs
+++ b/compiler/rustc_mir/src/dataflow/impls/storage_liveness.rs
@@ -1,6 +1,5 @@
 pub use super::*;
 
-use crate::dataflow::BottomValue;
 use crate::dataflow::{self, GenKill, Results, ResultsRefCursor};
 use crate::util::storage::AlwaysLiveLocals;
 use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor};
@@ -19,15 +18,16 @@ impl MaybeStorageLive {
 }
 
 impl dataflow::AnalysisDomain<'tcx> for MaybeStorageLive {
-    type Idx = Local;
+    type Domain = BitSet<Local>;
 
     const NAME: &'static str = "maybe_storage_live";
 
-    fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize {
-        body.local_decls.len()
+    fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
+        // bottom = dead
+        BitSet::new_empty(body.local_decls.len())
     }
 
-    fn initialize_start_block(&self, body: &mir::Body<'tcx>, on_entry: &mut BitSet<Self::Idx>) {
+    fn initialize_start_block(&self, body: &mir::Body<'tcx>, on_entry: &mut Self::Domain) {
         assert_eq!(body.local_decls.len(), self.always_live_locals.domain_size());
         for local in self.always_live_locals.iter() {
             on_entry.insert(local);
@@ -40,6 +40,8 @@ impl dataflow::AnalysisDomain<'tcx> for MaybeStorageLive {
 }
 
 impl dataflow::GenKillAnalysis<'tcx> for MaybeStorageLive {
+    type Idx = Local;
+
     fn statement_effect(
         &self,
         trans: &mut impl GenKill<Self::Idx>,
@@ -74,11 +76,6 @@ impl dataflow::GenKillAnalysis<'tcx> for MaybeStorageLive {
     }
 }
 
-impl BottomValue for MaybeStorageLive {
-    /// bottom = dead
-    const BOTTOM_VALUE: bool = false;
-}
-
 type BorrowedLocalsResults<'a, 'tcx> = ResultsRefCursor<'a, 'a, 'tcx, MaybeBorrowedLocals>;
 
 /// Dataflow analysis that determines whether each local requires storage at a
@@ -101,15 +98,16 @@ impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> {
 }
 
 impl<'mir, 'tcx> dataflow::AnalysisDomain<'tcx> for MaybeRequiresStorage<'mir, 'tcx> {
-    type Idx = Local;
+    type Domain = BitSet<Local>;
 
     const NAME: &'static str = "requires_storage";
 
-    fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize {
-        body.local_decls.len()
+    fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
+        // bottom = dead
+        BitSet::new_empty(body.local_decls.len())
     }
 
-    fn initialize_start_block(&self, body: &mir::Body<'tcx>, on_entry: &mut BitSet<Self::Idx>) {
+    fn initialize_start_block(&self, body: &mir::Body<'tcx>, on_entry: &mut Self::Domain) {
         // The resume argument is live on function entry (we don't care about
         // the `self` argument)
         for arg in body.args_iter().skip(1) {
@@ -119,6 +117,8 @@ impl<'mir, 'tcx> dataflow::AnalysisDomain<'tcx> for MaybeRequiresStorage<'mir, '
 }
 
 impl<'mir, 'tcx> dataflow::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tcx> {
+    type Idx = Local;
+
     fn before_statement_effect(
         &self,
         trans: &mut impl GenKill<Self::Idx>,
@@ -285,11 +285,6 @@ impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> {
     }
 }
 
-impl<'mir, 'tcx> BottomValue for MaybeRequiresStorage<'mir, 'tcx> {
-    /// bottom = dead
-    const BOTTOM_VALUE: bool = false;
-}
-
 struct MoveVisitor<'a, 'mir, 'tcx, T> {
     borrowed_locals: &'a RefCell<BorrowedLocalsResults<'mir, 'tcx>>,
     trans: &'a mut T,
diff --git a/compiler/rustc_mir/src/dataflow/mod.rs b/compiler/rustc_mir/src/dataflow/mod.rs
index a0c24636059..5575a97982f 100644
--- a/compiler/rustc_mir/src/dataflow/mod.rs
+++ b/compiler/rustc_mir/src/dataflow/mod.rs
@@ -5,9 +5,9 @@ use rustc_span::symbol::{sym, Symbol};
 
 pub(crate) use self::drop_flag_effects::*;
 pub use self::framework::{
-    visit_results, Analysis, AnalysisDomain, Backward, BorrowckFlowState, BorrowckResults,
-    BottomValue, Engine, Forward, GenKill, GenKillAnalysis, Results, ResultsCursor,
-    ResultsRefCursor, ResultsVisitor,
+    fmt, lattice, visit_results, Analysis, AnalysisDomain, Backward, BorrowckFlowState,
+    BorrowckResults, Engine, Forward, GenKill, GenKillAnalysis, JoinSemiLattice, Results,
+    ResultsCursor, ResultsRefCursor, ResultsVisitor,
 };
 
 use self::move_paths::MoveData;
diff --git a/compiler/rustc_mir/src/dataflow/move_paths/builder.rs b/compiler/rustc_mir/src/dataflow/move_paths/builder.rs
index e088dc6a954..5c3e3538401 100644
--- a/compiler/rustc_mir/src/dataflow/move_paths/builder.rs
+++ b/compiler/rustc_mir/src/dataflow/move_paths/builder.rs
@@ -4,7 +4,6 @@ use rustc_middle::mir::*;
 use rustc_middle::ty::{self, TyCtxt};
 use smallvec::{smallvec, SmallVec};
 
-use std::convert::TryInto;
 use std::mem;
 
 use super::abs_domain::Lift;
@@ -110,7 +109,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
             let body = self.builder.body;
             let tcx = self.builder.tcx;
             let place_ty = Place::ty_from(place.local, proj_base, body, tcx).ty;
-            match place_ty.kind {
+            match place_ty.kind() {
                 ty::Ref(..) | ty::RawPtr(..) => {
                     let proj = &place.projection[..i + 1];
                     return Err(MoveError::cannot_move_out_of(
@@ -480,13 +479,8 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
                 }
             };
             let base_ty = base_place.ty(self.builder.body, self.builder.tcx).ty;
-            let len: u64 = match base_ty.kind {
-                ty::Array(_, size) => {
-                    let length = size.eval_usize(self.builder.tcx, self.builder.param_env);
-                    length
-                        .try_into()
-                        .expect("slice pattern of array with more than u32::MAX elements")
-                }
+            let len: u64 = match base_ty.kind() {
+                ty::Array(_, size) => size.eval_usize(self.builder.tcx, self.builder.param_env),
                 _ => bug!("from_end: false slice pattern of non-array type"),
             };
             for offset in from..to {
@@ -525,7 +519,9 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
         // of the union so it is marked as initialized again.
         if let [proj_base @ .., ProjectionElem::Field(_, _)] = place.projection {
             if let ty::Adt(def, _) =
-                Place::ty_from(place.local, proj_base, self.builder.body, self.builder.tcx).ty.kind
+                Place::ty_from(place.local, proj_base, self.builder.body, self.builder.tcx)
+                    .ty
+                    .kind()
             {
                 if def.is_union() {
                     place = PlaceRef { local: place.local, projection: proj_base }
diff --git a/compiler/rustc_mir/src/interpret/cast.rs b/compiler/rustc_mir/src/interpret/cast.rs
index 501a5bcddb3..0e16b0caefa 100644
--- a/compiler/rustc_mir/src/interpret/cast.rs
+++ b/compiler/rustc_mir/src/interpret/cast.rs
@@ -47,7 +47,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
             Pointer(PointerCast::ReifyFnPointer) => {
                 // The src operand does not matter, just its type
-                match src.layout.ty.kind {
+                match *src.layout.ty.kind() {
                     ty::FnDef(def_id, substs) => {
                         // All reifications must be monomorphic, bail out otherwise.
                         ensure_monomorphic_enough(*self.tcx, src.layout.ty)?;
@@ -76,7 +76,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
             Pointer(PointerCast::UnsafeFnPointer) => {
                 let src = self.read_immediate(src)?;
-                match cast_ty.kind {
+                match cast_ty.kind() {
                     ty::FnPtr(_) => {
                         // No change to value
                         self.write_immediate(*src, dest)?;
@@ -87,7 +87,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
             Pointer(PointerCast::ClosureFnPointer(_)) => {
                 // The src operand does not matter, just its type
-                match src.layout.ty.kind {
+                match *src.layout.ty.kind() {
                     ty::Closure(def_id, substs) => {
                         // All reifications must be monomorphic, bail out otherwise.
                         ensure_monomorphic_enough(*self.tcx, src.layout.ty)?;
@@ -116,7 +116,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         use rustc_middle::ty::TyKind::*;
         trace!("Casting {:?}: {:?} to {:?}", *src, src.layout.ty, cast_ty);
 
-        match src.layout.ty.kind {
+        match src.layout.ty.kind() {
             // Floating point
             Float(FloatTy::F32) => {
                 return Ok(self.cast_from_float(src.to_scalar()?.to_f32()?, cast_ty).into());
@@ -196,9 +196,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         let v = if signed { self.sign_extend(v, src_layout) } else { v };
         trace!("cast_from_scalar: {}, {} -> {}", v, src_layout.ty, cast_ty);
         use rustc_middle::ty::TyKind::*;
-        match cast_ty.kind {
+        match *cast_ty.kind() {
             Int(_) | Uint(_) | RawPtr(_) => {
-                let size = match cast_ty.kind {
+                let size = match *cast_ty.kind() {
                     Int(t) => Integer::from_attr(self, attr::IntType::SignedInt(t)).size(),
                     Uint(t) => Integer::from_attr(self, attr::IntType::UnsignedInt(t)).size(),
                     RawPtr(_) => self.pointer_size(),
@@ -228,7 +228,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         F: Float + Into<Scalar<M::PointerTag>> + FloatConvert<Single> + FloatConvert<Double>,
     {
         use rustc_middle::ty::TyKind::*;
-        match dest_ty.kind {
+        match *dest_ty.kind() {
             // float -> uint
             Uint(t) => {
                 let size = Integer::from_attr(self, attr::IntType::UnsignedInt(t)).size();
@@ -267,7 +267,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         let (src_pointee_ty, dest_pointee_ty) =
             self.tcx.struct_lockstep_tails_erasing_lifetimes(source_ty, cast_ty, self.param_env);
 
-        match (&src_pointee_ty.kind, &dest_pointee_ty.kind) {
+        match (&src_pointee_ty.kind(), &dest_pointee_ty.kind()) {
             (&ty::Array(_, length), &ty::Slice(_)) => {
                 let ptr = self.read_immediate(src)?.to_scalar()?;
                 // u64 cast is from usize to u64, which is always good
@@ -303,7 +303,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         dest: PlaceTy<'tcx, M::PointerTag>,
     ) -> InterpResult<'tcx> {
         trace!("Unsizing {:?} of type {} into {:?}", *src, src.layout.ty, cast_ty.ty);
-        match (&src.layout.ty.kind, &cast_ty.ty.kind) {
+        match (&src.layout.ty.kind(), &cast_ty.ty.kind()) {
             (&ty::Ref(_, s, _), &ty::Ref(_, c, _) | &ty::RawPtr(TypeAndMut { ty: c, .. }))
             | (&ty::RawPtr(TypeAndMut { ty: s, .. }), &ty::RawPtr(TypeAndMut { ty: c, .. })) => {
                 self.unsize_into_ptr(src, dest, s, c)
diff --git a/compiler/rustc_mir/src/interpret/eval_context.rs b/compiler/rustc_mir/src/interpret/eval_context.rs
index 525da87463a..f97096984fa 100644
--- a/compiler/rustc_mir/src/interpret/eval_context.rs
+++ b/compiler/rustc_mir/src/interpret/eval_context.rs
@@ -20,7 +20,7 @@ use rustc_span::{Pos, Span};
 use rustc_target::abi::{Align, HasDataLayout, LayoutOf, Size, TargetDataLayout};
 
 use super::{
-    Immediate, MPlaceTy, Machine, MemPlace, MemPlaceMeta, Memory, OpTy, Operand, Place, PlaceTy,
+    Immediate, MPlaceTy, Machine, MemPlace, MemPlaceMeta, Memory, Operand, Place, PlaceTy,
     ScalarMaybeUninit, StackPopJump,
 };
 use crate::transform::validate::equal_up_to_regions;
@@ -482,13 +482,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// The `substs` are assumed to already be in our interpreter "universe" (param_env).
     pub(super) fn resolve(
         &self,
-        def_id: DefId,
+        def: ty::WithOptConstParam<DefId>,
         substs: SubstsRef<'tcx>,
     ) -> InterpResult<'tcx, ty::Instance<'tcx>> {
-        trace!("resolve: {:?}, {:#?}", def_id, substs);
+        trace!("resolve: {:?}, {:#?}", def, substs);
         trace!("param_env: {:#?}", self.param_env);
         trace!("substs: {:#?}", substs);
-        match ty::Instance::resolve(*self.tcx, self.param_env, def_id, substs) {
+        match ty::Instance::resolve_opt_const_arg(*self.tcx, self.param_env, def, substs) {
             Ok(Some(instance)) => Ok(instance),
             Ok(None) => throw_inval!(TooGeneric),
 
@@ -534,7 +534,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         if !layout.is_unsized() {
             return Ok(Some((layout.size, layout.align.abi)));
         }
-        match layout.ty.kind {
+        match layout.ty.kind() {
             ty::Adt(..) | ty::Tuple(..) => {
                 // First get the size of all statically known fields.
                 // Don't use type_of::sizing_type_of because that expects t to be sized,
@@ -875,32 +875,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         Ok(())
     }
 
-    pub(super) fn const_eval(
-        &self,
-        gid: GlobalId<'tcx>,
-        ty: Ty<'tcx>,
-    ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
-        // For statics we pick `ParamEnv::reveal_all`, because statics don't have generics
-        // and thus don't care about the parameter environment. While we could just use
-        // `self.param_env`, that would mean we invoke the query to evaluate the static
-        // with different parameter environments, thus causing the static to be evaluated
-        // multiple times.
-        let param_env = if self.tcx.is_static(gid.instance.def_id()) {
-            ty::ParamEnv::reveal_all()
-        } else {
-            self.param_env
-        };
-        let val = self.tcx.const_eval_global_id(param_env, gid, Some(self.tcx.span))?;
-
-        // Even though `ecx.const_eval` is called from `const_to_op` we can never have a
-        // recursion deeper than one level, because the `tcx.const_eval` above is guaranteed to not
-        // return `ConstValue::Unevaluated`, which is the only way that `const_to_op` will call
-        // `ecx.const_eval`.
-        let const_ = ty::Const { val: ty::ConstKind::Value(val), ty };
-        self.const_to_op(&const_, None)
-    }
-
-    pub fn const_eval_raw(
+    pub fn eval_to_allocation(
         &self,
         gid: GlobalId<'tcx>,
     ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
@@ -914,14 +889,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         } else {
             self.param_env
         };
-        // We use `const_eval_raw` here, and get an unvalidated result.  That is okay:
-        // Our result will later be validated anyway, and there seems no good reason
-        // to have to fail early here.  This is also more consistent with
-        // `Memory::get_static_alloc` which has to use `const_eval_raw` to avoid cycles.
-        // FIXME: We can hit delay_span_bug if this is an invalid const, interning finds
-        // that problem, but we never run validation to show an error. Can we ensure
-        // this does not happen?
-        let val = self.tcx.const_eval_raw(param_env.and(gid))?;
+        let val = self.tcx.eval_to_allocation_raw(param_env.and(gid))?;
         self.raw_const_to_mplace(val)
     }
 
diff --git a/compiler/rustc_mir/src/interpret/intern.rs b/compiler/rustc_mir/src/interpret/intern.rs
index 606be7cad2b..dd5e9c99774 100644
--- a/compiler/rustc_mir/src/interpret/intern.rs
+++ b/compiler/rustc_mir/src/interpret/intern.rs
@@ -195,13 +195,13 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir
         // Raw pointers (and boxes) are handled by the `leftover_relocations` logic.
         let tcx = self.ecx.tcx;
         let ty = mplace.layout.ty;
-        if let ty::Ref(_, referenced_ty, ref_mutability) = ty.kind {
+        if let ty::Ref(_, referenced_ty, ref_mutability) = *ty.kind() {
             let value = self.ecx.read_immediate(mplace.into())?;
             let mplace = self.ecx.ref_to_mplace(value)?;
             assert_eq!(mplace.layout.ty, referenced_ty);
             // Handle trait object vtables.
             if let ty::Dynamic(..) =
-                tcx.struct_tail_erasing_lifetimes(referenced_ty, self.ecx.param_env).kind
+                tcx.struct_tail_erasing_lifetimes(referenced_ty, self.ecx.param_env).kind()
             {
                 // Validation will error (with a better message) on an invalid vtable pointer
                 // so we can safely not do anything if this is not a real pointer.
@@ -253,7 +253,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir
                         // This helps to prevent users from accidentally exploiting UB that they
                         // caused (by somehow getting a mutable reference in a `const`).
                         if ref_mutability == Mutability::Mut {
-                            match referenced_ty.kind {
+                            match referenced_ty.kind() {
                                 ty::Array(_, n) if n.eval_usize(*tcx, self.ecx.param_env) == 0 => {}
                                 ty::Slice(_)
                                     if mplace.meta.unwrap_meta().to_machine_usize(self.ecx)?
diff --git a/compiler/rustc_mir/src/interpret/intrinsics.rs b/compiler/rustc_mir/src/interpret/intrinsics.rs
index b37dcd42f4c..0664f25e409 100644
--- a/compiler/rustc_mir/src/interpret/intrinsics.rs
+++ b/compiler/rustc_mir/src/interpret/intrinsics.rs
@@ -76,7 +76,7 @@ crate fn eval_nullary_intrinsic<'tcx>(
             ConstValue::from_u64(tcx.type_id_hash(tp_ty))
         }
         sym::variant_count => {
-            if let ty::Adt(ref adt, _) = tp_ty.kind {
+            if let ty::Adt(ref adt, _) = tp_ty.kind() {
                 ConstValue::from_machine_usize(adt.variants.len() as u64, &tcx)
             } else {
                 ConstValue::from_machine_usize(0u64, &tcx)
@@ -88,6 +88,8 @@ crate fn eval_nullary_intrinsic<'tcx>(
 
 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// Returns `true` if emulation happened.
+    /// Here we implement the intrinsics that are common to all Miri instances; individual machines can add their own
+    /// intrinsic handling.
     pub fn emulate_intrinsic(
         &mut self,
         instance: ty::Instance<'tcx>,
@@ -150,7 +152,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                     sym::type_name => self.tcx.mk_static_str(),
                     _ => bug!("already checked for nullary intrinsics"),
                 };
-                let val = self.const_eval(gid, ty)?;
+                let val =
+                    self.tcx.const_eval_global_id(self.param_env, gid, Some(self.tcx.span))?;
+                let const_ = ty::Const { val: ty::ConstKind::Value(val), ty };
+                let val = self.const_to_op(&const_, None)?;
                 self.copy_op(val, dest)?;
             }
 
@@ -328,16 +333,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 let offset_ptr = ptr.ptr_wrapping_signed_offset(offset_bytes, self);
                 self.write_scalar(offset_ptr, dest)?;
             }
-            sym::ptr_guaranteed_eq | sym::ptr_guaranteed_ne => {
-                let a = self.read_immediate(args[0])?.to_scalar()?;
-                let b = self.read_immediate(args[1])?.to_scalar()?;
-                let cmp = if intrinsic_name == sym::ptr_guaranteed_eq {
-                    self.guaranteed_eq(a, b)
-                } else {
-                    self.guaranteed_ne(a, b)
-                };
-                self.write_scalar(Scalar::from_bool(cmp), dest)?;
-            }
             sym::ptr_offset_from => {
                 let a = self.read_immediate(args[0])?.to_scalar()?;
                 let b = self.read_immediate(args[1])?.to_scalar()?;
@@ -448,37 +443,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         Ok(true)
     }
 
-    fn guaranteed_eq(&mut self, a: Scalar<M::PointerTag>, b: Scalar<M::PointerTag>) -> bool {
-        match (a, b) {
-            // Comparisons between integers are always known.
-            (Scalar::Raw { .. }, Scalar::Raw { .. }) => a == b,
-            // Equality with integers can never be known for sure.
-            (Scalar::Raw { .. }, Scalar::Ptr(_)) | (Scalar::Ptr(_), Scalar::Raw { .. }) => false,
-            // FIXME: return `true` for when both sides are the same pointer, *except* that
-            // some things (like functions and vtables) do not have stable addresses
-            // so we need to be careful around them.
-            (Scalar::Ptr(_), Scalar::Ptr(_)) => false,
-        }
-    }
-
-    fn guaranteed_ne(&mut self, a: Scalar<M::PointerTag>, b: Scalar<M::PointerTag>) -> bool {
-        match (a, b) {
-            // Comparisons between integers are always known.
-            (Scalar::Raw { .. }, Scalar::Raw { .. }) => a != b,
-            // Comparisons of abstract pointers with null pointers are known if the pointer
-            // is in bounds, because if they are in bounds, the pointer can't be null.
-            (Scalar::Raw { data: 0, .. }, Scalar::Ptr(ptr))
-            | (Scalar::Ptr(ptr), Scalar::Raw { data: 0, .. }) => !self.memory.ptr_may_be_null(ptr),
-            // Inequality with integers other than null can never be known for sure.
-            (Scalar::Raw { .. }, Scalar::Ptr(_)) | (Scalar::Ptr(_), Scalar::Raw { .. }) => false,
-            // FIXME: return `true` for at least some comparisons where we can reliably
-            // determine the result of runtime inequality tests at compile-time.
-            // Examples include comparison of addresses in static items, for these we can
-            // give reliable results.
-            (Scalar::Ptr(_), Scalar::Ptr(_)) => false,
-        }
-    }
-
     pub fn exact_div(
         &mut self,
         a: ImmTy<'tcx, M::PointerTag>,
diff --git a/compiler/rustc_mir/src/interpret/intrinsics/type_name.rs b/compiler/rustc_mir/src/interpret/intrinsics/type_name.rs
index 379117f3b84..8c0014e10d0 100644
--- a/compiler/rustc_mir/src/interpret/intrinsics/type_name.rs
+++ b/compiler/rustc_mir/src/interpret/intrinsics/type_name.rs
@@ -32,7 +32,7 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
     }
 
     fn print_type(mut self, ty: Ty<'tcx>) -> Result<Self::Type, Self::Error> {
-        match ty.kind {
+        match *ty.kind() {
             // Types without identity.
             ty::Bool
             | ty::Char
diff --git a/compiler/rustc_mir/src/interpret/memory.rs b/compiler/rustc_mir/src/interpret/memory.rs
index d4be2ce0568..f3e373813ca 100644
--- a/compiler/rustc_mir/src/interpret/memory.rs
+++ b/compiler/rustc_mir/src/interpret/memory.rs
@@ -285,9 +285,11 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
             None => {
                 // Deallocating global memory -- always an error
                 return Err(match self.tcx.get_global_alloc(ptr.alloc_id) {
-                    Some(GlobalAlloc::Function(..)) => err_ub_format!("deallocating a function"),
+                    Some(GlobalAlloc::Function(..)) => {
+                        err_ub_format!("deallocating {}, which is a function", ptr.alloc_id)
+                    }
                     Some(GlobalAlloc::Static(..) | GlobalAlloc::Memory(..)) => {
-                        err_ub_format!("deallocating static memory")
+                        err_ub_format!("deallocating {}, which is static memory", ptr.alloc_id)
                     }
                     None => err_ub!(PointerUseAfterFree(ptr.alloc_id)),
                 }
@@ -297,7 +299,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
 
         if alloc_kind != kind {
             throw_ub_format!(
-                "deallocating {} memory using {} deallocation operation",
+                "deallocating {}, which is {} memory, using {} deallocation operation",
+                ptr.alloc_id,
                 alloc_kind,
                 kind
             );
@@ -305,7 +308,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
         if let Some((size, align)) = old_size_and_align {
             if size != alloc.size || align != alloc.align {
                 throw_ub_format!(
-                    "incorrect layout on deallocation: allocation has size {} and alignment {}, but gave size {} and alignment {}",
+                    "incorrect layout on deallocation: {} has size {} and alignment {}, but gave size {} and alignment {}",
+                    ptr.alloc_id,
                     alloc.size.bytes(),
                     alloc.align.bytes(),
                     size.bytes(),
@@ -469,7 +473,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
                 // Notice that every static has two `AllocId` that will resolve to the same
                 // thing here: one maps to `GlobalAlloc::Static`, this is the "lazy" ID,
                 // and the other one is maps to `GlobalAlloc::Memory`, this is returned by
-                // `const_eval_raw` and it is the "resolved" ID.
+                // `eval_static_initializer` and it is the "resolved" ID.
                 // The resolved ID is never used by the interpreted program, it is hidden.
                 // This is relied upon for soundness of const-patterns; a pointer to the resolved
                 // ID would "sidestep" the checks that make sure consts do not point to statics!
diff --git a/compiler/rustc_mir/src/interpret/operand.rs b/compiler/rustc_mir/src/interpret/operand.rs
index 0b58caef54d..735f890a33b 100644
--- a/compiler/rustc_mir/src/interpret/operand.rs
+++ b/compiler/rustc_mir/src/interpret/operand.rs
@@ -549,21 +549,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         };
         // Early-return cases.
         let val_val = match val.val {
-            ty::ConstKind::Param(_) => throw_inval!(TooGeneric),
+            ty::ConstKind::Param(_) | ty::ConstKind::Bound(..) => throw_inval!(TooGeneric),
             ty::ConstKind::Error(_) => throw_inval!(TypeckError(ErrorReported)),
             ty::ConstKind::Unevaluated(def, substs, promoted) => {
-                let instance = self.resolve(def.did, substs)?;
-                // We use `const_eval` here and `const_eval_raw` elsewhere in mir interpretation.
-                // The reason we use `const_eval_raw` everywhere else is to prevent cycles during
-                // validation, because validation automatically reads through any references, thus
-                // potentially requiring the current static to be evaluated again. This is not a
-                // problem here, because we are building an operand which means an actual read is
-                // happening.
-                return Ok(self.const_eval(GlobalId { instance, promoted }, val.ty)?);
+                let instance = self.resolve(def, substs)?;
+                return Ok(self.eval_to_allocation(GlobalId { instance, promoted })?.into());
             }
-            ty::ConstKind::Infer(..)
-            | ty::ConstKind::Bound(..)
-            | ty::ConstKind::Placeholder(..) => {
+            ty::ConstKind::Infer(..) | ty::ConstKind::Placeholder(..) => {
                 span_bug!(self.cur_span(), "const_to_op: Unexpected ConstKind {:?}", val)
             }
             ty::ConstKind::Value(val_val) => val_val,
@@ -662,7 +654,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 let discr_val = self.cast_from_scalar(tag_bits, tag_layout, discr_layout.ty);
                 let discr_bits = discr_val.assert_bits(discr_layout.size);
                 // Convert discriminant to variant index, and catch invalid discriminants.
-                let index = match op.layout.ty.kind {
+                let index = match *op.layout.ty.kind() {
                     ty::Adt(adt, _) => {
                         adt.discriminants(*self.tcx).find(|(_, var)| var.val == discr_bits)
                     }
diff --git a/compiler/rustc_mir/src/interpret/operator.rs b/compiler/rustc_mir/src/interpret/operator.rs
index 30c40b8fde9..fc266fa74bf 100644
--- a/compiler/rustc_mir/src/interpret/operator.rs
+++ b/compiler/rustc_mir/src/interpret/operator.rs
@@ -282,7 +282,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             right.layout.ty
         );
 
-        match left.layout.ty.kind {
+        match left.layout.ty.kind() {
             ty::Char => {
                 assert_eq!(left.layout.ty, right.layout.ty);
                 let left = left.to_scalar()?;
@@ -368,7 +368,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         let val = val.to_scalar()?;
         trace!("Running unary op {:?}: {:?} ({:?})", un_op, val, layout.ty);
 
-        match layout.ty.kind {
+        match layout.ty.kind() {
             ty::Bool => {
                 let val = val.to_bool()?;
                 let res = match un_op {
diff --git a/compiler/rustc_mir/src/interpret/place.rs b/compiler/rustc_mir/src/interpret/place.rs
index 6ba6103b311..72551b23370 100644
--- a/compiler/rustc_mir/src/interpret/place.rs
+++ b/compiler/rustc_mir/src/interpret/place.rs
@@ -13,9 +13,9 @@ use rustc_target::abi::{Abi, Align, FieldsShape, TagEncoding};
 use rustc_target::abi::{HasDataLayout, LayoutOf, Size, VariantIdx, Variants};
 
 use super::{
-    mir_assign_valid_types, truncate, AllocId, AllocMap, Allocation, AllocationExtra, ImmTy,
-    Immediate, InterpCx, InterpResult, LocalValue, Machine, MemoryKind, OpTy, Operand, Pointer,
-    PointerArithmetic, RawConst, Scalar, ScalarMaybeUninit,
+    mir_assign_valid_types, truncate, AllocId, AllocMap, Allocation, AllocationExtra, ConstAlloc,
+    ImmTy, Immediate, InterpCx, InterpResult, LocalValue, Machine, MemoryKind, OpTy, Operand,
+    Pointer, PointerArithmetic, Scalar, ScalarMaybeUninit,
 };
 
 #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable)]
@@ -202,7 +202,7 @@ impl<'tcx, Tag> MPlaceTy<'tcx, Tag> {
     pub(super) fn len(self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> {
         if self.layout.is_unsized() {
             // We need to consult `meta` metadata
-            match self.layout.ty.kind {
+            match self.layout.ty.kind() {
                 ty::Slice(..) | ty::Str => self.mplace.meta.unwrap_meta().to_machine_usize(cx),
                 _ => bug!("len not supported on unsized type {:?}", self.layout.ty),
             }
@@ -218,7 +218,7 @@ impl<'tcx, Tag> MPlaceTy<'tcx, Tag> {
 
     #[inline]
     pub(super) fn vtable(self) -> Scalar<Tag> {
-        match self.layout.ty.kind {
+        match self.layout.ty.kind() {
             ty::Dynamic(..) => self.mplace.meta.unwrap_meta(),
             _ => bug!("vtable not supported on type {:?}", self.layout.ty),
         }
@@ -498,7 +498,7 @@ where
 
         // Compute meta and new layout
         let inner_len = actual_to.checked_sub(from).unwrap();
-        let (meta, ty) = match base.layout.ty.kind {
+        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)),
@@ -551,7 +551,7 @@ where
                 let n = base.len(self)?;
                 if n < min_length {
                     // This can only be reached in ConstProp and non-rustc-MIR.
-                    throw_ub!(BoundsCheckFailed { len: min_length.into(), index: n });
+                    throw_ub!(BoundsCheckFailed { len: min_length, index: n });
                 }
 
                 let index = if from_end {
@@ -565,9 +565,7 @@ where
                 self.mplace_index(base, index)?
             }
 
-            Subslice { from, to, from_end } => {
-                self.mplace_subslice(base, u64::from(from), u64::from(to), from_end)?
-            }
+            Subslice { from, to, from_end } => self.mplace_subslice(base, from, to, from_end)?,
         })
     }
 
@@ -1122,7 +1120,7 @@ where
 
     pub fn raw_const_to_mplace(
         &self,
-        raw: RawConst<'tcx>,
+        raw: ConstAlloc<'tcx>,
     ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
         // This must be an allocation in `tcx`
         let _ = self.tcx.global_alloc(raw.alloc_id);
diff --git a/compiler/rustc_mir/src/interpret/terminator.rs b/compiler/rustc_mir/src/interpret/terminator.rs
index 9a036a0f299..b789cb76e9f 100644
--- a/compiler/rustc_mir/src/interpret/terminator.rs
+++ b/compiler/rustc_mir/src/interpret/terminator.rs
@@ -55,7 +55,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 let old_stack = self.frame_idx();
                 let old_loc = self.frame().loc;
                 let func = self.eval_operand(func, None)?;
-                let (fn_val, abi) = match func.layout.ty.kind {
+                let (fn_val, abi) = match *func.layout.ty.kind() {
                     ty::FnPtr(sig) => {
                         let caller_abi = sig.abi();
                         let fn_ptr = self.read_scalar(func)?.check_init()?;
@@ -64,7 +64,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                     }
                     ty::FnDef(def_id, substs) => {
                         let sig = func.layout.ty.fn_sig(*self.tcx);
-                        (FnVal::Instance(self.resolve(def_id, substs)?), sig.abi())
+                        (
+                            FnVal::Instance(
+                                self.resolve(ty::WithOptConstParam::unknown(def_id), substs)?,
+                            ),
+                            sig.abi(),
+                        )
                     }
                     _ => span_bug!(
                         terminator.source_info.span,
@@ -222,7 +227,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         {
             let callee_abi = {
                 let instance_ty = instance.ty(*self.tcx, self.param_env);
-                match instance_ty.kind {
+                match instance_ty.kind() {
                     ty::FnDef(..) => instance_ty.fn_sig(*self.tcx).abi(),
                     ty::Closure(..) => Abi::RustCall,
                     ty::Generator(..) => Abi::Rust,
@@ -431,7 +436,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         // implementation fail -- a problem shared by rustc.
         let place = self.force_allocation(place)?;
 
-        let (instance, place) = match place.layout.ty.kind {
+        let (instance, place) = match place.layout.ty.kind() {
             ty::Dynamic(..) => {
                 // Dropping a trait object.
                 self.unpack_dyn_trait(place)?
diff --git a/compiler/rustc_mir/src/interpret/util.rs b/compiler/rustc_mir/src/interpret/util.rs
index 57c5fc59cc0..fc5a25ffbf2 100644
--- a/compiler/rustc_mir/src/interpret/util.rs
+++ b/compiler/rustc_mir/src/interpret/util.rs
@@ -33,7 +33,7 @@ where
                 return false;
             }
 
-            match ty.kind {
+            match *ty.kind() {
                 ty::Param(_) => true,
                 ty::Closure(def_id, substs)
                 | ty::Generator(def_id, substs, ..)
@@ -59,7 +59,7 @@ where
                             // `ty::Param`/`ty::ConstKind::Param`.
                             (false, true) if cfg!(debug_assertions) => match subst.unpack() {
                                 ty::subst::GenericArgKind::Type(ty) => {
-                                    assert!(matches!(ty.kind, ty::Param(_)))
+                                    assert!(matches!(ty.kind(), ty::Param(_)))
                                 }
                                 ty::subst::GenericArgKind::Const(ct) => {
                                     assert!(matches!(ct.val, ty::ConstKind::Param(_)))
diff --git a/compiler/rustc_mir/src/interpret/validity.rs b/compiler/rustc_mir/src/interpret/validity.rs
index 9cd20340138..2b83e1c8134 100644
--- a/compiler/rustc_mir/src/interpret/validity.rs
+++ b/compiler/rustc_mir/src/interpret/validity.rs
@@ -26,18 +26,22 @@ use super::{
 
 macro_rules! throw_validation_failure {
     ($where:expr, { $( $what_fmt:expr ),+ } $( expected { $( $expected_fmt:expr ),+ } )?) => {{
-        let mut msg = String::new();
-        msg.push_str("encountered ");
-        write!(&mut msg, $($what_fmt),+).unwrap();
-        let where_ = &$where;
-        if !where_.is_empty() {
-            msg.push_str(" at ");
-            write_path(&mut msg, where_);
-        }
-        $(
-            msg.push_str(", but expected ");
-            write!(&mut msg, $($expected_fmt),+).unwrap();
-        )?
+        let msg = rustc_middle::ty::print::with_no_trimmed_paths(|| {
+            let mut msg = String::new();
+            msg.push_str("encountered ");
+            write!(&mut msg, $($what_fmt),+).unwrap();
+            let where_ = &$where;
+            if !where_.is_empty() {
+                msg.push_str(" at ");
+                write_path(&mut msg, where_);
+            }
+            $(
+                msg.push_str(", but expected ");
+                write!(&mut msg, $($expected_fmt),+).unwrap();
+            )?
+
+            msg
+        });
         throw_ub!(ValidationFailure(msg))
     }};
 }
@@ -210,7 +214,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
         match layout.variants {
             Variants::Multiple { tag_field, .. } => {
                 if tag_field == field {
-                    return match layout.ty.kind {
+                    return match layout.ty.kind() {
                         ty::Adt(def, ..) if def.is_enum() => PathElem::EnumTag,
                         ty::Generator(..) => PathElem::GeneratorTag,
                         _ => bug!("non-variant type {:?}", layout.ty),
@@ -221,7 +225,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
         }
 
         // Now we know we are projecting to a field, so figure out which one.
-        match layout.ty.kind {
+        match layout.ty.kind() {
             // generators and closures.
             ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => {
                 let mut name = None;
@@ -299,7 +303,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
         pointee: TyAndLayout<'tcx>,
     ) -> InterpResult<'tcx> {
         let tail = self.ecx.tcx.struct_tail_erasing_lifetimes(pointee.ty, self.ecx.param_env);
-        match tail.kind {
+        match tail.kind() {
             ty::Dynamic(..) => {
                 let vtable = meta.unwrap_meta();
                 // Direct call to `check_ptr_access_align` checks alignment even on CTFE machines.
@@ -421,26 +425,28 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
                 let alloc_kind = self.ecx.tcx.get_global_alloc(ptr.alloc_id);
                 if let Some(GlobalAlloc::Static(did)) = alloc_kind {
                     assert!(!self.ecx.tcx.is_thread_local_static(did));
-                    // See const_eval::machine::MemoryExtra::can_access_statics for why
-                    // this check is so important.
-                    // This check is reachable when the const just referenced the static,
-                    // but never read it (so we never entered `before_access_global`).
-                    // We also need to do it here instead of going on to avoid running
-                    // into the `before_access_global` check during validation.
-                    if !self.may_ref_to_static && self.ecx.tcx.is_static(did) {
+                    assert!(self.ecx.tcx.is_static(did));
+                    if self.may_ref_to_static {
+                        // We skip checking other statics. These statics must be sound by
+                        // themselves, and the only way to get broken statics here is by using
+                        // unsafe code.
+                        // The reasons we don't check other statics is twofold. For one, in all
+                        // sound cases, the static was already validated on its own, and second, we
+                        // trigger cycle errors if we try to compute the value of the other static
+                        // and that static refers back to us.
+                        // We might miss const-invalid data,
+                        // but things are still sound otherwise (in particular re: consts
+                        // referring to statics).
+                        return Ok(());
+                    } else {
+                        // See const_eval::machine::MemoryExtra::can_access_statics for why
+                        // this check is so important.
+                        // This check is reachable when the const just referenced the static,
+                        // but never read it (so we never entered `before_access_global`).
                         throw_validation_failure!(self.path,
                             { "a {} pointing to a static variable", kind }
                         );
                     }
-                    // `extern static` cannot be validated as they have no body.
-                    // FIXME: Statics from other crates are also skipped.
-                    // They might be checked at a different type, but for now we
-                    // want to avoid recursing too deeply.  We might miss const-invalid data,
-                    // but things are still sound otherwise (in particular re: consts
-                    // referring to statics).
-                    if !did.is_local() || self.ecx.tcx.is_foreign_item(did) {
-                        return Ok(());
-                    }
                 }
             }
             // Proceed recursively even for ZST, no reason to skip them!
@@ -473,7 +479,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
     ) -> InterpResult<'tcx, bool> {
         // Go over all the primitive types
         let ty = value.layout.ty;
-        match ty.kind {
+        match ty.kind() {
             ty::Bool => {
                 let value = self.ecx.read_scalar(value)?;
                 try_validation!(
@@ -688,7 +694,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
         variant_id: VariantIdx,
         new_op: OpTy<'tcx, M::PointerTag>,
     ) -> InterpResult<'tcx> {
-        let name = match old_op.layout.ty.kind {
+        let name = match old_op.layout.ty.kind() {
             ty::Adt(adt, _) => PathElem::Variant(adt.variants[variant_id].ident.name),
             // Generators also have variants
             ty::Generator(..) => PathElem::GeneratorState(variant_id),
@@ -758,7 +764,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
         op: OpTy<'tcx, M::PointerTag>,
         fields: impl Iterator<Item = InterpResult<'tcx, Self::V>>,
     ) -> InterpResult<'tcx> {
-        match op.layout.ty.kind {
+        match op.layout.ty.kind() {
             ty::Str => {
                 let mplace = op.assert_mem_place(self.ecx); // strings are never immediate
                 let len = mplace.len(self.ecx)?;
@@ -775,7 +781,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
                     // FIXME(wesleywiser) This logic could be extended further to arbitrary structs
                     // or tuples made up of integer/floating point types or inhabited ZSTs with no
                     // padding.
-                    match tys.kind {
+                    match tys.kind() {
                         ty::Int(..) | ty::Uint(..) | ty::Float(..) => true,
                         _ => false,
                     }
diff --git a/compiler/rustc_mir/src/interpret/visitor.rs b/compiler/rustc_mir/src/interpret/visitor.rs
index 6c53df40a7c..097b9ae6ca1 100644
--- a/compiler/rustc_mir/src/interpret/visitor.rs
+++ b/compiler/rustc_mir/src/interpret/visitor.rs
@@ -203,7 +203,7 @@ macro_rules! make_value_visitor {
                 trace!("walk_value: type: {}", v.layout().ty);
 
                 // Special treatment for special types, where the (static) layout is not sufficient.
-                match v.layout().ty.kind {
+                match *v.layout().ty.kind() {
                     // If it is a trait object, switch to the real type that was used to create it.
                     ty::Dynamic(..) => {
                         // immediate trait objects are not a thing
diff --git a/compiler/rustc_mir/src/lib.rs b/compiler/rustc_mir/src/lib.rs
index 2e3b5084635..a356ae1709d 100644
--- a/compiler/rustc_mir/src/lib.rs
+++ b/compiler/rustc_mir/src/lib.rs
@@ -6,6 +6,8 @@ Rust MIR: a lowered representation of Rust.
 
 #![feature(nll)]
 #![feature(in_band_lifetimes)]
+#![feature(array_windows)]
+#![feature(bindings_after_at)]
 #![feature(bool_to_option)]
 #![feature(box_patterns)]
 #![feature(box_syntax)]
@@ -13,19 +15,18 @@ Rust MIR: a lowered representation of Rust.
 #![feature(const_panic)]
 #![feature(crate_visibility_modifier)]
 #![feature(decl_macro)]
-#![feature(drain_filter)]
+#![feature(exact_size_is_empty)]
 #![feature(exhaustive_patterns)]
-#![feature(iter_order_by)]
 #![feature(never_type)]
 #![feature(min_specialization)]
 #![feature(trusted_len)]
 #![feature(try_blocks)]
-#![feature(associated_type_bounds)]
 #![feature(associated_type_defaults)]
 #![feature(stmt_expr_attributes)]
 #![feature(trait_alias)]
 #![feature(option_expect_none)]
 #![feature(or_patterns)]
+#![feature(once_cell)]
 #![recursion_limit = "256"]
 
 #[macro_use]
@@ -51,8 +52,8 @@ pub fn provide(providers: &mut Providers) {
     transform::provide(providers);
     monomorphize::partitioning::provide(providers);
     monomorphize::polymorphize::provide(providers);
-    providers.const_eval_validated = const_eval::const_eval_validated_provider;
-    providers.const_eval_raw = const_eval::const_eval_raw_provider;
+    providers.eval_to_const_value_raw = const_eval::eval_to_const_value_raw_provider;
+    providers.eval_to_allocation_raw = const_eval::eval_to_allocation_raw_provider;
     providers.const_caller_location = const_eval::const_caller_location;
     providers.destructure_const = |tcx, param_env_and_value| {
         let (param_env, value) = param_env_and_value.into_parts();
diff --git a/compiler/rustc_mir/src/monomorphize/collector.rs b/compiler/rustc_mir/src/monomorphize/collector.rs
index d379f4ef428..7e12cc9176e 100644
--- a/compiler/rustc_mir/src/monomorphize/collector.rs
+++ b/compiler/rustc_mir/src/monomorphize/collector.rs
@@ -191,13 +191,13 @@ use rustc_middle::mir::mono::{InstantiationMode, MonoItem};
 use rustc_middle::mir::visit::Visitor as MirVisitor;
 use rustc_middle::mir::{self, Local, Location};
 use rustc_middle::ty::adjustment::{CustomCoerceUnsized, PointerCast};
-use rustc_middle::ty::print::obsolete::DefPathBasedNames;
 use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts};
 use rustc_middle::ty::{self, GenericParamDefKind, Instance, Ty, TyCtxt, TypeFoldable};
 use rustc_session::config::EntryFnType;
 use rustc_span::source_map::{dummy_spanned, respan, Span, Spanned, DUMMY_SP};
 use smallvec::SmallVec;
 use std::iter;
+use std::path::PathBuf;
 
 #[derive(PartialEq)]
 pub enum MonoItemCollectionMode {
@@ -348,7 +348,7 @@ fn collect_items_rec<'tcx>(
         // We've been here already, no need to search again.
         return;
     }
-    debug!("BEGIN collect_items_rec({})", starting_point.node.to_string(tcx, true));
+    debug!("BEGIN collect_items_rec({})", starting_point.node);
 
     let mut neighbors = Vec::new();
     let recursion_depth_reset;
@@ -365,8 +365,10 @@ fn collect_items_rec<'tcx>(
 
             recursion_depth_reset = None;
 
-            if let Ok(val) = tcx.const_eval_poly(def_id) {
-                collect_const_value(tcx, val, &mut neighbors);
+            if let Ok(alloc) = tcx.eval_static_initializer(def_id) {
+                for &((), id) in alloc.relocations().values() {
+                    collect_miri(tcx, id, &mut neighbors);
+                }
             }
         }
         MonoItem::Fn(instance) => {
@@ -397,7 +399,7 @@ fn collect_items_rec<'tcx>(
         recursion_depths.insert(def_id, depth);
     }
 
-    debug!("END collect_items_rec({})", starting_point.node.to_string(tcx, true));
+    debug!("END collect_items_rec({})", starting_point.node);
 }
 
 fn record_accesses<'a, 'tcx: 'a>(
@@ -419,6 +421,40 @@ fn record_accesses<'a, 'tcx: 'a>(
     inlining_map.lock_mut().record_accesses(caller, &accesses);
 }
 
+/// Format instance name that is already known to be too long for rustc.
+/// Show only the first and last 32 characters to avoid blasting
+/// the user's terminal with thousands of lines of type-name.
+///
+/// If the type name is longer than before+after, it will be written to a file.
+fn shrunk_instance_name(
+    tcx: TyCtxt<'tcx>,
+    instance: &Instance<'tcx>,
+    before: usize,
+    after: usize,
+) -> (String, Option<PathBuf>) {
+    let s = instance.to_string();
+
+    // Only use the shrunk version if it's really shorter.
+    // This also avoids the case where before and after slices overlap.
+    if s.chars().nth(before + after + 1).is_some() {
+        // An iterator of all byte positions including the end of the string.
+        let positions = || s.char_indices().map(|(i, _)| i).chain(iter::once(s.len()));
+
+        let shrunk = format!(
+            "{before}...{after}",
+            before = &s[..positions().nth(before).unwrap_or(s.len())],
+            after = &s[positions().rev().nth(after).unwrap_or(0)..],
+        );
+
+        let path = tcx.output_filenames(LOCAL_CRATE).temp_path_ext("long-type.txt", None);
+        let written_to_path = std::fs::write(&path, s).ok().map(|_| path);
+
+        (shrunk, written_to_path)
+    } else {
+        (s, None)
+    }
+}
+
 fn check_recursion_limit<'tcx>(
     tcx: TyCtxt<'tcx>,
     instance: Instance<'tcx>,
@@ -441,12 +477,16 @@ fn check_recursion_limit<'tcx>(
     // more than the recursion limit is assumed to be causing an
     // infinite expansion.
     if !tcx.sess.recursion_limit().value_within_limit(adjusted_recursion_depth) {
-        let error = format!("reached the recursion limit while instantiating `{}`", instance);
+        let (shrunk, written_to_path) = shrunk_instance_name(tcx, &instance, 32, 32);
+        let error = format!("reached the recursion limit while instantiating `{}`", shrunk);
         let mut err = tcx.sess.struct_span_fatal(span, &error);
         err.span_note(
             tcx.def_span(def_id),
             &format!("`{}` defined here", tcx.def_path_str(def_id)),
         );
+        if let Some(path) = written_to_path {
+            err.note(&format!("the full type name has been written to '{}'", path.display()));
+        }
         err.emit();
         FatalError.raise();
     }
@@ -475,29 +515,13 @@ fn check_type_length_limit<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) {
     //
     // Bail out in these cases to avoid that bad user experience.
     if !tcx.sess.type_length_limit().value_within_limit(type_length) {
-        // The instance name is already known to be too long for rustc.
-        // Show only the first and last 32 characters to avoid blasting
-        // the user's terminal with thousands of lines of type-name.
-        let shrink = |s: String, before: usize, after: usize| {
-            // An iterator of all byte positions including the end of the string.
-            let positions = || s.char_indices().map(|(i, _)| i).chain(iter::once(s.len()));
-
-            let shrunk = format!(
-                "{before}...{after}",
-                before = &s[..positions().nth(before).unwrap_or(s.len())],
-                after = &s[positions().rev().nth(after).unwrap_or(0)..],
-            );
-
-            // Only use the shrunk version if it's really shorter.
-            // This also avoids the case where before and after slices overlap.
-            if shrunk.len() < s.len() { shrunk } else { s }
-        };
-        let msg = format!(
-            "reached the type-length limit while instantiating `{}`",
-            shrink(instance.to_string(), 32, 32)
-        );
+        let (shrunk, written_to_path) = shrunk_instance_name(tcx, &instance, 32, 32);
+        let msg = format!("reached the type-length limit while instantiating `{}`", shrunk);
         let mut diag = tcx.sess.struct_span_fatal(tcx.def_span(instance.def_id()), &msg);
-        diag.note(&format!(
+        if let Some(path) = written_to_path {
+            diag.note(&format!("the full type name has been written to '{}'", path.display()));
+        }
+        diag.help(&format!(
             "consider adding a `#![type_length_limit=\"{}\"]` attribute to your crate",
             type_length
         ));
@@ -576,7 +600,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
             ) => {
                 let source_ty = operand.ty(self.body, self.tcx);
                 let source_ty = self.monomorphize(source_ty);
-                match source_ty.kind {
+                match *source_ty.kind() {
                     ty::Closure(def_id, substs) => {
                         let instance = Instance::resolve_closure(
                             self.tcx,
@@ -717,7 +741,7 @@ fn visit_fn_use<'tcx>(
     source: Span,
     output: &mut Vec<Spanned<MonoItem<'tcx>>>,
 ) {
-    if let ty::FnDef(def_id, substs) = ty.kind {
+    if let ty::FnDef(def_id, substs) = *ty.kind() {
         let instance = if is_direct_call {
             ty::Instance::resolve(tcx, ty::ParamEnv::reveal_all(), def_id, substs).unwrap().unwrap()
         } else {
@@ -854,7 +878,7 @@ fn find_vtable_types_for_unsizing<'tcx>(
                 return false;
             }
             let tail = tcx.struct_tail_erasing_lifetimes(ty, param_env);
-            match tail.kind {
+            match tail.kind() {
                 ty::Foreign(..) => false,
                 ty::Str | ty::Slice(..) | ty::Dynamic(..) => true,
                 _ => bug!("unexpected unsized tail: {:?}", tail),
@@ -867,7 +891,7 @@ fn find_vtable_types_for_unsizing<'tcx>(
         }
     };
 
-    match (&source_ty.kind, &target_ty.kind) {
+    match (&source_ty.kind(), &target_ty.kind()) {
         (&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(ty::TypeAndMut { ty: b, .. }))
         | (&ty::RawPtr(ty::TypeAndMut { ty: a, .. }), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => {
             ptr_vtable(a, b)
@@ -923,7 +947,7 @@ fn create_mono_items_for_vtable_methods<'tcx>(
 ) {
     assert!(!trait_ty.has_escaping_bound_vars() && !impl_ty.has_escaping_bound_vars());
 
-    if let ty::Dynamic(ref trait_ty, ..) = trait_ty.kind {
+    if let ty::Dynamic(ref trait_ty, ..) = trait_ty.kind() {
         if let Some(principal) = trait_ty.principal() {
             let poly_trait_ref = principal.with_self_ty(tcx, impl_ty);
             assert!(!poly_trait_ref.has_escaping_bound_vars());
@@ -992,7 +1016,7 @@ impl ItemLikeVisitor<'v> for RootCollector<'_, 'v> {
                         let def_id = self.tcx.hir().local_def_id(item.hir_id);
                         debug!(
                             "RootCollector: ADT drop-glue for {}",
-                            def_id_to_string(self.tcx, def_id)
+                            self.tcx.def_path_str(def_id.to_def_id())
                         );
 
                         let ty = Instance::new(def_id.to_def_id(), InternalSubsts::empty())
@@ -1004,14 +1028,14 @@ impl ItemLikeVisitor<'v> for RootCollector<'_, 'v> {
             hir::ItemKind::GlobalAsm(..) => {
                 debug!(
                     "RootCollector: ItemKind::GlobalAsm({})",
-                    def_id_to_string(self.tcx, self.tcx.hir().local_def_id(item.hir_id))
+                    self.tcx.def_path_str(self.tcx.hir().local_def_id(item.hir_id).to_def_id())
                 );
                 self.output.push(dummy_spanned(MonoItem::GlobalAsm(item.hir_id)));
             }
             hir::ItemKind::Static(..) => {
-                let def_id = self.tcx.hir().local_def_id(item.hir_id);
-                debug!("RootCollector: ItemKind::Static({})", def_id_to_string(self.tcx, def_id));
-                self.output.push(dummy_spanned(MonoItem::Static(def_id.to_def_id())));
+                let def_id = self.tcx.hir().local_def_id(item.hir_id).to_def_id();
+                debug!("RootCollector: ItemKind::Static({})", self.tcx.def_path_str(def_id));
+                self.output.push(dummy_spanned(MonoItem::Static(def_id)));
             }
             hir::ItemKind::Const(..) => {
                 // const items only generate mono items if they are
@@ -1134,7 +1158,7 @@ fn create_mono_items_for_default_impls<'tcx>(
 
             debug!(
                 "create_mono_items_for_default_impls(item={})",
-                def_id_to_string(tcx, impl_def_id)
+                tcx.def_path_str(impl_def_id.to_def_id())
             );
 
             if let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id) {
@@ -1218,13 +1242,6 @@ fn collect_neighbours<'tcx>(
     MirNeighborCollector { tcx, body: &body, output, instance }.visit_body(&body);
 }
 
-fn def_id_to_string(tcx: TyCtxt<'_>, def_id: LocalDefId) -> String {
-    let mut output = String::new();
-    let printer = DefPathBasedNames::new(tcx, false, false);
-    printer.push_def_path(def_id.to_def_id(), &mut output);
-    output
-}
-
 fn collect_const_value<'tcx>(
     tcx: TyCtxt<'tcx>,
     value: ConstValue<'tcx>,
diff --git a/compiler/rustc_mir/src/monomorphize/partitioning/default.rs b/compiler/rustc_mir/src/monomorphize/partitioning/default.rs
index b48bae83787..827d037f319 100644
--- a/compiler/rustc_mir/src/monomorphize/partitioning/default.rs
+++ b/compiler/rustc_mir/src/monomorphize/partitioning/default.rs
@@ -11,6 +11,7 @@ use rustc_middle::ty::print::characteristic_def_id_of_type;
 use rustc_middle::ty::{self, DefIdTree, InstanceDef, TyCtxt};
 use rustc_span::symbol::Symbol;
 
+use super::PartitioningCx;
 use crate::monomorphize::collector::InliningMap;
 use crate::monomorphize::partitioning::merging;
 use crate::monomorphize::partitioning::{
@@ -22,35 +23,36 @@ pub struct DefaultPartitioning;
 impl<'tcx> Partitioner<'tcx> for DefaultPartitioning {
     fn place_root_mono_items(
         &mut self,
-        tcx: TyCtxt<'tcx>,
+        cx: &PartitioningCx<'_, 'tcx>,
         mono_items: &mut dyn Iterator<Item = MonoItem<'tcx>>,
     ) -> PreInliningPartitioning<'tcx> {
         let mut roots = FxHashSet::default();
         let mut codegen_units = FxHashMap::default();
-        let is_incremental_build = tcx.sess.opts.incremental.is_some();
+        let is_incremental_build = cx.tcx.sess.opts.incremental.is_some();
         let mut internalization_candidates = FxHashSet::default();
 
         // Determine if monomorphizations instantiated in this crate will be made
         // available to downstream crates. This depends on whether we are in
         // share-generics mode and whether the current crate can even have
         // downstream crates.
-        let export_generics = tcx.sess.opts.share_generics() && tcx.local_crate_exports_generics();
+        let export_generics =
+            cx.tcx.sess.opts.share_generics() && cx.tcx.local_crate_exports_generics();
 
-        let cgu_name_builder = &mut CodegenUnitNameBuilder::new(tcx);
+        let cgu_name_builder = &mut CodegenUnitNameBuilder::new(cx.tcx);
         let cgu_name_cache = &mut FxHashMap::default();
 
         for mono_item in mono_items {
-            match mono_item.instantiation_mode(tcx) {
+            match mono_item.instantiation_mode(cx.tcx) {
                 InstantiationMode::GloballyShared { .. } => {}
                 InstantiationMode::LocalCopy => continue,
             }
 
-            let characteristic_def_id = characteristic_def_id_of_mono_item(tcx, mono_item);
+            let characteristic_def_id = characteristic_def_id_of_mono_item(cx.tcx, mono_item);
             let is_volatile = is_incremental_build && mono_item.is_generic_fn();
 
             let codegen_unit_name = match characteristic_def_id {
                 Some(def_id) => compute_codegen_unit_name(
-                    tcx,
+                    cx.tcx,
                     cgu_name_builder,
                     def_id,
                     is_volatile,
@@ -65,7 +67,7 @@ impl<'tcx> Partitioner<'tcx> for DefaultPartitioning {
 
             let mut can_be_internalized = true;
             let (linkage, visibility) = mono_item_linkage_and_visibility(
-                tcx,
+                cx.tcx,
                 &mono_item,
                 &mut can_be_internalized,
                 export_generics,
@@ -97,17 +99,16 @@ impl<'tcx> Partitioner<'tcx> for DefaultPartitioning {
 
     fn merge_codegen_units(
         &mut self,
-        tcx: TyCtxt<'tcx>,
+        cx: &PartitioningCx<'_, 'tcx>,
         initial_partitioning: &mut PreInliningPartitioning<'tcx>,
-        target_cgu_count: usize,
     ) {
-        merging::merge_codegen_units(tcx, initial_partitioning, target_cgu_count);
+        merging::merge_codegen_units(cx, initial_partitioning);
     }
 
     fn place_inlined_mono_items(
         &mut self,
+        cx: &PartitioningCx<'_, 'tcx>,
         initial_partitioning: PreInliningPartitioning<'tcx>,
-        inlining_map: &InliningMap<'tcx>,
     ) -> PostInliningPartitioning<'tcx> {
         let mut new_partitioning = Vec::new();
         let mut mono_item_placements = FxHashMap::default();
@@ -124,7 +125,7 @@ impl<'tcx> Partitioner<'tcx> for DefaultPartitioning {
             // Collect all items that need to be available in this codegen unit.
             let mut reachable = FxHashSet::default();
             for root in old_codegen_unit.items().keys() {
-                follow_inlining(*root, inlining_map, &mut reachable);
+                follow_inlining(*root, cx.inlining_map, &mut reachable);
             }
 
             let mut new_codegen_unit = CodegenUnit::new(old_codegen_unit.name());
@@ -198,9 +199,8 @@ impl<'tcx> Partitioner<'tcx> for DefaultPartitioning {
 
     fn internalize_symbols(
         &mut self,
-        _tcx: TyCtxt<'tcx>,
+        cx: &PartitioningCx<'_, 'tcx>,
         partitioning: &mut PostInliningPartitioning<'tcx>,
-        inlining_map: &InliningMap<'tcx>,
     ) {
         if partitioning.codegen_units.len() == 1 {
             // Fast path for when there is only one codegen unit. In this case we
@@ -218,7 +218,7 @@ impl<'tcx> Partitioner<'tcx> for DefaultPartitioning {
         // Build a map from every monomorphization to all the monomorphizations that
         // reference it.
         let mut accessor_map: FxHashMap<MonoItem<'tcx>, Vec<MonoItem<'tcx>>> = Default::default();
-        inlining_map.iter_accesses(|accessor, accessees| {
+        cx.inlining_map.iter_accesses(|accessor, accessees| {
             for accessee in accessees {
                 accessor_map.entry(*accessee).or_default().push(accessor);
             }
diff --git a/compiler/rustc_mir/src/monomorphize/partitioning/merging.rs b/compiler/rustc_mir/src/monomorphize/partitioning/merging.rs
index 1787e6df1b9..5107e697263 100644
--- a/compiler/rustc_mir/src/monomorphize/partitioning/merging.rs
+++ b/compiler/rustc_mir/src/monomorphize/partitioning/merging.rs
@@ -3,17 +3,16 @@ use std::cmp;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::def_id::LOCAL_CRATE;
 use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder};
-use rustc_middle::ty::TyCtxt;
 use rustc_span::symbol::{Symbol, SymbolStr};
 
+use super::PartitioningCx;
 use crate::monomorphize::partitioning::PreInliningPartitioning;
 
 pub fn merge_codegen_units<'tcx>(
-    tcx: TyCtxt<'tcx>,
+    cx: &PartitioningCx<'_, 'tcx>,
     initial_partitioning: &mut PreInliningPartitioning<'tcx>,
-    target_cgu_count: usize,
 ) {
-    assert!(target_cgu_count >= 1);
+    assert!(cx.target_cgu_count >= 1);
     let codegen_units = &mut initial_partitioning.codegen_units;
 
     // Note that at this point in time the `codegen_units` here may not be in a
@@ -32,7 +31,7 @@ pub fn merge_codegen_units<'tcx>(
         codegen_units.iter().map(|cgu| (cgu.name(), vec![cgu.name().as_str()])).collect();
 
     // Merge the two smallest codegen units until the target size is reached.
-    while codegen_units.len() > target_cgu_count {
+    while codegen_units.len() > cx.target_cgu_count {
         // Sort small cgus to the back
         codegen_units.sort_by_cached_key(|cgu| cmp::Reverse(cgu.size_estimate()));
         let mut smallest = codegen_units.pop().unwrap();
@@ -56,9 +55,9 @@ pub fn merge_codegen_units<'tcx>(
         );
     }
 
-    let cgu_name_builder = &mut CodegenUnitNameBuilder::new(tcx);
+    let cgu_name_builder = &mut CodegenUnitNameBuilder::new(cx.tcx);
 
-    if tcx.sess.opts.incremental.is_some() {
+    if cx.tcx.sess.opts.incremental.is_some() {
         // If we are doing incremental compilation, we want CGU names to
         // reflect the path of the source level module they correspond to.
         // For CGUs that contain the code of multiple modules because of the
@@ -74,7 +73,9 @@ pub fn merge_codegen_units<'tcx>(
 
                 // Sort the names, so things are deterministic and easy to
                 // predict.
-                cgu_contents.sort();
+
+                // We are sorting primitive &strs here so we can use unstable sort
+                cgu_contents.sort_unstable();
 
                 (current_cgu_name, cgu_contents.join("--"))
             })
@@ -82,7 +83,7 @@ pub fn merge_codegen_units<'tcx>(
 
         for cgu in codegen_units.iter_mut() {
             if let Some(new_cgu_name) = new_cgu_names.get(&cgu.name()) {
-                if tcx.sess.opts.debugging_opts.human_readable_cgu_names {
+                if cx.tcx.sess.opts.debugging_opts.human_readable_cgu_names {
                     cgu.set_name(Symbol::intern(&new_cgu_name));
                 } else {
                     // If we don't require CGU names to be human-readable, we
diff --git a/compiler/rustc_mir/src/monomorphize/partitioning/mod.rs b/compiler/rustc_mir/src/monomorphize/partitioning/mod.rs
index 9dfbd65e1b1..db6d3b2d912 100644
--- a/compiler/rustc_mir/src/monomorphize/partitioning/mod.rs
+++ b/compiler/rustc_mir/src/monomorphize/partitioning/mod.rs
@@ -100,6 +100,7 @@ use rustc_data_structures::sync;
 use rustc_hir::def_id::{CrateNum, DefIdSet, LOCAL_CRATE};
 use rustc_middle::mir::mono::MonoItem;
 use rustc_middle::mir::mono::{CodegenUnit, Linkage};
+use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::TyCtxt;
 use rustc_span::symbol::Symbol;
@@ -107,31 +108,35 @@ use rustc_span::symbol::Symbol;
 use crate::monomorphize::collector::InliningMap;
 use crate::monomorphize::collector::{self, MonoItemCollectionMode};
 
+pub struct PartitioningCx<'a, 'tcx> {
+    tcx: TyCtxt<'tcx>,
+    target_cgu_count: usize,
+    inlining_map: &'a InliningMap<'tcx>,
+}
+
 trait Partitioner<'tcx> {
     fn place_root_mono_items(
         &mut self,
-        tcx: TyCtxt<'tcx>,
+        cx: &PartitioningCx<'_, 'tcx>,
         mono_items: &mut dyn Iterator<Item = MonoItem<'tcx>>,
     ) -> PreInliningPartitioning<'tcx>;
 
     fn merge_codegen_units(
         &mut self,
-        tcx: TyCtxt<'tcx>,
+        cx: &PartitioningCx<'_, 'tcx>,
         initial_partitioning: &mut PreInliningPartitioning<'tcx>,
-        target_cgu_count: usize,
     );
 
     fn place_inlined_mono_items(
         &mut self,
+        cx: &PartitioningCx<'_, 'tcx>,
         initial_partitioning: PreInliningPartitioning<'tcx>,
-        inlining_map: &InliningMap<'tcx>,
     ) -> PostInliningPartitioning<'tcx>;
 
     fn internalize_symbols(
         &mut self,
-        tcx: TyCtxt<'tcx>,
+        cx: &PartitioningCx<'_, 'tcx>,
         partitioning: &mut PostInliningPartitioning<'tcx>,
-        inlining_map: &InliningMap<'tcx>,
     );
 }
 
@@ -156,12 +161,13 @@ pub fn partition<'tcx>(
     let _prof_timer = tcx.prof.generic_activity("cgu_partitioning");
 
     let mut partitioner = get_partitioner(tcx);
+    let cx = &PartitioningCx { tcx, target_cgu_count: max_cgu_count, inlining_map };
     // In the first step, we place all regular monomorphizations into their
     // respective 'home' codegen unit. Regular monomorphizations are all
     // functions and statics defined in the local crate.
     let mut initial_partitioning = {
         let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_place_roots");
-        partitioner.place_root_mono_items(tcx, mono_items)
+        partitioner.place_root_mono_items(cx, mono_items)
     };
 
     initial_partitioning.codegen_units.iter_mut().for_each(|cgu| cgu.estimate_size(tcx));
@@ -171,7 +177,7 @@ pub fn partition<'tcx>(
     // Merge until we have at most `max_cgu_count` codegen units.
     {
         let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_merge_cgus");
-        partitioner.merge_codegen_units(tcx, &mut initial_partitioning, max_cgu_count);
+        partitioner.merge_codegen_units(cx, &mut initial_partitioning);
         debug_dump(tcx, "POST MERGING:", initial_partitioning.codegen_units.iter());
     }
 
@@ -181,7 +187,7 @@ pub fn partition<'tcx>(
     // local functions the definition of which is marked with `#[inline]`.
     let mut post_inlining = {
         let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_place_inline_items");
-        partitioner.place_inlined_mono_items(initial_partitioning, inlining_map)
+        partitioner.place_inlined_mono_items(cx, initial_partitioning)
     };
 
     post_inlining.codegen_units.iter_mut().for_each(|cgu| cgu.estimate_size(tcx));
@@ -190,9 +196,9 @@ pub fn partition<'tcx>(
 
     // Next we try to make as many symbols "internal" as possible, so LLVM has
     // more freedom to optimize.
-    if tcx.sess.opts.cg.link_dead_code != Some(true) {
+    if !tcx.sess.link_dead_code() {
         let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_internalize_symbols");
-        partitioner.internalize_symbols(tcx, &mut post_inlining, inlining_map);
+        partitioner.internalize_symbols(cx, &mut post_inlining);
     }
 
     // Finally, sort by codegen unit name, so that we get deterministic results.
@@ -246,7 +252,7 @@ where
 
                 debug!(
                     " - {} [{:?}] [{}] estimated size {}",
-                    mono_item.to_string(tcx, true),
+                    mono_item,
                     linkage,
                     symbol_hash,
                     mono_item.size_estimate(tcx)
@@ -271,14 +277,8 @@ where
 
     symbols.sort_by_key(|sym| sym.1);
 
-    for pair in symbols.windows(2) {
-        let sym1 = &pair[0].1;
-        let sym2 = &pair[1].1;
-
+    for &[(mono_item1, ref sym1), (mono_item2, ref sym2)] in symbols.array_windows() {
         if sym1 == sym2 {
-            let mono_item1 = pair[0].0;
-            let mono_item2 = pair[1].0;
-
             let span1 = mono_item1.local_span(tcx);
             let span2 = mono_item2.local_span(tcx);
 
@@ -327,7 +327,7 @@ fn collect_and_partition_mono_items<'tcx>(
             }
         }
         None => {
-            if tcx.sess.opts.cg.link_dead_code == Some(true) {
+            if tcx.sess.link_dead_code() {
                 MonoItemCollectionMode::Eager
             } else {
                 MonoItemCollectionMode::Lazy
@@ -374,14 +374,14 @@ fn collect_and_partition_mono_items<'tcx>(
         let mut item_keys: Vec<_> = items
             .iter()
             .map(|i| {
-                let mut output = i.to_string(tcx, false);
+                let mut output = with_no_trimmed_paths(|| i.to_string());
                 output.push_str(" @@");
                 let mut empty = Vec::new();
                 let cgus = item_to_cgus.get_mut(i).unwrap_or(&mut empty);
                 cgus.sort_by_key(|(name, _)| *name);
                 cgus.dedup();
                 for &(ref cgu_name, (linkage, _)) in cgus.iter() {
-                    output.push_str(" ");
+                    output.push(' ');
                     output.push_str(&cgu_name.as_str());
 
                     let linkage_abbrev = match linkage {
@@ -398,9 +398,9 @@ fn collect_and_partition_mono_items<'tcx>(
                         Linkage::Common => "Common",
                     };
 
-                    output.push_str("[");
+                    output.push('[');
                     output.push_str(linkage_abbrev);
-                    output.push_str("]");
+                    output.push(']');
                 }
                 output
             })
diff --git a/compiler/rustc_mir/src/monomorphize/polymorphize.rs b/compiler/rustc_mir/src/monomorphize/polymorphize.rs
index 69f3288ee39..3f6f117acdc 100644
--- a/compiler/rustc_mir/src/monomorphize/polymorphize.rs
+++ b/compiler/rustc_mir/src/monomorphize/polymorphize.rs
@@ -288,7 +288,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> {
             return false;
         }
 
-        match ty.kind {
+        match *ty.kind() {
             ty::Closure(def_id, substs) | ty::Generator(def_id, substs, ..) => {
                 debug!("visit_ty: def_id={:?}", def_id);
                 // Avoid cycle errors with generators.
@@ -337,7 +337,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for HasUsedGenericParams<'a> {
             return false;
         }
 
-        match ty.kind {
+        match ty.kind() {
             ty::Param(param) => !self.unused_parameters.contains(param.index).unwrap_or(false),
             _ => ty.super_visit_with(self),
         }
diff --git a/compiler/rustc_mir/src/shim.rs b/compiler/rustc_mir/src/shim.rs
index 479b6c2a6ca..7e4d189f0b7 100644
--- a/compiler/rustc_mir/src/shim.rs
+++ b/compiler/rustc_mir/src/shim.rs
@@ -33,7 +33,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
     let mut result = match instance {
         ty::InstanceDef::Item(..) => bug!("item {:?} passed to make_shim", instance),
         ty::InstanceDef::VtableShim(def_id) => {
-            build_call_shim(tcx, instance, Some(Adjustment::Deref), CallKind::Direct(def_id), None)
+            build_call_shim(tcx, instance, Some(Adjustment::Deref), CallKind::Direct(def_id))
         }
         ty::InstanceDef::FnPtrShim(def_id, ty) => {
             let trait_ = tcx.trait_of_item(def_id).unwrap();
@@ -42,16 +42,8 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
                 Some(ty::ClosureKind::FnMut | ty::ClosureKind::Fn) => Adjustment::Deref,
                 None => bug!("fn pointer {:?} is not an fn", ty),
             };
-            // HACK: we need the "real" argument types for the MIR,
-            // but because our substs are (Self, Args), where Args
-            // is a tuple, we must include the *concrete* argument
-            // types in the MIR. They will be substituted again with
-            // the param-substs, but because they are concrete, this
-            // will not do any harm.
-            let sig = tcx.erase_late_bound_regions(&ty.fn_sig(tcx));
-            let arg_tys = sig.inputs();
-
-            build_call_shim(tcx, instance, Some(adjustment), CallKind::Indirect(ty), Some(arg_tys))
+
+            build_call_shim(tcx, instance, Some(adjustment), CallKind::Indirect(ty))
         }
         // We are generating a call back to our def-id, which the
         // codegen backend knows to turn to an actual call, be it
@@ -59,7 +51,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
         // indirect calls must be codegen'd differently than direct ones
         // (such as `#[track_caller]`).
         ty::InstanceDef::ReifyShim(def_id) => {
-            build_call_shim(tcx, instance, None, CallKind::Direct(def_id), None)
+            build_call_shim(tcx, instance, None, CallKind::Direct(def_id))
         }
         ty::InstanceDef::ClosureOnceShim { call_once: _ } => {
             let fn_mut = tcx.require_lang_item(LangItem::FnMut, None);
@@ -70,13 +62,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
                 .unwrap()
                 .def_id;
 
-            build_call_shim(
-                tcx,
-                instance,
-                Some(Adjustment::RefMut),
-                CallKind::Direct(call_mut),
-                None,
-            )
+            build_call_shim(tcx, instance, Some(Adjustment::RefMut), CallKind::Direct(call_mut))
         }
         ty::InstanceDef::DropGlue(def_id, ty) => build_drop_shim(tcx, def_id, ty),
         ty::InstanceDef::CloneShim(def_id, ty) => build_clone_shim(tcx, def_id, ty),
@@ -149,7 +135,7 @@ fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option<Ty<'tcx>>)
     debug!("build_drop_shim(def_id={:?}, ty={:?})", def_id, ty);
 
     // Check if this is a generator, if so, return the drop glue for it
-    if let Some(&ty::TyS { kind: ty::Generator(gen_def_id, substs, _), .. }) = ty {
+    if let Some(&ty::Generator(gen_def_id, substs, _)) = ty.map(|ty| ty.kind()) {
         let body = &**tcx.optimized_mir(gen_def_id).generator_drop.as_ref().unwrap();
         return body.subst(tcx, substs);
     }
@@ -312,7 +298,7 @@ fn build_clone_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) -
     let dest = Place::return_place();
     let src = tcx.mk_place_deref(Place::from(Local::new(1 + 0)));
 
-    match self_ty.kind {
+    match self_ty.kind() {
         _ if is_copy => builder.copy_shim(),
         ty::Array(ty, len) => {
             let len = len.eval_usize(tcx, param_env);
@@ -641,29 +627,45 @@ impl CloneShimBuilder<'tcx> {
     }
 }
 
-/// Builds a "call" shim for `instance`. The shim calls the
-/// function specified by `call_kind`, first adjusting its first
-/// argument according to `rcvr_adjustment`.
-///
-/// If `untuple_args` is a vec of types, the second argument of the
-/// function will be untupled as these types.
+/// Builds a "call" shim for `instance`. The shim calls the function specified by `call_kind`,
+/// first adjusting its first argument according to `rcvr_adjustment`.
 fn build_call_shim<'tcx>(
     tcx: TyCtxt<'tcx>,
     instance: ty::InstanceDef<'tcx>,
     rcvr_adjustment: Option<Adjustment>,
     call_kind: CallKind<'tcx>,
-    untuple_args: Option<&[Ty<'tcx>]>,
 ) -> Body<'tcx> {
     debug!(
-        "build_call_shim(instance={:?}, rcvr_adjustment={:?}, \
-            call_kind={:?}, untuple_args={:?})",
-        instance, rcvr_adjustment, call_kind, untuple_args
+        "build_call_shim(instance={:?}, rcvr_adjustment={:?}, call_kind={:?})",
+        instance, rcvr_adjustment, call_kind
     );
 
+    // `FnPtrShim` contains the fn pointer type that a call shim is being built for - this is used
+    // to substitute into the signature of the shim. It is not necessary for users of this
+    // MIR body to perform further substitutions (see `InstanceDef::has_polymorphic_mir_body`).
+    let (sig_substs, untuple_args) = if let ty::InstanceDef::FnPtrShim(_, ty) = instance {
+        let sig = tcx.erase_late_bound_regions(&ty.fn_sig(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.iter());
+        let sig_substs = tcx.mk_substs_trait(ty, &[ty::subst::GenericArg::from(arg_tup)]);
+
+        (Some(sig_substs), Some(untuple_args))
+    } else {
+        (None, None)
+    };
+
     let def_id = instance.def_id();
     let sig = tcx.fn_sig(def_id);
     let mut sig = tcx.erase_late_bound_regions(&sig);
 
+    assert_eq!(sig_substs.is_some(), !instance.has_polymorphic_mir_body());
+    if let Some(sig_substs) = sig_substs {
+        sig = sig.subst(tcx, sig_substs);
+    }
+
     if let CallKind::Indirect(fnty) = call_kind {
         // `sig` determines our local decls, and thus the callee type in the `Call` terminator. This
         // can only be an `FnDef` or `FnPtr`, but currently will be `Self` since the types come from
@@ -853,7 +855,7 @@ pub fn build_adt_ctor(tcx: TyCtxt<'_>, ctor_id: DefId) -> Body<'_> {
     let sig = tcx.fn_sig(ctor_id).no_bound_vars().expect("LBR in ADT constructor signature");
     let sig = tcx.normalize_erasing_regions(param_env, sig);
 
-    let (adt_def, substs) = match sig.output().kind {
+    let (adt_def, substs) = match sig.output().kind() {
         ty::Adt(adt_def, substs) => (adt_def, substs),
         _ => bug!("unexpected type for ADT ctor {:?}", sig.output()),
     };
diff --git a/compiler/rustc_mir/src/transform/add_retag.rs b/compiler/rustc_mir/src/transform/add_retag.rs
index 324289166b9..0c596ba7154 100644
--- a/compiler/rustc_mir/src/transform/add_retag.rs
+++ b/compiler/rustc_mir/src/transform/add_retag.rs
@@ -35,7 +35,7 @@ fn is_stable(place: PlaceRef<'_>) -> bool {
 
 /// Determine whether this type may be a reference (or box), and thus needs retagging.
 fn may_be_reference(ty: Ty<'tcx>) -> bool {
-    match ty.kind {
+    match ty.kind() {
         // Primitive types that are not references
         ty::Bool
         | ty::Char
diff --git a/compiler/rustc_mir/src/transform/check_const_item_mutation.rs b/compiler/rustc_mir/src/transform/check_const_item_mutation.rs
new file mode 100644
index 00000000000..70c1aed0957
--- /dev/null
+++ b/compiler/rustc_mir/src/transform/check_const_item_mutation.rs
@@ -0,0 +1,114 @@
+use rustc_errors::DiagnosticBuilder;
+use rustc_middle::lint::LintDiagnosticBuilder;
+use rustc_middle::mir::visit::Visitor;
+use rustc_middle::mir::*;
+use rustc_middle::ty::TyCtxt;
+use rustc_session::lint::builtin::CONST_ITEM_MUTATION;
+use rustc_span::def_id::DefId;
+
+use crate::transform::{MirPass, MirSource};
+
+pub struct CheckConstItemMutation;
+
+impl<'tcx> MirPass<'tcx> for CheckConstItemMutation {
+    fn run_pass(&self, tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut Body<'tcx>) {
+        let mut checker = ConstMutationChecker { body, tcx, target_local: None };
+        checker.visit_body(&body);
+    }
+}
+
+struct ConstMutationChecker<'a, 'tcx> {
+    body: &'a Body<'tcx>,
+    tcx: TyCtxt<'tcx>,
+    target_local: Option<Local>,
+}
+
+impl<'a, 'tcx> ConstMutationChecker<'a, 'tcx> {
+    fn is_const_item(&self, local: Local) -> Option<DefId> {
+        if let Some(box LocalInfo::ConstRef { def_id }) = self.body.local_decls[local].local_info {
+            Some(def_id)
+        } else {
+            None
+        }
+    }
+    fn lint_const_item_usage(
+        &self,
+        const_item: DefId,
+        location: Location,
+        decorate: impl for<'b> FnOnce(LintDiagnosticBuilder<'b>) -> DiagnosticBuilder<'b>,
+    ) {
+        let source_info = self.body.source_info(location);
+        let lint_root = self.body.source_scopes[source_info.scope]
+            .local_data
+            .as_ref()
+            .assert_crate_local()
+            .lint_root;
+
+        self.tcx.struct_span_lint_hir(CONST_ITEM_MUTATION, lint_root, source_info.span, |lint| {
+            decorate(lint)
+                .span_note(self.tcx.def_span(const_item), "`const` item defined here")
+                .emit()
+        });
+    }
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for ConstMutationChecker<'a, 'tcx> {
+    fn visit_statement(&mut self, stmt: &Statement<'tcx>, loc: Location) {
+        if let StatementKind::Assign(box (lhs, _)) = &stmt.kind {
+            // Check for assignment to fields of a constant
+            // Assigning directly to a constant (e.g. `FOO = true;`) is a hard error,
+            // so emitting a lint would be redundant.
+            if !lhs.projection.is_empty() {
+                if let Some(def_id) = self.is_const_item(lhs.local) {
+                    self.lint_const_item_usage(def_id, loc, |lint| {
+                        let mut lint = lint.build("attempting to modify a `const` item");
+                        lint.note("each usage of a `const` item creates a new temporary - the original `const` item will not be modified");
+                        lint
+                    })
+                }
+            }
+            // We are looking for MIR of the form:
+            //
+            // ```
+            // _1 = const FOO;
+            // _2 = &mut _1;
+            // method_call(_2, ..)
+            // ```
+            //
+            // Record our current LHS, so that we can detect this
+            // pattern in `visit_rvalue`
+            self.target_local = lhs.as_local();
+        }
+        self.super_statement(stmt, loc);
+        self.target_local = None;
+    }
+    fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, loc: Location) {
+        if let Rvalue::Ref(_, BorrowKind::Mut { .. }, place) = rvalue {
+            let local = place.local;
+            if let Some(def_id) = self.is_const_item(local) {
+                // If this Rvalue is being used as the right-hand side of a
+                // `StatementKind::Assign`, see if it ends up getting used as
+                // the `self` parameter of a method call (as the terminator of our current
+                // BasicBlock). If so, we emit a more specific lint.
+                let method_did = self.target_local.and_then(|target_local| {
+                    crate::util::find_self_call(self.tcx, &self.body, target_local, loc.block)
+                });
+                let lint_loc =
+                    if method_did.is_some() { self.body.terminator_loc(loc.block) } else { loc };
+                self.lint_const_item_usage(def_id, lint_loc, |lint| {
+                    let mut lint = lint.build("taking a mutable reference to a `const` item");
+                    lint
+                        .note("each usage of a `const` item creates a new temporary")
+                        .note("the mutable reference will refer to this temporary, not the original `const` item");
+
+                    if let Some((method_did, _substs)) = method_did {
+                        lint.span_note(self.tcx.def_span(method_did), "mutable reference created due to call to this method");
+                    }
+
+                    lint
+                });
+            }
+        }
+        self.super_rvalue(rvalue, loc);
+    }
+}
diff --git a/compiler/rustc_mir/src/transform/check_consts/mod.rs b/compiler/rustc_mir/src/transform/check_consts/mod.rs
index 81c1b0b5bd4..8d4efd8ae80 100644
--- a/compiler/rustc_mir/src/transform/check_consts/mod.rs
+++ b/compiler/rustc_mir/src/transform/check_consts/mod.rs
@@ -4,10 +4,12 @@
 //! has interior mutability or needs to be dropped, as well as the visitor that emits errors when
 //! it finds operations that are invalid in a certain context.
 
+use rustc_attr as attr;
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_middle::mir;
 use rustc_middle::ty::{self, TyCtxt};
+use rustc_span::Symbol;
 
 pub use self::qualifs::Qualif;
 
@@ -49,9 +51,55 @@ impl ConstCx<'mir, 'tcx> {
     pub fn const_kind(&self) -> hir::ConstContext {
         self.const_kind.expect("`const_kind` must not be called on a non-const fn")
     }
+
+    pub fn is_const_stable_const_fn(&self) -> bool {
+        self.const_kind == Some(hir::ConstContext::ConstFn)
+            && self.tcx.features().staged_api
+            && is_const_stable_const_fn(self.tcx, self.def_id.to_def_id())
+    }
 }
 
 /// Returns `true` if this `DefId` points to one of the official `panic` lang items.
 pub fn is_lang_panic_fn(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool {
     Some(def_id) == tcx.lang_items().panic_fn() || Some(def_id) == tcx.lang_items().begin_panic_fn()
 }
+
+pub fn allow_internal_unstable(tcx: TyCtxt<'tcx>, def_id: DefId, feature_gate: Symbol) -> bool {
+    let attrs = tcx.get_attrs(def_id);
+    attr::allow_internal_unstable(&tcx.sess, attrs)
+        .map_or(false, |mut features| features.any(|name| name == feature_gate))
+}
+
+// Returns `true` if the given `const fn` is "const-stable".
+//
+// Panics if the given `DefId` does not refer to a `const fn`.
+//
+// Const-stability is only relevant for `const fn` within a `staged_api` crate. Only "const-stable"
+// functions can be called in a const-context by users of the stable compiler. "const-stable"
+// functions are subject to more stringent restrictions than "const-unstable" functions: They
+// cannot use unstable features and can only call other "const-stable" functions.
+pub fn is_const_stable_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool {
+    use attr::{ConstStability, Stability, StabilityLevel};
+
+    // Const-stability is only relevant for `const fn`.
+    assert!(tcx.is_const_fn_raw(def_id));
+
+    // Functions with `#[rustc_const_unstable]` are const-unstable.
+    match tcx.lookup_const_stability(def_id) {
+        Some(ConstStability { level: StabilityLevel::Unstable { .. }, .. }) => return false,
+        Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }) => return true,
+        None => {}
+    }
+
+    // Functions with `#[unstable]` are const-unstable.
+    //
+    // FIXME(ecstaticmorse): We should keep const-stability attributes wholly separate from normal stability
+    // attributes. `#[unstable]` should be irrelevant.
+    if let Some(Stability { level: StabilityLevel::Unstable { .. }, .. }) =
+        tcx.lookup_stability(def_id)
+    {
+        return false;
+    }
+
+    true
+}
diff --git a/compiler/rustc_mir/src/transform/check_consts/ops.rs b/compiler/rustc_mir/src/transform/check_consts/ops.rs
index ea025f208e4..496e620dd9d 100644
--- a/compiler/rustc_mir/src/transform/check_consts/ops.rs
+++ b/compiler/rustc_mir/src/transform/check_consts/ops.rs
@@ -1,6 +1,6 @@
 //! Concrete error types for all operations which may be invalid in a certain const context.
 
-use rustc_errors::struct_span_err;
+use rustc_errors::{struct_span_err, Applicability};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_session::config::nightly_options;
@@ -10,39 +10,63 @@ use rustc_span::{Span, Symbol};
 
 use super::ConstCx;
 
-/// Emits an error if `op` is not allowed in the given const context.
-pub fn non_const<O: NonConstOp>(ccx: &ConstCx<'_, '_>, op: O, span: Span) {
+/// Emits an error and returns `true` if `op` is not allowed in the given const context.
+pub fn non_const<O: NonConstOp>(ccx: &ConstCx<'_, '_>, op: O, span: Span) -> bool {
     debug!("illegal_op: op={:?}", op);
 
-    if op.is_allowed_in_item(ccx) {
-        return;
-    }
+    let gate = match op.status_in_item(ccx) {
+        Status::Allowed => return false,
+
+        Status::Unstable(gate) if ccx.tcx.features().enabled(gate) => {
+            let unstable_in_stable = ccx.is_const_stable_const_fn()
+                && !super::allow_internal_unstable(ccx.tcx, ccx.def_id.to_def_id(), gate);
+
+            if unstable_in_stable {
+                ccx.tcx.sess
+                    .struct_span_err(
+                        span,
+                        &format!("const-stable function cannot use `#[feature({})]`", gate.as_str()),
+                    )
+                    .span_suggestion(
+                        ccx.body.span,
+                        "if it is not part of the public API, make this function unstably const",
+                        concat!(r#"#[rustc_const_unstable(feature = "...", issue = "...")]"#, '\n').to_owned(),
+                        Applicability::HasPlaceholders,
+                    )
+                    .note("otherwise `#[allow_internal_unstable]` can be used to bypass stability checks")
+                    .emit();
+            }
+
+            return unstable_in_stable;
+        }
+
+        Status::Unstable(gate) => Some(gate),
+        Status::Forbidden => None,
+    };
 
     if ccx.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
-        ccx.tcx.sess.miri_unleashed_feature(span, O::feature_gate());
-        return;
+        ccx.tcx.sess.miri_unleashed_feature(span, gate);
+        return false;
     }
 
     op.emit_error(ccx, span);
+    true
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum Status {
+    Allowed,
+    Unstable(Symbol),
+    Forbidden,
 }
 
 /// An operation that is not *always* allowed in a const context.
 pub trait NonConstOp: std::fmt::Debug {
-    /// Returns the `Symbol` corresponding to the feature gate that would enable this operation,
-    /// or `None` if such a feature gate does not exist.
-    fn feature_gate() -> Option<Symbol> {
-        None
-    }
+    const STOPS_CONST_CHECKING: bool = false;
 
-    /// Returns `true` if this operation is allowed in the given item.
-    ///
-    /// This check should assume that we are not in a non-const `fn`, where all operations are
-    /// legal.
-    ///
-    /// By default, it returns `true` if and only if this operation has a corresponding feature
-    /// gate and that gate is enabled.
-    fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool {
-        Self::feature_gate().map_or(false, |gate| ccx.tcx.features().enabled(gate))
+    /// Returns an enum indicating whether this operation is allowed within the given item.
+    fn status_in_item(&self, _ccx: &ConstCx<'_, '_>) -> Status {
+        Status::Forbidden
     }
 
     fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
@@ -53,9 +77,13 @@ pub trait NonConstOp: std::fmt::Debug {
             "{} contains unimplemented expression type",
             ccx.const_kind()
         );
-        if let Some(feat) = Self::feature_gate() {
-            err.help(&format!("add `#![feature({})]` to the crate attributes to enable", feat));
+
+        if let Status::Unstable(gate) = self.status_in_item(ccx) {
+            if !ccx.tcx.features().enabled(gate) && nightly_options::is_nightly_build() {
+                err.help(&format!("add `#![feature({})]` to the crate attributes to enable", gate));
+            }
         }
+
         if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
             err.note(
                 "A function call isn't allowed in the const's initialization expression \
@@ -70,6 +98,34 @@ pub trait NonConstOp: std::fmt::Debug {
     }
 }
 
+#[derive(Debug)]
+pub struct Abort;
+impl NonConstOp for Abort {
+    const STOPS_CONST_CHECKING: bool = true;
+
+    fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
+        mcf_status_in_item(ccx)
+    }
+
+    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
+        mcf_emit_error(ccx, span, "abort is not stable in const fn")
+    }
+}
+
+#[derive(Debug)]
+pub struct NonPrimitiveOp;
+impl NonConstOp for NonPrimitiveOp {
+    const STOPS_CONST_CHECKING: bool = true;
+
+    fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
+        mcf_status_in_item(ccx)
+    }
+
+    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
+        mcf_emit_error(ccx, span, "only int, `bool` and `char` operations are stable in const fn")
+    }
+}
+
 /// A function call where the callee is a pointer.
 #[derive(Debug)]
 pub struct FnCallIndirect;
@@ -102,7 +158,8 @@ impl NonConstOp for FnCallNonConst {
 ///
 /// Contains the name of the feature that would allow the use of this function.
 #[derive(Debug)]
-pub struct FnCallUnstable(pub DefId, pub Symbol);
+pub struct FnCallUnstable(pub DefId, pub Option<Symbol>);
+
 impl NonConstOp for FnCallUnstable {
     fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
         let FnCallUnstable(def_id, feature) = *self;
@@ -111,14 +168,52 @@ impl NonConstOp for FnCallUnstable {
             span,
             &format!("`{}` is not yet stable as a const fn", ccx.tcx.def_path_str(def_id)),
         );
-        if nightly_options::is_nightly_build() {
-            err.help(&format!("add `#![feature({})]` to the crate attributes to enable", feature));
+
+        if ccx.is_const_stable_const_fn() {
+            err.help("Const-stable functions can only call other const-stable functions");
+        } else if nightly_options::is_nightly_build() {
+            if let Some(feature) = feature {
+                err.help(&format!(
+                    "add `#![feature({})]` to the crate attributes to enable",
+                    feature
+                ));
+            }
         }
         err.emit();
     }
 }
 
 #[derive(Debug)]
+pub struct FnPtrCast;
+impl NonConstOp for FnPtrCast {
+    const STOPS_CONST_CHECKING: bool = true;
+
+    fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
+        mcf_status_in_item(ccx)
+    }
+
+    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
+        mcf_emit_error(ccx, span, "function pointer casts are not allowed in const fn");
+    }
+}
+
+#[derive(Debug)]
+pub struct Generator;
+impl NonConstOp for Generator {
+    const STOPS_CONST_CHECKING: bool = true;
+
+    fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
+        // FIXME: This means generator-only MIR is only forbidden in const fn. This is for
+        // compatibility with the old code. Such MIR should be forbidden everywhere.
+        mcf_status_in_item(ccx)
+    }
+
+    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
+        mcf_emit_error(ccx, span, "const fn generators are unstable");
+    }
+}
+
+#[derive(Debug)]
 pub struct HeapAllocation;
 impl NonConstOp for HeapAllocation {
     fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
@@ -147,7 +242,9 @@ pub struct InlineAsm;
 impl NonConstOp for InlineAsm {}
 
 #[derive(Debug)]
-pub struct LiveDrop(pub Option<Span>);
+pub struct LiveDrop {
+    pub dropped_at: Option<Span>,
+}
 impl NonConstOp for LiveDrop {
     fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
         let mut diagnostic = struct_span_err!(
@@ -157,7 +254,7 @@ impl NonConstOp for LiveDrop {
             "destructors cannot be evaluated at compile-time"
         );
         diagnostic.span_label(span, format!("{}s cannot evaluate destructors", ccx.const_kind()));
-        if let Some(span) = self.0 {
+        if let Some(span) = self.dropped_at {
             diagnostic.span_label(span, "value is dropped here");
         }
         diagnostic.emit();
@@ -182,14 +279,13 @@ impl NonConstOp for CellBorrow {
 #[derive(Debug)]
 pub struct MutBorrow;
 impl NonConstOp for MutBorrow {
-    fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool {
-        // Forbid everywhere except in const fn
-        ccx.const_kind() == hir::ConstContext::ConstFn
-            && ccx.tcx.features().enabled(Self::feature_gate().unwrap())
-    }
-
-    fn feature_gate() -> Option<Symbol> {
-        Some(sym::const_mut_refs)
+    fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
+        // Forbid everywhere except in const fn with a feature gate
+        if ccx.const_kind() == hir::ConstContext::ConstFn {
+            Status::Unstable(sym::const_mut_refs)
+        } else {
+            Status::Forbidden
+        }
     }
 
     fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
@@ -201,15 +297,16 @@ impl NonConstOp for MutBorrow {
                 &format!("mutable references are not allowed in {}s", ccx.const_kind()),
             )
         } else {
-            struct_span_err!(
+            let mut err = struct_span_err!(
                 ccx.tcx.sess,
                 span,
                 E0764,
                 "mutable references are not allowed in {}s",
                 ccx.const_kind(),
-            )
+            );
+            err.span_label(span, format!("`&mut` is only allowed in `const fn`"));
+            err
         };
-        err.span_label(span, "`&mut` is only allowed in `const fn`".to_string());
         if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
             err.note(
                 "References in statics and constants may only refer \
@@ -226,11 +323,17 @@ impl NonConstOp for MutBorrow {
     }
 }
 
+// FIXME(ecstaticmorse): Unify this with `MutBorrow`. It has basically the same issues.
 #[derive(Debug)]
 pub struct MutAddressOf;
 impl NonConstOp for MutAddressOf {
-    fn feature_gate() -> Option<Symbol> {
-        Some(sym::const_mut_refs)
+    fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
+        // Forbid everywhere except in const fn with a feature gate
+        if ccx.const_kind() == hir::ConstContext::ConstFn {
+            Status::Unstable(sym::const_mut_refs)
+        } else {
+            Status::Forbidden
+        }
     }
 
     fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
@@ -247,16 +350,16 @@ impl NonConstOp for MutAddressOf {
 #[derive(Debug)]
 pub struct MutDeref;
 impl NonConstOp for MutDeref {
-    fn feature_gate() -> Option<Symbol> {
-        Some(sym::const_mut_refs)
+    fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
+        Status::Unstable(sym::const_mut_refs)
     }
 }
 
 #[derive(Debug)]
 pub struct Panic;
 impl NonConstOp for Panic {
-    fn feature_gate() -> Option<Symbol> {
-        Some(sym::const_panic)
+    fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
+        Status::Unstable(sym::const_panic)
     }
 
     fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
@@ -289,8 +392,8 @@ impl NonConstOp for RawPtrComparison {
 #[derive(Debug)]
 pub struct RawPtrDeref;
 impl NonConstOp for RawPtrDeref {
-    fn feature_gate() -> Option<Symbol> {
-        Some(sym::const_raw_ptr_deref)
+    fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
+        Status::Unstable(sym::const_raw_ptr_deref)
     }
 
     fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
@@ -307,8 +410,8 @@ impl NonConstOp for RawPtrDeref {
 #[derive(Debug)]
 pub struct RawPtrToIntCast;
 impl NonConstOp for RawPtrToIntCast {
-    fn feature_gate() -> Option<Symbol> {
-        Some(sym::const_raw_ptr_to_usize_cast)
+    fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
+        Status::Unstable(sym::const_raw_ptr_to_usize_cast)
     }
 
     fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
@@ -326,8 +429,12 @@ impl NonConstOp for RawPtrToIntCast {
 #[derive(Debug)]
 pub struct StaticAccess;
 impl NonConstOp for StaticAccess {
-    fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool {
-        matches!(ccx.const_kind(), hir::ConstContext::Static(_))
+    fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
+        if let hir::ConstContext::Static(_) = ccx.const_kind() {
+            Status::Allowed
+        } else {
+            Status::Forbidden
+        }
     }
 
     fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
@@ -369,16 +476,40 @@ impl NonConstOp for ThreadLocalAccess {
 }
 
 #[derive(Debug)]
+pub struct Transmute;
+impl NonConstOp for Transmute {
+    const STOPS_CONST_CHECKING: bool = true;
+
+    fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
+        if ccx.const_kind() != hir::ConstContext::ConstFn {
+            Status::Allowed
+        } else {
+            Status::Unstable(sym::const_fn_transmute)
+        }
+    }
+
+    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
+        feature_err(
+            &ccx.tcx.sess.parse_sess,
+            sym::const_fn_transmute,
+            span,
+            &format!("`transmute` is not allowed in {}s", ccx.const_kind()),
+        )
+        .note("`transmute` is only allowed in constants and statics for now")
+        .emit();
+    }
+}
+
+#[derive(Debug)]
 pub struct UnionAccess;
 impl NonConstOp for UnionAccess {
-    fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool {
+    fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
         // Union accesses are stable in all contexts except `const fn`.
-        ccx.const_kind() != hir::ConstContext::ConstFn
-            || ccx.tcx.features().enabled(Self::feature_gate().unwrap())
-    }
-
-    fn feature_gate() -> Option<Symbol> {
-        Some(sym::const_fn_union)
+        if ccx.const_kind() != hir::ConstContext::ConstFn {
+            Status::Allowed
+        } else {
+            Status::Unstable(sym::const_fn_union)
+        }
     }
 
     fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
@@ -391,3 +522,138 @@ impl NonConstOp for UnionAccess {
         .emit();
     }
 }
+
+/// See [#64992].
+///
+/// [#64992]: https://github.com/rust-lang/rust/issues/64992
+#[derive(Debug)]
+pub struct UnsizingCast;
+impl NonConstOp for UnsizingCast {
+    fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
+        mcf_status_in_item(ccx)
+    }
+
+    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
+        mcf_emit_error(
+            ccx,
+            span,
+            "unsizing casts to types besides slices are not allowed in const fn",
+        );
+    }
+}
+
+// Types that cannot appear in the signature or locals of a `const fn`.
+pub mod ty {
+    use super::*;
+
+    #[derive(Debug)]
+    pub struct MutRef;
+    impl NonConstOp for MutRef {
+        const STOPS_CONST_CHECKING: bool = true;
+
+        fn status_in_item(&self, _ccx: &ConstCx<'_, '_>) -> Status {
+            Status::Unstable(sym::const_mut_refs)
+        }
+
+        fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
+            feature_err(
+                &ccx.tcx.sess.parse_sess,
+                sym::const_mut_refs,
+                span,
+                &format!("mutable references are not allowed in {}s", ccx.const_kind()),
+            )
+            .emit()
+        }
+    }
+
+    #[derive(Debug)]
+    pub struct FnPtr;
+    impl NonConstOp for FnPtr {
+        const STOPS_CONST_CHECKING: bool = true;
+
+        fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
+            // FIXME: This attribute a hack to allow the specialization of the `futures` API. See
+            // #59739. We should have a proper feature gate for this.
+            if ccx.tcx.has_attr(ccx.def_id.to_def_id(), sym::rustc_allow_const_fn_ptr) {
+                Status::Allowed
+            } else {
+                mcf_status_in_item(ccx)
+            }
+        }
+
+        fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
+            mcf_emit_error(ccx, span, "function pointers in const fn are unstable");
+        }
+    }
+
+    #[derive(Debug)]
+    pub struct ImplTrait;
+    impl NonConstOp for ImplTrait {
+        const STOPS_CONST_CHECKING: bool = true;
+
+        fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
+            mcf_status_in_item(ccx)
+        }
+
+        fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
+            mcf_emit_error(ccx, span, "`impl Trait` in const fn is unstable");
+        }
+    }
+
+    #[derive(Debug)]
+    pub struct TraitBound;
+    impl NonConstOp for TraitBound {
+        const STOPS_CONST_CHECKING: bool = true;
+
+        fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
+            mcf_status_in_item(ccx)
+        }
+
+        fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
+            mcf_emit_error(
+                ccx,
+                span,
+                "trait bounds other than `Sized` on const fn parameters are unstable",
+            );
+        }
+    }
+
+    /// A trait bound with the `?const Trait` opt-out
+    #[derive(Debug)]
+    pub struct TraitBoundNotConst;
+    impl NonConstOp for TraitBoundNotConst {
+        const STOPS_CONST_CHECKING: bool = true;
+
+        fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
+            Status::Unstable(sym::const_trait_bound_opt_out)
+        }
+
+        fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
+            feature_err(
+                &ccx.tcx.sess.parse_sess,
+                sym::const_trait_bound_opt_out,
+                span,
+                "`?const Trait` syntax is unstable",
+            )
+            .emit()
+        }
+    }
+}
+
+fn mcf_status_in_item(ccx: &ConstCx<'_, '_>) -> Status {
+    if ccx.const_kind() != hir::ConstContext::ConstFn {
+        Status::Allowed
+    } else {
+        Status::Unstable(sym::const_fn)
+    }
+}
+
+fn mcf_emit_error(ccx: &ConstCx<'_, '_>, span: Span, msg: &str) {
+    struct_span_err!(ccx.tcx.sess, span, E0723, "{}", msg)
+        .note(
+            "see issue #57563 <https://github.com/rust-lang/rust/issues/57563> \
+             for more information",
+        )
+        .help("add `#![feature(const_fn)]` to the crate attributes to enable")
+        .emit();
+}
diff --git a/compiler/rustc_mir/src/transform/check_consts/post_drop_elaboration.rs b/compiler/rustc_mir/src/transform/check_consts/post_drop_elaboration.rs
index 55075b3ab5e..0c171bbc464 100644
--- a/compiler/rustc_mir/src/transform/check_consts/post_drop_elaboration.rs
+++ b/compiler/rustc_mir/src/transform/check_consts/post_drop_elaboration.rs
@@ -11,8 +11,13 @@ use super::ConstCx;
 
 /// Returns `true` if we should use the more precise live drop checker that runs after drop
 /// elaboration.
-pub fn checking_enabled(tcx: TyCtxt<'tcx>) -> bool {
-    tcx.features().const_precise_live_drops
+pub fn checking_enabled(ccx: &ConstCx<'_, '_>) -> bool {
+    // Const-stable functions must always use the stable live drop checker.
+    if ccx.is_const_stable_const_fn() {
+        return false;
+    }
+
+    ccx.tcx.features().const_precise_live_drops
 }
 
 /// Look for live drops in a const context.
@@ -25,12 +30,11 @@ pub fn check_live_drops(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &mir::Body<
         return;
     }
 
-    if !checking_enabled(tcx) {
+    let ccx = ConstCx { body, tcx, def_id, const_kind, param_env: tcx.param_env(def_id) };
+    if !checking_enabled(&ccx) {
         return;
     }
 
-    let ccx = ConstCx { body, tcx, def_id, const_kind, param_env: tcx.param_env(def_id) };
-
     let mut visitor = CheckLiveDrops { ccx: &ccx, qualifs: Qualifs::default() };
 
     visitor.visit_body(body);
@@ -52,7 +56,7 @@ impl std::ops::Deref for CheckLiveDrops<'mir, 'tcx> {
 
 impl CheckLiveDrops<'mir, 'tcx> {
     fn check_live_drop(&self, span: Span) {
-        ops::non_const(self.ccx, ops::LiveDrop(None), span);
+        ops::non_const(self.ccx, ops::LiveDrop { dropped_at: None }, span);
     }
 }
 
diff --git a/compiler/rustc_mir/src/transform/check_consts/qualifs.rs b/compiler/rustc_mir/src/transform/check_consts/qualifs.rs
index 445a0230afd..3f4b3ca2eed 100644
--- a/compiler/rustc_mir/src/transform/check_consts/qualifs.rs
+++ b/compiler/rustc_mir/src/transform/check_consts/qualifs.rs
@@ -170,7 +170,7 @@ where
             // Special-case reborrows to be more like a copy of the reference.
             if let &[ref proj_base @ .., ProjectionElem::Deref] = place.projection.as_ref() {
                 let base_ty = Place::ty_from(place.local, proj_base, cx.body, cx.tcx).ty;
-                if let ty::Ref(..) = base_ty.kind {
+                if let ty::Ref(..) = base_ty.kind() {
                     return in_place::<Q, _>(
                         cx,
                         in_local,
diff --git a/compiler/rustc_mir/src/transform/check_consts/resolver.rs b/compiler/rustc_mir/src/transform/check_consts/resolver.rs
index b8104292aab..a00301952b3 100644
--- a/compiler/rustc_mir/src/transform/check_consts/resolver.rs
+++ b/compiler/rustc_mir/src/transform/check_consts/resolver.rs
@@ -165,23 +165,19 @@ where
     }
 }
 
-impl<Q> dataflow::BottomValue for FlowSensitiveAnalysis<'_, '_, '_, Q> {
-    const BOTTOM_VALUE: bool = false;
-}
-
 impl<Q> dataflow::AnalysisDomain<'tcx> for FlowSensitiveAnalysis<'_, '_, 'tcx, Q>
 where
     Q: Qualif,
 {
-    type Idx = Local;
+    type Domain = BitSet<Local>;
 
     const NAME: &'static str = Q::ANALYSIS_NAME;
 
-    fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize {
-        body.local_decls.len()
+    fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
+        BitSet::new_empty(body.local_decls.len())
     }
 
-    fn initialize_start_block(&self, _body: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>) {
+    fn initialize_start_block(&self, _body: &mir::Body<'tcx>, state: &mut Self::Domain) {
         self.transfer_function(state).initialize_state();
     }
 }
@@ -192,7 +188,7 @@ where
 {
     fn apply_statement_effect(
         &self,
-        state: &mut BitSet<Self::Idx>,
+        state: &mut Self::Domain,
         statement: &mir::Statement<'tcx>,
         location: Location,
     ) {
@@ -201,7 +197,7 @@ where
 
     fn apply_terminator_effect(
         &self,
-        state: &mut BitSet<Self::Idx>,
+        state: &mut Self::Domain,
         terminator: &mir::Terminator<'tcx>,
         location: Location,
     ) {
@@ -210,7 +206,7 @@ where
 
     fn apply_call_return_effect(
         &self,
-        state: &mut BitSet<Self::Idx>,
+        state: &mut Self::Domain,
         block: BasicBlock,
         func: &mir::Operand<'tcx>,
         args: &[mir::Operand<'tcx>],
diff --git a/compiler/rustc_mir/src/transform/check_consts/validation.rs b/compiler/rustc_mir/src/transform/check_consts/validation.rs
index e21f314ca15..dc28ba46d7c 100644
--- a/compiler/rustc_mir/src/transform/check_consts/validation.rs
+++ b/compiler/rustc_mir/src/transform/check_consts/validation.rs
@@ -7,19 +7,21 @@ use rustc_infer::infer::TyCtxtInferExt;
 use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
 use rustc_middle::mir::*;
 use rustc_middle::ty::cast::CastTy;
-use rustc_middle::ty::{self, Instance, InstanceDef, TyCtxt};
-use rustc_span::Span;
+use rustc_middle::ty::subst::GenericArgKind;
+use rustc_middle::ty::{
+    self, adjustment::PointerCast, Instance, InstanceDef, Ty, TyCtxt, TypeAndMut,
+};
+use rustc_span::{sym, Span};
 use rustc_trait_selection::traits::error_reporting::InferCtxtExt;
 use rustc_trait_selection::traits::{self, TraitEngine};
 
-use std::borrow::Cow;
 use std::ops::Deref;
 
 use super::ops::{self, NonConstOp};
 use super::qualifs::{self, CustomEq, HasMutInterior, NeedsDrop};
 use super::resolver::FlowSensitiveAnalysis;
 use super::{is_lang_panic_fn, ConstCx, Qualif};
-use crate::const_eval::{is_const_fn, is_unstable_const_fn};
+use crate::const_eval::is_unstable_const_fn;
 use crate::dataflow::impls::MaybeMutBorrowedLocals;
 use crate::dataflow::{self, Analysis};
 
@@ -57,6 +59,7 @@ impl Qualifs<'mir, 'tcx> {
             MaybeMutBorrowedLocals::mut_borrows_only(tcx, &body, param_env)
                 .unsound_ignore_borrow_on_drop()
                 .into_engine(tcx, &body, def_id.to_def_id())
+                .pass_name("const_qualification")
                 .iterate_to_fixpoint()
                 .into_results_cursor(&body)
         });
@@ -176,6 +179,8 @@ pub struct Validator<'mir, 'tcx> {
 
     /// The span of the current statement.
     span: Span,
+
+    const_checking_stopped: bool,
 }
 
 impl Deref for Validator<'mir, 'tcx> {
@@ -188,30 +193,60 @@ impl Deref for Validator<'mir, 'tcx> {
 
 impl Validator<'mir, 'tcx> {
     pub fn new(ccx: &'mir ConstCx<'mir, 'tcx>) -> Self {
-        Validator { span: ccx.body.span, ccx, qualifs: Default::default() }
+        Validator {
+            span: ccx.body.span,
+            ccx,
+            qualifs: Default::default(),
+            const_checking_stopped: false,
+        }
     }
 
     pub fn check_body(&mut self) {
-        let ConstCx { tcx, body, def_id, const_kind, .. } = *self.ccx;
-
-        let use_min_const_fn_checks = (const_kind == Some(hir::ConstContext::ConstFn)
-            && crate::const_eval::is_min_const_fn(tcx, def_id.to_def_id()))
-            && !tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you;
-
-        if use_min_const_fn_checks {
-            // Enforce `min_const_fn` for stable `const fn`s.
-            use crate::transform::qualify_min_const_fn::is_min_const_fn;
-            if let Err((span, err)) = is_min_const_fn(tcx, def_id.to_def_id(), &body) {
-                error_min_const_fn_violation(tcx, span, err);
-                return;
+        let ConstCx { tcx, body, def_id, .. } = *self.ccx;
+
+        // HACK: This function has side-effects???? Make sure we call it.
+        let _ = crate::const_eval::is_min_const_fn(tcx, def_id.to_def_id());
+
+        // The local type and predicate checks are not free and only relevant for `const fn`s.
+        if self.const_kind() == hir::ConstContext::ConstFn {
+            // Prevent const trait methods from being annotated as `stable`.
+            // FIXME: Do this as part of stability checking.
+            if self.is_const_stable_const_fn() {
+                let hir_id = tcx.hir().local_def_id_to_hir_id(self.def_id);
+                if crate::const_eval::is_parent_const_impl_raw(tcx, hir_id) {
+                    struct_span_err!(
+                        self.ccx.tcx.sess,
+                        self.span,
+                        E0723,
+                        "trait methods cannot be stable const fn"
+                    )
+                    .emit();
+                }
             }
+
+            self.check_item_predicates();
+
+            for local in &body.local_decls {
+                if local.internal {
+                    continue;
+                }
+
+                self.span = local.source_info.span;
+                self.check_local_or_return_ty(local.ty);
+            }
+
+            // impl trait is gone in MIR, so check the return type of a const fn by its signature
+            // instead of the type of the return place.
+            self.span = body.local_decls[RETURN_PLACE].source_info.span;
+            let return_ty = tcx.fn_sig(def_id).output();
+            self.check_local_or_return_ty(return_ty.skip_binder());
         }
 
         self.visit_body(&body);
 
         // Ensure that the end result is `Sync` in a non-thread local `static`.
-        let should_check_for_sync = const_kind
-            == Some(hir::ConstContext::Static(hir::Mutability::Not))
+        let should_check_for_sync = self.const_kind()
+            == hir::ConstContext::Static(hir::Mutability::Not)
             && !tcx.is_thread_local_static(def_id.to_def_id());
 
         if should_check_for_sync {
@@ -226,13 +261,22 @@ impl Validator<'mir, 'tcx> {
 
     /// Emits an error if an expression cannot be evaluated in the current context.
     pub fn check_op(&mut self, op: impl NonConstOp) {
-        ops::non_const(self.ccx, op, self.span);
+        self.check_op_spanned(op, self.span);
     }
 
     /// Emits an error at the given `span` if an expression cannot be evaluated in the current
     /// context.
-    pub fn check_op_spanned(&mut self, op: impl NonConstOp, span: Span) {
-        ops::non_const(self.ccx, op, span);
+    pub fn check_op_spanned<O: NonConstOp>(&mut self, op: O, span: Span) {
+        // HACK: This is for strict equivalence with the old `qualify_min_const_fn` pass, which
+        // only emitted one error per function. It should be removed and the test output updated.
+        if self.const_checking_stopped {
+            return;
+        }
+
+        let err_emitted = ops::non_const(self.ccx, op, span);
+        if err_emitted && O::STOPS_CONST_CHECKING {
+            self.const_checking_stopped = true;
+        }
     }
 
     fn check_static(&mut self, def_id: DefId, span: Span) {
@@ -242,6 +286,100 @@ impl Validator<'mir, 'tcx> {
         );
         self.check_op_spanned(ops::StaticAccess, span)
     }
+
+    fn check_local_or_return_ty(&mut self, ty: Ty<'tcx>) {
+        for ty in ty.walk() {
+            let ty = match ty.unpack() {
+                GenericArgKind::Type(ty) => ty,
+
+                // No constraints on lifetimes or constants, except potentially
+                // constants' types, but `walk` will get to them as well.
+                GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => continue,
+            };
+
+            match *ty.kind() {
+                ty::Ref(_, _, hir::Mutability::Mut) => self.check_op(ops::ty::MutRef),
+                ty::Opaque(..) => self.check_op(ops::ty::ImplTrait),
+                ty::FnPtr(..) => self.check_op(ops::ty::FnPtr),
+
+                ty::Dynamic(preds, _) => {
+                    for pred in preds.iter() {
+                        match pred.skip_binder() {
+                            ty::ExistentialPredicate::AutoTrait(_)
+                            | ty::ExistentialPredicate::Projection(_) => {
+                                self.check_op(ops::ty::TraitBound)
+                            }
+                            ty::ExistentialPredicate::Trait(trait_ref) => {
+                                if Some(trait_ref.def_id) != self.tcx.lang_items().sized_trait() {
+                                    self.check_op(ops::ty::TraitBound)
+                                }
+                            }
+                        }
+                    }
+                }
+                _ => {}
+            }
+        }
+    }
+
+    fn check_item_predicates(&mut self) {
+        let ConstCx { tcx, def_id, .. } = *self.ccx;
+
+        let mut current = def_id.to_def_id();
+        loop {
+            let predicates = tcx.predicates_of(current);
+            for (predicate, _) in predicates.predicates {
+                match predicate.skip_binders() {
+                    ty::PredicateAtom::RegionOutlives(_)
+                    | ty::PredicateAtom::TypeOutlives(_)
+                    | ty::PredicateAtom::WellFormed(_)
+                    | ty::PredicateAtom::Projection(_)
+                    | ty::PredicateAtom::ConstEvaluatable(..)
+                    | ty::PredicateAtom::ConstEquate(..)
+                    | ty::PredicateAtom::TypeWellFormedFromEnv(..) => continue,
+                    ty::PredicateAtom::ObjectSafe(_) => {
+                        bug!("object safe predicate on function: {:#?}", predicate)
+                    }
+                    ty::PredicateAtom::ClosureKind(..) => {
+                        bug!("closure kind predicate on function: {:#?}", predicate)
+                    }
+                    ty::PredicateAtom::Subtype(_) => {
+                        bug!("subtype predicate on function: {:#?}", predicate)
+                    }
+                    ty::PredicateAtom::Trait(pred, constness) => {
+                        if Some(pred.def_id()) == tcx.lang_items().sized_trait() {
+                            continue;
+                        }
+                        match pred.self_ty().kind() {
+                            ty::Param(p) => {
+                                let generics = tcx.generics_of(current);
+                                let def = generics.type_param(p, tcx);
+                                let span = tcx.def_span(def.def_id);
+
+                                if constness == hir::Constness::Const {
+                                    self.check_op_spanned(ops::ty::TraitBound, span);
+                                } else if !tcx.features().const_fn
+                                    || self.ccx.is_const_stable_const_fn()
+                                {
+                                    // HACK: We shouldn't need the conditional above, but trait
+                                    // bounds on containing impl blocks are wrongly being marked as
+                                    // "not-const".
+                                    self.check_op_spanned(ops::ty::TraitBound, span);
+                                }
+                            }
+                            // other kinds of bounds are either tautologies
+                            // or cause errors in other passes
+                            _ => continue,
+                        }
+                    }
+                }
+            }
+            match predicates.parent {
+                Some(parent) => current = parent,
+                None => break,
+            }
+        }
+    }
 }
 
 impl Visitor<'tcx> for Validator<'mir, 'tcx> {
@@ -309,11 +447,6 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> {
 
             Rvalue::Use(_)
             | Rvalue::Repeat(..)
-            | Rvalue::UnaryOp(UnOp::Neg, _)
-            | Rvalue::UnaryOp(UnOp::Not, _)
-            | Rvalue::NullaryOp(NullOp::SizeOf, _)
-            | Rvalue::CheckedBinaryOp(..)
-            | Rvalue::Cast(CastKind::Pointer(_), ..)
             | Rvalue::Discriminant(..)
             | Rvalue::Len(_)
             | Rvalue::Aggregate(..) => {}
@@ -321,7 +454,7 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> {
             Rvalue::Ref(_, kind @ BorrowKind::Mut { .. }, ref place)
             | Rvalue::Ref(_, kind @ BorrowKind::Unique, ref place) => {
                 let ty = place.ty(self.body, self.tcx).ty;
-                let is_allowed = match ty.kind {
+                let is_allowed = match ty.kind() {
                     // Inside a `static mut`, `&mut [...]` is allowed.
                     ty::Array(..) | ty::Slice(_)
                         if self.const_kind() == hir::ConstContext::Static(hir::Mutability::Mut) =>
@@ -363,6 +496,35 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> {
                 }
             }
 
+            Rvalue::Cast(
+                CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer),
+                _,
+                _,
+            ) => {}
+
+            Rvalue::Cast(
+                CastKind::Pointer(
+                    PointerCast::UnsafeFnPointer
+                    | PointerCast::ClosureFnPointer(_)
+                    | PointerCast::ReifyFnPointer,
+                ),
+                _,
+                _,
+            ) => self.check_op(ops::FnPtrCast),
+
+            Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), _, cast_ty) => {
+                if let Some(TypeAndMut { ty, .. }) = cast_ty.builtin_deref(true) {
+                    let unsized_ty = self.tcx.struct_tail_erasing_lifetimes(ty, self.param_env);
+
+                    // Casting/coercing things to slices is fine.
+                    if let ty::Slice(_) | ty::Str = unsized_ty.kind() {
+                        return;
+                    }
+                }
+
+                self.check_op(ops::UnsizingCast);
+            }
+
             Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => {
                 let operand_ty = operand.ty(self.body, self.tcx);
                 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
@@ -373,8 +535,23 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> {
                 }
             }
 
-            Rvalue::BinaryOp(op, ref lhs, _) => {
-                if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).kind {
+            Rvalue::NullaryOp(NullOp::SizeOf, _) => {}
+            Rvalue::NullaryOp(NullOp::Box, _) => self.check_op(ops::HeapAllocation),
+
+            Rvalue::UnaryOp(_, ref operand) => {
+                let ty = operand.ty(self.body, self.tcx);
+                if !(ty.is_integral() || ty.is_bool()) {
+                    self.check_op(ops::NonPrimitiveOp)
+                }
+            }
+
+            Rvalue::BinaryOp(op, ref lhs, ref rhs)
+            | Rvalue::CheckedBinaryOp(op, ref lhs, ref rhs) => {
+                let lhs_ty = lhs.ty(self.body, self.tcx);
+                let rhs_ty = rhs.ty(self.body, self.tcx);
+
+                if let ty::RawPtr(_) | ty::FnPtr(..) = lhs_ty.kind() {
+                    assert_eq!(lhs_ty, rhs_ty);
                     assert!(
                         op == BinOp::Eq
                             || op == BinOp::Ne
@@ -387,10 +564,12 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> {
 
                     self.check_op(ops::RawPtrComparison);
                 }
-            }
 
-            Rvalue::NullaryOp(NullOp::Box, _) => {
-                self.check_op(ops::HeapAllocation);
+                if !(lhs_ty.is_integral() || lhs_ty.is_bool() || lhs_ty.is_char())
+                    || !(rhs_ty.is_integral() || rhs_ty.is_bool() || rhs_ty.is_char())
+                {
+                    self.check_op(ops::NonPrimitiveOp)
+                }
             }
         }
     }
@@ -426,7 +605,7 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> {
         match elem {
             ProjectionElem::Deref => {
                 let base_ty = Place::ty_from(place_local, proj_base, self.body, self.tcx).ty;
-                if let ty::RawPtr(_) = base_ty.kind {
+                if let ty::RawPtr(_) = base_ty.kind() {
                     if proj_base.is_empty() {
                         if let (local, []) = (place_local, proj_base) {
                             let decl = &self.body.local_decls[local];
@@ -491,14 +670,19 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> {
     }
 
     fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
+        use rustc_target::spec::abi::Abi::RustIntrinsic;
+
         trace!("visit_terminator: terminator={:?} location={:?}", terminator, location);
         self.super_terminator(terminator, location);
 
         match &terminator.kind {
             TerminatorKind::Call { func, .. } => {
-                let fn_ty = func.ty(self.body, self.tcx);
+                let ConstCx { tcx, body, def_id: caller, param_env, .. } = *self.ccx;
+                let caller = caller.to_def_id();
+
+                let fn_ty = func.ty(body, tcx);
 
-                let (def_id, substs) = match fn_ty.kind {
+                let (mut callee, substs) = match *fn_ty.kind() {
                     ty::FnDef(def_id, substs) => (def_id, substs),
 
                     ty::FnPtr(_) => {
@@ -510,38 +694,80 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> {
                     }
                 };
 
-                // At this point, we are calling a function whose `DefId` is known...
-                if is_const_fn(self.tcx, def_id) {
-                    return;
-                }
-
-                // See if this is a trait method for a concrete type whose impl of that trait is
-                // `const`.
+                // Resolve a trait method call to its concrete implementation, which may be in a
+                // `const` trait impl.
                 if self.tcx.features().const_trait_impl {
-                    let instance = Instance::resolve(self.tcx, self.param_env, def_id, substs);
-                    debug!("Resolving ({:?}) -> {:?}", def_id, instance);
+                    let instance = Instance::resolve(tcx, param_env, callee, substs);
+                    debug!("Resolving ({:?}) -> {:?}", callee, instance);
                     if let Ok(Some(func)) = instance {
                         if let InstanceDef::Item(def) = func.def {
-                            if is_const_fn(self.tcx, def.did) {
-                                return;
-                            }
+                            callee = def.did;
                         }
                     }
                 }
 
-                if is_lang_panic_fn(self.tcx, def_id) {
+                // At this point, we are calling a function, `callee`, whose `DefId` is known...
+
+                if is_lang_panic_fn(tcx, callee) {
                     self.check_op(ops::Panic);
-                } else if let Some(feature) = is_unstable_const_fn(self.tcx, def_id) {
-                    // Exempt unstable const fns inside of macros or functions with
-                    // `#[allow_internal_unstable]`.
-                    use crate::transform::qualify_min_const_fn::lib_feature_allowed;
-                    if !self.span.allows_unstable(feature)
-                        && !lib_feature_allowed(self.tcx, self.def_id.to_def_id(), feature)
-                    {
-                        self.check_op(ops::FnCallUnstable(def_id, feature));
+                    return;
+                }
+
+                // HACK: This is to "unstabilize" the `transmute` intrinsic
+                // within const fns. `transmute` is allowed in all other const contexts.
+                // This won't really scale to more intrinsics or functions. Let's allow const
+                // transmutes in const fn before we add more hacks to this.
+                if tcx.fn_sig(callee).abi() == RustIntrinsic
+                    && tcx.item_name(callee) == sym::transmute
+                {
+                    self.check_op(ops::Transmute);
+                    return;
+                }
+
+                if !tcx.is_const_fn_raw(callee) {
+                    self.check_op(ops::FnCallNonConst(callee));
+                    return;
+                }
+
+                // If the `const fn` we are trying to call is not const-stable, ensure that we have
+                // the proper feature gate enabled.
+                if let Some(gate) = is_unstable_const_fn(tcx, callee) {
+                    if self.span.allows_unstable(gate) {
+                        return;
+                    }
+
+                    // Calling an unstable function *always* requires that the corresponding gate
+                    // be enabled, even if the function has `#[allow_internal_unstable(the_gate)]`.
+                    if !tcx.features().declared_lib_features.iter().any(|&(sym, _)| sym == gate) {
+                        self.check_op(ops::FnCallUnstable(callee, Some(gate)));
+                        return;
+                    }
+
+                    // If this crate is not using stability attributes, or the caller is not claiming to be a
+                    // stable `const fn`, that is all that is required.
+                    if !self.ccx.is_const_stable_const_fn() {
+                        return;
+                    }
+
+                    // Otherwise, we are something const-stable calling a const-unstable fn.
+
+                    if super::allow_internal_unstable(tcx, caller, gate) {
+                        return;
+                    }
+
+                    self.check_op(ops::FnCallUnstable(callee, Some(gate)));
+                    return;
+                }
+
+                // FIXME(ecstaticmorse); For compatibility, we consider `unstable` callees that
+                // have no `rustc_const_stable` attributes to be const-unstable as well. This
+                // should be fixed later.
+                let callee_is_unstable_unmarked = tcx.lookup_const_stability(callee).is_none()
+                    && tcx.lookup_stability(callee).map_or(false, |s| s.level.is_unstable());
+                if callee_is_unstable_unmarked {
+                    if self.ccx.is_const_stable_const_fn() {
+                        self.check_op(ops::FnCallUnstable(callee, None));
                     }
-                } else {
-                    self.check_op(ops::FnCallNonConst(def_id));
                 }
             }
 
@@ -551,7 +777,7 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> {
             | TerminatorKind::DropAndReplace { place: dropped_place, .. } => {
                 // If we are checking live drops after drop-elaboration, don't emit duplicate
                 // errors here.
-                if super::post_drop_elaboration::checking_enabled(self.tcx) {
+                if super::post_drop_elaboration::checking_enabled(self.ccx) {
                     return;
                 }
 
@@ -576,43 +802,31 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> {
 
                 if needs_drop {
                     self.check_op_spanned(
-                        ops::LiveDrop(Some(terminator.source_info.span)),
+                        ops::LiveDrop { dropped_at: Some(terminator.source_info.span) },
                         err_span,
                     );
                 }
             }
 
-            TerminatorKind::InlineAsm { .. } => {
-                self.check_op(ops::InlineAsm);
+            TerminatorKind::InlineAsm { .. } => self.check_op(ops::InlineAsm),
+            TerminatorKind::Abort => self.check_op(ops::Abort),
+
+            TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
+                self.check_op(ops::Generator)
             }
 
-            // FIXME: Some of these are only caught by `min_const_fn`, but should error here
-            // instead.
-            TerminatorKind::Abort
-            | TerminatorKind::Assert { .. }
+            TerminatorKind::Assert { .. }
             | TerminatorKind::FalseEdge { .. }
             | TerminatorKind::FalseUnwind { .. }
-            | TerminatorKind::GeneratorDrop
             | TerminatorKind::Goto { .. }
             | TerminatorKind::Resume
             | TerminatorKind::Return
             | TerminatorKind::SwitchInt { .. }
-            | TerminatorKind::Unreachable
-            | TerminatorKind::Yield { .. } => {}
+            | TerminatorKind::Unreachable => {}
         }
     }
 }
 
-fn error_min_const_fn_violation(tcx: TyCtxt<'_>, span: Span, msg: Cow<'_, str>) {
-    struct_span_err!(tcx.sess, span, E0723, "{}", msg)
-        .note(
-            "see issue #57563 <https://github.com/rust-lang/rust/issues/57563> \
-             for more information",
-        )
-        .help("add `#![feature(const_fn)]` to the crate attributes to enable")
-        .emit();
-}
-
 fn check_return_ty_is_sync(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, hir_id: HirId) {
     let ty = body.return_ty();
     tcx.infer_ctxt().enter(|infcx| {
@@ -647,7 +861,7 @@ fn place_as_reborrow(
         // This is sufficient to prevent an access to a `static mut` from being marked as a
         // reborrow, even if the check above were to disappear.
         let inner_ty = Place::ty_from(place.local, inner, body, tcx).ty;
-        match inner_ty.kind {
+        match inner_ty.kind() {
             ty::Ref(..) => Some(inner),
             _ => None,
         }
diff --git a/compiler/rustc_mir/src/transform/check_unsafety.rs b/compiler/rustc_mir/src/transform/check_unsafety.rs
index c3e04e698db..7309a4129e4 100644
--- a/compiler/rustc_mir/src/transform/check_unsafety.rs
+++ b/compiler/rustc_mir/src/transform/check_unsafety.rs
@@ -91,8 +91,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
                     )
                 }
 
-                if let ty::FnDef(func_id, _) = func_ty.kind {
-                    self.check_target_features(func_id);
+                if let ty::FnDef(func_id, _) = func_ty.kind() {
+                    self.check_target_features(*func_id);
                 }
             }
 
@@ -227,7 +227,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
                 }
             }
             let base_ty = Place::ty_from(place.local, proj_base, self.body, self.tcx).ty;
-            match base_ty.kind {
+            match base_ty.kind() {
                 ty::RawPtr(..) => self.require_unsafe(
                     UnsafetyViolationKind::GeneralAndConstFn,
                     UnsafetyViolationDetails::DerefOfRawPointer,
@@ -394,7 +394,7 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
                 ProjectionElem::Field(..) => {
                     let ty =
                         Place::ty_from(place.local, proj_base, &self.body.local_decls, self.tcx).ty;
-                    if let ty::Adt(def, _) = ty.kind {
+                    if let ty::Adt(def, _) = ty.kind() {
                         if self.tcx.layout_scalar_valid_range(def.did)
                             != (Bound::Unbounded, Bound::Unbounded)
                         {
diff --git a/compiler/rustc_mir/src/transform/const_prop.rs b/compiler/rustc_mir/src/transform/const_prop.rs
index 56479b047fa..aa88719c26a 100644
--- a/compiler/rustc_mir/src/transform/const_prop.rs
+++ b/compiler/rustc_mir/src/transform/const_prop.rs
@@ -832,7 +832,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
                     // FIXME: enable the general case stated above ^.
                     let ty = &value.layout.ty;
                     // Only do it for tuples
-                    if let ty::Tuple(substs) = ty.kind {
+                    if let ty::Tuple(substs) = ty.kind() {
                         // Only do it if tuple is also a pair with two scalars
                         if substs.len() == 2 {
                             let alloc = self.use_ecx(|this| {
diff --git a/compiler/rustc_mir/src/transform/dest_prop.rs b/compiler/rustc_mir/src/transform/dest_prop.rs
new file mode 100644
index 00000000000..46cbced2d54
--- /dev/null
+++ b/compiler/rustc_mir/src/transform/dest_prop.rs
@@ -0,0 +1,1057 @@
+//! Propagates assignment destinations backwards in the CFG to eliminate redundant assignments.
+//!
+//! # Motivation
+//!
+//! MIR building can insert a lot of redundant copies, and Rust code in general often tends to move
+//! values around a lot. The result is a lot of assignments of the form `dest = {move} src;` in MIR.
+//! MIR building for constants in particular tends to create additional locals that are only used
+//! inside a single block to shuffle a value around unnecessarily.
+//!
+//! LLVM by itself is not good enough at eliminating these redundant copies (eg. see
+//! https://github.com/rust-lang/rust/issues/32966), so this leaves some performance on the table
+//! that we can regain by implementing an optimization for removing these assign statements in rustc
+//! itself. When this optimization runs fast enough, it can also speed up the constant evaluation
+//! and code generation phases of rustc due to the reduced number of statements and locals.
+//!
+//! # The Optimization
+//!
+//! Conceptually, this optimization is "destination propagation". It is similar to the Named Return
+//! Value Optimization, or NRVO, known from the C++ world, except that it isn't limited to return
+//! values or the return place `_0`. On a very high level, independent of the actual implementation
+//! details, it does the following:
+//!
+//! 1) Identify `dest = src;` statements that can be soundly eliminated.
+//! 2) Replace all mentions of `src` with `dest` ("unifying" them and propagating the destination
+//!    backwards).
+//! 3) Delete the `dest = src;` statement (by making it a `nop`).
+//!
+//! Step 1) is by far the hardest, so it is explained in more detail below.
+//!
+//! ## Soundness
+//!
+//! Given an `Assign` statement `dest = src;`, where `dest` is a `Place` and `src` is an `Rvalue`,
+//! there are a few requirements that must hold for the optimization to be sound:
+//!
+//! * `dest` must not contain any *indirection* through a pointer. It must access part of the base
+//!   local. Otherwise it might point to arbitrary memory that is hard to track.
+//!
+//!   It must also not contain any indexing projections, since those take an arbitrary `Local` as
+//!   the index, and that local might only be initialized shortly before `dest` is used.
+//!
+//!   Subtle case: If `dest` is a, or projects through a union, then we have to make sure that there
+//!   remains an assignment to it, since that sets the "active field" of the union. But if `src` is
+//!   a ZST, it might not be initialized, so there might not be any use of it before the assignment,
+//!   and performing the optimization would simply delete the assignment, leaving `dest`
+//!   uninitialized.
+//!
+//! * `src` must be a bare `Local` without any indirections or field projections (FIXME: Is this a
+//!   fundamental restriction or just current impl state?). It can be copied or moved by the
+//!   assignment.
+//!
+//! * The `dest` and `src` locals must never be [*live*][liveness] at the same time. If they are, it
+//!   means that they both hold a (potentially different) value that is needed by a future use of
+//!   the locals. Unifying them would overwrite one of the values.
+//!
+//!   Note that computing liveness of locals that have had their address taken is more difficult:
+//!   Short of doing full escape analysis on the address/pointer/reference, the pass would need to
+//!   assume that any operation that can potentially involve opaque user code (such as function
+//!   calls, destructors, and inline assembly) may access any local that had its address taken
+//!   before that point.
+//!
+//! Here, the first two conditions are simple structural requirements on the `Assign` statements
+//! that can be trivially checked. The liveness requirement however is more difficult and costly to
+//! check.
+//!
+//! ## Previous Work
+//!
+//! A [previous attempt] at implementing an optimization like this turned out to be a significant
+//! regression in compiler performance. Fixing the regressions introduced a lot of undesirable
+//! complexity to the implementation.
+//!
+//! A [subsequent approach] tried to avoid the costly computation by limiting itself to acyclic
+//! CFGs, but still turned out to be far too costly to run due to suboptimal performance within
+//! individual basic blocks, requiring a walk across the entire block for every assignment found
+//! within the block. For the `tuple-stress` benchmark, which has 458745 statements in a single
+//! block, this proved to be far too costly.
+//!
+//! Since the first attempt at this, the compiler has improved dramatically, and new analysis
+//! frameworks have been added that should make this approach viable without requiring a limited
+//! approach that only works for some classes of CFGs:
+//! - rustc now has a powerful dataflow analysis framework that can handle forwards and backwards
+//!   analyses efficiently.
+//! - Layout optimizations for generators have been added to improve code generation for
+//!   async/await, which are very similar in spirit to what this optimization does. Both walk the
+//!   MIR and record conflicting uses of locals in a `BitMatrix`.
+//!
+//! Also, rustc now has a simple NRVO pass (see `nrvo.rs`), which handles a subset of the cases that
+//! this destination propagation pass handles, proving that similar optimizations can be performed
+//! on MIR.
+//!
+//! ## Pre/Post Optimization
+//!
+//! It is recommended to run `SimplifyCfg` and then `SimplifyLocals` some time after this pass, as
+//! it replaces the eliminated assign statements with `nop`s and leaves unused locals behind.
+//!
+//! [liveness]: https://en.wikipedia.org/wiki/Live_variable_analysis
+//! [previous attempt]: https://github.com/rust-lang/rust/pull/47954
+//! [subsequent approach]: https://github.com/rust-lang/rust/pull/71003
+
+use crate::dataflow::impls::{MaybeInitializedLocals, MaybeLiveLocals};
+use crate::dataflow::Analysis;
+use crate::{
+    transform::{MirPass, MirSource},
+    util::{dump_mir, PassWhere},
+};
+use itertools::Itertools;
+use rustc_data_structures::unify::{InPlaceUnificationTable, UnifyKey};
+use rustc_index::{
+    bit_set::{BitMatrix, BitSet},
+    vec::IndexVec,
+};
+use rustc_middle::mir::tcx::PlaceTy;
+use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor};
+use rustc_middle::mir::{
+    traversal, Body, InlineAsmOperand, Local, LocalKind, Location, Operand, Place, PlaceElem,
+    Rvalue, Statement, StatementKind, Terminator, TerminatorKind,
+};
+use rustc_middle::ty::{self, Ty, TyCtxt};
+
+// Empirical measurements have resulted in some observations:
+// - Running on a body with a single block and 500 locals takes barely any time
+// - Running on a body with ~400 blocks and ~300 relevant locals takes "too long"
+// ...so we just limit both to somewhat reasonable-ish looking values.
+const MAX_LOCALS: usize = 500;
+const MAX_BLOCKS: usize = 250;
+
+pub struct DestinationPropagation;
+
+impl<'tcx> MirPass<'tcx> for DestinationPropagation {
+    fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
+        // Only run at mir-opt-level=2 or higher for now (we don't fix up debuginfo and remove
+        // storage statements at the moment).
+        if tcx.sess.opts.debugging_opts.mir_opt_level <= 1 {
+            return;
+        }
+
+        let candidates = find_candidates(tcx, body);
+        if candidates.is_empty() {
+            debug!("{:?}: no dest prop candidates, done", source.def_id());
+            return;
+        }
+
+        // Collect all locals we care about. We only compute conflicts for these to save time.
+        let mut relevant_locals = BitSet::new_empty(body.local_decls.len());
+        for CandidateAssignment { dest, src, loc: _ } in &candidates {
+            relevant_locals.insert(dest.local);
+            relevant_locals.insert(*src);
+        }
+
+        // This pass unfortunately has `O(l² * s)` performance, where `l` is the number of locals
+        // and `s` is the number of statements and terminators in the function.
+        // To prevent blowing up compile times too much, we bail out when there are too many locals.
+        let relevant = relevant_locals.count();
+        debug!(
+            "{:?}: {} locals ({} relevant), {} blocks",
+            source.def_id(),
+            body.local_decls.len(),
+            relevant,
+            body.basic_blocks().len()
+        );
+        if relevant > MAX_LOCALS {
+            warn!(
+                "too many candidate locals in {:?} ({}, max is {}), not optimizing",
+                source.def_id(),
+                relevant,
+                MAX_LOCALS
+            );
+            return;
+        }
+        if body.basic_blocks().len() > MAX_BLOCKS {
+            warn!(
+                "too many blocks in {:?} ({}, max is {}), not optimizing",
+                source.def_id(),
+                body.basic_blocks().len(),
+                MAX_BLOCKS
+            );
+            return;
+        }
+
+        let mut conflicts = Conflicts::build(tcx, body, source, &relevant_locals);
+
+        let mut replacements = Replacements::new(body.local_decls.len());
+        for candidate @ CandidateAssignment { dest, src, loc } in candidates {
+            // Merge locals that don't conflict.
+            if !conflicts.can_unify(dest.local, src) {
+                debug!("at assignment {:?}, conflict {:?} vs. {:?}", loc, dest.local, src);
+                continue;
+            }
+
+            if replacements.for_src(candidate.src).is_some() {
+                debug!("src {:?} already has replacement", candidate.src);
+                continue;
+            }
+
+            if !tcx.consider_optimizing(|| {
+                format!("DestinationPropagation {:?} {:?}", source.def_id(), candidate)
+            }) {
+                break;
+            }
+
+            replacements.push(candidate);
+            conflicts.unify(candidate.src, candidate.dest.local);
+        }
+
+        replacements.flatten(tcx);
+
+        debug!("replacements {:?}", replacements.map);
+
+        Replacer { tcx, replacements, place_elem_cache: Vec::new() }.visit_body(body);
+
+        // FIXME fix debug info
+    }
+}
+
+#[derive(Debug, Eq, PartialEq, Copy, Clone)]
+struct UnifyLocal(Local);
+
+impl From<Local> for UnifyLocal {
+    fn from(l: Local) -> Self {
+        Self(l)
+    }
+}
+
+impl UnifyKey for UnifyLocal {
+    type Value = ();
+    fn index(&self) -> u32 {
+        self.0.as_u32()
+    }
+    fn from_index(u: u32) -> Self {
+        Self(Local::from_u32(u))
+    }
+    fn tag() -> &'static str {
+        "UnifyLocal"
+    }
+}
+
+struct Replacements<'tcx> {
+    /// Maps locals to their replacement.
+    map: IndexVec<Local, Option<Place<'tcx>>>,
+
+    /// Whose locals' live ranges to kill.
+    kill: BitSet<Local>,
+}
+
+impl Replacements<'tcx> {
+    fn new(locals: usize) -> Self {
+        Self { map: IndexVec::from_elem_n(None, locals), kill: BitSet::new_empty(locals) }
+    }
+
+    fn push(&mut self, candidate: CandidateAssignment<'tcx>) {
+        trace!("Replacements::push({:?})", candidate);
+        let entry = &mut self.map[candidate.src];
+        assert!(entry.is_none());
+
+        *entry = Some(candidate.dest);
+        self.kill.insert(candidate.src);
+        self.kill.insert(candidate.dest.local);
+    }
+
+    /// Applies the stored replacements to all replacements, until no replacements would result in
+    /// locals that need further replacements when applied.
+    fn flatten(&mut self, tcx: TyCtxt<'tcx>) {
+        // Note: This assumes that there are no cycles in the replacements, which is enforced via
+        // `self.unified_locals`. Otherwise this can cause an infinite loop.
+
+        for local in self.map.indices() {
+            if let Some(replacement) = self.map[local] {
+                // Substitute the base local of `replacement` until fixpoint.
+                let mut base = replacement.local;
+                let mut reversed_projection_slices = Vec::with_capacity(1);
+                while let Some(replacement_for_replacement) = self.map[base] {
+                    base = replacement_for_replacement.local;
+                    reversed_projection_slices.push(replacement_for_replacement.projection);
+                }
+
+                let projection: Vec<_> = reversed_projection_slices
+                    .iter()
+                    .rev()
+                    .flat_map(|projs| projs.iter())
+                    .chain(replacement.projection.iter())
+                    .collect();
+                let projection = tcx.intern_place_elems(&projection);
+
+                // Replace with the final `Place`.
+                self.map[local] = Some(Place { local: base, projection });
+            }
+        }
+    }
+
+    fn for_src(&self, src: Local) -> Option<Place<'tcx>> {
+        self.map[src]
+    }
+}
+
+struct Replacer<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    replacements: Replacements<'tcx>,
+    place_elem_cache: Vec<PlaceElem<'tcx>>,
+}
+
+impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> {
+    fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
+        self.tcx
+    }
+
+    fn visit_local(&mut self, local: &mut Local, context: PlaceContext, location: Location) {
+        if context.is_use() && self.replacements.for_src(*local).is_some() {
+            bug!(
+                "use of local {:?} should have been replaced by visit_place; context={:?}, loc={:?}",
+                local,
+                context,
+                location,
+            );
+        }
+    }
+
+    fn process_projection_elem(
+        &mut self,
+        elem: PlaceElem<'tcx>,
+        _: Location,
+    ) -> Option<PlaceElem<'tcx>> {
+        match elem {
+            PlaceElem::Index(local) => {
+                if let Some(replacement) = self.replacements.for_src(local) {
+                    bug!(
+                        "cannot replace {:?} with {:?} in index projection {:?}",
+                        local,
+                        replacement,
+                        elem,
+                    );
+                } else {
+                    None
+                }
+            }
+            _ => None,
+        }
+    }
+
+    fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) {
+        if let Some(replacement) = self.replacements.for_src(place.local) {
+            // Rebase `place`s projections onto `replacement`'s.
+            self.place_elem_cache.clear();
+            self.place_elem_cache.extend(replacement.projection.iter().chain(place.projection));
+            let projection = self.tcx.intern_place_elems(&self.place_elem_cache);
+            let new_place = Place { local: replacement.local, projection };
+
+            debug!("Replacer: {:?} -> {:?}", place, new_place);
+            *place = new_place;
+        }
+
+        self.super_place(place, context, location);
+    }
+
+    fn visit_statement(&mut self, statement: &mut Statement<'tcx>, location: Location) {
+        self.super_statement(statement, location);
+
+        match &statement.kind {
+            // FIXME: Don't delete storage statements, merge the live ranges instead
+            StatementKind::StorageDead(local) | StatementKind::StorageLive(local)
+                if self.replacements.kill.contains(*local) =>
+            {
+                statement.make_nop()
+            }
+
+            StatementKind::Assign(box (dest, rvalue)) => {
+                match rvalue {
+                    Rvalue::Use(Operand::Copy(place) | Operand::Move(place)) => {
+                        // These might've been turned into self-assignments by the replacement
+                        // (this includes the original statement we wanted to eliminate).
+                        if dest == place {
+                            debug!("{:?} turned into self-assignment, deleting", location);
+                            statement.make_nop();
+                        }
+                    }
+                    _ => {}
+                }
+            }
+
+            _ => {}
+        }
+    }
+}
+
+struct Conflicts<'a> {
+    relevant_locals: &'a BitSet<Local>,
+
+    /// The conflict matrix. It is always symmetric and the adjacency matrix of the corresponding
+    /// conflict graph.
+    matrix: BitMatrix<Local, Local>,
+
+    /// Preallocated `BitSet` used by `unify`.
+    unify_cache: BitSet<Local>,
+
+    /// Tracks locals that have been merged together to prevent cycles and propagate conflicts.
+    unified_locals: InPlaceUnificationTable<UnifyLocal>,
+}
+
+impl Conflicts<'a> {
+    fn build<'tcx>(
+        tcx: TyCtxt<'tcx>,
+        body: &'_ Body<'tcx>,
+        source: MirSource<'tcx>,
+        relevant_locals: &'a BitSet<Local>,
+    ) -> Self {
+        // We don't have to look out for locals that have their address taken, since
+        // `find_candidates` already takes care of that.
+
+        let conflicts = BitMatrix::from_row_n(
+            &BitSet::new_empty(body.local_decls.len()),
+            body.local_decls.len(),
+        );
+
+        let def_id = source.def_id();
+        let mut init = MaybeInitializedLocals
+            .into_engine(tcx, body, def_id)
+            .iterate_to_fixpoint()
+            .into_results_cursor(body);
+        let mut live = MaybeLiveLocals
+            .into_engine(tcx, body, def_id)
+            .iterate_to_fixpoint()
+            .into_results_cursor(body);
+
+        let mut reachable = None;
+        dump_mir(
+            tcx,
+            None,
+            "DestinationPropagation-dataflow",
+            &"",
+            source,
+            body,
+            |pass_where, w| {
+                let reachable =
+                    reachable.get_or_insert_with(|| traversal::reachable_as_bitset(body));
+
+                match pass_where {
+                    PassWhere::BeforeLocation(loc) if reachable.contains(loc.block) => {
+                        init.seek_before_primary_effect(loc);
+                        live.seek_after_primary_effect(loc);
+
+                        writeln!(w, "        // init: {:?}", init.get())?;
+                        writeln!(w, "        // live: {:?}", live.get())?;
+                    }
+                    PassWhere::AfterTerminator(bb) if reachable.contains(bb) => {
+                        let loc = body.terminator_loc(bb);
+                        init.seek_after_primary_effect(loc);
+                        live.seek_before_primary_effect(loc);
+
+                        writeln!(w, "        // init: {:?}", init.get())?;
+                        writeln!(w, "        // live: {:?}", live.get())?;
+                    }
+
+                    PassWhere::BeforeBlock(bb) if reachable.contains(bb) => {
+                        init.seek_to_block_start(bb);
+                        live.seek_to_block_start(bb);
+
+                        writeln!(w, "    // init: {:?}", init.get())?;
+                        writeln!(w, "    // live: {:?}", live.get())?;
+                    }
+
+                    PassWhere::BeforeCFG | PassWhere::AfterCFG | PassWhere::AfterLocation(_) => {}
+
+                    PassWhere::BeforeLocation(_) | PassWhere::AfterTerminator(_) => {
+                        writeln!(w, "        // init: <unreachable>")?;
+                        writeln!(w, "        // live: <unreachable>")?;
+                    }
+
+                    PassWhere::BeforeBlock(_) => {
+                        writeln!(w, "    // init: <unreachable>")?;
+                        writeln!(w, "    // live: <unreachable>")?;
+                    }
+                }
+
+                Ok(())
+            },
+        );
+
+        let mut this = Self {
+            relevant_locals,
+            matrix: conflicts,
+            unify_cache: BitSet::new_empty(body.local_decls.len()),
+            unified_locals: {
+                let mut table = InPlaceUnificationTable::new();
+                // Pre-fill table with all locals (this creates N nodes / "connected" components,
+                // "graph"-ically speaking).
+                for local in 0..body.local_decls.len() {
+                    assert_eq!(table.new_key(()), UnifyLocal(Local::from_usize(local)));
+                }
+                table
+            },
+        };
+
+        let mut live_and_init_locals = Vec::new();
+
+        // Visit only reachable basic blocks. The exact order is not important.
+        for (block, data) in traversal::preorder(body) {
+            // We need to observe the dataflow state *before* all possible locations (statement or
+            // terminator) in each basic block, and then observe the state *after* the terminator
+            // effect is applied. As long as neither `init` nor `borrowed` has a "before" effect,
+            // we will observe all possible dataflow states.
+
+            // Since liveness is a backwards analysis, we need to walk the results backwards. To do
+            // that, we first collect in the `MaybeInitializedLocals` results in a forwards
+            // traversal.
+
+            live_and_init_locals.resize_with(data.statements.len() + 1, || {
+                BitSet::new_empty(body.local_decls.len())
+            });
+
+            // First, go forwards for `MaybeInitializedLocals` and apply intra-statement/terminator
+            // conflicts.
+            for (i, statement) in data.statements.iter().enumerate() {
+                this.record_statement_conflicts(statement);
+
+                let loc = Location { block, statement_index: i };
+                init.seek_before_primary_effect(loc);
+
+                live_and_init_locals[i].clone_from(init.get());
+            }
+
+            this.record_terminator_conflicts(data.terminator());
+            let term_loc = Location { block, statement_index: data.statements.len() };
+            init.seek_before_primary_effect(term_loc);
+            live_and_init_locals[term_loc.statement_index].clone_from(init.get());
+
+            // Now, go backwards and union with the liveness results.
+            for statement_index in (0..=data.statements.len()).rev() {
+                let loc = Location { block, statement_index };
+                live.seek_after_primary_effect(loc);
+
+                live_and_init_locals[statement_index].intersect(live.get());
+
+                trace!("record conflicts at {:?}", loc);
+
+                this.record_dataflow_conflicts(&mut live_and_init_locals[statement_index]);
+            }
+
+            init.seek_to_block_end(block);
+            live.seek_to_block_end(block);
+            let mut conflicts = init.get().clone();
+            conflicts.intersect(live.get());
+            trace!("record conflicts at end of {:?}", block);
+
+            this.record_dataflow_conflicts(&mut conflicts);
+        }
+
+        this
+    }
+
+    fn record_dataflow_conflicts(&mut self, new_conflicts: &mut BitSet<Local>) {
+        // Remove all locals that are not candidates.
+        new_conflicts.intersect(self.relevant_locals);
+
+        for local in new_conflicts.iter() {
+            self.matrix.union_row_with(&new_conflicts, local);
+        }
+    }
+
+    fn record_local_conflict(&mut self, a: Local, b: Local, why: &str) {
+        trace!("conflict {:?} <-> {:?} due to {}", a, b, why);
+        self.matrix.insert(a, b);
+        self.matrix.insert(b, a);
+    }
+
+    /// Records locals that must not overlap during the evaluation of `stmt`. These locals conflict
+    /// and must not be merged.
+    fn record_statement_conflicts(&mut self, stmt: &Statement<'_>) {
+        match &stmt.kind {
+            // While the left and right sides of an assignment must not overlap, we do not mark
+            // conflicts here as that would make this optimization useless. When we optimize, we
+            // eliminate the resulting self-assignments automatically.
+            StatementKind::Assign(_) => {}
+
+            StatementKind::LlvmInlineAsm(asm) => {
+                // Inputs and outputs must not overlap.
+                for (_, input) in &*asm.inputs {
+                    if let Some(in_place) = input.place() {
+                        if !in_place.is_indirect() {
+                            for out_place in &*asm.outputs {
+                                if !out_place.is_indirect() && !in_place.is_indirect() {
+                                    self.record_local_conflict(
+                                        in_place.local,
+                                        out_place.local,
+                                        "aliasing llvm_asm! operands",
+                                    );
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+
+            StatementKind::SetDiscriminant { .. }
+            | StatementKind::StorageLive(..)
+            | StatementKind::StorageDead(..)
+            | StatementKind::Retag(..)
+            | StatementKind::FakeRead(..)
+            | StatementKind::AscribeUserType(..)
+            | StatementKind::Coverage(..)
+            | StatementKind::Nop => {}
+        }
+    }
+
+    fn record_terminator_conflicts(&mut self, term: &Terminator<'_>) {
+        match &term.kind {
+            TerminatorKind::DropAndReplace {
+                place: dropped_place,
+                value,
+                target: _,
+                unwind: _,
+            } => {
+                if let Some(place) = value.place() {
+                    if !place.is_indirect() && !dropped_place.is_indirect() {
+                        self.record_local_conflict(
+                            place.local,
+                            dropped_place.local,
+                            "DropAndReplace operand overlap",
+                        );
+                    }
+                }
+            }
+            TerminatorKind::Yield { value, resume: _, resume_arg, drop: _ } => {
+                if let Some(place) = value.place() {
+                    if !place.is_indirect() && !resume_arg.is_indirect() {
+                        self.record_local_conflict(
+                            place.local,
+                            resume_arg.local,
+                            "Yield operand overlap",
+                        );
+                    }
+                }
+            }
+            TerminatorKind::Call {
+                func,
+                args,
+                destination: Some((dest_place, _)),
+                cleanup: _,
+                from_hir_call: _,
+                fn_span: _,
+            } => {
+                // No arguments may overlap with the destination.
+                for arg in args.iter().chain(Some(func)) {
+                    if let Some(place) = arg.place() {
+                        if !place.is_indirect() && !dest_place.is_indirect() {
+                            self.record_local_conflict(
+                                dest_place.local,
+                                place.local,
+                                "call dest/arg overlap",
+                            );
+                        }
+                    }
+                }
+            }
+            TerminatorKind::InlineAsm {
+                template: _,
+                operands,
+                options: _,
+                line_spans: _,
+                destination: _,
+            } => {
+                // The intended semantics here aren't documented, we just assume that nothing that
+                // could be written to by the assembly may overlap with any other operands.
+                for op in operands {
+                    match op {
+                        InlineAsmOperand::Out { reg: _, late: _, place: Some(dest_place) }
+                        | InlineAsmOperand::InOut {
+                            reg: _,
+                            late: _,
+                            in_value: _,
+                            out_place: Some(dest_place),
+                        } => {
+                            // For output place `place`, add all places accessed by the inline asm.
+                            for op in operands {
+                                match op {
+                                    InlineAsmOperand::In { reg: _, value } => {
+                                        if let Some(p) = value.place() {
+                                            if !p.is_indirect() && !dest_place.is_indirect() {
+                                                self.record_local_conflict(
+                                                    p.local,
+                                                    dest_place.local,
+                                                    "asm! operand overlap",
+                                                );
+                                            }
+                                        }
+                                    }
+                                    InlineAsmOperand::Out {
+                                        reg: _,
+                                        late: _,
+                                        place: Some(place),
+                                    } => {
+                                        if !place.is_indirect() && !dest_place.is_indirect() {
+                                            self.record_local_conflict(
+                                                place.local,
+                                                dest_place.local,
+                                                "asm! operand overlap",
+                                            );
+                                        }
+                                    }
+                                    InlineAsmOperand::InOut {
+                                        reg: _,
+                                        late: _,
+                                        in_value,
+                                        out_place,
+                                    } => {
+                                        if let Some(place) = in_value.place() {
+                                            if !place.is_indirect() && !dest_place.is_indirect() {
+                                                self.record_local_conflict(
+                                                    place.local,
+                                                    dest_place.local,
+                                                    "asm! operand overlap",
+                                                );
+                                            }
+                                        }
+
+                                        if let Some(place) = out_place {
+                                            if !place.is_indirect() && !dest_place.is_indirect() {
+                                                self.record_local_conflict(
+                                                    place.local,
+                                                    dest_place.local,
+                                                    "asm! operand overlap",
+                                                );
+                                            }
+                                        }
+                                    }
+                                    InlineAsmOperand::Out { reg: _, late: _, place: None }
+                                    | InlineAsmOperand::Const { value: _ }
+                                    | InlineAsmOperand::SymFn { value: _ }
+                                    | InlineAsmOperand::SymStatic { def_id: _ } => {}
+                                }
+                            }
+                        }
+                        InlineAsmOperand::Const { value } => {
+                            assert!(value.place().is_none());
+                        }
+                        InlineAsmOperand::InOut {
+                            reg: _,
+                            late: _,
+                            in_value: _,
+                            out_place: None,
+                        }
+                        | InlineAsmOperand::In { reg: _, value: _ }
+                        | InlineAsmOperand::Out { reg: _, late: _, place: None }
+                        | InlineAsmOperand::SymFn { value: _ }
+                        | InlineAsmOperand::SymStatic { def_id: _ } => {}
+                    }
+                }
+            }
+
+            TerminatorKind::Goto { .. }
+            | TerminatorKind::Call { destination: None, .. }
+            | TerminatorKind::SwitchInt { .. }
+            | TerminatorKind::Resume
+            | TerminatorKind::Abort
+            | TerminatorKind::Return
+            | TerminatorKind::Unreachable
+            | TerminatorKind::Drop { .. }
+            | TerminatorKind::Assert { .. }
+            | TerminatorKind::GeneratorDrop
+            | TerminatorKind::FalseEdge { .. }
+            | TerminatorKind::FalseUnwind { .. } => {}
+        }
+    }
+
+    /// Checks whether `a` and `b` may be merged. Returns `false` if there's a conflict.
+    fn can_unify(&mut self, a: Local, b: Local) -> bool {
+        // After some locals have been unified, their conflicts are only tracked in the root key,
+        // so look that up.
+        let a = self.unified_locals.find(a).0;
+        let b = self.unified_locals.find(b).0;
+
+        if a == b {
+            // Already merged (part of the same connected component).
+            return false;
+        }
+
+        if self.matrix.contains(a, b) {
+            // Conflict (derived via dataflow, intra-statement conflicts, or inherited from another
+            // local during unification).
+            return false;
+        }
+
+        true
+    }
+
+    /// Merges the conflicts of `a` and `b`, so that each one inherits all conflicts of the other.
+    ///
+    /// `can_unify` must have returned `true` for the same locals, or this may panic or lead to
+    /// miscompiles.
+    ///
+    /// This is called when the pass makes the decision to unify `a` and `b` (or parts of `a` and
+    /// `b`) and is needed to ensure that future unification decisions take potentially newly
+    /// introduced conflicts into account.
+    ///
+    /// For an example, assume we have locals `_0`, `_1`, `_2`, and `_3`. There are these conflicts:
+    ///
+    /// * `_0` <-> `_1`
+    /// * `_1` <-> `_2`
+    /// * `_3` <-> `_0`
+    ///
+    /// We then decide to merge `_2` with `_3` since they don't conflict. Then we decide to merge
+    /// `_2` with `_0`, which also doesn't have a conflict in the above list. However `_2` is now
+    /// `_3`, which does conflict with `_0`.
+    fn unify(&mut self, a: Local, b: Local) {
+        trace!("unify({:?}, {:?})", a, b);
+
+        // Get the root local of the connected components. The root local stores the conflicts of
+        // all locals in the connected component (and *is stored* as the conflicting local of other
+        // locals).
+        let a = self.unified_locals.find(a).0;
+        let b = self.unified_locals.find(b).0;
+        assert_ne!(a, b);
+
+        trace!("roots: a={:?}, b={:?}", a, b);
+        trace!("{:?} conflicts: {:?}", a, self.matrix.iter(a).format(", "));
+        trace!("{:?} conflicts: {:?}", b, self.matrix.iter(b).format(", "));
+
+        self.unified_locals.union(a, b);
+
+        let root = self.unified_locals.find(a).0;
+        assert!(root == a || root == b);
+
+        // Make all locals that conflict with `a` also conflict with `b`, and vice versa.
+        self.unify_cache.clear();
+        for conflicts_with_a in self.matrix.iter(a) {
+            self.unify_cache.insert(conflicts_with_a);
+        }
+        for conflicts_with_b in self.matrix.iter(b) {
+            self.unify_cache.insert(conflicts_with_b);
+        }
+        for conflicts_with_a_or_b in self.unify_cache.iter() {
+            // Set both `a` and `b` for this local's row.
+            self.matrix.insert(conflicts_with_a_or_b, a);
+            self.matrix.insert(conflicts_with_a_or_b, b);
+        }
+
+        // Write the locals `a` conflicts with to `b`'s row.
+        self.matrix.union_rows(a, b);
+        // Write the locals `b` conflicts with to `a`'s row.
+        self.matrix.union_rows(b, a);
+    }
+}
+
+/// A `dest = {move} src;` statement at `loc`.
+///
+/// We want to consider merging `dest` and `src` due to this assignment.
+#[derive(Debug, Copy, Clone)]
+struct CandidateAssignment<'tcx> {
+    /// Does not contain indirection or indexing (so the only local it contains is the place base).
+    dest: Place<'tcx>,
+    src: Local,
+    loc: Location,
+}
+
+/// Scans the MIR for assignments between locals that we might want to consider merging.
+///
+/// This will filter out assignments that do not match the right form (as described in the top-level
+/// comment) and also throw out assignments that involve a local that has its address taken or is
+/// otherwise ineligible (eg. locals used as array indices are ignored because we cannot propagate
+/// arbitrary places into array indices).
+fn find_candidates<'a, 'tcx>(
+    tcx: TyCtxt<'tcx>,
+    body: &'a Body<'tcx>,
+) -> Vec<CandidateAssignment<'tcx>> {
+    let mut visitor = FindAssignments {
+        tcx,
+        body,
+        candidates: Vec::new(),
+        ever_borrowed_locals: ever_borrowed_locals(body),
+        locals_used_as_array_index: locals_used_as_array_index(body),
+    };
+    visitor.visit_body(body);
+    visitor.candidates
+}
+
+struct FindAssignments<'a, 'tcx> {
+    tcx: TyCtxt<'tcx>,
+    body: &'a Body<'tcx>,
+    candidates: Vec<CandidateAssignment<'tcx>>,
+    ever_borrowed_locals: BitSet<Local>,
+    locals_used_as_array_index: BitSet<Local>,
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for FindAssignments<'a, 'tcx> {
+    fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
+        if let StatementKind::Assign(box (
+            dest,
+            Rvalue::Use(Operand::Copy(src) | Operand::Move(src)),
+        )) = &statement.kind
+        {
+            // `dest` must not have pointer indirection.
+            if dest.is_indirect() {
+                return;
+            }
+
+            // `src` must be a plain local.
+            if !src.projection.is_empty() {
+                return;
+            }
+
+            // Since we want to replace `src` with `dest`, `src` must not be required.
+            if is_local_required(src.local, self.body) {
+                return;
+            }
+
+            // Can't optimize if both locals ever have their address taken (can introduce
+            // aliasing).
+            // FIXME: This can be smarter and take `StorageDead` into account (which
+            // invalidates borrows).
+            if self.ever_borrowed_locals.contains(dest.local)
+                && self.ever_borrowed_locals.contains(src.local)
+            {
+                return;
+            }
+
+            assert_ne!(dest.local, src.local, "self-assignments are UB");
+
+            // We can't replace locals occurring in `PlaceElem::Index` for now.
+            if self.locals_used_as_array_index.contains(src.local) {
+                return;
+            }
+
+            // Handle the "subtle case" described above by rejecting any `dest` that is or
+            // projects through a union.
+            let is_union = |ty: Ty<'_>| {
+                if let ty::Adt(def, _) = ty.kind() {
+                    if def.is_union() {
+                        return true;
+                    }
+                }
+
+                false
+            };
+            let mut place_ty = PlaceTy::from_ty(self.body.local_decls[dest.local].ty);
+            if is_union(place_ty.ty) {
+                return;
+            }
+            for elem in dest.projection {
+                if let PlaceElem::Index(_) = elem {
+                    // `dest` contains an indexing projection.
+                    return;
+                }
+
+                place_ty = place_ty.projection_ty(self.tcx, elem);
+                if is_union(place_ty.ty) {
+                    return;
+                }
+            }
+
+            self.candidates.push(CandidateAssignment {
+                dest: *dest,
+                src: src.local,
+                loc: location,
+            });
+        }
+    }
+}
+
+/// Some locals are part of the function's interface and can not be removed.
+///
+/// Note that these locals *can* still be merged with non-required locals by removing that other
+/// local.
+fn is_local_required(local: Local, body: &Body<'_>) -> bool {
+    match body.local_kind(local) {
+        LocalKind::Arg | LocalKind::ReturnPointer => true,
+        LocalKind::Var | LocalKind::Temp => false,
+    }
+}
+
+/// Walks MIR to find all locals that have their address taken anywhere.
+fn ever_borrowed_locals(body: &Body<'_>) -> BitSet<Local> {
+    let mut visitor = BorrowCollector { locals: BitSet::new_empty(body.local_decls.len()) };
+    visitor.visit_body(body);
+    visitor.locals
+}
+
+struct BorrowCollector {
+    locals: BitSet<Local>,
+}
+
+impl<'tcx> Visitor<'tcx> for BorrowCollector {
+    fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
+        self.super_rvalue(rvalue, location);
+
+        match rvalue {
+            Rvalue::AddressOf(_, borrowed_place) | Rvalue::Ref(_, _, borrowed_place) => {
+                if !borrowed_place.is_indirect() {
+                    self.locals.insert(borrowed_place.local);
+                }
+            }
+
+            Rvalue::Cast(..)
+            | Rvalue::Use(..)
+            | Rvalue::Repeat(..)
+            | Rvalue::Len(..)
+            | Rvalue::BinaryOp(..)
+            | Rvalue::CheckedBinaryOp(..)
+            | Rvalue::NullaryOp(..)
+            | Rvalue::UnaryOp(..)
+            | Rvalue::Discriminant(..)
+            | Rvalue::Aggregate(..)
+            | Rvalue::ThreadLocalRef(..) => {}
+        }
+    }
+
+    fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
+        self.super_terminator(terminator, location);
+
+        match terminator.kind {
+            TerminatorKind::Drop { place: dropped_place, .. }
+            | TerminatorKind::DropAndReplace { place: dropped_place, .. } => {
+                self.locals.insert(dropped_place.local);
+            }
+
+            TerminatorKind::Abort
+            | TerminatorKind::Assert { .. }
+            | TerminatorKind::Call { .. }
+            | TerminatorKind::FalseEdge { .. }
+            | TerminatorKind::FalseUnwind { .. }
+            | TerminatorKind::GeneratorDrop
+            | TerminatorKind::Goto { .. }
+            | TerminatorKind::Resume
+            | TerminatorKind::Return
+            | TerminatorKind::SwitchInt { .. }
+            | TerminatorKind::Unreachable
+            | TerminatorKind::Yield { .. }
+            | TerminatorKind::InlineAsm { .. } => {}
+        }
+    }
+}
+
+/// `PlaceElem::Index` only stores a `Local`, so we can't replace that with a full `Place`.
+///
+/// Collect locals used as indices so we don't generate candidates that are impossible to apply
+/// later.
+fn locals_used_as_array_index(body: &Body<'_>) -> BitSet<Local> {
+    let mut visitor = IndexCollector { locals: BitSet::new_empty(body.local_decls.len()) };
+    visitor.visit_body(body);
+    visitor.locals
+}
+
+struct IndexCollector {
+    locals: BitSet<Local>,
+}
+
+impl<'tcx> Visitor<'tcx> for IndexCollector {
+    fn visit_projection_elem(
+        &mut self,
+        local: Local,
+        proj_base: &[PlaceElem<'tcx>],
+        elem: PlaceElem<'tcx>,
+        context: PlaceContext,
+        location: Location,
+    ) {
+        if let PlaceElem::Index(i) = elem {
+            self.locals.insert(i);
+        }
+        self.super_projection_elem(local, proj_base, elem, context, location);
+    }
+}
diff --git a/compiler/rustc_mir/src/transform/early_otherwise_branch.rs b/compiler/rustc_mir/src/transform/early_otherwise_branch.rs
new file mode 100644
index 00000000000..67e679a8b08
--- /dev/null
+++ b/compiler/rustc_mir/src/transform/early_otherwise_branch.rs
@@ -0,0 +1,339 @@
+use crate::{
+    transform::{MirPass, MirSource},
+    util::patch::MirPatch,
+};
+use rustc_middle::mir::*;
+use rustc_middle::ty::{Ty, TyCtxt};
+use std::{borrow::Cow, fmt::Debug};
+
+use super::simplify::simplify_cfg;
+
+/// This pass optimizes something like
+/// ```text
+/// let x: Option<()>;
+/// let y: Option<()>;
+/// match (x,y) {
+///     (Some(_), Some(_)) => {0},
+///     _ => {1}
+/// }
+/// ```
+/// into something like
+/// ```text
+/// let x: Option<()>;
+/// let y: Option<()>;
+/// let discriminant_x = // get discriminant of x
+/// let discriminant_y = // get discriminant of y
+/// if discriminant_x != discriminant_y || discriminant_x == None {1} else {0}
+/// ```
+pub struct EarlyOtherwiseBranch;
+
+impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch {
+    fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
+        if tcx.sess.opts.debugging_opts.mir_opt_level < 1 {
+            return;
+        }
+        trace!("running EarlyOtherwiseBranch on {:?}", source);
+        // we are only interested in this bb if the terminator is a switchInt
+        let bbs_with_switch =
+            body.basic_blocks().iter_enumerated().filter(|(_, bb)| is_switch(bb.terminator()));
+
+        let opts_to_apply: Vec<OptimizationToApply<'tcx>> = bbs_with_switch
+            .flat_map(|(bb_idx, bb)| {
+                let switch = bb.terminator();
+                let helper = Helper { body, tcx };
+                let infos = helper.go(bb, switch)?;
+                Some(OptimizationToApply { infos, basic_block_first_switch: bb_idx })
+            })
+            .collect();
+
+        let should_cleanup = !opts_to_apply.is_empty();
+
+        for opt_to_apply in opts_to_apply {
+            trace!("SUCCESS: found optimization possibility to apply: {:?}", &opt_to_apply);
+
+            let statements_before =
+                body.basic_blocks()[opt_to_apply.basic_block_first_switch].statements.len();
+            let end_of_block_location = Location {
+                block: opt_to_apply.basic_block_first_switch,
+                statement_index: statements_before,
+            };
+
+            let mut patch = MirPatch::new(body);
+
+            // create temp to store second discriminant in
+            let discr_type = opt_to_apply.infos[0].second_switch_info.discr_ty;
+            let discr_span = opt_to_apply.infos[0].second_switch_info.discr_source_info.span;
+            let second_discriminant_temp = patch.new_temp(discr_type, discr_span);
+
+            patch.add_statement(
+                end_of_block_location,
+                StatementKind::StorageLive(second_discriminant_temp),
+            );
+
+            // create assignment of discriminant
+            let place_of_adt_to_get_discriminant_of =
+                opt_to_apply.infos[0].second_switch_info.place_of_adt_discr_read;
+            patch.add_assign(
+                end_of_block_location,
+                Place::from(second_discriminant_temp),
+                Rvalue::Discriminant(place_of_adt_to_get_discriminant_of),
+            );
+
+            // create temp to store NotEqual comparison between the two discriminants
+            let not_equal = BinOp::Ne;
+            let not_equal_res_type = not_equal.ty(tcx, discr_type, discr_type);
+            let not_equal_temp = patch.new_temp(not_equal_res_type, discr_span);
+            patch.add_statement(end_of_block_location, StatementKind::StorageLive(not_equal_temp));
+
+            // create NotEqual comparison between the two discriminants
+            let first_descriminant_place =
+                opt_to_apply.infos[0].first_switch_info.discr_used_in_switch;
+            let not_equal_rvalue = Rvalue::BinaryOp(
+                not_equal,
+                Operand::Copy(Place::from(second_discriminant_temp)),
+                Operand::Copy(Place::from(first_descriminant_place)),
+            );
+            patch.add_statement(
+                end_of_block_location,
+                StatementKind::Assign(box (Place::from(not_equal_temp), not_equal_rvalue)),
+            );
+
+            let (mut targets_to_jump_to, values_to_jump_to): (Vec<_>, Vec<_>) = opt_to_apply
+                .infos
+                .iter()
+                .flat_map(|x| x.second_switch_info.targets_with_values.iter())
+                .cloned()
+                .unzip();
+
+            // add otherwise case in the end
+            targets_to_jump_to.push(opt_to_apply.infos[0].first_switch_info.otherwise_bb);
+            // new block that jumps to the correct discriminant case. This block is switched to if the discriminants are equal
+            let new_switch_data = BasicBlockData::new(Some(Terminator {
+                source_info: opt_to_apply.infos[0].second_switch_info.discr_source_info,
+                kind: TerminatorKind::SwitchInt {
+                    // the first and second discriminants are equal, so just pick one
+                    discr: Operand::Copy(first_descriminant_place),
+                    switch_ty: discr_type,
+                    values: Cow::from(values_to_jump_to),
+                    targets: targets_to_jump_to,
+                },
+            }));
+
+            let new_switch_bb = patch.new_block(new_switch_data);
+
+            // switch on the NotEqual. If true, then jump to the `otherwise` case.
+            // If false, then jump to a basic block that then jumps to the correct disciminant case
+            let true_case = opt_to_apply.infos[0].first_switch_info.otherwise_bb;
+            let false_case = new_switch_bb;
+            patch.patch_terminator(
+                opt_to_apply.basic_block_first_switch,
+                TerminatorKind::if_(
+                    tcx,
+                    Operand::Move(Place::from(not_equal_temp)),
+                    true_case,
+                    false_case,
+                ),
+            );
+
+            // generate StorageDead for the second_discriminant_temp not in use anymore
+            patch.add_statement(
+                end_of_block_location,
+                StatementKind::StorageDead(second_discriminant_temp),
+            );
+
+            // Generate a StorageDead for not_equal_temp in each of the targets, since we moved it into the switch
+            for bb in [false_case, true_case].iter() {
+                patch.add_statement(
+                    Location { block: *bb, statement_index: 0 },
+                    StatementKind::StorageDead(not_equal_temp),
+                );
+            }
+
+            patch.apply(body);
+        }
+
+        // Since this optimization adds new basic blocks and invalidates others,
+        // clean up the cfg to make it nicer for other passes
+        if should_cleanup {
+            simplify_cfg(body);
+        }
+    }
+}
+
+fn is_switch<'tcx>(terminator: &Terminator<'tcx>) -> bool {
+    match terminator.kind {
+        TerminatorKind::SwitchInt { .. } => true,
+        _ => false,
+    }
+}
+
+struct Helper<'a, 'tcx> {
+    body: &'a Body<'tcx>,
+    tcx: TyCtxt<'tcx>,
+}
+
+#[derive(Debug, Clone)]
+struct SwitchDiscriminantInfo<'tcx> {
+    /// Type of the discriminant being switched on
+    discr_ty: Ty<'tcx>,
+    /// The basic block that the otherwise branch points to
+    otherwise_bb: BasicBlock,
+    /// Target along with the value being branched from. Otherwise is not included
+    targets_with_values: Vec<(BasicBlock, u128)>,
+    discr_source_info: SourceInfo,
+    /// The place of the discriminant used in the switch
+    discr_used_in_switch: Place<'tcx>,
+    /// The place of the adt that has its discriminant read
+    place_of_adt_discr_read: Place<'tcx>,
+    /// The type of the adt that has its discriminant read
+    type_adt_matched_on: Ty<'tcx>,
+}
+
+#[derive(Debug)]
+struct OptimizationToApply<'tcx> {
+    infos: Vec<OptimizationInfo<'tcx>>,
+    /// Basic block of the original first switch
+    basic_block_first_switch: BasicBlock,
+}
+
+#[derive(Debug)]
+struct OptimizationInfo<'tcx> {
+    /// Info about the first switch and discriminant
+    first_switch_info: SwitchDiscriminantInfo<'tcx>,
+    /// Info about the second switch and discriminant
+    second_switch_info: SwitchDiscriminantInfo<'tcx>,
+}
+
+impl<'a, 'tcx> Helper<'a, 'tcx> {
+    pub fn go(
+        &self,
+        bb: &BasicBlockData<'tcx>,
+        switch: &Terminator<'tcx>,
+    ) -> Option<Vec<OptimizationInfo<'tcx>>> {
+        // try to find the statement that defines the discriminant that is used for the switch
+        let discr = self.find_switch_discriminant_info(bb, switch)?;
+
+        // go through each target, finding a discriminant read, and a switch
+        let results = discr.targets_with_values.iter().map(|(target, value)| {
+            self.find_discriminant_switch_pairing(&discr, target.clone(), value.clone())
+        });
+
+        // if the optimization did not apply for one of the targets, then abort
+        if results.clone().any(|x| x.is_none()) || results.len() == 0 {
+            trace!("NO: not all of the targets matched the pattern for optimization");
+            return None;
+        }
+
+        Some(results.flatten().collect())
+    }
+
+    fn find_discriminant_switch_pairing(
+        &self,
+        discr_info: &SwitchDiscriminantInfo<'tcx>,
+        target: BasicBlock,
+        value: u128,
+    ) -> Option<OptimizationInfo<'tcx>> {
+        let bb = &self.body.basic_blocks()[target];
+        // find switch
+        let terminator = bb.terminator();
+        if is_switch(terminator) {
+            let this_bb_discr_info = self.find_switch_discriminant_info(bb, terminator)?;
+
+            // the types of the two adts matched on have to be equalfor this optimization to apply
+            if discr_info.type_adt_matched_on != this_bb_discr_info.type_adt_matched_on {
+                trace!(
+                    "NO: types do not match. LHS: {:?}, RHS: {:?}",
+                    discr_info.type_adt_matched_on,
+                    this_bb_discr_info.type_adt_matched_on
+                );
+                return None;
+            }
+
+            // the otherwise branch of the two switches have to point to the same bb
+            if discr_info.otherwise_bb != this_bb_discr_info.otherwise_bb {
+                trace!("NO: otherwise target is not the same");
+                return None;
+            }
+
+            // check that the value being matched on is the same. The
+            if this_bb_discr_info.targets_with_values.iter().find(|x| x.1 == value).is_none() {
+                trace!("NO: values being matched on are not the same");
+                return None;
+            }
+
+            // only allow optimization if the left and right of the tuple being matched are the same variants.
+            // so the following should not optimize
+            //  ```rust
+            // let x: Option<()>;
+            // let y: Option<()>;
+            // match (x,y) {
+            //     (Some(_), None) => {},
+            //     _ => {}
+            // }
+            //  ```
+            // We check this by seeing that the value of the first discriminant is the only other discriminant value being used as a target in the second switch
+            if !(this_bb_discr_info.targets_with_values.len() == 1
+                && this_bb_discr_info.targets_with_values[0].1 == value)
+            {
+                trace!(
+                    "NO: The second switch did not have only 1 target (besides otherwise) that had the same value as the value from the first switch that got us here"
+                );
+                return None;
+            }
+
+            // if we reach this point, the optimization applies, and we should be able to optimize this case
+            // store the info that is needed to apply the optimization
+
+            Some(OptimizationInfo {
+                first_switch_info: discr_info.clone(),
+                second_switch_info: this_bb_discr_info,
+            })
+        } else {
+            None
+        }
+    }
+
+    fn find_switch_discriminant_info(
+        &self,
+        bb: &BasicBlockData<'tcx>,
+        switch: &Terminator<'tcx>,
+    ) -> Option<SwitchDiscriminantInfo<'tcx>> {
+        match &switch.kind {
+            TerminatorKind::SwitchInt { discr, targets, values, .. } => {
+                let discr_local = discr.place()?.as_local()?;
+                // the declaration of the discriminant read. Place of this read is being used in the switch
+                let discr_decl = &self.body.local_decls()[discr_local];
+                let discr_ty = discr_decl.ty;
+                // the otherwise target lies as the last element
+                let otherwise_bb = targets.get(values.len())?.clone();
+                let targets_with_values = targets
+                    .iter()
+                    .zip(values.iter())
+                    .map(|(t, v)| (t.clone(), v.clone()))
+                    .collect();
+
+                // find the place of the adt where the discriminant is being read from
+                // assume this is the last statement of the block
+                let place_of_adt_discr_read = match bb.statements.last()?.kind {
+                    StatementKind::Assign(box (_, Rvalue::Discriminant(adt_place))) => {
+                        Some(adt_place)
+                    }
+                    _ => None,
+                }?;
+
+                let type_adt_matched_on = place_of_adt_discr_read.ty(self.body, self.tcx).ty;
+
+                Some(SwitchDiscriminantInfo {
+                    discr_used_in_switch: discr.place()?,
+                    discr_ty,
+                    otherwise_bb,
+                    targets_with_values,
+                    discr_source_info: discr_decl.source_info,
+                    place_of_adt_discr_read,
+                    type_adt_matched_on,
+                })
+            }
+            _ => unreachable!("must only be passed terminator that is a switch"),
+        }
+    }
+}
diff --git a/compiler/rustc_mir/src/transform/elaborate_drops.rs b/compiler/rustc_mir/src/transform/elaborate_drops.rs
index 5f193069356..a8b2ee5705f 100644
--- a/compiler/rustc_mir/src/transform/elaborate_drops.rs
+++ b/compiler/rustc_mir/src/transform/elaborate_drops.rs
@@ -44,6 +44,7 @@ impl<'tcx> MirPass<'tcx> for ElaborateDrops {
             let inits = MaybeInitializedPlaces::new(tcx, body, &env)
                 .into_engine(tcx, body, def_id)
                 .dead_unwinds(&dead_unwinds)
+                .pass_name("elaborate_drops")
                 .iterate_to_fixpoint()
                 .into_results_cursor(body);
 
@@ -51,6 +52,7 @@ impl<'tcx> MirPass<'tcx> for ElaborateDrops {
                 .mark_inactive_variants_as_uninit()
                 .into_engine(tcx, body, def_id)
                 .dead_unwinds(&dead_unwinds)
+                .pass_name("elaborate_drops")
                 .iterate_to_fixpoint()
                 .into_results_cursor(body);
 
@@ -83,6 +85,7 @@ fn find_dead_unwinds<'tcx>(
     let mut dead_unwinds = BitSet::new_empty(body.basic_blocks().len());
     let mut flow_inits = MaybeInitializedPlaces::new(tcx, body, &env)
         .into_engine(tcx, body, def_id)
+        .pass_name("find_dead_unwinds")
         .iterate_to_fixpoint()
         .into_results_cursor(body);
     for (bb, bb_data) in body.basic_blocks().iter_enumerated() {
diff --git a/compiler/rustc_mir/src/transform/generator.rs b/compiler/rustc_mir/src/transform/generator.rs
index a22075e760a..1fffcf81515 100644
--- a/compiler/rustc_mir/src/transform/generator.rs
+++ b/compiler/rustc_mir/src/transform/generator.rs
@@ -467,8 +467,10 @@ fn locals_live_across_suspend_points(
 
     // Calculate the MIR locals which have been previously
     // borrowed (even if they are still active).
-    let borrowed_locals_results =
-        MaybeBorrowedLocals::all_borrows().into_engine(tcx, body_ref, def_id).iterate_to_fixpoint();
+    let borrowed_locals_results = MaybeBorrowedLocals::all_borrows()
+        .into_engine(tcx, body_ref, def_id)
+        .pass_name("generator")
+        .iterate_to_fixpoint();
 
     let mut borrowed_locals_cursor =
         dataflow::ResultsCursor::new(body_ref, &borrowed_locals_results);
@@ -484,6 +486,7 @@ fn locals_live_across_suspend_points(
     // Calculate the liveness of MIR locals ignoring borrows.
     let mut liveness = MaybeLiveLocals
         .into_engine(tcx, body_ref, def_id)
+        .pass_name("generator")
         .iterate_to_fixpoint()
         .into_results_cursor(body_ref);
 
@@ -726,12 +729,12 @@ fn sanitize_witness<'tcx>(
     saved_locals: &GeneratorSavedLocals,
 ) {
     let allowed_upvars = tcx.erase_regions(upvars);
-    let allowed = match witness.kind {
+    let allowed = match witness.kind() {
         ty::GeneratorWitness(s) => tcx.erase_late_bound_regions(&s),
         _ => {
             tcx.sess.delay_span_bug(
                 body.span,
-                &format!("unexpected generator witness type {:?}", witness.kind),
+                &format!("unexpected generator witness type {:?}", witness.kind()),
             );
             return;
         }
@@ -1252,7 +1255,7 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
         let gen_ty = body.local_decls.raw[1].ty;
 
         // Get the interior types and substs which typeck computed
-        let (upvars, interior, discr_ty, movable) = match gen_ty.kind {
+        let (upvars, interior, discr_ty, movable) = match *gen_ty.kind() {
             ty::Generator(_, substs, movability) => {
                 let substs = substs.as_generator();
                 (
diff --git a/compiler/rustc_mir/src/transform/inline.rs b/compiler/rustc_mir/src/transform/inline.rs
index 315d4fa9d47..4e7cacc2f4a 100644
--- a/compiler/rustc_mir/src/transform/inline.rs
+++ b/compiler/rustc_mir/src/transform/inline.rs
@@ -4,7 +4,7 @@ use rustc_attr as attr;
 use rustc_hir::def_id::DefId;
 use rustc_index::bit_set::BitSet;
 use rustc_index::vec::{Idx, IndexVec};
-use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
+use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
 use rustc_middle::mir::visit::*;
 use rustc_middle::mir::*;
 use rustc_middle::ty::subst::{Subst, SubstsRef};
@@ -45,7 +45,8 @@ impl<'tcx> MirPass<'tcx> for Inline {
                 // based function.
                 debug!("function inlining is disabled when compiling with `instrument_coverage`");
             } else {
-                Inliner { tcx, source }.run_pass(body);
+                Inliner { tcx, source, codegen_fn_attrs: tcx.codegen_fn_attrs(source.def_id()) }
+                    .run_pass(body);
             }
         }
     }
@@ -54,6 +55,7 @@ impl<'tcx> MirPass<'tcx> for Inline {
 struct Inliner<'tcx> {
     tcx: TyCtxt<'tcx>,
     source: MirSource<'tcx>,
+    codegen_fn_attrs: &'tcx CodegenFnAttrs,
 }
 
 impl Inliner<'tcx> {
@@ -107,8 +109,14 @@ impl Inliner<'tcx> {
                     // Avoid a cycle here by only using `optimized_mir` only if we have
                     // a lower `HirId` than the callee. This ensures that the callee will
                     // not inline us. This trick only works without incremental compilation.
-                    // So don't do it if that is enabled.
-                    if !self.tcx.dep_graph.is_fully_enabled() && self_hir_id < callee_hir_id {
+                    // So don't do it if that is enabled. Also avoid inlining into generators,
+                    // since their `optimized_mir` is used for layout computation, which can
+                    // create a cycle, even when no attempt is made to inline the function
+                    // in the other direction.
+                    if !self.tcx.dep_graph.is_fully_enabled()
+                        && self_hir_id < callee_hir_id
+                        && caller_body.generator_kind.is_none()
+                    {
                         self.tcx.optimized_mir(callsite.callee)
                     } else {
                         continue;
@@ -191,7 +199,7 @@ impl Inliner<'tcx> {
         // Only consider direct calls to functions
         let terminator = bb_data.terminator();
         if let TerminatorKind::Call { func: ref op, .. } = terminator.kind {
-            if let ty::FnDef(callee_def_id, substs) = op.ty(caller_body, self.tcx).kind {
+            if let ty::FnDef(callee_def_id, substs) = *op.ty(caller_body, self.tcx).kind() {
                 let instance =
                     Instance::resolve(self.tcx, param_env, callee_def_id, substs).ok().flatten()?;
 
@@ -236,9 +244,19 @@ impl Inliner<'tcx> {
             return false;
         }
 
-        // Avoid inlining functions marked as no_sanitize if sanitizer is enabled,
-        // since instrumentation might be enabled and performed on the caller.
-        if self.tcx.sess.opts.debugging_opts.sanitizer.intersects(codegen_fn_attrs.no_sanitize) {
+        let self_features = &self.codegen_fn_attrs.target_features;
+        let callee_features = &codegen_fn_attrs.target_features;
+        if callee_features.iter().any(|feature| !self_features.contains(feature)) {
+            debug!("`callee has extra target features - not inlining");
+            return false;
+        }
+
+        let self_no_sanitize =
+            self.codegen_fn_attrs.no_sanitize & self.tcx.sess.opts.debugging_opts.sanitizer;
+        let callee_no_sanitize =
+            codegen_fn_attrs.no_sanitize & self.tcx.sess.opts.debugging_opts.sanitizer;
+        if self_no_sanitize != callee_no_sanitize {
+            debug!("`callee has incompatible no_sanitize attribute - not inlining");
             return false;
         }
 
@@ -336,7 +354,7 @@ impl Inliner<'tcx> {
                 }
 
                 TerminatorKind::Call { func: Operand::Constant(ref f), cleanup, .. } => {
-                    if let ty::FnDef(def_id, _) = f.literal.ty.kind {
+                    if let ty::FnDef(def_id, _) = *f.literal.ty.kind() {
                         // Don't give intrinsics the extra penalty for calls
                         let f = tcx.fn_sig(def_id);
                         if f.abi() == Abi::RustIntrinsic || f.abi() == Abi::PlatformIntrinsic {
@@ -488,7 +506,7 @@ impl Inliner<'tcx> {
                 let return_block = destination.1;
 
                 // Copy the arguments if needed.
-                let args: Vec<_> = self.make_call_args(args, &callsite, caller_body);
+                let args: Vec<_> = self.make_call_args(args, &callsite, caller_body, return_block);
 
                 let bb_len = caller_body.basic_blocks().len();
                 let mut integrator = Integrator {
@@ -535,6 +553,7 @@ impl Inliner<'tcx> {
         args: Vec<Operand<'tcx>>,
         callsite: &CallSite<'tcx>,
         caller_body: &mut Body<'tcx>,
+        return_block: BasicBlock,
     ) -> Vec<Local> {
         let tcx = self.tcx;
 
@@ -563,12 +582,22 @@ impl Inliner<'tcx> {
         // and the vector is `[closure_ref, tmp0, tmp1, tmp2]`.
         if tcx.is_closure(callsite.callee) {
             let mut args = args.into_iter();
-            let self_ = self.create_temp_if_necessary(args.next().unwrap(), callsite, caller_body);
-            let tuple = self.create_temp_if_necessary(args.next().unwrap(), callsite, caller_body);
+            let self_ = self.create_temp_if_necessary(
+                args.next().unwrap(),
+                callsite,
+                caller_body,
+                return_block,
+            );
+            let tuple = self.create_temp_if_necessary(
+                args.next().unwrap(),
+                callsite,
+                caller_body,
+                return_block,
+            );
             assert!(args.next().is_none());
 
             let tuple = Place::from(tuple);
-            let tuple_tys = if let ty::Tuple(s) = tuple.ty(caller_body, tcx).ty.kind {
+            let tuple_tys = if let ty::Tuple(s) = tuple.ty(caller_body, tcx).ty.kind() {
                 s
             } else {
                 bug!("Closure arguments are not passed as a tuple");
@@ -584,13 +613,13 @@ impl Inliner<'tcx> {
                     Operand::Move(tcx.mk_place_field(tuple, Field::new(i), ty.expect_ty()));
 
                 // Spill to a local to make e.g., `tmp0`.
-                self.create_temp_if_necessary(tuple_field, callsite, caller_body)
+                self.create_temp_if_necessary(tuple_field, callsite, caller_body, return_block)
             });
 
             closure_ref_arg.chain(tuple_tmp_args).collect()
         } else {
             args.into_iter()
-                .map(|a| self.create_temp_if_necessary(a, callsite, caller_body))
+                .map(|a| self.create_temp_if_necessary(a, callsite, caller_body, return_block))
                 .collect()
         }
     }
@@ -602,6 +631,7 @@ impl Inliner<'tcx> {
         arg: Operand<'tcx>,
         callsite: &CallSite<'tcx>,
         caller_body: &mut Body<'tcx>,
+        return_block: BasicBlock,
     ) -> Local {
         // FIXME: Analysis of the usage of the arguments to avoid
         // unnecessary temporaries.
@@ -624,11 +654,19 @@ impl Inliner<'tcx> {
         let arg_tmp = LocalDecl::new(ty, callsite.location.span);
         let arg_tmp = caller_body.local_decls.push(arg_tmp);
 
-        let stmt = Statement {
+        caller_body[callsite.bb].statements.push(Statement {
+            source_info: callsite.location,
+            kind: StatementKind::StorageLive(arg_tmp),
+        });
+        caller_body[callsite.bb].statements.push(Statement {
             source_info: callsite.location,
             kind: StatementKind::Assign(box (Place::from(arg_tmp), arg)),
-        };
-        caller_body[callsite.bb].statements.push(stmt);
+        });
+        caller_body[return_block].statements.insert(
+            0,
+            Statement { source_info: callsite.location, kind: StatementKind::StorageDead(arg_tmp) },
+        );
+
         arg_tmp
     }
 }
diff --git a/compiler/rustc_mir/src/transform/instcombine.rs b/compiler/rustc_mir/src/transform/instcombine.rs
index 7967137e01e..3ed0aea1404 100644
--- a/compiler/rustc_mir/src/transform/instcombine.rs
+++ b/compiler/rustc_mir/src/transform/instcombine.rs
@@ -4,9 +4,14 @@ use crate::transform::{MirPass, MirSource};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_hir::Mutability;
 use rustc_index::vec::Idx;
-use rustc_middle::mir::visit::{MutVisitor, Visitor};
 use rustc_middle::mir::{
-    Body, Constant, Local, Location, Operand, Place, PlaceRef, ProjectionElem, Rvalue,
+    visit::PlaceContext,
+    visit::{MutVisitor, Visitor},
+    Statement,
+};
+use rustc_middle::mir::{
+    BinOp, Body, BorrowKind, Constant, Local, Location, Operand, Place, PlaceRef, ProjectionElem,
+    Rvalue,
 };
 use rustc_middle::ty::{self, TyCtxt};
 use std::mem;
@@ -66,10 +71,41 @@ impl<'tcx> MutVisitor<'tcx> for InstCombineVisitor<'tcx> {
             *rvalue = Rvalue::Use(Operand::Constant(box constant));
         }
 
+        if let Some(operand) = self.optimizations.unneeded_equality_comparison.remove(&location) {
+            debug!("replacing {:?} with {:?}", rvalue, operand);
+            *rvalue = Rvalue::Use(operand);
+        }
+
+        if let Some(place) = self.optimizations.unneeded_deref.remove(&location) {
+            debug!("unneeded_deref: replacing {:?} with {:?}", rvalue, place);
+            *rvalue = Rvalue::Use(Operand::Copy(place));
+        }
+
         self.super_rvalue(rvalue, location)
     }
 }
 
+struct MutatingUseVisitor {
+    has_mutating_use: bool,
+    local_to_look_for: Local,
+}
+
+impl MutatingUseVisitor {
+    fn has_mutating_use_in_stmt(local: Local, stmt: &Statement<'tcx>, location: Location) -> bool {
+        let mut _self = Self { has_mutating_use: false, local_to_look_for: local };
+        _self.visit_statement(stmt, location);
+        _self.has_mutating_use
+    }
+}
+
+impl<'tcx> Visitor<'tcx> for MutatingUseVisitor {
+    fn visit_local(&mut self, local: &Local, context: PlaceContext, _: Location) {
+        if *local == self.local_to_look_for {
+            self.has_mutating_use |= context.is_mutating_use();
+        }
+    }
+}
+
 /// Finds optimization opportunities on the MIR.
 struct OptimizationFinder<'b, 'tcx> {
     body: &'b Body<'tcx>,
@@ -81,6 +117,127 @@ impl OptimizationFinder<'b, 'tcx> {
     fn new(body: &'b Body<'tcx>, tcx: TyCtxt<'tcx>) -> OptimizationFinder<'b, 'tcx> {
         OptimizationFinder { body, tcx, optimizations: OptimizationList::default() }
     }
+
+    fn find_deref_of_address(&mut self, rvalue: &Rvalue<'tcx>, location: Location) -> Option<()> {
+        // Look for the sequence
+        //
+        // _2 = &_1;
+        // ...
+        // _5 = (*_2);
+        //
+        // which we can replace the last statement with `_5 = _1;` to avoid the load of `_2`.
+        if let Rvalue::Use(op) = rvalue {
+            let local_being_derefed = match op.place()?.as_ref() {
+                PlaceRef { local, projection: [ProjectionElem::Deref] } => Some(local),
+                _ => None,
+            }?;
+
+            let stmt_index = location.statement_index;
+            // Look behind for statement that assigns the local from a address of operator.
+            // 6 is chosen as a heuristic determined by seeing the number of times
+            // the optimization kicked in compiling rust std.
+            let lower_index = stmt_index.saturating_sub(6);
+            let statements_to_look_in = self.body.basic_blocks()[location.block].statements
+                [lower_index..stmt_index]
+                .iter()
+                .rev();
+            for stmt in statements_to_look_in {
+                match &stmt.kind {
+                    // Exhaustive match on statements to detect conditions that warrant we bail out of the optimization.
+                    rustc_middle::mir::StatementKind::Assign(box (l, r))
+                        if l.local == local_being_derefed =>
+                    {
+                        match r {
+                            // Looking for immutable reference e.g _local_being_deref = &_1;
+                            Rvalue::Ref(
+                                _,
+                                // Only apply the optimization if it is an immutable borrow.
+                                BorrowKind::Shared,
+                                place_taken_address_of,
+                            ) => {
+                                self.optimizations
+                                    .unneeded_deref
+                                    .insert(location, *place_taken_address_of);
+                                return Some(());
+                            }
+
+                            // We found an assignment of `local_being_deref` that is not an immutable ref, e.g the following sequence
+                            // _2 = &_1;
+                            // _3 = &5
+                            // _2 = _3;  <-- this means it is no longer valid to replace the last statement with `_5 = _1;`
+                            // _5 = (*_2);
+                            _ => return None,
+                        }
+                    }
+
+                    // Inline asm can do anything, so bail out of the optimization.
+                    rustc_middle::mir::StatementKind::LlvmInlineAsm(_) => return None,
+
+                    // Check that `local_being_deref` is not being used in a mutating way which can cause misoptimization.
+                    rustc_middle::mir::StatementKind::Assign(box (_, _))
+                    | rustc_middle::mir::StatementKind::Coverage(_)
+                    | rustc_middle::mir::StatementKind::Nop
+                    | rustc_middle::mir::StatementKind::FakeRead(_, _)
+                    | rustc_middle::mir::StatementKind::StorageLive(_)
+                    | rustc_middle::mir::StatementKind::StorageDead(_)
+                    | rustc_middle::mir::StatementKind::Retag(_, _)
+                    | rustc_middle::mir::StatementKind::AscribeUserType(_, _)
+                    | rustc_middle::mir::StatementKind::SetDiscriminant { .. } => {
+                        if MutatingUseVisitor::has_mutating_use_in_stmt(
+                            local_being_derefed,
+                            stmt,
+                            location,
+                        ) {
+                            return None;
+                        }
+                    }
+                }
+            }
+        }
+        Some(())
+    }
+
+    fn find_unneeded_equality_comparison(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
+        // find Ne(_place, false) or Ne(false, _place)
+        // or   Eq(_place, true) or Eq(true, _place)
+        if let Rvalue::BinaryOp(op, l, r) = rvalue {
+            let const_to_find = if *op == BinOp::Ne {
+                false
+            } else if *op == BinOp::Eq {
+                true
+            } else {
+                return;
+            };
+            // (const, _place)
+            if let Some(o) = self.find_operand_in_equality_comparison_pattern(l, r, const_to_find) {
+                self.optimizations.unneeded_equality_comparison.insert(location, o.clone());
+            }
+            // (_place, const)
+            else if let Some(o) =
+                self.find_operand_in_equality_comparison_pattern(r, l, const_to_find)
+            {
+                self.optimizations.unneeded_equality_comparison.insert(location, o.clone());
+            }
+        }
+    }
+
+    fn find_operand_in_equality_comparison_pattern(
+        &self,
+        l: &Operand<'tcx>,
+        r: &'a Operand<'tcx>,
+        const_to_find: bool,
+    ) -> Option<&'a Operand<'tcx>> {
+        let const_ = l.constant()?;
+        if const_.literal.ty == self.tcx.types.bool
+            && const_.literal.val.try_to_bool() == Some(const_to_find)
+        {
+            if r.place().is_some() {
+                return Some(r);
+            }
+        }
+
+        None
+    }
 }
 
 impl Visitor<'tcx> for OptimizationFinder<'b, 'tcx> {
@@ -91,7 +248,7 @@ impl Visitor<'tcx> for OptimizationFinder<'b, 'tcx> {
             {
                 // The dereferenced place must have type `&_`.
                 let ty = Place::ty_from(local, proj_base, self.body, self.tcx).ty;
-                if let ty::Ref(_, _, Mutability::Not) = ty.kind {
+                if let ty::Ref(_, _, Mutability::Not) = ty.kind() {
                     self.optimizations.and_stars.insert(location);
                 }
             }
@@ -99,13 +256,17 @@ impl Visitor<'tcx> for OptimizationFinder<'b, 'tcx> {
 
         if let Rvalue::Len(ref place) = *rvalue {
             let place_ty = place.ty(&self.body.local_decls, self.tcx).ty;
-            if let ty::Array(_, len) = place_ty.kind {
+            if let ty::Array(_, len) = place_ty.kind() {
                 let span = self.body.source_info(location).span;
                 let constant = Constant { span, literal: len, user_ty: None };
                 self.optimizations.arrays_lengths.insert(location, constant);
             }
         }
 
+        let _ = self.find_deref_of_address(rvalue, location);
+
+        self.find_unneeded_equality_comparison(rvalue, location);
+
         self.super_rvalue(rvalue, location)
     }
 }
@@ -114,4 +275,6 @@ impl Visitor<'tcx> for OptimizationFinder<'b, 'tcx> {
 struct OptimizationList<'tcx> {
     and_stars: FxHashSet<Location>,
     arrays_lengths: FxHashMap<Location, Constant<'tcx>>,
+    unneeded_equality_comparison: FxHashMap<Location, Operand<'tcx>>,
+    unneeded_deref: FxHashMap<Location, Place<'tcx>>,
 }
diff --git a/compiler/rustc_mir/src/transform/instrument_coverage.rs b/compiler/rustc_mir/src/transform/instrument_coverage.rs
index f60e6da714a..a5b30a25a9b 100644
--- a/compiler/rustc_mir/src/transform/instrument_coverage.rs
+++ b/compiler/rustc_mir/src/transform/instrument_coverage.rs
@@ -1,23 +1,34 @@
 use crate::transform::{MirPass, MirSource};
+use crate::util::pretty;
+use crate::util::spanview::{
+    source_range_no_file, statement_kind_name, terminator_kind_name, write_spanview_document,
+    SpanViewable, TOOLTIP_INDENT,
+};
+
 use rustc_data_structures::fingerprint::Fingerprint;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
+use rustc_index::bit_set::BitSet;
 use rustc_middle::hir;
 use rustc_middle::ich::StableHashingContext;
 use rustc_middle::mir;
 use rustc_middle::mir::coverage::*;
 use rustc_middle::mir::visit::Visitor;
-use rustc_middle::mir::{BasicBlock, Coverage, CoverageInfo, Location, Statement, StatementKind};
+use rustc_middle::mir::{
+    BasicBlock, BasicBlockData, Coverage, CoverageInfo, Location, Statement, StatementKind,
+    TerminatorKind,
+};
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::TyCtxt;
 use rustc_span::def_id::DefId;
 use rustc_span::{FileName, Pos, RealFileName, Span, Symbol};
 
-/// Inserts call to count_code_region() as a placeholder to be replaced during code generation with
-/// the intrinsic llvm.instrprof.increment.
+/// Inserts `StatementKind::Coverage` statements that either instrument the binary with injected
+/// counters, via intrinsic `llvm.instrprof.increment`, and/or inject metadata used during codegen
+/// to construct the coverage map.
 pub struct InstrumentCoverage;
 
-/// The `query` provider for `CoverageInfo`, requested by `codegen_intrinsic_call()` when
-/// constructing the arguments for `llvm.instrprof.increment`.
+/// The `query` provider for `CoverageInfo`, requested by `codegen_coverage()` (to inject each
+/// counter) and `FunctionCoverage::new()` (to extract the coverage map metadata from the MIR).
 pub(crate) fn provide(providers: &mut Providers) {
     providers.coverageinfo = |tcx, def_id| coverageinfo_from_mir(tcx, def_id);
 }
@@ -43,8 +54,8 @@ impl Visitor<'_> for CoverageVisitor {
     }
 }
 
-fn coverageinfo_from_mir<'tcx>(tcx: TyCtxt<'tcx>, mir_def_id: DefId) -> CoverageInfo {
-    let mir_body = tcx.optimized_mir(mir_def_id);
+fn coverageinfo_from_mir<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> CoverageInfo {
+    let mir_body = tcx.optimized_mir(def_id);
 
     // The `num_counters` argument to `llvm.instrprof.increment` is the number of injected
     // counters, with each counter having a counter ID from `0..num_counters-1`. MIR optimization
@@ -63,18 +74,30 @@ fn coverageinfo_from_mir<'tcx>(tcx: TyCtxt<'tcx>, mir_def_id: DefId) -> Coverage
 }
 
 impl<'tcx> MirPass<'tcx> for InstrumentCoverage {
-    fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, mir_body: &mut mir::Body<'tcx>) {
+    fn run_pass(
+        &self,
+        tcx: TyCtxt<'tcx>,
+        mir_source: MirSource<'tcx>,
+        mir_body: &mut mir::Body<'tcx>,
+    ) {
         // If the InstrumentCoverage pass is called on promoted MIRs, skip them.
         // See: https://github.com/rust-lang/rust/pull/73011#discussion_r438317601
-        if src.promoted.is_none() {
-            Instrumentor::new(tcx, src, mir_body).inject_counters();
+        if mir_source.promoted.is_none() {
+            Instrumentor::new(&self.name(), tcx, mir_source, mir_body).inject_counters();
         }
     }
 }
 
+#[derive(Clone)]
+struct CoverageRegion {
+    pub span: Span,
+    pub blocks: Vec<BasicBlock>,
+}
+
 struct Instrumentor<'a, 'tcx> {
+    pass_name: &'a str,
     tcx: TyCtxt<'tcx>,
-    mir_def_id: DefId,
+    mir_source: MirSource<'tcx>,
     mir_body: &'a mut mir::Body<'tcx>,
     hir_body: &'tcx rustc_hir::Body<'tcx>,
     function_source_hash: Option<u64>,
@@ -83,12 +106,17 @@ struct Instrumentor<'a, 'tcx> {
 }
 
 impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
-    fn new(tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, mir_body: &'a mut mir::Body<'tcx>) -> Self {
-        let mir_def_id = src.def_id();
-        let hir_body = hir_body(tcx, mir_def_id);
+    fn new(
+        pass_name: &'a str,
+        tcx: TyCtxt<'tcx>,
+        mir_source: MirSource<'tcx>,
+        mir_body: &'a mut mir::Body<'tcx>,
+    ) -> Self {
+        let hir_body = hir_body(tcx, mir_source.def_id());
         Self {
+            pass_name,
             tcx,
-            mir_def_id,
+            mir_source,
             mir_body,
             hir_body,
             function_source_hash: None,
@@ -127,19 +155,100 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
     }
 
     fn inject_counters(&mut self) {
+        let tcx = self.tcx;
+        let def_id = self.mir_source.def_id();
+        let mir_body = &self.mir_body;
         let body_span = self.hir_body.value.span;
-        debug!("instrumenting {:?}, span: {:?}", self.mir_def_id, body_span);
-
-        // FIXME(richkadel): As a first step, counters are only injected at the top of each
-        // function. The complete solution will inject counters at each conditional code branch.
-        let block = rustc_middle::mir::START_BLOCK;
-        let counter = self.make_counter();
-        self.inject_statement(counter, body_span, block);
-
-        // FIXME(richkadel): The next step to implement source based coverage analysis will be
-        // instrumenting branches within functions, and some regions will be counted by "counter
-        // expression". The function to inject counter expression is implemented. Replace this
-        // "fake use" with real use.
+        debug!(
+            "instrumenting {:?}, span: {}",
+            def_id,
+            tcx.sess.source_map().span_to_string(body_span)
+        );
+
+        if !tcx.sess.opts.debugging_opts.experimental_coverage {
+            // Coverage at the function level should be accurate. This is the default implementation
+            // if `-Z experimental-coverage` is *NOT* enabled.
+            let block = rustc_middle::mir::START_BLOCK;
+            let counter = self.make_counter();
+            self.inject_statement(counter, body_span, block);
+            return;
+        }
+        // FIXME(richkadel): else if `-Z experimental-coverage` *IS* enabled: Efforts are still in
+        // progress to identify the correct code region spans and associated counters to generate
+        // accurate Rust coverage reports.
+
+        let block_span = |data: &BasicBlockData<'tcx>| {
+            // The default span will be the `Terminator` span; but until we have a smarter solution,
+            // the coverage region also incorporates at least the statements in this BasicBlock as
+            // well. Extend the span to encompass all, if possible.
+            // FIXME(richkadel): Assuming the terminator's span is already known to be contained in `body_span`.
+            let mut span = data.terminator().source_info.span;
+            // FIXME(richkadel): It's looking unlikely that we should compute a span from MIR
+            // spans, but if we do keep something like this logic, we will need a smarter way
+            // to combine `Statement`s and/or `Terminator`s with `Span`s from different
+            // files.
+            for statement_span in data.statements.iter().map(|statement| statement.source_info.span)
+            {
+                // Only combine Spans from the function's body_span.
+                if body_span.contains(statement_span) {
+                    span = span.to(statement_span);
+                }
+            }
+            span
+        };
+
+        // Traverse the CFG but ignore anything following an `unwind`
+        let cfg_without_unwind = ShortCircuitPreorder::new(mir_body, |term_kind| {
+            let mut successors = term_kind.successors();
+            match &term_kind {
+                // SwitchInt successors are never unwind, and all of them should be traversed
+                TerminatorKind::SwitchInt { .. } => successors,
+                // For all other kinds, return only the first successor, if any, and ignore unwinds
+                _ => successors.next().into_iter().chain(&[]),
+            }
+        });
+
+        let mut coverage_regions = Vec::with_capacity(cfg_without_unwind.size_hint().0);
+        for (bb, data) in cfg_without_unwind {
+            if !body_span.contains(data.terminator().source_info.span) {
+                continue;
+            }
+
+            // FIXME(richkadel): Regions will soon contain multiple blocks.
+            let mut blocks = Vec::new();
+            blocks.push(bb);
+            let span = block_span(data);
+            coverage_regions.push(CoverageRegion { span, blocks });
+        }
+
+        let span_viewables = if pretty::dump_enabled(tcx, self.pass_name, def_id) {
+            Some(self.span_viewables(&coverage_regions))
+        } else {
+            None
+        };
+
+        // Inject counters for the selected spans
+        for CoverageRegion { span, blocks } in coverage_regions {
+            debug!(
+                "Injecting counter at: {:?}:\n{}\n==========",
+                span,
+                tcx.sess.source_map().span_to_snippet(span).expect("Error getting source for span"),
+            );
+            let counter = self.make_counter();
+            self.inject_statement(counter, span, blocks[0]);
+        }
+
+        if let Some(span_viewables) = span_viewables {
+            let mut file =
+                pretty::create_dump_file(tcx, "html", None, self.pass_name, &0, self.mir_source)
+                    .expect("Unexpected error creating MIR spanview HTML file");
+            write_spanview_document(tcx, def_id, span_viewables, &mut file)
+                .expect("Unexpected IO error dumping coverage spans as HTML");
+        }
+
+        // FIXME(richkadel): Some regions will be counted by "counter expression". Counter
+        // expressions are supported, but are not yet generated. When they are, remove this `fake_use`
+        // block.
         let fake_use = false;
         if fake_use {
             let add = false;
@@ -193,6 +302,83 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
         };
         data.statements.push(statement);
     }
+
+    /// Converts the computed `CoverageRegion`s into `SpanViewable`s.
+    fn span_viewables(&self, coverage_regions: &Vec<CoverageRegion>) -> Vec<SpanViewable> {
+        let mut span_viewables = Vec::new();
+        for coverage_region in coverage_regions {
+            span_viewables.push(SpanViewable {
+                span: coverage_region.span,
+                id: format!("{}", coverage_region.blocks[0].index()),
+                tooltip: self.make_tooltip_text(coverage_region),
+            });
+        }
+        span_viewables
+    }
+
+    /// A custom tooltip renderer used in a spanview HTML+CSS document used for coverage analysis.
+    fn make_tooltip_text(&self, coverage_region: &CoverageRegion) -> String {
+        const INCLUDE_COVERAGE_STATEMENTS: bool = false;
+        let tcx = self.tcx;
+        let source_map = tcx.sess.source_map();
+        let mut text = Vec::new();
+        for (i, &bb) in coverage_region.blocks.iter().enumerate() {
+            if i > 0 {
+                text.push("\n".to_owned());
+            }
+            text.push(format!("{:?}: {}:", bb, &source_map.span_to_string(coverage_region.span)));
+            let data = &self.mir_body.basic_blocks()[bb];
+            for statement in &data.statements {
+                let statement_string = match statement.kind {
+                    StatementKind::Coverage(box ref coverage) => match coverage.kind {
+                        CoverageKind::Counter { id, .. } => {
+                            if !INCLUDE_COVERAGE_STATEMENTS {
+                                continue;
+                            }
+                            format!("increment counter #{}", id.index())
+                        }
+                        CoverageKind::Expression { id, lhs, op, rhs } => {
+                            if !INCLUDE_COVERAGE_STATEMENTS {
+                                continue;
+                            }
+                            format!(
+                                "expression #{} = {} {} {}",
+                                id.index(),
+                                lhs.index(),
+                                if op == Op::Add { "+" } else { "-" },
+                                rhs.index()
+                            )
+                        }
+                        CoverageKind::Unreachable => {
+                            if !INCLUDE_COVERAGE_STATEMENTS {
+                                continue;
+                            }
+                            String::from("unreachable")
+                        }
+                    },
+                    _ => format!("{:?}", statement),
+                };
+                let source_range = source_range_no_file(tcx, &statement.source_info.span);
+                text.push(format!(
+                    "\n{}{}: {}: {}",
+                    TOOLTIP_INDENT,
+                    source_range,
+                    statement_kind_name(statement),
+                    statement_string
+                ));
+            }
+            let term = data.terminator();
+            let source_range = source_range_no_file(tcx, &term.source_info.span);
+            text.push(format!(
+                "\n{}{}: {}: {:?}",
+                TOOLTIP_INDENT,
+                source_range,
+                terminator_kind_name(term),
+                term.kind
+            ));
+        }
+        text.join("")
+    }
 }
 
 /// Convert the Span into its file name, start line and column, and end line and column
@@ -227,7 +413,7 @@ fn make_code_region<'tcx>(tcx: TyCtxt<'tcx>, span: &Span) -> CodeRegion {
 }
 
 fn hir_body<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx rustc_hir::Body<'tcx> {
-    let hir_node = tcx.hir().get_if_local(def_id).expect("DefId is local");
+    let hir_node = tcx.hir().get_if_local(def_id).expect("expected DefId is local");
     let fn_body_id = hir::map::associated_body(hir_node).expect("HIR node is a function with body");
     tcx.hir().body(fn_body_id)
 }
@@ -245,3 +431,61 @@ fn hash(
     node.hash_stable(hcx, &mut stable_hasher);
     stable_hasher.finish()
 }
+
+pub struct ShortCircuitPreorder<
+    'a,
+    'tcx,
+    F: Fn(&'tcx TerminatorKind<'tcx>) -> mir::Successors<'tcx>,
+> {
+    body: &'a mir::Body<'tcx>,
+    visited: BitSet<BasicBlock>,
+    worklist: Vec<BasicBlock>,
+    filtered_successors: F,
+}
+
+impl<'a, 'tcx, F: Fn(&'tcx TerminatorKind<'tcx>) -> mir::Successors<'tcx>>
+    ShortCircuitPreorder<'a, 'tcx, F>
+{
+    pub fn new(
+        body: &'a mir::Body<'tcx>,
+        filtered_successors: F,
+    ) -> ShortCircuitPreorder<'a, 'tcx, F> {
+        let worklist = vec![mir::START_BLOCK];
+
+        ShortCircuitPreorder {
+            body,
+            visited: BitSet::new_empty(body.basic_blocks().len()),
+            worklist,
+            filtered_successors,
+        }
+    }
+}
+
+impl<'a: 'tcx, 'tcx, F: Fn(&'tcx TerminatorKind<'tcx>) -> mir::Successors<'tcx>> Iterator
+    for ShortCircuitPreorder<'a, 'tcx, F>
+{
+    type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
+
+    fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> {
+        while let Some(idx) = self.worklist.pop() {
+            if !self.visited.insert(idx) {
+                continue;
+            }
+
+            let data = &self.body[idx];
+
+            if let Some(ref term) = data.terminator {
+                self.worklist.extend((self.filtered_successors)(&term.kind));
+            }
+
+            return Some((idx, data));
+        }
+
+        None
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        let size = self.body.basic_blocks().len() - self.visited.count();
+        (size, Some(size))
+    }
+}
diff --git a/compiler/rustc_mir/src/transform/mod.rs b/compiler/rustc_mir/src/transform/mod.rs
index c3a34756122..f48ad039b4f 100644
--- a/compiler/rustc_mir/src/transform/mod.rs
+++ b/compiler/rustc_mir/src/transform/mod.rs
@@ -16,6 +16,7 @@ use std::borrow::Cow;
 pub mod add_call_guards;
 pub mod add_moves_for_packed_drops;
 pub mod add_retag;
+pub mod check_const_item_mutation;
 pub mod check_consts;
 pub mod check_packed_ref;
 pub mod check_unsafety;
@@ -23,7 +24,9 @@ pub mod cleanup_post_borrowck;
 pub mod const_prop;
 pub mod copy_prop;
 pub mod deaggregator;
+pub mod dest_prop;
 pub mod dump_mir;
+pub mod early_otherwise_branch;
 pub mod elaborate_drops;
 pub mod generator;
 pub mod inline;
@@ -35,6 +38,7 @@ pub mod nrvo;
 pub mod promote_consts;
 pub mod qualify_min_const_fn;
 pub mod remove_noop_landing_pads;
+pub mod remove_unneeded_drops;
 pub mod required_consts;
 pub mod rustc_peek;
 pub mod simplify;
@@ -307,6 +311,7 @@ fn mir_const<'tcx>(
         &[&[
             // MIR-level lints.
             &check_packed_ref::CheckPackedRef,
+            &check_const_item_mutation::CheckConstItemMutation,
             // What we need to do constant evaluation.
             &simplify::SimplifyCfg::new("initial"),
             &rustc_peek::SanityCheck,
@@ -327,7 +332,11 @@ fn mir_promoted(
     // this point, before we steal the mir-const result.
     // Also this means promotion can rely on all const checks having been done.
     let _ = tcx.mir_const_qualif_opt_const_arg(def);
-
+    let _ = if let Some(param_did) = def.const_param_did {
+        tcx.mir_abstract_const_of_const_arg((def.did, param_did))
+    } else {
+        tcx.mir_abstract_const(def.did.to_def_id())
+    };
     let mut body = tcx.mir_const(def).steal();
 
     let mut required_consts = Vec::new();
@@ -453,13 +462,17 @@ fn run_optimization_passes<'tcx>(
 
     // The main optimizations that we do on MIR.
     let optimizations: &[&dyn MirPass<'tcx>] = &[
-        &instcombine::InstCombine,
+        &remove_unneeded_drops::RemoveUnneededDrops,
         &match_branches::MatchBranchSimplification,
+        // inst combine is after MatchBranchSimplification to clean up Ne(_1, false)
+        &instcombine::InstCombine,
         &const_prop::ConstProp,
         &simplify_branches::SimplifyBranches::new("after-const-prop"),
+        &early_otherwise_branch::EarlyOtherwiseBranch,
         &simplify_comparison_integral::SimplifyComparisonIntegral,
         &simplify_try::SimplifyArmIdentity,
         &simplify_try::SimplifyBranchSame,
+        &dest_prop::DestinationPropagation,
         &copy_prop::CopyPropagation,
         &simplify_branches::SimplifyBranches::new("after-copy-prop"),
         &remove_noop_landing_pads::RemoveNoopLandingPads,
diff --git a/compiler/rustc_mir/src/transform/nrvo.rs b/compiler/rustc_mir/src/transform/nrvo.rs
index 1f3d7bb7cc6..1ffb5a87c47 100644
--- a/compiler/rustc_mir/src/transform/nrvo.rs
+++ b/compiler/rustc_mir/src/transform/nrvo.rs
@@ -1,6 +1,6 @@
 use rustc_hir::Mutability;
 use rustc_index::bit_set::HybridBitSet;
-use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor};
+use rustc_middle::mir::visit::{MutVisitor, NonUseContext, PlaceContext, Visitor};
 use rustc_middle::mir::{self, BasicBlock, Local, Location};
 use rustc_middle::ty::TyCtxt;
 
@@ -36,6 +36,12 @@ impl<'tcx> MirPass<'tcx> for RenameReturnPlace {
             return;
         }
 
+        if tcx.sess.opts.debugging_opts.mir_opt_level >= 2 {
+            // The `DestinationPropagation` pass runs at level 2, so this pass is redundant (and
+            // fails some asserts).
+            return;
+        }
+
         let returned_local = match local_eligible_for_nrvo(body) {
             Some(l) => l,
             None => {
@@ -196,9 +202,10 @@ impl MutVisitor<'tcx> for RenameToReturnPlace<'tcx> {
         self.super_terminator(terminator, loc);
     }
 
-    fn visit_local(&mut self, l: &mut Local, _: PlaceContext, _: Location) {
-        assert_ne!(*l, mir::RETURN_PLACE);
-        if *l == self.to_rename {
+    fn visit_local(&mut self, l: &mut Local, ctxt: PlaceContext, _: Location) {
+        if *l == mir::RETURN_PLACE {
+            assert_eq!(ctxt, PlaceContext::NonUse(NonUseContext::VarDebugInfo));
+        } else if *l == self.to_rename {
             *l = mir::RETURN_PLACE;
         }
     }
diff --git a/compiler/rustc_mir/src/transform/promote_consts.rs b/compiler/rustc_mir/src/transform/promote_consts.rs
index b2dda1caa54..cd361e430fa 100644
--- a/compiler/rustc_mir/src/transform/promote_consts.rs
+++ b/compiler/rustc_mir/src/transform/promote_consts.rs
@@ -92,7 +92,7 @@ pub enum TempState {
 impl TempState {
     pub fn is_promotable(&self) -> bool {
         debug!("is_promotable: self={:?}", self);
-        if let TempState::Defined { .. } = *self { true } else { false }
+        matches!(self, TempState::Defined { .. } )
     }
 }
 
@@ -220,7 +220,7 @@ impl<'tcx> Visitor<'tcx> for Collector<'_, 'tcx> {
 
         match terminator.kind {
             TerminatorKind::Call { ref func, .. } => {
-                if let ty::FnDef(def_id, _) = func.ty(self.ccx.body, self.ccx.tcx).kind {
+                if let ty::FnDef(def_id, _) = *func.ty(self.ccx.body, self.ccx.tcx).kind() {
                     let fn_sig = self.ccx.tcx.fn_sig(def_id);
                     if let Abi::RustIntrinsic | Abi::PlatformIntrinsic = fn_sig.abi() {
                         let name = self.ccx.tcx.item_name(def_id);
@@ -242,11 +242,8 @@ impl<'tcx> Visitor<'tcx> for Collector<'_, 'tcx> {
             }
             TerminatorKind::InlineAsm { ref operands, .. } => {
                 for (index, op) in operands.iter().enumerate() {
-                    match op {
-                        InlineAsmOperand::Const { .. } => {
-                            self.candidates.push(Candidate::InlineAsm { bb: location.block, index })
-                        }
-                        _ => {}
+                    if let InlineAsmOperand::Const { .. } = op {
+                        self.candidates.push(Candidate::InlineAsm { bb: location.block, index })
                     }
                 }
             }
@@ -297,6 +294,17 @@ impl std::ops::Deref for Validator<'a, 'tcx> {
 struct Unpromotable;
 
 impl<'tcx> Validator<'_, 'tcx> {
+    /// Determines if this code could be executed at runtime and thus is subject to codegen.
+    /// That means even unused constants need to be evaluated.
+    ///
+    /// `const_kind` should not be used in this file other than through this method!
+    fn maybe_runtime(&self) -> bool {
+        match self.const_kind {
+            None | Some(hir::ConstContext::ConstFn) => true,
+            Some(hir::ConstContext::Static(_) | hir::ConstContext::Const) => false,
+        }
+    }
+
     fn validate_candidate(&self, candidate: Candidate) -> Result<(), Unpromotable> {
         match candidate {
             Candidate::Ref(loc) => {
@@ -363,20 +371,10 @@ impl<'tcx> Validator<'_, 'tcx> {
 
                             // In theory, any zero-sized value could be borrowed
                             // mutably without consequences. However, only &mut []
-                            // is allowed right now, and only in functions.
-                            if self.const_kind
-                                == Some(hir::ConstContext::Static(hir::Mutability::Mut))
-                            {
-                                // Inside a `static mut`, &mut [...] is also allowed.
-                                match ty.kind {
-                                    ty::Array(..) | ty::Slice(_) => {}
-                                    _ => return Err(Unpromotable),
-                                }
-                            } else if let ty::Array(_, len) = ty.kind {
-                                // FIXME(eddyb) the `self.is_non_const_fn` condition
-                                // seems unnecessary, given that this is merely a ZST.
+                            // is allowed right now.
+                            if let ty::Array(_, len) = ty.kind() {
                                 match len.try_eval_usize(self.tcx, self.param_env) {
-                                    Some(0) if self.const_kind.is_none() => {}
+                                    Some(0) => {}
                                     _ => return Err(Unpromotable),
                                 }
                             } else {
@@ -503,9 +501,10 @@ impl<'tcx> Validator<'_, 'tcx> {
         match place {
             PlaceRef { local, projection: [] } => self.validate_local(local),
             PlaceRef { local, projection: [proj_base @ .., elem] } => {
+                // Validate topmost projection, then recurse.
                 match *elem {
                     ProjectionElem::Deref => {
-                        let mut not_promotable = true;
+                        let mut promotable = false;
                         // This is a special treatment for cases like *&STATIC where STATIC is a
                         // global static variable.
                         // This pattern is generated only when global static variables are directly
@@ -520,6 +519,9 @@ impl<'tcx> Validator<'_, 'tcx> {
                             }) = def_stmt
                             {
                                 if let Some(did) = c.check_static_ptr(self.tcx) {
+                                    // Evaluating a promoted may not read statics except if it got
+                                    // promoted from a static (this is a CTFE check). So we
+                                    // can only promote static accesses inside statics.
                                     if let Some(hir::ConstContext::Static(..)) = self.const_kind {
                                         // The `is_empty` predicate is introduced to exclude the case
                                         // where the projection operations are [ .field, * ].
@@ -532,13 +534,13 @@ impl<'tcx> Validator<'_, 'tcx> {
                                         if proj_base.is_empty()
                                             && !self.tcx.is_thread_local_static(did)
                                         {
-                                            not_promotable = false;
+                                            promotable = true;
                                         }
                                     }
                                 }
                             }
                         }
-                        if not_promotable {
+                        if !promotable {
                             return Err(Unpromotable);
                         }
                     }
@@ -553,7 +555,7 @@ impl<'tcx> Validator<'_, 'tcx> {
                     }
 
                     ProjectionElem::Field(..) => {
-                        if self.const_kind.is_none() {
+                        if self.maybe_runtime() {
                             let base_ty =
                                 Place::ty_from(place.local, proj_base, self.body, self.tcx).ty;
                             if let Some(def) = base_ty.ty_adt_def() {
@@ -581,6 +583,10 @@ impl<'tcx> Validator<'_, 'tcx> {
                 if let Some(def_id) = c.check_static_ptr(self.tcx) {
                     // Only allow statics (not consts) to refer to other statics.
                     // FIXME(eddyb) does this matter at all for promotion?
+                    // FIXME(RalfJung) it makes little sense to not promote this in `fn`/`const fn`,
+                    // and in `const` this cannot occur anyway. The only concern is that we might
+                    // promote even `let x = &STATIC` which would be useless, but this applies to
+                    // promotion inside statics as well.
                     let is_static = matches!(self.const_kind, Some(hir::ConstContext::Static(_)));
                     if !is_static {
                         return Err(Unpromotable);
@@ -599,21 +605,18 @@ impl<'tcx> Validator<'_, 'tcx> {
 
     fn validate_rvalue(&self, rvalue: &Rvalue<'tcx>) -> Result<(), Unpromotable> {
         match *rvalue {
-            Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) if self.const_kind.is_none() => {
+            Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => {
                 let operand_ty = operand.ty(self.body, self.tcx);
                 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
                 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
-                match (cast_in, cast_out) {
-                    (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => {
-                        // in normal functions, mark such casts as not promotable
-                        return Err(Unpromotable);
-                    }
-                    _ => {}
+                if let (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) = (cast_in, cast_out) {
+                    // ptr-to-int casts are not possible in consts and thus not promotable
+                    return Err(Unpromotable);
                 }
             }
 
-            Rvalue::BinaryOp(op, ref lhs, _) if self.const_kind.is_none() => {
-                if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).kind {
+            Rvalue::BinaryOp(op, ref lhs, _) => {
+                if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).kind() {
                     assert!(
                         op == BinOp::Eq
                             || op == BinOp::Ne
@@ -624,13 +627,14 @@ impl<'tcx> Validator<'_, 'tcx> {
                             || op == BinOp::Offset
                     );
 
-                    // raw pointer operations are not allowed inside promoteds
+                    // raw pointer operations are not allowed inside consts and thus not promotable
                     return Err(Unpromotable);
                 }
             }
 
             Rvalue::NullaryOp(NullOp::Box, _) => return Err(Unpromotable),
 
+            // FIXME(RalfJung): the rest is *implicitly considered promotable*... that seems dangerous.
             _ => {}
         }
 
@@ -652,11 +656,11 @@ impl<'tcx> Validator<'_, 'tcx> {
             }
 
             Rvalue::AddressOf(_, place) => {
-                // Raw reborrows can come from reference to pointer coercions,
-                // so are allowed.
+                // We accept `&raw *`, i.e., raw reborrows -- creating a raw pointer is
+                // no problem, only using it is.
                 if let [proj_base @ .., ProjectionElem::Deref] = place.projection.as_ref() {
                     let base_ty = Place::ty_from(place.local, proj_base, self.body, self.tcx).ty;
-                    if let ty::Ref(..) = base_ty.kind {
+                    if let ty::Ref(..) = base_ty.kind() {
                         return self.validate_place(PlaceRef {
                             local: place.local,
                             projection: proj_base,
@@ -672,18 +676,10 @@ impl<'tcx> Validator<'_, 'tcx> {
 
                     // In theory, any zero-sized value could be borrowed
                     // mutably without consequences. However, only &mut []
-                    // is allowed right now, and only in functions.
-                    if self.const_kind == Some(hir::ConstContext::Static(hir::Mutability::Mut)) {
-                        // Inside a `static mut`, &mut [...] is also allowed.
-                        match ty.kind {
-                            ty::Array(..) | ty::Slice(_) => {}
-                            _ => return Err(Unpromotable),
-                        }
-                    } else if let ty::Array(_, len) = ty.kind {
-                        // FIXME(eddyb): We only return `Unpromotable` for `&mut []` inside a
-                        // const context which seems unnecessary given that this is merely a ZST.
+                    // is allowed right now.
+                    if let ty::Array(_, len) = ty.kind() {
                         match len.try_eval_usize(self.tcx, self.param_env) {
-                            Some(0) if self.const_kind.is_none() => {}
+                            Some(0) => {}
                             _ => return Err(Unpromotable),
                         }
                     } else {
@@ -695,7 +691,7 @@ impl<'tcx> Validator<'_, 'tcx> {
                 let mut place = place.as_ref();
                 if let [proj_base @ .., ProjectionElem::Deref] = &place.projection {
                     let base_ty = Place::ty_from(place.local, proj_base, self.body, self.tcx).ty;
-                    if let ty::Ref(..) = base_ty.kind {
+                    if let ty::Ref(..) = base_ty.kind() {
                         place = PlaceRef { local: place.local, projection: proj_base };
                     }
                 }
@@ -748,8 +744,8 @@ impl<'tcx> Validator<'_, 'tcx> {
     ) -> Result<(), Unpromotable> {
         let fn_ty = callee.ty(self.body, self.tcx);
 
-        if !self.explicit && self.const_kind.is_none() {
-            if let ty::FnDef(def_id, _) = fn_ty.kind {
+        if !self.explicit && self.maybe_runtime() {
+            if let ty::FnDef(def_id, _) = *fn_ty.kind() {
                 // Never promote runtime `const fn` calls of
                 // functions without `#[rustc_promotable]`.
                 if !self.tcx.is_promotable_const_fn(def_id) {
@@ -758,7 +754,7 @@ impl<'tcx> Validator<'_, 'tcx> {
             }
         }
 
-        let is_const_fn = match fn_ty.kind {
+        let is_const_fn = match *fn_ty.kind() {
             ty::FnDef(def_id, _) => {
                 is_const_fn(self.tcx, def_id)
                     || is_unstable_const_fn(self.tcx, def_id).is_some()
diff --git a/compiler/rustc_mir/src/transform/qualify_min_const_fn.rs b/compiler/rustc_mir/src/transform/qualify_min_const_fn.rs
index 26db4600a2b..f15a7f7c2c8 100644
--- a/compiler/rustc_mir/src/transform/qualify_min_const_fn.rs
+++ b/compiler/rustc_mir/src/transform/qualify_min_const_fn.rs
@@ -1,4 +1,3 @@
-use rustc_attr as attr;
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_middle::mir::*;
@@ -30,7 +29,8 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>) -
                 | ty::PredicateAtom::WellFormed(_)
                 | ty::PredicateAtom::Projection(_)
                 | ty::PredicateAtom::ConstEvaluatable(..)
-                | ty::PredicateAtom::ConstEquate(..) => continue,
+                | ty::PredicateAtom::ConstEquate(..)
+                | ty::PredicateAtom::TypeWellFormedFromEnv(..) => continue,
                 ty::PredicateAtom::ObjectSafe(_) => {
                     bug!("object safe predicate on function: {:#?}", predicate)
                 }
@@ -44,7 +44,7 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>) -
                     if Some(pred.def_id()) == tcx.lang_items().sized_trait() {
                         continue;
                     }
-                    match pred.self_ty().kind {
+                    match pred.self_ty().kind() {
                         ty::Param(ref p) => {
                             // Allow `T: ?const Trait`
                             if constness == hir::Constness::NotConst
@@ -106,7 +106,7 @@ fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span, fn_def_id: DefId) -> Mc
             GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => continue,
         };
 
-        match ty.kind {
+        match ty.kind() {
             ty::Ref(_, _, hir::Mutability::Mut) => {
                 if !feature_allowed(tcx, fn_def_id, sym::const_mut_refs) {
                     return Err((span, "mutable references in const fn are unstable".into()));
@@ -203,7 +203,7 @@ fn check_rvalue(
                 ));
             };
             let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id));
-            if let ty::Slice(_) | ty::Str = unsized_ty.kind {
+            if let ty::Slice(_) | ty::Str = unsized_ty.kind() {
                 check_operand(tcx, op, span, def_id, body)?;
                 // Casting/coercing things to slices is fine.
                 Ok(())
@@ -343,8 +343,7 @@ fn feature_allowed(tcx: TyCtxt<'tcx>, def_id: DefId, feature_gate: Symbol) -> bo
 
     // However, we cannot allow stable `const fn`s to use unstable features without an explicit
     // opt-in via `allow_internal_unstable`.
-    attr::allow_internal_unstable(&tcx.sess, &tcx.get_attrs(def_id))
-        .map_or(false, |mut features| features.any(|name| name == feature_gate))
+    super::check_consts::allow_internal_unstable(tcx, def_id, feature_gate)
 }
 
 /// Returns `true` if the given library feature gate is allowed within the function with the given `DefId`.
@@ -363,8 +362,7 @@ pub fn lib_feature_allowed(tcx: TyCtxt<'tcx>, def_id: DefId, feature_gate: Symbo
 
     // However, we cannot allow stable `const fn`s to use unstable features without an explicit
     // opt-in via `allow_internal_unstable`.
-    attr::allow_internal_unstable(&tcx.sess, &tcx.get_attrs(def_id))
-        .map_or(false, |mut features| features.any(|name| name == feature_gate))
+    super::check_consts::allow_internal_unstable(tcx, def_id, feature_gate)
 }
 
 fn check_terminator(
@@ -406,7 +404,7 @@ fn check_terminator(
             fn_span: _,
         } => {
             let fn_ty = func.ty(body, tcx);
-            if let ty::FnDef(fn_def_id, _) = fn_ty.kind {
+            if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() {
                 // Allow unstable const if we opt in by using #[allow_internal_unstable]
                 // on function or macro declaration.
                 if !crate::const_eval::is_min_const_fn(tcx, fn_def_id)
diff --git a/compiler/rustc_mir/src/transform/remove_noop_landing_pads.rs b/compiler/rustc_mir/src/transform/remove_noop_landing_pads.rs
index 0bad1e5037a..4079f0110e2 100644
--- a/compiler/rustc_mir/src/transform/remove_noop_landing_pads.rs
+++ b/compiler/rustc_mir/src/transform/remove_noop_landing_pads.rs
@@ -102,6 +102,16 @@ impl RemoveNoopLandingPads {
         let postorder: Vec<_> = traversal::postorder(body).map(|(bb, _)| bb).collect();
         for bb in postorder {
             debug!("  processing {:?}", bb);
+            if let Some(unwind) = body[bb].terminator_mut().unwind_mut() {
+                if let Some(unwind_bb) = *unwind {
+                    if nop_landing_pads.contains(unwind_bb) {
+                        debug!("    removing noop landing pad");
+                        landing_pads_removed += 1;
+                        *unwind = None;
+                    }
+                }
+            }
+
             for target in body[bb].terminator_mut().successors_mut() {
                 if *target != resume_block && nop_landing_pads.contains(*target) {
                     debug!("    folding noop jump to {:?} to resume block", target);
@@ -110,15 +120,6 @@ impl RemoveNoopLandingPads {
                 }
             }
 
-            if let Some(unwind) = body[bb].terminator_mut().unwind_mut() {
-                if *unwind == Some(resume_block) {
-                    debug!("    removing noop landing pad");
-                    jumps_folded -= 1;
-                    landing_pads_removed += 1;
-                    *unwind = None;
-                }
-            }
-
             let is_nop_landing_pad = self.is_nop_landing_pad(bb, body, &nop_landing_pads);
             if is_nop_landing_pad {
                 nop_landing_pads.insert(bb);
diff --git a/compiler/rustc_mir/src/transform/remove_unneeded_drops.rs b/compiler/rustc_mir/src/transform/remove_unneeded_drops.rs
new file mode 100644
index 00000000000..b9f29786c64
--- /dev/null
+++ b/compiler/rustc_mir/src/transform/remove_unneeded_drops.rs
@@ -0,0 +1,58 @@
+//! This pass replaces a drop of a type that does not need dropping, with a goto
+
+use crate::transform::{MirPass, MirSource};
+use rustc_hir::def_id::LocalDefId;
+use rustc_middle::mir::visit::Visitor;
+use rustc_middle::mir::*;
+use rustc_middle::ty::TyCtxt;
+
+use super::simplify::simplify_cfg;
+
+pub struct RemoveUnneededDrops;
+
+impl<'tcx> MirPass<'tcx> for RemoveUnneededDrops {
+    fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
+        trace!("Running RemoveUnneededDrops on {:?}", source);
+        let mut opt_finder = RemoveUnneededDropsOptimizationFinder {
+            tcx,
+            body,
+            optimizations: vec![],
+            def_id: source.def_id().expect_local(),
+        };
+        opt_finder.visit_body(body);
+        let should_simplify = !opt_finder.optimizations.is_empty();
+        for (loc, target) in opt_finder.optimizations {
+            let terminator = body.basic_blocks_mut()[loc.block].terminator_mut();
+            debug!("SUCCESS: replacing `drop` with goto({:?})", target);
+            terminator.kind = TerminatorKind::Goto { target };
+        }
+
+        // if we applied optimizations, we potentially have some cfg to cleanup to
+        // make it easier for further passes
+        if should_simplify {
+            simplify_cfg(body);
+        }
+    }
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for RemoveUnneededDropsOptimizationFinder<'a, 'tcx> {
+    fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
+        match terminator.kind {
+            TerminatorKind::Drop { place, target, .. } => {
+                let ty = place.ty(self.body, self.tcx);
+                let needs_drop = ty.ty.needs_drop(self.tcx, self.tcx.param_env(self.def_id));
+                if !needs_drop {
+                    self.optimizations.push((location, target));
+                }
+            }
+            _ => {}
+        }
+        self.super_terminator(terminator, location);
+    }
+}
+pub struct RemoveUnneededDropsOptimizationFinder<'a, 'tcx> {
+    tcx: TyCtxt<'tcx>,
+    body: &'a Body<'tcx>,
+    optimizations: Vec<(Location, BasicBlock)>,
+    def_id: LocalDefId,
+}
diff --git a/compiler/rustc_mir/src/transform/rustc_peek.rs b/compiler/rustc_mir/src/transform/rustc_peek.rs
index 00d269a4af8..015af44b80f 100644
--- a/compiler/rustc_mir/src/transform/rustc_peek.rs
+++ b/compiler/rustc_mir/src/transform/rustc_peek.rs
@@ -1,4 +1,6 @@
-use rustc_ast as ast;
+use std::borrow::Borrow;
+
+use rustc_ast::ast;
 use rustc_span::symbol::sym;
 use rustc_span::Span;
 use rustc_target::spec::abi::Abi;
@@ -16,7 +18,7 @@ use crate::dataflow::impls::{
 use crate::dataflow::move_paths::{HasMoveData, MoveData};
 use crate::dataflow::move_paths::{LookupResult, MovePathIndex};
 use crate::dataflow::MoveDataParamEnv;
-use crate::dataflow::{Analysis, Results, ResultsCursor};
+use crate::dataflow::{Analysis, JoinSemiLattice, Results, ResultsCursor};
 
 pub struct SanityCheck;
 
@@ -180,7 +182,7 @@ enum PeekCallKind {
 
 impl PeekCallKind {
     fn from_arg_ty(arg: Ty<'_>) -> Self {
-        match arg.kind {
+        match arg.kind() {
             ty::Ref(_, _, _) => PeekCallKind::ByRef,
             _ => PeekCallKind::ByVal,
         }
@@ -205,7 +207,7 @@ impl PeekCall {
         if let mir::TerminatorKind::Call { func: Operand::Constant(func), args, .. } =
             &terminator.kind
         {
-            if let ty::FnDef(def_id, substs) = func.literal.ty.kind {
+            if let ty::FnDef(def_id, substs) = *func.literal.ty.kind() {
                 let sig = tcx.fn_sig(def_id);
                 let name = tcx.item_name(def_id);
                 if sig.abi() != Abi::RustIntrinsic || name != sym::rustc_peek {
@@ -248,25 +250,26 @@ pub trait RustcPeekAt<'tcx>: Analysis<'tcx> {
         &self,
         tcx: TyCtxt<'tcx>,
         place: mir::Place<'tcx>,
-        flow_state: &BitSet<Self::Idx>,
+        flow_state: &Self::Domain,
         call: PeekCall,
     );
 }
 
-impl<'tcx, A> RustcPeekAt<'tcx> for A
+impl<'tcx, A, D> RustcPeekAt<'tcx> for A
 where
-    A: Analysis<'tcx, Idx = MovePathIndex> + HasMoveData<'tcx>,
+    A: Analysis<'tcx, Domain = D> + HasMoveData<'tcx>,
+    D: JoinSemiLattice + Clone + Borrow<BitSet<MovePathIndex>>,
 {
     fn peek_at(
         &self,
         tcx: TyCtxt<'tcx>,
         place: mir::Place<'tcx>,
-        flow_state: &BitSet<Self::Idx>,
+        flow_state: &Self::Domain,
         call: PeekCall,
     ) {
         match self.move_data().rev_lookup.find(place.as_ref()) {
             LookupResult::Exact(peek_mpi) => {
-                let bit_state = flow_state.contains(peek_mpi);
+                let bit_state = flow_state.borrow().contains(peek_mpi);
                 debug!("rustc_peek({:?} = &{:?}) bit_state: {}", call.arg, place, bit_state);
                 if !bit_state {
                     tcx.sess.span_err(call.span, "rustc_peek: bit not set");
diff --git a/compiler/rustc_mir/src/transform/simplify.rs b/compiler/rustc_mir/src/transform/simplify.rs
index d8995e92abf..3fc8e6d4b04 100644
--- a/compiler/rustc_mir/src/transform/simplify.rs
+++ b/compiler/rustc_mir/src/transform/simplify.rs
@@ -281,8 +281,7 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
 
     fn strip_nops(&mut self) {
         for blk in self.basic_blocks.iter_mut() {
-            blk.statements
-                .retain(|stmt| if let StatementKind::Nop = stmt.kind { false } else { true })
+            blk.statements.retain(|stmt| !matches!(stmt.kind, StatementKind::Nop))
         }
     }
 }
diff --git a/compiler/rustc_mir/src/transform/simplify_comparison_integral.rs b/compiler/rustc_mir/src/transform/simplify_comparison_integral.rs
index a450a75d091..9b460c9ecb1 100644
--- a/compiler/rustc_mir/src/transform/simplify_comparison_integral.rs
+++ b/compiler/rustc_mir/src/transform/simplify_comparison_integral.rs
@@ -61,26 +61,6 @@ impl<'tcx> MirPass<'tcx> for SimplifyComparisonIntegral {
                 _ => unreachable!(),
             }
 
-            let terminator = bb.terminator_mut();
-
-            // add StorageDead for the place switched on at the top of each target
-            for bb_idx in new_targets.iter() {
-                storage_deads_to_insert.push((
-                    *bb_idx,
-                    Statement {
-                        source_info: terminator.source_info,
-                        kind: StatementKind::StorageDead(opt.to_switch_on.local),
-                    },
-                ));
-            }
-
-            terminator.kind = TerminatorKind::SwitchInt {
-                discr: Operand::Move(opt.to_switch_on),
-                switch_ty: opt.branch_value_ty,
-                values: vec![new_value].into(),
-                targets: new_targets,
-            };
-
             // delete comparison statement if it the value being switched on was moved, which means it can not be user later on
             if opt.can_remove_bin_op_stmt {
                 bb.statements[opt.bin_op_stmt_idx].make_nop();
@@ -106,14 +86,35 @@ impl<'tcx> MirPass<'tcx> for SimplifyComparisonIntegral {
                 }
             }
 
+            let terminator = bb.terminator();
+
             // remove StorageDead (if it exists) being used in the assign of the comparison
             for (stmt_idx, stmt) in bb.statements.iter().enumerate() {
                 if !matches!(stmt.kind, StatementKind::StorageDead(local) if local == opt.to_switch_on.local)
                 {
                     continue;
                 }
-                storage_deads_to_remove.push((stmt_idx, opt.bb_idx))
+                storage_deads_to_remove.push((stmt_idx, opt.bb_idx));
+                // if we have StorageDeads to remove then make sure to insert them at the top of each target
+                for bb_idx in new_targets.iter() {
+                    storage_deads_to_insert.push((
+                        *bb_idx,
+                        Statement {
+                            source_info: terminator.source_info,
+                            kind: StatementKind::StorageDead(opt.to_switch_on.local),
+                        },
+                    ));
+                }
             }
+
+            let terminator = bb.terminator_mut();
+
+            terminator.kind = TerminatorKind::SwitchInt {
+                discr: Operand::Move(opt.to_switch_on),
+                switch_ty: opt.branch_value_ty,
+                values: vec![new_value].into(),
+                targets: new_targets,
+            };
         }
 
         for (idx, bb_idx) in storage_deads_to_remove {
diff --git a/compiler/rustc_mir/src/transform/simplify_try.rs b/compiler/rustc_mir/src/transform/simplify_try.rs
index 06829cc2f14..4935997eb82 100644
--- a/compiler/rustc_mir/src/transform/simplify_try.rs
+++ b/compiler/rustc_mir/src/transform/simplify_try.rs
@@ -16,7 +16,7 @@ use rustc_middle::mir::visit::{NonUseContext, PlaceContext, Visitor};
 use rustc_middle::mir::*;
 use rustc_middle::ty::{self, List, Ty, TyCtxt};
 use rustc_target::abi::VariantIdx;
-use std::iter::{Enumerate, Peekable};
+use std::iter::{once, Enumerate, Peekable};
 use std::slice::Iter;
 
 /// Simplifies arms of form `Variant(x) => Variant(x)` to just a move.
@@ -230,8 +230,8 @@ fn get_arm_identity_info<'a, 'tcx>(
             }
         }
     }
-
-    nop_stmts.sort();
+    // We sort primitive usize here so we can use unstable sort
+    nop_stmts.sort_unstable();
 
     // Use one of the statements we're going to discard between the point
     // where the storage location for the variant field becomes live and
@@ -367,11 +367,7 @@ fn optimization_applies<'tcx>(
 }
 
 impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity {
-    fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
-        if tcx.sess.opts.debugging_opts.mir_opt_level < 2 {
-            return;
-        }
-
+    fn run_pass(&self, _tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
         trace!("running SimplifyArmIdentity on {:?}", source);
         let local_uses = LocalUseCounter::get_local_uses(body);
         let (basic_blocks, local_decls, debug_info) =
@@ -555,6 +551,12 @@ struct SimplifyBranchSameOptimization {
     bb_to_opt_terminator: BasicBlock,
 }
 
+struct SwitchTargetAndValue {
+    target: BasicBlock,
+    // None in case of the `otherwise` case
+    value: Option<u128>,
+}
+
 struct SimplifyBranchSameOptimizationFinder<'a, 'tcx> {
     body: &'a Body<'tcx>,
     tcx: TyCtxt<'tcx>,
@@ -566,8 +568,16 @@ impl<'a, 'tcx> SimplifyBranchSameOptimizationFinder<'a, 'tcx> {
             .basic_blocks()
             .iter_enumerated()
             .filter_map(|(bb_idx, bb)| {
-                let (discr_switched_on, targets) = match &bb.terminator().kind {
-                    TerminatorKind::SwitchInt { targets, discr, .. } => (discr, targets),
+                let (discr_switched_on, targets_and_values) = match &bb.terminator().kind {
+                    TerminatorKind::SwitchInt { targets, discr, values, .. } => {
+                        // if values.len() == targets.len() - 1, we need to include None where no value is present
+                        // such that the zip does not throw away targets. If no `otherwise` case is in targets, the zip will simply throw away the added None
+                        let values_extended = values.iter().map(|x|Some(*x)).chain(once(None));
+                        let targets_and_values:Vec<_> = targets.iter().zip(values_extended)
+                            .map(|(target, value)| SwitchTargetAndValue{target:*target, value})
+                            .collect();
+                        assert_eq!(targets.len(), targets_and_values.len());
+                        (discr, targets_and_values)},
                     _ => return None,
                 };
 
@@ -591,9 +601,9 @@ impl<'a, 'tcx> SimplifyBranchSameOptimizationFinder<'a, 'tcx> {
                     },
                 };
 
-                let mut iter_bbs_reachable = targets
+                let mut iter_bbs_reachable = targets_and_values
                     .iter()
-                    .map(|idx| (*idx, &self.body.basic_blocks()[*idx]))
+                    .map(|target_and_value| (target_and_value, &self.body.basic_blocks()[target_and_value.target]))
                     .filter(|(_, bb)| {
                         // Reaching `unreachable` is UB so assume it doesn't happen.
                         bb.terminator().kind != TerminatorKind::Unreachable
@@ -607,16 +617,16 @@ impl<'a, 'tcx> SimplifyBranchSameOptimizationFinder<'a, 'tcx> {
                     })
                     .peekable();
 
-                let bb_first = iter_bbs_reachable.peek().map(|(idx, _)| *idx).unwrap_or(targets[0]);
+                let bb_first = iter_bbs_reachable.peek().map(|(idx, _)| *idx).unwrap_or(&targets_and_values[0]);
                 let mut all_successors_equivalent = StatementEquality::TrivialEqual;
 
                 // All successor basic blocks must be equal or contain statements that are pairwise considered equal.
-                for ((bb_l_idx,bb_l), (bb_r_idx,bb_r)) in iter_bbs_reachable.tuple_windows() {
+                for ((target_and_value_l,bb_l), (target_and_value_r,bb_r)) in iter_bbs_reachable.tuple_windows() {
                     let trivial_checks = bb_l.is_cleanup == bb_r.is_cleanup
                     && bb_l.terminator().kind == bb_r.terminator().kind;
                     let statement_check = || {
                         bb_l.statements.iter().zip(&bb_r.statements).try_fold(StatementEquality::TrivialEqual, |acc,(l,r)| {
-                            let stmt_equality = self.statement_equality(*adt_matched_on, &l, bb_l_idx, &r, bb_r_idx);
+                            let stmt_equality = self.statement_equality(*adt_matched_on, &l, target_and_value_l, &r, target_and_value_r);
                             if matches!(stmt_equality, StatementEquality::NotEqual) {
                                 // short circuit
                                 None
@@ -638,7 +648,7 @@ impl<'a, 'tcx> SimplifyBranchSameOptimizationFinder<'a, 'tcx> {
                         // statements are trivially equal, so just take first
                         trace!("Statements are trivially equal");
                         Some(SimplifyBranchSameOptimization {
-                            bb_to_goto: bb_first,
+                            bb_to_goto: bb_first.target,
                             bb_to_opt_terminator: bb_idx,
                         })
                     }
@@ -673,16 +683,16 @@ impl<'a, 'tcx> SimplifyBranchSameOptimizationFinder<'a, 'tcx> {
         &self,
         adt_matched_on: Place<'tcx>,
         x: &Statement<'tcx>,
-        x_bb_idx: BasicBlock,
+        x_target_and_value: &SwitchTargetAndValue,
         y: &Statement<'tcx>,
-        y_bb_idx: BasicBlock,
+        y_target_and_value: &SwitchTargetAndValue,
     ) -> StatementEquality {
         let helper = |rhs: &Rvalue<'tcx>,
-                      place: &Box<Place<'tcx>>,
+                      place: &Place<'tcx>,
                       variant_index: &VariantIdx,
                       side_to_choose| {
             let place_type = place.ty(self.body, self.tcx).ty;
-            let adt = match place_type.kind {
+            let adt = match *place_type.kind() {
                 ty::Adt(adt, _) if adt.is_enum() => adt,
                 _ => return StatementEquality::NotEqual,
             };
@@ -714,16 +724,20 @@ impl<'a, 'tcx> SimplifyBranchSameOptimizationFinder<'a, 'tcx> {
             (
                 StatementKind::Assign(box (_, rhs)),
                 StatementKind::SetDiscriminant { place, variant_index },
-            ) => {
+            )
+            // we need to make sure that the switch value that targets the bb with SetDiscriminant (y), is the same as the variant index
+            if Some(variant_index.index() as u128) == y_target_and_value.value => {
                 // choose basic block of x, as that has the assign
-                helper(rhs, place, variant_index, x_bb_idx)
+                helper(rhs, place, variant_index, x_target_and_value.target)
             }
             (
                 StatementKind::SetDiscriminant { place, variant_index },
                 StatementKind::Assign(box (_, rhs)),
-            ) => {
+            )
+            // we need to make sure that the switch value that targets the bb with SetDiscriminant (x), is the same as the variant index
+            if Some(variant_index.index() as u128) == x_target_and_value.value  => {
                 // choose basic block of y, as that has the assign
-                helper(rhs, place, variant_index, y_bb_idx)
+                helper(rhs, place, variant_index, y_target_and_value.target)
             }
             _ => {
                 trace!("NO: statements `{:?}` and `{:?}` not considered equal", x, y);
diff --git a/compiler/rustc_mir/src/transform/validate.rs b/compiler/rustc_mir/src/transform/validate.rs
index d7c9ecd0655..d3ca14abdca 100644
--- a/compiler/rustc_mir/src/transform/validate.rs
+++ b/compiler/rustc_mir/src/transform/validate.rs
@@ -4,8 +4,8 @@ use super::{MirPass, MirSource};
 use rustc_middle::mir::visit::Visitor;
 use rustc_middle::{
     mir::{
-        AggregateKind, BasicBlock, Body, Location, MirPhase, Operand, Rvalue, Statement,
-        StatementKind, Terminator, TerminatorKind,
+        AggregateKind, BasicBlock, Body, BorrowKind, Location, MirPhase, Operand, Rvalue,
+        Statement, StatementKind, Terminator, TerminatorKind,
     },
     ty::{
         self,
@@ -274,9 +274,33 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                             )
                         }
                     }
+                    Rvalue::Ref(_, BorrowKind::Shallow, _) => {
+                        if self.mir_phase > MirPhase::DropLowering {
+                            self.fail(
+                                location,
+                                "`Assign` statement with a `Shallow` borrow should have been removed after drop lowering phase",
+                            );
+                        }
+                    }
                     _ => {}
                 }
             }
+            StatementKind::AscribeUserType(..) => {
+                if self.mir_phase > MirPhase::DropLowering {
+                    self.fail(
+                        location,
+                        "`AscribeUserType` should have been removed after drop lowering phase",
+                    );
+                }
+            }
+            StatementKind::FakeRead(..) => {
+                if self.mir_phase > MirPhase::DropLowering {
+                    self.fail(
+                        location,
+                        "`FakeRead` should have been removed after drop lowering phase",
+                    );
+                }
+            }
             _ => {}
         }
     }
@@ -331,7 +355,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
             }
             TerminatorKind::Call { func, destination, cleanup, .. } => {
                 let func_ty = func.ty(&self.body.local_decls, self.tcx);
-                match func_ty.kind {
+                match func_ty.kind() {
                     ty::FnPtr(..) | ty::FnDef(..) => {}
                     _ => self.fail(
                         location,
diff --git a/compiler/rustc_mir/src/util/alignment.rs b/compiler/rustc_mir/src/util/alignment.rs
index 202e5e27f1d..a0728a6a630 100644
--- a/compiler/rustc_mir/src/util/alignment.rs
+++ b/compiler/rustc_mir/src/util/alignment.rs
@@ -47,7 +47,7 @@ where
             ProjectionElem::Deref => break,
             ProjectionElem::Field(..) => {
                 let ty = Place::ty_from(place.local, proj_base, local_decls, tcx).ty;
-                match ty.kind {
+                match ty.kind() {
                     ty::Adt(def, _) if def.repr.packed() => return true,
                     _ => {}
                 }
diff --git a/compiler/rustc_mir/src/util/borrowck_errors.rs b/compiler/rustc_mir/src/util/borrowck_errors.rs
index f8bb7e7a85d..83bf7584f2e 100644
--- a/compiler/rustc_mir/src/util/borrowck_errors.rs
+++ b/compiler/rustc_mir/src/util/borrowck_errors.rs
@@ -287,7 +287,7 @@ impl<'cx, 'tcx> crate::borrow_check::MirBorrowckCtxt<'cx, 'tcx> {
         ty: Ty<'_>,
         is_index: Option<bool>,
     ) -> DiagnosticBuilder<'cx> {
-        let type_name = match (&ty.kind, is_index) {
+        let type_name = match (&ty.kind(), is_index) {
             (&ty::Array(_, _), Some(true)) | (&ty::Array(_, _), None) => "array",
             (&ty::Slice(_), _) => "slice",
             _ => span_bug!(move_from_span, "this path should not cause illegal move"),
diff --git a/compiler/rustc_mir/src/util/elaborate_drops.rs b/compiler/rustc_mir/src/util/elaborate_drops.rs
index 642935d243d..bf0a6be9a7d 100644
--- a/compiler/rustc_mir/src/util/elaborate_drops.rs
+++ b/compiler/rustc_mir/src/util/elaborate_drops.rs
@@ -858,7 +858,7 @@ where
     /// ADT, both in the success case or if one of the destructors fail.
     fn open_drop(&mut self) -> BasicBlock {
         let ty = self.place_ty(self.place);
-        match ty.kind {
+        match ty.kind() {
             ty::Closure(_, substs) => {
                 let tys: Vec<_> = substs.as_closure().upvar_tys().collect();
                 self.open_drop_for_tuple(&tys)
diff --git a/compiler/rustc_mir/src/util/find_self_call.rs b/compiler/rustc_mir/src/util/find_self_call.rs
new file mode 100644
index 00000000000..5b146eeb87c
--- /dev/null
+++ b/compiler/rustc_mir/src/util/find_self_call.rs
@@ -0,0 +1,36 @@
+use rustc_middle::mir::*;
+use rustc_middle::ty::subst::SubstsRef;
+use rustc_middle::ty::{self, TyCtxt};
+use rustc_span::def_id::DefId;
+
+/// Checks if the specified `local` is used as the `self` prameter of a method call
+/// in the provided `BasicBlock`. If it is, then the `DefId` of the called method is
+/// returned.
+pub fn find_self_call<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    body: &Body<'tcx>,
+    local: Local,
+    block: BasicBlock,
+) -> Option<(DefId, SubstsRef<'tcx>)> {
+    debug!("find_self_call(local={:?}): terminator={:?}", local, &body[block].terminator);
+    if let Some(Terminator { kind: TerminatorKind::Call { func, args, .. }, .. }) =
+        &body[block].terminator
+    {
+        debug!("find_self_call: func={:?}", func);
+        if let Operand::Constant(box Constant { literal: ty::Const { ty, .. }, .. }) = func {
+            if let ty::FnDef(def_id, substs) = *ty.kind() {
+                if let Some(ty::AssocItem { fn_has_self_parameter: true, .. }) =
+                    tcx.opt_associated_item(def_id)
+                {
+                    debug!("find_self_call: args={:?}", args);
+                    if let [Operand::Move(self_place) | Operand::Copy(self_place), ..] = **args {
+                        if self_place.as_local() == Some(local) {
+                            return Some((def_id, substs));
+                        }
+                    }
+                }
+            }
+        }
+    }
+    None
+}
diff --git a/compiler/rustc_mir/src/util/graphviz.rs b/compiler/rustc_mir/src/util/graphviz.rs
index 50193c4a0db..4511962d68f 100644
--- a/compiler/rustc_mir/src/util/graphviz.rs
+++ b/compiler/rustc_mir/src/util/graphviz.rs
@@ -55,16 +55,28 @@ where
     writeln!(w, "{} {}Mir_{} {{", kind, cluster, def_name)?;
 
     // Global graph properties
-    writeln!(w, r#"    graph [fontname="monospace"];"#)?;
-    writeln!(w, r#"    node [fontname="monospace"];"#)?;
-    writeln!(w, r#"    edge [fontname="monospace"];"#)?;
+    let font = format!(r#"fontname="{}""#, tcx.sess.opts.debugging_opts.graphviz_font);
+    let mut graph_attrs = vec![&font[..]];
+    let mut content_attrs = vec![&font[..]];
+
+    let dark_mode = tcx.sess.opts.debugging_opts.graphviz_dark_mode;
+    if dark_mode {
+        graph_attrs.push(r#"bgcolor="black""#);
+        content_attrs.push(r#"color="white""#);
+        content_attrs.push(r#"fontcolor="white""#);
+    }
+
+    writeln!(w, r#"    graph [{}];"#, graph_attrs.join(" "))?;
+    let content_attrs_str = content_attrs.join(" ");
+    writeln!(w, r#"    node [{}];"#, content_attrs_str)?;
+    writeln!(w, r#"    edge [{}];"#, content_attrs_str)?;
 
     // Graph label
     write_graph_label(tcx, def_id, body, w)?;
 
     // Nodes
     for (block, _) in body.basic_blocks().iter_enumerated() {
-        write_node(def_id, block, body, w)?;
+        write_node(def_id, block, body, dark_mode, w)?;
     }
 
     // Edges
@@ -84,6 +96,7 @@ where
 pub fn write_node_label<W: Write, INIT, FINI>(
     block: BasicBlock,
     body: &Body<'_>,
+    dark_mode: bool,
     w: &mut W,
     num_cols: u32,
     init: INIT,
@@ -100,8 +113,9 @@ where
     // Basic block number at the top.
     write!(
         w,
-        r#"<tr><td {attrs} colspan="{colspan}">{blk}</td></tr>"#,
-        attrs = r#"bgcolor="gray" align="center""#,
+        r#"<tr><td bgcolor="{bgcolor}" {attrs} colspan="{colspan}">{blk}</td></tr>"#,
+        bgcolor = if dark_mode { "dimgray" } else { "gray" },
+        attrs = r#"align="center""#,
         colspan = num_cols,
         blk = block.index()
     )?;
@@ -134,11 +148,12 @@ fn write_node<W: Write>(
     def_id: DefId,
     block: BasicBlock,
     body: &Body<'_>,
+    dark_mode: bool,
     w: &mut W,
 ) -> io::Result<()> {
     // Start a new node with the label to follow, in one of DOT's pseudo-HTML tables.
     write!(w, r#"    {} [shape="none", label=<"#, node(def_id, block))?;
-    write_node_label(block, body, w, 1, |_| Ok(()), |_| Ok(()))?;
+    write_node_label(block, body, dark_mode, w, 1, |_| Ok(()), |_| Ok(()))?;
     // Close the node label and the node itself.
     writeln!(w, ">];")
 }
diff --git a/compiler/rustc_mir/src/util/mod.rs b/compiler/rustc_mir/src/util/mod.rs
index 8bbe207c077..699f3bcf014 100644
--- a/compiler/rustc_mir/src/util/mod.rs
+++ b/compiler/rustc_mir/src/util/mod.rs
@@ -7,11 +7,14 @@ pub mod storage;
 
 mod alignment;
 pub mod collect_writes;
+mod find_self_call;
 mod graphviz;
 pub(crate) mod pretty;
+pub(crate) mod spanview;
 
 pub use self::aggregate::expand_aggregate;
 pub use self::alignment::is_disaligned;
+pub use self::find_self_call::find_self_call;
 pub use self::graphviz::write_node_label as write_graphviz_node_label;
 pub use self::graphviz::{graphviz_safe_def_name, write_mir_graphviz};
 pub use self::pretty::{dump_enabled, dump_mir, write_mir_pretty, PassWhere};
diff --git a/compiler/rustc_mir/src/util/pretty.rs b/compiler/rustc_mir/src/util/pretty.rs
index 2a9cbc7fc0e..49c644a20bf 100644
--- a/compiler/rustc_mir/src/util/pretty.rs
+++ b/compiler/rustc_mir/src/util/pretty.rs
@@ -6,6 +6,7 @@ use std::io::{self, Write};
 use std::path::{Path, PathBuf};
 
 use super::graphviz::write_mir_fn_graphviz;
+use super::spanview::write_mir_fn_spanview;
 use crate::transform::MirSource;
 use either::Either;
 use rustc_data_structures::fx::FxHashMap;
@@ -147,6 +148,16 @@ fn dump_matched_mir_node<'tcx, F>(
             write_mir_fn_graphviz(tcx, source.def_id(), body, false, &mut file)?;
         };
     }
+
+    if let Some(spanview) = tcx.sess.opts.debugging_opts.dump_mir_spanview {
+        let _: io::Result<()> = try {
+            let mut file =
+                create_dump_file(tcx, "html", pass_num, pass_name, disambiguator, source)?;
+            if source.def_id().is_local() {
+                write_mir_fn_spanview(tcx, source.def_id(), body, spanview, &mut file)?;
+            }
+        };
+    }
 }
 
 /// Returns the path to the filename where we should dump a given MIR.
@@ -387,7 +398,7 @@ impl Visitor<'tcx> for ExtraComments<'tcx> {
     fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) {
         self.super_constant(constant, location);
         let Constant { span, user_ty, literal } = constant;
-        match literal.ty.kind {
+        match literal.ty.kind() {
             ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char => {}
             // Unit type
             ty::Tuple(tys) if tys.is_empty() => {}
@@ -405,7 +416,7 @@ impl Visitor<'tcx> for ExtraComments<'tcx> {
     fn visit_const(&mut self, constant: &&'tcx ty::Const<'tcx>, _: Location) {
         self.super_const(constant);
         let ty::Const { ty, val, .. } = constant;
-        match ty.kind {
+        match ty.kind() {
             ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char | ty::Float(_) => {}
             // Unit type
             ty::Tuple(tys) if tys.is_empty() => {}
@@ -503,7 +514,7 @@ fn write_scope_tree(
                 write!(indented_decl, " as {:?}", user_ty).unwrap();
             }
         }
-        indented_decl.push_str(";");
+        indented_decl.push(';');
 
         let local_name =
             if local == RETURN_PLACE { " return place".to_string() } else { String::new() };
@@ -620,14 +631,11 @@ pub fn write_allocations<'tcx>(
             None => write!(w, " (deallocated)")?,
             Some(GlobalAlloc::Function(inst)) => write!(w, " (fn: {})", inst)?,
             Some(GlobalAlloc::Static(did)) if !tcx.is_foreign_item(did) => {
-                match tcx.const_eval_poly(did) {
-                    Ok(ConstValue::ByRef { alloc, .. }) => {
+                match tcx.eval_static_initializer(did) {
+                    Ok(alloc) => {
                         write!(w, " (static: {}, ", tcx.def_path_str(did))?;
                         write_allocation_track_relocs(w, alloc)?;
                     }
-                    Ok(_) => {
-                        span_bug!(tcx.def_span(did), " static item without `ByRef` initializer")
-                    }
                     Err(_) => write!(
                         w,
                         " (static: {}, error during initializer evaluation)",
diff --git a/compiler/rustc_mir/src/util/spanview.rs b/compiler/rustc_mir/src/util/spanview.rs
new file mode 100644
index 00000000000..fe33fffe0ea
--- /dev/null
+++ b/compiler/rustc_mir/src/util/spanview.rs
@@ -0,0 +1,672 @@
+use rustc_hir::def_id::DefId;
+use rustc_middle::hir;
+use rustc_middle::mir::*;
+use rustc_middle::ty::TyCtxt;
+use rustc_session::config::MirSpanview;
+use rustc_span::{BytePos, Pos, Span, SyntaxContext};
+
+use std::cmp;
+use std::io::{self, Write};
+
+pub const TOOLTIP_INDENT: &str = "    ";
+
+const CARET: char = '\u{2038}'; // Unicode `CARET`
+const ANNOTATION_LEFT_BRACKET: char = '\u{298a}'; // Unicode `Z NOTATION RIGHT BINDING BRACKET
+const ANNOTATION_RIGHT_BRACKET: char = '\u{2989}'; // Unicode `Z NOTATION LEFT BINDING BRACKET`
+const NEW_LINE_SPAN: &str = "</span>\n<span class=\"line\">";
+const HEADER: &str = r#"<!DOCTYPE html>
+<html>
+<head>
+    <title>coverage_of_if_else - Code Regions</title>
+    <style>
+    .line {
+        counter-increment: line;
+    }
+    .line:before {
+        content: counter(line) ": ";
+        font-family: Menlo, Monaco, monospace;
+        font-style: italic;
+        width: 3.8em;
+        display: inline-block;
+        text-align: right;
+        filter: opacity(50%);
+        -webkit-user-select: none;
+    }
+    .code {
+        color: #dddddd;
+        background-color: #222222;
+        font-family: Menlo, Monaco, monospace;
+        line-height: 1.4em;
+        border-bottom: 2px solid #222222;
+        white-space: pre;
+        display: inline-block;
+    }
+    .odd {
+        background-color: #55bbff;
+        color: #223311;
+    }
+    .even {
+        background-color: #ee7756;
+        color: #551133;
+    }
+    .code {
+        --index: calc(var(--layer) - 1);
+        padding-top: calc(var(--index) * 0.15em);
+        filter:
+            hue-rotate(calc(var(--index) * 25deg))
+            saturate(calc(100% - (var(--index) * 2%)))
+            brightness(calc(100% - (var(--index) * 1.5%)));
+    }
+    .annotation {
+        color: #4444ff;
+        font-family: monospace;
+        font-style: italic;
+        display: none;
+        -webkit-user-select: none;
+    }
+    body:active .annotation {
+        /* requires holding mouse down anywhere on the page */
+        display: inline-block;
+    }
+    span:hover .annotation {
+        /* requires hover over a span ONLY on its first line */
+        display: inline-block;
+    }
+    </style>
+</head>
+<body>"#;
+
+const FOOTER: &str = r#"
+</body>
+</html>"#;
+
+/// Metadata to highlight the span of a MIR BasicBlock, Statement, or Terminator.
+pub struct SpanViewable {
+    pub span: Span,
+    pub id: String,
+    pub tooltip: String,
+}
+
+/// Write a spanview HTML+CSS file to analyze MIR element spans.
+pub fn write_mir_fn_spanview<'tcx, W>(
+    tcx: TyCtxt<'tcx>,
+    def_id: DefId,
+    body: &Body<'tcx>,
+    spanview: MirSpanview,
+    w: &mut W,
+) -> io::Result<()>
+where
+    W: Write,
+{
+    let body_span = hir_body(tcx, def_id).value.span;
+    let mut span_viewables = Vec::new();
+    for (bb, data) in body.basic_blocks().iter_enumerated() {
+        match spanview {
+            MirSpanview::Statement => {
+                for (i, statement) in data.statements.iter().enumerate() {
+                    if let Some(span_viewable) =
+                        statement_span_viewable(tcx, body_span, bb, i, statement)
+                    {
+                        span_viewables.push(span_viewable);
+                    }
+                }
+                if let Some(span_viewable) = terminator_span_viewable(tcx, body_span, bb, data) {
+                    span_viewables.push(span_viewable);
+                }
+            }
+            MirSpanview::Terminator => {
+                if let Some(span_viewable) = terminator_span_viewable(tcx, body_span, bb, data) {
+                    span_viewables.push(span_viewable);
+                }
+            }
+            MirSpanview::Block => {
+                if let Some(span_viewable) = block_span_viewable(tcx, body_span, bb, data) {
+                    span_viewables.push(span_viewable);
+                }
+            }
+        }
+    }
+    write_spanview_document(tcx, def_id, span_viewables, w)?;
+    Ok(())
+}
+
+/// Generate a spanview HTML+CSS document for the given local function `def_id`, and a pre-generated
+/// list `SpanViewable`s.
+pub fn write_spanview_document<'tcx, W>(
+    tcx: TyCtxt<'tcx>,
+    def_id: DefId,
+    mut span_viewables: Vec<SpanViewable>,
+    w: &mut W,
+) -> io::Result<()>
+where
+    W: Write,
+{
+    let fn_span = fn_span(tcx, def_id);
+    let mut from_pos = fn_span.lo();
+    let end_pos = fn_span.hi();
+    let source_map = tcx.sess.source_map();
+    let start = source_map.lookup_char_pos(from_pos);
+    let indent_to_initial_start_col = " ".repeat(start.col.to_usize());
+    debug!(
+        "fn_span source is:\n{}{}",
+        indent_to_initial_start_col,
+        source_map.span_to_snippet(fn_span).expect("function should have printable source")
+    );
+    writeln!(w, "{}", HEADER)?;
+    write!(
+        w,
+        r#"<div class="code" style="counter-reset: line {}"><span class="line">{}"#,
+        start.line - 1,
+        indent_to_initial_start_col,
+    )?;
+    span_viewables.sort_unstable_by(|a, b| {
+        let a = a.span;
+        let b = b.span;
+        if a.lo() == b.lo() {
+            // Sort hi() in reverse order so shorter spans are attempted after longer spans.
+            // This should give shorter spans a higher "layer", so they are not covered by
+            // the longer spans.
+            b.hi().partial_cmp(&a.hi())
+        } else {
+            a.lo().partial_cmp(&b.lo())
+        }
+        .unwrap()
+    });
+    let mut ordered_viewables = &span_viewables[..];
+    const LOWEST_VIEWABLE_LAYER: usize = 1;
+    let mut alt = false;
+    while ordered_viewables.len() > 0 {
+        debug!(
+            "calling write_next_viewable with from_pos={}, end_pos={}, and viewables len={}",
+            from_pos.to_usize(),
+            end_pos.to_usize(),
+            ordered_viewables.len()
+        );
+        let (next_from_pos, next_ordered_viewables) = write_next_viewable_with_overlaps(
+            tcx,
+            from_pos,
+            end_pos,
+            ordered_viewables,
+            alt,
+            LOWEST_VIEWABLE_LAYER,
+            w,
+        )?;
+        debug!(
+            "DONE calling write_next_viewable, with new from_pos={}, \
+             and remaining viewables len={}",
+            next_from_pos.to_usize(),
+            next_ordered_viewables.len()
+        );
+        assert!(
+            from_pos != next_from_pos || ordered_viewables.len() != next_ordered_viewables.len(),
+            "write_next_viewable_with_overlaps() must make a state change"
+        );
+        from_pos = next_from_pos;
+        if next_ordered_viewables.len() != ordered_viewables.len() {
+            ordered_viewables = next_ordered_viewables;
+            alt = !alt;
+        }
+    }
+    if from_pos < end_pos {
+        write_coverage_gap(tcx, from_pos, end_pos, w)?;
+    }
+    write!(w, r#"</span></div>"#)?;
+    writeln!(w, "{}", FOOTER)?;
+    Ok(())
+}
+
+/// Format a string showing the start line and column, and end line and column within a file.
+pub fn source_range_no_file<'tcx>(tcx: TyCtxt<'tcx>, span: &Span) -> String {
+    let source_map = tcx.sess.source_map();
+    let start = source_map.lookup_char_pos(span.lo());
+    let end = source_map.lookup_char_pos(span.hi());
+    format!("{}:{}-{}:{}", start.line, start.col.to_usize() + 1, end.line, end.col.to_usize() + 1)
+}
+
+pub fn statement_kind_name(statement: &Statement<'_>) -> &'static str {
+    use StatementKind::*;
+    match statement.kind {
+        Assign(..) => "Assign",
+        FakeRead(..) => "FakeRead",
+        SetDiscriminant { .. } => "SetDiscriminant",
+        StorageLive(..) => "StorageLive",
+        StorageDead(..) => "StorageDead",
+        LlvmInlineAsm(..) => "LlvmInlineAsm",
+        Retag(..) => "Retag",
+        AscribeUserType(..) => "AscribeUserType",
+        Coverage(..) => "Coverage",
+        Nop => "Nop",
+    }
+}
+
+pub fn terminator_kind_name(term: &Terminator<'_>) -> &'static str {
+    use TerminatorKind::*;
+    match term.kind {
+        Goto { .. } => "Goto",
+        SwitchInt { .. } => "SwitchInt",
+        Resume => "Resume",
+        Abort => "Abort",
+        Return => "Return",
+        Unreachable => "Unreachable",
+        Drop { .. } => "Drop",
+        DropAndReplace { .. } => "DropAndReplace",
+        Call { .. } => "Call",
+        Assert { .. } => "Assert",
+        Yield { .. } => "Yield",
+        GeneratorDrop => "GeneratorDrop",
+        FalseEdge { .. } => "FalseEdge",
+        FalseUnwind { .. } => "FalseUnwind",
+        InlineAsm { .. } => "InlineAsm",
+    }
+}
+
+fn statement_span_viewable<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    body_span: Span,
+    bb: BasicBlock,
+    i: usize,
+    statement: &Statement<'tcx>,
+) -> Option<SpanViewable> {
+    let span = statement.source_info.span;
+    if !body_span.contains(span) {
+        return None;
+    }
+    let id = format!("{}[{}]", bb.index(), i);
+    let tooltip = tooltip(tcx, &id, span, vec![statement.clone()], &None);
+    Some(SpanViewable { span, id, tooltip })
+}
+
+fn terminator_span_viewable<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    body_span: Span,
+    bb: BasicBlock,
+    data: &BasicBlockData<'tcx>,
+) -> Option<SpanViewable> {
+    let term = data.terminator();
+    let span = term.source_info.span;
+    if !body_span.contains(span) {
+        return None;
+    }
+    let id = format!("{}:{}", bb.index(), terminator_kind_name(term));
+    let tooltip = tooltip(tcx, &id, span, vec![], &data.terminator);
+    Some(SpanViewable { span, id, tooltip })
+}
+
+fn block_span_viewable<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    body_span: Span,
+    bb: BasicBlock,
+    data: &BasicBlockData<'tcx>,
+) -> Option<SpanViewable> {
+    let span = compute_block_span(data, body_span);
+    if !body_span.contains(span) {
+        return None;
+    }
+    let id = format!("{}", bb.index());
+    let tooltip = tooltip(tcx, &id, span, data.statements.clone(), &data.terminator);
+    Some(SpanViewable { span, id, tooltip })
+}
+
+fn compute_block_span<'tcx>(data: &BasicBlockData<'tcx>, body_span: Span) -> Span {
+    let mut span = data.terminator().source_info.span;
+    for statement_span in data.statements.iter().map(|statement| statement.source_info.span) {
+        // Only combine Spans from the root context, and within the function's body_span.
+        if statement_span.ctxt() == SyntaxContext::root() && body_span.contains(statement_span) {
+            span = span.to(statement_span);
+        }
+    }
+    span
+}
+
+/// Recursively process each ordered span. Spans that overlap will have progressively varying
+/// styles, such as increased padding for each overlap. Non-overlapping adjacent spans will
+/// have alternating style choices, to help distinguish between them if, visually adjacent.
+/// The `layer` is incremented for each overlap, and the `alt` bool alternates between true
+/// and false, for each adjacent non-overlapping span. Source code between the spans (code
+/// that is not in any coverage region) has neutral styling.
+fn write_next_viewable_with_overlaps<'tcx, 'b, W>(
+    tcx: TyCtxt<'tcx>,
+    mut from_pos: BytePos,
+    mut to_pos: BytePos,
+    ordered_viewables: &'b [SpanViewable],
+    alt: bool,
+    layer: usize,
+    w: &mut W,
+) -> io::Result<(BytePos, &'b [SpanViewable])>
+where
+    W: Write,
+{
+    let debug_indent = "  ".repeat(layer);
+    let (viewable, mut remaining_viewables) =
+        ordered_viewables.split_first().expect("ordered_viewables should have some");
+
+    if from_pos < viewable.span.lo() {
+        debug!(
+            "{}advance from_pos to next SpanViewable (from from_pos={} to viewable.span.lo()={} \
+             of {:?}), with to_pos={}",
+            debug_indent,
+            from_pos.to_usize(),
+            viewable.span.lo().to_usize(),
+            viewable.span,
+            to_pos.to_usize()
+        );
+        let hi = cmp::min(viewable.span.lo(), to_pos);
+        write_coverage_gap(tcx, from_pos, hi, w)?;
+        from_pos = hi;
+        if from_pos < viewable.span.lo() {
+            debug!(
+                "{}EARLY RETURN: stopped before getting to next SpanViewable, at {}",
+                debug_indent,
+                from_pos.to_usize()
+            );
+            return Ok((from_pos, ordered_viewables));
+        }
+    }
+
+    if from_pos < viewable.span.hi() {
+        // Set to_pos to the end of this `viewable` to ensure the recursive calls stop writing
+        // with room to print the tail.
+        to_pos = cmp::min(viewable.span.hi(), to_pos);
+        debug!(
+            "{}update to_pos (if not closer) to viewable.span.hi()={}; to_pos is now {}",
+            debug_indent,
+            viewable.span.hi().to_usize(),
+            to_pos.to_usize()
+        );
+    }
+
+    let mut subalt = false;
+    while remaining_viewables.len() > 0 && remaining_viewables[0].span.overlaps(viewable.span) {
+        let overlapping_viewable = &remaining_viewables[0];
+        debug!("{}overlapping_viewable.span={:?}", debug_indent, overlapping_viewable.span);
+
+        let span =
+            trim_span(viewable.span, from_pos, cmp::min(overlapping_viewable.span.lo(), to_pos));
+        let mut some_html_snippet = if from_pos <= viewable.span.hi() || viewable.span.is_empty() {
+            // `viewable` is not yet fully rendered, so start writing the span, up to either the
+            // `to_pos` or the next `overlapping_viewable`, whichever comes first.
+            debug!(
+                "{}make html_snippet (may not write it if early exit) for partial span {:?} \
+                 of viewable.span {:?}",
+                debug_indent, span, viewable.span
+            );
+            from_pos = span.hi();
+            make_html_snippet(tcx, span, Some(&viewable))
+        } else {
+            None
+        };
+
+        // Defer writing the HTML snippet (until after early return checks) ONLY for empty spans.
+        // An empty Span with Some(html_snippet) is probably a tail marker. If there is an early
+        // exit, there should be another opportunity to write the tail marker.
+        if !span.is_empty() {
+            if let Some(ref html_snippet) = some_html_snippet {
+                debug!(
+                    "{}write html_snippet for that partial span of viewable.span {:?}",
+                    debug_indent, viewable.span
+                );
+                write_span(html_snippet, &viewable.tooltip, alt, layer, w)?;
+            }
+            some_html_snippet = None;
+        }
+
+        if from_pos < overlapping_viewable.span.lo() {
+            debug!(
+                "{}EARLY RETURN: from_pos={} has not yet reached the \
+                 overlapping_viewable.span {:?}",
+                debug_indent,
+                from_pos.to_usize(),
+                overlapping_viewable.span
+            );
+            // must have reached `to_pos` before reaching the start of the
+            // `overlapping_viewable.span`
+            return Ok((from_pos, ordered_viewables));
+        }
+
+        if from_pos == to_pos
+            && !(from_pos == overlapping_viewable.span.lo() && overlapping_viewable.span.is_empty())
+        {
+            debug!(
+                "{}EARLY RETURN: from_pos=to_pos={} and overlapping_viewable.span {:?} is not \
+                 empty, or not from_pos",
+                debug_indent,
+                to_pos.to_usize(),
+                overlapping_viewable.span
+            );
+            // `to_pos` must have occurred before the overlapping viewable. Return
+            // `ordered_viewables` so we can continue rendering the `viewable`, from after the
+            // `to_pos`.
+            return Ok((from_pos, ordered_viewables));
+        }
+
+        if let Some(ref html_snippet) = some_html_snippet {
+            debug!(
+                "{}write html_snippet for that partial span of viewable.span {:?}",
+                debug_indent, viewable.span
+            );
+            write_span(html_snippet, &viewable.tooltip, alt, layer, w)?;
+        }
+
+        debug!(
+            "{}recursively calling write_next_viewable with from_pos={}, to_pos={}, \
+             and viewables len={}",
+            debug_indent,
+            from_pos.to_usize(),
+            to_pos.to_usize(),
+            remaining_viewables.len()
+        );
+        // Write the overlaps (and the overlaps' overlaps, if any) up to `to_pos`.
+        let (next_from_pos, next_remaining_viewables) = write_next_viewable_with_overlaps(
+            tcx,
+            from_pos,
+            to_pos,
+            &remaining_viewables,
+            subalt,
+            layer + 1,
+            w,
+        )?;
+        debug!(
+            "{}DONE recursively calling write_next_viewable, with new from_pos={}, and remaining \
+             viewables len={}",
+            debug_indent,
+            next_from_pos.to_usize(),
+            next_remaining_viewables.len()
+        );
+        assert!(
+            from_pos != next_from_pos
+                || remaining_viewables.len() != next_remaining_viewables.len(),
+            "write_next_viewable_with_overlaps() must make a state change"
+        );
+        from_pos = next_from_pos;
+        if next_remaining_viewables.len() != remaining_viewables.len() {
+            remaining_viewables = next_remaining_viewables;
+            subalt = !subalt;
+        }
+    }
+    if from_pos <= viewable.span.hi() {
+        let span = trim_span(viewable.span, from_pos, to_pos);
+        debug!(
+            "{}After overlaps, writing (end span?) {:?} of viewable.span {:?}",
+            debug_indent, span, viewable.span
+        );
+        if let Some(ref html_snippet) = make_html_snippet(tcx, span, Some(&viewable)) {
+            from_pos = span.hi();
+            write_span(html_snippet, &viewable.tooltip, alt, layer, w)?;
+        }
+    }
+    debug!("{}RETURN: No more overlap", debug_indent);
+    Ok((
+        from_pos,
+        if from_pos < viewable.span.hi() { ordered_viewables } else { remaining_viewables },
+    ))
+}
+
+#[inline(always)]
+fn write_coverage_gap<'tcx, W>(
+    tcx: TyCtxt<'tcx>,
+    lo: BytePos,
+    hi: BytePos,
+    w: &mut W,
+) -> io::Result<()>
+where
+    W: Write,
+{
+    let span = Span::with_root_ctxt(lo, hi);
+    if let Some(ref html_snippet) = make_html_snippet(tcx, span, None) {
+        write_span(html_snippet, "", false, 0, w)
+    } else {
+        Ok(())
+    }
+}
+
+fn write_span<W>(
+    html_snippet: &str,
+    tooltip: &str,
+    alt: bool,
+    layer: usize,
+    w: &mut W,
+) -> io::Result<()>
+where
+    W: Write,
+{
+    let maybe_alt_class = if layer > 0 {
+        if alt { " odd" } else { " even" }
+    } else {
+        ""
+    };
+    let maybe_title_attr = if !tooltip.is_empty() {
+        format!(" title=\"{}\"", escape_attr(tooltip))
+    } else {
+        "".to_owned()
+    };
+    if layer == 1 {
+        write!(w, "<span>")?;
+    }
+    for (i, line) in html_snippet.lines().enumerate() {
+        if i > 0 {
+            write!(w, "{}", NEW_LINE_SPAN)?;
+        }
+        write!(
+            w,
+            r#"<span class="code{}" style="--layer: {}"{}>{}</span>"#,
+            maybe_alt_class, layer, maybe_title_attr, line
+        )?;
+    }
+    // Check for and translate trailing newlines, because `str::lines()` ignores them
+    if html_snippet.ends_with('\n') {
+        write!(w, "{}", NEW_LINE_SPAN)?;
+    }
+    if layer == 1 {
+        write!(w, "</span>")?;
+    }
+    Ok(())
+}
+
+fn make_html_snippet<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    span: Span,
+    some_viewable: Option<&SpanViewable>,
+) -> Option<String> {
+    let source_map = tcx.sess.source_map();
+    let snippet = source_map
+        .span_to_snippet(span)
+        .unwrap_or_else(|err| bug!("span_to_snippet error for span {:?}: {:?}", span, err));
+    let html_snippet = if let Some(viewable) = some_viewable {
+        let is_head = span.lo() == viewable.span.lo();
+        let is_tail = span.hi() == viewable.span.hi();
+        let mut labeled_snippet = if is_head {
+            format!(r#"<span class="annotation">{}{}</span>"#, viewable.id, ANNOTATION_LEFT_BRACKET)
+        } else {
+            "".to_owned()
+        };
+        if span.is_empty() {
+            if is_head && is_tail {
+                labeled_snippet.push(CARET);
+            }
+        } else {
+            labeled_snippet.push_str(&escape_html(&snippet));
+        };
+        if is_tail {
+            labeled_snippet.push_str(&format!(
+                r#"<span class="annotation">{}{}</span>"#,
+                ANNOTATION_RIGHT_BRACKET, viewable.id
+            ));
+        }
+        labeled_snippet
+    } else {
+        escape_html(&snippet)
+    };
+    if html_snippet.is_empty() { None } else { Some(html_snippet) }
+}
+
+fn tooltip<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    spanview_id: &str,
+    span: Span,
+    statements: Vec<Statement<'tcx>>,
+    terminator: &Option<Terminator<'tcx>>,
+) -> String {
+    let source_map = tcx.sess.source_map();
+    let mut text = Vec::new();
+    text.push(format!("{}: {}:", spanview_id, &source_map.span_to_string(span)));
+    for statement in statements {
+        let source_range = source_range_no_file(tcx, &statement.source_info.span);
+        text.push(format!(
+            "\n{}{}: {}: {}",
+            TOOLTIP_INDENT,
+            source_range,
+            statement_kind_name(&statement),
+            format!("{:?}", statement)
+        ));
+    }
+    if let Some(term) = terminator {
+        let source_range = source_range_no_file(tcx, &term.source_info.span);
+        text.push(format!(
+            "\n{}{}: {}: {:?}",
+            TOOLTIP_INDENT,
+            source_range,
+            terminator_kind_name(term),
+            term.kind
+        ));
+    }
+    text.join("")
+}
+
+fn trim_span(span: Span, from_pos: BytePos, to_pos: BytePos) -> Span {
+    trim_span_hi(trim_span_lo(span, from_pos), to_pos)
+}
+
+fn trim_span_lo(span: Span, from_pos: BytePos) -> Span {
+    if from_pos <= span.lo() { span } else { span.with_lo(cmp::min(span.hi(), from_pos)) }
+}
+
+fn trim_span_hi(span: Span, to_pos: BytePos) -> Span {
+    if to_pos >= span.hi() { span } else { span.with_hi(cmp::max(span.lo(), to_pos)) }
+}
+
+fn fn_span<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Span {
+    let hir_id =
+        tcx.hir().local_def_id_to_hir_id(def_id.as_local().expect("expected DefId is local"));
+    let fn_decl_span = tcx.hir().span(hir_id);
+    let body_span = hir_body(tcx, def_id).value.span;
+    debug_assert_eq!(fn_decl_span.ctxt(), body_span.ctxt());
+    fn_decl_span.to(body_span)
+}
+
+fn hir_body<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx rustc_hir::Body<'tcx> {
+    let hir_node = tcx.hir().get_if_local(def_id).expect("expected DefId is local");
+    let fn_body_id = hir::map::associated_body(hir_node).expect("HIR node is a function with body");
+    tcx.hir().body(fn_body_id)
+}
+
+fn escape_html(s: &str) -> String {
+    s.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
+}
+
+fn escape_attr(s: &str) -> String {
+    s.replace("&", "&amp;")
+        .replace("\"", "&quot;")
+        .replace("'", "&#39;")
+        .replace("<", "&lt;")
+        .replace(">", "&gt;")
+}
diff --git a/compiler/rustc_mir_build/src/build/block.rs b/compiler/rustc_mir_build/src/build/block.rs
index d1cbf209b06..beaf12b1db0 100644
--- a/compiler/rustc_mir_build/src/build/block.rs
+++ b/compiler/rustc_mir_build/src/build/block.rs
@@ -96,8 +96,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     );
                 }
                 StmtKind::Let { remainder_scope, init_scope, pattern, initializer, lint_level } => {
-                    let ignores_expr_result =
-                        if let PatKind::Wild = *pattern.kind { true } else { false };
+                    let ignores_expr_result = matches!(*pattern.kind, PatKind::Wild);
                     this.block_context.push(BlockFrame::Statement { ignores_expr_result });
 
                     // Enter the remainder scope, i.e., the bindings' destruction scope.
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 982aefcf604..244a70f83b0 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_constant.rs
+++ b/compiler/rustc_mir_build/src/build/expr/as_constant.rs
@@ -21,7 +21,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         let Expr { ty, temp_lifetime: _, span, kind } = expr;
         match kind {
             ExprKind::Scope { region_scope: _, lint_level: _, value } => this.as_constant(value),
-            ExprKind::Literal { literal, user_ty } => {
+            ExprKind::Literal { literal, user_ty, const_id: _ } => {
                 let user_ty = user_ty.map(|user_ty| {
                     this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
                         span,
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 1e3e104c2ba..39dbb6dd3ff 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_place.rs
+++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs
@@ -367,7 +367,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         let tcx = self.hir.tcx();
         let place_ty =
             Place::ty_from(base_place.local, &base_place.projection, &self.local_decls, tcx);
-        if let ty::Slice(_) = place_ty.ty.kind {
+        if let ty::Slice(_) = place_ty.ty.kind() {
             // We need to create fake borrows to ensure that the bounds
             // check that we just did stays valid. Since we can't assign to
             // unsized values, we only need to ensure that none of the
@@ -406,7 +406,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                             &self.local_decls,
                             tcx,
                         );
-                        match index_ty.ty.kind {
+                        match index_ty.ty.kind() {
                             // The previous index expression has already
                             // done any index expressions needed here.
                             ty::Slice(_) => break,
diff --git a/compiler/rustc_mir_build/src/build/expr/as_temp.rs b/compiler/rustc_mir_build/src/build/expr/as_temp.rs
index a9cc0cc2f24..9984b527ffd 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_temp.rs
+++ b/compiler/rustc_mir_build/src/build/expr/as_temp.rs
@@ -76,6 +76,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     local_decl.local_info =
                         Some(box LocalInfo::StaticRef { def_id, is_thread_local: true });
                 }
+                ExprKind::Literal { const_id: Some(def_id), .. } => {
+                    local_decl.local_info = Some(box LocalInfo::ConstRef { def_id });
+                }
                 _ => {}
             }
             this.local_decls.push(local_decl)
diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs
index 3d623abfa6e..319fae5009e 100644
--- a/compiler/rustc_mir_build/src/build/expr/into.rs
+++ b/compiler/rustc_mir_build/src/build/expr/into.rs
@@ -168,7 +168,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 exit_block.unit()
             }
             ExprKind::Call { ty, fun, args, from_hir_call, fn_span } => {
-                let intrinsic = match ty.kind {
+                let intrinsic = match *ty.kind() {
                     ty::FnDef(def_id, _) => {
                         let f = ty.fn_sig(this.hir.tcx());
                         if f.abi() == Abi::RustIntrinsic || f.abi() == Abi::PlatformIntrinsic {
diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs
index 3a525d10b08..a9b8a6181d4 100644
--- a/compiler/rustc_mir_build/src/build/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/build/matches/mod.rs
@@ -321,7 +321,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             let target_block = self.cfg.start_new_block();
             let mut schedule_drops = true;
             // We keep a stack of all of the bindings and type asciptions
-            // from the the parent candidates that we visit, that also need to
+            // from the parent candidates that we visit, that also need to
             // be bound for each candidate.
             traverse_candidate(
                 candidate,
@@ -1793,7 +1793,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     .flat_map(|(bindings, _)| bindings)
                     .chain(&candidate.bindings)
                     .filter(|binding| {
-                        if let BindingMode::ByValue = binding.binding_mode { true } else { false }
+                        matches!(binding.binding_mode, BindingMode::ByValue )
                     });
             // Read all of the by reference bindings to ensure that the
             // place they refer to can't be modified by the guard.
diff --git a/compiler/rustc_mir_build/src/build/matches/simplify.rs b/compiler/rustc_mir_build/src/build/matches/simplify.rs
index e584aeb9226..a28a181e935 100644
--- a/compiler/rustc_mir_build/src/build/matches/simplify.rs
+++ b/compiler/rustc_mir_build/src/build/matches/simplify.rs
@@ -154,7 +154,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             }
 
             PatKind::Range(PatRange { lo, hi, end }) => {
-                let (range, bias) = match lo.ty.kind {
+                let (range, bias) = match *lo.ty.kind() {
                     ty::Char => {
                         (Some(('\u{0000}' as u128, '\u{10FFFF}' as u128, Size::from_bits(32))), 0)
                     }
diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs
index c4a87a554a3..d81c3b68f48 100644
--- a/compiler/rustc_mir_build/src/build/matches/test.rs
+++ b/compiler/rustc_mir_build/src/build/matches/test.rs
@@ -215,7 +215,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
 
             TestKind::SwitchInt { switch_ty, ref options } => {
                 let target_blocks = make_target_blocks(self);
-                let terminator = if switch_ty.kind == ty::Bool {
+                let terminator = if *switch_ty.kind() == ty::Bool {
                     assert!(!options.is_empty() && options.len() <= 2);
                     if let [first_bb, second_bb] = *target_blocks {
                         let (true_bb, false_bb) = match options[0] {
@@ -368,8 +368,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         // We want to do this even when the scrutinee is a reference to an
         // array, so we can call `<[u8]>::eq` rather than having to find an
         // `<[u8; N]>::eq`.
-        let unsize = |ty: Ty<'tcx>| match ty.kind {
-            ty::Ref(region, rty, _) => match rty.kind {
+        let unsize = |ty: Ty<'tcx>| match ty.kind() {
+            ty::Ref(region, rty, _) => match rty.kind() {
                 ty::Array(inner_ty, n) => Some((region, inner_ty, n)),
                 _ => None,
             },
@@ -407,7 +407,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             }
         }
 
-        let deref_ty = match ty.kind {
+        let deref_ty = match *ty.kind() {
             ty::Ref(_, deref_ty, _) => deref_ty,
             _ => bug!("non_scalar_compare called on non-reference type: {}", ty),
         };
diff --git a/compiler/rustc_mir_build/src/build/matches/util.rs b/compiler/rustc_mir_build/src/build/matches/util.rs
index c6d39947f7d..4ef88c25cad 100644
--- a/compiler/rustc_mir_build/src/build/matches/util.rs
+++ b/compiler/rustc_mir_build/src/build/matches/util.rs
@@ -31,9 +31,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         suffix: &'pat [Pat<'tcx>],
     ) {
         let tcx = self.hir.tcx();
-        let (min_length, exact_size) = match place.ty(&self.local_decls, tcx).ty.kind {
+        let (min_length, exact_size) = match place.ty(&self.local_decls, tcx).ty.kind() {
             ty::Array(_, length) => {
-                (length.eval_usize(tcx, self.hir.param_env).try_into().unwrap(), true)
+                (length.eval_usize(tcx, self.hir.param_env), true)
             }
             _ => ((prefix.len() + suffix.len()).try_into().unwrap(), false),
         };
diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs
index 249cce0ba19..aa96ae87591 100644
--- a/compiler/rustc_mir_build/src/build/mod.rs
+++ b/compiler/rustc_mir_build/src/build/mod.rs
@@ -96,7 +96,7 @@ fn mir_build(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -> Body<'_
             let body = tcx.hir().body(body_id);
             let ty = tcx.type_of(fn_def_id);
             let mut abi = fn_sig.abi;
-            let implicit_argument = match ty.kind {
+            let implicit_argument = match ty.kind() {
                 ty::Closure(..) => {
                     // HACK(eddyb) Avoid having RustCall on closures,
                     // as it adds unnecessary (and wrong) auto-tupling.
@@ -159,7 +159,7 @@ fn mir_build(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -> Body<'_
 
             let (yield_ty, return_ty) = if body.generator_kind.is_some() {
                 let gen_ty = tcx.typeck_body(body_id).node_type(id);
-                let gen_sig = match gen_ty.kind {
+                let gen_sig = match gen_ty.kind() {
                     ty::Generator(_, gen_substs, ..) => gen_substs.as_generator().sig(),
                     _ => span_bug!(tcx.hir().span(id), "generator w/o generator type: {:?}", ty),
                 };
@@ -228,7 +228,7 @@ fn liberated_closure_env_ty(
 ) -> Ty<'_> {
     let closure_ty = tcx.typeck_body(body_id).node_type(closure_expr_id);
 
-    let (closure_def_id, closure_substs) = match closure_ty.kind {
+    let (closure_def_id, closure_substs) = match *closure_ty.kind() {
         ty::Closure(closure_def_id, closure_substs) => (closure_def_id, closure_substs),
         _ => bug!("closure expr does not have closure type: {:?}", closure_ty),
     };
@@ -840,11 +840,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             let closure_env_arg = Local::new(1);
             let mut closure_env_projs = vec![];
             let mut closure_ty = self.local_decls[closure_env_arg].ty;
-            if let ty::Ref(_, ty, _) = closure_ty.kind {
+            if let ty::Ref(_, ty, _) = closure_ty.kind() {
                 closure_env_projs.push(ProjectionElem::Deref);
                 closure_ty = ty;
             }
-            let upvar_substs = match closure_ty.kind {
+            let upvar_substs = match closure_ty.kind() {
                 ty::Closure(_, substs) => ty::UpvarSubsts::Closure(substs),
                 ty::Generator(_, substs, _) => ty::UpvarSubsts::Generator(substs),
                 _ => span_bug!(self.fn_span, "upvars with non-closure env ty {:?}", closure_ty),
diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs
index 313bb979a51..714041ad4e8 100644
--- a/compiler/rustc_mir_build/src/lib.rs
+++ b/compiler/rustc_mir_build/src/lib.rs
@@ -1,11 +1,12 @@
 //! Construction of MIR from HIR.
 //!
 //! This crate also contains the match exhaustiveness and usefulness checking.
-
+#![feature(array_windows)]
 #![feature(box_patterns)]
 #![feature(box_syntax)]
 #![feature(const_fn)]
 #![feature(const_panic)]
+#![feature(control_flow_enum)]
 #![feature(crate_visibility_modifier)]
 #![feature(bool_to_option)]
 #![feature(or_patterns)]
diff --git a/compiler/rustc_mir_build/src/lints.rs b/compiler/rustc_mir_build/src/lints.rs
index fd2d5a4abd4..a8d7c612a84 100644
--- a/compiler/rustc_mir_build/src/lints.rs
+++ b/compiler/rustc_mir_build/src/lints.rs
@@ -70,7 +70,7 @@ impl<'mir, 'tcx> Search<'mir, 'tcx> {
         let param_env = tcx.param_env(def_id);
 
         let func_ty = func.ty(body, tcx);
-        if let ty::FnDef(fn_def_id, substs) = func_ty.kind {
+        if let ty::FnDef(fn_def_id, substs) = *func_ty.kind() {
             let (call_fn_id, call_substs) =
                 if let Ok(Some(instance)) = Instance::resolve(tcx, param_env, fn_def_id, substs) {
                     (instance.def_id(), instance.substs)
@@ -117,7 +117,7 @@ impl<'mir, 'tcx> TriColorVisitor<&'mir Body<'tcx>> for Search<'mir, 'tcx> {
             // A diverging InlineAsm is treated as non-recursing
             TerminatorKind::InlineAsm { destination, .. } => {
                 if destination.is_some() {
-                    ControlFlow::Continue
+                    ControlFlow::CONTINUE
                 } else {
                     ControlFlow::Break(NonRecursive)
                 }
@@ -131,7 +131,7 @@ impl<'mir, 'tcx> TriColorVisitor<&'mir Body<'tcx>> for Search<'mir, 'tcx> {
             | TerminatorKind::FalseEdge { .. }
             | TerminatorKind::FalseUnwind { .. }
             | TerminatorKind::Goto { .. }
-            | TerminatorKind::SwitchInt { .. } => ControlFlow::Continue,
+            | TerminatorKind::SwitchInt { .. } => ControlFlow::CONTINUE,
         }
     }
 
@@ -144,7 +144,7 @@ impl<'mir, 'tcx> TriColorVisitor<&'mir Body<'tcx>> for Search<'mir, 'tcx> {
             }
         }
 
-        ControlFlow::Continue
+        ControlFlow::CONTINUE
     }
 
     fn ignore_edge(&mut self, bb: BasicBlock, target: BasicBlock) -> bool {
diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs
index dd5515d39b0..a7bb2864daf 100644
--- a/compiler/rustc_mir_build/src/thir/constant.rs
+++ b/compiler/rustc_mir_build/src/thir/constant.rs
@@ -2,7 +2,7 @@ use rustc_ast as ast;
 use rustc_middle::mir::interpret::{
     truncate, Allocation, ConstValue, LitToConstError, LitToConstInput, Scalar,
 };
-use rustc_middle::ty::{self, ParamEnv, TyCtxt, TyS};
+use rustc_middle::ty::{self, ParamEnv, TyCtxt};
 use rustc_span::symbol::Symbol;
 use rustc_target::abi::Size;
 
@@ -21,19 +21,21 @@ crate fn lit_to_const<'tcx>(
         Ok(ConstValue::Scalar(Scalar::from_uint(result, width)))
     };
 
-    let lit = match (lit, &ty.kind) {
-        (ast::LitKind::Str(s, _), ty::Ref(_, TyS { kind: ty::Str, .. }, _)) => {
+    let lit = match (lit, &ty.kind()) {
+        (ast::LitKind::Str(s, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_str() => {
             let s = s.as_str();
             let allocation = Allocation::from_byte_aligned_bytes(s.as_bytes());
             let allocation = tcx.intern_const_alloc(allocation);
             ConstValue::Slice { data: allocation, start: 0, end: s.len() }
         }
-        (ast::LitKind::ByteStr(data), ty::Ref(_, TyS { kind: ty::Slice(_), .. }, _)) => {
+        (ast::LitKind::ByteStr(data), ty::Ref(_, inner_ty, _))
+            if matches!(inner_ty.kind(), ty::Slice(_)) =>
+        {
             let allocation = Allocation::from_byte_aligned_bytes(data as &Vec<u8>);
             let allocation = tcx.intern_const_alloc(allocation);
             ConstValue::Slice { data: allocation, start: 0, end: data.len() }
         }
-        (ast::LitKind::ByteStr(data), ty::Ref(_, TyS { kind: ty::Array(_, _), .. }, _)) => {
+        (ast::LitKind::ByteStr(data), ty::Ref(_, inner_ty, _)) if inner_ty.is_array() => {
             let id = tcx.allocate_bytes(data);
             ConstValue::Scalar(Scalar::Ptr(id.into()))
         }
diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs
index 066e46fec14..13e69474cfb 100644
--- a/compiler/rustc_mir_build/src/thir/cx/expr.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs
@@ -117,7 +117,14 @@ fn apply_adjustment<'a, 'tcx>(
                 },
             };
 
-            overloaded_place(cx, hir_expr, adjustment.target, Some(call), vec![expr.to_ref()])
+            overloaded_place(
+                cx,
+                hir_expr,
+                adjustment.target,
+                Some(call),
+                vec![expr.to_ref()],
+                deref.span,
+            )
         }
         Adjust::Borrow(AutoBorrow::Ref(_, m)) => {
             ExprKind::Borrow { borrow_kind: m.to_borrow_kind(), arg: expr.to_ref() }
@@ -247,6 +254,7 @@ fn make_mirror_unadjusted<'a, 'tcx>(
         hir::ExprKind::Lit(ref lit) => ExprKind::Literal {
             literal: cx.const_eval_literal(&lit.node, expr_ty, lit.span, false),
             user_ty: None,
+            const_id: None,
         },
 
         hir::ExprKind::Binary(op, ref lhs, ref rhs) => {
@@ -276,7 +284,14 @@ fn make_mirror_unadjusted<'a, 'tcx>(
 
         hir::ExprKind::Index(ref lhs, ref index) => {
             if cx.typeck_results().is_method_call(expr) {
-                overloaded_place(cx, expr, expr_ty, None, vec![lhs.to_ref(), index.to_ref()])
+                overloaded_place(
+                    cx,
+                    expr,
+                    expr_ty,
+                    None,
+                    vec![lhs.to_ref(), index.to_ref()],
+                    expr.span,
+                )
             } else {
                 ExprKind::Index { lhs: lhs.to_ref(), index: index.to_ref() }
             }
@@ -284,7 +299,7 @@ fn make_mirror_unadjusted<'a, 'tcx>(
 
         hir::ExprKind::Unary(hir::UnOp::UnDeref, ref arg) => {
             if cx.typeck_results().is_method_call(expr) {
-                overloaded_place(cx, expr, expr_ty, None, vec![arg.to_ref()])
+                overloaded_place(cx, expr, expr_ty, None, vec![arg.to_ref()], expr.span)
             } else {
                 ExprKind::Deref { arg: arg.to_ref() }
             }
@@ -306,6 +321,7 @@ fn make_mirror_unadjusted<'a, 'tcx>(
                     ExprKind::Literal {
                         literal: cx.const_eval_literal(&lit.node, expr_ty, lit.span, true),
                         user_ty: None,
+                        const_id: None,
                     }
                 } else {
                     ExprKind::Unary { op: UnOp::Neg, arg: arg.to_ref() }
@@ -313,7 +329,7 @@ fn make_mirror_unadjusted<'a, 'tcx>(
             }
         }
 
-        hir::ExprKind::Struct(ref qpath, ref fields, ref base) => match expr_ty.kind {
+        hir::ExprKind::Struct(ref qpath, ref fields, ref base) => match expr_ty.kind() {
             ty::Adt(adt, substs) => match adt.adt_kind() {
                 AdtKind::Struct | AdtKind::Union => {
                     let user_provided_types = cx.typeck_results().user_provided_types();
@@ -363,7 +379,7 @@ fn make_mirror_unadjusted<'a, 'tcx>(
 
         hir::ExprKind::Closure(..) => {
             let closure_ty = cx.typeck_results().expr_ty(expr);
-            let (def_id, substs, movability) = match closure_ty.kind {
+            let (def_id, substs, movability) = match *closure_ty.kind() {
                 ty::Closure(def_id, substs) => (def_id, UpvarSubsts::Closure(substs), None),
                 ty::Generator(def_id, substs, movability) => {
                     (def_id, UpvarSubsts::Generator(substs), Some(movability))
@@ -447,6 +463,7 @@ fn make_mirror_unadjusted<'a, 'tcx>(
                                             kind: ExprKind::Literal {
                                                 literal: ty::Const::zero_sized(cx.tcx, ty),
                                                 user_ty,
+                                                const_id: None,
                                             },
                                         }
                                         .to_ref(),
@@ -473,6 +490,7 @@ fn make_mirror_unadjusted<'a, 'tcx>(
                                             kind: ExprKind::Literal {
                                                 literal: ty::Const::zero_sized(cx.tcx, ty),
                                                 user_ty: None,
+                                                const_id: None,
                                             },
                                         }
                                         .to_ref(),
@@ -585,7 +603,7 @@ fn make_mirror_unadjusted<'a, 'tcx>(
                             temp_lifetime,
                             ty: var_ty,
                             span: expr.span,
-                            kind: ExprKind::Literal { literal, user_ty: None },
+                            kind: ExprKind::Literal { literal, user_ty: None, const_id: None },
                         }
                         .to_ref()
                     };
@@ -714,7 +732,11 @@ fn method_callee<'a, 'tcx>(
         temp_lifetime,
         ty,
         span,
-        kind: ExprKind::Literal { literal: ty::Const::zero_sized(cx.tcx(), ty), user_ty },
+        kind: ExprKind::Literal {
+            literal: ty::Const::zero_sized(cx.tcx(), ty),
+            user_ty,
+            const_id: None,
+        },
     }
 }
 
@@ -777,6 +799,7 @@ fn convert_path_expr<'a, 'tcx>(
             ExprKind::Literal {
                 literal: ty::Const::zero_sized(cx.tcx, cx.typeck_results().node_type(expr.hir_id)),
                 user_ty,
+                const_id: None,
             }
         }
 
@@ -794,6 +817,7 @@ fn convert_path_expr<'a, 'tcx>(
                     .tcx
                     .mk_const(ty::Const { val, ty: cx.typeck_results().node_type(expr.hir_id) }),
                 user_ty: None,
+                const_id: Some(def_id),
             }
         }
 
@@ -810,6 +834,7 @@ fn convert_path_expr<'a, 'tcx>(
                     ty: cx.typeck_results().node_type(expr.hir_id),
                 }),
                 user_ty,
+                const_id: Some(def_id),
             }
         }
 
@@ -818,7 +843,7 @@ fn convert_path_expr<'a, 'tcx>(
             let user_provided_type = user_provided_types.get(expr.hir_id).copied();
             debug!("convert_path_expr: user_provided_type={:?}", user_provided_type);
             let ty = cx.typeck_results().node_type(expr.hir_id);
-            match ty.kind {
+            match ty.kind() {
                 // A unit struct/variant which is used as a value.
                 // We return a completely different ExprKind here to account for this special case.
                 ty::Adt(adt_def, substs) => ExprKind::Adt {
@@ -899,7 +924,7 @@ fn convert_var<'tcx>(
             });
             let region = cx.tcx.mk_region(region);
 
-            let self_expr = if let ty::Closure(_, closure_substs) = closure_ty.kind {
+            let self_expr = if let ty::Closure(_, closure_substs) = closure_ty.kind() {
                 match cx.infcx.closure_kind(closure_substs).unwrap() {
                     ty::ClosureKind::Fn => {
                         let ref_closure_ty = cx.tcx.mk_ref(
@@ -1014,6 +1039,7 @@ fn overloaded_place<'a, 'tcx>(
     place_ty: Ty<'tcx>,
     overloaded_callee: Option<(DefId, SubstsRef<'tcx>)>,
     args: Vec<ExprRef<'tcx>>,
+    span: Span,
 ) -> ExprKind<'tcx> {
     // For an overloaded *x or x[y] expression of type T, the method
     // call returns an &T and we must add the deref so that the types
@@ -1027,26 +1053,26 @@ fn overloaded_place<'a, 'tcx>(
     // Reconstruct the output assuming it's a reference with the
     // same region and mutability as the receiver. This holds for
     // `Deref(Mut)::Deref(_mut)` and `Index(Mut)::index(_mut)`.
-    let (region, mutbl) = match recv_ty.kind {
+    let (region, mutbl) = match *recv_ty.kind() {
         ty::Ref(region, _, mutbl) => (region, mutbl),
-        _ => span_bug!(expr.span, "overloaded_place: receiver is not a reference"),
+        _ => span_bug!(span, "overloaded_place: receiver is not a reference"),
     };
     let ref_ty = cx.tcx.mk_ref(region, ty::TypeAndMut { ty: place_ty, mutbl });
 
     // construct the complete expression `foo()` for the overloaded call,
     // which will yield the &T type
     let temp_lifetime = cx.region_scope_tree.temporary_scope(expr.hir_id.local_id);
-    let fun = method_callee(cx, expr, expr.span, overloaded_callee);
+    let fun = method_callee(cx, expr, span, overloaded_callee);
     let ref_expr = Expr {
         temp_lifetime,
         ty: ref_ty,
-        span: expr.span,
+        span,
         kind: ExprKind::Call {
             ty: fun.ty,
             fun: fun.to_ref(),
             args,
             from_hir_call: false,
-            fn_span: expr.span,
+            fn_span: span,
         },
     };
 
diff --git a/compiler/rustc_mir_build/src/thir/mod.rs b/compiler/rustc_mir_build/src/thir/mod.rs
index 2837bfa040f..4d57fd5c64f 100644
--- a/compiler/rustc_mir_build/src/thir/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/mod.rs
@@ -273,6 +273,10 @@ crate enum ExprKind<'tcx> {
     Literal {
         literal: &'tcx Const<'tcx>,
         user_ty: Option<Canonical<'tcx, UserType<'tcx>>>,
+        /// The `DefId` of the `const` item this literal
+        /// was produced from, if this is not a user-written
+        /// literal value.
+        const_id: Option<DefId>,
     },
     /// A literal containing the address of a `static`.
     ///
diff --git a/compiler/rustc_mir_build/src/thir/pattern/_match.rs b/compiler/rustc_mir_build/src/thir/pattern/_match.rs
index 3202f7d1b1b..904524e13ae 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/_match.rs
@@ -139,10 +139,10 @@
 //!
 //!    It is computed as follows. We look at the pattern `p_1` on top of the stack,
 //!    and we have three cases:
-//!         1.1. `p_1 = c(r_1, .., r_a)`. We discard the current stack and return nothing.
-//!         1.2. `p_1 = _`. We return the rest of the stack:
+//!         2.1. `p_1 = c(r_1, .., r_a)`. We discard the current stack and return nothing.
+//!         2.2. `p_1 = _`. We return the rest of the stack:
 //!                 p_2, .., p_n
-//!         1.3. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting
+//!         2.3. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting
 //!           stack.
 //!                 D((r_1, p_2, .., p_n))
 //!                 D((r_2, p_2, .., p_n))
@@ -276,7 +276,7 @@ use self::Usefulness::*;
 use self::WitnessPreference::*;
 
 use rustc_data_structures::captures::Captures;
-use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_index::vec::Idx;
 
 use super::{compare_const_vals, PatternFoldable, PatternFolder};
@@ -327,7 +327,7 @@ impl<'tcx> LiteralExpander<'tcx> {
         crty: Ty<'tcx>,
     ) -> ConstValue<'tcx> {
         debug!("fold_const_value_deref {:?} {:?} {:?}", val, rty, crty);
-        match (val, &crty.kind, &rty.kind) {
+        match (val, &crty.kind(), &rty.kind()) {
             // the easy case, deref a reference
             (ConstValue::Scalar(p), x, y) if x == y => {
                 match p {
@@ -368,41 +368,35 @@ impl<'tcx> LiteralExpander<'tcx> {
 
 impl<'tcx> PatternFolder<'tcx> for LiteralExpander<'tcx> {
     fn fold_pattern(&mut self, pat: &Pat<'tcx>) -> Pat<'tcx> {
-        debug!("fold_pattern {:?} {:?} {:?}", pat, pat.ty.kind, pat.kind);
-        match (&pat.ty.kind, &*pat.kind) {
-            (
-                &ty::Ref(_, rty, _),
-                &PatKind::Constant {
-                    value:
-                        Const {
-                            val: ty::ConstKind::Value(val),
-                            ty: ty::TyS { kind: ty::Ref(_, crty, _), .. },
-                        },
-                },
-            ) => Pat {
-                ty: pat.ty,
-                span: pat.span,
-                kind: box PatKind::Deref {
-                    subpattern: Pat {
-                        ty: rty,
+        debug!("fold_pattern {:?} {:?} {:?}", pat, pat.ty.kind(), pat.kind);
+        match (pat.ty.kind(), &*pat.kind) {
+            (&ty::Ref(_, rty, _), &PatKind::Constant { value: Const { val, ty: const_ty } })
+                if const_ty.is_ref() =>
+            {
+                let crty =
+                    if let ty::Ref(_, crty, _) = const_ty.kind() { crty } else { unreachable!() };
+                if let ty::ConstKind::Value(val) = val {
+                    Pat {
+                        ty: pat.ty,
                         span: pat.span,
-                        kind: box PatKind::Constant {
-                            value: Const::from_value(
-                                self.tcx,
-                                self.fold_const_value_deref(*val, rty, crty),
-                                rty,
-                            ),
+                        kind: box PatKind::Deref {
+                            subpattern: Pat {
+                                ty: rty,
+                                span: pat.span,
+                                kind: box PatKind::Constant {
+                                    value: Const::from_value(
+                                        self.tcx,
+                                        self.fold_const_value_deref(*val, rty, crty),
+                                        rty,
+                                    ),
+                                },
+                            },
                         },
-                    },
-                },
-            },
-
-            (
-                &ty::Ref(_, rty, _),
-                &PatKind::Constant {
-                    value: Const { val, ty: ty::TyS { kind: ty::Ref(_, crty, _), .. } },
-                },
-            ) => bug!("cannot deref {:#?}, {} -> {}", val, crty, rty),
+                    }
+                } else {
+                    bug!("cannot deref {:#?}, {} -> {}", val, crty, rty)
+                }
+            }
 
             (_, &PatKind::Binding { subpattern: Some(ref s), .. }) => s.fold_with(self),
             (_, &PatKind::AscribeUserType { subpattern: ref s, .. }) => s.fold_with(self),
@@ -422,7 +416,7 @@ impl<'tcx> Pat<'tcx> {
 
 /// A row of a matrix. Rows of len 1 are very common, which is why `SmallVec[_; 2]`
 /// works well.
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, PartialEq)]
 crate struct PatStack<'p, 'tcx>(SmallVec<[&'p Pat<'tcx>; 2]>);
 
 impl<'p, 'tcx> PatStack<'p, 'tcx> {
@@ -510,13 +504,36 @@ impl<'p, 'tcx> FromIterator<&'p Pat<'tcx>> for PatStack<'p, 'tcx> {
     }
 }
 
+/// Depending on the match patterns, the specialization process might be able to use a fast path.
+/// Tracks whether we can use the fast path and the lookup table needed in those cases.
+#[derive(Clone, Debug, PartialEq)]
+enum SpecializationCache {
+    /// Patterns consist of only enum variants.
+    /// Variant patterns does not intersect with each other (in contrast to range patterns),
+    /// so it is possible to precompute the result of `Matrix::specialize_constructor` at a
+    /// lower computational complexity.
+    /// `lookup` is responsible for holding the precomputed result of
+    /// `Matrix::specialize_constructor`, while `wilds` is used for two purposes: the first one is
+    /// the precomputed result of `Matrix::specialize_wildcard`, and the second is to be used as a
+    /// fallback for `Matrix::specialize_constructor` when it tries to apply a constructor that
+    /// has not been seen in the `Matrix`. See `update_cache` for further explanations.
+    Variants { lookup: FxHashMap<DefId, SmallVec<[usize; 1]>>, wilds: SmallVec<[usize; 1]> },
+    /// Does not belong to the cases above, use the slow path.
+    Incompatible,
+}
+
 /// A 2D matrix.
-#[derive(Clone)]
-crate struct Matrix<'p, 'tcx>(Vec<PatStack<'p, 'tcx>>);
+#[derive(Clone, PartialEq)]
+crate struct Matrix<'p, 'tcx> {
+    patterns: Vec<PatStack<'p, 'tcx>>,
+    cache: SpecializationCache,
+}
 
 impl<'p, 'tcx> Matrix<'p, 'tcx> {
     crate fn empty() -> Self {
-        Matrix(vec![])
+        // Use `SpecializationCache::Incompatible` as a placeholder; we will initialize it on the
+        // first call to `push`. See the first half of `update_cache`.
+        Matrix { patterns: vec![], cache: SpecializationCache::Incompatible }
     }
 
     /// Pushes a new row to the matrix. If the row starts with an or-pattern, this expands it.
@@ -528,18 +545,101 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
                 self.push(row)
             }
         } else {
-            self.0.push(row);
+            self.patterns.push(row);
+            self.update_cache(self.patterns.len() - 1);
+        }
+    }
+
+    fn update_cache(&mut self, idx: usize) {
+        let row = &self.patterns[idx];
+        // We don't know which kind of cache could be used until we see the first row; therefore an
+        // empty `Matrix` is initialized with `SpecializationCache::Empty`, then the cache is
+        // assigned the appropriate variant below on the first call to `push`.
+        if self.patterns.is_empty() {
+            self.cache = if row.is_empty() {
+                SpecializationCache::Incompatible
+            } else {
+                match *row.head().kind {
+                    PatKind::Variant { .. } => SpecializationCache::Variants {
+                        lookup: FxHashMap::default(),
+                        wilds: SmallVec::new(),
+                    },
+                    // Note: If the first pattern is a wildcard, then all patterns after that is not
+                    // useful. The check is simple enough so we treat it as the same as unsupported
+                    // patterns.
+                    _ => SpecializationCache::Incompatible,
+                }
+            };
+        }
+        // Update the cache.
+        match &mut self.cache {
+            SpecializationCache::Variants { ref mut lookup, ref mut wilds } => {
+                let head = row.head();
+                match *head.kind {
+                    _ if head.is_wildcard() => {
+                        // Per rule 1.3 in the top-level comments, a wildcard pattern is included in
+                        // the result of `specialize_constructor` for *any* `Constructor`.
+                        // We push the wildcard pattern to the precomputed result for constructors
+                        // that we have seen before; results for constructors we have not yet seen
+                        // defaults to `wilds`, which is updated right below.
+                        for (_, v) in lookup.iter_mut() {
+                            v.push(idx);
+                        }
+                        // Per rule 2.1 and 2.2 in the top-level comments, only wildcard patterns
+                        // are included in the result of `specialize_wildcard`.
+                        // What we do here is to track the wildcards we have seen; so in addition to
+                        // acting as the precomputed result of `specialize_wildcard`, `wilds` also
+                        // serves as the default value of `specialize_constructor` for constructors
+                        // that are not in `lookup`.
+                        wilds.push(idx);
+                    }
+                    PatKind::Variant { adt_def, variant_index, .. } => {
+                        // Handle the cases of rule 1.1 and 1.2 in the top-level comments.
+                        // A variant pattern can only be included in the results of
+                        // `specialize_constructor` for a particular constructor, therefore we are
+                        // using a HashMap to track that.
+                        lookup
+                            .entry(adt_def.variants[variant_index].def_id)
+                            // Default to `wilds` for absent keys. See above for an explanation.
+                            .or_insert_with(|| wilds.clone())
+                            .push(idx);
+                    }
+                    _ => {
+                        self.cache = SpecializationCache::Incompatible;
+                    }
+                }
+            }
+            SpecializationCache::Incompatible => {}
         }
     }
 
     /// Iterate over the first component of each row
     fn heads<'a>(&'a self) -> impl Iterator<Item = &'a Pat<'tcx>> + Captures<'p> {
-        self.0.iter().map(|r| r.head())
+        self.patterns.iter().map(|r| r.head())
     }
 
     /// This computes `D(self)`. See top of the file for explanations.
     fn specialize_wildcard(&self) -> Self {
-        self.0.iter().filter_map(|r| r.specialize_wildcard()).collect()
+        match &self.cache {
+            SpecializationCache::Variants { wilds, .. } => {
+                let result =
+                    wilds.iter().filter_map(|&i| self.patterns[i].specialize_wildcard()).collect();
+                // When debug assertions are enabled, check the results against the "slow path"
+                // result.
+                debug_assert_eq!(
+                    result,
+                    Self {
+                        patterns: self.patterns.clone(),
+                        cache: SpecializationCache::Incompatible
+                    }
+                    .specialize_wildcard()
+                );
+                result
+            }
+            SpecializationCache::Incompatible => {
+                self.patterns.iter().filter_map(|r| r.specialize_wildcard()).collect()
+            }
+        }
     }
 
     /// This computes `S(constructor, self)`. See top of the file for explanations.
@@ -549,10 +649,47 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
         constructor: &Constructor<'tcx>,
         ctor_wild_subpatterns: &Fields<'p, 'tcx>,
     ) -> Matrix<'p, 'tcx> {
-        self.0
-            .iter()
-            .filter_map(|r| r.specialize_constructor(cx, constructor, ctor_wild_subpatterns))
-            .collect()
+        match &self.cache {
+            SpecializationCache::Variants { lookup, wilds } => {
+                let result: Self = if let Constructor::Variant(id) = constructor {
+                    lookup
+                        .get(id)
+                        // Default to `wilds` for absent keys. See `update_cache` for an explanation.
+                        .unwrap_or(&wilds)
+                        .iter()
+                        .filter_map(|&i| {
+                            self.patterns[i].specialize_constructor(
+                                cx,
+                                constructor,
+                                ctor_wild_subpatterns,
+                            )
+                        })
+                        .collect()
+                } else {
+                    unreachable!()
+                };
+                // When debug assertions are enabled, check the results against the "slow path"
+                // result.
+                debug_assert_eq!(
+                    result,
+                    Matrix {
+                        patterns: self.patterns.clone(),
+                        cache: SpecializationCache::Incompatible
+                    }
+                    .specialize_constructor(
+                        cx,
+                        constructor,
+                        ctor_wild_subpatterns
+                    )
+                );
+                result
+            }
+            SpecializationCache::Incompatible => self
+                .patterns
+                .iter()
+                .filter_map(|r| r.specialize_constructor(cx, constructor, ctor_wild_subpatterns))
+                .collect(),
+        }
     }
 }
 
@@ -574,7 +711,7 @@ impl<'p, 'tcx> fmt::Debug for Matrix<'p, 'tcx> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         write!(f, "\n")?;
 
-        let &Matrix(ref m) = self;
+        let Matrix { patterns: m, .. } = self;
         let pretty_printed_matrix: Vec<Vec<String>> =
             m.iter().map(|row| row.iter().map(|pat| format!("{:?}", pat)).collect()).collect();
 
@@ -639,7 +776,7 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
 
     /// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`.
     crate fn is_foreign_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool {
-        match ty.kind {
+        match ty.kind() {
             ty::Adt(def, ..) => {
                 def.is_enum() && def.is_variant_list_non_exhaustive() && !def.did.is_local()
             }
@@ -920,14 +1057,14 @@ impl<'tcx> Constructor<'tcx> {
         let mut subpatterns = fields.all_patterns();
 
         let pat = match self {
-            Single | Variant(_) => match ty.kind {
+            Single | Variant(_) => match ty.kind() {
                 ty::Adt(..) | ty::Tuple(..) => {
                     let subpatterns = subpatterns
                         .enumerate()
                         .map(|(i, p)| FieldPat { field: Field::new(i), pattern: p })
                         .collect();
 
-                    if let ty::Adt(adt, substs) = ty.kind {
+                    if let ty::Adt(adt, substs) = ty.kind() {
                         if adt.is_enum() {
                             PatKind::Variant {
                                 adt_def: adt,
@@ -1074,7 +1211,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
         let wildcard_from_ty = |ty| &*cx.pattern_arena.alloc(Pat::wildcard_from_ty(ty));
 
         let ret = match constructor {
-            Single | Variant(_) => match ty.kind {
+            Single | Variant(_) => match ty.kind() {
                 ty::Tuple(ref fs) => {
                     Fields::wildcards_from_tys(cx, fs.into_iter().map(|ty| ty.expect_ty()))
                 }
@@ -1125,7 +1262,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
                 }
                 _ => Fields::empty(),
             },
-            Slice(slice) => match ty.kind {
+            Slice(slice) => match *ty.kind() {
                 ty::Slice(ty) | ty::Array(ty, _) => {
                     let arity = slice.arity();
                     Fields::wildcards_from_tys(cx, (0..arity).map(|_| ty))
@@ -1443,7 +1580,7 @@ fn all_constructors<'a, 'tcx>(
                 .unwrap(),
         )
     };
-    match pcx.ty.kind {
+    match *pcx.ty.kind() {
         ty::Bool => {
             [true, false].iter().map(|&b| ConstantValue(ty::Const::from_bool(cx.tcx, b))).collect()
         }
@@ -1558,7 +1695,7 @@ struct IntRange<'tcx> {
 impl<'tcx> IntRange<'tcx> {
     #[inline]
     fn is_integral(ty: Ty<'_>) -> bool {
-        match ty.kind {
+        match ty.kind() {
             ty::Char | ty::Int(_) | ty::Uint(_) => true,
             _ => false,
         }
@@ -1580,7 +1717,7 @@ impl<'tcx> IntRange<'tcx> {
 
     #[inline]
     fn integral_size_and_signed_bias(tcx: TyCtxt<'tcx>, ty: Ty<'_>) -> Option<(Size, u128)> {
-        match ty.kind {
+        match *ty.kind() {
             ty::Char => Some((Size::from_bytes(4), 0)),
             ty::Int(ity) => {
                 let size = Integer::from_attr(&tcx, SignedInt(ity)).size();
@@ -1658,7 +1795,7 @@ impl<'tcx> IntRange<'tcx> {
 
     // The return value of `signed_bias` should be XORed with an endpoint to encode/decode it.
     fn signed_bias(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> u128 {
-        match ty.kind {
+        match *ty.kind() {
             ty::Int(ity) => {
                 let bits = Integer::from_attr(&tcx, SignedInt(ity)).size().bits() as u128;
                 1u128 << (bits - 1)
@@ -1830,7 +1967,7 @@ crate fn is_useful<'p, 'tcx>(
     is_under_guard: bool,
     is_top_level: bool,
 ) -> Usefulness<'tcx> {
-    let &Matrix(ref rows) = matrix;
+    let Matrix { patterns: rows, .. } = matrix;
     debug!("is_useful({:#?}, {:#?})", matrix, v);
 
     // The base case. We are pattern-matching on () and the return value is
@@ -2070,7 +2207,7 @@ fn pat_constructor<'tcx>(
             if let Some(int_range) = IntRange::from_const(tcx, param_env, value, pat.span) {
                 Some(IntRange(int_range))
             } else {
-                match (value.val, &value.ty.kind) {
+                match (value.val, &value.ty.kind()) {
                     (_, ty::Array(_, n)) => {
                         let len = n.eval_usize(tcx, param_env);
                         Some(Slice(Slice { array_len: Some(len), kind: FixedLen(len) }))
@@ -2102,7 +2239,7 @@ fn pat_constructor<'tcx>(
         }
         PatKind::Array { ref prefix, ref slice, ref suffix }
         | PatKind::Slice { ref prefix, ref slice, ref suffix } => {
-            let array_len = match pat.ty.kind {
+            let array_len = match pat.ty.kind() {
                 ty::Array(_, length) => Some(length.eval_usize(tcx, param_env)),
                 ty::Slice(_) => None,
                 _ => span_bug!(pat.span, "bad ty {:?} for slice pattern", pat.ty),
@@ -2141,7 +2278,7 @@ fn slice_pat_covered_by_const<'tcx>(
         )
     };
 
-    let data: &[u8] = match (const_val_val, &const_val.ty.kind) {
+    let data: &[u8] = match (const_val_val, &const_val.ty.kind()) {
         (ConstValue::ByRef { offset, alloc, .. }, ty::Array(t, n)) => {
             assert_eq!(*t, tcx.types.u8);
             let n = n.eval_usize(tcx, param_env);
@@ -2272,7 +2409,7 @@ fn split_grouped_constructors<'p, 'tcx>(
                 // `borders` is the set of borders between equivalence classes: each equivalence
                 // class lies between 2 borders.
                 let row_borders = matrix
-                    .0
+                    .patterns
                     .iter()
                     .flat_map(|row| {
                         IntRange::from_pat(tcx, param_env, row.head()).map(|r| (r, row.len()))
@@ -2305,19 +2442,19 @@ fn split_grouped_constructors<'p, 'tcx>(
                 // interval into a constructor.
                 split_ctors.extend(
                     borders
-                        .windows(2)
-                        .filter_map(|window| match (window[0], window[1]) {
-                            (Border::JustBefore(n), Border::JustBefore(m)) => {
+                        .array_windows()
+                        .filter_map(|&pair| match pair {
+                            [Border::JustBefore(n), Border::JustBefore(m)] => {
                                 if n < m {
                                     Some(IntRange { range: n..=(m - 1), ty, span })
                                 } else {
                                     None
                                 }
                             }
-                            (Border::JustBefore(n), Border::AfterMax) => {
+                            [Border::JustBefore(n), Border::AfterMax] => {
                                 Some(IntRange { range: n..=u128::MAX, ty, span })
                             }
-                            (Border::AfterMax, _) => None,
+                            [Border::AfterMax, _] => None,
                         })
                         .map(IntRange),
                 );
@@ -2561,7 +2698,7 @@ fn specialize_one_pattern<'p, 'tcx>(
             // elements don't necessarily point to memory, they are usually
             // just integers. The only time they should be pointing to memory
             // is when they are subslices of nonzero slices.
-            let (alloc, offset, n, ty) = match value.ty.kind {
+            let (alloc, offset, n, ty) = match value.ty.kind() {
                 ty::Array(t, n) => {
                     let n = n.eval_usize(cx.tcx, cx.param_env);
                     // Shortcut for `n == 0` where no matter what `alloc` and `offset` we produce,
diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
index 4e7108667e1..047bf7db4c8 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -289,7 +289,7 @@ fn check_for_bindings_named_same_as_variants(cx: &MatchVisitor<'_, '_>, pat: &Pa
                 cx.typeck_results.extract_binding_mode(cx.tcx.sess, p.hir_id, p.span)
             {
                 let pat_ty = cx.typeck_results.pat_ty(p).peel_refs();
-                if let ty::Adt(edef, _) = pat_ty.kind {
+                if let ty::Adt(edef, _) = pat_ty.kind() {
                     if edef.is_enum()
                         && edef.variants.iter().any(|variant| {
                             variant.ident == ident && variant.ctor_kind == CtorKind::Const
@@ -442,7 +442,7 @@ fn check_exhaustive<'p, 'tcx>(
     // In the absence of the `exhaustive_patterns` feature, empty matches are not detected by
     // `is_useful` to exhaustively match uninhabited types, so we manually check here.
     if is_empty_match && !cx.tcx.features().exhaustive_patterns {
-        let scrutinee_is_visibly_uninhabited = match scrut_ty.kind {
+        let scrutinee_is_visibly_uninhabited = match scrut_ty.kind() {
             ty::Never => true,
             ty::Adt(def, _) => {
                 def.is_enum()
@@ -462,7 +462,7 @@ fn check_exhaustive<'p, 'tcx>(
         Err(err) => err,
     };
 
-    let non_empty_enum = match scrut_ty.kind {
+    let non_empty_enum = match scrut_ty.kind() {
         ty::Adt(def, _) => def.is_enum() && !def.variants.is_empty(),
         _ => false,
     };
@@ -541,7 +541,7 @@ fn adt_defined_here(
     witnesses: &[super::Pat<'_>],
 ) {
     let ty = ty.peel_refs();
-    if let ty::Adt(def, _) = ty.kind {
+    if let ty::Adt(def, _) = ty.kind() {
         if let Some(sp) = cx.tcx.hir().span_if_local(def.did) {
             err.span_label(sp, format!("`{}` defined here", ty));
         }
@@ -556,7 +556,7 @@ fn adt_defined_here(
 
 fn maybe_point_at_variant(ty: Ty<'_>, patterns: &[super::Pat<'_>]) -> Vec<Span> {
     let mut covered = vec![];
-    if let ty::Adt(def, _) = ty.kind {
+    if let ty::Adt(def, _) = ty.kind() {
         // Don't point at variants that have already been covered due to other patterns to avoid
         // visual clutter.
         for pattern in patterns {
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 f6d3ccc1ae0..2816bad7eab 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
@@ -2,6 +2,7 @@ use rustc_hir as hir;
 use rustc_index::vec::Idx;
 use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
 use rustc_middle::mir::Field;
+use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_session::lint;
 use rustc_span::Span;
@@ -118,7 +119,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
             }
 
             if let Some(non_sm_ty) = structural {
-                let msg = match non_sm_ty {
+                let msg = with_no_trimmed_paths(|| match non_sm_ty {
                     traits::NonStructuralMatchTy::Adt(adt_def) => {
                         let path = self.tcx().def_path_str(adt_def.did);
                         format!(
@@ -148,7 +149,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
                     traits::NonStructuralMatchTy::Foreign => {
                         bug!("use of a value of a foreign type inside a pattern")
                     }
-                };
+                });
 
                 // double-check there even *is* a semantic `PartialEq` to dispatch to.
                 //
@@ -216,7 +217,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
                 .collect()
         };
 
-        let kind = match cv.ty.kind {
+        let kind = match cv.ty.kind() {
             ty::Float(_) => {
                 tcx.struct_span_lint_hir(
                     lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
@@ -246,11 +247,9 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
                 PatKind::Wild
             }
             // keep old code until future-compat upgraded to errors.
-            ty::Ref(_, adt_ty @ ty::TyS { kind: ty::Adt(_, _), .. }, _)
-                if !self.type_marked_structural(adt_ty) =>
-            {
+            ty::Ref(_, adt_ty, _) if adt_ty.is_adt() && !self.type_marked_structural(adt_ty) => {
                 let adt_def =
-                    if let ty::Adt(adt_def, _) = adt_ty.kind { adt_def } else { unreachable!() };
+                    if let ty::Adt(adt_def, _) = adt_ty.kind() { adt_def } else { unreachable!() };
 
                 debug!(
                     "adt_def {:?} has !type_marked_structural for adt_ty: {:?}",
diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
index c163cb0e60c..718ed78889f 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -39,19 +39,19 @@ crate enum PatternError {
     NonConstPath(Span),
 }
 
-#[derive(Copy, Clone, Debug)]
+#[derive(Copy, Clone, Debug, PartialEq)]
 crate enum BindingMode {
     ByValue,
     ByRef(BorrowKind),
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, PartialEq)]
 crate struct FieldPat<'tcx> {
     crate field: Field,
     crate pattern: Pat<'tcx>,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, PartialEq)]
 crate struct Pat<'tcx> {
     crate ty: Ty<'tcx>,
     crate span: Span,
@@ -116,7 +116,7 @@ crate struct Ascription<'tcx> {
     crate user_ty_span: Span,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, PartialEq)]
 crate enum PatKind<'tcx> {
     Wild,
 
@@ -237,7 +237,7 @@ impl<'tcx> fmt::Display for Pat<'tcx> {
                         Some(&adt_def.variants[variant_index])
                     }
                     _ => {
-                        if let ty::Adt(adt, _) = self.ty.kind {
+                        if let ty::Adt(adt, _) = self.ty.kind() {
                             if !adt.is_enum() {
                                 Some(&adt.variants[VariantIdx::new(0)])
                             } else {
@@ -302,7 +302,7 @@ impl<'tcx> fmt::Display for Pat<'tcx> {
                 Ok(())
             }
             PatKind::Deref { ref subpattern } => {
-                match self.ty.kind {
+                match self.ty.kind() {
                     ty::Adt(def, _) if def.is_box() => write!(f, "box ")?,
                     ty::Ref(_, _, mutbl) => {
                         write!(f, "&{}", mutbl.prefix_str())?;
@@ -559,7 +559,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
             }
 
             hir::PatKind::Tuple(ref pats, ddpos) => {
-                let tys = match ty.kind {
+                let tys = match ty.kind() {
                     ty::Tuple(ref tys) => tys,
                     _ => span_bug!(pat.span, "unexpected type for tuple pattern: {:?}", ty),
                 };
@@ -588,7 +588,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
                 // x's type, which is &T, where we want T (the type being matched).
                 let var_ty = ty;
                 if let ty::BindByReference(_) = bm {
-                    if let ty::Ref(_, rty, _) = ty.kind {
+                    if let ty::Ref(_, rty, _) = ty.kind() {
                         ty = rty;
                     } else {
                         bug!("`ref {}` has wrong type {}", ident, ty);
@@ -608,7 +608,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
 
             hir::PatKind::TupleStruct(ref qpath, ref pats, ddpos) => {
                 let res = self.typeck_results.qpath_res(qpath, pat.hir_id);
-                let adt_def = match ty.kind {
+                let adt_def = match ty.kind() {
                     ty::Adt(adt_def, _) => adt_def,
                     _ => span_bug!(pat.span, "tuple struct pattern not applied to an ADT {:?}", ty),
                 };
@@ -670,7 +670,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
         let prefix = self.lower_patterns(prefix);
         let slice = self.lower_opt_pattern(slice);
         let suffix = self.lower_patterns(suffix);
-        match ty.kind {
+        match ty.kind() {
             // Matching a slice, `[T]`.
             ty::Slice(..) => PatKind::Slice { prefix, slice, suffix },
             // Fixed-length array, `[T; len]`.
@@ -704,7 +704,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
                 let enum_id = self.tcx.parent(variant_id).unwrap();
                 let adt_def = self.tcx.adt_def(enum_id);
                 if adt_def.is_enum() {
-                    let substs = match ty.kind {
+                    let substs = match ty.kind() {
                         ty::Adt(_, substs) | ty::FnDef(_, substs) => substs,
                         ty::Error(_) => {
                             // Avoid ICE (#50585)
@@ -1058,7 +1058,7 @@ crate fn compare_const_vals<'tcx>(
 
     if let (Some(a), Some(b)) = (a_bits, b_bits) {
         use rustc_apfloat::Float;
-        return match ty.kind {
+        return match *ty.kind() {
             ty::Float(ast::FloatTy::F32) => {
                 let l = ::rustc_apfloat::ieee::Single::from_bits(a);
                 let r = ::rustc_apfloat::ieee::Single::from_bits(b);
@@ -1081,7 +1081,7 @@ crate fn compare_const_vals<'tcx>(
         };
     }
 
-    if let ty::Str = ty.kind {
+    if let ty::Str = ty.kind() {
         if let (
             ty::ConstKind::Value(a_val @ ConstValue::Slice { .. }),
             ty::ConstKind::Value(b_val @ ConstValue::Slice { .. }),
diff --git a/compiler/rustc_mir_build/src/thir/util.rs b/compiler/rustc_mir_build/src/thir/util.rs
index 7de60ddda41..aea8667314f 100644
--- a/compiler/rustc_mir_build/src/thir/util.rs
+++ b/compiler/rustc_mir_build/src/thir/util.rs
@@ -17,7 +17,7 @@ crate trait UserAnnotatedTyHelpers<'tcx> {
         let mut user_ty = *user_provided_types.get(hir_id)?;
         debug!("user_subts_applied_to_ty_of_hir_id: user_ty={:?}", user_ty);
         let ty = self.typeck_results().node_type(hir_id);
-        match ty.kind {
+        match ty.kind() {
             ty::Adt(adt_def, ..) => {
                 if let UserType::TypeOf(ref mut did, _) = &mut user_ty.value {
                     *did = adt_def.did;
diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs
index a65d3446819..32b124970cf 100644
--- a/compiler/rustc_parse/src/lexer/mod.rs
+++ b/compiler/rustc_parse/src/lexer/mod.rs
@@ -1,21 +1,19 @@
 use rustc_ast::ast::AttrStyle;
 use rustc_ast::token::{self, CommentKind, Token, TokenKind};
-use rustc_data_structures::sync::Lrc;
-use rustc_errors::{error_code, Applicability, DiagnosticBuilder, FatalError};
-use rustc_lexer::Base;
-use rustc_lexer::{unescape, RawStrError};
+use rustc_ast::tokenstream::{Spacing, TokenStream};
+use rustc_errors::{error_code, Applicability, DiagnosticBuilder, FatalError, PResult};
+use rustc_lexer::unescape::{self, Mode};
+use rustc_lexer::{Base, DocStyle, RawStrError};
 use rustc_session::parse::ParseSess;
 use rustc_span::symbol::{sym, Symbol};
 use rustc_span::{BytePos, Pos, Span};
 
-use std::char;
 use tracing::debug;
 
 mod tokentrees;
 mod unescape_error_reporting;
 mod unicode_chars;
 
-use rustc_lexer::{unescape::Mode, DocStyle};
 use unescape_error_reporting::{emit_unescape_error, push_escaped_char};
 
 #[derive(Clone, Debug)]
@@ -27,7 +25,17 @@ pub struct UnmatchedBrace {
     pub candidate_span: Option<Span>,
 }
 
-pub struct StringReader<'a> {
+crate fn parse_token_trees<'a>(
+    sess: &'a ParseSess,
+    src: &'a str,
+    start_pos: BytePos,
+    override_span: Option<Span>,
+) -> (PResult<'a, TokenStream>, Vec<UnmatchedBrace>) {
+    StringReader { sess, start_pos, pos: start_pos, end_src_index: src.len(), src, override_span }
+        .into_token_trees()
+}
+
+struct StringReader<'a> {
     sess: &'a ParseSess,
     /// Initial position, read-only.
     start_pos: BytePos,
@@ -36,71 +44,55 @@ pub struct StringReader<'a> {
     /// Stop reading src at this index.
     end_src_index: usize,
     /// Source text to tokenize.
-    src: Lrc<String>,
+    src: &'a str,
     override_span: Option<Span>,
 }
 
 impl<'a> StringReader<'a> {
-    pub fn new(
-        sess: &'a ParseSess,
-        source_file: Lrc<rustc_span::SourceFile>,
-        override_span: Option<Span>,
-    ) -> Self {
-        let src = source_file.src.clone().unwrap_or_else(|| {
-            sess.span_diagnostic
-                .bug(&format!("cannot lex `source_file` without source: {}", source_file.name));
-        });
-
-        StringReader {
-            sess,
-            start_pos: source_file.start_pos,
-            pos: source_file.start_pos,
-            end_src_index: src.len(),
-            src,
-            override_span,
-        }
-    }
-
     fn mk_sp(&self, lo: BytePos, hi: BytePos) -> Span {
         self.override_span.unwrap_or_else(|| Span::with_root_ctxt(lo, hi))
     }
 
-    /// Returns the next token, including trivia like whitespace or comments.
-    pub fn next_token(&mut self) -> Token {
+    /// Returns the next token, and info about preceding whitespace, if any.
+    fn next_token(&mut self) -> (Spacing, Token) {
+        let mut spacing = Spacing::Joint;
+
+        // Skip `#!` at the start of the file
         let start_src_index = self.src_index(self.pos);
         let text: &str = &self.src[start_src_index..self.end_src_index];
-
-        if text.is_empty() {
-            let span = self.mk_sp(self.pos, self.pos);
-            return Token::new(token::Eof, span);
+        let is_beginning_of_file = self.pos == self.start_pos;
+        if is_beginning_of_file {
+            if let Some(shebang_len) = rustc_lexer::strip_shebang(text) {
+                self.pos = self.pos + BytePos::from_usize(shebang_len);
+                spacing = Spacing::Alone;
+            }
         }
 
-        {
-            let is_beginning_of_file = self.pos == self.start_pos;
-            if is_beginning_of_file {
-                if let Some(shebang_len) = rustc_lexer::strip_shebang(text) {
-                    let start = self.pos;
-                    self.pos = self.pos + BytePos::from_usize(shebang_len);
-
-                    let sym = self.symbol_from(start + BytePos::from_usize("#!".len()));
-                    let kind = token::Shebang(sym);
+        // Skip trivial (whitespace & comments) tokens
+        loop {
+            let start_src_index = self.src_index(self.pos);
+            let text: &str = &self.src[start_src_index..self.end_src_index];
 
-                    let span = self.mk_sp(start, self.pos);
-                    return Token::new(kind, span);
-                }
+            if text.is_empty() {
+                let span = self.mk_sp(self.pos, self.pos);
+                return (spacing, Token::new(token::Eof, span));
             }
-        }
 
-        let token = rustc_lexer::first_token(text);
+            let token = rustc_lexer::first_token(text);
 
-        let start = self.pos;
-        self.pos = self.pos + BytePos::from_usize(token.len);
+            let start = self.pos;
+            self.pos = self.pos + BytePos::from_usize(token.len);
 
-        debug!("try_next_token: {:?}({:?})", token.kind, self.str_from(start));
+            debug!("next_token: {:?}({:?})", token.kind, self.str_from(start));
 
-        let kind = self.cook_lexer_token(token.kind, start);
-        let span = self.mk_sp(start, self.pos);
-        Token::new(kind, span)
+            match self.cook_lexer_token(token.kind, start) {
+                Some(kind) => {
+                    let span = self.mk_sp(start, self.pos);
+                    return (spacing, Token::new(kind, span));
+                }
+                None => spacing = Spacing::Alone,
+            }
+        }
     }
 
     /// Report a fatal lexical error with a given span.
@@ -140,19 +132,16 @@ impl<'a> StringReader<'a> {
     /// Turns simple `rustc_lexer::TokenKind` enum into a rich
     /// `librustc_ast::TokenKind`. This turns strings into interned
     /// symbols and runs additional validation.
-    fn cook_lexer_token(&self, token: rustc_lexer::TokenKind, start: BytePos) -> TokenKind {
-        match token {
+    fn cook_lexer_token(&self, token: rustc_lexer::TokenKind, start: BytePos) -> Option<TokenKind> {
+        Some(match token {
             rustc_lexer::TokenKind::LineComment { doc_style } => {
-                match doc_style {
-                    Some(doc_style) => {
-                        // Opening delimiter of the length 3 is not included into the symbol.
-                        let content_start = start + BytePos(3);
-                        let content = self.str_from(content_start);
+                // Skip non-doc comments
+                let doc_style = doc_style?;
 
-                        self.cook_doc_comment(content_start, content, CommentKind::Line, doc_style)
-                    }
-                    None => token::Comment,
-                }
+                // Opening delimiter of the length 3 is not included into the symbol.
+                let content_start = start + BytePos(3);
+                let content = self.str_from(content_start);
+                self.cook_doc_comment(content_start, content, CommentKind::Line, doc_style)
             }
             rustc_lexer::TokenKind::BlockComment { doc_style, terminated } => {
                 if !terminated {
@@ -171,20 +160,18 @@ impl<'a> StringReader<'a> {
                         .emit();
                     FatalError.raise();
                 }
-                match doc_style {
-                    Some(doc_style) => {
-                        // Opening delimiter of the length 3 and closing delimiter of the length 2
-                        // are not included into the symbol.
-                        let content_start = start + BytePos(3);
-                        let content_end = self.pos - BytePos(if terminated { 2 } else { 0 });
-                        let content = self.str_from_to(content_start, content_end);
-
-                        self.cook_doc_comment(content_start, content, CommentKind::Block, doc_style)
-                    }
-                    None => token::Comment,
-                }
+
+                // Skip non-doc comments
+                let doc_style = doc_style?;
+
+                // Opening delimiter of the length 3 and closing delimiter of the length 2
+                // are not included into the symbol.
+                let content_start = start + BytePos(3);
+                let content_end = self.pos - BytePos(if terminated { 2 } else { 0 });
+                let content = self.str_from_to(content_start, content_end);
+                self.cook_doc_comment(content_start, content, CommentKind::Block, doc_style)
             }
-            rustc_lexer::TokenKind::Whitespace => token::Whitespace,
+            rustc_lexer::TokenKind::Whitespace => return None,
             rustc_lexer::TokenKind::Ident | rustc_lexer::TokenKind::RawIdent => {
                 let is_raw_ident = token == rustc_lexer::TokenKind::RawIdent;
                 let mut ident_start = start;
@@ -282,12 +269,11 @@ impl<'a> StringReader<'a> {
                 // this should be inside `rustc_lexer`. However, we should first remove compound
                 // tokens like `<<` from `rustc_lexer`, and then add fancier error recovery to it,
                 // as there will be less overall work to do this way.
-                let token = unicode_chars::check_for_substitution(self, start, c, &mut err)
-                    .unwrap_or_else(|| token::Unknown(self.symbol_from(start)));
+                let token = unicode_chars::check_for_substitution(self, start, c, &mut err);
                 err.emit();
-                token
+                token?
             }
-        }
+        })
     }
 
     fn cook_doc_comment(
@@ -439,10 +425,6 @@ impl<'a> StringReader<'a> {
         (lit_kind, id)
     }
 
-    pub fn pos(&self) -> BytePos {
-        self.pos
-    }
-
     #[inline]
     fn src_index(&self, pos: BytePos) -> usize {
         (pos - self.start_pos).to_usize()
@@ -454,12 +436,6 @@ impl<'a> StringReader<'a> {
         self.str_from_to(start, self.pos)
     }
 
-    /// Creates a Symbol from a given offset to the current offset.
-    fn symbol_from(&self, start: BytePos) -> Symbol {
-        debug!("taking an ident from {:?} to {:?}", start, self.pos);
-        Symbol::intern(self.str_from(start))
-    }
-
     /// As symbol_from, with an explicit endpoint.
     fn symbol_from_to(&self, start: BytePos, end: BytePos) -> Symbol {
         debug!("taking an ident from {:?} to {:?}", start, end);
diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs
index c08659ec9f6..6233549dc85 100644
--- a/compiler/rustc_parse/src/lexer/tokentrees.rs
+++ b/compiler/rustc_parse/src/lexer/tokentrees.rs
@@ -3,8 +3,8 @@ use super::{StringReader, UnmatchedBrace};
 use rustc_ast::token::{self, DelimToken, Token};
 use rustc_ast::tokenstream::{
     DelimSpan,
-    IsJoint::{self, *},
-    TokenStream, TokenTree, TreeAndJoint,
+    Spacing::{self, *},
+    TokenStream, TokenTree, TreeAndSpacing,
 };
 use rustc_ast_pretty::pprust::token_to_string;
 use rustc_data_structures::fx::FxHashMap;
@@ -12,11 +12,10 @@ use rustc_errors::PResult;
 use rustc_span::Span;
 
 impl<'a> StringReader<'a> {
-    crate fn into_token_trees(self) -> (PResult<'a, TokenStream>, Vec<UnmatchedBrace>) {
+    pub(super) fn into_token_trees(self) -> (PResult<'a, TokenStream>, Vec<UnmatchedBrace>) {
         let mut tt_reader = TokenTreesReader {
             string_reader: self,
             token: Token::dummy(),
-            joint_to_prev: Joint,
             open_braces: Vec::new(),
             unmatched_braces: Vec::new(),
             matching_delim_spans: Vec::new(),
@@ -32,7 +31,6 @@ impl<'a> StringReader<'a> {
 struct TokenTreesReader<'a> {
     string_reader: StringReader<'a>,
     token: Token,
-    joint_to_prev: IsJoint,
     /// Stack of open delimiters and their spans. Used for error message.
     open_braces: Vec<(token::DelimToken, Span)>,
     unmatched_braces: Vec<UnmatchedBrace>,
@@ -53,7 +51,7 @@ impl<'a> TokenTreesReader<'a> {
     fn parse_all_token_trees(&mut self) -> PResult<'a, TokenStream> {
         let mut buf = TokenStreamBuilder::default();
 
-        self.real_token();
+        self.bump();
         while self.token != token::Eof {
             buf.push(self.parse_token_tree()?);
         }
@@ -79,7 +77,7 @@ impl<'a> TokenTreesReader<'a> {
         }
     }
 
-    fn parse_token_tree(&mut self) -> PResult<'a, TreeAndJoint> {
+    fn parse_token_tree(&mut self) -> PResult<'a, TreeAndSpacing> {
         let sm = self.string_reader.sess.source_map();
 
         match self.token.kind {
@@ -126,7 +124,7 @@ impl<'a> TokenTreesReader<'a> {
 
                 // Parse the open delimiter.
                 self.open_braces.push((delim, self.token.span));
-                self.real_token();
+                self.bump();
 
                 // Parse the token trees within the delimiters.
                 // We stop at any delimiter so we can try to recover if the user
@@ -151,12 +149,9 @@ impl<'a> TokenTreesReader<'a> {
                             }
                         }
 
-                        match (open_brace, delim) {
-                            //only add braces
-                            (DelimToken::Brace, DelimToken::Brace) => {
-                                self.matching_block_spans.push((open_brace_span, close_brace_span));
-                            }
-                            _ => {}
+                        //only add braces
+                        if let (DelimToken::Brace, DelimToken::Brace) = (open_brace, delim) {
+                            self.matching_block_spans.push((open_brace_span, close_brace_span));
                         }
 
                         if self.open_braces.is_empty() {
@@ -171,7 +166,7 @@ impl<'a> TokenTreesReader<'a> {
                             ));
                         }
                         // Parse the closing delimiter.
-                        self.real_token();
+                        self.bump();
                     }
                     // Incorrect delimiter.
                     token::CloseDelim(other) => {
@@ -217,7 +212,7 @@ impl<'a> TokenTreesReader<'a> {
                         //     bar(baz(
                         // }  // Incorrect delimiter but matches the earlier `{`
                         if !self.open_braces.iter().any(|&(b, _)| b == other) {
-                            self.real_token();
+                            self.bump();
                         }
                     }
                     token::Eof => {
@@ -264,37 +259,29 @@ impl<'a> TokenTreesReader<'a> {
             }
             _ => {
                 let tt = TokenTree::Token(self.token.take());
-                self.real_token();
-                let is_joint = self.joint_to_prev == Joint && self.token.is_op();
-                Ok((tt, if is_joint { Joint } else { NonJoint }))
+                let mut spacing = self.bump();
+                if !self.token.is_op() {
+                    spacing = Alone;
+                }
+                Ok((tt, spacing))
             }
         }
     }
 
-    fn real_token(&mut self) {
-        self.joint_to_prev = Joint;
-        loop {
-            let token = self.string_reader.next_token();
-            match token.kind {
-                token::Whitespace | token::Comment | token::Shebang(_) | token::Unknown(_) => {
-                    self.joint_to_prev = NonJoint;
-                }
-                _ => {
-                    self.token = token;
-                    return;
-                }
-            }
-        }
+    fn bump(&mut self) -> Spacing {
+        let (spacing, token) = self.string_reader.next_token();
+        self.token = token;
+        spacing
     }
 }
 
 #[derive(Default)]
 struct TokenStreamBuilder {
-    buf: Vec<TreeAndJoint>,
+    buf: Vec<TreeAndSpacing>,
 }
 
 impl TokenStreamBuilder {
-    fn push(&mut self, (tree, joint): TreeAndJoint) {
+    fn push(&mut self, (tree, joint): TreeAndSpacing) {
         if let Some((TokenTree::Token(prev_token), Joint)) = self.buf.last() {
             if let TokenTree::Token(token) = &tree {
                 if let Some(glued) = prev_token.glue(token) {
diff --git a/compiler/rustc_parse/src/lexer/unicode_chars.rs b/compiler/rustc_parse/src/lexer/unicode_chars.rs
index ac395f6cbc2..40e2e34aa05 100644
--- a/compiler/rustc_parse/src/lexer/unicode_chars.rs
+++ b/compiler/rustc_parse/src/lexer/unicode_chars.rs
@@ -303,7 +303,7 @@ const UNICODE_ARRAY: &[(char, &str, char)] = &[
 // However, we should first remove compound tokens like `<<` from `rustc_lexer`, and then add
 // fancier error recovery to it, as there will be less overall work to do this way.
 const ASCII_ARRAY: &[(char, &str, Option<token::TokenKind>)] = &[
-    (' ', "Space", Some(token::Whitespace)),
+    (' ', "Space", None),
     ('_', "Underscore", Some(token::Ident(kw::Underscore, false))),
     ('-', "Minus/Hyphen", Some(token::BinOp(token::Minus))),
     (',', "Comma", Some(token::Comma)),
@@ -332,7 +332,7 @@ const ASCII_ARRAY: &[(char, &str, Option<token::TokenKind>)] = &[
     ('"', "Quotation Mark", None),
 ];
 
-crate fn check_for_substitution<'a>(
+pub(super) fn check_for_substitution<'a>(
     reader: &StringReader<'a>,
     pos: BytePos,
     ch: char,
diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs
index bc857c97742..21bbdc9ba8d 100644
--- a/compiler/rustc_parse/src/lib.rs
+++ b/compiler/rustc_parse/src/lib.rs
@@ -3,12 +3,12 @@
 #![feature(bool_to_option)]
 #![feature(crate_visibility_modifier)]
 #![feature(bindings_after_at)]
-#![feature(try_blocks)]
+#![feature(iter_order_by)]
 #![feature(or_patterns)]
 
 use rustc_ast as ast;
 use rustc_ast::token::{self, DelimToken, Nonterminal, Token, TokenKind};
-use rustc_ast::tokenstream::{self, IsJoint, TokenStream, TokenTree};
+use rustc_ast::tokenstream::{self, Spacing, TokenStream, TokenTree};
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::{Diagnostic, FatalError, Level, PResult};
@@ -109,7 +109,7 @@ pub fn maybe_new_parser_from_source_str(
 }
 
 /// Creates a new parser, handling errors as appropriate if the file doesn't exist.
-/// If a span is given, that is used on an error as the as the source of the problem.
+/// If a span is given, that is used on an error as the source of the problem.
 pub fn new_parser_from_file<'a>(sess: &'a ParseSess, path: &Path, sp: Option<Span>) -> Parser<'a> {
     source_file_to_parser(sess, file_to_source_file(sess, path, sp))
 }
@@ -200,8 +200,13 @@ pub fn maybe_file_to_stream(
     source_file: Lrc<SourceFile>,
     override_span: Option<Span>,
 ) -> Result<(TokenStream, Vec<lexer::UnmatchedBrace>), Vec<Diagnostic>> {
-    let srdr = lexer::StringReader::new(sess, source_file, override_span);
-    let (token_trees, unmatched_braces) = srdr.into_token_trees();
+    let src = source_file.src.as_ref().unwrap_or_else(|| {
+        sess.span_diagnostic
+            .bug(&format!("cannot lex `source_file` without source: {}", source_file.name));
+    });
+
+    let (token_trees, unmatched_braces) =
+        lexer::parse_token_trees(sess, src.as_str(), source_file.start_pos, override_span);
 
     match token_trees {
         Ok(stream) => Ok((stream, unmatched_braces)),
@@ -263,21 +268,32 @@ pub fn nt_to_tokenstream(nt: &Nonterminal, sess: &ParseSess, span: Span) -> Toke
         Nonterminal::NtItem(ref item) => {
             prepend_attrs(sess, &item.attrs, item.tokens.as_ref(), span)
         }
+        Nonterminal::NtBlock(ref block) => block.tokens.clone(),
+        Nonterminal::NtStmt(ref stmt) => {
+            // FIXME: We currently only collect tokens for `:stmt`
+            // matchers in `macro_rules!` macros. When we start collecting
+            // tokens for attributes on statements, we will need to prepend
+            // attributes here
+            stmt.tokens.clone()
+        }
         Nonterminal::NtPat(ref pat) => pat.tokens.clone(),
+        Nonterminal::NtTy(ref ty) => ty.tokens.clone(),
         Nonterminal::NtIdent(ident, is_raw) => {
             Some(tokenstream::TokenTree::token(token::Ident(ident.name, is_raw), ident.span).into())
         }
         Nonterminal::NtLifetime(ident) => {
             Some(tokenstream::TokenTree::token(token::Lifetime(ident.name), ident.span).into())
         }
+        Nonterminal::NtMeta(ref attr) => attr.tokens.clone(),
+        Nonterminal::NtPath(ref path) => path.tokens.clone(),
+        Nonterminal::NtVis(ref vis) => vis.tokens.clone(),
         Nonterminal::NtTT(ref tt) => Some(tt.clone().into()),
-        Nonterminal::NtExpr(ref expr) => {
+        Nonterminal::NtExpr(ref expr) | Nonterminal::NtLiteral(ref expr) => {
             if expr.tokens.is_none() {
                 debug!("missing tokens for expr {:?}", expr);
             }
             prepend_attrs(sess, &expr.attrs, expr.tokens.as_ref(), span)
         }
-        _ => None,
     };
 
     // FIXME(#43081): Avoid this pretty-print + reparse hack
@@ -348,9 +364,12 @@ pub fn tokenstream_probably_equal_for_proc_macro(
                 | token::CloseDelim(DelimToken::NoDelim)
                 // The pretty printer collapses many semicolons into one.
                 | token::Semi
-                // The pretty printer collapses whitespace arbitrarily and can
-                // introduce whitespace from `NoDelim`.
-                | token::Whitespace
+                // We don't preserve leading `|` tokens in patterns, so
+                // we ignore them entirely
+                | token::BinOp(token::BinOpToken::Or)
+                // We don't preserve trailing '+' tokens in trait bounds,
+                // so we ignore them entirely
+                | token::BinOp(token::BinOpToken::Plus)
                 // The pretty printer can turn `$crate` into `::crate_name`
                 | token::ModSep = token.kind {
                 return false;
@@ -435,20 +454,16 @@ pub fn tokenstream_probably_equal_for_proc_macro(
             // issue #75734 tracks resolving this.
             nt_to_tokenstream(nt, sess, *span).into_trees()
         } else {
-            TokenStream::new(vec![(tree, IsJoint::NonJoint)]).into_trees()
+            TokenStream::new(vec![(tree, Spacing::Alone)]).into_trees()
         }
     };
 
     // Break tokens after we expand any nonterminals, so that we break tokens
     // that are produced as a result of nonterminal expansion.
-    let mut t1 = first.trees().filter(semantic_tree).flat_map(expand_nt).flat_map(break_tokens);
-    let mut t2 = other.trees().filter(semantic_tree).flat_map(expand_nt).flat_map(break_tokens);
-    for (t1, t2) in t1.by_ref().zip(t2.by_ref()) {
-        if !tokentree_probably_equal_for_proc_macro(&t1, &t2, sess) {
-            return false;
-        }
-    }
-    t1.next().is_none() && t2.next().is_none()
+    let t1 = first.trees().filter(semantic_tree).flat_map(expand_nt).flat_map(break_tokens);
+    let t2 = other.trees().filter(semantic_tree).flat_map(expand_nt).flat_map(break_tokens);
+
+    t1.eq_by(t2, |t1, t2| tokentree_probably_equal_for_proc_macro(&t1, &t2, sess))
 }
 
 // See comments in `Nonterminal::to_tokenstream` for why we care about
@@ -506,8 +521,6 @@ fn token_probably_equal_for_proc_macro(first: &Token, other: &Token) -> bool {
         | (&Pound, &Pound)
         | (&Dollar, &Dollar)
         | (&Question, &Question)
-        | (&Whitespace, &Whitespace)
-        | (&Comment, &Comment)
         | (&Eof, &Eof) => true,
 
         (&BinOp(a), &BinOp(b)) | (&BinOpEq(a), &BinOpEq(b)) => a == b,
@@ -516,8 +529,6 @@ fn token_probably_equal_for_proc_macro(first: &Token, other: &Token) -> bool {
 
         (&DocComment(a1, a2, a3), &DocComment(b1, b2, b3)) => a1 == b1 && a2 == b2 && a3 == b3,
 
-        (&Shebang(a), &Shebang(b)) => a == b,
-
         (&Literal(a), &Literal(b)) => a == b,
 
         (&Lifetime(a), &Lifetime(b)) => a == b,
diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs
index 4e4429e461f..98f94098bfc 100644
--- a/compiler/rustc_parse/src/parser/attr.rs
+++ b/compiler/rustc_parse/src/parser/attr.rs
@@ -162,7 +162,7 @@ impl<'a> Parser<'a> {
         } else {
             let path = self.parse_path(PathStyle::Mod)?;
             let args = self.parse_attr_args()?;
-            ast::AttrItem { path, args }
+            ast::AttrItem { path, args, tokens: None }
         })
     }
 
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index 12efe391fb9..9ab13db4b5f 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -28,7 +28,7 @@ pub(super) fn dummy_arg(ident: Ident) -> Param {
         span: ident.span,
         tokens: None,
     });
-    let ty = Ty { kind: TyKind::Err, span: ident.span, id: ast::DUMMY_NODE_ID };
+    let ty = Ty { kind: TyKind::Err, span: ident.span, id: ast::DUMMY_NODE_ID, tokens: None };
     Param {
         attrs: AttrVec::default(),
         id: ast::DUMMY_NODE_ID,
@@ -75,7 +75,12 @@ impl RecoverQPath for Ty {
         Some(P(self.clone()))
     }
     fn recovered(qself: Option<QSelf>, path: ast::Path) -> Self {
-        Self { span: path.span, kind: TyKind::Path(qself, path), id: ast::DUMMY_NODE_ID }
+        Self {
+            span: path.span,
+            kind: TyKind::Path(qself, path),
+            id: ast::DUMMY_NODE_ID,
+            tokens: None,
+        }
     }
 }
 
@@ -548,6 +553,52 @@ impl<'a> Parser<'a> {
         }
     }
 
+    /// When writing a turbofish with multiple type parameters missing the leading `::`, we will
+    /// encounter a parse error when encountering the first `,`.
+    pub(super) fn check_mistyped_turbofish_with_multiple_type_params(
+        &mut self,
+        mut e: DiagnosticBuilder<'a>,
+        expr: &mut P<Expr>,
+    ) -> PResult<'a, ()> {
+        if let ExprKind::Binary(binop, _, _) = &expr.kind {
+            if let ast::BinOpKind::Lt = binop.node {
+                if self.eat(&token::Comma) {
+                    let x = self.parse_seq_to_before_end(
+                        &token::Gt,
+                        SeqSep::trailing_allowed(token::Comma),
+                        |p| p.parse_ty(),
+                    );
+                    match x {
+                        Ok((_, _, false)) => {
+                            self.bump(); // `>`
+                            match self.parse_expr() {
+                                Ok(_) => {
+                                    e.span_suggestion_verbose(
+                                        binop.span.shrink_to_lo(),
+                                        "use `::<...>` instead of `<...>` to specify type arguments",
+                                        "::".to_string(),
+                                        Applicability::MaybeIncorrect,
+                                    );
+                                    e.emit();
+                                    *expr = self.mk_expr_err(expr.span.to(self.prev_token.span));
+                                    return Ok(());
+                                }
+                                Err(mut err) => {
+                                    err.cancel();
+                                }
+                            }
+                        }
+                        Err(mut err) => {
+                            err.cancel();
+                        }
+                        _ => {}
+                    }
+                }
+            }
+        }
+        Err(e)
+    }
+
     /// Check to see if a pair of chained operators looks like an attempt at chained comparison,
     /// e.g. `1 < x <= 3`. If so, suggest either splitting the comparison into two, or
     /// parenthesising the leftmost comparison.
@@ -896,7 +947,7 @@ impl<'a> Parser<'a> {
     ) -> PResult<'a, P<T>> {
         self.expect(&token::ModSep)?;
 
-        let mut path = ast::Path { segments: Vec::new(), span: DUMMY_SP };
+        let mut path = ast::Path { segments: Vec::new(), span: DUMMY_SP, tokens: None };
         self.parse_path_segments(&mut path.segments, T::PATH_STYLE)?;
         path.span = ty_span.to(self.prev_token.span);
 
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index f022c628fe2..69d13b5cf53 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -1480,7 +1480,7 @@ impl<'a> Parser<'a> {
 
     /// Matches `'-' lit | lit` (cf. `ast_validation::AstValidator::check_expr_within_pat`).
     /// Keep this in sync with `Token::can_begin_literal_maybe_minus`.
-    pub(super) fn parse_literal_maybe_minus(&mut self) -> PResult<'a, P<Expr>> {
+    pub fn parse_literal_maybe_minus(&mut self) -> PResult<'a, P<Expr>> {
         maybe_whole_expr!(self);
 
         let lo = self.token.span;
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 9143af651df..26ca9980127 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -28,7 +28,7 @@ impl<'a> Parser<'a> {
     /// Parses a source module as a crate. This is the main entry point for the parser.
     pub fn parse_crate_mod(&mut self) -> PResult<'a, ast::Crate> {
         let lo = self.token.span;
-        let (module, attrs) = self.parse_mod(&token::Eof)?;
+        let (module, attrs) = self.parse_mod(&token::Eof, Unsafe::No)?;
         let span = lo.to(self.token.span);
         let proc_macros = Vec::new(); // Filled in by `proc_macro_harness::inject()`.
         Ok(ast::Crate { attrs, module, span, proc_macros })
@@ -36,27 +36,38 @@ impl<'a> Parser<'a> {
 
     /// Parses a `mod <foo> { ... }` or `mod <foo>;` item.
     fn parse_item_mod(&mut self, attrs: &mut Vec<Attribute>) -> PResult<'a, ItemInfo> {
+        let unsafety = self.parse_unsafety();
+        self.expect_keyword(kw::Mod)?;
         let id = self.parse_ident()?;
         let (module, mut inner_attrs) = if self.eat(&token::Semi) {
-            Default::default()
+            (Mod { inner: Span::default(), unsafety, items: Vec::new(), inline: false }, Vec::new())
         } else {
             self.expect(&token::OpenDelim(token::Brace))?;
-            self.parse_mod(&token::CloseDelim(token::Brace))?
+            self.parse_mod(&token::CloseDelim(token::Brace), unsafety)?
         };
         attrs.append(&mut inner_attrs);
         Ok((id, ItemKind::Mod(module)))
     }
 
     /// Parses the contents of a module (inner attributes followed by module items).
-    pub fn parse_mod(&mut self, term: &TokenKind) -> PResult<'a, (Mod, Vec<Attribute>)> {
+    pub fn parse_mod(
+        &mut self,
+        term: &TokenKind,
+        unsafety: Unsafe,
+    ) -> PResult<'a, (Mod, Vec<Attribute>)> {
         let lo = self.token.span;
         let attrs = self.parse_inner_attributes()?;
-        let module = self.parse_mod_items(term, lo)?;
+        let module = self.parse_mod_items(term, lo, unsafety)?;
         Ok((module, attrs))
     }
 
     /// Given a termination token, parses all of the items in a module.
-    fn parse_mod_items(&mut self, term: &TokenKind, inner_lo: Span) -> PResult<'a, Mod> {
+    fn parse_mod_items(
+        &mut self,
+        term: &TokenKind,
+        inner_lo: Span,
+        unsafety: Unsafe,
+    ) -> PResult<'a, Mod> {
         let mut items = vec![];
         while let Some(item) = self.parse_item()? {
             items.push(item);
@@ -75,7 +86,7 @@ impl<'a> Parser<'a> {
 
         let hi = if self.token.span.is_dummy() { inner_lo } else { self.prev_token.span };
 
-        Ok(Mod { inner: inner_lo.to(hi), items, inline: true })
+        Ok(Mod { inner: inner_lo.to(hi), unsafety, items, inline: true })
     }
 }
 
@@ -176,7 +187,7 @@ impl<'a> Parser<'a> {
 
     /// Error in-case a non-inherited visibility was parsed but no item followed.
     fn error_on_unmatched_vis(&self, vis: &Visibility) {
-        if let VisibilityKind::Inherited = vis.node {
+        if let VisibilityKind::Inherited = vis.kind {
             return;
         }
         let vs = pprust::vis_to_string(&vis);
@@ -235,8 +246,13 @@ impl<'a> Parser<'a> {
                 self.parse_item_extern_crate()?
             } else {
                 // EXTERN BLOCK
-                self.parse_item_foreign_mod(attrs)?
+                self.parse_item_foreign_mod(attrs, Unsafe::No)?
             }
+        } else if self.is_unsafe_foreign_mod() {
+            // EXTERN BLOCK
+            let unsafety = self.parse_unsafety();
+            self.expect_keyword(kw::Extern)?;
+            self.parse_item_foreign_mod(attrs, unsafety)?
         } else if self.is_static_global() {
             // STATIC ITEM
             self.bump(); // `static`
@@ -256,7 +272,9 @@ impl<'a> Parser<'a> {
         {
             // IMPL ITEM
             self.parse_item_impl(attrs, def())?
-        } else if self.eat_keyword(kw::Mod) {
+        } else if self.check_keyword(kw::Mod)
+            || self.check_keyword(kw::Unsafe) && self.is_keyword_ahead(1, &[kw::Mod])
+        {
             // MODULE ITEM
             self.parse_item_mod(attrs)?
         } else if self.eat_keyword(kw::Type) {
@@ -278,7 +296,7 @@ impl<'a> Parser<'a> {
         } else if self.is_macro_rules_item() {
             // MACRO_RULES ITEM
             self.parse_item_macro_rules(vis)?
-        } else if vis.node.is_pub() && self.isnt_macro_invocation() {
+        } else if vis.kind.is_pub() && self.isnt_macro_invocation() {
             self.recover_missing_kw_before_item()?;
             return Ok(None);
         } else if macros_allowed && self.check_path() {
@@ -492,7 +510,12 @@ impl<'a> Parser<'a> {
         {
             let span = self.prev_token.span.between(self.token.span);
             self.struct_span_err(span, "missing trait in a trait impl").emit();
-            P(Ty { kind: TyKind::Path(None, err_path(span)), span, id: DUMMY_NODE_ID })
+            P(Ty {
+                kind: TyKind::Path(None, err_path(span)),
+                span,
+                id: DUMMY_NODE_ID,
+                tokens: None,
+            })
         } else {
             self.parse_ty()?
         };
@@ -764,7 +787,7 @@ impl<'a> Parser<'a> {
     fn parse_use_tree(&mut self) -> PResult<'a, UseTree> {
         let lo = self.token.span;
 
-        let mut prefix = ast::Path { segments: Vec::new(), span: lo.shrink_to_lo() };
+        let mut prefix = ast::Path { segments: Vec::new(), span: lo.shrink_to_lo(), tokens: None };
         let kind = if self.check(&token::OpenDelim(token::Brace))
             || self.check(&token::BinOp(token::Star))
             || self.is_import_coupler()
@@ -893,10 +916,14 @@ impl<'a> Parser<'a> {
     /// extern "C" {}
     /// extern {}
     /// ```
-    fn parse_item_foreign_mod(&mut self, attrs: &mut Vec<Attribute>) -> PResult<'a, ItemInfo> {
+    fn parse_item_foreign_mod(
+        &mut self,
+        attrs: &mut Vec<Attribute>,
+        unsafety: Unsafe,
+    ) -> PResult<'a, ItemInfo> {
         let abi = self.parse_abi(); // ABI?
         let items = self.parse_item_list(attrs, |p| p.parse_foreign_item())?;
-        let module = ast::ForeignMod { abi, items };
+        let module = ast::ForeignMod { unsafety, abi, items };
         Ok((Ident::invalid(), ItemKind::ForeignMod(module)))
     }
 
@@ -938,6 +965,15 @@ impl<'a> Parser<'a> {
             .emit();
     }
 
+    fn is_unsafe_foreign_mod(&self) -> bool {
+        self.token.is_keyword(kw::Unsafe)
+            && self.is_keyword_ahead(1, &[kw::Extern])
+            && self.look_ahead(
+                2 + self.look_ahead(2, |t| t.can_begin_literal_maybe_minus() as usize),
+                |t| t.kind == token::OpenDelim(token::Brace),
+            )
+    }
+
     fn is_static_global(&mut self) -> bool {
         if self.check_keyword(kw::Static) {
             // Check if this could be a closure.
@@ -1015,7 +1051,7 @@ impl<'a> Parser<'a> {
 
         // The user intended that the type be inferred,
         // so treat this as if the user wrote e.g. `const A: _ = expr;`.
-        P(Ty { kind: TyKind::Infer, span: id.span, id: ast::DUMMY_NODE_ID })
+        P(Ty { kind: TyKind::Infer, span: id.span, id: ast::DUMMY_NODE_ID, tokens: None })
     }
 
     /// Parses an enum declaration.
@@ -1382,7 +1418,7 @@ impl<'a> Parser<'a> {
     /// Item macro invocations or `macro_rules!` definitions need inherited visibility.
     /// If that's not the case, emit an error.
     fn complain_if_pub_macro(&self, vis: &Visibility, macro_rules: bool) {
-        if let VisibilityKind::Inherited = vis.node {
+        if let VisibilityKind::Inherited = vis.kind {
             return;
         }
 
@@ -1552,10 +1588,14 @@ impl<'a> Parser<'a> {
             // `$qual fn` or `$qual $qual`:
             || QUALS.iter().any(|&kw| self.check_keyword(kw))
                 && self.look_ahead(1, |t| {
-                    // ...qualified and then `fn`, e.g. `const fn`.
+                    // `$qual fn`, e.g. `const fn` or `async fn`.
                     t.is_keyword(kw::Fn)
-                    // Two qualifiers. This is enough. Due `async` we need to check that it's reserved.
-                    || t.is_non_raw_ident_where(|i| QUALS.contains(&i.name) && i.is_reserved())
+                    // Two qualifiers `$qual $qual` is enough, e.g. `async unsafe`.
+                    || t.is_non_raw_ident_where(|i| QUALS.contains(&i.name)
+                        // Rule out 2015 `const async: T = val`.
+                        && i.is_reserved()
+                        // Rule out unsafe extern block.
+                        && !self.is_unsafe_foreign_mod())
                 })
             // `extern ABI fn`
             || self.check_keyword(kw::Extern)
@@ -1567,9 +1607,9 @@ impl<'a> Parser<'a> {
     /// up to and including the `fn` keyword. The formal grammar is:
     ///
     /// ```
-    /// Extern = "extern" StringLit ;
+    /// Extern = "extern" StringLit? ;
     /// FnQual = "const"? "async"? "unsafe"? Extern? ;
-    /// FnFrontMatter = FnQual? "fn" ;
+    /// FnFrontMatter = FnQual "fn" ;
     /// ```
     pub(super) fn parse_fn_front_matter(&mut self) -> PResult<'a, FnHeader> {
         let constness = self.parse_constness();
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index d67ed74bc99..7340c574480 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -15,14 +15,14 @@ pub use path::PathStyle;
 
 use rustc_ast::ptr::P;
 use rustc_ast::token::{self, DelimToken, Token, TokenKind};
-use rustc_ast::tokenstream::{self, DelimSpan, TokenStream, TokenTree, TreeAndJoint};
+use rustc_ast::tokenstream::{self, DelimSpan, TokenStream, TokenTree, TreeAndSpacing};
 use rustc_ast::DUMMY_NODE_ID;
 use rustc_ast::{self as ast, AttrStyle, AttrVec, Const, CrateSugar, Extern, Unsafe};
 use rustc_ast::{Async, MacArgs, MacDelimiter, Mutability, StrLit, Visibility, VisibilityKind};
 use rustc_ast_pretty::pprust;
 use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, FatalError, PResult};
 use rustc_session::parse::ParseSess;
-use rustc_span::source_map::{respan, Span, DUMMY_SP};
+use rustc_span::source_map::{Span, DUMMY_SP};
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use tracing::debug;
 
@@ -118,7 +118,7 @@ impl<'a> Drop for Parser<'a> {
 struct TokenCursor {
     frame: TokenCursorFrame,
     stack: Vec<TokenCursorFrame>,
-    cur_token: Option<TreeAndJoint>,
+    cur_token: Option<TreeAndSpacing>,
     collecting: Option<Collecting>,
 }
 
@@ -136,7 +136,7 @@ struct TokenCursorFrame {
 struct Collecting {
     /// Holds the current tokens captured during the most
     /// recent call to `collect_tokens`
-    buf: Vec<TreeAndJoint>,
+    buf: Vec<TreeAndSpacing>,
     /// The depth of the `TokenCursor` stack at the time
     /// collection was started. When we encounter a `TokenTree::Delimited`,
     /// we want to record the `TokenTree::Delimited` itself,
@@ -167,7 +167,7 @@ impl TokenCursor {
             let tree = if !self.frame.open_delim {
                 self.frame.open_delim = true;
                 TokenTree::open_tt(self.frame.span, self.frame.delim).into()
-            } else if let Some(tree) = self.frame.tree_cursor.next_with_joint() {
+            } else if let Some(tree) = self.frame.tree_cursor.next_with_spacing() {
                 tree
             } else if !self.frame.close_delim {
                 self.frame.close_delim = true;
@@ -694,9 +694,13 @@ impl<'a> Parser<'a> {
                                 Ok(t) => {
                                     // Parsed successfully, therefore most probably the code only
                                     // misses a separator.
+                                    let mut exp_span = self.sess.source_map().next_point(sp);
+                                    if self.sess.source_map().is_multiline(exp_span) {
+                                        exp_span = sp;
+                                    }
                                     expect_err
                                         .span_suggestion_short(
-                                            self.sess.source_map().next_point(sp),
+                                            exp_span,
                                             &format!("missing `{}`", token_str),
                                             token_str,
                                             Applicability::MaybeIncorrect,
@@ -1014,21 +1018,30 @@ impl<'a> Parser<'a> {
     /// If the following element can't be a tuple (i.e., it's a function definition), then
     /// it's not a tuple struct field), and the contents within the parentheses isn't valid,
     /// so emit a proper diagnostic.
-    pub(crate) fn parse_visibility(&mut self, fbt: FollowedByType) -> PResult<'a, Visibility> {
+    // Public for rustfmt usage.
+    pub fn parse_visibility(&mut self, fbt: FollowedByType) -> PResult<'a, Visibility> {
         maybe_whole!(self, NtVis, |x| x);
 
         self.expected_tokens.push(TokenType::Keyword(kw::Crate));
         if self.is_crate_vis() {
             self.bump(); // `crate`
             self.sess.gated_spans.gate(sym::crate_visibility_modifier, self.prev_token.span);
-            return Ok(respan(self.prev_token.span, VisibilityKind::Crate(CrateSugar::JustCrate)));
+            return Ok(Visibility {
+                span: self.prev_token.span,
+                kind: VisibilityKind::Crate(CrateSugar::JustCrate),
+                tokens: None,
+            });
         }
 
         if !self.eat_keyword(kw::Pub) {
             // We need a span for our `Spanned<VisibilityKind>`, but there's inherently no
             // keyword to grab a span from for inherited visibility; an empty span at the
             // beginning of the current token would seem to be the "Schelling span".
-            return Ok(respan(self.token.span.shrink_to_lo(), VisibilityKind::Inherited));
+            return Ok(Visibility {
+                span: self.token.span.shrink_to_lo(),
+                kind: VisibilityKind::Inherited,
+                tokens: None,
+            });
         }
         let lo = self.prev_token.span;
 
@@ -1045,7 +1058,11 @@ impl<'a> Parser<'a> {
                 self.bump(); // `crate`
                 self.expect(&token::CloseDelim(token::Paren))?; // `)`
                 let vis = VisibilityKind::Crate(CrateSugar::PubCrate);
-                return Ok(respan(lo.to(self.prev_token.span), vis));
+                return Ok(Visibility {
+                    span: lo.to(self.prev_token.span),
+                    kind: vis,
+                    tokens: None,
+                });
             } else if self.is_keyword_ahead(1, &[kw::In]) {
                 // Parse `pub(in path)`.
                 self.bump(); // `(`
@@ -1053,7 +1070,11 @@ impl<'a> Parser<'a> {
                 let path = self.parse_path(PathStyle::Mod)?; // `path`
                 self.expect(&token::CloseDelim(token::Paren))?; // `)`
                 let vis = VisibilityKind::Restricted { path: P(path), id: ast::DUMMY_NODE_ID };
-                return Ok(respan(lo.to(self.prev_token.span), vis));
+                return Ok(Visibility {
+                    span: lo.to(self.prev_token.span),
+                    kind: vis,
+                    tokens: None,
+                });
             } else if self.look_ahead(2, |t| t == &token::CloseDelim(token::Paren))
                 && self.is_keyword_ahead(1, &[kw::Super, kw::SelfLower])
             {
@@ -1062,7 +1083,11 @@ impl<'a> Parser<'a> {
                 let path = self.parse_path(PathStyle::Mod)?; // `super`/`self`
                 self.expect(&token::CloseDelim(token::Paren))?; // `)`
                 let vis = VisibilityKind::Restricted { path: P(path), id: ast::DUMMY_NODE_ID };
-                return Ok(respan(lo.to(self.prev_token.span), vis));
+                return Ok(Visibility {
+                    span: lo.to(self.prev_token.span),
+                    kind: vis,
+                    tokens: None,
+                });
             } else if let FollowedByType::No = fbt {
                 // Provide this diagnostic if a type cannot follow;
                 // in particular, if this is not a tuple struct.
@@ -1071,7 +1096,7 @@ impl<'a> Parser<'a> {
             }
         }
 
-        Ok(respan(lo, VisibilityKind::Public))
+        Ok(Visibility { span: lo, kind: VisibilityKind::Public, tokens: None })
     }
 
     /// Recovery for e.g. `pub(something) fn ...` or `struct X { pub(something) y: Z }`
@@ -1153,7 +1178,7 @@ impl<'a> Parser<'a> {
         f: impl FnOnce(&mut Self) -> PResult<'a, R>,
     ) -> PResult<'a, (R, TokenStream)> {
         // Record all tokens we parse when parsing this item.
-        let tokens: Vec<TreeAndJoint> = self.token_cursor.cur_token.clone().into_iter().collect();
+        let tokens: Vec<TreeAndSpacing> = self.token_cursor.cur_token.clone().into_iter().collect();
         debug!("collect_tokens: starting with {:?}", tokens);
 
         // We need special handling for the case where `collect_tokens` is called
@@ -1233,6 +1258,10 @@ impl<'a> Parser<'a> {
                 *t == token::OpenDelim(token::Brace) || *t == token::BinOp(token::Star)
             })
     }
+
+    pub fn clear_expected_tokens(&mut self) {
+        self.expected_tokens.clear();
+    }
 }
 
 crate fn make_unclosed_delims_error(
diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs
index f40cd1131d2..15660fd574c 100644
--- a/compiler/rustc_parse/src/parser/nonterminal.rs
+++ b/compiler/rustc_parse/src/parser/nonterminal.rs
@@ -111,11 +111,28 @@ impl<'a> Parser<'a> {
                     return Err(self.struct_span_err(self.token.span, "expected an item keyword"));
                 }
             },
-            NonterminalKind::Block => token::NtBlock(self.parse_block()?),
-            NonterminalKind::Stmt => match self.parse_stmt()? {
-                Some(s) => token::NtStmt(s),
-                None => return Err(self.struct_span_err(self.token.span, "expected a statement")),
-            },
+            NonterminalKind::Block => {
+                let (mut block, tokens) = self.collect_tokens(|this| this.parse_block())?;
+                // We have have eaten an NtBlock, which could already have tokens
+                if block.tokens.is_none() {
+                    block.tokens = Some(tokens);
+                }
+                token::NtBlock(block)
+            }
+            NonterminalKind::Stmt => {
+                let (stmt, tokens) = self.collect_tokens(|this| this.parse_stmt())?;
+                match stmt {
+                    Some(mut s) => {
+                        if s.tokens.is_none() {
+                            s.tokens = Some(tokens);
+                        }
+                        token::NtStmt(s)
+                    }
+                    None => {
+                        return Err(self.struct_span_err(self.token.span, "expected a statement"));
+                    }
+                }
+            }
             NonterminalKind::Pat => {
                 let (mut pat, tokens) = self.collect_tokens(|this| this.parse_pat(None))?;
                 // We have have eaten an NtPat, which could already have tokens
@@ -133,8 +150,23 @@ impl<'a> Parser<'a> {
                 }
                 token::NtExpr(expr)
             }
-            NonterminalKind::Literal => token::NtLiteral(self.parse_literal_maybe_minus()?),
-            NonterminalKind::Ty => token::NtTy(self.parse_ty()?),
+            NonterminalKind::Literal => {
+                let (mut lit, tokens) =
+                    self.collect_tokens(|this| this.parse_literal_maybe_minus())?;
+                // We have have eaten a nonterminal, which  could already have tokens
+                if lit.tokens.is_none() {
+                    lit.tokens = Some(tokens);
+                }
+                token::NtLiteral(lit)
+            }
+            NonterminalKind::Ty => {
+                let (mut ty, tokens) = self.collect_tokens(|this| this.parse_ty())?;
+                // We have an eaten an NtTy, which could already have tokens
+                if ty.tokens.is_none() {
+                    ty.tokens = Some(tokens);
+                }
+                token::NtTy(ty)
+            }
             // this could be handled like a token, since it is one
             NonterminalKind::Ident => {
                 if let Some((ident, is_raw)) = get_macro_ident(&self.token) {
@@ -146,10 +178,33 @@ impl<'a> Parser<'a> {
                     return Err(self.struct_span_err(self.token.span, msg));
                 }
             }
-            NonterminalKind::Path => token::NtPath(self.parse_path(PathStyle::Type)?),
-            NonterminalKind::Meta => token::NtMeta(P(self.parse_attr_item()?)),
+            NonterminalKind::Path => {
+                let (mut path, tokens) =
+                    self.collect_tokens(|this| this.parse_path(PathStyle::Type))?;
+                // We have have eaten an NtPath, which could already have tokens
+                if path.tokens.is_none() {
+                    path.tokens = Some(tokens);
+                }
+                token::NtPath(path)
+            }
+            NonterminalKind::Meta => {
+                let (mut attr, tokens) = self.collect_tokens(|this| this.parse_attr_item())?;
+                // We may have eaten a nonterminal, which could already have tokens
+                if attr.tokens.is_none() {
+                    attr.tokens = Some(tokens);
+                }
+                token::NtMeta(P(attr))
+            }
             NonterminalKind::TT => token::NtTT(self.parse_token_tree()),
-            NonterminalKind::Vis => token::NtVis(self.parse_visibility(FollowedByType::Yes)?),
+            NonterminalKind::Vis => {
+                let (mut vis, tokens) =
+                    self.collect_tokens(|this| this.parse_visibility(FollowedByType::Yes))?;
+                // We may have etan an `NtVis`, which could already have tokens
+                if vis.tokens.is_none() {
+                    vis.tokens = Some(tokens);
+                }
+                token::NtVis(vis)
+            }
             NonterminalKind::Lifetime => {
                 if self.check_lifetime() {
                     token::NtLifetime(self.expect_lifetime().ident)
diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs
index 54b4df8613f..66ce015d02e 100644
--- a/compiler/rustc_parse/src/parser/path.rs
+++ b/compiler/rustc_parse/src/parser/path.rs
@@ -64,7 +64,7 @@ impl<'a> Parser<'a> {
             path_span = path_lo.to(self.prev_token.span);
         } else {
             path_span = self.token.span.to(self.token.span);
-            path = ast::Path { segments: Vec::new(), span: path_span };
+            path = ast::Path { segments: Vec::new(), span: path_span, tokens: None };
         }
 
         // See doc comment for `unmatched_angle_bracket_count`.
@@ -81,7 +81,10 @@ impl<'a> Parser<'a> {
         let qself = QSelf { ty, path_span, position: path.segments.len() };
         self.parse_path_segments(&mut path.segments, style)?;
 
-        Ok((qself, Path { segments: path.segments, span: lo.to(self.prev_token.span) }))
+        Ok((
+            qself,
+            Path { segments: path.segments, span: lo.to(self.prev_token.span), tokens: None },
+        ))
     }
 
     /// Recover from an invalid single colon, when the user likely meant a qualified path.
@@ -144,7 +147,7 @@ impl<'a> Parser<'a> {
         }
         self.parse_path_segments(&mut segments, style)?;
 
-        Ok(Path { segments, span: lo.to(self.prev_token.span) })
+        Ok(Path { segments, span: lo.to(self.prev_token.span), tokens: None })
     }
 
     pub(super) fn parse_path_segments(
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index ac067cb0eab..fd1c6b25aec 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -10,7 +10,7 @@ use rustc_ast as ast;
 use rustc_ast::ptr::P;
 use rustc_ast::token::{self, TokenKind};
 use rustc_ast::util::classify;
-use rustc_ast::{AttrStyle, AttrVec, Attribute, MacCall, MacStmtStyle};
+use rustc_ast::{AttrStyle, AttrVec, Attribute, MacCall, MacCallStmt, MacStmtStyle};
 use rustc_ast::{Block, BlockCheckMode, Expr, ExprKind, Local, Stmt, StmtKind, DUMMY_NODE_ID};
 use rustc_errors::{Applicability, PResult};
 use rustc_span::source_map::{BytePos, Span};
@@ -21,7 +21,8 @@ use std::mem;
 impl<'a> Parser<'a> {
     /// Parses a statement. This stops just before trailing semicolons on everything but items.
     /// e.g., a `StmtKind::Semi` parses to a `StmtKind::Expr`, leaving the trailing `;` unconsumed.
-    pub(super) fn parse_stmt(&mut self) -> PResult<'a, Option<Stmt>> {
+    // Public for rustfmt usage.
+    pub fn parse_stmt(&mut self) -> PResult<'a, Option<Stmt>> {
         Ok(self.parse_stmt_without_recovery().unwrap_or_else(|mut e| {
             e.emit();
             self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore);
@@ -106,7 +107,7 @@ impl<'a> Parser<'a> {
 
         let kind = if delim == token::Brace || self.token == token::Semi || self.token == token::Eof
         {
-            StmtKind::MacCall(P((mac, style, attrs)))
+            StmtKind::MacCall(P(MacCallStmt { mac, style, attrs }))
         } else {
             // Since none of the above applied, this is an expression statement macro.
             let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac), AttrVec::new());
@@ -363,7 +364,7 @@ impl<'a> Parser<'a> {
         let mut eat_semi = true;
         match stmt.kind {
             // Expression without semicolon.
-            StmtKind::Expr(ref expr)
+            StmtKind::Expr(ref mut expr)
                 if self.token != token::Eof && classify::expr_requires_semi_to_be_stmt(expr) =>
             {
                 // Just check for errors and recover; do not eat semicolon yet.
@@ -387,15 +388,29 @@ impl<'a> Parser<'a> {
                             );
                         }
                     }
-                    e.emit();
-                    self.recover_stmt();
+                    if let Err(mut e) =
+                        self.check_mistyped_turbofish_with_multiple_type_params(e, expr)
+                    {
+                        e.emit();
+                        self.recover_stmt();
+                    }
                     // Don't complain about type errors in body tail after parse error (#57383).
                     let sp = expr.span.to(self.prev_token.span);
-                    stmt.kind = StmtKind::Expr(self.mk_expr_err(sp));
+                    *expr = self.mk_expr_err(sp);
                 }
             }
-            StmtKind::Local(..) => {
-                self.expect_semi()?;
+            StmtKind::Local(ref mut local) => {
+                if let Err(e) = self.expect_semi() {
+                    // We might be at the `,` in `let x = foo<bar, baz>;`. Try to recover.
+                    match &mut local.init {
+                        Some(ref mut expr) => {
+                            self.check_mistyped_turbofish_with_multiple_type_params(e, expr)?;
+                            // We found `foo<bar, baz>`, have we fully recovered?
+                            self.expect_semi()?;
+                        }
+                        None => return Err(e),
+                    }
+                }
                 eat_semi = false;
             }
             StmtKind::Empty => eat_semi = false,
@@ -410,11 +425,11 @@ impl<'a> Parser<'a> {
     }
 
     pub(super) fn mk_block(&self, stmts: Vec<Stmt>, rules: BlockCheckMode, span: Span) -> P<Block> {
-        P(Block { stmts, id: DUMMY_NODE_ID, rules, span })
+        P(Block { stmts, id: DUMMY_NODE_ID, rules, span, tokens: None })
     }
 
     pub(super) fn mk_stmt(&self, span: Span, kind: StmtKind) -> Stmt {
-        Stmt { id: DUMMY_NODE_ID, kind, span }
+        Stmt { id: DUMMY_NODE_ID, kind, span, tokens: None }
     }
 
     fn mk_stmt_err(&self, span: Span) -> Stmt {
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index 4356850818e..d42a786a18f 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -67,7 +67,7 @@ impl<'a> Parser<'a> {
 
     /// Parse a type suitable for a function or function pointer parameter.
     /// The difference from `parse_ty` is that this version allows `...`
-    /// (`CVarArgs`) at the top level of the the type.
+    /// (`CVarArgs`) at the top level of the type.
     pub(super) fn parse_ty_for_param(&mut self) -> PResult<'a, P<Ty>> {
         self.parse_ty_common(AllowPlus::Yes, RecoverQPath::Yes, AllowCVariadic::Yes)
     }
@@ -276,8 +276,34 @@ impl<'a> Parser<'a> {
     }
 
     fn parse_borrowed_pointee(&mut self) -> PResult<'a, TyKind> {
-        let opt_lifetime = if self.check_lifetime() { Some(self.expect_lifetime()) } else { None };
+        let and_span = self.prev_token.span;
+        let mut opt_lifetime =
+            if self.check_lifetime() { Some(self.expect_lifetime()) } else { None };
         let mutbl = self.parse_mutability();
+        if self.token.is_lifetime() && mutbl == Mutability::Mut && opt_lifetime.is_none() {
+            // A lifetime is invalid here: it would be part of a bare trait bound, which requires
+            // it to be followed by a plus, but we disallow plus in the pointee type.
+            // So we can handle this case as an error here, and suggest `'a mut`.
+            // If there *is* a plus next though, handling the error later provides better suggestions
+            // (like adding parentheses)
+            if !self.look_ahead(1, |t| t.is_like_plus()) {
+                let lifetime_span = self.token.span;
+                let span = and_span.to(lifetime_span);
+
+                let mut err = self.struct_span_err(span, "lifetime must precede `mut`");
+                if let Ok(lifetime_src) = self.span_to_snippet(lifetime_span) {
+                    err.span_suggestion(
+                        span,
+                        "place the lifetime before `mut`",
+                        format!("&{} mut", lifetime_src),
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+                err.emit();
+
+                opt_lifetime = Some(self.expect_lifetime());
+            }
+        }
         let ty = self.parse_ty_no_plus()?;
         Ok(TyKind::Rptr(opt_lifetime, MutTy { ty, mutbl }))
     }
@@ -626,6 +652,6 @@ impl<'a> Parser<'a> {
     }
 
     pub(super) fn mk_ty(&self, span: Span, kind: TyKind) -> P<Ty> {
-        P(Ty { kind, span, id: ast::DUMMY_NODE_ID })
+        P(Ty { kind, span, id: ast::DUMMY_NODE_ID, tokens: None })
     }
 }
diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs
index e07b8b86aef..25e3e67e28e 100644
--- a/compiler/rustc_parse_format/src/lib.rs
+++ b/compiler/rustc_parse_format/src/lib.rs
@@ -5,14 +5,12 @@
 //! generated instead.
 
 #![doc(
-    html_root_url = "https://doc.rust-lang.org/nightly/",
+    html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/",
     html_playground_url = "https://play.rust-lang.org/",
     test(attr(deny(warnings)))
 )]
 #![feature(nll)]
 #![feature(or_patterns)]
-#![feature(rustc_private)]
-#![feature(unicode_internals)]
 #![feature(bool_to_option)]
 
 pub use Alignment::*;
@@ -527,12 +525,9 @@ impl<'a> Parser<'a> {
 
         // fill character
         if let Some(&(_, c)) = self.cur.peek() {
-            match self.cur.clone().nth(1) {
-                Some((_, '>' | '<' | '^')) => {
-                    spec.fill = Some(c);
-                    self.cur.next();
-                }
-                _ => {}
+            if let Some((_, '>' | '<' | '^')) = self.cur.clone().nth(1) {
+                spec.fill = Some(c);
+                self.cur.next();
             }
         }
         // Alignment
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 832cde86d0b..efe947daa28 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -66,12 +66,26 @@ impl CheckAttrVisitor<'tcx> {
             } else if self.tcx.sess.check_name(attr, sym::marker) {
                 self.check_marker(attr, span, target)
             } else if self.tcx.sess.check_name(attr, sym::target_feature) {
-                self.check_target_feature(attr, span, target)
+                self.check_target_feature(hir_id, attr, span, target)
             } else if self.tcx.sess.check_name(attr, sym::track_caller) {
                 self.check_track_caller(&attr.span, attrs, span, target)
             } else if self.tcx.sess.check_name(attr, sym::doc) {
                 self.check_doc_alias(attr, hir_id, target)
+            } else if self.tcx.sess.check_name(attr, sym::no_link) {
+                self.check_no_link(&attr, span, target)
+            } else if self.tcx.sess.check_name(attr, sym::export_name) {
+                self.check_export_name(&attr, span, target)
             } else {
+                // lint-only checks
+                if self.tcx.sess.check_name(attr, sym::cold) {
+                    self.check_cold(hir_id, attr, span, target);
+                } else if self.tcx.sess.check_name(attr, sym::link_name) {
+                    self.check_link_name(hir_id, attr, span, target);
+                } else if self.tcx.sess.check_name(attr, sym::link_section) {
+                    self.check_link_section(hir_id, attr, span, target);
+                } else if self.tcx.sess.check_name(attr, sym::no_mangle) {
+                    self.check_no_mangle(hir_id, attr, span, target);
+                }
                 true
             };
         }
@@ -109,12 +123,12 @@ impl CheckAttrVisitor<'tcx> {
                     lint.build("`#[inline]` is ignored on constants")
                         .warn(
                             "this was previously accepted by the compiler but is \
-                               being phased out; it will become a hard error in \
-                               a future release!",
+                             being phased out; it will become a hard error in \
+                             a future release!",
                         )
                         .note(
                             "see issue #65833 <https://github.com/rust-lang/rust/issues/65833> \
-                                 for more information",
+                             for more information",
                         )
                         .emit();
                 });
@@ -153,7 +167,7 @@ impl CheckAttrVisitor<'tcx> {
                 .emit();
                 false
             }
-            Target::Fn | Target::Method(..) | Target::ForeignFn => true,
+            Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => true,
             _ => {
                 struct_span_err!(
                     self.tcx.sess,
@@ -202,10 +216,31 @@ impl CheckAttrVisitor<'tcx> {
     }
 
     /// Checks if the `#[target_feature]` attribute on `item` is valid. Returns `true` if valid.
-    fn check_target_feature(&self, attr: &Attribute, span: &Span, target: Target) -> bool {
+    fn check_target_feature(
+        &self,
+        hir_id: HirId,
+        attr: &Attribute,
+        span: &Span,
+        target: Target,
+    ) -> bool {
         match target {
             Target::Fn
             | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
+            // FIXME: #[target_feature] was previously erroneously allowed on statements and some
+            // crates used this, so only emit a warning.
+            Target::Statement => {
+                self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
+                    lint.build("attribute should be applied to a function")
+                        .warn(
+                            "this was previously accepted by the compiler but is \
+                             being phased out; it will become a hard error in \
+                             a future release!",
+                        )
+                        .span_label(*span, "not a function")
+                        .emit();
+                });
+                true
+            }
             _ => {
                 self.tcx
                     .sess
@@ -250,13 +285,23 @@ impl CheckAttrVisitor<'tcx> {
                                     None
                                 }
                             }
+                            Target::AssocConst => {
+                                let parent_hir_id = self.tcx.hir().get_parent_item(hir_id);
+                                let containing_item = self.tcx.hir().expect_item(parent_hir_id);
+                                // We can't link to trait impl's consts.
+                                let err = "associated constant in trait implementation block";
+                                match containing_item.kind {
+                                    ItemKind::Impl { of_trait: Some(_), .. } => Some(err),
+                                    _ => None,
+                                }
+                            }
                             _ => None,
                         } {
                             self.tcx
                                 .sess
                                 .struct_span_err(
                                     meta.span(),
-                                    &format!("`#[doc(alias = \"...\")]` isn't allowed on {}", err,),
+                                    &format!("`#[doc(alias = \"...\")]` isn't allowed on {}", err),
                                 )
                                 .emit();
                         }
@@ -267,6 +312,136 @@ impl CheckAttrVisitor<'tcx> {
         true
     }
 
+    /// Checks if `#[cold]` is applied to a non-function. Returns `true` if valid.
+    fn check_cold(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
+        match target {
+            Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => {}
+            _ => {
+                // FIXME: #[cold] was previously allowed on non-functions and some crates used
+                // this, so only emit a warning.
+                self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
+                    lint.build("attribute should be applied to a function")
+                        .warn(
+                            "this was previously accepted by the compiler but is \
+                             being phased out; it will become a hard error in \
+                             a future release!",
+                        )
+                        .span_label(*span, "not a function")
+                        .emit();
+                });
+            }
+        }
+    }
+
+    /// Checks if `#[link_name]` is applied to an item other than a foreign function or static.
+    fn check_link_name(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
+        match target {
+            Target::ForeignFn | Target::ForeignStatic => {}
+            _ => {
+                // FIXME: #[cold] was previously allowed on non-functions/statics and some crates
+                // used this, so only emit a warning.
+                self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
+                    let mut diag =
+                        lint.build("attribute should be applied to a foreign function or static");
+                    diag.warn(
+                        "this was previously accepted by the compiler but is \
+                         being phased out; it will become a hard error in \
+                         a future release!",
+                    );
+
+                    // See issue #47725
+                    if let Target::ForeignMod = target {
+                        if let Some(value) = attr.value_str() {
+                            diag.span_help(
+                                attr.span,
+                                &format!(r#"try `#[link(name = "{}")]` instead"#, value),
+                            );
+                        } else {
+                            diag.span_help(attr.span, r#"try `#[link(name = "...")]` instead"#);
+                        }
+                    }
+
+                    diag.span_label(*span, "not a foreign function or static");
+                    diag.emit();
+                });
+            }
+        }
+    }
+
+    /// Checks if `#[no_link]` is applied to an `extern crate`. Returns `true` if valid.
+    fn check_no_link(&self, attr: &Attribute, span: &Span, target: Target) -> bool {
+        if target == Target::ExternCrate {
+            true
+        } else {
+            self.tcx
+                .sess
+                .struct_span_err(attr.span, "attribute should be applied to an `extern crate` item")
+                .span_label(*span, "not an `extern crate` item")
+                .emit();
+            false
+        }
+    }
+
+    /// Checks if `#[export_name]` is applied to a function or static. Returns `true` if valid.
+    fn check_export_name(&self, attr: &Attribute, span: &Span, target: Target) -> bool {
+        match target {
+            Target::Static | Target::Fn | Target::Method(..) => true,
+            _ => {
+                self.tcx
+                    .sess
+                    .struct_span_err(
+                        attr.span,
+                        "attribute should be applied to a function or static",
+                    )
+                    .span_label(*span, "not a function or static")
+                    .emit();
+                false
+            }
+        }
+    }
+
+    /// Checks if `#[link_section]` is applied to a function or static.
+    fn check_link_section(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
+        match target {
+            Target::Static | Target::Fn | Target::Method(..) => {}
+            _ => {
+                // FIXME: #[link_section] was previously allowed on non-functions/statics and some
+                // crates used this, so only emit a warning.
+                self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
+                    lint.build("attribute should be applied to a function or static")
+                        .warn(
+                            "this was previously accepted by the compiler but is \
+                             being phased out; it will become a hard error in \
+                             a future release!",
+                        )
+                        .span_label(*span, "not a function or static")
+                        .emit();
+                });
+            }
+        }
+    }
+
+    /// Checks if `#[no_mangle]` is applied to a function or static.
+    fn check_no_mangle(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
+        match target {
+            Target::Static | Target::Fn | Target::Method(..) => {}
+            _ => {
+                // FIXME: #[no_mangle] was previously allowed on non-functions/statics and some
+                // crates used this, so only emit a warning.
+                self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
+                    lint.build("attribute should be applied to a function or static")
+                        .warn(
+                            "this was previously accepted by the compiler but is \
+                             being phased out; it will become a hard error in \
+                             a future release!",
+                        )
+                        .span_label(*span, "not a function or static")
+                        .emit();
+                });
+            }
+        }
+    }
+
     /// Checks if the `#[repr]` attributes on `item` are valid.
     fn check_repr(
         &self,
@@ -311,7 +486,11 @@ impl CheckAttrVisitor<'tcx> {
                 }
                 sym::simd => {
                     is_simd = true;
-                    if target != Target::Struct { ("a", "struct") } else { continue }
+                    if target != Target::Struct {
+                        ("a", "struct")
+                    } else {
+                        continue;
+                    }
                 }
                 sym::transparent => {
                     is_transparent = true;
@@ -348,7 +527,11 @@ impl CheckAttrVisitor<'tcx> {
                 | sym::isize
                 | sym::usize => {
                     int_reprs += 1;
-                    if target != Target::Enum { ("an", "enum") } else { continue }
+                    if target != Target::Enum {
+                        ("an", "enum")
+                    } else {
+                        continue;
+                    }
                 }
                 _ => continue,
             };
@@ -411,10 +594,8 @@ impl CheckAttrVisitor<'tcx> {
     fn check_stmt_attributes(&self, stmt: &hir::Stmt<'_>) {
         // When checking statements ignore expressions, they will be checked later
         if let hir::StmtKind::Local(ref l) = stmt.kind {
+            self.check_attributes(l.hir_id, &l.attrs, &stmt.span, Target::Statement, None);
             for attr in l.attrs.iter() {
-                if self.tcx.sess.check_name(attr, sym::inline) {
-                    self.check_inline(l.hir_id, attr, &stmt.span, Target::Statement);
-                }
                 if self.tcx.sess.check_name(attr, sym::repr) {
                     self.emit_repr_error(
                         attr.span,
@@ -432,10 +613,8 @@ impl CheckAttrVisitor<'tcx> {
             hir::ExprKind::Closure(..) => Target::Closure,
             _ => Target::Expression,
         };
+        self.check_attributes(expr.hir_id, &expr.attrs, &expr.span, target, None);
         for attr in expr.attrs.iter() {
-            if self.tcx.sess.check_name(attr, sym::inline) {
-                self.check_inline(expr.hir_id, attr, &expr.span, target);
-            }
             if self.tcx.sess.check_name(attr, sym::repr) {
                 self.emit_repr_error(
                     attr.span,
diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs
index 01da33ddd2e..fe6653e98da 100644
--- a/compiler/rustc_passes/src/dead.rs
+++ b/compiler/rustc_passes/src/dead.rs
@@ -104,7 +104,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
                 if let Some(t) = t {
                     self.check_def_id(t);
                 }
-                if let Some(i) = i {
+                if let Some((i, _)) = i {
                     self.check_def_id(i);
                 }
             }
@@ -124,7 +124,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
     }
 
     fn handle_field_access(&mut self, lhs: &hir::Expr<'_>, hir_id: hir::HirId) {
-        match self.typeck_results().expr_ty_adjusted(lhs).kind {
+        match self.typeck_results().expr_ty_adjusted(lhs).kind() {
             ty::Adt(def, _) => {
                 let index = self.tcx.field_index(hir_id, self.typeck_results());
                 self.insert_def_id(def.non_enum_variant().fields[index].did);
@@ -140,7 +140,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
         res: Res,
         pats: &[hir::FieldPat<'_>],
     ) {
-        let variant = match self.typeck_results().node_type(lhs.hir_id).kind {
+        let variant = match self.typeck_results().node_type(lhs.hir_id).kind() {
             ty::Adt(adt, _) => adt.variant_of_res(res),
             _ => span_bug!(lhs.span, "non-ADT in struct pattern"),
         };
@@ -269,7 +269,7 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
             hir::ExprKind::Struct(ref qpath, ref fields, _) => {
                 let res = self.typeck_results().qpath_res(qpath, expr.hir_id);
                 self.handle_res(res);
-                if let ty::Adt(ref adt, _) = self.typeck_results().expr_ty(expr).kind {
+                if let ty::Adt(ref adt, _) = self.typeck_results().expr_ty(expr).kind() {
                     self.mark_as_used_if_union(adt, fields);
                 }
             }
diff --git a/compiler/rustc_passes/src/diagnostic_items.rs b/compiler/rustc_passes/src/diagnostic_items.rs
index df0f9f157ae..94592935c7f 100644
--- a/compiler/rustc_passes/src/diagnostic_items.rs
+++ b/compiler/rustc_passes/src/diagnostic_items.rs
@@ -12,11 +12,11 @@
 use rustc_ast as ast;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir as hir;
-use rustc_hir::def_id::{DefId, LOCAL_CRATE};
 use rustc_hir::itemlikevisit::ItemLikeVisitor;
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
+use rustc_span::def_id::{DefId, LOCAL_CRATE};
 use rustc_span::symbol::{sym, Symbol};
 
 struct DiagnosticItemCollector<'tcx> {
@@ -100,6 +100,18 @@ fn collect<'tcx>(tcx: TyCtxt<'tcx>) -> FxHashMap<Symbol, DefId> {
 
     // Collect diagnostic items in this crate.
     tcx.hir().krate().visit_all_item_likes(&mut collector);
+    // FIXME(visit_all_item_likes): Foreign items are not visited
+    // here, so we have to manually look at them for now.
+    for foreign_module in tcx.foreign_modules(LOCAL_CRATE) {
+        for &foreign_item in foreign_module.foreign_items.iter() {
+            match tcx.hir().get(tcx.hir().local_def_id_to_hir_id(foreign_item.expect_local())) {
+                hir::Node::ForeignItem(item) => {
+                    collector.observe_item(item.attrs, item.hir_id);
+                }
+                item => bug!("unexpected foreign item {:?}", item),
+            }
+        }
+    }
 
     collector.items
 }
diff --git a/compiler/rustc_passes/src/intrinsicck.rs b/compiler/rustc_passes/src/intrinsicck.rs
index ebe231009d3..79f1c2b9da8 100644
--- a/compiler/rustc_passes/src/intrinsicck.rs
+++ b/compiler/rustc_passes/src/intrinsicck.rs
@@ -35,7 +35,7 @@ struct ExprVisitor<'tcx> {
 /// If the type is `Option<T>`, it will return `T`, otherwise
 /// the type itself. Works on most `Option`-like types.
 fn unpack_option_like<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
-    let (def, substs) = match ty.kind {
+    let (def, substs) = match *ty.kind() {
         ty::Adt(def, substs) => (def, substs),
         _ => return ty,
     };
@@ -81,7 +81,7 @@ impl ExprVisitor<'tcx> {
             // Special-case transmutting from `typeof(function)` and
             // `Option<typeof(function)>` to present a clearer error.
             let from = unpack_option_like(self.tcx, from);
-            if let (&ty::FnDef(..), SizeSkeleton::Known(size_to)) = (&from.kind, sk_to) {
+            if let (&ty::FnDef(..), SizeSkeleton::Known(size_to)) = (from.kind(), sk_to) {
                 if size_to == Pointer.size(&self.tcx) {
                     struct_span_err!(self.tcx.sess, span, E0591, "can't transmute zero-sized type")
                         .note(&format!("source type: {}", from))
@@ -127,7 +127,7 @@ impl ExprVisitor<'tcx> {
         if ty.is_sized(self.tcx.at(DUMMY_SP), self.param_env) {
             return true;
         }
-        if let ty::Foreign(..) = ty.kind {
+        if let ty::Foreign(..) = ty.kind() {
             return true;
         }
         false
@@ -149,7 +149,7 @@ impl ExprVisitor<'tcx> {
             64 => InlineAsmType::I64,
             _ => unreachable!(),
         };
-        let asm_ty = match ty.kind {
+        let asm_ty = match *ty.kind() {
             ty::Never | ty::Error(_) => return None,
             ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Some(InlineAsmType::I8),
             ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Some(InlineAsmType::I16),
@@ -166,7 +166,7 @@ impl ExprVisitor<'tcx> {
             ty::Adt(adt, substs) if adt.repr.simd() => {
                 let fields = &adt.non_enum_variant().fields;
                 let elem_ty = fields[0].ty(self.tcx, substs);
-                match elem_ty.kind {
+                match elem_ty.kind() {
                     ty::Never | ty::Error(_) => return None,
                     ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => {
                         Some(InlineAsmType::VecI8(fields.len() as u64))
@@ -374,7 +374,7 @@ impl ExprVisitor<'tcx> {
                 }
                 hir::InlineAsmOperand::Const { ref expr } => {
                     let ty = self.typeck_results.expr_ty_adjusted(expr);
-                    match ty.kind {
+                    match ty.kind() {
                         ty::Int(_) | ty::Uint(_) | ty::Float(_) => {}
                         _ => {
                             let msg =
diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs
index be4c542ec3a..c14d6aace87 100644
--- a/compiler/rustc_passes/src/lib.rs
+++ b/compiler/rustc_passes/src/lib.rs
@@ -4,7 +4,7 @@
 //!
 //! This API is completely unstable and subject to change.
 
-#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
+#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![feature(in_band_lifetimes)]
 #![feature(nll)]
 #![feature(or_patterns)]
diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs
index 55525586479..db9495201cf 100644
--- a/compiler/rustc_passes/src/liveness.rs
+++ b/compiler/rustc_passes/src/liveness.rs
@@ -958,7 +958,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
         }
 
         let ty = self.typeck_results.node_type(id);
-        match ty.kind {
+        match ty.kind() {
             ty::Closure(_def_id, substs) => match substs.as_closure().kind() {
                 ty::ClosureKind::Fn => {}
                 ty::ClosureKind::FnMut => {}
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index 91edc7d9db7..4ca52f405fb 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -9,13 +9,14 @@ use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE};
 use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
-use rustc_hir::{Generics, HirId, Item, StructField, Variant};
+use rustc_hir::{Generics, HirId, Item, StructField, TraitRef, Ty, TyKind, Variant};
 use rustc_middle::hir::map::Map;
 use rustc_middle::middle::privacy::AccessLevels;
 use rustc_middle::middle::stability::{DeprecationEntry, Index};
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::lint;
+use rustc_session::lint::builtin::INEFFECTIVE_UNSTABLE_TRAIT_IMPL;
 use rustc_session::parse::feature_err;
 use rustc_session::Session;
 use rustc_span::symbol::{sym, Symbol};
@@ -538,7 +539,37 @@ impl Visitor<'tcx> for Checker<'tcx> {
             // For implementations of traits, check the stability of each item
             // individually as it's possible to have a stable trait with unstable
             // items.
-            hir::ItemKind::Impl { of_trait: Some(ref t), items, .. } => {
+            hir::ItemKind::Impl { of_trait: Some(ref t), self_ty, items, .. } => {
+                if self.tcx.features().staged_api {
+                    // If this impl block has an #[unstable] attribute, give an
+                    // error if all involved types and traits are stable, because
+                    // it will have no effect.
+                    // See: https://github.com/rust-lang/rust/issues/55436
+                    if let (Some(Stability { level: attr::Unstable { .. }, .. }), _) =
+                        attr::find_stability(&self.tcx.sess, &item.attrs, item.span)
+                    {
+                        let mut c = CheckTraitImplStable { tcx: self.tcx, fully_stable: true };
+                        c.visit_ty(self_ty);
+                        c.visit_trait_ref(t);
+                        if c.fully_stable {
+                            let span = item
+                                .attrs
+                                .iter()
+                                .find(|a| a.has_name(sym::unstable))
+                                .map_or(item.span, |a| a.span);
+                            self.tcx.struct_span_lint_hir(
+                                INEFFECTIVE_UNSTABLE_TRAIT_IMPL,
+                                item.hir_id,
+                                span,
+                                |lint| lint
+                                    .build("an `#[unstable]` annotation here has no effect")
+                                    .note("see issue #55436 <https://github.com/rust-lang/rust/issues/55436> for more information")
+                                    .emit()
+                            );
+                        }
+                    }
+                }
+
                 if let Res::Def(DefKind::Trait, trait_did) = t.path.res {
                     for impl_item_ref in items {
                         let impl_item = self.tcx.hir().impl_item(impl_item_ref.id);
@@ -598,6 +629,44 @@ impl Visitor<'tcx> for Checker<'tcx> {
     }
 }
 
+struct CheckTraitImplStable<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    fully_stable: bool,
+}
+
+impl Visitor<'tcx> for CheckTraitImplStable<'tcx> {
+    type Map = Map<'tcx>;
+
+    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+        NestedVisitorMap::None
+    }
+
+    fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _id: hir::HirId) {
+        if let Some(def_id) = path.res.opt_def_id() {
+            if let Some(stab) = self.tcx.lookup_stability(def_id) {
+                self.fully_stable &= stab.level.is_stable();
+            }
+        }
+        intravisit::walk_path(self, path)
+    }
+
+    fn visit_trait_ref(&mut self, t: &'tcx TraitRef<'tcx>) {
+        if let Res::Def(DefKind::Trait, trait_did) = t.path.res {
+            if let Some(stab) = self.tcx.lookup_stability(trait_did) {
+                self.fully_stable &= stab.level.is_stable();
+            }
+        }
+        intravisit::walk_trait_ref(self, t)
+    }
+
+    fn visit_ty(&mut self, t: &'tcx Ty<'tcx>) {
+        if let TyKind::Never = t.kind {
+            self.fully_stable = false;
+        }
+        intravisit::walk_ty(self, t)
+    }
+}
+
 /// Given the list of enabled features that were not language features (i.e., that
 /// were expected to be library features), and the list of features used from
 /// libraries, identify activated features that don't exist and error about them.
diff --git a/compiler/rustc_plugin_impl/src/lib.rs b/compiler/rustc_plugin_impl/src/lib.rs
index 1eb65dd96ba..5bf4d300e9e 100644
--- a/compiler/rustc_plugin_impl/src/lib.rs
+++ b/compiler/rustc_plugin_impl/src/lib.rs
@@ -6,7 +6,7 @@
 //! feature](https://doc.rust-lang.org/nightly/unstable-book/language-features/plugin.html)
 //! of the Unstable Book for some examples.
 
-#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
+#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![feature(nll)]
 #![recursion_limit = "256"]
 
diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs
index deb4277cb38..8d1b826ea35 100644
--- a/compiler/rustc_privacy/src/lib.rs
+++ b/compiler/rustc_privacy/src/lib.rs
@@ -1,7 +1,6 @@
-#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
+#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![feature(in_band_lifetimes)]
 #![feature(nll)]
-#![feature(or_patterns)]
 #![recursion_limit = "256"]
 
 use rustc_attr as attr;
@@ -97,6 +96,15 @@ where
                 ty.visit_with(self)
             }
             ty::PredicateAtom::RegionOutlives(..) => false,
+            ty::PredicateAtom::ConstEvaluatable(..)
+                if self.def_id_visitor.tcx().features().const_evaluatable_checked =>
+            {
+                // FIXME(const_evaluatable_checked): If the constant used here depends on a
+                // private function we may have to do something here...
+                //
+                // For now, let's just pretend that everything is fine.
+                false
+            }
             _ => bug!("unexpected predicate: {:?}", predicate),
         }
     }
@@ -119,7 +127,7 @@ where
     fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool {
         let tcx = self.def_id_visitor.tcx();
         // InternalSubsts are not visited here because they are visited below in `super_visit_with`.
-        match ty.kind {
+        match *ty.kind() {
             ty::Adt(&ty::AdtDef { did: def_id, .. }, ..)
             | ty::Foreign(def_id)
             | ty::FnDef(def_id, ..)
@@ -134,7 +142,7 @@ where
                 // Default type visitor doesn't visit signatures of fn types.
                 // Something like `fn() -> Priv {my_func}` is considered a private type even if
                 // `my_func` is public, so we need to visit signatures.
-                if let ty::FnDef(..) = ty.kind {
+                if let ty::FnDef(..) = ty.kind() {
                     if tcx.fn_sig(def_id).visit_with(self) {
                         return true;
                     }
diff --git a/compiler/rustc_query_system/Cargo.toml b/compiler/rustc_query_system/Cargo.toml
index 7defb00a881..f38d62dec00 100644
--- a/compiler/rustc_query_system/Cargo.toml
+++ b/compiler/rustc_query_system/Cargo.toml
@@ -17,5 +17,5 @@ rustc_macros = { path = "../rustc_macros" }
 rustc_index = { path = "../rustc_index" }
 rustc_serialize = { path = "../rustc_serialize" }
 rustc_span = { path = "../rustc_span" }
-parking_lot = "0.10"
+parking_lot = "0.11"
 smallvec = { version = "1.0", features = ["union", "may_dangle"] }
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index 761724be57d..a48d002b2a3 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -218,7 +218,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
         speculative: bool,
     ) -> Result<ty::Visibility, VisResolutionError<'ast>> {
         let parent_scope = &self.parent_scope;
-        match vis.node {
+        match vis.kind {
             ast::VisibilityKind::Public => Ok(ty::Visibility::Public),
             ast::VisibilityKind::Crate(..) => {
                 Ok(ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX)))
@@ -395,7 +395,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
         // so prefixes are prepended with crate root segment if necessary.
         // The root is prepended lazily, when the first non-empty prefix or terminating glob
         // appears, so imports in braced groups can have roots prepended independently.
-        let is_glob = if let ast::UseTreeKind::Glob = use_tree.kind { true } else { false };
+        let is_glob = matches!(use_tree.kind, ast::UseTreeKind::Glob);
         let crate_root = match prefix_iter.peek() {
             Some(seg) if !seg.ident.is_path_segment_keyword() && seg.ident.span.rust_2015() => {
                 Some(seg.ident.span.ctxt())
@@ -796,23 +796,26 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
                         vis
                     };
 
+                    let mut ret_fields = Vec::with_capacity(vdata.fields().len());
+
                     for field in vdata.fields() {
                         // NOTE: The field may be an expansion placeholder, but expansion sets
                         // correct visibilities for unnamed field placeholders specifically, so the
                         // constructor visibility should still be determined correctly.
-                        if let Ok(field_vis) = self.resolve_visibility_speculative(&field.vis, true)
-                        {
-                            if ctor_vis.is_at_least(field_vis, &*self.r) {
-                                ctor_vis = field_vis;
-                            }
+                        let field_vis = self
+                            .resolve_visibility_speculative(&field.vis, true)
+                            .unwrap_or(ty::Visibility::Public);
+                        if ctor_vis.is_at_least(field_vis, &*self.r) {
+                            ctor_vis = field_vis;
                         }
+                        ret_fields.push(field_vis);
                     }
                     let ctor_res = Res::Def(
                         DefKind::Ctor(CtorOf::Struct, CtorKind::from_ast(vdata)),
                         self.r.local_def_id(ctor_node_id).to_def_id(),
                     );
                     self.r.define(parent, ident, ValueNS, (ctor_res, ctor_vis, sp, expansion));
-                    self.r.struct_constructors.insert(def_id, (ctor_res, ctor_vis));
+                    self.r.struct_constructors.insert(def_id, (ctor_res, ctor_vis, ret_fields));
                 }
             }
 
@@ -964,7 +967,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
             Res::Def(DefKind::Ctor(CtorOf::Struct, ..), def_id) => {
                 let parent = cstore.def_key(def_id).parent;
                 if let Some(struct_def_id) = parent.map(|index| DefId { index, ..def_id }) {
-                    self.r.struct_constructors.insert(struct_def_id, (res, vis));
+                    self.r.struct_constructors.insert(struct_def_id, (res, vis, vec![]));
                 }
             }
             _ => {}
diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs
index 5624a6b6acc..89ce89b2e9a 100644
--- a/compiler/rustc_resolve/src/check_unused.rs
+++ b/compiler/rustc_resolve/src/check_unused.rs
@@ -105,7 +105,7 @@ impl<'a, 'b> Visitor<'a> for UnusedImportCheckVisitor<'a, 'b> {
         // because this means that they were generated in some fashion by the
         // compiler and we don't need to consider them.
         if let ast::ItemKind::Use(..) = item.kind {
-            if item.vis.node.is_pub() || item.span.is_dummy() {
+            if item.vis.kind.is_pub() || item.span.is_dummy() {
                 return;
             }
         }
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index 48e1068b8da..612bc3e7491 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -112,7 +112,7 @@ impl<'a> Resolver<'a> {
                 match outer_res {
                     Res::SelfTy(maybe_trait_defid, maybe_impl_defid) => {
                         if let Some(impl_span) =
-                            maybe_impl_defid.and_then(|def_id| self.opt_span(def_id))
+                            maybe_impl_defid.and_then(|(def_id, _)| self.opt_span(def_id))
                         {
                             err.span_label(
                                 reduce_impl_span_to_impl_keyword(sm, impl_span),
@@ -466,7 +466,7 @@ impl<'a> Resolver<'a> {
                 );
                 err
             }
-            ResolutionError::ParamInNonTrivialAnonConst(name) => {
+            ResolutionError::ParamInNonTrivialAnonConst { name, is_type } => {
                 let mut err = self.session.struct_span_err(
                     span,
                     "generic parameters must not be used inside of non trivial constant values",
@@ -478,9 +478,17 @@ impl<'a> Resolver<'a> {
                         name
                     ),
                 );
-                err.help(
-                    &format!("it is currently only allowed to use either `{0}` or `{{ {0} }}` as generic constants", name)
-                );
+
+                if is_type {
+                    err.note("type parameters are currently not permitted in anonymous constants");
+                } else {
+                    err.help(
+                        &format!("it is currently only allowed to use either `{0}` or `{{ {0} }}` as generic constants",
+                                 name
+                        )
+                    );
+                }
+
                 err
             }
             ResolutionError::SelfInTyParamDefault => {
@@ -794,7 +802,7 @@ impl<'a> Resolver<'a> {
                         }
 
                         segms.push(ast::PathSegment::from_ident(ident));
-                        let path = Path { span: name_binding.span, segments: segms };
+                        let path = Path { span: name_binding.span, segments: segms, tokens: None };
                         let did = match res {
                             Res::Def(DefKind::Ctor(..), did) => this.parent(did),
                             _ => res.opt_def_id(),
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index d113eb22aba..2c01934b490 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -110,6 +110,9 @@ crate enum RibKind<'a> {
     ItemRibKind(HasGenericParams),
 
     /// We're in a constant item. Can't refer to dynamic stuff.
+    ///
+    /// The `bool` indicates if this constant may reference generic parameters
+    /// and is used to only allow generic parameters to be used in trivial constant expressions.
     ConstantItemRibKind(bool),
 
     /// We passed through a module.
@@ -188,7 +191,7 @@ crate enum PathSource<'a> {
     // Paths in struct expressions and patterns `Path { .. }`.
     Struct,
     // Paths in tuple struct patterns `Path(..)`.
-    TupleStruct(Span),
+    TupleStruct(Span, &'a [Span]),
     // `m::A::B` in `<T as m::A>::B::C`.
     TraitItem(Namespace),
 }
@@ -197,7 +200,7 @@ impl<'a> PathSource<'a> {
     fn namespace(self) -> Namespace {
         match self {
             PathSource::Type | PathSource::Trait(_) | PathSource::Struct => TypeNS,
-            PathSource::Expr(..) | PathSource::Pat | PathSource::TupleStruct(_) => ValueNS,
+            PathSource::Expr(..) | PathSource::Pat | PathSource::TupleStruct(..) => ValueNS,
             PathSource::TraitItem(ns) => ns,
         }
     }
@@ -208,7 +211,7 @@ impl<'a> PathSource<'a> {
             | PathSource::Expr(..)
             | PathSource::Pat
             | PathSource::Struct
-            | PathSource::TupleStruct(_) => true,
+            | PathSource::TupleStruct(..) => true,
             PathSource::Trait(_) | PathSource::TraitItem(..) => false,
         }
     }
@@ -219,7 +222,7 @@ impl<'a> PathSource<'a> {
             PathSource::Trait(_) => "trait",
             PathSource::Pat => "unit struct, unit variant or constant",
             PathSource::Struct => "struct, variant or union type",
-            PathSource::TupleStruct(_) => "tuple struct or tuple variant",
+            PathSource::TupleStruct(..) => "tuple struct or tuple variant",
             PathSource::TraitItem(ns) => match ns {
                 TypeNS => "associated type",
                 ValueNS => "method or associated constant",
@@ -305,7 +308,7 @@ impl<'a> PathSource<'a> {
                 | Res::SelfCtor(..) => true,
                 _ => false,
             },
-            PathSource::TupleStruct(_) => match res {
+            PathSource::TupleStruct(..) => match res {
                 Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) | Res::SelfCtor(..) => true,
                 _ => false,
             },
@@ -340,8 +343,8 @@ impl<'a> PathSource<'a> {
             (PathSource::Struct, false) => error_code!(E0422),
             (PathSource::Expr(..), true) => error_code!(E0423),
             (PathSource::Expr(..), false) => error_code!(E0425),
-            (PathSource::Pat | PathSource::TupleStruct(_), true) => error_code!(E0532),
-            (PathSource::Pat | PathSource::TupleStruct(_), false) => error_code!(E0531),
+            (PathSource::Pat | PathSource::TupleStruct(..), true) => error_code!(E0532),
+            (PathSource::Pat | PathSource::TupleStruct(..), false) => error_code!(E0531),
             (PathSource::TraitItem(..), true) => error_code!(E0575),
             (PathSource::TraitItem(..), false) => error_code!(E0576),
         }
@@ -378,6 +381,9 @@ struct DiagnosticMetadata<'ast> {
 
     /// Only used for better errors on `let <pat>: <expr, not type>;`.
     current_let_binding: Option<(Span, Option<Span>, Option<Span>)>,
+
+    /// Used to detect possible `if let` written without `let` and to provide structured suggestion.
+    in_if_condition: Option<&'ast Expr>,
 }
 
 struct LateResolutionVisitor<'a, 'b, 'ast> {
@@ -403,12 +409,12 @@ struct LateResolutionVisitor<'a, 'b, 'ast> {
     ///
     /// In particular, rustdoc uses this to avoid giving errors for `cfg()` items.
     /// In most cases this will be `None`, in which case errors will always be reported.
-    /// If it is `Some(_)`, then it will be updated when entering a nested function or trait body.
+    /// If it is `true`, then it will be updated when entering a nested function or trait body.
     in_func_body: bool,
 }
 
 /// Walks the whole crate in DFS order, visiting each item, resolving names as it goes.
-impl<'a, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
+impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
     fn visit_item(&mut self, item: &'ast Item) {
         let prev = replace(&mut self.diagnostic_metadata.current_item, Some(item));
         // Always report errors in items we just entered.
@@ -656,7 +662,7 @@ impl<'a, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
     }
 }
 
-impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
+impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
     fn new(resolver: &'b mut Resolver<'a>) -> LateResolutionVisitor<'a, 'b, 'ast> {
         // During late resolution we only track the module component of the parent scope,
         // although it may be useful to track other components as well for diagnostics.
@@ -845,7 +851,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
         self.with_current_self_item(item, |this| {
             this.with_generic_param_rib(generics, ItemRibKind(HasGenericParams::Yes), |this| {
                 let item_def_id = this.r.local_def_id(item.id).to_def_id();
-                this.with_self_rib(Res::SelfTy(None, Some(item_def_id)), |this| {
+                this.with_self_rib(Res::SelfTy(None, Some((item_def_id, false))), |this| {
                     visit::walk_item(this, item);
                 });
             });
@@ -1028,7 +1034,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
             let mut add_bindings_for_ns = |ns| {
                 let parent_rib = self.ribs[ns]
                     .iter()
-                    .rfind(|r| if let ItemRibKind(_) = r.kind { true } else { false })
+                    .rfind(|r| matches!(r.kind, ItemRibKind(_)))
                     .expect("associated item outside of an item");
                 seen_bindings
                     .extend(parent_rib.bindings.iter().map(|(ident, _)| (*ident, ident.span)));
@@ -1212,7 +1218,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                 // Resolve the trait reference, if necessary.
                 this.with_optional_trait_ref(opt_trait_reference.as_ref(), |this, trait_id| {
                     let item_def_id = this.r.local_def_id(item_id).to_def_id();
-                    this.with_self_rib(Res::SelfTy(trait_id, Some(item_def_id)), |this| {
+                    this.with_self_rib(Res::SelfTy(trait_id, Some((item_def_id, false))), |this| {
                         if let Some(trait_ref) = opt_trait_reference.as_ref() {
                             // Resolve type arguments in the trait path.
                             visit::walk_trait_ref(this, trait_ref);
@@ -1536,8 +1542,16 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                         .unwrap_or_else(|| self.fresh_binding(ident, pat.id, pat_src, bindings));
                     self.r.record_partial_res(pat.id, PartialRes::new(res));
                 }
-                PatKind::TupleStruct(ref path, ..) => {
-                    self.smart_resolve_path(pat.id, None, path, PathSource::TupleStruct(pat.span));
+                PatKind::TupleStruct(ref path, ref sub_patterns) => {
+                    self.smart_resolve_path(
+                        pat.id,
+                        None,
+                        path,
+                        PathSource::TupleStruct(
+                            pat.span,
+                            self.r.arenas.alloc_pattern_spans(sub_patterns.iter().map(|p| p.span)),
+                        ),
+                    );
                 }
                 PatKind::Path(ref qself, ref path) => {
                     self.smart_resolve_path(pat.id, qself.as_ref(), path, PathSource::Pat);
@@ -1964,7 +1978,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
 
         if qself.is_none() {
             let path_seg = |seg: &Segment| PathSegment::from_ident(seg.ident);
-            let path = Path { segments: path.iter().map(path_seg).collect(), span };
+            let path = Path { segments: path.iter().map(path_seg).collect(), span, tokens: None };
             if let Ok((_, res)) =
                 self.r.resolve_macro_path(&path, None, &self.parent_scope, false, false)
             {
@@ -2199,7 +2213,9 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
 
             ExprKind::If(ref cond, ref then, ref opt_else) => {
                 self.with_rib(ValueNS, NormalRibKind, |this| {
+                    let old = this.diagnostic_metadata.in_if_condition.replace(cond);
                     this.visit_expr(cond);
+                    this.diagnostic_metadata.in_if_condition = old;
                     this.visit_block(then);
                 });
                 if let Some(expr) = opt_else {
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index d392967af38..ced272e474d 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -83,13 +83,14 @@ fn import_candidate_to_enum_paths(suggestion: &ImportSuggestion) -> (String, Str
     let enum_path = ast::Path {
         span: suggestion.path.span,
         segments: suggestion.path.segments[0..path_len - 1].to_vec(),
+        tokens: None,
     };
     let enum_path_string = path_names_to_string(&enum_path);
 
     (variant_path_string, enum_path_string)
 }
 
-impl<'a> LateResolutionVisitor<'a, '_, '_> {
+impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
     fn def_span(&self, def_id: DefId) -> Option<Span> {
         match def_id.krate {
             LOCAL_CRATE => self.r.opt_span(def_id),
@@ -176,6 +177,19 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> {
         let code = source.error_code(res.is_some());
         let mut err = self.r.session.struct_span_err_with_code(base_span, &base_msg, code);
 
+        match (source, self.diagnostic_metadata.in_if_condition) {
+            (PathSource::Expr(_), Some(Expr { span, kind: ExprKind::Assign(..), .. })) => {
+                err.span_suggestion_verbose(
+                    span.shrink_to_lo(),
+                    "you might have meant to use pattern matching",
+                    "let ".to_string(),
+                    Applicability::MaybeIncorrect,
+                );
+                self.r.session.if_let_suggestions.borrow_mut().insert(*span);
+            }
+            _ => {}
+        }
+
         let is_assoc_fn = self.self_type_is_available(span);
         // Emit help message for fake-self from other languages (e.g., `this` in Javascript).
         if ["this", "my"].contains(&&*item_str.as_str()) && is_assoc_fn {
@@ -609,12 +623,12 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> {
                         );
                     }
                 }
-                PathSource::Expr(_) | PathSource::TupleStruct(_) | PathSource::Pat => {
+                PathSource::Expr(_) | PathSource::TupleStruct(..) | PathSource::Pat => {
                     let span = match &source {
                         PathSource::Expr(Some(Expr {
                             span, kind: ExprKind::Call(_, _), ..
                         }))
-                        | PathSource::TupleStruct(span) => {
+                        | PathSource::TupleStruct(span, _) => {
                             // We want the main underline to cover the suggested code as well for
                             // cleaner output.
                             err.set_span(*span);
@@ -626,7 +640,7 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> {
                         err.span_label(span, &format!("`{}` defined here", path_str));
                     }
                     let (tail, descr, applicability) = match source {
-                        PathSource::Pat | PathSource::TupleStruct(_) => {
+                        PathSource::Pat | PathSource::TupleStruct(..) => {
                             ("", "pattern", Applicability::MachineApplicable)
                         }
                         _ => (": val", "literal", Applicability::HasPlaceholders),
@@ -691,7 +705,7 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> {
             }
             (
                 Res::Def(DefKind::Enum, def_id),
-                PathSource::TupleStruct(_) | PathSource::Expr(..),
+                PathSource::TupleStruct(..) | PathSource::Expr(..),
             ) => {
                 if self
                     .diagnostic_metadata
@@ -731,15 +745,50 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> {
                 }
             }
             (Res::Def(DefKind::Struct, def_id), _) if ns == ValueNS => {
-                if let Some((ctor_def, ctor_vis)) = self.r.struct_constructors.get(&def_id).cloned()
+                if let Some((ctor_def, ctor_vis, fields)) =
+                    self.r.struct_constructors.get(&def_id).cloned()
                 {
                     let accessible_ctor =
                         self.r.is_accessible_from(ctor_vis, self.parent_scope.module);
                     if is_expected(ctor_def) && !accessible_ctor {
-                        err.span_label(
-                            span,
-                            "constructor is not visible here due to private fields".to_string(),
-                        );
+                        let mut better_diag = false;
+                        if let PathSource::TupleStruct(_, pattern_spans) = source {
+                            if pattern_spans.len() > 0 && fields.len() == pattern_spans.len() {
+                                let non_visible_spans: Vec<Span> = fields
+                                    .iter()
+                                    .zip(pattern_spans.iter())
+                                    .filter_map(|(vis, span)| {
+                                        match self
+                                            .r
+                                            .is_accessible_from(*vis, self.parent_scope.module)
+                                        {
+                                            true => None,
+                                            false => Some(*span),
+                                        }
+                                    })
+                                    .collect();
+                                // Extra check to be sure
+                                if non_visible_spans.len() > 0 {
+                                    let mut m: rustc_span::MultiSpan =
+                                        non_visible_spans.clone().into();
+                                    non_visible_spans.into_iter().for_each(|s| {
+                                        m.push_span_label(s, "private field".to_string())
+                                    });
+                                    err.span_note(
+                                        m,
+                                        "constructor is not visible here due to private fields",
+                                    );
+                                    better_diag = true;
+                                }
+                            }
+                        }
+
+                        if !better_diag {
+                            err.span_label(
+                                span,
+                                "constructor is not visible here due to private fields".to_string(),
+                            );
+                        }
                     }
                 } else {
                     bad_struct_syntax_suggestion(def_id);
@@ -1052,7 +1101,8 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> {
                     path_segments.push(ast::PathSegment::from_ident(ident));
                     let module_def_id = module.def_id().unwrap();
                     if module_def_id == def_id {
-                        let path = Path { span: name_binding.span, segments: path_segments };
+                        let path =
+                            Path { span: name_binding.span, segments: path_segments, tokens: None };
                         result = Some((
                             module,
                             ImportSuggestion {
@@ -1082,7 +1132,7 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> {
                 if let Res::Def(DefKind::Variant, _) = name_binding.res() {
                     let mut segms = enum_import_suggestion.path.segments.clone();
                     segms.push(ast::PathSegment::from_ident(ident));
-                    variants.push(Path { span: name_binding.span, segments: segms });
+                    variants.push(Path { span: name_binding.span, segments: segms, tokens: None });
                 }
             });
             variants
@@ -1368,13 +1418,13 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
                         if snippet.starts_with('&') && !snippet.starts_with("&'") {
                             introduce_suggestion
                                 .push((param.span, format!("&{} {}", lt_name, &snippet[1..])));
-                        } else if snippet.starts_with("&'_ ") {
+                        } else if let Some(stripped) = snippet.strip_prefix("&'_ ") {
                             introduce_suggestion
-                                .push((param.span, format!("&{} {}", lt_name, &snippet[4..])));
+                                .push((param.span, format!("&{} {}", lt_name, stripped)));
                         }
                     }
                 }
-                introduce_suggestion.push((*for_span, for_sugg.to_string()));
+                introduce_suggestion.push((*for_span, for_sugg));
                 introduce_suggestion.push((span, formatter(&lt_name)));
                 err.multipart_suggestion(&msg, introduce_suggestion, Applicability::MaybeIncorrect);
             }
@@ -1484,7 +1534,7 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
             }
         };
 
-        let lifetime_names: Vec<_> = lifetime_names.into_iter().collect();
+        let lifetime_names: Vec<_> = lifetime_names.iter().collect();
         match (&lifetime_names[..], snippet.as_deref()) {
             ([name], Some("&")) => {
                 suggest_existing(err, &name.as_str()[..], &|name| format!("&{} ", name));
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 5892edf7652..283db1404d0 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -8,7 +8,7 @@
 //!
 //! Type-relative name resolution (methods, fields, associated items) happens in `librustc_typeck`.
 
-#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
+#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![feature(bool_to_option)]
 #![feature(crate_visibility_modifier)]
 #![feature(nll)]
@@ -221,7 +221,7 @@ enum ResolutionError<'a> {
     /// generic parameters must not be used inside of non trivial constant values.
     ///
     /// This error is only emitted when using `min_const_generics`.
-    ParamInNonTrivialAnonConst(Symbol),
+    ParamInNonTrivialAnonConst { name: Symbol, is_type: bool },
     /// Error E0735: type parameters with a default cannot use `Self`
     SelfInTyParamDefault,
     /// Error E0767: use of unreachable label
@@ -534,11 +534,8 @@ impl<'a> ModuleData<'a> {
                 if ns != TypeNS {
                     return;
                 }
-                match binding.res() {
-                    Res::Def(DefKind::Trait | DefKind::TraitAlias, _) => {
-                        collected_traits.push((name, binding))
-                    }
-                    _ => (),
+                if let Res::Def(DefKind::Trait | DefKind::TraitAlias, _) = binding.res() {
+                    collected_traits.push((name, binding))
                 }
             });
             *traits = Some(collected_traits.into_boxed_slice());
@@ -867,6 +864,12 @@ pub struct ExternPreludeEntry<'a> {
     pub introduced_by_item: bool,
 }
 
+/// Used for better errors for E0773
+enum BuiltinMacroState {
+    NotYetSeen(SyntaxExtension),
+    AlreadySeen(Span),
+}
+
 /// The main resolver class.
 ///
 /// This is the visitor that walks the whole crate.
@@ -960,7 +963,7 @@ pub struct Resolver<'a> {
 
     crate_loader: CrateLoader<'a>,
     macro_names: FxHashSet<Ident>,
-    builtin_macros: FxHashMap<Symbol, SyntaxExtension>,
+    builtin_macros: FxHashMap<Symbol, BuiltinMacroState>,
     registered_attrs: FxHashSet<Ident>,
     registered_tools: FxHashSet<Ident>,
     macro_use_prelude: FxHashMap<Symbol, &'a NameBinding<'a>>,
@@ -999,7 +1002,8 @@ pub struct Resolver<'a> {
 
     /// Table for mapping struct IDs into struct constructor IDs,
     /// it's not used during normal resolution, only for better error reporting.
-    struct_constructors: DefIdMap<(Res, ty::Visibility)>,
+    /// Also includes of list of each fields visibility
+    struct_constructors: DefIdMap<(Res, ty::Visibility, Vec<ty::Visibility>)>,
 
     /// Features enabled for this crate.
     active_features: FxHashSet<Symbol>,
@@ -1036,6 +1040,7 @@ pub struct ResolverArenas<'a> {
     name_resolutions: TypedArena<RefCell<NameResolution<'a>>>,
     macro_rules_bindings: TypedArena<MacroRulesBinding<'a>>,
     ast_paths: TypedArena<ast::Path>,
+    pattern_spans: TypedArena<Span>,
 }
 
 impl<'a> ResolverArenas<'a> {
@@ -1067,6 +1072,9 @@ impl<'a> ResolverArenas<'a> {
     fn alloc_ast_paths(&'a self, paths: &[ast::Path]) -> &'a [ast::Path] {
         self.ast_paths.alloc_from_iter(paths.iter().cloned())
     }
+    fn alloc_pattern_spans(&'a self, spans: impl Iterator<Item = Span>) -> &'a [Span] {
+        self.pattern_spans.alloc_from_iter(spans)
+    }
 }
 
 impl<'a> AsMut<Resolver<'a>> for Resolver<'a> {
@@ -2407,7 +2415,14 @@ impl<'a> Resolver<'a> {
                             (format!("maybe a missing crate `{}`?", ident), None)
                         }
                     } else if i == 0 {
-                        (format!("use of undeclared type or module `{}`", ident), None)
+                        if ident
+                            .name
+                            .with(|n| n.chars().next().map_or(false, |c| c.is_ascii_uppercase()))
+                        {
+                            (format!("use of undeclared type `{}`", ident), None)
+                        } else {
+                            (format!("use of undeclared crate or module `{}`", ident), None)
+                        }
                     } else {
                         let mut msg =
                             format!("could not find `{}` in `{}`", ident, path[i - 1].ident);
@@ -2521,7 +2536,7 @@ impl<'a> Resolver<'a> {
         &mut self,
         rib_index: usize,
         rib_ident: Ident,
-        res: Res,
+        mut res: Res,
         record_used: bool,
         span: Span,
         all_ribs: &[Rib<'a>],
@@ -2611,13 +2626,23 @@ impl<'a> Resolver<'a> {
                         ConstantItemRibKind(trivial) => {
                             // HACK(min_const_generics): We currently only allow `N` or `{ N }`.
                             if !trivial && self.session.features_untracked().min_const_generics {
-                                if record_used {
-                                    self.report_error(
-                                        span,
-                                        ResolutionError::ParamInNonTrivialAnonConst(rib_ident.name),
-                                    );
+                                // HACK(min_const_generics): If we encounter `Self` in an anonymous constant
+                                // we can't easily tell if it's generic at this stage, so we instead remember
+                                // this and then enforce the self type to be concrete later on.
+                                if let Res::SelfTy(trait_def, Some((impl_def, _))) = res {
+                                    res = Res::SelfTy(trait_def, Some((impl_def, true)));
+                                } else {
+                                    if record_used {
+                                        self.report_error(
+                                            span,
+                                            ResolutionError::ParamInNonTrivialAnonConst {
+                                                name: rib_ident.name,
+                                                is_type: true,
+                                            },
+                                        );
+                                    }
+                                    return Res::Err;
                                 }
-                                return Res::Err;
                             }
 
                             if in_ty_param_default {
@@ -2691,7 +2716,10 @@ impl<'a> Resolver<'a> {
                                 if record_used {
                                     self.report_error(
                                         span,
-                                        ResolutionError::ParamInNonTrivialAnonConst(rib_ident.name),
+                                        ResolutionError::ParamInNonTrivialAnonConst {
+                                            name: rib_ident.name,
+                                            is_type: false,
+                                        },
                                     );
                                 }
                                 return Res::Err;
@@ -3176,6 +3204,7 @@ impl<'a> Resolver<'a> {
                     .chain(path_str.split("::").skip(1).map(Ident::from_str))
                     .map(|i| self.new_ast_path_segment(i))
                     .collect(),
+                tokens: None,
             }
         } else {
             ast::Path {
@@ -3185,6 +3214,7 @@ impl<'a> Resolver<'a> {
                     .map(Ident::from_str)
                     .map(|i| self.new_ast_path_segment(i))
                     .collect(),
+                tokens: None,
             }
         };
         let module = self.get_module(module_id);
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index 51518d63ae9..bea71389647 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -3,7 +3,7 @@
 
 use crate::imports::ImportResolver;
 use crate::Namespace::*;
-use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, Determinacy};
+use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, BuiltinMacroState, Determinacy};
 use crate::{CrateLint, ParentScope, ResolutionError, Resolver, Scope, ScopeSet, Weak};
 use crate::{ModuleKind, ModuleOrUniformRoot, NameBinding, PathResult, Segment, ToNameBinding};
 use rustc_ast::{self as ast, NodeId};
@@ -11,6 +11,7 @@ use rustc_ast_lowering::ResolverAstLowering;
 use rustc_ast_pretty::pprust;
 use rustc_attr::StabilityLevel;
 use rustc_data_structures::fx::FxHashSet;
+use rustc_errors::struct_span_err;
 use rustc_expand::base::{Indeterminate, InvocationRes, ResolverExpand, SyntaxExtension};
 use rustc_expand::compile_declarative_macro;
 use rustc_expand::expand::{AstFragment, AstFragmentKind, Invocation, InvocationKind};
@@ -166,7 +167,7 @@ impl<'a> ResolverExpand for Resolver<'a> {
     }
 
     fn register_builtin_macro(&mut self, ident: Ident, ext: SyntaxExtension) {
-        if self.builtin_macros.insert(ident.name, ext).is_some() {
+        if self.builtin_macros.insert(ident.name, BuiltinMacroState::NotYetSeen(ext)).is_some() {
             self.session
                 .span_err(ident.span, &format!("built-in macro `{}` was already defined", ident));
         }
@@ -1076,10 +1077,23 @@ impl<'a> Resolver<'a> {
 
         if result.is_builtin {
             // The macro was marked with `#[rustc_builtin_macro]`.
-            if let Some(ext) = self.builtin_macros.remove(&item.ident.name) {
+            if let Some(builtin_macro) = self.builtin_macros.get_mut(&item.ident.name) {
                 // The macro is a built-in, replace its expander function
                 // while still taking everything else from the source code.
-                result.kind = ext.kind;
+                // If we already loaded this builtin macro, give a better error message than 'no such builtin macro'.
+                match mem::replace(builtin_macro, BuiltinMacroState::AlreadySeen(item.span)) {
+                    BuiltinMacroState::NotYetSeen(ext) => result.kind = ext.kind,
+                    BuiltinMacroState::AlreadySeen(span) => {
+                        struct_span_err!(
+                            self.session,
+                            item.span,
+                            E0773,
+                            "attempted to define built-in macro more than once"
+                        )
+                        .span_note(span, "previously defined here")
+                        .emit();
+                    }
+                }
             } else {
                 let msg = format!("cannot find a built-in macro with name `{}`", item.ident);
                 self.session.span_err(item.span, &msg);
diff --git a/compiler/rustc_save_analysis/src/lib.rs b/compiler/rustc_save_analysis/src/lib.rs
index 629051c1820..f6434689fec 100644
--- a/compiler/rustc_save_analysis/src/lib.rs
+++ b/compiler/rustc_save_analysis/src/lib.rs
@@ -1,4 +1,4 @@
-#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
+#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![feature(nll)]
 #![feature(or_patterns)]
 #![recursion_limit = "256"]
@@ -21,7 +21,7 @@ use rustc_hir_pretty::{enum_def_to_string, fn_to_string, ty_to_string};
 use rustc_middle::hir::map::Map;
 use rustc_middle::middle::cstore::ExternCrate;
 use rustc_middle::middle::privacy::AccessLevels;
-use rustc_middle::ty::{self, DefIdTree, TyCtxt};
+use rustc_middle::ty::{self, print::with_no_trimmed_paths, DefIdTree, TyCtxt};
 use rustc_middle::{bug, span_bug};
 use rustc_session::config::{CrateType, Input, OutputType};
 use rustc_session::output::{filename_for_metadata, out_filename};
@@ -438,7 +438,7 @@ impl<'tcx> SaveContext<'tcx> {
                                     .next()
                                     .map(|item| item.def_id);
                             }
-                            qualname.push_str(">");
+                            qualname.push('>');
 
                             (qualname, trait_id, decl_id, docs, attrs)
                         }
@@ -524,12 +524,12 @@ impl<'tcx> SaveContext<'tcx> {
 
     pub fn get_expr_data(&self, expr: &hir::Expr<'_>) -> Option<Data> {
         let ty = self.typeck_results().expr_ty_adjusted_opt(expr)?;
-        if matches!(ty.kind, ty::Error(_)) {
+        if matches!(ty.kind(), ty::Error(_)) {
             return None;
         }
         match expr.kind {
             hir::ExprKind::Field(ref sub_ex, ident) => {
-                match self.typeck_results().expr_ty_adjusted(&sub_ex).kind {
+                match self.typeck_results().expr_ty_adjusted(&sub_ex).kind() {
                     ty::Adt(def, _) if !def.is_enum() => {
                         let variant = &def.non_enum_variant();
                         filter!(self.span_utils, ident.span);
@@ -551,7 +551,7 @@ impl<'tcx> SaveContext<'tcx> {
                     }
                 }
             }
-            hir::ExprKind::Struct(qpath, ..) => match ty.kind {
+            hir::ExprKind::Struct(qpath, ..) => match ty.kind() {
                 ty::Adt(def, _) => {
                     let sub_span = qpath.last_segment_span();
                     filter!(self.span_utils, sub_span);
@@ -989,32 +989,34 @@ pub fn process_crate<'l, 'tcx, H: SaveHandler>(
     config: Option<Config>,
     mut handler: H,
 ) {
-    tcx.dep_graph.with_ignore(|| {
-        info!("Dumping crate {}", cratename);
-
-        // Privacy checking requires and is done after type checking; use a
-        // fallback in case the access levels couldn't have been correctly computed.
-        let access_levels = match tcx.sess.compile_status() {
-            Ok(..) => tcx.privacy_access_levels(LOCAL_CRATE),
-            Err(..) => tcx.arena.alloc(AccessLevels::default()),
-        };
+    with_no_trimmed_paths(|| {
+        tcx.dep_graph.with_ignore(|| {
+            info!("Dumping crate {}", cratename);
+
+            // Privacy checking requires and is done after type checking; use a
+            // fallback in case the access levels couldn't have been correctly computed.
+            let access_levels = match tcx.sess.compile_status() {
+                Ok(..) => tcx.privacy_access_levels(LOCAL_CRATE),
+                Err(..) => tcx.arena.alloc(AccessLevels::default()),
+            };
 
-        let save_ctxt = SaveContext {
-            tcx,
-            maybe_typeck_results: None,
-            access_levels: &access_levels,
-            span_utils: SpanUtils::new(&tcx.sess),
-            config: find_config(config),
-            impl_counter: Cell::new(0),
-        };
+            let save_ctxt = SaveContext {
+                tcx,
+                maybe_typeck_results: None,
+                access_levels: &access_levels,
+                span_utils: SpanUtils::new(&tcx.sess),
+                config: find_config(config),
+                impl_counter: Cell::new(0),
+            };
 
-        let mut visitor = DumpVisitor::new(save_ctxt);
+            let mut visitor = DumpVisitor::new(save_ctxt);
 
-        visitor.dump_crate_info(cratename, tcx.hir().krate());
-        visitor.dump_compilation_options(input, cratename);
-        visitor.process_crate(tcx.hir().krate());
+            visitor.dump_crate_info(cratename, tcx.hir().krate());
+            visitor.dump_compilation_options(input, cratename);
+            visitor.process_crate(tcx.hir().krate());
 
-        handler.save(&visitor.save_ctxt, &visitor.analysis())
+            handler.save(&visitor.save_ctxt, &visitor.analysis())
+        })
     })
 }
 
diff --git a/compiler/rustc_save_analysis/src/sig.rs b/compiler/rustc_save_analysis/src/sig.rs
index 6dd7f89d594..747e198cd93 100644
--- a/compiler/rustc_save_analysis/src/sig.rs
+++ b/compiler/rustc_save_analysis/src/sig.rs
@@ -497,7 +497,7 @@ impl<'hir> Sig for hir::Item<'hir> {
                     sig.text.push_str(&bounds_to_string(bounds));
                 }
                 // FIXME where clause
-                sig.text.push_str(";");
+                sig.text.push(';');
 
                 Ok(sig)
             }
diff --git a/compiler/rustc_serialize/src/lib.rs b/compiler/rustc_serialize/src/lib.rs
index 265b3b95e95..ed48fbf40ac 100644
--- a/compiler/rustc_serialize/src/lib.rs
+++ b/compiler/rustc_serialize/src/lib.rs
@@ -5,7 +5,7 @@ Core encoding and decoding interfaces.
 */
 
 #![doc(
-    html_root_url = "https://doc.rust-lang.org/nightly/",
+    html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/",
     html_playground_url = "https://play.rust-lang.org/",
     test(attr(allow(unused_variables), deny(warnings)))
 )]
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 1808a0ca59b..ab96b0333f4 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -12,6 +12,7 @@ use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::impl_stable_hash_via_hash;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 
+use rustc_target::abi::{Align, TargetDataLayout};
 use rustc_target::spec::{Target, TargetTriple};
 
 use crate::parse::CrateConfig;
@@ -163,6 +164,21 @@ pub enum LtoCli {
     Unspecified,
 }
 
+/// The different settings that the `-Z dump_mir_spanview` flag can have. `Statement` generates a
+/// document highlighting each span of every statement (including terminators). `Terminator` and
+/// `Block` highlight a single span per `BasicBlock`: the span of the block's `Terminator`, or a
+/// computed span for the block, representing the entire range, covering the block's terminator and
+/// all of its statements.
+#[derive(Clone, Copy, PartialEq, Hash, Debug)]
+pub enum MirSpanview {
+    /// Default `-Z dump_mir_spanview` or `-Z dump_mir_spanview=statement`
+    Statement,
+    /// `-Z dump_mir_spanview=terminator`
+    Terminator,
+    /// `-Z dump_mir_spanview=block`
+    Block,
+}
+
 #[derive(Clone, PartialEq, Hash)]
 pub enum LinkerPluginLto {
     LinkerPlugin(PathBuf),
@@ -313,6 +329,23 @@ impl Default for ErrorOutputType {
     }
 }
 
+/// Parameter to control path trimming.
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+pub enum TrimmedDefPaths {
+    /// `try_print_trimmed_def_path` never prints a trimmed path and never calls the expensive query
+    Never,
+    /// `try_print_trimmed_def_path` calls the expensive query, the query doesn't call `delay_good_path_bug`
+    Always,
+    /// `try_print_trimmed_def_path` calls the expensive query, the query calls `delay_good_path_bug`
+    GoodPath,
+}
+
+impl Default for TrimmedDefPaths {
+    fn default() -> Self {
+        Self::Never
+    }
+}
+
 /// Use tree-based collections to cheaply get a deterministic `Hash` implementation.
 /// *Do not* switch `BTreeMap` out for an unsorted container type! That would break
 /// dependency tracking for command-line arguments.
@@ -549,9 +582,9 @@ impl OutputFilenames {
 
         if !ext.is_empty() {
             if !extension.is_empty() {
-                extension.push_str(".");
+                extension.push('.');
                 extension.push_str(RUST_CGU_EXT);
-                extension.push_str(".");
+                extension.push('.');
             }
 
             extension.push_str(ext);
@@ -606,6 +639,7 @@ impl Default for Options {
             unstable_features: UnstableFeatures::Disallow,
             debug_assertions: true,
             actually_rustdoc: false,
+            trimmed_def_paths: TrimmedDefPaths::default(),
             cli_forced_codegen_units: None,
             cli_forced_thinlto_off: false,
             remap_path_prefix: Vec::new(),
@@ -715,6 +749,9 @@ pub fn default_configuration(sess: &Session) -> CrateConfig {
     let min_atomic_width = sess.target.target.min_atomic_width();
     let max_atomic_width = sess.target.target.max_atomic_width();
     let atomic_cas = sess.target.target.options.atomic_cas;
+    let layout = TargetDataLayout::parse(&sess.target.target).unwrap_or_else(|err| {
+        sess.fatal(&err);
+    });
 
     let mut ret = FxHashSet::default();
     ret.reserve(6); // the minimum number of insertions
@@ -736,18 +773,27 @@ pub fn default_configuration(sess: &Session) -> CrateConfig {
     if sess.target.target.options.has_elf_tls {
         ret.insert((sym::target_thread_local, None));
     }
-    for &i in &[8, 16, 32, 64, 128] {
+    for &(i, align) in &[
+        (8, layout.i8_align.abi),
+        (16, layout.i16_align.abi),
+        (32, layout.i32_align.abi),
+        (64, layout.i64_align.abi),
+        (128, layout.i128_align.abi),
+    ] {
         if i >= min_atomic_width && i <= max_atomic_width {
-            let mut insert_atomic = |s| {
+            let mut insert_atomic = |s, align: Align| {
                 ret.insert((sym::target_has_atomic_load_store, Some(Symbol::intern(s))));
                 if atomic_cas {
                     ret.insert((sym::target_has_atomic, Some(Symbol::intern(s))));
                 }
+                if align.bits() == i {
+                    ret.insert((sym::target_has_atomic_equal_alignment, Some(Symbol::intern(s))));
+                }
             };
             let s = i.to_string();
-            insert_atomic(&s);
+            insert_atomic(&s, align);
             if &s == wordsz {
-                insert_atomic("ptr");
+                insert_atomic("ptr", layout.pointer_align.abi);
             }
         }
     }
@@ -785,10 +831,11 @@ pub fn build_configuration(sess: &Session, mut user_cfg: CrateConfig) -> CrateCo
     user_cfg
 }
 
-pub fn build_target_config(opts: &Options, error_format: ErrorOutputType) -> Config {
-    let target = Target::search(&opts.target_triple).unwrap_or_else(|e| {
+pub fn build_target_config(opts: &Options, target_override: Option<Target>) -> Config {
+    let target_result = target_override.map_or_else(|| Target::search(&opts.target_triple), Ok);
+    let target = target_result.unwrap_or_else(|e| {
         early_error(
-            error_format,
+            opts.error_format,
             &format!(
                 "Error loading target specification: {}. \
             Use `--print target-list` for a list of built-in targets",
@@ -802,7 +849,7 @@ pub fn build_target_config(opts: &Options, error_format: ErrorOutputType) -> Con
         "32" => 32,
         "64" => 64,
         w => early_error(
-            error_format,
+            opts.error_format,
             &format!(
                 "target specification was invalid: \
              unrecognized target-pointer-width {}",
@@ -1709,6 +1756,10 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
         );
     }
 
+    if debugging_opts.experimental_coverage {
+        debugging_opts.instrument_coverage = true;
+    }
+
     if debugging_opts.instrument_coverage {
         if cg.profile_generate.enabled() || cg.profile_use.is_some() {
             early_error(
@@ -1718,20 +1769,15 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
             );
         }
 
-        // `-Z instrument-coverage` implies:
-        //   * `-Z symbol-mangling-version=v0` - to ensure consistent and reversible name mangling.
-        //     Note, LLVM coverage tools can analyze coverage over multiple runs, including some
-        //     changes to source code; so mangled names must be consistent across compilations.
-        //   * `-C link-dead-code` - so unexecuted code is still counted as zero, rather than be
-        //     optimized out. Note that instrumenting dead code can be explicitly disabled with:
-        //         `-Z instrument-coverage -C link-dead-code=no`.
+        // `-Z instrument-coverage` implies `-Z symbol-mangling-version=v0` - to ensure consistent
+        // and reversible name mangling. Note, LLVM coverage tools can analyze coverage over
+        // multiple runs, including some changes to source code; so mangled names must be consistent
+        // across compilations.
         debugging_opts.symbol_mangling_version = SymbolManglingVersion::V0;
-        if cg.link_dead_code == None {
-            // FIXME(richkadel): Investigate if the `instrument-coverage` implementation can
-            // inject ["zero counters"](https://llvm.org/docs/CoverageMappingFormat.html#counter)
-            // in the coverage map when "dead code" is removed, rather than forcing `link-dead-code`.
-            cg.link_dead_code = Some(true);
-        }
+    }
+
+    if let Ok(graphviz_font) = std::env::var("RUSTC_GRAPHVIZ_FONT") {
+        debugging_opts.graphviz_font = graphviz_font;
     }
 
     if !cg.embed_bitcode {
@@ -1805,6 +1851,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
         unstable_features: UnstableFeatures::from_environment(),
         debug_assertions,
         actually_rustdoc: false,
+        trimmed_def_paths: TrimmedDefPaths::default(),
         cli_forced_codegen_units: codegen_units,
         cli_forced_thinlto_off: disable_thinlto,
         remap_path_prefix,
@@ -2051,7 +2098,7 @@ crate mod dep_tracking {
     use super::{
         CFGuard, CrateType, DebugInfo, ErrorOutputType, LinkerPluginLto, LtoCli, OptLevel,
         OutputTypes, Passes, SanitizerSet, SourceFileHashAlgorithm, SwitchWithOptPath,
-        SymbolManglingVersion,
+        SymbolManglingVersion, TrimmedDefPaths,
     };
     use crate::lint;
     use crate::utils::NativeLibKind;
@@ -2132,6 +2179,7 @@ crate mod dep_tracking {
     impl_dep_tracking_hash_via_hash!(SwitchWithOptPath);
     impl_dep_tracking_hash_via_hash!(SymbolManglingVersion);
     impl_dep_tracking_hash_via_hash!(Option<SourceFileHashAlgorithm>);
+    impl_dep_tracking_hash_via_hash!(TrimmedDefPaths);
 
     impl_dep_tracking_hash_for_sortable_vec_of!(String);
     impl_dep_tracking_hash_for_sortable_vec_of!(PathBuf);
diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs
index c2ea141a06f..a808261798d 100644
--- a/compiler/rustc_session/src/lib.rs
+++ b/compiler/rustc_session/src/lib.rs
@@ -1,4 +1,5 @@
 #![feature(crate_visibility_modifier)]
+#![feature(once_cell)]
 #![feature(or_patterns)]
 
 #[macro_use]
diff --git a/compiler/rustc_session/src/lint.rs b/compiler/rustc_session/src/lint.rs
index 0dcbee08abe..62e021d5e45 100644
--- a/compiler/rustc_session/src/lint.rs
+++ b/compiler/rustc_session/src/lint.rs
@@ -65,9 +65,15 @@ pub struct Lint {
     ///
     /// The name is written with underscores, e.g., "unused_imports".
     /// On the command line, underscores become dashes.
+    ///
+    /// See https://rustc-dev-guide.rust-lang.org/diagnostics.html#lint-naming
+    /// for naming guidelines.
     pub name: &'static str,
 
     /// Default level for the lint.
+    ///
+    /// See https://rustc-dev-guide.rust-lang.org/diagnostics.html#diagnostic-levels
+    /// for guidelines on choosing a default level.
     pub default_level: Level,
 
     /// Description of the lint or the issue it detects.
@@ -275,17 +281,60 @@ impl LintBuffer {
 }
 
 /// Declares a static item of type `&'static Lint`.
+///
+/// See https://rustc-dev-guide.rust-lang.org/diagnostics.html for documentation
+/// and guidelines on writing lints.
+///
+/// The macro call should start with a doc comment explaining the lint
+/// which will be embedded in the rustc user documentation book. It should
+/// be written in markdown and have a format that looks like this:
+///
+/// ```rust,ignore (doc-example)
+/// /// The `my_lint_name` lint detects [short explanation here].
+/// ///
+/// /// ### Example
+/// ///
+/// /// ```rust
+/// /// [insert a concise example that triggers the lint]
+/// /// ```
+/// ///
+/// /// {{produces}}
+/// ///
+/// /// ### Explanation
+/// ///
+/// /// This should be a detailed explanation of *why* the lint exists,
+/// /// and also include suggestions on how the user should fix the problem.
+/// /// Try to keep the text simple enough that a beginner can understand,
+/// /// and include links to other documentation for terminology that a
+/// /// beginner may not be familiar with. If this is "allow" by default,
+/// /// it should explain why (are there false positives or other issues?). If
+/// /// this is a future-incompatible lint, it should say so, with text that
+/// /// looks roughly like this:
+/// ///
+/// /// This is a [future-incompatible] lint to transition this to a hard
+/// /// error in the future. See [issue #xxxxx] for more details.
+/// ///
+/// /// [issue #xxxxx]: https://github.com/rust-lang/rust/issues/xxxxx
+/// ```
+///
+/// The `{{produces}}` tag will be automatically replaced with the output from
+/// the example by the build system. You can build and view the rustc book
+/// with `x.py doc --stage=1 src/doc/rustc --open`. If the lint example is too
+/// complex to run as a simple example (for example, it needs an extern
+/// crate), mark it with `ignore` and manually paste the expected output below
+/// the example.
 #[macro_export]
 macro_rules! declare_lint {
-    ($vis: vis $NAME: ident, $Level: ident, $desc: expr) => (
+    ($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr) => (
         $crate::declare_lint!(
-            $vis $NAME, $Level, $desc,
+            $(#[$attr])* $vis $NAME, $Level, $desc,
         );
     );
-    ($vis: vis $NAME: ident, $Level: ident, $desc: expr,
+    ($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr,
      $(@future_incompatible = $fi:expr;)?
      $(@feature_gate = $gate:expr;)?
      $($v:ident),*) => (
+        $(#[$attr])*
         $vis static $NAME: &$crate::lint::Lint = &$crate::lint::Lint {
             name: stringify!($NAME),
             default_level: $crate::lint::$Level,
@@ -298,9 +347,10 @@ macro_rules! declare_lint {
             ..$crate::lint::Lint::default_fields_for_macro()
         };
     );
-    ($vis: vis $NAME: ident, $Level: ident, $desc: expr,
+    ($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr,
      $lint_edition: expr => $edition_level: ident
     ) => (
+        $(#[$attr])*
         $vis static $NAME: &$crate::lint::Lint = &$crate::lint::Lint {
             name: stringify!($NAME),
             default_level: $crate::lint::$Level,
diff --git a/compiler/rustc_session/src/lint/builtin.rs b/compiler/rustc_session/src/lint/builtin.rs
index 2db4d2a7f51..562df176b14 100644
--- a/compiler/rustc_session/src/lint/builtin.rs
+++ b/compiler/rustc_session/src/lint/builtin.rs
@@ -5,11 +5,37 @@
 //! lints are all available in `rustc_lint::builtin`.
 
 use crate::lint::FutureIncompatibleInfo;
-use crate::{declare_lint, declare_lint_pass};
+use crate::{declare_lint, declare_lint_pass, declare_tool_lint};
 use rustc_span::edition::Edition;
 use rustc_span::symbol::sym;
 
 declare_lint! {
+    /// The `ill_formed_attribute_input` lint detects ill-formed attribute
+    /// inputs that were previously accepted and used in practice.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// #[inline = "this is not valid"]
+    /// fn foo() {}
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Previously, inputs for many built-in attributes weren't validated and
+    /// nonsensical attribute inputs were accepted. After validation was
+    /// added, it was determined that some existing projects made use of these
+    /// invalid forms. This is a [future-incompatible] lint to transition this
+    /// to a hard error in the future. See [issue #57571] for more details.
+    ///
+    /// Check the [attribute reference] for details on the valid inputs for
+    /// attributes.
+    ///
+    /// [issue #57571]: https://github.com/rust-lang/rust/issues/57571
+    /// [attribute reference]: https://doc.rust-lang.org/nightly/reference/attributes.html
+    /// [future-incompatible]: ../index.md#future-incompatible-lints
     pub ILL_FORMED_ATTRIBUTE_INPUT,
     Deny,
     "ill-formed attribute inputs that were previously accepted and used in practice",
@@ -21,6 +47,32 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `conflicting_repr_hints` lint detects [`repr` attributes] with
+    /// conflicting hints.
+    ///
+    /// [`repr` attributes]: https://doc.rust-lang.org/reference/type-layout.html#representations
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// #[repr(u32, u64)]
+    /// enum Foo {
+    ///     Variant1,
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// The compiler incorrectly accepted these conflicting representations in
+    /// the past. This is a [future-incompatible] lint to transition this to a
+    /// hard error in the future. See [issue #68585] for more details.
+    ///
+    /// To correct the issue, remove one of the conflicting hints.
+    ///
+    /// [issue #68585]: https://github.com/rust-lang/rust/issues/68585
+    /// [future-incompatible]: ../index.md#future-incompatible-lints
     pub CONFLICTING_REPR_HINTS,
     Deny,
     "conflicts between `#[repr(..)]` hints that were previously accepted and used in practice",
@@ -31,30 +83,198 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `meta_variable_misuse` lint detects possible meta-variable misuse
+    /// in macro definitions.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// #![deny(meta_variable_misuse)]
+    ///
+    /// macro_rules! foo {
+    ///     () => {};
+    ///     ($( $i:ident = $($j:ident),+ );*) => { $( $( $i = $k; )+ )* };
+    /// }
+    ///
+    /// fn main() {
+    ///     foo!();
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// There are quite a few different ways a [`macro_rules`] macro can be
+    /// improperly defined. Many of these errors were previously only detected
+    /// when the macro was expanded or not at all. This lint is an attempt to
+    /// catch some of these problems when the macro is *defined*.
+    ///
+    /// This lint is "allow" by default because it may have false positives
+    /// and other issues. See [issue #61053] for more details.
+    ///
+    /// [`macro_rules`]: https://doc.rust-lang.org/reference/macros-by-example.html
+    /// [issue #61053]: https://github.com/rust-lang/rust/issues/61053
     pub META_VARIABLE_MISUSE,
     Allow,
     "possible meta-variable misuse at macro definition"
 }
 
 declare_lint! {
+    /// The `incomplete_include` lint detects the use of the [`include!`]
+    /// macro with a file that contains more than one expression.
+    ///
+    /// [`include!`]: https://doc.rust-lang.org/std/macro.include.html
+    ///
+    /// ### Example
+    ///
+    /// ```rust,ignore (needs separate file)
+    /// fn main() {
+    ///     include!("foo.txt");
+    /// }
+    /// ```
+    ///
+    /// where the file `foo.txt` contains:
+    ///
+    /// ```text
+    /// println!("hi!");
+    /// ```
+    ///
+    /// produces:
+    ///
+    /// ```text
+    /// error: include macro expected single expression in source
+    ///  --> foo.txt:1:14
+    ///   |
+    /// 1 | println!("1");
+    ///   |              ^
+    ///   |
+    ///   = note: `#[deny(incomplete_include)]` on by default
+    /// ```
+    ///
+    /// ### Explanation
+    ///
+    /// The [`include!`] macro is currently only intended to be used to
+    /// include a single [expression] or multiple [items]. Historically it
+    /// would ignore any contents after the first expression, but that can be
+    /// confusing. In the example above, the `println!` expression ends just
+    /// before the semicolon, making the semicolon "extra" information that is
+    /// ignored. Perhaps even more surprising, if the included file had
+    /// multiple print statements, the subsequent ones would be ignored!
+    ///
+    /// One workaround is to place the contents in braces to create a [block
+    /// expression]. Also consider alternatives, like using functions to
+    /// encapsulate the expressions, or use [proc-macros].
+    ///
+    /// This is a lint instead of a hard error because existing projects were
+    /// found to hit this error. To be cautious, it is a lint for now. The
+    /// future semantics of the `include!` macro are also uncertain, see
+    /// [issue #35560].
+    ///
+    /// [items]: https://doc.rust-lang.org/reference/items.html
+    /// [expression]: https://doc.rust-lang.org/reference/expressions.html
+    /// [block expression]: https://doc.rust-lang.org/reference/expressions/block-expr.html
+    /// [proc-macros]: https://doc.rust-lang.org/reference/procedural-macros.html
+    /// [issue #35560]: https://github.com/rust-lang/rust/issues/35560
     pub INCOMPLETE_INCLUDE,
     Deny,
     "trailing content in included file"
 }
 
 declare_lint! {
+    /// The `arithmetic_overflow` lint detects that an arithmetic operation
+    /// will [overflow].
+    ///
+    /// [overflow]: https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// 1_i32 << 32;
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// It is very likely a mistake to perform an arithmetic operation that
+    /// overflows its value. If the compiler is able to detect these kinds of
+    /// overflows at compile-time, it will trigger this lint. Consider
+    /// adjusting the expression to avoid overflow, or use a data type that
+    /// will not overflow.
     pub ARITHMETIC_OVERFLOW,
     Deny,
     "arithmetic operation overflows"
 }
 
 declare_lint! {
+    /// The `unconditional_panic` lint detects an operation that will cause a
+    /// panic at runtime.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// # #![allow(unused)]
+    /// let x = 1 / 0;
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// This lint detects code that is very likely incorrect. When possible,
+    /// the compiler will attempt to detect situations where code can be
+    /// evaluated at compile-time to generate more efficient code. While
+    /// evaluating such code, if it detects that the code will unconditionally
+    /// panic, this usually indicates that it is doing something incorrectly.
+    /// If this lint is allowed, then the code will not be evaluated at
+    /// compile-time, and instead continue to generate code to evaluate at
+    /// runtime, which may panic during runtime.
     pub UNCONDITIONAL_PANIC,
     Deny,
     "operation will cause a panic at runtime"
 }
 
 declare_lint! {
+    /// The `const_err` lint detects an erroneous expression while doing
+    /// constant evaluation.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// #![allow(unconditional_panic)]
+    /// let x: &'static i32 = &(1 / 0);
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// This lint detects code that is very likely incorrect. If this lint is
+    /// allowed, then the code will not be evaluated at compile-time, and
+    /// instead continue to generate code to evaluate at runtime, which may
+    /// panic during runtime.
+    ///
+    /// Note that this lint may trigger in either inside or outside of a
+    /// [const context]. Outside of a [const context], the compiler can
+    /// sometimes evaluate an expression at compile-time in order to generate
+    /// more efficient code. As the compiler becomes better at doing this, it
+    /// needs to decide what to do when it encounters code that it knows for
+    /// certain will panic or is otherwise incorrect. Making this a hard error
+    /// would prevent existing code that exhibited this behavior from
+    /// compiling, breaking backwards-compatibility. However, this is almost
+    /// certainly incorrect code, so this is a deny-by-default lint. For more
+    /// details, see [RFC 1229] and [issue #28238].
+    ///
+    /// Note that there are several other more specific lints associated with
+    /// compile-time evaluation, such as [`arithmetic_overflow`],
+    /// [`unconditional_panic`].
+    ///
+    /// [const context]: https://doc.rust-lang.org/reference/const_eval.html#const-context
+    /// [RFC 1229]: https://github.com/rust-lang/rfcs/blob/master/text/1229-compile-time-asserts.md
+    /// [issue #28238]: https://github.com/rust-lang/rust/issues/28238
+    /// [`arithmetic_overflow`]: deny-by-default.html#arithmetic-overflow
+    /// [`unconditional_panic`]: deny-by-default.html#unconditional-panic
     pub CONST_ERR,
     Deny,
     "constant evaluation detected erroneous expression",
@@ -62,18 +282,105 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `unused_imports` lint detects imports that are never used.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// use std::collections::HashMap;
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Unused imports may signal a mistake or unfinished code, and clutter
+    /// the code, and should be removed. If you intended to re-export the item
+    /// to make it available outside of the module, add a visibility modifier
+    /// like `pub`.
     pub UNUSED_IMPORTS,
     Warn,
     "imports that are never used"
 }
 
 declare_lint! {
+    /// The `unused_extern_crates` lint guards against `extern crate` items
+    /// that are never used.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// #![deny(unused_extern_crates)]
+    /// extern crate proc_macro;
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// `extern crate` items that are unused have no effect and should be
+    /// removed. Note that there are some cases where specifying an `extern
+    /// crate` is desired for the side effect of ensuring the given crate is
+    /// linked, even though it is not otherwise directly referenced. The lint
+    /// can be silenced by aliasing the crate to an underscore, such as
+    /// `extern crate foo as _`. Also note that it is no longer idiomatic to
+    /// use `extern crate` in the [2018 edition], as extern crates are now
+    /// automatically added in scope.
+    ///
+    /// This lint is "allow" by default because it can be noisy, and produce
+    /// false-positives. If a dependency is being removed from a project, it
+    /// is recommended to remove it from the build configuration (such as
+    /// `Cargo.toml`) to ensure stale build entries aren't left behind.
+    ///
+    /// [2018 edition]: https://doc.rust-lang.org/edition-guide/rust-2018/module-system/path-clarity.html#no-more-extern-crate
     pub UNUSED_EXTERN_CRATES,
     Allow,
     "extern crates that are never used"
 }
 
 declare_lint! {
+    /// The `unused_crate_dependencies` lint detects crate dependencies that
+    /// are never used.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,ignore (needs extern crate)
+    /// #![deny(unused_crate_dependencies)]
+    /// ```
+    ///
+    /// This will produce:
+    ///
+    /// ```text
+    /// error: external crate `regex` unused in `lint_example`: remove the dependency or add `use regex as _;`
+    ///   |
+    /// note: the lint level is defined here
+    ///  --> src/lib.rs:1:9
+    ///   |
+    /// 1 | #![deny(unused_crate_dependencies)]
+    ///   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
+    /// ```
+    ///
+    /// ### Explanation
+    ///
+    /// After removing the code that uses a dependency, this usually also
+    /// requires removing the dependency from the build configuration.
+    /// However, sometimes that step can be missed, which leads to time wasted
+    /// building dependencies that are no longer used. This lint can be
+    /// enabled to detect dependencies that are never used (more specifically,
+    /// any dependency passed with the `--extern` command-line flag that is
+    /// never referenced via [`use`], [`extern crate`], or in any [path]).
+    ///
+    /// This lint is "allow" by default because it can provide false positives
+    /// depending on how the build system is configured. For example, when
+    /// using Cargo, a "package" consists of multiple crates (such as a
+    /// library and a binary), but the dependencies are defined for the
+    /// package as a whole. If there is a dependency that is only used in the
+    /// binary, but not the library, then the lint will be incorrectly issued
+    /// in the library.
+    ///
+    /// [path]: https://doc.rust-lang.org/reference/paths.html
+    /// [`use`]: https://doc.rust-lang.org/reference/items/use-declarations.html
+    /// [`extern crate`]: https://doc.rust-lang.org/reference/items/extern-crates.html
     pub UNUSED_CRATE_DEPENDENCIES,
     Allow,
     "crate dependencies that are never used",
@@ -81,42 +388,174 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `unused_qualifications` lint detects unnecessarily qualified
+    /// names.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// #![deny(unused_qualifications)]
+    /// mod foo {
+    ///     pub fn bar() {}
+    /// }
+    ///
+    /// fn main() {
+    ///     use foo::bar;
+    ///     foo::bar();
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// If an item from another module is already brought into scope, then
+    /// there is no need to qualify it in this case. You can call `bar()`
+    /// directly, without the `foo::`.
+    ///
+    /// This lint is "allow" by default because it is somewhat pedantic, and
+    /// doesn't indicate an actual problem, but rather a stylistic choice, and
+    /// can be noisy when refactoring or moving around code.
     pub UNUSED_QUALIFICATIONS,
     Allow,
     "detects unnecessarily qualified names"
 }
 
 declare_lint! {
+    /// The `unknown_lints` lint detects unrecognized lint attribute.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// #![allow(not_a_real_lint)]
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// It is usually a mistake to specify a lint that does not exist. Check
+    /// the spelling, and check the lint listing for the correct name. Also
+    /// consider if you are using an old version of the compiler, and the lint
+    /// is only available in a newer version.
     pub UNKNOWN_LINTS,
     Warn,
     "unrecognized lint attribute"
 }
 
 declare_lint! {
+    /// The `unused_variables` lint detects variables which are not used in
+    /// any way.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// let x = 5;
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Unused variables may signal a mistake or unfinished code. To silence
+    /// the warning for the individual variable, prefix it with an underscore
+    /// such as `_x`.
     pub UNUSED_VARIABLES,
     Warn,
     "detect variables which are not used in any way"
 }
 
 declare_lint! {
+    /// The `unused_assignments` lint detects assignments that will never be read.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// let mut x = 5;
+    /// x = 6;
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Unused assignments may signal a mistake or unfinished code. If the
+    /// variable is never used after being assigned, then the assignment can
+    /// be removed. Variables with an underscore prefix such as `_x` will not
+    /// trigger this lint.
     pub UNUSED_ASSIGNMENTS,
     Warn,
     "detect assignments that will never be read"
 }
 
 declare_lint! {
+    /// The `dead_code` lint detects unused, unexported items.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// fn foo() {}
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Dead code may signal a mistake or unfinished code. To silence the
+    /// warning for individual items, prefix the name with an underscore such
+    /// as `_foo`. If it was intended to expose the item outside of the crate,
+    /// consider adding a visibility modifier like `pub`. Otherwise consider
+    /// removing the unused code.
     pub DEAD_CODE,
     Warn,
     "detect unused, unexported items"
 }
 
 declare_lint! {
+    /// The `unused_attributes` lint detects attributes that were not used by
+    /// the compiler.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// #![macro_export]
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Unused [attributes] may indicate the attribute is placed in the wrong
+    /// position. Consider removing it, or placing it in the correct position.
+    /// Also consider if you intended to use an _inner attribute_ (with a `!`
+    /// such as `#![allow(unused)]`) which applies to the item the attribute
+    /// is within, or an _outer attribute_ (without a `!` such as
+    /// `#[allow(unsued)]`) which applies to the item *following* the
+    /// attribute.
+    ///
+    /// [attributes]: https://doc.rust-lang.org/reference/attributes.html
     pub UNUSED_ATTRIBUTES,
     Warn,
     "detects attributes that were not used by the compiler"
 }
 
 declare_lint! {
+    /// The `unreachable_code` lint detects unreachable code paths.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,no_run
+    /// panic!("we never go past here!");
+    ///
+    /// let x = 5;
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Unreachable code may signal a mistake or unfinished code. If the code
+    /// is no longer in use, consider removing it.
     pub UNREACHABLE_CODE,
     Warn,
     "detects unreachable code paths",
@@ -124,48 +563,217 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `unreachable_patterns` lint detects unreachable patterns.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// let x = 5;
+    /// match x {
+    ///     y => (),
+    ///     5 => (),
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// This usually indicates a mistake in how the patterns are specified or
+    /// ordered. In this example, the `y` pattern will always match, so the
+    /// five is impossible to reach. Remember, match arms match in order, you
+    /// probably wanted to put the `5` case above the `y` case.
     pub UNREACHABLE_PATTERNS,
     Warn,
     "detects unreachable patterns"
 }
 
 declare_lint! {
+    /// The `overlapping_patterns` lint detects `match` arms that have
+    /// [range patterns] that overlap.
+    ///
+    /// [range patterns]: https://doc.rust-lang.org/nightly/reference/patterns.html#range-patterns
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// let x = 123u8;
+    /// match x {
+    ///     0..=100 => { println!("small"); }
+    ///     100..=255 => { println!("large"); }
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// It is likely a mistake to have range patterns in a match expression
+    /// that overlap. Check that the beginning and end values are what you
+    /// expect, and keep in mind that with `..=` the left and right bounds are
+    /// inclusive.
     pub OVERLAPPING_PATTERNS,
     Warn,
     "detects overlapping patterns"
 }
 
 declare_lint! {
+    /// The `bindings_with_variant_name` lint detects pattern bindings with
+    /// the same name as one of the matched variants.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// pub enum Enum {
+    ///     Foo,
+    ///     Bar,
+    /// }
+    ///
+    /// pub fn foo(x: Enum) {
+    ///     match x {
+    ///         Foo => {}
+    ///         Bar => {}
+    ///     }
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// It is usually a mistake to specify an enum variant name as an
+    /// [identifier pattern]. In the example above, the `match` arms are
+    /// specifying a variable name to bind the value of `x` to. The second arm
+    /// is ignored because the first one matches *all* values. The likely
+    /// intent is that the arm was intended to match on the enum variant.
+    ///
+    /// Two possible solutions are:
+    ///
+    /// * Specify the enum variant using a [path pattern], such as
+    ///   `Enum::Foo`.
+    /// * Bring the enum variants into local scope, such as adding `use
+    ///   Enum::*;` to the beginning of the `foo` function in the example
+    ///   above.
+    ///
+    /// [identifier pattern]: https://doc.rust-lang.org/reference/patterns.html#identifier-patterns
+    /// [path pattern]: https://doc.rust-lang.org/reference/patterns.html#path-patterns
     pub BINDINGS_WITH_VARIANT_NAME,
     Warn,
     "detects pattern bindings with the same name as one of the matched variants"
 }
 
 declare_lint! {
+    /// The `unused_macros` lint detects macros that were not used.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// macro_rules! unused {
+    ///     () => {};
+    /// }
+    ///
+    /// fn main() {
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Unused macros may signal a mistake or unfinished code. To silence the
+    /// warning for the individual macro, prefix the name with an underscore
+    /// such as `_my_macro`. If you intended to export the macro to make it
+    /// available outside of the crate, use the [`macro_export` attribute].
+    ///
+    /// [`macro_export` attribute]: https://doc.rust-lang.org/reference/macros-by-example.html#path-based-scope
     pub UNUSED_MACROS,
     Warn,
     "detects macros that were not used"
 }
 
 declare_lint! {
+    /// The `warnings` lint allows you to change the level of other
+    /// lints which produce warnings.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// #![deny(warnings)]
+    /// fn foo() {}
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// The `warnings` lint is a bit special; by changing its level, you
+    /// change every other warning that would produce a warning to whatever
+    /// value you'd like. As such, you won't ever trigger this lint in your
+    /// code directly.
     pub WARNINGS,
     Warn,
     "mass-change the level for lints which produce warnings"
 }
 
 declare_lint! {
+    /// The `unused_features` lint detects unused or unknown features found in
+    /// crate-level [`feature` attributes].
+    ///
+    /// [`feature` attributes]: https://doc.rust-lang.org/nightly/unstable-book/
+    ///
+    /// Note: This lint is currently not functional, see [issue #44232] for
+    /// more details.
+    ///
+    /// [issue #44232]: https://github.com/rust-lang/rust/issues/44232
     pub UNUSED_FEATURES,
     Warn,
     "unused features found in crate-level `#[feature]` directives"
 }
 
 declare_lint! {
+    /// The `stable_features` lint detects a [`feature` attribute] that
+    /// has since been made stable.
+    ///
+    /// [`feature` attribute]: https://doc.rust-lang.org/nightly/unstable-book/
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// #![feature(test_accepted_feature)]
+    /// fn main() {}
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// When a feature is stabilized, it is no longer necessary to include a
+    /// `#![feature]` attribute for it. To fix, simply remove the
+    /// `#![feature]` attribute.
     pub STABLE_FEATURES,
     Warn,
     "stable features found in `#[feature]` directive"
 }
 
 declare_lint! {
+    /// The `unknown_crate_types` lint detects an unknown crate type found in
+    /// a [`crate_type` attribute].
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// #![crate_type="lol"]
+    /// fn main() {}
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// An unknown value give to the `crate_type` attribute is almost
+    /// certainly a mistake.
+    ///
+    /// [`crate_type` attribute]: https://doc.rust-lang.org/reference/linkage.html
     pub UNKNOWN_CRATE_TYPES,
     Deny,
     "unknown crate type found in `#[crate_type]` directive",
@@ -173,18 +781,104 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `trivial_casts` lint detects trivial casts which could be replaced
+    /// with coercion, which may require [type ascription] or a temporary
+    /// variable.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// #![deny(trivial_casts)]
+    /// let x: &u32 = &42;
+    /// let y = x as *const u32;
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// A trivial cast is a cast `e as T` where `e` has type `U` and `U` is a
+    /// subtype of `T`. This type of cast is usually unnecessary, as it can be
+    /// usually be inferred.
+    ///
+    /// This lint is "allow" by default because there are situations, such as
+    /// with FFI interfaces or complex type aliases, where it triggers
+    /// incorrectly, or in situations where it will be more difficult to
+    /// clearly express the intent. It may be possible that this will become a
+    /// warning in the future, possibly with [type ascription] providing a
+    /// convenient way to work around the current issues. See [RFC 401] for
+    /// historical context.
+    ///
+    /// [type ascription]: https://github.com/rust-lang/rust/issues/23416
+    /// [RFC 401]: https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md
     pub TRIVIAL_CASTS,
     Allow,
     "detects trivial casts which could be removed"
 }
 
 declare_lint! {
+    /// The `trivial_numeric_casts` lint detects trivial numeric casts of types
+    /// which could be removed.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// #![deny(trivial_numeric_casts)]
+    /// let x = 42_i32 as i32;
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// A trivial numeric cast is a cast of a numeric type to the same numeric
+    /// type. This type of cast is usually unnecessary.
+    ///
+    /// This lint is "allow" by default because there are situations, such as
+    /// with FFI interfaces or complex type aliases, where it triggers
+    /// incorrectly, or in situations where it will be more difficult to
+    /// clearly express the intent. It may be possible that this will become a
+    /// warning in the future, possibly with [type ascription] providing a
+    /// convenient way to work around the current issues. See [RFC 401] for
+    /// historical context.
+    ///
+    /// [type ascription]: https://github.com/rust-lang/rust/issues/23416
+    /// [RFC 401]: https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md
     pub TRIVIAL_NUMERIC_CASTS,
     Allow,
     "detects trivial casts of numeric types which could be removed"
 }
 
 declare_lint! {
+    /// The `private_in_public` lint detects private items in public
+    /// interfaces not caught by the old implementation.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// # #![allow(unused)]
+    /// struct SemiPriv;
+    ///
+    /// mod m1 {
+    ///     struct Priv;
+    ///     impl super::SemiPriv {
+    ///         pub fn f(_: Priv) {}
+    ///     }
+    /// }
+    /// # fn main() {}
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// The visibility rules are intended to prevent exposing private items in
+    /// public interfaces. This is a [future-incompatible] lint to transition
+    /// this to a hard error in the future. See [issue #34537] for more
+    /// details.
+    ///
+    /// [issue #34537]: https://github.com/rust-lang/rust/issues/34537
+    /// [future-incompatible]: ../index.md#future-incompatible-lints
     pub PRIVATE_IN_PUBLIC,
     Warn,
     "detect private items in public interfaces not caught by the old implementation",
@@ -195,12 +889,76 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `exported_private_dependencies` lint detects private dependencies
+    /// that are exposed in a public interface.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,ignore (needs-dependency)
+    /// pub fn foo() -> Option<some_private_dependency::Thing> {
+    ///     None
+    /// }
+    /// ```
+    ///
+    /// This will produce:
+    ///
+    /// ```text
+    /// warning: type `bar::Thing` from private dependency 'bar' in public interface
+    ///  --> src/lib.rs:3:1
+    ///   |
+    /// 3 | pub fn foo() -> Option<bar::Thing> {
+    ///   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+    ///   |
+    ///   = note: `#[warn(exported_private_dependencies)]` on by default
+    /// ```
+    ///
+    /// ### Explanation
+    ///
+    /// Dependencies can be marked as "private" to indicate that they are not
+    /// exposed in the public interface of a crate. This can be used by Cargo
+    /// to independently resolve those dependencies because it can assume it
+    /// does not need to unify them with other packages using that same
+    /// dependency. This lint is an indication of a violation of that
+    /// contract.
+    ///
+    /// To fix this, avoid exposing the dependency in your public interface.
+    /// Or, switch the dependency to a public dependency.
+    ///
+    /// Note that support for this is only available on the nightly channel.
+    /// See [RFC 1977] for more details, as well as the [Cargo documentation].
+    ///
+    /// [RFC 1977]: https://github.com/rust-lang/rfcs/blob/master/text/1977-public-private-dependencies.md
+    /// [Cargo documentation]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#public-dependency
     pub EXPORTED_PRIVATE_DEPENDENCIES,
     Warn,
     "public interface leaks type from a private dependency"
 }
 
 declare_lint! {
+    /// The `pub_use_of_private_extern_crate` lint detects a specific
+    /// situation of re-exporting a private `extern crate`.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// extern crate core;
+    /// pub use core as reexported_core;
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// A public `use` declaration should not be used to publicly re-export a
+    /// private `extern crate`. `pub extern crate` should be used instead.
+    ///
+    /// This was historically allowed, but is not the intended behavior
+    /// according to the visibility rules. This is a [future-incompatible]
+    /// lint to transition this to a hard error in the future. See [issue
+    /// #34537] for more details.
+    ///
+    /// [issue #34537]: https://github.com/rust-lang/rust/issues/34537
+    /// [future-incompatible]: ../index.md#future-incompatible-lints
     pub PUB_USE_OF_PRIVATE_EXTERN_CRATE,
     Deny,
     "detect public re-exports of private extern crates",
@@ -211,6 +969,26 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `invalid_type_param_default` lint detects type parameter defaults
+    /// erroneously allowed in an invalid location.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// fn foo<T=i32>(t: T) {}
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Default type parameters were only intended to be allowed in certain
+    /// situations, but historically the compiler allowed them everywhere.
+    /// This is a [future-incompatible] lint to transition this to a hard
+    /// error in the future. See [issue #36887] for more details.
+    ///
+    /// [issue #36887]: https://github.com/rust-lang/rust/issues/36887
+    /// [future-incompatible]: ../index.md#future-incompatible-lints
     pub INVALID_TYPE_PARAM_DEFAULT,
     Deny,
     "type parameter default erroneously allowed in invalid location",
@@ -221,18 +999,174 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `renamed_and_removed_lints` lint detects lints that have been
+    /// renamed or removed.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// #![deny(raw_pointer_derive)]
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// To fix this, either remove the lint or use the new name. This can help
+    /// avoid confusion about lints that are no longer valid, and help
+    /// maintain consistency for renamed lints.
     pub RENAMED_AND_REMOVED_LINTS,
     Warn,
     "lints that have been renamed or removed"
 }
 
 declare_lint! {
+    /// The `unaligned_references` lint detects unaligned references to fields
+    /// of [packed] structs.
+    ///
+    /// [packed]: https://doc.rust-lang.org/reference/type-layout.html#the-alignment-modifiers
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// #![deny(unaligned_references)]
+    ///
+    /// #[repr(packed)]
+    /// pub struct Foo {
+    ///     field1: u64,
+    ///     field2: u8,
+    /// }
+    ///
+    /// fn main() {
+    ///     unsafe {
+    ///         let foo = Foo { field1: 0, field2: 0 };
+    ///         let _ = &foo.field1;
+    ///     }
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Creating a reference to an insufficiently aligned packed field is
+    /// [undefined behavior] and should be disallowed.
+    ///
+    /// This lint is "allow" by default because there is no stable
+    /// alternative, and it is not yet certain how widespread existing code
+    /// will trigger this lint.
+    ///
+    /// See [issue #27060] for more discussion.
+    ///
+    /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
+    /// [issue #27060]: https://github.com/rust-lang/rust/issues/27060
     pub UNALIGNED_REFERENCES,
     Allow,
     "detects unaligned references to fields of packed structs",
 }
 
 declare_lint! {
+    /// The `const_item_mutation` lint detects attempts to mutate a `const`
+    /// item.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// const FOO: [i32; 1] = [0];
+    ///
+    /// fn main() {
+    ///     FOO[0] = 1;
+    ///     // This will print "[0]".
+    ///     println!("{:?}", FOO);
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Trying to directly mutate a `const` item is almost always a mistake.
+    /// What is happening in the example above is that a temporary copy of the
+    /// `const` is mutated, but the original `const` is not. Each time you
+    /// refer to the `const` by name (such as `FOO` in the example above), a
+    /// separate copy of the value is inlined at that location.
+    ///
+    /// This lint checks for writing directly to a field (`FOO.field =
+    /// some_value`) or array entry (`FOO[0] = val`), or taking a mutable
+    /// reference to the const item (`&mut FOO`), including through an
+    /// autoderef (`FOO.some_mut_self_method()`).
+    ///
+    /// There are various alternatives depending on what you are trying to
+    /// accomplish:
+    ///
+    /// * First, always reconsider using mutable globals, as they can be
+    ///   difficult to use correctly, and can make the code more difficult to
+    ///   use or understand.
+    /// * If you are trying to perform a one-time initialization of a global:
+    ///     * If the value can be computed at compile-time, consider using
+    ///       const-compatible values (see [Constant Evaluation]).
+    ///     * For more complex single-initialization cases, consider using a
+    ///       third-party crate, such as [`lazy_static`] or [`once_cell`].
+    ///     * If you are using the [nightly channel], consider the new
+    ///       [`lazy`] module in the standard library.
+    /// * If you truly need a mutable global, consider using a [`static`],
+    ///   which has a variety of options:
+    ///   * Simple data types can be directly defined and mutated with an
+    ///     [`atomic`] type.
+    ///   * More complex types can be placed in a synchronization primitive
+    ///     like a [`Mutex`], which can be initialized with one of the options
+    ///     listed above.
+    ///   * A [mutable `static`] is a low-level primitive, requiring unsafe.
+    ///     Typically This should be avoided in preference of something
+    ///     higher-level like one of the above.
+    ///
+    /// [Constant Evaluation]: https://doc.rust-lang.org/reference/const_eval.html
+    /// [`static`]: https://doc.rust-lang.org/reference/items/static-items.html
+    /// [mutable `static`]: https://doc.rust-lang.org/reference/items/static-items.html#mutable-statics
+    /// [`lazy`]: https://doc.rust-lang.org/nightly/std/lazy/index.html
+    /// [`lazy_static`]: https://crates.io/crates/lazy_static
+    /// [`once_cell`]: https://crates.io/crates/once_cell
+    /// [`atomic`]: https://doc.rust-lang.org/std/sync/atomic/index.html
+    /// [`Mutex`]: https://doc.rust-lang.org/std/sync/struct.Mutex.html
+    pub CONST_ITEM_MUTATION,
+    Warn,
+    "detects attempts to mutate a `const` item",
+}
+
+declare_lint! {
+    /// The `safe_packed_borrows` lint detects borrowing a field in the
+    /// interior of a packed structure with alignment other than 1.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// #[repr(packed)]
+    /// pub struct Unaligned<T>(pub T);
+    ///
+    /// pub struct Foo {
+    ///     start: u8,
+    ///     data: Unaligned<u32>,
+    /// }
+    ///
+    /// fn main() {
+    ///     let x = Foo { start: 0, data: Unaligned(1) };
+    ///     let y = &x.data.0;
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// This type of borrow is unsafe and can cause errors on some platforms
+    /// and violates some assumptions made by the compiler. This was
+    /// previously allowed unintentionally. This is a [future-incompatible]
+    /// lint to transition this to a hard error in the future. See [issue
+    /// #46043] for more details, including guidance on how to solve the
+    /// problem.
+    ///
+    /// [issue #46043]: https://github.com/rust-lang/rust/issues/46043
+    /// [future-incompatible]: ../index.md#future-incompatible-lints
     pub SAFE_PACKED_BORROWS,
     Warn,
     "safe borrows of fields of packed structs were erroneously allowed",
@@ -243,6 +1177,49 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `patterns_in_fns_without_body` lint detects `mut` identifier
+    /// patterns as a parameter in functions without a body.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// trait Trait {
+    ///     fn foo(mut arg: u8);
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// To fix this, remove `mut` from the parameter in the trait definition;
+    /// it can be used in the implementation. That is, the following is OK:
+    ///
+    /// ```rust
+    /// trait Trait {
+    ///     fn foo(arg: u8); // Removed `mut` here
+    /// }
+    ///
+    /// impl Trait for i32 {
+    ///     fn foo(mut arg: u8) { // `mut` here is OK
+    ///
+    ///     }
+    /// }
+    /// ```
+    ///
+    /// Trait definitions can define functions without a body to specify a
+    /// function that implementors must define. The parameter names in the
+    /// body-less functions are only allowed to be `_` or an [identifier] for
+    /// documentation purposes (only the type is relevant). Previous versions
+    /// of the compiler erroneously allowed [identifier patterns] with the
+    /// `mut` keyword, but this was not intended to be allowed. This is a
+    /// [future-incompatible] lint to transition this to a hard error in the
+    /// future. See [issue #35203] for more details.
+    ///
+    /// [identifier]: https://doc.rust-lang.org/reference/identifiers.html
+    /// [identifier patterns]: https://doc.rust-lang.org/reference/patterns.html#identifier-patterns
+    /// [issue #35203]: https://github.com/rust-lang/rust/issues/35203
+    /// [future-incompatible]: ../index.md#future-incompatible-lints
     pub PATTERNS_IN_FNS_WITHOUT_BODY,
     Deny,
     "patterns in functions without body were erroneously allowed",
@@ -253,6 +1230,38 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `late_bound_lifetime_arguments` lint detects generic lifetime
+    /// arguments in path segments with late bound lifetime parameters.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// struct S;
+    ///
+    /// impl S {
+    ///     fn late<'a, 'b>(self, _: &'a u8, _: &'b u8) {}
+    /// }
+    ///
+    /// fn main() {
+    ///     S.late::<'static>(&0, &0);
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// It is not clear how to provide arguments for early-bound lifetime
+    /// parameters if they are intermixed with late-bound parameters in the
+    /// same list. For now, providing any explicit arguments will trigger this
+    /// lint if late-bound parameters are present, so in the future a solution
+    /// can be adopted without hitting backward compatibility issues. This is
+    /// a [future-incompatible] lint to transition this to a hard error in the
+    /// future. See [issue #42868] for more details, along with a description
+    /// of the difference between early and late-bound parameters.
+    ///
+    /// [issue #42868]: https://github.com/rust-lang/rust/issues/42868
+    /// [future-incompatible]: ../index.md#future-incompatible-lints
     pub LATE_BOUND_LIFETIME_ARGUMENTS,
     Warn,
     "detects generic lifetime arguments in path segments with late bound lifetime parameters",
@@ -263,6 +1272,32 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `order_dependent_trait_objects` lint detects a trait coherency
+    /// violation that would allow creating two trait impls for the same
+    /// dynamic trait object involving marker traits.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// pub trait Trait {}
+    ///
+    /// impl Trait for dyn Send + Sync { }
+    /// impl Trait for dyn Sync + Send { }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// A previous bug caused the compiler to interpret traits with different
+    /// orders (such as `Send + Sync` and `Sync + Send`) as distinct types
+    /// when they were intended to be treated the same. This allowed code to
+    /// define separate trait implementations when there should be a coherence
+    /// error. This is a [future-incompatible] lint to transition this to a
+    /// hard error in the future. See [issue #56484] for more details.
+    ///
+    /// [issue #56484]: https://github.com/rust-lang/rust/issues/56484
+    /// [future-incompatible]: ../index.md#future-incompatible-lints
     pub ORDER_DEPENDENT_TRAIT_OBJECTS,
     Deny,
     "trait-object types were treated as different depending on marker-trait order",
@@ -273,6 +1308,36 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `coherence_leak_check` lint detects conflicting implementations of
+    /// a trait that are only distinguished by the old leak-check code.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// trait SomeTrait { }
+    /// impl SomeTrait for for<'a> fn(&'a u8) { }
+    /// impl<'a> SomeTrait for fn(&'a u8) { }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// In the past, the compiler would accept trait implementations for
+    /// identical functions that differed only in where the lifetime binder
+    /// appeared. Due to a change in the borrow checker implementation to fix
+    /// several bugs, this is no longer allowed. However, since this affects
+    /// existing code, this is a [future-incompatible] lint to transition this
+    /// to a hard error in the future.
+    ///
+    /// Code relying on this pattern should introduce "[newtypes]",
+    /// like `struct Foo(for<'a> fn(&'a u8))`.
+    ///
+    /// See [issue #56105] for more details.
+    ///
+    /// [issue #56105]: https://github.com/rust-lang/rust/issues/56105
+    /// [newtypes]: https://doc.rust-lang.org/book/ch19-04-advanced-types.html#using-the-newtype-pattern-for-type-safety-and-abstraction
+    /// [future-incompatible]: ../index.md#future-incompatible-lints
     pub COHERENCE_LEAK_CHECK,
     Warn,
     "distinct impls distinguished only by the leak-check code",
@@ -283,6 +1348,29 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `deprecated` lint detects use of deprecated items.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// #[deprecated]
+    /// fn foo() {}
+    ///
+    /// fn bar() {
+    ///     foo();
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Items may be marked "deprecated" with the [`deprecated` attribute] to
+    /// indicate that they should no longer be used. Usually the attribute
+    /// should include a note on what to use instead, or check the
+    /// documentation.
+    ///
+    /// [`deprecated` attribute]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-deprecated-attribute
     pub DEPRECATED,
     Warn,
     "detects use of deprecated items",
@@ -290,36 +1378,158 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `unused_unsafe` lint detects unnecessary use of an `unsafe` block.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// unsafe {}
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// If nothing within the block requires `unsafe`, then remove the
+    /// `unsafe` marker because it is not required and may cause confusion.
     pub UNUSED_UNSAFE,
     Warn,
     "unnecessary use of an `unsafe` block"
 }
 
 declare_lint! {
+    /// The `unused_mut` lint detects mut variables which don't need to be
+    /// mutable.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// let mut x = 5;
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// The preferred style is to only mark variables as `mut` if it is
+    /// required.
     pub UNUSED_MUT,
     Warn,
     "detect mut variables which don't need to be mutable"
 }
 
 declare_lint! {
+    /// The `unconditional_recursion` lint detects functions that cannot
+    /// return without calling themselves.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// fn foo() {
+    ///     foo();
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// It is usually a mistake to have a recursive call that does not have
+    /// some condition to cause it to terminate. If you really intend to have
+    /// an infinite loop, using a `loop` expression is recommended.
     pub UNCONDITIONAL_RECURSION,
     Warn,
     "functions that cannot return without calling themselves"
 }
 
 declare_lint! {
+    /// The `single_use_lifetimes` lint detects lifetimes that are only used
+    /// once.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// #![deny(single_use_lifetimes)]
+    ///
+    /// fn foo<'a>(x: &'a u32) {}
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Specifying an explicit lifetime like `'a` in a function or `impl`
+    /// should only be used to link together two things. Otherwise, you should
+    /// just use `'_` to indicate that the lifetime is not linked to anything,
+    /// or elide the lifetime altogether if possible.
+    ///
+    /// This lint is "allow" by default because it was introduced at a time
+    /// when `'_` and elided lifetimes were first being introduced, and this
+    /// lint would be too noisy. Also, there are some known false positives
+    /// that it produces. See [RFC 2115] for historical context, and [issue
+    /// #44752] for more details.
+    ///
+    /// [RFC 2115]: https://github.com/rust-lang/rfcs/blob/master/text/2115-argument-lifetimes.md
+    /// [issue #44752]: https://github.com/rust-lang/rust/issues/44752
     pub SINGLE_USE_LIFETIMES,
     Allow,
     "detects lifetime parameters that are only used once"
 }
 
 declare_lint! {
+    /// The `unused_lifetimes` lint detects lifetime parameters that are never
+    /// used.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// #[deny(unused_lifetimes)]
+    ///
+    /// pub fn foo<'a>() {}
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Unused lifetime parameters may signal a mistake or unfinished code.
+    /// Consider removing the parameter.
     pub UNUSED_LIFETIMES,
     Allow,
     "detects lifetime parameters that are never used"
 }
 
 declare_lint! {
+    /// The `tyvar_behind_raw_pointer` lint detects raw pointer to an
+    /// inference variable.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,edition2015
+    /// // edition 2015
+    /// let data = std::ptr::null();
+    /// let _ = &data as *const *const ();
+    ///
+    /// if data.is_null() {}
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// This kind of inference was previously allowed, but with the future
+    /// arrival of [arbitrary self types], this can introduce ambiguity. To
+    /// resolve this, use an explicit type instead of relying on type
+    /// inference.
+    ///
+    /// This is a [future-incompatible] lint to transition this to a hard
+    /// error in the 2018 edition. See [issue #46906] for more details. This
+    /// is currently a hard-error on the 2018 edition, and is "warn" by
+    /// default in the 2015 edition.
+    ///
+    /// [arbitrary self types]: https://github.com/rust-lang/rust/issues/44874
+    /// [issue #46906]: https://github.com/rust-lang/rust/issues/46906
+    /// [future-incompatible]: ../index.md#future-incompatible-lints
     pub TYVAR_BEHIND_RAW_POINTER,
     Warn,
     "raw pointer to an inference variable",
@@ -330,6 +1540,34 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `elided_lifetimes_in_paths` lint detects the use of hidden
+    /// lifetime parameters.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// #![deny(elided_lifetimes_in_paths)]
+    /// struct Foo<'a> {
+    ///     x: &'a u32
+    /// }
+    ///
+    /// fn foo(x: &Foo) {
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Elided lifetime parameters can make it difficult to see at a glance
+    /// that borrowing is occurring. This lint ensures that lifetime
+    /// parameters are always explicitly stated, even if it is the `'_`
+    /// [placeholder lifetime].
+    ///
+    /// This lint is "allow" by default because it has some known issues, and
+    /// may require a significant transition for old code.
+    ///
+    /// [placeholder lifetime]: https://doc.rust-lang.org/reference/lifetime-elision.html#lifetime-elision-in-functions
     pub ELIDED_LIFETIMES_IN_PATHS,
     Allow,
     "hidden lifetime parameters in types are deprecated",
@@ -337,12 +1575,78 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `bare_trait_objects` lint suggests using `dyn Trait` for trait
+    /// objects.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// trait Trait { }
+    ///
+    /// fn takes_trait_object(_: Box<Trait>) {
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Without the `dyn` indicator, it can be ambiguous or confusing when
+    /// reading code as to whether or not you are looking at a trait object.
+    /// The `dyn` keyword makes it explicit, and adds a symmetry to contrast
+    /// with [`impl Trait`].
+    ///
+    /// [`impl Trait`]: https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
     pub BARE_TRAIT_OBJECTS,
     Warn,
     "suggest using `dyn Trait` for trait objects"
 }
 
 declare_lint! {
+    /// The `absolute_paths_not_starting_with_crate` lint detects fully
+    /// qualified paths that start with a module name instead of `crate`,
+    /// `self`, or an extern crate name
+    ///
+    /// ### Example
+    ///
+    /// ```rust,edition2015,compile_fail
+    /// #![deny(absolute_paths_not_starting_with_crate)]
+    ///
+    /// mod foo {
+    ///     pub fn bar() {}
+    /// }
+    ///
+    /// fn main() {
+    ///     ::foo::bar();
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Rust [editions] allow the language to evolve without breaking
+    /// backwards compatibility. This lint catches code that uses absolute
+    /// paths in the style of the 2015 edition. In the 2015 edition, absolute
+    /// paths (those starting with `::`) refer to either the crate root or an
+    /// external crate. In the 2018 edition it was changed so that they only
+    /// refer to external crates. The path prefix `crate::` should be used
+    /// instead to reference items from the crate root.
+    ///
+    /// If you switch the compiler from the 2015 to 2018 edition without
+    /// updating the code, then it will fail to compile if the old style paths
+    /// are used. You can manually change the paths to use the `crate::`
+    /// prefix to transition to the 2018 edition.
+    ///
+    /// This lint solves the problem automatically. It is "allow" by default
+    /// because the code is perfectly valid in the 2015 edition. The [`cargo
+    /// fix`] tool with the `--edition` flag will switch this lint to "warn"
+    /// and automatically apply the suggested fix from the compiler. This
+    /// provides a completely automated way to update old code to the 2018
+    /// edition.
+    ///
+    /// [editions]: https://doc.rust-lang.org/edition-guide/
+    /// [`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html
     pub ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE,
     Allow,
     "fully qualified paths that start with a module name \
@@ -354,6 +1658,45 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `illegal_floating_point_literal_pattern` lint detects
+    /// floating-point literals used in patterns.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// let x = 42.0;
+    ///
+    /// match x {
+    ///     5.0 => {}
+    ///     _ => {}
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Previous versions of the compiler accepted floating-point literals in
+    /// patterns, but it was later determined this was a mistake. The
+    /// semantics of comparing floating-point values may not be clear in a
+    /// pattern when contrasted with "structural equality". Typically you can
+    /// work around this by using a [match guard], such as:
+    ///
+    /// ```rust
+    /// # let x = 42.0;
+    ///
+    /// match x {
+    ///     y if y == 5.0 => {}
+    ///     _ => {}
+    /// }
+    /// ```
+    ///
+    /// This is a [future-incompatible] lint to transition this to a hard
+    /// error in the future. See [issue #41620] for more details.
+    ///
+    /// [issue #41620]: https://github.com/rust-lang/rust/issues/41620
+    /// [match guard]: https://doc.rust-lang.org/reference/expressions/match-expr.html#match-guards
+    /// [future-incompatible]: ../index.md#future-incompatible-lints
     pub ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
     Warn,
     "floating-point literals cannot be used in patterns",
@@ -364,6 +1707,40 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `unstable_name_collisions` lint detects that you have used a name
+    /// that the standard library plans to add in the future.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// trait MyIterator : Iterator {
+    ///     // is_sorted is an unstable method that already exists on the Iterator trait
+    ///     fn is_sorted(self) -> bool where Self: Sized {true}
+    /// }
+    ///
+    /// impl<T: ?Sized> MyIterator for T where T: Iterator { }
+    ///
+    /// let x = vec![1,2,3];
+    /// let _ = x.iter().is_sorted();
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// When new methods are added to traits in the standard library, they are
+    /// usually added in an "unstable" form which is only available on the
+    /// [nightly channel] with a [`feature` attribute]. If there is any
+    /// pre-existing code which extends a trait to have a method with the same
+    /// name, then the names will collide. In the future, when the method is
+    /// stabilized, this will cause an error due to the ambiguity. This lint
+    /// is an early-warning to let you know that there may be a collision in
+    /// the future. This can be avoided by adding type annotations to
+    /// disambiguate which trait method you intend to call, such as
+    /// `MyIterator::is_sorted(my_iter)` or renaming or removing the method.
+    ///
+    /// [nightly channel]: https://doc.rust-lang.org/book/appendix-07-nightly-rust.html
+    /// [`feature` attribute]: https://doc.rust-lang.org/nightly/unstable-book/
     pub UNSTABLE_NAME_COLLISIONS,
     Warn,
     "detects name collision with an existing but unstable method",
@@ -376,48 +1753,155 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `irrefutable_let_patterns` lint detects detects [irrefutable
+    /// patterns] in [if-let] and [while-let] statements.
+    ///
+    ///
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// if let _ = 123 {
+    ///     println!("always runs!");
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// There usually isn't a reason to have an irrefutable pattern in an
+    /// if-let or while-let statement, because the pattern will always match
+    /// successfully. A [`let`] or [`loop`] statement will suffice. However,
+    /// when generating code with a macro, forbidding irrefutable patterns
+    /// would require awkward workarounds in situations where the macro
+    /// doesn't know if the pattern is refutable or not. This lint allows
+    /// macros to accept this form, while alerting for a possibly incorrect
+    /// use in normal code.
+    ///
+    /// See [RFC 2086] for more details.
+    ///
+    /// [irrefutable patterns]: https://doc.rust-lang.org/reference/patterns.html#refutability
+    /// [if-let]: https://doc.rust-lang.org/reference/expressions/if-expr.html#if-let-expressions
+    /// [while-let]: https://doc.rust-lang.org/reference/expressions/loop-expr.html#predicate-pattern-loops
+    /// [`let`]: https://doc.rust-lang.org/reference/statements.html#let-statements
+    /// [`loop`]: https://doc.rust-lang.org/reference/expressions/loop-expr.html#infinite-loops
+    /// [RFC 2086]: https://github.com/rust-lang/rfcs/blob/master/text/2086-allow-if-let-irrefutables.md
     pub IRREFUTABLE_LET_PATTERNS,
     Warn,
     "detects irrefutable patterns in if-let and while-let statements"
 }
 
 declare_lint! {
+    /// The `unused_labels` lint detects [labels] that are never used.
+    ///
+    /// [labels]: https://doc.rust-lang.org/reference/expressions/loop-expr.html#loop-labels
+    ///
+    /// ### Example
+    ///
+    /// ```rust,no_run
+    /// 'unused_label: loop {}
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Unused labels may signal a mistake or unfinished code. To silence the
+    /// warning for the individual label, prefix it with an underscore such as
+    /// `'_my_label:`.
     pub UNUSED_LABELS,
     Warn,
     "detects labels that are never used"
 }
 
 declare_lint! {
+    /// The `broken_intra_doc_links` lint detects failures in resolving
+    /// intra-doc link targets. This is a `rustdoc` only lint, see the
+    /// documentation in the [rustdoc book].
+    ///
+    /// [rustdoc book]: ../../../rustdoc/lints.html#broken_intra_doc_links
     pub BROKEN_INTRA_DOC_LINKS,
     Warn,
     "failures in resolving intra-doc link targets"
 }
 
 declare_lint! {
+    /// The `invalid_codeblock_attributes` lint detects code block attributes
+    /// in documentation examples that have potentially mis-typed values. This
+    /// is a `rustdoc` only lint, see the documentation in the [rustdoc book].
+    ///
+    /// [rustdoc book]: ../../../rustdoc/lints.html#invalid_codeblock_attributes
     pub INVALID_CODEBLOCK_ATTRIBUTES,
     Warn,
     "codeblock attribute looks a lot like a known one"
 }
 
 declare_lint! {
+    /// The `missing_crate_level_docs` lint detects if documentation is
+    /// missing at the crate root. This is a `rustdoc` only lint, see the
+    /// documentation in the [rustdoc book].
+    ///
+    /// [rustdoc book]: ../../../rustdoc/lints.html#missing_crate_level_docs
     pub MISSING_CRATE_LEVEL_DOCS,
     Allow,
     "detects crates with no crate-level documentation"
 }
 
 declare_lint! {
+    /// The `missing_doc_code_examples` lint detects publicly-exported items
+    /// without code samples in their documentation. This is a `rustdoc` only
+    /// lint, see the documentation in the [rustdoc book].
+    ///
+    /// [rustdoc book]: ../../../rustdoc/lints.html#missing_doc_code_examples
     pub MISSING_DOC_CODE_EXAMPLES,
     Allow,
     "detects publicly-exported items without code samples in their documentation"
 }
 
 declare_lint! {
+    /// The `private_doc_tests` lint detects code samples in docs of private
+    /// items not documented by `rustdoc`. This is a `rustdoc` only lint, see
+    /// the documentation in the [rustdoc book].
+    ///
+    /// [rustdoc book]: ../../../rustdoc/lints.html#private_doc_tests
     pub PRIVATE_DOC_TESTS,
     Allow,
     "detects code samples in docs of private items not documented by rustdoc"
 }
 
 declare_lint! {
+    /// The `where_clauses_object_safety` lint detects for [object safety] of
+    /// [where clauses].
+    ///
+    /// [object safety]: https://doc.rust-lang.org/reference/items/traits.html#object-safety
+    /// [where clauses]: https://doc.rust-lang.org/reference/items/generics.html#where-clauses
+    ///
+    /// ### Example
+    ///
+    /// ```rust,no_run
+    /// trait Trait {}
+    ///
+    /// trait X { fn foo(&self) where Self: Trait; }
+    ///
+    /// impl X for () { fn foo(&self) {} }
+    ///
+    /// impl Trait for dyn X {}
+    ///
+    /// // Segfault at opt-level 0, SIGILL otherwise.
+    /// pub fn main() { <dyn X as X>::foo(&()); }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// The compiler previously allowed these object-unsafe bounds, which was
+    /// incorrect. This is a [future-incompatible] lint to transition this to
+    /// a hard error in the future. See [issue #51443] for more details.
+    ///
+    /// [issue #51443]: https://github.com/rust-lang/rust/issues/51443
+    /// [future-incompatible]: ../index.md#future-incompatible-lints
     pub WHERE_CLAUSES_OBJECT_SAFETY,
     Warn,
     "checks the object safety of where clauses",
@@ -428,6 +1912,63 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `proc_macro_derive_resolution_fallback` lint detects proc macro
+    /// derives using inaccessible names from parent modules.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,ignore (proc-macro)
+    /// // foo.rs
+    /// #![crate_type = "proc-macro"]
+    ///
+    /// extern crate proc_macro;
+    ///
+    /// use proc_macro::*;
+    ///
+    /// #[proc_macro_derive(Foo)]
+    /// pub fn foo1(a: TokenStream) -> TokenStream {
+    ///     drop(a);
+    ///     "mod __bar { static mut BAR: Option<Something> = None; }".parse().unwrap()
+    /// }
+    /// ```
+    ///
+    /// ```rust,ignore (needs-dependency)
+    /// // bar.rs
+    /// #[macro_use]
+    /// extern crate foo;
+    ///
+    /// struct Something;
+    ///
+    /// #[derive(Foo)]
+    /// struct Another;
+    ///
+    /// fn main() {}
+    /// ```
+    ///
+    /// This will produce:
+    ///
+    /// ```text
+    /// warning: cannot find type `Something` in this scope
+    ///  --> src/main.rs:8:10
+    ///   |
+    /// 8 | #[derive(Foo)]
+    ///   |          ^^^ names from parent modules are not accessible without an explicit import
+    ///   |
+    ///   = note: `#[warn(proc_macro_derive_resolution_fallback)]` on by default
+    ///   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+    ///   = note: for more information, see issue #50504 <https://github.com/rust-lang/rust/issues/50504>
+    /// ```
+    ///
+    /// ### Explanation
+    ///
+    /// If a proc-macro generates a module, the compiler unintentionally
+    /// allowed items in that module to refer to items in the crate root
+    /// without importing them. This is a [future-incompatible] lint to
+    /// transition this to a hard error in the future. See [issue #50504] for
+    /// more details.
+    ///
+    /// [issue #50504]: https://github.com/rust-lang/rust/issues/50504
+    /// [future-incompatible]: ../index.md#future-incompatible-lints
     pub PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
     Warn,
     "detects proc macro derives using inaccessible names from parent modules",
@@ -438,6 +1979,53 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `macro_use_extern_crate` lint detects the use of the
+    /// [`macro_use` attribute].
+    ///
+    /// ### Example
+    ///
+    /// ```rust,ignore (needs extern crate)
+    /// #![deny(macro_use_extern_crate)]
+    ///
+    /// #[macro_use]
+    /// extern crate serde_json;
+    ///
+    /// fn main() {
+    ///     let _ = json!{{}};
+    /// }
+    /// ```
+    ///
+    /// This will produce:
+    ///
+    /// ```text
+    /// error: deprecated `#[macro_use]` attribute used to import macros should be replaced at use sites with a `use` item to import the macro instead
+    ///  --> src/main.rs:3:1
+    ///   |
+    /// 3 | #[macro_use]
+    ///   | ^^^^^^^^^^^^
+    ///   |
+    /// note: the lint level is defined here
+    ///  --> src/main.rs:1:9
+    ///   |
+    /// 1 | #![deny(macro_use_extern_crate)]
+    ///   |         ^^^^^^^^^^^^^^^^^^^^^^
+    /// ```
+    ///
+    /// ### Explanation
+    ///
+    /// The [`macro_use` attribute] on an [`extern crate`] item causes
+    /// macros in that external crate to be brought into the prelude of the
+    /// crate, making the macros in scope everywhere. As part of the efforts
+    /// to simplify handling of dependencies in the [2018 edition], the use of
+    /// `extern crate` is being phased out. To bring macros from extern crates
+    /// into scope, it is recommended to use a [`use` import].
+    ///
+    /// This lint is "allow" by default because this is a stylistic choice
+    /// that has not been settled, see [issue #52043] for more information.
+    ///
+    /// [`macro_use` attribute]: https://doc.rust-lang.org/reference/macros-by-example.html#the-macro_use-attribute
+    /// [`use` import]: https://doc.rust-lang.org/reference/items/use-declarations.html
+    /// [issue #52043]: https://github.com/rust-lang/rust/issues/52043
     pub MACRO_USE_EXTERN_CRATE,
     Allow,
     "the `#[macro_use]` attribute is now deprecated in favor of using macros \
@@ -445,6 +2033,44 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `macro_expanded_macro_exports_accessed_by_absolute_paths` lint
+    /// detects macro-expanded [`macro_export`] macros from the current crate
+    /// that cannot be referred to by absolute paths.
+    ///
+    /// [`macro_export`]: https://doc.rust-lang.org/reference/macros-by-example.html#path-based-scope
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// macro_rules! define_exported {
+    ///     () => {
+    ///         #[macro_export]
+    ///         macro_rules! exported {
+    ///             () => {};
+    ///         }
+    ///     };
+    /// }
+    ///
+    /// define_exported!();
+    ///
+    /// fn main() {
+    ///     crate::exported!();
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// The intent is that all macros marked with the `#[macro_export]`
+    /// attribute are made available in the root of the crate. However, when a
+    /// `macro_rules!` definition is generated by another macro, the macro
+    /// expansion is unable to uphold this rule. This is a
+    /// [future-incompatible] lint to transition this to a hard error in the
+    /// future. See [issue #53495] for more details.
+    ///
+    /// [issue #53495]: https://github.com/rust-lang/rust/issues/53495
+    /// [future-incompatible]: ../index.md#future-incompatible-lints
     pub MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS,
     Deny,
     "macro-expanded `macro_export` macros from the current crate \
@@ -457,12 +2083,92 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `explicit_outlives_requirements` lint detects unnecessary
+    /// lifetime bounds that can be inferred.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// # #![allow(unused)]
+    /// #![deny(explicit_outlives_requirements)]
+    ///
+    /// struct SharedRef<'a, T>
+    /// where
+    ///     T: 'a,
+    /// {
+    ///     data: &'a T,
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// If a `struct` contains a reference, such as `&'a T`, the compiler
+    /// requires that `T` outlives the lifetime `'a`. This historically
+    /// required writing an explicit lifetime bound to indicate this
+    /// requirement. However, this can be overly explicit, causing clutter and
+    /// unnecessary complexity. The language was changed to automatically
+    /// infer the bound if it is not specified. Specifically, if the struct
+    /// contains a reference, directly or indirectly, to `T` with lifetime
+    /// `'x`, then it will infer that `T: 'x` is a requirement.
+    ///
+    /// This lint is "allow" by default because it can be noisy for existing
+    /// code that already had these requirements. This is a stylistic choice,
+    /// as it is still valid to explicitly state the bound. It also has some
+    /// false positives that can cause confusion.
+    ///
+    /// See [RFC 2093] for more details.
+    ///
+    /// [RFC 2093]: https://github.com/rust-lang/rfcs/blob/master/text/2093-infer-outlives.md
     pub EXPLICIT_OUTLIVES_REQUIREMENTS,
     Allow,
     "outlives requirements can be inferred"
 }
 
 declare_lint! {
+    /// The `indirect_structural_match` lint detects a `const` in a pattern
+    /// that manually implements [`PartialEq`] and [`Eq`].
+    ///
+    /// [`PartialEq`]: https://doc.rust-lang.org/std/cmp/trait.PartialEq.html
+    /// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// #![deny(indirect_structural_match)]
+    ///
+    /// struct Plus(i32, i32);
+    /// const ONE_PLUS_TWO: &&Plus = &&Plus(1, 2);
+    ///
+    /// impl PartialEq for Plus {
+    ///     fn eq(&self, other: &Self) -> bool {
+    ///         self.0 + self.1 == other.0 + other.1
+    ///     }
+    /// }
+    ///
+    /// impl Eq for Plus {}
+    ///
+    /// fn main() {
+    ///     if let ONE_PLUS_TWO = &&Plus(3, 0) {
+    ///         println!("semantic!");
+    ///     } else {
+    ///         println!("structural!");
+    ///     }
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// The compiler unintentionally accepted this form in the past. This is a
+    /// [future-incompatible] lint to transition this to a hard error in the
+    /// future. See [issue #62411] for a complete description of the problem,
+    /// and some possible solutions.
+    ///
+    /// [issue #62411]: https://github.com/rust-lang/rust/issues/62411
+    /// [future-incompatible]: ../index.md#future-incompatible-lints
     pub INDIRECT_STRUCTURAL_MATCH,
     // defaulting to allow until rust-lang/rust#62614 is fixed.
     Allow,
@@ -474,6 +2180,17 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `deprecated_in_future` lint is internal to rustc and should not be
+    /// used by user code.
+    ///
+    /// This lint is only enabled in the standard library. It works with the
+    /// use of `#[rustc_deprecated]` with a `since` field of a version in the
+    /// future. This allows something to be marked as deprecated in a future
+    /// version, and then this lint will ensure that the item is no longer
+    /// used in the standard library. See the [stability documentation] for
+    /// more details.
+    ///
+    /// [stability documentation]: https://rustc-dev-guide.rust-lang.org/stability.html#rustc_deprecated
     pub DEPRECATED_IN_FUTURE,
     Allow,
     "detects use of items that will be deprecated in a future version",
@@ -481,6 +2198,54 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `ambiguous_associated_items` lint detects ambiguity between
+    /// [associated items] and [enum variants].
+    ///
+    /// [associated items]: https://doc.rust-lang.org/reference/items/associated-items.html
+    /// [enum variants]: https://doc.rust-lang.org/reference/items/enumerations.html
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// enum E {
+    ///     V
+    /// }
+    ///
+    /// trait Tr {
+    ///     type V;
+    ///     fn foo() -> Self::V;
+    /// }
+    ///
+    /// impl Tr for E {
+    ///     type V = u8;
+    ///     // `Self::V` is ambiguous because it may refer to the associated type or
+    ///     // the enum variant.
+    ///     fn foo() -> Self::V { 0 }
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Previous versions of Rust did not allow accessing enum variants
+    /// through [type aliases]. When this ability was added (see [RFC 2338]), this
+    /// introduced some situations where it can be ambiguous what a type
+    /// was referring to.
+    ///
+    /// To fix this ambiguity, you should use a [qualified path] to explicitly
+    /// state which type to use. For example, in the above example the
+    /// function can be written as `fn f() -> <Self as Tr>::V { 0 }` to
+    /// specifically refer to the associated type.
+    ///
+    /// This is a [future-incompatible] lint to transition this to a hard
+    /// error in the future. See [issue #57644] for more details.
+    ///
+    /// [issue #57644]: https://github.com/rust-lang/rust/issues/57644
+    /// [type aliases]: https://doc.rust-lang.org/reference/items/type-aliases.html#type-aliases
+    /// [RFC 2338]: https://github.com/rust-lang/rfcs/blob/master/text/2338-type-alias-enum-variants.md
+    /// [qualified path]: https://doc.rust-lang.org/reference/paths.html#qualified-paths
+    /// [future-incompatible]: ../index.md#future-incompatible-lints
     pub AMBIGUOUS_ASSOCIATED_ITEMS,
     Deny,
     "ambiguous associated items",
@@ -491,6 +2256,27 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `mutable_borrow_reservation_conflict` lint detects the reservation
+    /// of a two-phased borrow that conflicts with other shared borrows.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// let mut v = vec![0, 1, 2];
+    /// let shared = &v;
+    /// v.push(shared.len());
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// This is a [future-incompatible] lint to transition this to a hard error
+    /// in the future. See [issue #59159] for a complete description of the
+    /// problem, and some possible solutions.
+    ///
+    /// [issue #59159]: https://github.com/rust-lang/rust/issues/59159
+    /// [future-incompatible]: ../index.md#future-incompatible-lints
     pub MUTABLE_BORROW_RESERVATION_CONFLICT,
     Warn,
     "reservation of a two-phased borrow conflicts with other shared borrows",
@@ -501,6 +2287,38 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `soft_unstable` lint detects unstable features that were
+    /// unintentionally allowed on stable.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// #[cfg(test)]
+    /// extern crate test;
+    ///
+    /// #[bench]
+    /// fn name(b: &mut test::Bencher) {
+    ///     b.iter(|| 123)
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// The [`bench` attribute] was accidentally allowed to be specified on
+    /// the [stable release channel]. Turning this to a hard error would have
+    /// broken some projects. This lint allows those projects to continue to
+    /// build correctly when [`--cap-lints`] is used, but otherwise signal an
+    /// error that `#[bench]` should not be used on the stable channel. This
+    /// is a [future-incompatible] lint to transition this to a hard error in
+    /// the future. See [issue #64266] for more details.
+    ///
+    /// [issue #64266]: https://github.com/rust-lang/rust/issues/64266
+    /// [`bench` attribute]: https://doc.rust-lang.org/nightly/unstable-book/library-features/test.html
+    /// [stable release channel]: https://doc.rust-lang.org/book/appendix-07-nightly-rust.html
+    /// [`--cap-lints`]: https://doc.rust-lang.org/rustc/lints/levels.html#capping-lints
+    /// [future-incompatible]: ../index.md#future-incompatible-lints
     pub SOFT_UNSTABLE,
     Deny,
     "a feature gate that doesn't break dependent crates",
@@ -511,18 +2329,135 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `inline_no_sanitize` lint detects incompatible use of
+    /// [`#[inline(always)]`][inline] and [`#[no_sanitize(...)]`][no_sanitize].
+    ///
+    /// [inline]: https://doc.rust-lang.org/reference/attributes/codegen.html#the-inline-attribute
+    /// [no_sanitize]: https://doc.rust-lang.org/nightly/unstable-book/language-features/no-sanitize.html
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// #![feature(no_sanitize)]
+    ///
+    /// #[inline(always)]
+    /// #[no_sanitize(address)]
+    /// fn x() {}
+    ///
+    /// fn main() {
+    ///     x()
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// The use of the [`#[inline(always)]`][inline] attribute prevents the
+    /// the [`#[no_sanitize(...)]`][no_sanitize] attribute from working.
+    /// Consider temporarily removing `inline` attribute.
     pub INLINE_NO_SANITIZE,
     Warn,
     "detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`",
 }
 
 declare_lint! {
+    /// The `asm_sub_register` lint detects using only a subset of a register
+    /// for inline asm inputs.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,ignore (fails on system llvm)
+    /// #![feature(asm)]
+    ///
+    /// fn main() {
+    ///     #[cfg(target_arch="x86_64")]
+    ///     unsafe {
+    ///         asm!("mov {0}, {0}", in(reg) 0i16);
+    ///     }
+    /// }
+    /// ```
+    ///
+    /// This will produce:
+    ///
+    /// ```text
+    /// warning: formatting may not be suitable for sub-register argument
+    ///  --> src/main.rs:6:19
+    ///   |
+    /// 6 |         asm!("mov {0}, {0}", in(reg) 0i16);
+    ///   |                   ^^^  ^^^           ---- for this argument
+    ///   |
+    ///   = note: `#[warn(asm_sub_register)]` on by default
+    ///   = help: use the `x` modifier to have the register formatted as `ax`
+    ///   = help: or use the `r` modifier to keep the default formatting of `rax`
+    /// ```
+    ///
+    /// ### Explanation
+    ///
+    /// Registers on some architectures can use different names to refer to a
+    /// subset of the register. By default, the compiler will use the name for
+    /// the full register size. To explicitly use a subset of the register,
+    /// you can override the default by using a modifier on the template
+    /// string operand to specify when subregister to use. This lint is issued
+    /// if you pass in a value with a smaller data type than the default
+    /// register size, to alert you of possibly using the incorrect width. To
+    /// fix this, add the suggested modifier to the template, or cast the
+    /// value to the correct size.
+    ///
+    /// See [register template modifiers] for more details.
+    ///
+    /// [register template modifiers]: https://doc.rust-lang.org/nightly/unstable-book/library-features/asm.html#register-template-modifiers
     pub ASM_SUB_REGISTER,
     Warn,
     "using only a subset of a register for inline asm inputs",
 }
 
 declare_lint! {
+    /// The `unsafe_op_in_unsafe_fn` lint detects unsafe operations in unsafe
+    /// functions without an explicit unsafe block. This lint only works on
+    /// the [**nightly channel**] with the
+    /// `#![feature(unsafe_block_in_unsafe_fn)]` feature.
+    ///
+    /// [**nightly channel**]: https://doc.rust-lang.org/book/appendix-07-nightly-rust.html
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// #![feature(unsafe_block_in_unsafe_fn)]
+    /// #![deny(unsafe_op_in_unsafe_fn)]
+    ///
+    /// unsafe fn foo() {}
+    ///
+    /// unsafe fn bar() {
+    ///     foo();
+    /// }
+    ///
+    /// fn main() {}
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Currently, an [`unsafe fn`] allows any [unsafe] operation within its
+    /// body. However, this can increase the surface area of code that needs
+    /// to be scrutinized for proper behavior. The [`unsafe` block] provides a
+    /// convenient way to make it clear exactly which parts of the code are
+    /// performing unsafe operations. In the future, it is desired to change
+    /// it so that unsafe operations cannot be performed in an `unsafe fn`
+    /// without an `unsafe` block.
+    ///
+    /// The fix to this is to wrap the unsafe code in an `unsafe` block.
+    ///
+    /// This lint is "allow" by default because it has not yet been
+    /// stabilized, and is not yet complete. See [RFC #2585] and [issue
+    /// #71668] for more details
+    ///
+    /// [`unsafe fn`]: https://doc.rust-lang.org/reference/unsafe-functions.html
+    /// [`unsafe` block]: https://doc.rust-lang.org/reference/expressions/block-expr.html#unsafe-blocks
+    /// [unsafe]: https://doc.rust-lang.org/reference/unsafety.html
+    /// [RFC #2585]: https://github.com/rust-lang/rfcs/blob/master/text/2585-unsafe-block-in-unsafe-fn.md
+    /// [issue #71668]: https://github.com/rust-lang/rust/issues/71668
     pub UNSAFE_OP_IN_UNSAFE_FN,
     Allow,
     "unsafe operations in unsafe functions without an explicit unsafe block are deprecated",
@@ -530,6 +2465,48 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `cenum_impl_drop_cast` lint detects an `as` cast of a field-less
+    /// `enum` that implements [`Drop`].
+    ///
+    /// [`Drop`]: https://doc.rust-lang.org/std/ops/trait.Drop.html
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// # #![allow(unused)]
+    /// enum E {
+    ///     A,
+    /// }
+    ///
+    /// impl Drop for E {
+    ///     fn drop(&mut self) {
+    ///         println!("Drop");
+    ///     }
+    /// }
+    ///
+    /// fn main() {
+    ///     let e = E::A;
+    ///     let i = e as u32;
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Casting a field-less `enum` that does not implement [`Copy`] to an
+    /// integer moves the value without calling `drop`. This can result in
+    /// surprising behavior if it was expected that `drop` should be called.
+    /// Calling `drop` automatically would be inconsistent with other move
+    /// operations. Since neither behavior is clear or consistent, it was
+    /// decided that a cast of this nature will no longer be allowed.
+    ///
+    /// This is a [future-incompatible] lint to transition this to a hard error
+    /// in the future. See [issue #73333] for more details.
+    ///
+    /// [future-incompatible]: ../index.md#future-incompatible-lints
+    /// [issue #73333]: https://github.com/rust-lang/rust/issues/73333
+    /// [`Copy`]: https://doc.rust-lang.org/std/marker/trait.Copy.html
     pub CENUM_IMPL_DROP_CAST,
     Warn,
     "a C-like enum implementing Drop is cast",
@@ -539,6 +2516,52 @@ declare_lint! {
     };
 }
 
+declare_lint! {
+    /// The `const_evaluatable_unchecked` lint detects a generic constant used
+    /// in a type.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// const fn foo<T>() -> usize {
+    ///     if std::mem::size_of::<*mut T>() < 8 { // size of *mut T does not depend on T
+    ///         4
+    ///     } else {
+    ///         8
+    ///     }
+    /// }
+    ///
+    /// fn test<T>() {
+    ///     let _ = [0; foo::<T>()];
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// In the 1.43 release, some uses of generic parameters in array repeat
+    /// expressions were accidentally allowed. This is a [future-incompatible]
+    /// lint to transition this to a hard error in the future. See [issue
+    /// #76200] for a more detailed description and possible fixes.
+    ///
+    /// [future-incompatible]: ../index.md#future-incompatible-lints
+    /// [issue #76200]: https://github.com/rust-lang/rust/issues/76200
+    pub CONST_EVALUATABLE_UNCHECKED,
+    Warn,
+    "detects a generic constant is used in a type without a emitting a warning",
+    @future_incompatible = FutureIncompatibleInfo {
+        reference: "issue #76200 <https://github.com/rust-lang/rust/issues/76200>",
+        edition: None,
+    };
+}
+
+declare_tool_lint! {
+    pub rustc::INEFFECTIVE_UNSTABLE_TRAIT_IMPL,
+    Deny,
+    "detects `#[unstable]` on stable trait implementations for stable types"
+}
+
 declare_lint_pass! {
     /// Does nothing as a lint pass, but registers some `Lint`s
     /// that are used by other parts of the compiler.
@@ -572,6 +2595,7 @@ declare_lint_pass! {
         CONST_ERR,
         RENAMED_AND_REMOVED_LINTS,
         UNALIGNED_REFERENCES,
+        CONST_ITEM_MUTATION,
         SAFE_PACKED_BORROWS,
         PATTERNS_IN_FNS_WITHOUT_BODY,
         LATE_BOUND_LIFETIME_ARGUMENTS,
@@ -612,10 +2636,29 @@ declare_lint_pass! {
         UNSAFE_OP_IN_UNSAFE_FN,
         INCOMPLETE_INCLUDE,
         CENUM_IMPL_DROP_CAST,
+        CONST_EVALUATABLE_UNCHECKED,
+        INEFFECTIVE_UNSTABLE_TRAIT_IMPL,
     ]
 }
 
 declare_lint! {
+    /// The `unused_doc_comments` lint detects doc comments that aren't used
+    /// by `rustdoc`.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// /// docs for x
+    /// let x = 12;
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// `rustdoc` does not use doc comments in all positions, and so the doc
+    /// comment will be ignored. Try changing it to a normal comment with `//`
+    /// to avoid the warning.
     pub UNUSED_DOC_COMMENTS,
     Warn,
     "detects doc comments that aren't used by rustdoc"
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index d05f1a3f34b..8cc55f4ebe8 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -125,6 +125,9 @@ top_level_options!(
         // try to not rely on this too much.
         actually_rustdoc: bool [TRACKED],
 
+        // Control path trimming.
+        trimmed_def_paths: TrimmedDefPaths [TRACKED],
+
         // Specifications of codegen units / ThinLTO which are forced as a
         // result of parsing command line options. These are not necessarily
         // what rustc was invoked with, but massaged a bit to agree with
@@ -255,6 +258,7 @@ macro_rules! options {
         pub const parse_strip: &str = "either `none`, `debuginfo`, or `symbols`";
         pub const parse_linker_flavor: &str = ::rustc_target::spec::LinkerFlavor::one_of();
         pub const parse_optimization_fuel: &str = "crate=integer";
+        pub const parse_mir_spanview: &str = "`statement` (default), `terminator`, or `block`";
         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_lto: &str =
@@ -551,6 +555,36 @@ macro_rules! options {
             }
         }
 
+        fn parse_mir_spanview(slot: &mut Option<MirSpanview>, v: Option<&str>) -> bool {
+            if v.is_some() {
+                let mut bool_arg = None;
+                if parse_opt_bool(&mut bool_arg, v) {
+                    *slot = if bool_arg.unwrap() {
+                        Some(MirSpanview::Statement)
+                    } else {
+                        None
+                    };
+                    return true
+                }
+            }
+
+            let v = match v {
+                None => {
+                    *slot = Some(MirSpanview::Statement);
+                    return true;
+                }
+                Some(v) => v,
+            };
+
+            *slot = Some(match v.trim_end_matches("s") {
+                "statement" | "stmt" => MirSpanview::Statement,
+                "terminator" | "term" => MirSpanview::Terminator,
+                "block" | "basicblock" => MirSpanview::Block,
+                _ => return false,
+            });
+            true
+        }
+
         fn parse_treat_err_as_bug(slot: &mut Option<usize>, v: Option<&str>) -> bool {
             match v {
                 Some(s) => { *slot = s.parse().ok().filter(|&x| x != 0); slot.unwrap_or(0) != 0 }
@@ -719,6 +753,9 @@ options! {CodegenOptions, CodegenSetter, basic_codegen_options,
         "extra arguments to append to the linker invocation (space separated)"),
     link_dead_code: Option<bool> = (None, parse_opt_bool, [UNTRACKED],
         "keep dead code at link time (useful for code coverage) (default: no)"),
+    link_self_contained: Option<bool> = (None, parse_opt_bool, [UNTRACKED],
+        "control whether to link Rust provided C objects/libraries or rely
+        on C toolchain installed in the system"),
     linker: Option<PathBuf> = (None, parse_opt_pathbuf, [UNTRACKED],
         "system linker to link outputs with"),
     linker_flavor: Option<LinkerFlavor> = (None, parse_linker_flavor, [UNTRACKED],
@@ -813,6 +850,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
         "enable the experimental Chalk-based trait solving engine"),
     codegen_backend: Option<String> = (None, parse_opt_string, [TRACKED],
         "the backend to use"),
+    combine_cgu: bool = (false, parse_bool, [TRACKED],
+        "combine CGUs into a single one"),
     crate_attr: Vec<String> = (Vec::new(), parse_string_push, [TRACKED],
         "inject the given attribute in the crate"),
     debug_macros: bool = (false, parse_bool, [TRACKED],
@@ -849,8 +888,18 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
         "exclude the pass number when dumping MIR (used in tests) (default: no)"),
     dump_mir_graphviz: bool = (false, parse_bool, [UNTRACKED],
         "in addition to `.mir` files, create graphviz `.dot` files (default: no)"),
+    dump_mir_spanview: Option<MirSpanview> = (None, parse_mir_spanview, [UNTRACKED],
+        "in addition to `.mir` files, create `.html` files to view spans for \
+        all `statement`s (including terminators), only `terminator` spans, or \
+        computed `block` spans (one span encompassing a block's terminator and \
+        all statements)."),
     emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED],
         "emit a section containing stack size metadata (default: no)"),
+    experimental_coverage: bool = (false, parse_bool, [TRACKED],
+        "enable and extend the `-Z instrument-coverage` function-level coverage \
+        feature, adding additional experimental (likely inaccurate) counters and \
+        code regions (used by `rustc` compiler developers to test new coverage \
+        counter placements) (default: no)"),
     fewer_names: bool = (false, parse_bool, [TRACKED],
         "reduce memory use by retaining fewer names within compilation artifacts (LLVM-IR) \
         (default: no)"),
@@ -860,6 +909,11 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
         "force all crates to be `rustc_private` unstable (default: no)"),
     fuel: Option<(String, u64)> = (None, parse_optimization_fuel, [TRACKED],
         "set the optimization fuel quota for a crate"),
+    graphviz_dark_mode: bool = (false, parse_bool, [UNTRACKED],
+        "use dark-themed colors in graphviz output (default: no)"),
+    graphviz_font: String = ("Courier, monospace".to_string(), parse_string, [UNTRACKED],
+        "use the given `fontname` in graphviz output; can be overridden by setting \
+        environment variable `RUSTC_GRAPHVIZ_FONT` (default: `Courier, monospace`)"),
     hir_stats: bool = (false, parse_bool, [UNTRACKED],
         "print some statistics about AST and HIR (default: no)"),
     human_readable_cgu_names: bool = (false, parse_bool, [TRACKED],
@@ -885,18 +939,15 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
         "instrument the generated code to support LLVM source-based code coverage \
         reports (note, the compiler build config must include `profiler = true`, \
         and is mutually exclusive with `-C profile-generate`/`-C profile-use`); \
-        implies `-C link-dead-code` (unless explicitly disabled)` and \
-        `-Z symbol-mangling-version=v0`; and disables/overrides some optimization \
-        options (default: no)"),
+        implies `-C link-dead-code` (unless targeting MSVC, or explicitly disabled) \
+        and `-Z symbol-mangling-version=v0`; disables/overrides some Rust \
+        optimizations (default: no)"),
     instrument_mcount: bool = (false, parse_bool, [TRACKED],
         "insert function instrument code for mcount-based tracing (default: no)"),
     keep_hygiene_data: bool = (false, parse_bool, [UNTRACKED],
         "keep hygiene data after analysis (default: no)"),
     link_native_libraries: bool = (true, parse_bool, [UNTRACKED],
         "link native libraries in the linker invocation (default: yes)"),
-    link_self_contained: Option<bool> = (None, parse_opt_bool, [TRACKED],
-        "control whether to link Rust provided C objects/libraries or rely
-         on C toolchain installed in the system"),
     link_only: bool = (false, parse_bool, [TRACKED],
         "link the `.rlink` file generated by `-Z no-link` (default: no)"),
     llvm_time_trace: bool = (false, parse_bool, [UNTRACKED],
@@ -967,6 +1018,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
         "print the result of the monomorphization collection pass"),
     print_type_sizes: bool = (false, parse_bool, [UNTRACKED],
         "print layout information for each type encountered (default: no)"),
+    proc_macro_backtrace: bool = (false, parse_bool, [UNTRACKED],
+         "show backtraces for panics during proc-macro execution (default: no)"),
     profile: bool = (false, parse_bool, [TRACKED],
         "insert profiling code (default: no)"),
     profile_emit: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED],
@@ -1046,6 +1099,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
         "for every macro invocation, print its name and arguments (default: no)"),
     treat_err_as_bug: Option<usize> = (None, parse_treat_err_as_bug, [TRACKED],
         "treat error number `val` that occurs as bug"),
+    trim_diagnostic_paths: bool = (true, parse_bool, [UNTRACKED],
+        "in diagnostics, use heuristics to shorten paths referring to items"),
     ui_testing: bool = (false, parse_bool, [UNTRACKED],
         "emit compiler diagnostics in a form suitable for UI testing (default: no)"),
     unleash_the_miri_inside_of_you: bool = (false, parse_bool, [TRACKED],
diff --git a/compiler/rustc_session/src/search_paths.rs b/compiler/rustc_session/src/search_paths.rs
index e12364b7dac..83b737a73b1 100644
--- a/compiler/rustc_session/src/search_paths.rs
+++ b/compiler/rustc_session/src/search_paths.rs
@@ -56,16 +56,16 @@ impl PathKind {
 
 impl SearchPath {
     pub fn from_cli_opt(path: &str, output: config::ErrorOutputType) -> Self {
-        let (kind, path) = if path.starts_with("native=") {
-            (PathKind::Native, &path["native=".len()..])
-        } else if path.starts_with("crate=") {
-            (PathKind::Crate, &path["crate=".len()..])
-        } else if path.starts_with("dependency=") {
-            (PathKind::Dependency, &path["dependency=".len()..])
-        } else if path.starts_with("framework=") {
-            (PathKind::Framework, &path["framework=".len()..])
-        } else if path.starts_with("all=") {
-            (PathKind::All, &path["all=".len()..])
+        let (kind, path) = if let Some(stripped) = path.strip_prefix("native=") {
+            (PathKind::Native, stripped)
+        } else if let Some(stripped) = path.strip_prefix("crate=") {
+            (PathKind::Crate, stripped)
+        } else if let Some(stripped) = path.strip_prefix("dependency=") {
+            (PathKind::Dependency, stripped)
+        } else if let Some(stripped) = path.strip_prefix("framework=") {
+            (PathKind::Framework, stripped)
+        } else if let Some(stripped) = path.strip_prefix("all=") {
+            (PathKind::All, stripped)
         } else {
             (PathKind::All, path)
         };
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index c006e593e47..ff67d3cb107 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -213,6 +213,9 @@ pub struct Session {
 
     known_attrs: Lock<MarkedAttrs>,
     used_attrs: Lock<MarkedAttrs>,
+
+    /// `Span`s for `if` conditions that we have suggested turning into `if let`.
+    pub if_let_suggestions: Lock<FxHashSet<Span>>,
 }
 
 pub struct PerfStats {
@@ -234,6 +237,14 @@ enum DiagnosticBuilderMethod {
                             // Add more variants as needed to support one-time diagnostics.
 }
 
+/// Trait implemented by error types. This should not be implemented manually. Instead, use
+/// `#[derive(SessionDiagnostic)]` -- see [rustc_macros::SessionDiagnostic].
+pub trait SessionDiagnostic<'a> {
+    /// Write out as a diagnostic out of `sess`.
+    #[must_use]
+    fn into_diagnostic(self, sess: &'a Session) -> DiagnosticBuilder<'a>;
+}
+
 /// Diagnostic message ID, used by `Session.one_time_diagnostics` to avoid
 /// emitting the same message more than once.
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
@@ -389,6 +400,9 @@ impl Session {
     pub fn err(&self, msg: &str) {
         self.diagnostic().err(msg)
     }
+    pub fn emit_err<'a>(&'a self, err: impl SessionDiagnostic<'a>) {
+        err.into_diagnostic(self).emit()
+    }
     pub fn err_count(&self) -> usize {
         self.diagnostic().err_count()
     }
@@ -439,6 +453,24 @@ impl Session {
     pub fn delay_span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
         self.diagnostic().delay_span_bug(sp, msg)
     }
+
+    /// Used for code paths of expensive computations that should only take place when
+    /// warnings or errors are emitted. If no messages are emitted ("good path"), then
+    /// it's likely a bug.
+    pub fn delay_good_path_bug(&self, msg: &str) {
+        if self.opts.debugging_opts.print_type_sizes
+            || self.opts.debugging_opts.query_dep_graph
+            || self.opts.debugging_opts.dump_mir.is_some()
+            || self.opts.debugging_opts.unpretty.is_some()
+            || self.opts.output_types.contains_key(&OutputType::Mir)
+            || std::env::var_os("RUSTC_LOG").is_some()
+        {
+            return;
+        }
+
+        self.diagnostic().delay_good_path_bug(msg)
+    }
+
     pub fn note_without_error(&self, msg: &str) {
         self.diagnostic().note_without_error(msg)
     }
@@ -1021,6 +1053,40 @@ impl Session {
         || self.opts.debugging_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY)
     }
 
+    pub fn link_dead_code(&self) -> bool {
+        match self.opts.cg.link_dead_code {
+            Some(explicitly_set) => explicitly_set,
+            None => {
+                self.opts.debugging_opts.instrument_coverage
+                    && !self.target.target.options.is_like_msvc
+                // Issue #76038: (rustc `-Clink-dead-code` causes MSVC linker to produce invalid
+                // binaries when LLVM InstrProf counters are enabled). As described by this issue,
+                // the "link dead code" option produces incorrect binaries when compiled and linked
+                // under MSVC. The resulting Rust programs typically crash with a segmentation
+                // fault, or produce an empty "*.profraw" file (profiling counter results normally
+                // generated during program exit).
+                //
+                // If not targeting MSVC, `-Z instrument-coverage` implies `-C link-dead-code`, so
+                // unexecuted code is still counted as zero, rather than be optimized out. Note that
+                // instrumenting dead code can be explicitly disabled with:
+                //
+                //     `-Z instrument-coverage -C link-dead-code=no`.
+                //
+                // FIXME(richkadel): Investigate if `instrument-coverage` implementation can inject
+                // [zero counters](https://llvm.org/docs/CoverageMappingFormat.html#counter) in the
+                // coverage map when "dead code" is removed, rather than forcing `link-dead-code`.
+                // This may not be possible, however, if (as it seems to appear) the "dead code"
+                // that would otherwise not be linked is only identified as "dead" by the native
+                // linker. If that's the case, I believe it is too late for the Rust compiler to
+                // leverage any information it might be able to get from the linker regarding what
+                // code is dead, to be able to add those counters.
+                //
+                // On the other hand, if any Rust compiler passes are optimizing out dead code blocks
+                // we should inject "zero" counters for those code regions.
+            }
+        }
+    }
+
     pub fn mark_attr_known(&self, attr: &Attribute) {
         self.known_attrs.lock().mark(attr)
     }
@@ -1168,6 +1234,7 @@ pub fn build_session(
     diagnostics_output: DiagnosticOutput,
     driver_lint_caps: FxHashMap<lint::LintId, lint::Level>,
     file_loader: Option<Box<dyn FileLoader + Send + Sync + 'static>>,
+    target_override: Option<Target>,
 ) -> Session {
     // FIXME: This is not general enough to make the warning lint completely override
     // normal diagnostic warnings, since the warning lint can also be denied and changed
@@ -1187,7 +1254,7 @@ pub fn build_session(
         DiagnosticOutput::Raw(write) => Some(write),
     };
 
-    let target_cfg = config::build_target_config(&sopts, sopts.error_format);
+    let target_cfg = config::build_target_config(&sopts, target_override);
     let host_triple = TargetTriple::from_triple(config::host_triple());
     let host = Target::search(&host_triple).unwrap_or_else(|e| {
         early_error(sopts.error_format, &format!("Error loading host specification: {}", e))
@@ -1354,6 +1421,7 @@ pub fn build_session(
         target_features: FxHashSet::default(),
         known_attrs: Lock::new(MarkedAttrs::new()),
         used_attrs: Lock::new(MarkedAttrs::new()),
+        if_let_suggestions: Default::default(),
     };
 
     validate_commandline_args_with_session_available(&sess);
@@ -1428,20 +1496,6 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
         );
     }
 
-    // FIXME(richkadel): See `src/test/run-make-fulldeps/instrument-coverage/Makefile`. After
-    // compiling with `-Zinstrument-coverage`, the resulting binary generates a segfault during
-    // the program's exit process (likely while attempting to generate the coverage stats in
-    // the "*.profraw" file). An investigation to resolve the problem on Windows is ongoing,
-    // but until this is resolved, the option is disabled on Windows, and the test is skipped
-    // when targeting `MSVC`.
-    if sess.opts.debugging_opts.instrument_coverage && sess.target.target.options.is_like_msvc {
-        sess.warn(
-            "Rust source-based code coverage instrumentation (with `-Z instrument-coverage`) \
-            is not yet supported on Windows when targeting MSVC. The resulting binaries will \
-            still be instrumented for experimentation purposes, but may not execute correctly.",
-        );
-    }
-
     const ASAN_SUPPORTED_TARGETS: &[&str] = &[
         "aarch64-fuchsia",
         "aarch64-unknown-linux-gnu",
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index c654dade2ab..96a6956a40c 100644
--- a/compiler/rustc_span/src/lib.rs
+++ b/compiler/rustc_span/src/lib.rs
@@ -4,16 +4,15 @@
 //!
 //! This API is completely unstable and subject to change.
 
-#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
+#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
+#![feature(array_windows)]
 #![feature(crate_visibility_modifier)]
 #![feature(const_fn)]
 #![feature(const_panic)]
 #![feature(negative_impls)]
 #![feature(nll)]
-#![feature(optin_builtin_traits)]
 #![feature(min_specialization)]
 #![feature(option_expect_none)]
-#![feature(refcell_take)]
 
 #[macro_use]
 extern crate rustc_macros;
@@ -400,6 +399,13 @@ impl Span {
         span.with_lo(span.hi)
     }
 
+    #[inline]
+    /// Returns true if hi == lo
+    pub fn is_empty(&self) -> bool {
+        let span = self.data();
+        span.hi == span.lo
+    }
+
     /// Returns `self` if `self` is not the dummy span, and `other` otherwise.
     pub fn substitute_dummy(self, other: Span) -> Span {
         if self.is_dummy() { other } else { self }
@@ -537,6 +543,12 @@ impl Span {
     }
 
     /// Returns a `Span` that would enclose both `self` and `end`.
+    ///
+    /// ```text
+    ///     ____             ___
+    ///     self lorem ipsum end
+    ///     ^^^^^^^^^^^^^^^^^^^^
+    /// ```
     pub fn to(self, end: Span) -> Span {
         let span_data = self.data();
         let end_data = end.data();
@@ -560,6 +572,12 @@ impl Span {
     }
 
     /// Returns a `Span` between the end of `self` to the beginning of `end`.
+    ///
+    /// ```text
+    ///     ____             ___
+    ///     self lorem ipsum end
+    ///         ^^^^^^^^^^^^^
+    /// ```
     pub fn between(self, end: Span) -> Span {
         let span = self.data();
         let end = end.data();
@@ -570,7 +588,13 @@ impl Span {
         )
     }
 
-    /// Returns a `Span` between the beginning of `self` to the beginning of `end`.
+    /// Returns a `Span` from the beginning of `self` until the beginning of `end`.
+    ///
+    /// ```text
+    ///     ____             ___
+    ///     self lorem ipsum end
+    ///     ^^^^^^^^^^^^^^^^^
+    /// ```
     pub fn until(self, end: Span) -> Span {
         let span = self.data();
         let end = end.data();
@@ -1133,7 +1157,12 @@ impl<S: Encoder> Encodable<S> for SourceFile {
                     let max_line_length = if lines.len() == 1 {
                         0
                     } else {
-                        lines.windows(2).map(|w| w[1] - w[0]).map(|bp| bp.to_usize()).max().unwrap()
+                        lines
+                            .array_windows()
+                            .map(|&[fst, snd]| snd - fst)
+                            .map(|bp| bp.to_usize())
+                            .max()
+                            .unwrap()
                     };
 
                     let bytes_per_diff: u8 = match max_line_length {
@@ -1148,7 +1177,7 @@ impl<S: Encoder> Encodable<S> for SourceFile {
                     // Encode the first element.
                     lines[0].encode(s)?;
 
-                    let diff_iter = (&lines[..]).windows(2).map(|w| (w[1] - w[0]));
+                    let diff_iter = lines[..].array_windows().map(|&[fst, snd]| snd - fst);
 
                     match bytes_per_diff {
                         1 => {
@@ -1529,58 +1558,71 @@ pub trait Pos {
     fn to_u32(&self) -> u32;
 }
 
-/// A byte offset. Keep this small (currently 32-bits), as AST contains
-/// a lot of them.
-#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
-pub struct BytePos(pub u32);
-
-/// A character offset. Because of multibyte UTF-8 characters, a byte offset
-/// is not equivalent to a character offset. The `SourceMap` will convert `BytePos`
-/// values to `CharPos` values as necessary.
-#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
-pub struct CharPos(pub usize);
+macro_rules! impl_pos {
+    (
+        $(
+            $(#[$attr:meta])*
+            $vis:vis struct $ident:ident($inner_vis:vis $inner_ty:ty);
+        )*
+    ) => {
+        $(
+            $(#[$attr])*
+            $vis struct $ident($inner_vis $inner_ty);
+
+            impl Pos for $ident {
+                #[inline(always)]
+                fn from_usize(n: usize) -> $ident {
+                    $ident(n as $inner_ty)
+                }
 
-// FIXME: lots of boilerplate in these impls, but so far my attempts to fix
-// have been unsuccessful.
+                #[inline(always)]
+                fn to_usize(&self) -> usize {
+                    self.0 as usize
+                }
 
-impl Pos for BytePos {
-    #[inline(always)]
-    fn from_usize(n: usize) -> BytePos {
-        BytePos(n as u32)
-    }
+                #[inline(always)]
+                fn from_u32(n: u32) -> $ident {
+                    $ident(n as $inner_ty)
+                }
 
-    #[inline(always)]
-    fn to_usize(&self) -> usize {
-        self.0 as usize
-    }
+                #[inline(always)]
+                fn to_u32(&self) -> u32 {
+                    self.0 as u32
+                }
+            }
 
-    #[inline(always)]
-    fn from_u32(n: u32) -> BytePos {
-        BytePos(n)
-    }
+            impl Add for $ident {
+                type Output = $ident;
 
-    #[inline(always)]
-    fn to_u32(&self) -> u32 {
-        self.0
-    }
-}
+                #[inline(always)]
+                fn add(self, rhs: $ident) -> $ident {
+                    $ident(self.0 + rhs.0)
+                }
+            }
 
-impl Add for BytePos {
-    type Output = BytePos;
+            impl Sub for $ident {
+                type Output = $ident;
 
-    #[inline(always)]
-    fn add(self, rhs: BytePos) -> BytePos {
-        BytePos((self.to_usize() + rhs.to_usize()) as u32)
-    }
+                #[inline(always)]
+                fn sub(self, rhs: $ident) -> $ident {
+                    $ident(self.0 - rhs.0)
+                }
+            }
+        )*
+    };
 }
 
-impl Sub for BytePos {
-    type Output = BytePos;
+impl_pos! {
+    /// A byte offset. Keep this small (currently 32-bits), as AST contains
+    /// a lot of them.
+    #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
+    pub struct BytePos(pub u32);
 
-    #[inline(always)]
-    fn sub(self, rhs: BytePos) -> BytePos {
-        BytePos((self.to_usize() - rhs.to_usize()) as u32)
-    }
+    /// A character offset. Because of multibyte UTF-8 characters, a byte offset
+    /// is not equivalent to a character offset. The `SourceMap` will convert `BytePos`
+    /// values to `CharPos` values as necessary.
+    #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
+    pub struct CharPos(pub usize);
 }
 
 impl<S: rustc_serialize::Encoder> Encodable<S> for BytePos {
@@ -1595,46 +1637,6 @@ impl<D: rustc_serialize::Decoder> Decodable<D> for BytePos {
     }
 }
 
-impl Pos for CharPos {
-    #[inline(always)]
-    fn from_usize(n: usize) -> CharPos {
-        CharPos(n)
-    }
-
-    #[inline(always)]
-    fn to_usize(&self) -> usize {
-        self.0
-    }
-
-    #[inline(always)]
-    fn from_u32(n: u32) -> CharPos {
-        CharPos(n as usize)
-    }
-
-    #[inline(always)]
-    fn to_u32(&self) -> u32 {
-        self.0 as u32
-    }
-}
-
-impl Add for CharPos {
-    type Output = CharPos;
-
-    #[inline(always)]
-    fn add(self, rhs: CharPos) -> CharPos {
-        CharPos(self.to_usize() + rhs.to_usize())
-    }
-}
-
-impl Sub for CharPos {
-    type Output = CharPos;
-
-    #[inline(always)]
-    fn sub(self, rhs: CharPos) -> CharPos {
-        CharPos(self.to_usize() - rhs.to_usize())
-    }
-}
-
 // _____________________________________________________________________________
 // Loc, SourceFileAndLine, SourceFileAndBytePos
 //
diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs
index 7c656db22ed..37596b8ef6f 100644
--- a/compiler/rustc_span/src/source_map.rs
+++ b/compiler/rustc_span/src/source_map.rs
@@ -487,6 +487,15 @@ impl SourceMap {
         }
     }
 
+    /// Returns a new `Span` covering the start and end `BytePos`s of the file containing the given
+    /// `pos`. This can be used to quickly determine if another `BytePos` or `Span` is from the same
+    /// file.
+    pub fn lookup_file_span(&self, pos: BytePos) -> Span {
+        let idx = self.lookup_source_file_idx(pos);
+        let SourceFile { start_pos, end_pos, .. } = *(*self.files.borrow().source_files)[idx];
+        Span::with_root_ctxt(start_pos, end_pos)
+    }
+
     /// Returns `Some(span)`, a union of the LHS and RHS span. The LHS must precede the RHS. If
     /// there are gaps between LHS and RHS, the resulting union will cross these gaps.
     /// For this to work,
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 5092b945f72..2d5c6451d1a 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -110,7 +110,7 @@ symbols! {
     // called `sym::proc_macro` because then it's easy to mistakenly think it
     // represents "proc_macro".
     //
-    // As well as the symbols listed, there are symbols for the the strings
+    // As well as the symbols listed, there are symbols for the strings
     // "0", "1", ..., "9", which are accessible via `sym::integer`.
     //
     // The proc macro will abort if symbols are not in alphabetical order (as
@@ -348,6 +348,7 @@ symbols! {
         const_compare_raw_pointers,
         const_constructor,
         const_eval_limit,
+        const_evaluatable_checked,
         const_extern_fn,
         const_fn,
         const_fn_transmute,
@@ -415,7 +416,9 @@ symbols! {
         deny,
         deprecated,
         deref,
+        deref_method,
         deref_mut,
+        deref_target,
         derive,
         diagnostic,
         direct,
@@ -676,7 +679,6 @@ symbols! {
         minnumf32,
         minnumf64,
         mips_target_feature,
-        mmx_target_feature,
         module,
         module_path,
         more_struct_aliases,
@@ -1069,6 +1071,7 @@ symbols! {
         target_feature,
         target_feature_11,
         target_has_atomic,
+        target_has_atomic_equal_alignment,
         target_has_atomic_load_store,
         target_os,
         target_pointer_width,
diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs
index 24356844baf..6f9fc115cae 100644
--- a/compiler/rustc_symbol_mangling/src/legacy.rs
+++ b/compiler/rustc_symbol_mangling/src/legacy.rs
@@ -110,7 +110,7 @@ fn get_symbol_hash<'tcx>(
         // If this is a function, we hash the signature as well.
         // This is not *strictly* needed, but it may help in some
         // situations, see the `run-make/a-b-a-linker-guard` test.
-        if let ty::FnDef(..) = item_type.kind {
+        if let ty::FnDef(..) = item_type.kind() {
             item_type.fn_sig(tcx).hash_stable(&mut hcx, &mut hasher);
         }
 
@@ -210,7 +210,7 @@ impl Printer<'tcx> for SymbolPrinter<'tcx> {
     }
 
     fn print_type(self, ty: Ty<'tcx>) -> Result<Self::Type, Self::Error> {
-        match ty.kind {
+        match *ty.kind() {
             // Print all nominal types as paths (unlike `pretty_print_type`).
             ty::FnDef(def_id, substs)
             | ty::Opaque(def_id, substs)
@@ -258,7 +258,7 @@ impl Printer<'tcx> for SymbolPrinter<'tcx> {
     ) -> Result<Self::Path, Self::Error> {
         // Similar to `pretty_path_qualified`, but for the other
         // types that are printed as paths (see `print_type` above).
-        match self_ty.kind {
+        match self_ty.kind() {
             ty::FnDef(..)
             | ty::Opaque(..)
             | ty::Projection(_)
diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs
index 296b40c4e39..75150a56c43 100644
--- a/compiler/rustc_symbol_mangling/src/lib.rs
+++ b/compiler/rustc_symbol_mangling/src/lib.rs
@@ -87,7 +87,7 @@
 //! virtually impossible. Thus, symbol hash generation exclusively relies on
 //! DefPaths which are much more robust in the face of changes to the code base.
 
-#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
+#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![feature(never_type)]
 #![feature(nll)]
 #![feature(or_patterns)]
diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs
index f01c3da8b5f..091d488138e 100644
--- a/compiler/rustc_symbol_mangling/src/v0.rs
+++ b/compiler/rustc_symbol_mangling/src/v0.rs
@@ -152,11 +152,8 @@ impl SymbolMangler<'tcx> {
         let _ = write!(self.out, "{}", ident.len());
 
         // Write a separating `_` if necessary (leading digit or `_`).
-        match ident.chars().next() {
-            Some('_' | '0'..='9') => {
-                self.push("_");
-            }
-            _ => {}
+        if let Some('_' | '0'..='9') = ident.chars().next() {
+            self.push("_");
         }
 
         self.push(ident);
@@ -323,7 +320,7 @@ impl Printer<'tcx> for SymbolMangler<'tcx> {
 
     fn print_type(mut self, ty: Ty<'tcx>) -> Result<Self::Type, Self::Error> {
         // Basic types, never cached (single-character).
-        let basic_type = match ty.kind {
+        let basic_type = match ty.kind() {
             ty::Bool => "b",
             ty::Char => "c",
             ty::Str => "e",
@@ -359,7 +356,7 @@ impl Printer<'tcx> for SymbolMangler<'tcx> {
         }
         let start = self.out.len();
 
-        match ty.kind {
+        match *ty.kind() {
             // Basic types, handled above.
             ty::Bool | ty::Char | ty::Str | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Never => {
                 unreachable!()
@@ -505,7 +502,7 @@ impl Printer<'tcx> for SymbolMangler<'tcx> {
         }
         let start = self.out.len();
 
-        match ct.ty.kind {
+        match ct.ty.kind() {
             ty::Uint(_) => {}
             _ => {
                 bug!("symbol_names: unsupported constant of type `{}` ({:?})", ct.ty, ct);
diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs
index 5788e1e8385..fb747dfcbd3 100644
--- a/compiler/rustc_target/src/lib.rs
+++ b/compiler/rustc_target/src/lib.rs
@@ -7,7 +7,7 @@
 //! more 'stuff' here in the future. It does not have a dependency on
 //! LLVM.
 
-#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
+#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![feature(bool_to_option)]
 #![feature(const_fn)]
 #![feature(const_panic)]
diff --git a/compiler/rustc_target/src/spec/avr_gnu_base.rs b/compiler/rustc_target/src/spec/avr_gnu_base.rs
index ff559c2bfd6..527a322d56a 100644
--- a/compiler/rustc_target/src/spec/avr_gnu_base.rs
+++ b/compiler/rustc_target/src/spec/avr_gnu_base.rs
@@ -45,6 +45,8 @@ pub fn target(target_cpu: String) -> TargetResult {
             late_link_args: vec![(LinkerFlavor::Gcc, vec!["-lgcc".to_owned()])]
                 .into_iter()
                 .collect(),
+            max_atomic_width: Some(0),
+            atomic_cas: false,
             ..TargetOptions::default()
         },
     })
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index d6e8b304380..f1e8330425e 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -654,6 +654,7 @@ supported_targets! {
     ("riscv32i-unknown-none-elf", riscv32i_unknown_none_elf),
     ("riscv32imc-unknown-none-elf", riscv32imc_unknown_none_elf),
     ("riscv32imac-unknown-none-elf", riscv32imac_unknown_none_elf),
+    ("riscv32gc-unknown-linux-gnu", riscv32gc_unknown_linux_gnu),
     ("riscv64imac-unknown-none-elf", riscv64imac_unknown_none_elf),
     ("riscv64gc-unknown-none-elf", riscv64gc_unknown_none_elf),
     ("riscv64gc-unknown-linux-gnu", riscv64gc_unknown_linux_gnu),
diff --git a/compiler/rustc_target/src/spec/riscv32gc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/riscv32gc_unknown_linux_gnu.rs
new file mode 100644
index 00000000000..28710c60175
--- /dev/null
+++ b/compiler/rustc_target/src/spec/riscv32gc_unknown_linux_gnu.rs
@@ -0,0 +1,25 @@
+use crate::spec::{CodeModel, LinkerFlavor, Target, TargetOptions, TargetResult};
+
+pub fn target() -> TargetResult {
+    Ok(Target {
+        llvm_target: "riscv32-unknown-linux-gnu".to_string(),
+        target_endian: "little".to_string(),
+        target_pointer_width: "32".to_string(),
+        target_c_int_width: "32".to_string(),
+        target_env: "gnu".to_string(),
+        data_layout: "e-m:e-p:32:32-i64:64-n32-S128".to_string(),
+        arch: "riscv32".to_string(),
+        target_os: "linux".to_string(),
+        target_vendor: "unknown".to_string(),
+        linker_flavor: LinkerFlavor::Gcc,
+        options: TargetOptions {
+            unsupported_abis: super::riscv_base::unsupported_abis(),
+            code_model: Some(CodeModel::Medium),
+            cpu: "generic-rv32".to_string(),
+            features: "+m,+a,+f,+d,+c".to_string(),
+            llvm_abiname: "ilp32d".to_string(),
+            max_atomic_width: Some(32),
+            ..super::linux_base::opts()
+        },
+    })
+}
diff --git a/compiler/rustc_target/src/spec/wasm32_base.rs b/compiler/rustc_target/src/spec/wasm32_base.rs
index 62fc8f06183..a7957d84cbe 100644
--- a/compiler/rustc_target/src/spec/wasm32_base.rs
+++ b/compiler/rustc_target/src/spec/wasm32_base.rs
@@ -83,6 +83,7 @@ pub fn options() -> TargetOptions {
         dll_prefix: String::new(),
         dll_suffix: ".wasm".to_string(),
         linker_is_gnu: false,
+        eh_frame_header: false,
 
         max_atomic_width: Some(64),
 
diff --git a/compiler/rustc_target/src/spec/wasm32_wasi.rs b/compiler/rustc_target/src/spec/wasm32_wasi.rs
index 0bba7bdd473..351167105ec 100644
--- a/compiler/rustc_target/src/spec/wasm32_wasi.rs
+++ b/compiler/rustc_target/src/spec/wasm32_wasi.rs
@@ -30,7 +30,7 @@
 //! ## No interop with C required
 //!
 //! By default the `crt-static` target feature is enabled, and when enabled
-//! this means that the the bundled version of `libc.a` found in `liblibc.rlib`
+//! this means that the bundled version of `libc.a` found in `liblibc.rlib`
 //! is used. This isn't intended really for interoperation with a C because it
 //! may be the case that Rust's bundled C library is incompatible with a
 //! foreign-compiled C library. In this use case, though, we use `rust-lld` and
diff --git a/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs b/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs
index fd55a0fc6a1..fcb2af0005f 100644
--- a/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs
+++ b/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs
@@ -22,7 +22,7 @@ pub fn opts() -> TargetOptions {
         "-lmingw32".to_string(),
     ];
     late_link_args.insert(LinkerFlavor::Gcc, mingw_libs.clone());
-    late_link_args.insert(LinkerFlavor::Lld(LldFlavor::Ld), mingw_libs.clone());
+    late_link_args.insert(LinkerFlavor::Lld(LldFlavor::Ld), mingw_libs);
 
     TargetOptions {
         executables: false,
diff --git a/compiler/rustc_trait_selection/src/autoderef.rs b/compiler/rustc_trait_selection/src/autoderef.rs
index 02eefe56223..b9c5123e49a 100644
--- a/compiler/rustc_trait_selection/src/autoderef.rs
+++ b/compiler/rustc_trait_selection/src/autoderef.rs
@@ -27,6 +27,7 @@ pub struct Autoderef<'a, 'tcx> {
     // Meta infos:
     infcx: &'a InferCtxt<'a, 'tcx>,
     span: Span,
+    overloaded_span: Span,
     body_id: hir::HirId,
     param_env: ty::ParamEnv<'tcx>,
 
@@ -98,10 +99,12 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
         body_id: hir::HirId,
         span: Span,
         base_ty: Ty<'tcx>,
+        overloaded_span: Span,
     ) -> Autoderef<'a, 'tcx> {
         Autoderef {
             infcx,
             span,
+            overloaded_span,
             body_id,
             param_env,
             state: AutoderefSnapshot {
@@ -190,6 +193,10 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
         self.span
     }
 
+    pub fn overloaded_span(&self) -> Span {
+        self.overloaded_span
+    }
+
     pub fn reached_recursion_limit(&self) -> bool {
         self.state.reached_recursion_limit
     }
diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs
index b5882df4729..ddeab340f38 100644
--- a/compiler/rustc_trait_selection/src/lib.rs
+++ b/compiler/rustc_trait_selection/src/lib.rs
@@ -10,10 +10,12 @@
 //!
 //! This API is completely unstable and subject to change.
 
-#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
+#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![feature(bool_to_option)]
+#![feature(box_patterns)]
 #![feature(drain_filter)]
 #![feature(in_band_lifetimes)]
+#![feature(never_type)]
 #![feature(crate_visibility_modifier)]
 #![feature(or_patterns)]
 #![recursion_limit = "512"] // For rustdoc
diff --git a/compiler/rustc_trait_selection/src/opaque_types.rs b/compiler/rustc_trait_selection/src/opaque_types.rs
index 379c976df69..28697ec4e3b 100644
--- a/compiler/rustc_trait_selection/src/opaque_types.rs
+++ b/compiler/rustc_trait_selection/src/opaque_types.rs
@@ -708,11 +708,11 @@ where
 
     fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool {
         // We're only interested in types involving regions
-        if !ty.flags.intersects(ty::TypeFlags::HAS_FREE_REGIONS) {
+        if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) {
             return false; // keep visiting
         }
 
-        match ty.kind {
+        match ty.kind() {
             ty::Closure(_, ref substs) => {
                 // Skip lifetime parameters of the enclosing item(s)
 
@@ -866,7 +866,7 @@ impl TypeFolder<'tcx> for ReverseMapper<'tcx> {
     }
 
     fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
-        match ty.kind {
+        match *ty.kind() {
             ty::Closure(def_id, substs) => {
                 // I am a horrible monster and I pray for death. When
                 // we encounter a closure here, it is always a closure
@@ -1003,7 +1003,7 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> {
             ty_op: |ty| {
                 if ty.references_error() {
                     return tcx.ty_error();
-                } else if let ty::Opaque(def_id, substs) = ty.kind {
+                } else if let ty::Opaque(def_id, substs) = ty.kind() {
                     // Check that this is `impl Trait` type is
                     // declared by `parent_def_id` -- i.e., one whose
                     // value we are inferring.  At present, this is
@@ -1261,7 +1261,8 @@ crate fn required_region_bounds(
                 | ty::PredicateAtom::ClosureKind(..)
                 | ty::PredicateAtom::RegionOutlives(..)
                 | ty::PredicateAtom::ConstEvaluatable(..)
-                | ty::PredicateAtom::ConstEquate(..) => None,
+                | ty::PredicateAtom::ConstEquate(..)
+                | ty::PredicateAtom::TypeWellFormedFromEnv(..) => None,
                 ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(ref t, ref r)) => {
                     // Search for a bound of the form `erased_self_ty
                     // : 'a`, but be wary of something like `for<'a>
diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
index fa3d0241998..6b87bc4f34a 100644
--- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs
+++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
@@ -373,14 +373,12 @@ impl AutoTraitFinder<'tcx> {
                 computed_preds.clone().chain(user_computed_preds.iter().cloned()),
             )
             .map(|o| o.predicate);
-            new_env =
-                ty::ParamEnv::new(tcx.mk_predicates(normalized_preds), param_env.reveal(), None);
+            new_env = ty::ParamEnv::new(tcx.mk_predicates(normalized_preds), param_env.reveal());
         }
 
         let final_user_env = ty::ParamEnv::new(
             tcx.mk_predicates(user_computed_preds.into_iter()),
             user_env.reveal(),
-            None,
         );
         debug!(
             "evaluate_nested_obligations(ty={:?}, trait_did={:?}): succeeded with '{:?}' \
@@ -598,7 +596,7 @@ impl AutoTraitFinder<'tcx> {
     }
 
     pub fn is_of_param(&self, ty: Ty<'_>) -> bool {
-        match ty.kind {
+        match ty.kind() {
             ty::Param(_) => true,
             ty::Projection(p) => self.is_of_param(p.self_ty()),
             _ => false,
@@ -606,7 +604,7 @@ impl AutoTraitFinder<'tcx> {
     }
 
     fn is_self_referential_projection(&self, p: ty::PolyProjectionPredicate<'_>) -> bool {
-        match p.ty().skip_binder().kind {
+        match *p.ty().skip_binder().kind() {
             ty::Projection(proj) if proj == p.skip_binder().projection_ty => true,
             _ => false,
         }
diff --git a/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs b/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs
index 0097097707f..adc8ae59086 100644
--- a/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs
+++ b/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs
@@ -4,12 +4,11 @@ use crate::infer::canonical::OriginalQueryValues;
 use crate::infer::InferCtxt;
 use crate::traits::query::NoSolution;
 use crate::traits::{
-    ChalkEnvironmentAndGoal, ChalkEnvironmentClause, FulfillmentError, FulfillmentErrorCode,
-    ObligationCause, PredicateObligation, SelectionError, TraitEngine,
+    ChalkEnvironmentAndGoal, FulfillmentError, FulfillmentErrorCode, ObligationCause,
+    PredicateObligation, SelectionError, TraitEngine,
 };
 use rustc_data_structures::fx::FxIndexSet;
-use rustc_hir::def_id::DefId;
-use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{self, Ty};
 
 pub struct FulfillmentContext<'tcx> {
     obligations: FxIndexSet<PredicateObligation<'tcx>>,
@@ -21,132 +20,6 @@ impl FulfillmentContext<'tcx> {
     }
 }
 
-fn environment<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    def_id: DefId,
-) -> &'tcx ty::List<ChalkEnvironmentClause<'tcx>> {
-    use rustc_hir::{ForeignItemKind, ImplItemKind, ItemKind, Node, TraitItemKind};
-    use rustc_middle::ty::subst::GenericArgKind;
-
-    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 environment(tcx, parent);
-    }
-
-    // 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().map(ChalkEnvironmentClause::Predicate);
-
-    let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
-    let node = tcx.hir().get(hir_id);
-
-    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 { of_trait: Some(_), .. } => NodeKind::TraitImpl,
-            ItemKind::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");
-
-            // 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);
-            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);
-            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() {
-            GenericArgKind::Type(ty) => Some(ChalkEnvironmentClause::TypeFromEnv(ty)),
-
-            // FIXME(eddyb) no WF conditions from lifetimes?
-            GenericArgKind::Lifetime(_) => None,
-
-            // FIXME(eddyb) support const generics in Chalk
-            GenericArgKind::Const(_) => None,
-        }
-    });
-
-    tcx.mk_chalk_environment_clause_list(clauses.chain(input_clauses))
-}
-
-/// We need to wrap a `ty::Predicate` in an elaborated environment *before* we
-/// canonicalize. This is due to the fact that we insert extra clauses into the
-/// environment for all input types (`FromEnv`).
-fn in_environment(
-    infcx: &InferCtxt<'_, 'tcx>,
-    obligation: &PredicateObligation<'tcx>,
-) -> ChalkEnvironmentAndGoal<'tcx> {
-    assert!(!infcx.is_in_snapshot());
-    let obligation = infcx.resolve_vars_if_possible(obligation);
-
-    let environment = match obligation.param_env.def_id {
-        Some(def_id) => environment(infcx.tcx, def_id),
-        None if obligation.param_env.caller_bounds().is_empty() => ty::List::empty(),
-        // FIXME(chalk): this is hit in ui/where-clauses/where-clause-constraints-are-local-for-trait-impl
-        // and ui/generics/generic-static-methods
-        //_ => bug!("non-empty `ParamEnv` with no def-id"),
-        _ => ty::List::empty(),
-    };
-
-    ChalkEnvironmentAndGoal { environment, goal: obligation.predicate }
-}
-
 impl TraitEngine<'tcx> for FulfillmentContext<'tcx> {
     fn normalize_projection_type(
         &mut self,
@@ -195,6 +68,8 @@ impl TraitEngine<'tcx> for FulfillmentContext<'tcx> {
         &mut self,
         infcx: &InferCtxt<'_, 'tcx>,
     ) -> Result<(), Vec<FulfillmentError<'tcx>>> {
+        assert!(!infcx.is_in_snapshot());
+
         let mut errors = Vec::new();
         let mut next_round = FxIndexSet::default();
         let mut making_progress;
@@ -205,10 +80,11 @@ impl TraitEngine<'tcx> for FulfillmentContext<'tcx> {
             // 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 goal_in_environment = in_environment(infcx, &obligation);
+                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();
-                let canonical_goal =
-                    infcx.canonicalize_query(&goal_in_environment, &mut orig_values);
+                let canonical_goal = infcx.canonicalize_query(&goal, &mut orig_values);
 
                 match infcx.tcx.evaluate_goal(canonical_goal) {
                     Ok(response) => {
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index b06cf4411d0..c53c65c00b7 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -182,7 +182,7 @@ fn overlap_within_probe(
     }
 
     if !skip_leak_check.is_yes() {
-        if let Err(_) = infcx.leak_check(true, snapshot) {
+        if infcx.leak_check(true, snapshot).is_err() {
             debug!("overlap: leak check failed");
             return None;
         }
@@ -412,7 +412,7 @@ fn orphan_check_trait_ref<'tcx>(
         if non_local_tys.is_empty() {
             debug!("orphan_check_trait_ref: ty_is_local `{:?}`", input_ty);
             return Ok(());
-        } else if let ty::Param(_) = input_ty.kind {
+        } else if let ty::Param(_) = input_ty.kind() {
             debug!("orphan_check_trait_ref: uncovered ty: `{:?}`", input_ty);
             let local_type = trait_ref
                 .substs
@@ -467,7 +467,7 @@ fn fundamental_ty_inner_tys(
     tcx: TyCtxt<'tcx>,
     ty: Ty<'tcx>,
 ) -> Option<impl Iterator<Item = Ty<'tcx>>> {
-    let (first_ty, rest_tys) = match ty.kind {
+    let (first_ty, rest_tys) = match *ty.kind() {
         ty::Ref(_, ty, _) => (ty, ty::subst::InternalSubsts::empty().types()),
         ty::Adt(def, substs) if def.is_fundamental() => {
             let mut types = substs.types();
@@ -504,7 +504,7 @@ fn def_id_is_local(def_id: DefId, in_crate: InCrate) -> bool {
 fn ty_is_local_constructor(ty: Ty<'_>, in_crate: InCrate) -> bool {
     debug!("ty_is_local_constructor({:?})", ty);
 
-    match ty.kind {
+    match *ty.kind() {
         ty::Bool
         | ty::Char
         | ty::Int(..)
diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
new file mode 100644
index 00000000000..0cfcaca9060
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
@@ -0,0 +1,470 @@
+//! Checking that constant values used in types can be successfully evaluated.
+//!
+//! For concrete constants, this is fairly simple as we can just try and evaluate it.
+//!
+//! When dealing with polymorphic constants, for example `std::mem::size_of::<T>() - 1`,
+//! this is not as easy.
+//!
+//! In this case we try to build an abstract representation of this constant using
+//! `mir_abstract_const` which can then be checked for structural equality with other
+//! generic constants mentioned in the `caller_bounds` of the current environment.
+use rustc_errors::ErrorReported;
+use rustc_hir::def::DefKind;
+use rustc_index::bit_set::BitSet;
+use rustc_index::vec::IndexVec;
+use rustc_infer::infer::InferCtxt;
+use rustc_middle::mir::abstract_const::{Node, NodeId};
+use rustc_middle::mir::interpret::ErrorHandled;
+use rustc_middle::mir::{self, Rvalue, StatementKind, TerminatorKind};
+use rustc_middle::ty::subst::Subst;
+use rustc_middle::ty::subst::SubstsRef;
+use rustc_middle::ty::{self, TyCtxt, TypeFoldable};
+use rustc_session::lint;
+use rustc_span::def_id::{DefId, LocalDefId};
+use rustc_span::Span;
+
+pub fn is_const_evaluatable<'cx, 'tcx>(
+    infcx: &InferCtxt<'cx, 'tcx>,
+    def: ty::WithOptConstParam<DefId>,
+    substs: SubstsRef<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    span: Span,
+) -> Result<(), ErrorHandled> {
+    debug!("is_const_evaluatable({:?}, {:?})", def, substs);
+    if infcx.tcx.features().const_evaluatable_checked {
+        if let Some(ct) = AbstractConst::new(infcx.tcx, def, substs)? {
+            for pred in param_env.caller_bounds() {
+                match pred.skip_binders() {
+                    ty::PredicateAtom::ConstEvaluatable(b_def, b_substs) => {
+                        debug!("is_const_evaluatable: caller_bound={:?}, {:?}", b_def, b_substs);
+                        if b_def == def && b_substs == substs {
+                            debug!("is_const_evaluatable: caller_bound ~~> ok");
+                            return Ok(());
+                        } else if AbstractConst::new(infcx.tcx, b_def, b_substs)?
+                            .map_or(false, |b_ct| try_unify(infcx.tcx, ct, b_ct))
+                        {
+                            debug!("is_const_evaluatable: abstract_const ~~> ok");
+                            return Ok(());
+                        }
+                    }
+                    _ => {} // don't care
+                }
+            }
+        }
+    }
+
+    let future_compat_lint = || {
+        if let Some(local_def_id) = def.did.as_local() {
+            infcx.tcx.struct_span_lint_hir(
+                lint::builtin::CONST_EVALUATABLE_UNCHECKED,
+                infcx.tcx.hir().local_def_id_to_hir_id(local_def_id),
+                span,
+                |err| {
+                    err.build("cannot use constants which depend on generic parameters in types")
+                        .emit();
+                },
+            );
+        }
+    };
+
+    // FIXME: We should only try to evaluate a given constant here if it is fully concrete
+    // as we don't want to allow things like `[u8; std::mem::size_of::<*mut T>()]`.
+    //
+    // We previously did not check this, so we only emit a future compat warning if
+    // const evaluation succeeds and the given constant is still polymorphic for now
+    // and hopefully soon change this to an error.
+    //
+    // See #74595 for more details about this.
+    let concrete = infcx.const_eval_resolve(param_env, def, substs, None, Some(span));
+
+    if concrete.is_ok() && substs.has_param_types_or_consts() {
+        match infcx.tcx.def_kind(def.did) {
+            DefKind::AnonConst => {
+                let mir_body = if let Some(def) = def.as_const_arg() {
+                    infcx.tcx.optimized_mir_of_const_arg(def)
+                } else {
+                    infcx.tcx.optimized_mir(def.did)
+                };
+
+                if mir_body.is_polymorphic {
+                    future_compat_lint();
+                }
+            }
+            _ => future_compat_lint(),
+        }
+    }
+
+    debug!(?concrete, "is_const_evaluatable");
+    concrete.map(drop)
+}
+
+/// A tree representing an anonymous constant.
+///
+/// This is only able to represent a subset of `MIR`,
+/// and should not leak any information about desugarings.
+#[derive(Clone, Copy)]
+pub struct AbstractConst<'tcx> {
+    // FIXME: Consider adding something like `IndexSlice`
+    // and use this here.
+    inner: &'tcx [Node<'tcx>],
+    substs: SubstsRef<'tcx>,
+}
+
+impl AbstractConst<'tcx> {
+    pub fn new(
+        tcx: TyCtxt<'tcx>,
+        def: ty::WithOptConstParam<DefId>,
+        substs: SubstsRef<'tcx>,
+    ) -> Result<Option<AbstractConst<'tcx>>, ErrorReported> {
+        let inner = match (def.did.as_local(), def.const_param_did) {
+            (Some(did), Some(param_did)) => {
+                tcx.mir_abstract_const_of_const_arg((did, param_did))?
+            }
+            _ => tcx.mir_abstract_const(def.did)?,
+        };
+
+        Ok(inner.map(|inner| AbstractConst { inner, substs }))
+    }
+
+    #[inline]
+    pub fn subtree(self, node: NodeId) -> AbstractConst<'tcx> {
+        AbstractConst { inner: &self.inner[..=node.index()], substs: self.substs }
+    }
+
+    #[inline]
+    pub fn root(self) -> Node<'tcx> {
+        self.inner.last().copied().unwrap()
+    }
+}
+
+struct AbstractConstBuilder<'a, 'tcx> {
+    tcx: TyCtxt<'tcx>,
+    body: &'a mir::Body<'tcx>,
+    /// The current WIP node tree.
+    nodes: IndexVec<NodeId, Node<'tcx>>,
+    locals: IndexVec<mir::Local, NodeId>,
+    /// We only allow field accesses if they access
+    /// the result of a checked operation.
+    checked_op_locals: BitSet<mir::Local>,
+}
+
+impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
+    fn error(&mut self, span: Option<Span>, msg: &str) -> Result<!, ErrorReported> {
+        self.tcx
+            .sess
+            .struct_span_err(self.body.span, "overly complex generic constant")
+            .span_label(span.unwrap_or(self.body.span), msg)
+            .help("consider moving this anonymous constant into a `const` function")
+            .emit();
+
+        Err(ErrorReported)
+    }
+
+    fn new(
+        tcx: TyCtxt<'tcx>,
+        body: &'a mir::Body<'tcx>,
+    ) -> Result<Option<AbstractConstBuilder<'a, 'tcx>>, ErrorReported> {
+        let mut builder = AbstractConstBuilder {
+            tcx,
+            body,
+            nodes: IndexVec::new(),
+            locals: IndexVec::from_elem(NodeId::MAX, &body.local_decls),
+            checked_op_locals: BitSet::new_empty(body.local_decls.len()),
+        };
+
+        // We don't have to look at concrete constants, as we
+        // can just evaluate them.
+        if !body.is_polymorphic {
+            return Ok(None);
+        }
+
+        // We only allow consts without control flow, so
+        // we check for cycles here which simplifies the
+        // rest of this implementation.
+        if body.is_cfg_cyclic() {
+            builder.error(None, "cyclic anonymous constants are forbidden")?;
+        }
+
+        Ok(Some(builder))
+    }
+
+    fn place_to_local(
+        &mut self,
+        span: Span,
+        p: &mir::Place<'tcx>,
+    ) -> Result<mir::Local, ErrorReported> {
+        const ZERO_FIELD: mir::Field = mir::Field::from_usize(0);
+        // Do not allow any projections.
+        //
+        // One exception are field accesses on the result of checked operations,
+        // which are required to support things like `1 + 2`.
+        if let Some(p) = p.as_local() {
+            debug_assert!(!self.checked_op_locals.contains(p));
+            Ok(p)
+        } else if let &[mir::ProjectionElem::Field(ZERO_FIELD, _)] = p.projection.as_ref() {
+            // Only allow field accesses if the given local
+            // contains the result of a checked operation.
+            if self.checked_op_locals.contains(p.local) {
+                Ok(p.local)
+            } else {
+                self.error(Some(span), "unsupported projection")?;
+            }
+        } else {
+            self.error(Some(span), "unsupported projection")?;
+        }
+    }
+
+    fn operand_to_node(
+        &mut self,
+        span: Span,
+        op: &mir::Operand<'tcx>,
+    ) -> Result<NodeId, ErrorReported> {
+        debug!("operand_to_node: op={:?}", op);
+        match op {
+            mir::Operand::Copy(p) | mir::Operand::Move(p) => {
+                let local = self.place_to_local(span, p)?;
+                Ok(self.locals[local])
+            }
+            mir::Operand::Constant(ct) => Ok(self.nodes.push(Node::Leaf(ct.literal))),
+        }
+    }
+
+    /// We do not allow all binary operations in abstract consts, so filter disallowed ones.
+    fn check_binop(op: mir::BinOp) -> bool {
+        use mir::BinOp::*;
+        match op {
+            Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Shl | Shr | Eq | Lt | Le
+            | Ne | Ge | Gt => true,
+            Offset => false,
+        }
+    }
+
+    /// While we currently allow all unary operations, we still want to explicitly guard against
+    /// future changes here.
+    fn check_unop(op: mir::UnOp) -> bool {
+        use mir::UnOp::*;
+        match op {
+            Not | Neg => true,
+        }
+    }
+
+    fn build_statement(&mut self, stmt: &mir::Statement<'tcx>) -> Result<(), ErrorReported> {
+        debug!("AbstractConstBuilder: stmt={:?}", stmt);
+        match stmt.kind {
+            StatementKind::Assign(box (ref place, ref rvalue)) => {
+                let local = self.place_to_local(stmt.source_info.span, place)?;
+                match *rvalue {
+                    Rvalue::Use(ref operand) => {
+                        self.locals[local] =
+                            self.operand_to_node(stmt.source_info.span, operand)?;
+                        Ok(())
+                    }
+                    Rvalue::BinaryOp(op, ref lhs, ref rhs) if Self::check_binop(op) => {
+                        let lhs = self.operand_to_node(stmt.source_info.span, lhs)?;
+                        let rhs = self.operand_to_node(stmt.source_info.span, rhs)?;
+                        self.locals[local] = self.nodes.push(Node::Binop(op, lhs, rhs));
+                        if op.is_checkable() {
+                            bug!("unexpected unchecked checkable binary operation");
+                        } else {
+                            Ok(())
+                        }
+                    }
+                    Rvalue::CheckedBinaryOp(op, ref lhs, ref rhs) if Self::check_binop(op) => {
+                        let lhs = self.operand_to_node(stmt.source_info.span, lhs)?;
+                        let rhs = self.operand_to_node(stmt.source_info.span, rhs)?;
+                        self.locals[local] = self.nodes.push(Node::Binop(op, lhs, rhs));
+                        self.checked_op_locals.insert(local);
+                        Ok(())
+                    }
+                    Rvalue::UnaryOp(op, ref operand) if Self::check_unop(op) => {
+                        let operand = self.operand_to_node(stmt.source_info.span, operand)?;
+                        self.locals[local] = self.nodes.push(Node::UnaryOp(op, operand));
+                        Ok(())
+                    }
+                    _ => self.error(Some(stmt.source_info.span), "unsupported rvalue")?,
+                }
+            }
+            // These are not actually relevant for us here, so we can ignore them.
+            StatementKind::StorageLive(_) | StatementKind::StorageDead(_) => Ok(()),
+            _ => self.error(Some(stmt.source_info.span), "unsupported statement")?,
+        }
+    }
+
+    /// Possible return values:
+    ///
+    /// - `None`: unsupported terminator, stop building
+    /// - `Some(None)`: supported terminator, finish building
+    /// - `Some(Some(block))`: support terminator, build `block` next
+    fn build_terminator(
+        &mut self,
+        terminator: &mir::Terminator<'tcx>,
+    ) -> Result<Option<mir::BasicBlock>, ErrorReported> {
+        debug!("AbstractConstBuilder: terminator={:?}", terminator);
+        match terminator.kind {
+            TerminatorKind::Goto { target } => Ok(Some(target)),
+            TerminatorKind::Return => Ok(None),
+            TerminatorKind::Call {
+                ref func,
+                ref args,
+                destination: Some((ref place, target)),
+                // We do not care about `cleanup` here. Any branch which
+                // uses `cleanup` will fail const-eval and they therefore
+                // do not matter when checking for const evaluatability.
+                //
+                // Do note that even if `panic::catch_unwind` is made const,
+                // we still do not have to care about this, as we do not look
+                // into functions.
+                cleanup: _,
+                // Do not allow overloaded operators for now,
+                // we probably do want to allow this in the future.
+                //
+                // This is currently fairly irrelevant as it requires `const Trait`s.
+                from_hir_call: true,
+                fn_span,
+            } => {
+                let local = self.place_to_local(fn_span, place)?;
+                let func = self.operand_to_node(fn_span, func)?;
+                let args = self.tcx.arena.alloc_from_iter(
+                    args.iter()
+                        .map(|arg| self.operand_to_node(terminator.source_info.span, arg))
+                        .collect::<Result<Vec<NodeId>, _>>()?,
+                );
+                self.locals[local] = self.nodes.push(Node::FunctionCall(func, args));
+                Ok(Some(target))
+            }
+            // We only allow asserts for checked operations.
+            //
+            // These asserts seem to all have the form `!_local.0` so
+            // we only allow exactly that.
+            TerminatorKind::Assert { ref cond, expected: false, target, .. } => {
+                let p = match cond {
+                    mir::Operand::Copy(p) | mir::Operand::Move(p) => p,
+                    mir::Operand::Constant(_) => bug!("unexpected assert"),
+                };
+
+                const ONE_FIELD: mir::Field = mir::Field::from_usize(1);
+                debug!("proj: {:?}", p.projection);
+                if let &[mir::ProjectionElem::Field(ONE_FIELD, _)] = p.projection.as_ref() {
+                    // Only allow asserts checking the result of a checked operation.
+                    if self.checked_op_locals.contains(p.local) {
+                        return Ok(Some(target));
+                    }
+                }
+
+                self.error(Some(terminator.source_info.span), "unsupported assertion")?;
+            }
+            _ => self.error(Some(terminator.source_info.span), "unsupported terminator")?,
+        }
+    }
+
+    /// Builds the abstract const by walking the mir from start to finish
+    /// and bailing out when encountering an unsupported operation.
+    fn build(mut self) -> Result<&'tcx [Node<'tcx>], ErrorReported> {
+        let mut block = &self.body.basic_blocks()[mir::START_BLOCK];
+        // We checked for a cyclic cfg above, so this should terminate.
+        loop {
+            debug!("AbstractConstBuilder: block={:?}", block);
+            for stmt in block.statements.iter() {
+                self.build_statement(stmt)?;
+            }
+
+            if let Some(next) = self.build_terminator(block.terminator())? {
+                block = &self.body.basic_blocks()[next];
+            } else {
+                return Ok(self.tcx.arena.alloc_from_iter(self.nodes));
+            }
+        }
+    }
+}
+
+/// Builds an abstract const, do not use this directly, but use `AbstractConst::new` instead.
+pub(super) fn mir_abstract_const<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    def: ty::WithOptConstParam<LocalDefId>,
+) -> Result<Option<&'tcx [mir::abstract_const::Node<'tcx>]>, ErrorReported> {
+    if tcx.features().const_evaluatable_checked {
+        match tcx.def_kind(def.did) {
+            // FIXME(const_evaluatable_checked): We currently only do this for anonymous constants,
+            // meaning that we do not look into associated constants. I(@lcnr) am not yet sure whether
+            // we want to look into them or treat them as opaque projections.
+            //
+            // Right now we do neither of that and simply always fail to unify them.
+            DefKind::AnonConst => (),
+            _ => return Ok(None),
+        }
+        let body = tcx.mir_const(def).borrow();
+        AbstractConstBuilder::new(tcx, &body)?.map(AbstractConstBuilder::build).transpose()
+    } else {
+        Ok(None)
+    }
+}
+
+pub(super) fn try_unify_abstract_consts<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    ((a, a_substs), (b, b_substs)): (
+        (ty::WithOptConstParam<DefId>, SubstsRef<'tcx>),
+        (ty::WithOptConstParam<DefId>, SubstsRef<'tcx>),
+    ),
+) -> bool {
+    (|| {
+        if let Some(a) = AbstractConst::new(tcx, a, a_substs)? {
+            if let Some(b) = AbstractConst::new(tcx, b, b_substs)? {
+                return Ok(try_unify(tcx, a, b));
+            }
+        }
+
+        Ok(false)
+    })()
+    .unwrap_or_else(|ErrorReported| true)
+    // FIXME(const_evaluatable_checked): We should instead have this
+    // method return the resulting `ty::Const` and return `ConstKind::Error`
+    // on `ErrorReported`.
+}
+
+/// Tries to unify two abstract constants using structural equality.
+pub(super) fn try_unify<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    a: AbstractConst<'tcx>,
+    b: AbstractConst<'tcx>,
+) -> bool {
+    match (a.root(), b.root()) {
+        (Node::Leaf(a_ct), Node::Leaf(b_ct)) => {
+            let a_ct = a_ct.subst(tcx, a.substs);
+            let b_ct = b_ct.subst(tcx, b.substs);
+            match (a_ct.val, b_ct.val) {
+                // We can just unify errors with everything to reduce the amount of
+                // emitted errors here.
+                (ty::ConstKind::Error(_), _) | (_, ty::ConstKind::Error(_)) => true,
+                (ty::ConstKind::Param(a_param), ty::ConstKind::Param(b_param)) => {
+                    a_param == b_param
+                }
+                (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val,
+                // If we have `fn a<const N: usize>() -> [u8; N + 1]` and `fn b<const M: usize>() -> [u8; 1 + M]`
+                // we do not want to use `assert_eq!(a(), b())` to infer that `N` and `M` have to be `1`. This
+                // means that we only allow inference variables if they are equal.
+                (ty::ConstKind::Infer(a_val), ty::ConstKind::Infer(b_val)) => a_val == b_val,
+                // FIXME(const_evaluatable_checked): We may want to either actually try
+                // to evaluate `a_ct` and `b_ct` if they are are fully concrete or something like
+                // this, for now we just return false here.
+                _ => false,
+            }
+        }
+        (Node::Binop(a_op, al, ar), Node::Binop(b_op, bl, br)) if a_op == b_op => {
+            try_unify(tcx, a.subtree(al), b.subtree(bl))
+                && try_unify(tcx, a.subtree(ar), b.subtree(br))
+        }
+        (Node::UnaryOp(a_op, av), Node::UnaryOp(b_op, bv)) if a_op == b_op => {
+            try_unify(tcx, a.subtree(av), b.subtree(bv))
+        }
+        (Node::FunctionCall(a_f, a_args), Node::FunctionCall(b_f, b_args))
+            if a_args.len() == b_args.len() =>
+        {
+            try_unify(tcx, a.subtree(a_f), b.subtree(b_f))
+                && a_args
+                    .iter()
+                    .zip(b_args)
+                    .all(|(&an, &bn)| try_unify(tcx, a.subtree(an), b.subtree(bn)))
+        }
+        _ => false,
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
index 28542d4b12e..bda4351b2f2 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -382,7 +382,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                             // If it has a custom `#[rustc_on_unimplemented]`
                             // error message, let's display it as the label!
                             err.span_label(span, s.as_str());
-                            if !matches!(trait_ref.skip_binder().self_ty().kind, ty::Param(_)) {
+                            if !matches!(trait_ref.skip_binder().self_ty().kind(), ty::Param(_)) {
                                 // When the self type is a type param We don't need to "the trait
                                 // `std::marker::Sized` is not implemented for `T`" as we will point
                                 // at the type param with a label to suggest constraining it.
@@ -446,12 +446,13 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                             self.tcx.lang_items().fn_once_trait(),
                         ]
                         .contains(&Some(trait_ref.def_id()));
-                        let is_target_feature_fn =
-                            if let ty::FnDef(def_id, _) = trait_ref.skip_binder().self_ty().kind {
-                                !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty()
-                            } else {
-                                false
-                            };
+                        let is_target_feature_fn = if let ty::FnDef(def_id, _) =
+                            *trait_ref.skip_binder().self_ty().kind()
+                        {
+                            !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty()
+                        } else {
+                            false
+                        };
                         if is_fn_trait && is_target_feature_fn {
                             err.note(
                                 "`#[target_feature]` functions do not implement the `Fn` traits",
@@ -662,6 +663,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                             obligation
                         )
                     }
+
+                    ty::PredicateAtom::TypeWellFormedFromEnv(..) => span_bug!(
+                        span,
+                        "TypeWellFormedFromEnv predicate should only exist in the environment"
+                    ),
                 }
             }
 
@@ -678,7 +684,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                     None => return,
                 };
 
-                let found_did = match found_trait_ty.kind {
+                let found_did = match *found_trait_ty.kind() {
                     ty::Closure(did, _) | ty::Foreign(did) | ty::FnDef(did, _) => Some(did),
                     ty::Adt(def, _) => Some(def.did),
                     _ => None,
@@ -696,13 +702,13 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
 
                 self.reported_closure_mismatch.borrow_mut().insert((span, found_span));
 
-                let found = match found_trait_ref.skip_binder().substs.type_at(1).kind {
+                let found = match found_trait_ref.skip_binder().substs.type_at(1).kind() {
                     ty::Tuple(ref tys) => vec![ArgKind::empty(); tys.len()],
                     _ => vec![ArgKind::empty()],
                 };
 
                 let expected_ty = expected_trait_ref.skip_binder().substs.type_at(1);
-                let expected = match expected_ty.kind {
+                let expected = match expected_ty.kind() {
                     ty::Tuple(ref tys) => tys
                         .iter()
                         .map(|t| ArgKind::from_expected_ty(t.expect_ty(), Some(span)))
@@ -1252,7 +1258,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> {
         /// returns the fuzzy category of a given type, or None
         /// if the type can be equated to any type.
         fn type_category(t: Ty<'_>) -> Option<u32> {
-            match t.kind {
+            match t.kind() {
                 ty::Bool => Some(0),
                 ty::Char => Some(1),
                 ty::Str => Some(2),
@@ -1281,7 +1287,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> {
         }
 
         match (type_category(a), type_category(b)) {
-            (Some(cat_a), Some(cat_b)) => match (&a.kind, &b.kind) {
+            (Some(cat_a), Some(cat_b)) => match (a.kind(), b.kind()) {
                 (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => def_a == def_b,
                 _ => cat_a == cat_b,
             },
@@ -1342,8 +1348,8 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> {
                     .normalize(candidate)
                     .ok();
                 match normalized {
-                    Some(normalized) => format!("\n  {:?}", normalized.value),
-                    None => format!("\n  {:?}", candidate),
+                    Some(normalized) => format!("\n  {}", normalized.value),
+                    None => format!("\n  {}", candidate),
                 }
             })
         };
@@ -1476,7 +1482,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> {
             ty::PredicateAtom::Trait(data, _) => {
                 let trait_ref = ty::Binder::bind(data.trait_ref);
                 let self_ty = trait_ref.skip_binder().self_ty();
-                debug!("self_ty {:?} {:?} trait_ref {:?}", self_ty, self_ty.kind, trait_ref);
+                debug!("self_ty {:?} {:?} trait_ref {:?}", self_ty, self_ty.kind(), trait_ref);
 
                 if predicate.references_error() {
                     return;
@@ -1506,12 +1512,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> {
                 // avoid inundating the user with unnecessary errors, but we now
                 // check upstream for type errors and don't add the obligations to
                 // begin with in those cases.
-                if self
-                    .tcx
-                    .lang_items()
-                    .sized_trait()
-                    .map_or(false, |sized_id| sized_id == trait_ref.def_id())
-                {
+                if self.tcx.lang_items().sized_trait() == Some(trait_ref.def_id()) {
                     self.need_type_info_err(body_id, span, self_ty, ErrorCode::E0282).emit();
                     return;
                 }
@@ -1664,7 +1665,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, '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::ParamTy { name, .. }) = *ty.kind() {
                     let infcx = self.infcx;
                     self.var_map.entry(ty).or_insert_with(|| {
                         infcx.next_ty_var(TypeVariableOrigin {
@@ -1938,8 +1939,8 @@ impl ArgKind {
     /// Creates an `ArgKind` from the expected type of an
     /// argument. It has no name (`_`) and an optional source span.
     pub fn from_expected_ty(t: Ty<'_>, span: Option<Span>) -> ArgKind {
-        match t.kind {
-            ty::Tuple(ref tys) => ArgKind::Tuple(
+        match t.kind() {
+            ty::Tuple(tys) => ArgKind::Tuple(
                 span,
                 tys.iter().map(|ty| ("_".to_owned(), ty.to_string())).collect::<Vec<_>>(),
             ),
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 d2b9f84af33..0f5aad5af12 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
@@ -194,7 +194,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
             flags.push((sym::_Self, Some("{integral}".to_owned())));
         }
 
-        if let ty::Array(aty, len) = self_ty.kind {
+        if let ty::Array(aty, len) = self_ty.kind() {
             flags.push((sym::_Self, Some("[]".to_owned())));
             flags.push((sym::_Self, Some(format!("[{}]", aty))));
             if let Some(def) = aty.ty_adt_def() {
@@ -218,7 +218,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                 }
             }
         }
-        if let ty::Dynamic(traits, _) = self_ty.kind {
+        if let ty::Dynamic(traits, _) = self_ty.kind() {
             for t in traits.skip_binder() {
                 if let ty::ExistentialPredicate::Trait(trait_ref) = t {
                     flags.push((sym::_Self, Some(self.tcx.def_path_str(trait_ref.def_id))))
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 138293c9533..90a8d9634ae 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -207,7 +207,7 @@ fn suggest_restriction(
     }
     // Given `fn foo(t: impl Trait)` where `Trait` requires assoc type `A`...
     if let Some((bound_str, fn_sig)) =
-        fn_sig.zip(projection).and_then(|(sig, p)| match p.self_ty().kind {
+        fn_sig.zip(projection).and_then(|(sig, p)| match p.self_ty().kind() {
             // Shenanigans to get the `Trait` from the `impl Trait`.
             ty::Param(param) => {
                 // `fn foo(t: impl Trait)`
@@ -323,7 +323,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
         body_id: hir::HirId,
     ) {
         let self_ty = trait_ref.skip_binder().self_ty();
-        let (param_ty, projection) = match &self_ty.kind {
+        let (param_ty, projection) = match self_ty.kind() {
             ty::Param(_) => (true, None),
             ty::Projection(projection) => (false, Some(projection)),
             _ => return,
@@ -482,8 +482,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
             None => return,
         };
 
-        if let ty::Ref(region, base_ty, mutbl) = real_ty.kind {
-            let mut autoderef = Autoderef::new(self, param_env, body_id, span, base_ty);
+        if let ty::Ref(region, base_ty, mutbl) = *real_ty.kind() {
+            let mut autoderef = Autoderef::new(self, param_env, body_id, span, base_ty, span);
             if let Some(steps) = autoderef.find_map(|(ty, steps)| {
                 // Re-add the `&`
                 let ty = self.tcx.mk_ref(region, TypeAndMut { ty, mutbl });
@@ -563,7 +563,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
             Some(ty) => ty,
         };
 
-        let (def_id, output_ty, callable) = match self_ty.kind {
+        let (def_id, output_ty, callable) = match *self_ty.kind() {
             ty::Closure(def_id, substs) => (def_id, substs.as_closure().sig().output(), "closure"),
             ty::FnDef(def_id, _) => (def_id, self_ty.fn_sig(self.tcx).output(), "function"),
             _ => return,
@@ -751,7 +751,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
             };
 
             for refs_remaining in 0..refs_number {
-                if let ty::Ref(_, inner_ty, _) = suggested_ty.kind {
+                if let ty::Ref(_, inner_ty, _) = suggested_ty.kind() {
                     suggested_ty = inner_ty;
 
                     let new_obligation = self.mk_trait_obligation_with_new_self_ty(
@@ -814,7 +814,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                 return;
             }
 
-            if let ty::Ref(region, t_type, mutability) = trait_ref.skip_binder().self_ty().kind {
+            if let ty::Ref(region, t_type, mutability) = *trait_ref.skip_binder().self_ty().kind() {
                 if region.is_late_bound() || t_type.has_escaping_bound_vars() {
                     // Avoid debug assertion in `mk_obligation_for_def_id`.
                     //
@@ -871,7 +871,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
         trait_ref: &ty::Binder<ty::TraitRef<'tcx>>,
     ) {
         let is_empty_tuple =
-            |ty: ty::Binder<Ty<'_>>| ty.skip_binder().kind == ty::Tuple(ty::List::empty());
+            |ty: ty::Binder<Ty<'_>>| *ty.skip_binder().kind() == ty::Tuple(ty::List::empty());
 
         let hir = self.tcx.hir();
         let parent_node = hir.get_parent_node(obligation.cause.body_id);
@@ -941,7 +941,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
         let body = hir.body(*body_id);
         let trait_ref = self.resolve_vars_if_possible(trait_ref);
         let ty = trait_ref.skip_binder().self_ty();
-        let is_object_safe = match ty.kind {
+        let is_object_safe = match ty.kind() {
             ty::Dynamic(predicates, _) => {
                 // If the `dyn Trait` is not object safe, do not suggest `Box<dyn Trait>`.
                 predicates
@@ -982,11 +982,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
              ty| {
                 let ty = self.resolve_vars_if_possible(&ty);
                 same &=
-                    !matches!(ty.kind, ty::Error(_))
+                    !matches!(ty.kind(), ty::Error(_))
                         && last_ty.map_or(true, |last_ty| {
                             // FIXME: ideally we would use `can_coerce` here instead, but `typeck` comes
                             // *after* in the dependency graph.
-                            match (&ty.kind, &last_ty.kind) {
+                            match (ty.kind(), last_ty.kind()) {
                                 (Infer(InferTy::IntVar(_)), Infer(InferTy::IntVar(_)))
                                 | (Infer(InferTy::FloatVar(_)), Infer(InferTy::FloatVar(_)))
                                 | (Infer(InferTy::FreshIntTy(_)), Infer(InferTy::FreshIntTy(_)))
@@ -997,12 +997,12 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                                 _ => ty == last_ty,
                             }
                         });
-                (Some(ty), same, only_never_return && matches!(ty.kind, ty::Never))
+                (Some(ty), same, only_never_return && matches!(ty.kind(), ty::Never))
             },
         );
         let all_returns_conform_to_trait =
             if let Some(ty_ret_ty) = typeck_results.node_type_opt(ret_ty.hir_id) {
-                match ty_ret_ty.kind {
+                match ty_ret_ty.kind() {
                     ty::Dynamic(predicates, _) => {
                         let cause = ObligationCause::misc(ret_ty.span, ret_ty.hir_id);
                         let param_env = ty::ParamEnv::empty();
@@ -1151,7 +1151,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
             trait_ref: ty::TraitRef<'tcx>,
         ) -> String {
             let inputs = trait_ref.substs.type_at(1);
-            let sig = if let ty::Tuple(inputs) = inputs.kind {
+            let sig = if let ty::Tuple(inputs) = inputs.kind() {
                 tcx.mk_fn_sig(
                     inputs.iter().map(|k| k.expect_ty()),
                     tcx.mk_ty_infer(ty::TyVar(ty::TyVid { index: 0 })),
@@ -1317,10 +1317,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                     debug!(
                         "maybe_note_obligation_cause_for_async_await: \
                             parent_trait_ref={:?} self_ty.kind={:?}",
-                        derived_obligation.parent_trait_ref, ty.kind
+                        derived_obligation.parent_trait_ref,
+                        ty.kind()
                     );
 
-                    match ty.kind {
+                    match *ty.kind() {
                         ty::Generator(did, ..) => {
                             generator = generator.or(Some(did));
                             outer_generator = Some(did);
diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs
index a5c6dc042ab..7e5be8276f7 100644
--- a/compiler/rustc_trait_selection/src/traits/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs
@@ -10,6 +10,7 @@ use rustc_middle::ty::ToPredicate;
 use rustc_middle::ty::{self, Binder, Const, Ty, TypeFoldable};
 use std::marker::PhantomData;
 
+use super::const_evaluatable;
 use super::project;
 use super::select::SelectionContext;
 use super::wf;
@@ -304,8 +305,34 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> {
             return ProcessResult::Unchanged;
         }
 
-        // This part of the code is much colder.
+        self.progress_changed_obligations(pending_obligation)
+    }
 
+    fn process_backedge<'c, I>(
+        &mut self,
+        cycle: I,
+        _marker: PhantomData<&'c PendingPredicateObligation<'tcx>>,
+    ) where
+        I: Clone + Iterator<Item = &'c PendingPredicateObligation<'tcx>>,
+    {
+        if self.selcx.coinductive_match(cycle.clone().map(|s| s.obligation.predicate)) {
+            debug!("process_child_obligations: coinductive match");
+        } else {
+            let cycle: Vec<_> = cycle.map(|c| c.obligation.clone()).collect();
+            self.selcx.infcx().report_overflow_error_cycle(&cycle);
+        }
+    }
+}
+
+impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> {
+    // The code calling this method is extremely hot and only rarely
+    // actually uses this, so move this part of the code
+    // out of that loop.
+    #[inline(never)]
+    fn progress_changed_obligations(
+        &mut self,
+        pending_obligation: &mut PendingPredicateObligation<'tcx>,
+    ) -> ProcessResult<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>> {
         pending_obligation.stalled_on.truncate(0);
 
         let obligation = &mut pending_obligation.obligation;
@@ -354,6 +381,9 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> {
                         obligation.with(pred.to_predicate(self.selcx.tcx())),
                     ]))
                 }
+                ty::PredicateAtom::TypeWellFormedFromEnv(..) => {
+                    bug!("TypeWellFormedFromEnv is only used for Chalk")
+                }
             },
             &ty::PredicateKind::Atom(atom) => match atom {
                 ty::PredicateAtom::Trait(ref data, _) => {
@@ -458,20 +488,39 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> {
                 }
 
                 ty::PredicateAtom::ConstEvaluatable(def_id, substs) => {
-                    match self.selcx.infcx().const_eval_resolve(
-                        obligation.param_env,
+                    match const_evaluatable::is_const_evaluatable(
+                        self.selcx.infcx(),
                         def_id,
                         substs,
-                        None,
-                        Some(obligation.cause.span),
+                        obligation.param_env,
+                        obligation.cause.span,
                     ) {
-                        Ok(_) => ProcessResult::Changed(vec![]),
-                        Err(err) => ProcessResult::Error(CodeSelectionError(ConstEvalFailure(err))),
+                        Ok(()) => ProcessResult::Changed(vec![]),
+                        Err(e) => ProcessResult::Error(CodeSelectionError(ConstEvalFailure(e))),
                     }
                 }
 
                 ty::PredicateAtom::ConstEquate(c1, c2) => {
                     debug!("equating consts: c1={:?} c2={:?}", c1, c2);
+                    if self.selcx.tcx().features().const_evaluatable_checked {
+                        // FIXME: we probably should only try to unify abstract constants
+                        // if the constants depend on generic parameters.
+                        //
+                        // Let's just see where this breaks :shrug:
+                        if let (
+                            ty::ConstKind::Unevaluated(a_def, a_substs, None),
+                            ty::ConstKind::Unevaluated(b_def, b_substs, None),
+                        ) = (c1.val, c2.val)
+                        {
+                            if self
+                                .selcx
+                                .tcx()
+                                .try_unify_abstract_consts(((a_def, a_substs), (b_def, b_substs)))
+                            {
+                                return ProcessResult::Changed(vec![]);
+                            }
+                        }
+                    }
 
                     let stalled_on = &mut pending_obligation.stalled_on;
 
@@ -535,27 +584,13 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> {
                         }
                     }
                 }
+                ty::PredicateAtom::TypeWellFormedFromEnv(..) => {
+                    bug!("TypeWellFormedFromEnv is only used for Chalk")
+                }
             },
         }
     }
 
-    fn process_backedge<'c, I>(
-        &mut self,
-        cycle: I,
-        _marker: PhantomData<&'c PendingPredicateObligation<'tcx>>,
-    ) where
-        I: Clone + Iterator<Item = &'c PendingPredicateObligation<'tcx>>,
-    {
-        if self.selcx.coinductive_match(cycle.clone().map(|s| s.obligation.predicate)) {
-            debug!("process_child_obligations: coinductive match");
-        } else {
-            let cycle: Vec<_> = cycle.map(|c| c.obligation.clone()).collect();
-            self.selcx.infcx().report_overflow_error_cycle(&cycle);
-        }
-    }
-}
-
-impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> {
     fn process_trait_obligation(
         &mut self,
         obligation: &PredicateObligation<'tcx>,
diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs
index 61567aeb57c..e23f5a583b2 100644
--- a/compiler/rustc_trait_selection/src/traits/misc.rs
+++ b/compiler/rustc_trait_selection/src/traits/misc.rs
@@ -23,7 +23,7 @@ pub fn can_type_implement_copy(
 ) -> Result<(), CopyImplementationError<'tcx>> {
     // FIXME: (@jroesch) float this code up
     tcx.infer_ctxt().enter(|infcx| {
-        let (adt, substs) = match self_type.kind {
+        let (adt, substs) = match self_type.kind() {
             // These types used to have a builtin impl.
             // Now libcore provides that impl.
             ty::Uint(_)
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index fe406e88c52..79495ba7f9b 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -7,6 +7,7 @@ pub mod auto_trait;
 mod chalk_fulfill;
 pub mod codegen;
 mod coherence;
+mod const_evaluatable;
 mod engine;
 pub mod error_reporting;
 mod fulfill;
@@ -301,11 +302,8 @@ pub fn normalize_param_env_or_error<'tcx>(
 
     debug!("normalize_param_env_or_error: elaborated-predicates={:?}", predicates);
 
-    let elaborated_env = ty::ParamEnv::new(
-        tcx.intern_predicates(&predicates),
-        unnormalized_env.reveal(),
-        unnormalized_env.def_id,
-    );
+    let elaborated_env =
+        ty::ParamEnv::new(tcx.intern_predicates(&predicates), unnormalized_env.reveal());
 
     // HACK: we are trying to normalize the param-env inside *itself*. The problem is that
     // normalization expects its param-env to be already normalized, which means we have
@@ -359,7 +357,7 @@ pub fn normalize_param_env_or_error<'tcx>(
     let outlives_env: Vec<_> =
         non_outlives_predicates.iter().chain(&outlives_predicates).cloned().collect();
     let outlives_env =
-        ty::ParamEnv::new(tcx.intern_predicates(&outlives_env), unnormalized_env.reveal(), None);
+        ty::ParamEnv::new(tcx.intern_predicates(&outlives_env), unnormalized_env.reveal());
     let outlives_predicates = match do_normalize_predicates(
         tcx,
         region_context,
@@ -379,11 +377,7 @@ pub fn normalize_param_env_or_error<'tcx>(
     let mut predicates = non_outlives_predicates;
     predicates.extend(outlives_predicates);
     debug!("normalize_param_env_or_error: final predicates={:?}", predicates);
-    ty::ParamEnv::new(
-        tcx.intern_predicates(&predicates),
-        unnormalized_env.reveal(),
-        unnormalized_env.def_id,
-    )
+    ty::ParamEnv::new(tcx.intern_predicates(&predicates), unnormalized_env.reveal())
 }
 
 pub fn fully_normalize<'a, 'tcx, T>(
@@ -558,6 +552,21 @@ pub fn provide(providers: &mut ty::query::Providers) {
         vtable_methods,
         type_implements_trait,
         subst_and_check_impossible_predicates,
+        mir_abstract_const: |tcx, def_id| {
+            let def_id = def_id.expect_local();
+            if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) {
+                tcx.mir_abstract_const_of_const_arg(def)
+            } else {
+                const_evaluatable::mir_abstract_const(tcx, ty::WithOptConstParam::unknown(def_id))
+            }
+        },
+        mir_abstract_const_of_const_arg: |tcx, (did, param_did)| {
+            const_evaluatable::mir_abstract_const(
+                tcx,
+                ty::WithOptConstParam { did, const_param_did: Some(param_did) },
+            )
+        },
+        try_unify_abstract_consts: const_evaluatable::try_unify_abstract_consts,
         ..*providers
     };
 }
diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs
index c003e4f8068..2f2ac9f094d 100644
--- a/compiler/rustc_trait_selection/src/traits/object_safety.rs
+++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs
@@ -276,7 +276,8 @@ fn predicates_reference_self(
                 | ty::PredicateAtom::ClosureKind(..)
                 | ty::PredicateAtom::Subtype(..)
                 | ty::PredicateAtom::ConstEvaluatable(..)
-                | ty::PredicateAtom::ConstEquate(..) => None,
+                | ty::PredicateAtom::ConstEquate(..)
+                | ty::PredicateAtom::TypeWellFormedFromEnv(..) => None,
             }
         })
         .collect()
@@ -310,7 +311,8 @@ fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
             | ty::PredicateAtom::ClosureKind(..)
             | ty::PredicateAtom::TypeOutlives(..)
             | ty::PredicateAtom::ConstEvaluatable(..)
-            | ty::PredicateAtom::ConstEquate(..) => false,
+            | ty::PredicateAtom::ConstEquate(..)
+            | ty::PredicateAtom::TypeWellFormedFromEnv(..) => false,
         }
     })
 }
@@ -654,11 +656,7 @@ fn receiver_is_dispatchable<'tcx>(
             .chain(iter::once(trait_predicate))
             .collect();
 
-        ty::ParamEnv::new(
-            tcx.intern_predicates(&caller_bounds),
-            param_env.reveal(),
-            param_env.def_id,
-        )
+        ty::ParamEnv::new(tcx.intern_predicates(&caller_bounds), param_env.reveal())
     };
 
     // Receiver: DispatchFromDyn<Receiver[Self => U]>
@@ -732,7 +730,7 @@ fn contains_illegal_self_type_reference<'tcx>(
 
     impl<'tcx> TypeVisitor<'tcx> for IllegalSelfTypeVisitor<'tcx> {
         fn visit_ty(&mut self, t: Ty<'tcx>) -> bool {
-            match t.kind {
+            match t.kind() {
                 ty::Param(_) => t == self.self_ty,
                 ty::Projection(ref data) => {
                     // This is a projected type `<Foo as SomeTrait>::X`.
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index c788e4f5c90..d37f819f376 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -345,7 +345,7 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> {
         // should occur eventually).
 
         let ty = ty.super_fold_with(self);
-        match ty.kind {
+        match *ty.kind() {
             ty::Opaque(def_id, substs) => {
                 // Only normalize `impl Trait` after type-checking, usually in codegen.
                 match self.param_env.reveal() {
@@ -908,7 +908,7 @@ fn assemble_candidates_from_trait_def<'cx, 'tcx>(
     let tcx = selcx.tcx();
     // Check whether the self-type is itself a projection.
     // If so, extract what we know from the trait and try to come up with a good answer.
-    let bounds = match obligation_trait_ref.self_ty().kind {
+    let bounds = match *obligation_trait_ref.self_ty().kind() {
         ty::Projection(ref data) => {
             tcx.projection_predicates(data.item_def_id).subst(tcx, data.substs)
         }
@@ -1067,7 +1067,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
                 // Any type with multiple potential discriminant types is therefore not eligible.
                 let self_ty = selcx.infcx().shallow_resolve(obligation.predicate.self_ty());
 
-                match self_ty.kind {
+                match self_ty.kind() {
                     ty::Bool
                     | ty::Char
                     | ty::Int(_)
@@ -1219,8 +1219,8 @@ fn confirm_object_candidate<'cx, 'tcx>(
     let self_ty = obligation_trait_ref.self_ty();
     let object_ty = selcx.infcx().shallow_resolve(self_ty);
     debug!("confirm_object_candidate(object_ty={:?})", object_ty);
-    let data = match object_ty.kind {
-        ty::Dynamic(ref data, ..) => data,
+    let data = match object_ty.kind() {
+        ty::Dynamic(data, ..) => data,
         _ => span_bug!(
             obligation.cause.span,
             "confirm_object_candidate called with non-object: {:?}",
diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs
index d07c95270e0..424b3bd67ff 100644
--- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs
@@ -82,7 +82,7 @@ impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> {
 /// Note also that `needs_drop` requires a "global" type (i.e., one
 /// with erased regions), but this function does not.
 pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
-    match ty.kind {
+    match ty.kind() {
         // None of these types have a destructor and hence they do not
         // require anything in particular to outlive the dtor's
         // execution.
diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
index 93652329305..3dcebbcc244 100644
--- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
@@ -7,6 +7,7 @@ use crate::infer::canonical::OriginalQueryValues;
 use crate::infer::{InferCtxt, InferOk};
 use crate::traits::error_reporting::InferCtxtExt;
 use crate::traits::{Obligation, ObligationCause, PredicateObligation, Reveal};
+use rustc_data_structures::mini_map::MiniMap;
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_infer::traits::Normalized;
 use rustc_middle::ty::fold::{TypeFoldable, TypeFolder};
@@ -57,6 +58,7 @@ impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> {
             param_env: self.param_env,
             obligations: vec![],
             error: false,
+            cache: MiniMap::new(),
             anon_depth: 0,
         };
 
@@ -85,6 +87,7 @@ struct QueryNormalizer<'cx, 'tcx> {
     cause: &'cx ObligationCause<'tcx>,
     param_env: ty::ParamEnv<'tcx>,
     obligations: Vec<PredicateObligation<'tcx>>,
+    cache: MiniMap<Ty<'tcx>, Ty<'tcx>>,
     error: bool,
     anon_depth: usize,
 }
@@ -99,8 +102,12 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> {
             return ty;
         }
 
+        if let Some(ty) = self.cache.get(&ty) {
+            return ty;
+        }
+
         let ty = ty.super_fold_with(self);
-        match ty.kind {
+        let res = (|| match *ty.kind() {
             ty::Opaque(def_id, substs) => {
                 // Only normalize `impl Trait` after type-checking, usually in codegen.
                 match self.param_env.reveal() {
@@ -197,7 +204,9 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> {
             }
 
             _ => ty,
-        }
+        })();
+        self.cache.insert(ty, res);
+        res
     }
 
     fn fold_const(&mut self, constant: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
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 1d5441b8eff..a4943231dfd 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -152,7 +152,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
         // Before we go into the whole placeholder thing, just
         // quickly check if the self-type is a projection at all.
-        match obligation.predicate.skip_binder().trait_ref.self_ty().kind {
+        match obligation.predicate.skip_binder().trait_ref.self_ty().kind() {
             ty::Projection(_) | ty::Opaque(..) => {}
             ty::Infer(ty::TyVar(_)) => {
                 span_bug!(
@@ -221,7 +221,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         // touch bound regions, they just capture the in-scope
         // type/region parameters.
         let self_ty = obligation.self_ty().skip_binder();
-        match self_ty.kind {
+        match self_ty.kind() {
             ty::Generator(..) => {
                 debug!(
                     "assemble_generator_candidates: self_ty={:?} obligation={:?}",
@@ -261,7 +261,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         // Okay to skip binder because the substs on closure types never
         // touch bound regions, they just capture the in-scope
         // type/region parameters
-        match obligation.self_ty().skip_binder().kind {
+        match *obligation.self_ty().skip_binder().kind() {
             ty::Closure(_, closure_substs) => {
                 debug!("assemble_unboxed_candidates: kind={:?} obligation={:?}", kind, obligation);
                 match self.infcx.closure_kind(closure_substs) {
@@ -300,7 +300,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
         // Okay to skip binder because what we are inspecting doesn't involve bound regions.
         let self_ty = obligation.self_ty().skip_binder();
-        match self_ty.kind {
+        match *self_ty.kind() {
             ty::Infer(ty::TyVar(_)) => {
                 debug!("assemble_fn_pointer_candidates: ambiguous self-type");
                 candidates.ambiguous = true; // Could wind up being a fn() type.
@@ -382,7 +382,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         let def_id = obligation.predicate.def_id();
 
         if self.tcx().trait_is_auto(def_id) {
-            match self_ty.kind {
+            match self_ty.kind() {
                 ty::Dynamic(..) => {
                     // For object types, we don't know what the closed
                     // over types are. This means we conservatively
@@ -453,7 +453,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             // self-ty here doesn't escape this probe, so just erase
             // any LBR.
             let self_ty = self.tcx().erase_late_bound_regions(&obligation.self_ty());
-            let poly_trait_ref = match self_ty.kind {
+            let poly_trait_ref = match self_ty.kind() {
                 ty::Dynamic(ref data, ..) => {
                     if data.auto_traits().any(|did| did == obligation.predicate.def_id()) {
                         debug!(
@@ -539,7 +539,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
         debug!("assemble_candidates_for_unsizing(source={:?}, target={:?})", source, target);
 
-        let may_apply = match (&source.kind, &target.kind) {
+        let may_apply = match (source.kind(), target.kind()) {
             // Trait+Kx+'a -> Trait+Ky+'b (upcasts).
             (&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => {
                 // Upcasts permit two things:
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index 3d6eb845136..9906c1f325f 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -327,8 +327,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         // from the object. Have to try to make a broken test case that
         // results.
         let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
-        let poly_trait_ref = match self_ty.kind {
-            ty::Dynamic(ref data, ..) => data
+        let poly_trait_ref = match self_ty.kind() {
+            ty::Dynamic(data, ..) => data
                 .principal()
                 .unwrap_or_else(|| {
                     span_bug!(obligation.cause.span, "object candidate with no principal")
@@ -449,7 +449,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         // touch bound regions, they just capture the in-scope
         // type/region parameters.
         let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
-        let (generator_def_id, substs) = match self_ty.kind {
+        let (generator_def_id, substs) = match *self_ty.kind() {
             ty::Generator(id, substs, _) => (id, substs),
             _ => bug!("closure candidate for non-closure {:?}", obligation),
         };
@@ -498,7 +498,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         // touch bound regions, they just capture the in-scope
         // type/region parameters.
         let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
-        let (closure_def_id, substs) = match self_ty.kind {
+        let (closure_def_id, substs) = match *self_ty.kind() {
             ty::Closure(id, substs) => (id, substs),
             _ => bug!("closure candidate for non-closure {:?}", obligation),
         };
@@ -594,7 +594,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         debug!("confirm_builtin_unsize_candidate(source={:?}, target={:?})", source, target);
 
         let mut nested = vec![];
-        match (&source.kind, &target.kind) {
+        match (source.kind(), target.kind()) {
             // Trait+Kx+'a -> Trait+Ky+'b (upcasts).
             (&ty::Dynamic(ref data_a, r_a), &ty::Dynamic(ref data_b, r_b)) => {
                 // See `assemble_candidates_for_unsizing` for more info.
@@ -693,7 +693,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             // `Struct<T>` -> `Struct<U>`
             (&ty::Adt(def, substs_a), &ty::Adt(_, substs_b)) => {
                 let maybe_unsizing_param_idx = |arg: GenericArg<'tcx>| match arg.unpack() {
-                    GenericArgKind::Type(ty) => match ty.kind {
+                    GenericArgKind::Type(ty) => match ty.kind() {
                         ty::Param(p) => Some(p.index),
                         _ => None,
                     },
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 82f476b463d..5e2f7d81d00 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -6,6 +6,7 @@ use self::EvaluationResult::*;
 use self::SelectionCandidate::*;
 
 use super::coherence::{self, Conflict};
+use super::const_evaluatable;
 use super::project;
 use super::project::normalize_with_depth_to;
 use super::util;
@@ -32,6 +33,7 @@ use rustc_hir::def_id::DefId;
 use rustc_middle::dep_graph::{DepKind, DepNodeIndex};
 use rustc_middle::mir::interpret::ErrorHandled;
 use rustc_middle::ty::fast_reject;
+use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::relate::TypeRelation;
 use rustc_middle::ty::subst::{GenericArgKind, Subst, SubstsRef};
 use rustc_middle::ty::{
@@ -448,150 +450,167 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             None => self.check_recursion_limit(&obligation, &obligation)?,
         }
 
-        match obligation.predicate.skip_binders() {
-            ty::PredicateAtom::Trait(t, _) => {
-                let t = ty::Binder::bind(t);
-                debug_assert!(!t.has_escaping_bound_vars());
-                let obligation = obligation.with(t);
-                self.evaluate_trait_predicate_recursively(previous_stack, obligation)
-            }
+        ensure_sufficient_stack(|| {
+            match obligation.predicate.skip_binders() {
+                ty::PredicateAtom::Trait(t, _) => {
+                    let t = ty::Binder::bind(t);
+                    debug_assert!(!t.has_escaping_bound_vars());
+                    let obligation = obligation.with(t);
+                    self.evaluate_trait_predicate_recursively(previous_stack, obligation)
+                }
+
+                ty::PredicateAtom::Subtype(p) => {
+                    let p = ty::Binder::bind(p);
+                    // Does this code ever run?
+                    match self.infcx.subtype_predicate(&obligation.cause, obligation.param_env, p) {
+                        Some(Ok(InferOk { mut obligations, .. })) => {
+                            self.add_depth(obligations.iter_mut(), obligation.recursion_depth);
+                            self.evaluate_predicates_recursively(
+                                previous_stack,
+                                obligations.into_iter(),
+                            )
+                        }
+                        Some(Err(_)) => Ok(EvaluatedToErr),
+                        None => Ok(EvaluatedToAmbig),
+                    }
+                }
 
-            ty::PredicateAtom::Subtype(p) => {
-                let p = ty::Binder::bind(p);
-                // Does this code ever run?
-                match self.infcx.subtype_predicate(&obligation.cause, obligation.param_env, p) {
-                    Some(Ok(InferOk { mut obligations, .. })) => {
+                ty::PredicateAtom::WellFormed(arg) => match wf::obligations(
+                    self.infcx,
+                    obligation.param_env,
+                    obligation.cause.body_id,
+                    arg,
+                    obligation.cause.span,
+                ) {
+                    Some(mut obligations) => {
                         self.add_depth(obligations.iter_mut(), obligation.recursion_depth);
                         self.evaluate_predicates_recursively(
                             previous_stack,
                             obligations.into_iter(),
                         )
                     }
-                    Some(Err(_)) => Ok(EvaluatedToErr),
                     None => Ok(EvaluatedToAmbig),
-                }
-            }
+                },
 
-            ty::PredicateAtom::WellFormed(arg) => match wf::obligations(
-                self.infcx,
-                obligation.param_env,
-                obligation.cause.body_id,
-                arg,
-                obligation.cause.span,
-            ) {
-                Some(mut obligations) => {
-                    self.add_depth(obligations.iter_mut(), obligation.recursion_depth);
-                    self.evaluate_predicates_recursively(previous_stack, obligations.into_iter())
+                ty::PredicateAtom::TypeOutlives(..) | ty::PredicateAtom::RegionOutlives(..) => {
+                    // We do not consider region relationships when evaluating trait matches.
+                    Ok(EvaluatedToOkModuloRegions)
                 }
-                None => Ok(EvaluatedToAmbig),
-            },
-
-            ty::PredicateAtom::TypeOutlives(..) | ty::PredicateAtom::RegionOutlives(..) => {
-                // We do not consider region relationships when evaluating trait matches.
-                Ok(EvaluatedToOkModuloRegions)
-            }
 
-            ty::PredicateAtom::ObjectSafe(trait_def_id) => {
-                if self.tcx().is_object_safe(trait_def_id) {
-                    Ok(EvaluatedToOk)
-                } else {
-                    Ok(EvaluatedToErr)
+                ty::PredicateAtom::ObjectSafe(trait_def_id) => {
+                    if self.tcx().is_object_safe(trait_def_id) {
+                        Ok(EvaluatedToOk)
+                    } else {
+                        Ok(EvaluatedToErr)
+                    }
                 }
-            }
 
-            ty::PredicateAtom::Projection(data) => {
-                let data = ty::Binder::bind(data);
-                let project_obligation = obligation.with(data);
-                match project::poly_project_and_unify_type(self, &project_obligation) {
-                    Ok(Ok(Some(mut subobligations))) => {
-                        self.add_depth(subobligations.iter_mut(), obligation.recursion_depth);
-                        let result = self.evaluate_predicates_recursively(
-                            previous_stack,
-                            subobligations.into_iter(),
-                        );
-                        if let Some(key) =
-                            ProjectionCacheKey::from_poly_projection_predicate(self, data)
-                        {
-                            self.infcx.inner.borrow_mut().projection_cache().complete(key);
+                ty::PredicateAtom::Projection(data) => {
+                    let data = ty::Binder::bind(data);
+                    let project_obligation = obligation.with(data);
+                    match project::poly_project_and_unify_type(self, &project_obligation) {
+                        Ok(Ok(Some(mut subobligations))) => {
+                            self.add_depth(subobligations.iter_mut(), obligation.recursion_depth);
+                            let result = self.evaluate_predicates_recursively(
+                                previous_stack,
+                                subobligations.into_iter(),
+                            );
+                            if let Some(key) =
+                                ProjectionCacheKey::from_poly_projection_predicate(self, data)
+                            {
+                                self.infcx.inner.borrow_mut().projection_cache().complete(key);
+                            }
+                            result
                         }
-                        result
+                        Ok(Ok(None)) => Ok(EvaluatedToAmbig),
+                        // EvaluatedToRecur might also be acceptable here, but use
+                        // Unknown for now because it means that we won't dismiss a
+                        // selection candidate solely because it has a projection
+                        // cycle. This is closest to the previous behavior of
+                        // immediately erroring.
+                        Ok(Err(project::InProgress)) => Ok(EvaluatedToUnknown),
+                        Err(_) => Ok(EvaluatedToErr),
                     }
-                    Ok(Ok(None)) => Ok(EvaluatedToAmbig),
-                    // EvaluatedToRecur might also be acceptable here, but use
-                    // Unknown for now because it means that we won't dismiss a
-                    // selection candidate solely because it has a projection
-                    // cycle. This is closest to the previous behavior of
-                    // immediately erroring.
-                    Ok(Err(project::InProgress)) => Ok(EvaluatedToUnknown),
-                    Err(_) => Ok(EvaluatedToErr),
                 }
-            }
 
-            ty::PredicateAtom::ClosureKind(_, closure_substs, kind) => {
-                match self.infcx.closure_kind(closure_substs) {
-                    Some(closure_kind) => {
-                        if closure_kind.extends(kind) {
-                            Ok(EvaluatedToOk)
-                        } else {
-                            Ok(EvaluatedToErr)
+                ty::PredicateAtom::ClosureKind(_, closure_substs, kind) => {
+                    match self.infcx.closure_kind(closure_substs) {
+                        Some(closure_kind) => {
+                            if closure_kind.extends(kind) {
+                                Ok(EvaluatedToOk)
+                            } else {
+                                Ok(EvaluatedToErr)
+                            }
                         }
+                        None => Ok(EvaluatedToAmbig),
                     }
-                    None => Ok(EvaluatedToAmbig),
                 }
-            }
 
-            ty::PredicateAtom::ConstEvaluatable(def_id, substs) => {
-                match self.tcx().const_eval_resolve(
-                    obligation.param_env,
-                    def_id,
-                    substs,
-                    None,
-                    None,
-                ) {
-                    Ok(_) => Ok(EvaluatedToOk),
-                    Err(ErrorHandled::TooGeneric) => Ok(EvaluatedToAmbig),
-                    Err(_) => Ok(EvaluatedToErr),
+                ty::PredicateAtom::ConstEvaluatable(def_id, substs) => {
+                    match const_evaluatable::is_const_evaluatable(
+                        self.infcx,
+                        def_id,
+                        substs,
+                        obligation.param_env,
+                        obligation.cause.span,
+                    ) {
+                        Ok(()) => Ok(EvaluatedToOk),
+                        Err(ErrorHandled::TooGeneric) => Ok(EvaluatedToAmbig),
+                        Err(_) => Ok(EvaluatedToErr),
+                    }
                 }
-            }
 
-            ty::PredicateAtom::ConstEquate(c1, c2) => {
-                debug!("evaluate_predicate_recursively: equating consts c1={:?} c2={:?}", c1, c2);
-
-                let evaluate = |c: &'tcx ty::Const<'tcx>| {
-                    if let ty::ConstKind::Unevaluated(def, substs, promoted) = c.val {
-                        self.infcx
-                            .const_eval_resolve(
-                                obligation.param_env,
-                                def,
-                                substs,
-                                promoted,
-                                Some(obligation.cause.span),
-                            )
-                            .map(|val| ty::Const::from_value(self.tcx(), val, c.ty))
-                    } else {
-                        Ok(c)
-                    }
-                };
+                ty::PredicateAtom::ConstEquate(c1, c2) => {
+                    debug!(
+                        "evaluate_predicate_recursively: equating consts c1={:?} c2={:?}",
+                        c1, c2
+                    );
 
-                match (evaluate(c1), evaluate(c2)) {
-                    (Ok(c1), Ok(c2)) => {
-                        match self.infcx().at(&obligation.cause, obligation.param_env).eq(c1, c2) {
-                            Ok(_) => Ok(EvaluatedToOk),
-                            Err(_) => Ok(EvaluatedToErr),
+                    let evaluate = |c: &'tcx ty::Const<'tcx>| {
+                        if let ty::ConstKind::Unevaluated(def, substs, promoted) = c.val {
+                            self.infcx
+                                .const_eval_resolve(
+                                    obligation.param_env,
+                                    def,
+                                    substs,
+                                    promoted,
+                                    Some(obligation.cause.span),
+                                )
+                                .map(|val| ty::Const::from_value(self.tcx(), val, c.ty))
+                        } else {
+                            Ok(c)
+                        }
+                    };
+
+                    match (evaluate(c1), evaluate(c2)) {
+                        (Ok(c1), Ok(c2)) => {
+                            match self
+                                .infcx()
+                                .at(&obligation.cause, obligation.param_env)
+                                .eq(c1, c2)
+                            {
+                                Ok(_) => Ok(EvaluatedToOk),
+                                Err(_) => Ok(EvaluatedToErr),
+                            }
+                        }
+                        (Err(ErrorHandled::Reported(ErrorReported)), _)
+                        | (_, Err(ErrorHandled::Reported(ErrorReported))) => Ok(EvaluatedToErr),
+                        (Err(ErrorHandled::Linted), _) | (_, Err(ErrorHandled::Linted)) => {
+                            span_bug!(
+                                obligation.cause.span(self.tcx()),
+                                "ConstEquate: const_eval_resolve returned an unexpected error"
+                            )
+                        }
+                        (Err(ErrorHandled::TooGeneric), _) | (_, Err(ErrorHandled::TooGeneric)) => {
+                            Ok(EvaluatedToAmbig)
                         }
                     }
-                    (Err(ErrorHandled::Reported(ErrorReported)), _)
-                    | (_, Err(ErrorHandled::Reported(ErrorReported))) => Ok(EvaluatedToErr),
-                    (Err(ErrorHandled::Linted), _) | (_, Err(ErrorHandled::Linted)) => span_bug!(
-                        obligation.cause.span(self.tcx()),
-                        "ConstEquate: const_eval_resolve returned an unexpected error"
-                    ),
-                    (Err(ErrorHandled::TooGeneric), _) | (_, Err(ErrorHandled::TooGeneric)) => {
-                        Ok(EvaluatedToAmbig)
-                    }
+                }
+                ty::PredicateAtom::TypeWellFormedFromEnv(..) => {
+                    bug!("TypeWellFormedFromEnv is only used for chalk")
                 }
             }
-        }
+        })
     }
 
     fn evaluate_trait_predicate_recursively<'o>(
@@ -778,14 +797,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     if !candidate_set.ambiguous && candidate_set.vec.is_empty() {
                         let trait_ref = stack.obligation.predicate.skip_binder().trait_ref;
                         let self_ty = trait_ref.self_ty();
-                        let cause = IntercrateAmbiguityCause::DownstreamCrate {
-                            trait_desc: trait_ref.print_only_trait_path().to_string(),
-                            self_desc: if self_ty.has_concrete_skeleton() {
-                                Some(self_ty.to_string())
-                            } else {
-                                None
-                            },
-                        };
+                        let cause =
+                            with_no_trimmed_paths(|| IntercrateAmbiguityCause::DownstreamCrate {
+                                trait_desc: trait_ref.print_only_trait_path().to_string(),
+                                self_desc: if self_ty.has_concrete_skeleton() {
+                                    Some(self_ty.to_string())
+                                } else {
+                                    None
+                                },
+                            });
+
                         debug!("evaluate_stack: pushing cause = {:?}", cause);
                         self.intercrate_ambiguity_causes.as_mut().unwrap().push(cause);
                     }
@@ -1030,12 +1051,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     if !candidate_set.ambiguous && no_candidates_apply {
                         let trait_ref = stack.obligation.predicate.skip_binder().trait_ref;
                         let self_ty = trait_ref.self_ty();
-                        let trait_desc = trait_ref.print_only_trait_path().to_string();
-                        let self_desc = if self_ty.has_concrete_skeleton() {
-                            Some(self_ty.to_string())
-                        } else {
-                            None
-                        };
+                        let (trait_desc, self_desc) = with_no_trimmed_paths(|| {
+                            let trait_desc = trait_ref.print_only_trait_path().to_string();
+                            let self_desc = if self_ty.has_concrete_skeleton() {
+                                Some(self_ty.to_string())
+                            } else {
+                                None
+                            };
+                            (trait_desc, self_desc)
+                        });
                         let cause = if let Conflict::Upstream = conflict {
                             IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_desc, self_desc }
                         } else {
@@ -1301,7 +1325,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         );
 
         let tcx = self.infcx.tcx;
-        let predicates = match placeholder_trait_predicate.trait_ref.self_ty().kind {
+        let predicates = match *placeholder_trait_predicate.trait_ref.self_ty().kind() {
             ty::Projection(ref data) => {
                 tcx.projection_predicates(data.item_def_id).subst(tcx, data.substs)
             }
@@ -1560,7 +1584,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         // NOTE: binder moved to (*)
         let self_ty = self.infcx.shallow_resolve(obligation.predicate.skip_binder().self_ty());
 
-        match self_ty.kind {
+        match self_ty.kind() {
             ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
             | ty::Uint(_)
             | ty::Int(_)
@@ -1615,7 +1639,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
         use self::BuiltinImplConditions::{Ambiguous, None, Where};
 
-        match self_ty.kind {
+        match self_ty.kind() {
             ty::Infer(ty::IntVar(_))
             | ty::Infer(ty::FloatVar(_))
             | ty::FnDef(..)
@@ -1689,7 +1713,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     /// Zed<i32> where enum Zed { A(T), B(u32) } -> [i32, u32]
     /// ```
     fn constituent_types_for_ty(&self, t: Ty<'tcx>) -> Vec<Ty<'tcx>> {
-        match t.kind {
+        match *t.kind() {
             ty::Uint(_)
             | ty::Int(_)
             | ty::Bool
diff --git a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs
index 56b8354d68c..c8bcab6efd7 100644
--- a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs
+++ b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs
@@ -3,6 +3,7 @@ use super::OverlapError;
 use crate::traits;
 use rustc_hir::def_id::DefId;
 use rustc_middle::ty::fast_reject::{self, SimplifiedType};
+use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{self, TyCtxt, TypeFoldable};
 
 pub use rustc_middle::traits::specialization_graph::*;
@@ -102,7 +103,8 @@ impl ChildrenExt for Children {
                 let trait_ref = overlap.impl_header.trait_ref.unwrap();
                 let self_ty = trait_ref.self_ty();
 
-                OverlapError {
+                // FIXME: should postpone string formatting until we decide to actually emit.
+                with_no_trimmed_paths(|| OverlapError {
                     with_impl: possible_sibling,
                     trait_desc: trait_ref.print_only_trait_path().to_string(),
                     // Only report the `Self` type if it has at least
@@ -115,7 +117,7 @@ impl ChildrenExt for Children {
                     },
                     intercrate_ambiguity_causes: overlap.intercrate_ambiguity_causes,
                     involves_placeholder: overlap.involves_placeholder,
-                }
+                })
             };
 
             let report_overlap_error = |overlap: traits::coherence::OverlapResult<'_>,
diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs
index 78186a5e8a5..4f7fa2c3988 100644
--- a/compiler/rustc_trait_selection/src/traits/structural_match.rs
+++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs
@@ -137,7 +137,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> {
     fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool {
         debug!("Search visiting ty: {:?}", ty);
 
-        let (adt_def, substs) = match ty.kind {
+        let (adt_def, substs) = match *ty.kind() {
             ty::Adt(adt_def, substs) => (adt_def, substs),
             ty::Param(_) => {
                 self.found = Some(NonStructuralMatchTy::Param);
diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs
index 0ac3c6ffe62..909cd2aa155 100644
--- a/compiler/rustc_trait_selection/src/traits/wf.rs
+++ b/compiler/rustc_trait_selection/src/traits/wf.rs
@@ -7,8 +7,9 @@ use rustc_hir::lang_items::LangItem;
 use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
 use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness};
 use rustc_span::Span;
-use std::rc::Rc;
 
+use std::iter;
+use std::rc::Rc;
 /// Returns the set of obligations needed to make `arg` well-formed.
 /// If `arg` contains unresolved inference variables, this may include
 /// further WF obligations. However, if `arg` IS an unresolved
@@ -25,7 +26,7 @@ pub fn obligations<'a, 'tcx>(
     // Handle the "livelock" case (see comment above) by bailing out if necessary.
     let arg = match arg.unpack() {
         GenericArgKind::Type(ty) => {
-            match ty.kind {
+            match ty.kind() {
                 ty::Infer(ty::TyVar(_)) => {
                     let resolved_ty = infcx.shallow_resolve(ty);
                     if resolved_ty == ty {
@@ -127,6 +128,9 @@ pub fn predicate_obligations<'a, 'tcx>(
             wf.compute(c1.into());
             wf.compute(c2.into());
         }
+        ty::PredicateAtom::TypeWellFormedFromEnv(..) => {
+            bug!("TypeWellFormedFromEnv is only used for Chalk")
+        }
     }
 
     wf.normalize()
@@ -200,7 +204,7 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>(
             // projection coming from another associated type. See
             // `src/test/ui/associated-types/point-at-type-on-obligation-failure.rs` and
             // `traits-assoc-type-in-supertrait-bad.rs`.
-            if let ty::Projection(projection_ty) = proj.ty.kind {
+            if let ty::Projection(projection_ty) = proj.ty.kind() {
                 let trait_assoc_item = tcx.associated_item(projection_ty.item_def_id);
                 if let Some(impl_item_span) =
                     items.iter().find(|item| item.ident == trait_assoc_item.ident).map(fix_span)
@@ -213,7 +217,7 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>(
             // An associated item obligation born out of the `trait` failed to be met. An example
             // can be seen in `ui/associated-types/point-at-type-on-obligation-failure-2.rs`.
             debug!("extended_cause_with_original_assoc_item_obligation trait proj {:?}", pred);
-            if let ty::Projection(ty::ProjectionTy { item_def_id, .. }) = pred.self_ty().kind {
+            if let ty::Projection(ty::ProjectionTy { item_def_id, .. }) = *pred.self_ty().kind() {
                 if let Some(impl_item_span) = trait_assoc_items
                     .find(|i| i.def_id == item_def_id)
                     .and_then(|trait_assoc_item| {
@@ -412,7 +416,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
                 }
             };
 
-            match ty.kind {
+            match *ty.kind() {
                 ty::Bool
                 | ty::Char
                 | ty::Int(..)
@@ -590,7 +594,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
                 // prevention, which happens before this can be reached.
                 ty::Infer(_) => {
                     let ty = self.infcx.shallow_resolve(ty);
-                    if let ty::Infer(ty::TyVar(_)) = ty.kind {
+                    if let ty::Infer(ty::TyVar(_)) = ty.kind() {
                         // Not yet resolved, but we've made progress.
                         let cause = self.cause(traits::MiscObligation);
                         self.out.push(traits::Obligation::new(
@@ -613,13 +617,24 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
         def_id: DefId,
         substs: SubstsRef<'tcx>,
     ) -> Vec<traits::PredicateObligation<'tcx>> {
-        let predicates = self.infcx.tcx.predicates_of(def_id).instantiate(self.infcx.tcx, substs);
+        let predicates = self.infcx.tcx.predicates_of(def_id);
+        let mut origins = vec![def_id; predicates.predicates.len()];
+        let mut head = predicates;
+        while let Some(parent) = head.parent {
+            head = self.infcx.tcx.predicates_of(parent);
+            origins.extend(iter::repeat(parent).take(head.predicates.len()));
+        }
+
+        let predicates = predicates.instantiate(self.infcx.tcx, substs);
+        debug_assert_eq!(predicates.predicates.len(), origins.len());
+
         predicates
             .predicates
             .into_iter()
             .zip(predicates.spans.into_iter())
-            .map(|(pred, span)| {
-                let cause = self.cause(traits::BindingObligation(def_id, span));
+            .zip(origins.into_iter().rev())
+            .map(|((pred, span), origin_def_id)| {
+                let cause = self.cause(traits::BindingObligation(origin_def_id, span));
                 traits::Obligation::new(cause, self.param_env, pred)
             })
             .filter(|pred| !pred.has_escaping_bound_vars())
diff --git a/compiler/rustc_traits/Cargo.toml b/compiler/rustc_traits/Cargo.toml
index 2d63fc51220..369d003eb22 100644
--- a/compiler/rustc_traits/Cargo.toml
+++ b/compiler/rustc_traits/Cargo.toml
@@ -12,8 +12,9 @@ rustc_hir = { path = "../rustc_hir" }
 rustc_index = { path = "../rustc_index" }
 rustc_ast = { path = "../rustc_ast" }
 rustc_span = { path = "../rustc_span" }
-chalk-ir = "0.14.0"
-chalk-solve = "0.14.0"
+chalk-ir = "0.28.0"
+chalk-solve = "0.28.0"
+chalk-engine = "0.28.0"
 smallvec = { version = "1.0", 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
index 4c8be8eb610..828ee6dea62 100644
--- a/compiler/rustc_traits/src/chalk/db.rs
+++ b/compiler/rustc_traits/src/chalk/db.rs
@@ -8,7 +8,7 @@
 
 use rustc_middle::traits::ChalkRustInterner as RustInterner;
 use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef};
-use rustc_middle::ty::{self, AssocItemContainer, AssocKind, TyCtxt};
+use rustc_middle::ty::{self, AssocItemContainer, AssocKind, TyCtxt, TypeFoldable};
 
 use rustc_hir::def_id::DefId;
 
@@ -17,11 +17,13 @@ use rustc_span::symbol::sym;
 use std::fmt;
 use std::sync::Arc;
 
-use crate::chalk::lowering::LowerInto;
+use crate::chalk::lowering::{self, LowerInto};
+use rustc_ast::ast;
 
 pub struct RustIrDatabase<'tcx> {
-    pub tcx: TyCtxt<'tcx>,
-    pub interner: RustInterner<'tcx>,
+    pub(crate) interner: RustInterner<'tcx>,
+    pub(crate) restatic_placeholder: ty::Region<'tcx>,
+    pub(crate) reempty_placeholder: ty::Region<'tcx>,
 }
 
 impl fmt::Debug for RustIrDatabase<'_> {
@@ -30,6 +32,26 @@ impl fmt::Debug for RustIrDatabase<'_> {
     }
 }
 
+impl<'tcx> RustIrDatabase<'tcx> {
+    fn where_clauses_for(
+        &self,
+        def_id: DefId,
+        bound_vars: SubstsRef<'tcx>,
+    ) -> Vec<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>> {
+        let predicates = self.interner.tcx.predicates_of(def_id).predicates;
+        let mut regions_substitutor = lowering::RegionsSubstitutor::new(
+            self.interner.tcx,
+            self.restatic_placeholder,
+            self.reempty_placeholder,
+        );
+        predicates
+            .iter()
+            .map(|(wc, _)| wc.subst(self.interner.tcx, bound_vars))
+            .map(|wc| wc.fold_with(&mut regions_substitutor))
+            .filter_map(|wc| LowerInto::<Option<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>>>::lower_into(wc, &self.interner)).collect()
+    }
+}
+
 impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'tcx> {
     fn interner(&self) -> &RustInterner<'tcx> {
         &self.interner
@@ -40,7 +62,7 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t
         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.tcx.associated_item(def_id);
+        let assoc_item = self.interner.tcx.associated_item(def_id);
         let trait_def_id = match assoc_item.container {
             AssocItemContainer::TraitContainer(def_id) => def_id,
             _ => unimplemented!("Not possible??"),
@@ -49,16 +71,12 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t
             AssocKind::Type => {}
             _ => unimplemented!("Not possible??"),
         }
-        let bound_vars = bound_vars_for_item(self.tcx, def_id);
+        let bound_vars = bound_vars_for_item(self.interner.tcx, def_id);
         let binders = binders_for(&self.interner, bound_vars);
         // FIXME(chalk): this really isn't right I don't think. The functions
         // for GATs are a bit hard to figure out. Are these supposed to be where
         // clauses or bounds?
-        let predicates = self.tcx.predicates_defined_on(def_id).predicates;
-        let where_clauses: Vec<_> = predicates
-            .iter()
-            .map(|(wc, _)| wc.subst(self.tcx, &bound_vars))
-            .filter_map(|wc| LowerInto::<Option<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>>>::lower_into(wc, &self.interner)).collect();
+        let where_clauses = self.where_clauses_for(def_id, bound_vars);
 
         Arc::new(chalk_solve::rust_ir::AssociatedTyDatum {
             trait_id: chalk_ir::TraitId(trait_def_id),
@@ -76,16 +94,15 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t
         trait_id: chalk_ir::TraitId<RustInterner<'tcx>>,
     ) -> Arc<chalk_solve::rust_ir::TraitDatum<RustInterner<'tcx>>> {
         let def_id = trait_id.0;
-        let trait_def = self.tcx.trait_def(def_id);
+        let trait_def = self.interner.tcx.trait_def(def_id);
 
-        let bound_vars = bound_vars_for_item(self.tcx, def_id);
+        let bound_vars = bound_vars_for_item(self.interner.tcx, def_id);
         let binders = binders_for(&self.interner, bound_vars);
-        let predicates = self.tcx.predicates_defined_on(def_id).predicates;
-        let where_clauses: Vec<_> = predicates
-            .iter()
-            .map(|(wc, _)| wc.subst(self.tcx, &bound_vars))
-            .filter_map(|wc| LowerInto::<Option<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>>>::lower_into(wc, &self.interner)).collect();
+
+        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()
@@ -93,24 +110,37 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t
             .map(|i| chalk_ir::AssocTypeId(i.def_id))
             .collect();
 
-        let well_known =
-            if self.tcx.lang_items().sized_trait().map(|t| def_id == t).unwrap_or(false) {
-                Some(chalk_solve::rust_ir::WellKnownTrait::Sized)
-            } else if self.tcx.lang_items().copy_trait().map(|t| def_id == t).unwrap_or(false) {
-                Some(chalk_solve::rust_ir::WellKnownTrait::Copy)
-            } else if self.tcx.lang_items().clone_trait().map(|t| def_id == t).unwrap_or(false) {
-                Some(chalk_solve::rust_ir::WellKnownTrait::Clone)
-            } else if self.tcx.lang_items().drop_trait().map(|t| def_id == t).unwrap_or(false) {
-                Some(chalk_solve::rust_ir::WellKnownTrait::Drop)
-            } else if self.tcx.lang_items().fn_trait().map(|t| def_id == t).unwrap_or(false) {
-                Some(chalk_solve::rust_ir::WellKnownTrait::Fn)
-            } else if self.tcx.lang_items().fn_once_trait().map(|t| def_id == t).unwrap_or(false) {
-                Some(chalk_solve::rust_ir::WellKnownTrait::FnOnce)
-            } else if self.tcx.lang_items().fn_mut_trait().map(|t| def_id == t).unwrap_or(false) {
-                Some(chalk_solve::rust_ir::WellKnownTrait::FnMut)
-            } else {
-                None
-            };
+        let well_known = if self.interner.tcx.lang_items().sized_trait() == Some(def_id) {
+            Some(chalk_solve::rust_ir::WellKnownTrait::Sized)
+        } else if self.interner.tcx.lang_items().copy_trait() == Some(def_id) {
+            Some(chalk_solve::rust_ir::WellKnownTrait::Copy)
+        } else if self.interner.tcx.lang_items().clone_trait() == Some(def_id) {
+            Some(chalk_solve::rust_ir::WellKnownTrait::Clone)
+        } else if self.interner.tcx.lang_items().drop_trait() == Some(def_id) {
+            Some(chalk_solve::rust_ir::WellKnownTrait::Drop)
+        } else if self.interner.tcx.lang_items().fn_trait() == Some(def_id) {
+            Some(chalk_solve::rust_ir::WellKnownTrait::Fn)
+        } else if self
+            .interner
+            .tcx
+            .lang_items()
+            .fn_once_trait()
+            .map(|t| def_id == t)
+            .unwrap_or(false)
+        {
+            Some(chalk_solve::rust_ir::WellKnownTrait::FnOnce)
+        } else if self
+            .interner
+            .tcx
+            .lang_items()
+            .fn_mut_trait()
+            .map(|t| def_id == t)
+            .unwrap_or(false)
+        {
+            Some(chalk_solve::rust_ir::WellKnownTrait::FnMut)
+        } else {
+            None
+        };
         Arc::new(chalk_solve::rust_ir::TraitDatum {
             id: trait_id,
             binders: chalk_ir::Binders::new(
@@ -121,7 +151,7 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t
                 auto: trait_def.has_auto_impl,
                 marker: trait_def.is_marker,
                 upstream: !def_id.is_local(),
-                fundamental: self.tcx.has_attr(def_id, sym::fundamental),
+                fundamental: self.interner.tcx.has_attr(def_id, sym::fundamental),
                 non_enumerable: true,
                 coinductive: false,
             },
@@ -136,45 +166,50 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t
     ) -> Arc<chalk_solve::rust_ir::AdtDatum<RustInterner<'tcx>>> {
         let adt_def = adt_id.0;
 
-        let bound_vars = bound_vars_for_item(self.tcx, adt_def.did);
+        let bound_vars = bound_vars_for_item(self.interner.tcx, adt_def.did);
         let binders = binders_for(&self.interner, bound_vars);
 
-        let predicates = self.tcx.predicates_of(adt_def.did).predicates;
-        let where_clauses: Vec<_> = predicates
+        let where_clauses = self.where_clauses_for(adt_def.did, bound_vars);
+
+        let variants: Vec<_> = adt_def
+            .variants
             .iter()
-            .map(|(wc, _)| wc.subst(self.tcx, bound_vars))
-            .filter_map(|wc| LowerInto::<Option<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>>>::lower_into(wc, &self.interner))
-            .collect();
-        let fields = match adt_def.adt_kind() {
-            ty::AdtKind::Struct | ty::AdtKind::Union => {
-                let variant = adt_def.non_enum_variant();
-                variant
+            .map(|variant| chalk_solve::rust_ir::AdtVariantDatum {
+                fields: variant
                     .fields
                     .iter()
-                    .map(|field| {
-                        self.tcx
-                            .type_of(field.did)
-                            .subst(self.tcx, bound_vars)
-                            .lower_into(&self.interner)
-                    })
-                    .collect()
-            }
-            // FIXME(chalk): handle enums; force_impl_for requires this
-            ty::AdtKind::Enum => vec![],
-        };
-        let struct_datum = Arc::new(chalk_solve::rust_ir::AdtDatum {
+                    .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 { fields, where_clauses },
+                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(),
             },
-        });
-        struct_datum
+            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>>,
+    ) -> chalk_solve::rust_ir::AdtRepr {
+        let adt_def = adt_id.0;
+        chalk_solve::rust_ir::AdtRepr {
+            repr_c: adt_def.repr.c(),
+            repr_packed: adt_def.repr.packed(),
+        }
     }
 
     fn fn_def_datum(
@@ -182,30 +217,25 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t
         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.tcx, def_id);
+        let bound_vars = bound_vars_for_item(self.interner.tcx, def_id);
         let binders = binders_for(&self.interner, bound_vars);
 
-        let predicates = self.tcx.predicates_defined_on(def_id).predicates;
-        let where_clauses: Vec<_> = predicates
-            .iter()
-            .map(|(wc, _)| wc.subst(self.tcx, &bound_vars))
-            .filter_map(|wc| LowerInto::<Option<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>>>::lower_into(wc, &self.interner)).collect();
+        let where_clauses = self.where_clauses_for(def_id, bound_vars);
 
-        let sig = self.tcx.fn_sig(def_id);
-        let inputs_and_output = sig.inputs_and_output();
+        let sig = self.interner.tcx.fn_sig(def_id);
         let (inputs_and_output, iobinders, _) = crate::chalk::lowering::collect_bound_vars(
             &self.interner,
-            self.tcx,
-            &inputs_and_output,
+            self.interner.tcx,
+            &sig.inputs_and_output().subst(self.interner.tcx, bound_vars),
         );
 
         let argument_types = inputs_and_output[..inputs_and_output.len() - 1]
             .iter()
-            .map(|t| t.subst(self.tcx, &bound_vars).lower_into(&self.interner))
+            .map(|t| t.subst(self.interner.tcx, &bound_vars).lower_into(&self.interner))
             .collect();
 
         let return_type = inputs_and_output[inputs_and_output.len() - 1]
-            .subst(self.tcx, &bound_vars)
+            .subst(self.interner.tcx, &bound_vars)
             .lower_into(&self.interner);
 
         let bound = chalk_solve::rust_ir::FnDefDatumBound {
@@ -217,7 +247,7 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t
         };
         Arc::new(chalk_solve::rust_ir::FnDefDatum {
             id: fn_def_id,
-            abi: sig.abi(),
+            sig: sig.lower_into(&self.interner),
             binders: chalk_ir::Binders::new(binders, bound),
         })
     }
@@ -227,17 +257,19 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t
         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.tcx, def_id);
+        let bound_vars = bound_vars_for_item(self.interner.tcx, def_id);
         let binders = binders_for(&self.interner, bound_vars);
 
-        let trait_ref = self.tcx.impl_trait_ref(def_id).expect("not an impl");
-        let trait_ref = trait_ref.subst(self.tcx, 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 mut regions_substitutor = lowering::RegionsSubstitutor::new(
+            self.interner.tcx,
+            self.restatic_placeholder,
+            self.reempty_placeholder,
+        );
+        let trait_ref = trait_ref.fold_with(&mut regions_substitutor);
 
-        let predicates = self.tcx.predicates_of(def_id).predicates;
-        let where_clauses: Vec<_> = predicates
-            .iter()
-            .map(|(wc, _)| wc.subst(self.tcx, bound_vars))
-            .filter_map(|wc| LowerInto::<Option<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>>>::lower_into(wc, &self.interner)).collect();
+        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),
@@ -256,6 +288,7 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t
         &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;
 
@@ -263,14 +296,20 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t
         // require us to be able to interconvert `Ty<'tcx>`, and we're
         // not there yet.
 
-        let all_impls = self.tcx.all_impls(def_id);
+        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.tcx.impl_trait_ref(*impl_def_id).unwrap();
-            let bound_vars = bound_vars_for_item(self.tcx, *impl_def_id);
+            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.self_ty();
-            let self_ty = self_ty.subst(self.tcx, bound_vars);
+            let self_ty = self_ty.subst(self.interner.tcx, bound_vars);
+            let mut regions_substitutor = lowering::RegionsSubstitutor::new(
+                self.interner.tcx,
+                self.restatic_placeholder,
+                self.reempty_placeholder,
+            );
+            let self_ty = self_ty.fold_with(&mut regions_substitutor);
             let lowered_ty = self_ty.lower_into(&self.interner);
 
             parameters[0].assert_ty_ref(&self.interner).could_match(&self.interner, &lowered_ty)
@@ -283,21 +322,75 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t
     fn impl_provided_for(
         &self,
         auto_trait_id: chalk_ir::TraitId<RustInterner<'tcx>>,
-        adt_id: chalk_ir::AdtId<RustInterner<'tcx>>,
+        app_ty: &chalk_ir::ApplicationTy<RustInterner<'tcx>>,
     ) -> bool {
+        use chalk_ir::Scalar::*;
+        use chalk_ir::TypeName::*;
+
         let trait_def_id = auto_trait_id.0;
-        let adt_def = adt_id.0;
-        let all_impls = self.tcx.all_impls(trait_def_id);
+        let all_impls = self.interner.tcx.all_impls(trait_def_id);
         for impl_def_id in all_impls {
-            let trait_ref = self.tcx.impl_trait_ref(impl_def_id).unwrap();
+            let trait_ref = self.interner.tcx.impl_trait_ref(impl_def_id).unwrap();
             let self_ty = trait_ref.self_ty();
-            match self_ty.kind {
-                ty::Adt(impl_adt_def, _) => {
-                    if impl_adt_def == adt_def {
-                        return true;
+            let provides = match (self_ty.kind(), app_ty.name) {
+                (&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))) => match (ty1, ty2) {
+                    (ast::IntTy::Isize, chalk_ir::IntTy::Isize)
+                    | (ast::IntTy::I8, chalk_ir::IntTy::I8)
+                    | (ast::IntTy::I16, chalk_ir::IntTy::I16)
+                    | (ast::IntTy::I32, chalk_ir::IntTy::I32)
+                    | (ast::IntTy::I64, chalk_ir::IntTy::I64)
+                    | (ast::IntTy::I128, chalk_ir::IntTy::I128) => true,
+                    _ => false,
+                },
+                (ty::Uint(ty1), Scalar(Uint(ty2))) => match (ty1, ty2) {
+                    (ast::UintTy::Usize, chalk_ir::UintTy::Usize)
+                    | (ast::UintTy::U8, chalk_ir::UintTy::U8)
+                    | (ast::UintTy::U16, chalk_ir::UintTy::U16)
+                    | (ast::UintTy::U32, chalk_ir::UintTy::U32)
+                    | (ast::UintTy::U64, chalk_ir::UintTy::U64)
+                    | (ast::UintTy::U128, chalk_ir::UintTy::U128) => true,
+                    _ => false,
+                },
+                (ty::Float(ty1), Scalar(Float(ty2))) => match (ty1, ty2) {
+                    (ast::FloatTy::F32, chalk_ir::FloatTy::F32)
+                    | (ast::FloatTy::F64, chalk_ir::FloatTy::F64) => true,
+                    _ => false,
+                },
+                (&ty::Tuple(..), Tuple(..)) => true,
+                (&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::Opaque(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
@@ -308,7 +401,7 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t
         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.tcx.associated_item(def_id);
+        let assoc_item = self.interner.tcx.associated_item(def_id);
         let impl_id = match assoc_item.container {
             AssocItemContainer::TraitContainer(def_id) => def_id,
             _ => unimplemented!("Not possible??"),
@@ -317,9 +410,9 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t
             AssocKind::Type => {}
             _ => unimplemented!("Not possible??"),
         }
-        let bound_vars = bound_vars_for_item(self.tcx, def_id);
+        let bound_vars = bound_vars_for_item(self.interner.tcx, def_id);
         let binders = binders_for(&self.interner, bound_vars);
-        let ty = self.tcx.type_of(def_id);
+        let ty = self.interner.tcx.type_of(def_id);
 
         Arc::new(chalk_solve::rust_ir::AssociatedTyValue {
             impl_id: chalk_ir::ImplId(impl_id),
@@ -346,78 +439,20 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t
         &self,
         opaque_ty_id: chalk_ir::OpaqueTyId<RustInterner<'tcx>>,
     ) -> Arc<chalk_solve::rust_ir::OpaqueTyDatum<RustInterner<'tcx>>> {
-        let bound_vars = bound_vars_for_item(self.tcx, opaque_ty_id.0);
+        let bound_vars = bound_vars_for_item(self.interner.tcx, opaque_ty_id.0);
         let binders = binders_for(&self.interner, bound_vars);
-        let predicates = self.tcx.predicates_defined_on(opaque_ty_id.0).predicates;
-        let where_clauses: Vec<_> = predicates
-            .iter()
-            .map(|(wc, _)| wc.subst(self.tcx, &bound_vars))
-            .filter_map(|wc| LowerInto::<Option<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>>>::lower_into(wc, &self.interner)).collect();
+        let where_clauses = self.where_clauses_for(opaque_ty_id.0, bound_vars);
 
         let value = chalk_solve::rust_ir::OpaqueTyDatumBound {
-            bounds: chalk_ir::Binders::new(binders, where_clauses),
+            bounds: chalk_ir::Binders::new(binders.clone(), vec![]),
+            where_clauses: chalk_ir::Binders::new(binders, where_clauses),
         };
         Arc::new(chalk_solve::rust_ir::OpaqueTyDatum {
             opaque_ty_id,
-            bound: chalk_ir::Binders::new(chalk_ir::VariableKinds::new(&self.interner), value),
+            bound: chalk_ir::Binders::empty(&self.interner, value),
         })
     }
 
-    /// Since Chalk can't handle all Rust types currently, we have to handle
-    /// some specially for now. Over time, these `Some` returns will change to
-    /// `None` and eventually this function will be removed.
-    fn force_impl_for(
-        &self,
-        well_known: chalk_solve::rust_ir::WellKnownTrait,
-        ty: &chalk_ir::TyData<RustInterner<'tcx>>,
-    ) -> Option<bool> {
-        use chalk_ir::TyData::*;
-        match well_known {
-            chalk_solve::rust_ir::WellKnownTrait::Sized => match ty {
-                Apply(apply) => match apply.name {
-                    chalk_ir::TypeName::Adt(chalk_ir::AdtId(adt_def)) => match adt_def.adt_kind() {
-                        ty::AdtKind::Struct | ty::AdtKind::Union => None,
-                        ty::AdtKind::Enum => {
-                            let constraint = self.tcx.adt_sized_constraint(adt_def.did);
-                            if !constraint.0.is_empty() { unimplemented!() } else { Some(true) }
-                        }
-                    },
-                    _ => None,
-                },
-                Dyn(_)
-                | Alias(_)
-                | Placeholder(_)
-                | Function(_)
-                | InferenceVar(_, _)
-                | BoundVar(_) => None,
-            },
-            chalk_solve::rust_ir::WellKnownTrait::Copy
-            | chalk_solve::rust_ir::WellKnownTrait::Clone => match ty {
-                Apply(apply) => match apply.name {
-                    chalk_ir::TypeName::Adt(chalk_ir::AdtId(adt_def)) => match adt_def.adt_kind() {
-                        ty::AdtKind::Struct | ty::AdtKind::Union => None,
-                        ty::AdtKind::Enum => {
-                            let constraint = self.tcx.adt_sized_constraint(adt_def.did);
-                            if !constraint.0.is_empty() { unimplemented!() } else { Some(true) }
-                        }
-                    },
-                    _ => None,
-                },
-                Dyn(_)
-                | Alias(_)
-                | Placeholder(_)
-                | Function(_)
-                | InferenceVar(_, _)
-                | BoundVar(_) => None,
-            },
-            chalk_solve::rust_ir::WellKnownTrait::Drop => None,
-            chalk_solve::rust_ir::WellKnownTrait::Fn => None,
-            chalk_solve::rust_ir::WellKnownTrait::FnMut => None,
-            chalk_solve::rust_ir::WellKnownTrait::FnOnce => None,
-            chalk_solve::rust_ir::WellKnownTrait::Unsize => None,
-        }
-    }
-
     fn program_clauses_for_env(
         &self,
         environment: &chalk_ir::Environment<RustInterner<'tcx>>,
@@ -430,21 +465,24 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t
         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 => self.tcx.lang_items().sized_trait(),
-            Copy => self.tcx.lang_items().copy_trait(),
-            Clone => self.tcx.lang_items().clone_trait(),
-            Drop => self.tcx.lang_items().drop_trait(),
-            Fn => self.tcx.lang_items().fn_trait(),
-            FnMut => self.tcx.lang_items().fn_mut_trait(),
-            FnOnce => self.tcx.lang_items().fn_once_trait(),
-            Unsize => self.tcx.lang_items().unsize_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(),
+            Unsize => lang_items.unsize_trait(),
+            Unpin => lang_items.unpin_trait(),
+            CoerceUnsized => lang_items.coerce_unsized_trait(),
         };
         def_id.map(chalk_ir::TraitId)
     }
 
     fn is_object_safe(&self, trait_id: chalk_ir::TraitId<RustInterner<'tcx>>) -> bool {
-        self.tcx.is_object_safe(trait_id.0)
+        self.interner.tcx.is_object_safe(trait_id.0)
     }
 
     fn hidden_opaque_type(
@@ -452,7 +490,10 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t
         _id: chalk_ir::OpaqueTyId<RustInterner<'tcx>>,
     ) -> chalk_ir::Ty<RustInterner<'tcx>> {
         // FIXME(chalk): actually get hidden ty
-        self.tcx.mk_ty(ty::Tuple(self.tcx.intern_substs(&[]))).lower_into(&self.interner)
+        self.interner
+            .tcx
+            .mk_ty(ty::Tuple(self.interner.tcx.intern_substs(&[])))
+            .lower_into(&self.interner)
     }
 
     fn closure_kind(
@@ -460,7 +501,7 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t
         _closure_id: chalk_ir::ClosureId<RustInterner<'tcx>>,
         substs: &chalk_ir::Substitution<RustInterner<'tcx>>,
     ) -> chalk_solve::rust_ir::ClosureKind {
-        let kind = &substs.parameters(&self.interner)[substs.len(&self.interner) - 3];
+        let kind = &substs.as_slice(&self.interner)[substs.len(&self.interner) - 3];
         match kind.assert_ty_ref(&self.interner).data(&self.interner) {
             chalk_ir::TyData::Apply(apply) => match apply.name {
                 chalk_ir::TypeName::Scalar(scalar) => match scalar {
@@ -484,10 +525,10 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t
         substs: &chalk_ir::Substitution<RustInterner<'tcx>>,
     ) -> chalk_ir::Binders<chalk_solve::rust_ir::FnDefInputsAndOutputDatum<RustInterner<'tcx>>>
     {
-        let sig = &substs.parameters(&self.interner)[substs.len(&self.interner) - 2];
+        let sig = &substs.as_slice(&self.interner)[substs.len(&self.interner) - 2];
         match sig.assert_ty_ref(&self.interner).data(&self.interner) {
             chalk_ir::TyData::Function(f) => {
-                let substitution = f.substitution.parameters(&self.interner);
+                let substitution = f.substitution.as_slice(&self.interner);
                 let return_type =
                     substitution.last().unwrap().assert_ty_ref(&self.interner).clone();
                 // Closure arguments are tupled
@@ -506,7 +547,7 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t
                 };
 
                 chalk_ir::Binders::new(
-                    chalk_ir::VariableKinds::from(
+                    chalk_ir::VariableKinds::from_iter(
                         &self.interner,
                         (0..f.num_binders).map(|_| chalk_ir::VariableKind::Lifetime),
                     ),
@@ -523,7 +564,7 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t
         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.parameters(&self.interner).last().unwrap().assert_ty_ref(&self.interner);
+        let tuple = substs.as_slice(&self.interner).last().unwrap().assert_ty_ref(&self.interner);
         inputs_and_output.map_ref(|_| tuple.clone())
     }
 
@@ -532,8 +573,8 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t
         _closure_id: chalk_ir::ClosureId<RustInterner<'tcx>>,
         substs: &chalk_ir::Substitution<RustInterner<'tcx>>,
     ) -> chalk_ir::Substitution<RustInterner<'tcx>> {
-        let substitution = &substs.parameters(&self.interner)[0..substs.len(&self.interner) - 3];
-        chalk_ir::Substitution::from(&self.interner, substitution)
+        let substitution = &substs.as_slice(&self.interner)[0..substs.len(&self.interner) - 3];
+        chalk_ir::Substitution::from_iter(&self.interner, substitution)
     }
 }
 
@@ -573,7 +614,7 @@ fn binders_for<'tcx>(
     interner: &RustInterner<'tcx>,
     bound_vars: SubstsRef<'tcx>,
 ) -> chalk_ir::VariableKinds<RustInterner<'tcx>> {
-    chalk_ir::VariableKinds::from(
+    chalk_ir::VariableKinds::from_iter(
         interner,
         bound_vars.iter().map(|arg| match arg.unpack() {
             ty::subst::GenericArgKind::Lifetime(_re) => chalk_ir::VariableKind::Lifetime,
diff --git a/compiler/rustc_traits/src/chalk/lowering.rs b/compiler/rustc_traits/src/chalk/lowering.rs
index a043fa3f4c8..1e1841a57f8 100644
--- a/compiler/rustc_traits/src/chalk/lowering.rs
+++ b/compiler/rustc_traits/src/chalk/lowering.rs
@@ -31,9 +31,7 @@
 //! not. To lower anything wrapped in a `Binder`, we first deeply find any bound
 //! variables from the current `Binder`.
 
-use rustc_middle::traits::{
-    ChalkEnvironmentAndGoal, ChalkEnvironmentClause, ChalkRustInterner as RustInterner,
-};
+use rustc_middle::traits::{ChalkEnvironmentAndGoal, ChalkRustInterner as RustInterner};
 use rustc_middle::ty::fold::TypeFolder;
 use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
 use rustc_middle::ty::{
@@ -41,10 +39,10 @@ use rustc_middle::ty::{
 };
 use rustc_span::def_id::DefId;
 
+use chalk_ir::{FnSig, ForeignDefId};
+use rustc_hir::Unsafety;
 use std::collections::btree_map::{BTreeMap, Entry};
 
-use chalk_ir::fold::shift::Shift;
-
 /// Essentially an `Into` with a `&RustInterner` parameter
 crate trait LowerInto<'tcx, T> {
     /// Lower a rustc construct (e.g., `ty::TraitPredicate`) to a chalk type, consuming `self`.
@@ -56,7 +54,13 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Substitution<RustInterner<'tcx>>> for Subst
         self,
         interner: &RustInterner<'tcx>,
     ) -> chalk_ir::Substitution<RustInterner<'tcx>> {
-        chalk_ir::Substitution::from(interner, self.iter().map(|s| s.lower_into(interner)))
+        chalk_ir::Substitution::from_iter(interner, self.iter().map(|s| s.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(self.iter(interner).map(|subst| subst.lower_into(interner)))
     }
 }
 
@@ -76,107 +80,51 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::InEnvironment<chalk_ir::Goal<RustInterner<'
         self,
         interner: &RustInterner<'tcx>,
     ) -> chalk_ir::InEnvironment<chalk_ir::Goal<RustInterner<'tcx>>> {
-        let clauses = self.environment.into_iter().filter_map(|clause| match clause {
-            ChalkEnvironmentClause::Predicate(predicate) => {
-                // FIXME(chalk): forall
-                match predicate.bound_atom(interner.tcx).skip_binder() {
-                    ty::PredicateAtom::Trait(predicate, _) => {
-                        let predicate = ty::Binder::bind(predicate);
-                        let (predicate, binders, _named_regions) =
-                            collect_bound_vars(interner, interner.tcx, &predicate);
-
-                        Some(
-                            chalk_ir::ProgramClauseData(chalk_ir::Binders::new(
-                                binders,
-                                chalk_ir::ProgramClauseImplication {
-                                    consequence: chalk_ir::DomainGoal::FromEnv(
-                                        chalk_ir::FromEnv::Trait(
-                                            predicate.trait_ref.lower_into(interner),
-                                        ),
-                                    ),
-                                    conditions: chalk_ir::Goals::new(interner),
-                                    priority: chalk_ir::ClausePriority::High,
-                                },
-                            ))
-                            .intern(interner),
-                        )
-                    }
-                    ty::PredicateAtom::RegionOutlives(predicate) => {
-                        let predicate = ty::Binder::bind(predicate);
-                        let (predicate, binders, _named_regions) =
-                            collect_bound_vars(interner, interner.tcx, &predicate);
-
-                        Some(
-                            chalk_ir::ProgramClauseData(chalk_ir::Binders::new(
-                                binders,
-                                chalk_ir::ProgramClauseImplication {
-                                    consequence: chalk_ir::DomainGoal::Holds(
-                                        chalk_ir::WhereClause::LifetimeOutlives(
-                                            chalk_ir::LifetimeOutlives {
-                                                a: predicate.0.lower_into(interner),
-                                                b: predicate.1.lower_into(interner),
-                                            },
-                                        ),
-                                    ),
-                                    conditions: chalk_ir::Goals::new(interner),
-                                    priority: chalk_ir::ClausePriority::High,
-                                },
-                            ))
-                            .intern(interner),
-                        )
-                    }
-                    // FIXME(chalk): need to add TypeOutlives
-                    ty::PredicateAtom::TypeOutlives(_) => None,
-                    ty::PredicateAtom::Projection(predicate) => {
-                        let predicate = ty::Binder::bind(predicate);
-                        let (predicate, binders, _named_regions) =
-                            collect_bound_vars(interner, interner.tcx, &predicate);
-
-                        Some(
-                            chalk_ir::ProgramClauseData(chalk_ir::Binders::new(
-                                binders,
-                                chalk_ir::ProgramClauseImplication {
-                                    consequence: chalk_ir::DomainGoal::Holds(
-                                        chalk_ir::WhereClause::AliasEq(
-                                            predicate.lower_into(interner),
-                                        ),
-                                    ),
-                                    conditions: chalk_ir::Goals::new(interner),
-                                    priority: chalk_ir::ClausePriority::High,
-                                },
-                            ))
-                            .intern(interner),
-                        )
-                    }
-                    ty::PredicateAtom::WellFormed(..)
-                    | ty::PredicateAtom::ObjectSafe(..)
-                    | ty::PredicateAtom::ClosureKind(..)
-                    | ty::PredicateAtom::Subtype(..)
-                    | ty::PredicateAtom::ConstEvaluatable(..)
-                    | ty::PredicateAtom::ConstEquate(..) => {
-                        bug!("unexpected predicate {}", predicate)
-                    }
+        let clauses = self.environment.into_iter().map(|predicate| {
+            let (predicate, binders, _named_regions) =
+                collect_bound_vars(interner, interner.tcx, &predicate.bound_atom(interner.tcx));
+            let consequence = match predicate {
+                ty::PredicateAtom::TypeWellFormedFromEnv(ty) => {
+                    chalk_ir::DomainGoal::FromEnv(chalk_ir::FromEnv::Ty(ty.lower_into(interner)))
                 }
-            }
-            ChalkEnvironmentClause::TypeFromEnv(ty) => Some(
-                chalk_ir::ProgramClauseData(chalk_ir::Binders::new(
-                    chalk_ir::VariableKinds::new(interner),
-                    chalk_ir::ProgramClauseImplication {
-                        consequence: chalk_ir::DomainGoal::FromEnv(chalk_ir::FromEnv::Ty(
-                            ty.lower_into(interner).shifted_in(interner),
-                        )),
-                        conditions: chalk_ir::Goals::new(interner),
-                        priority: chalk_ir::ClausePriority::High,
-                    },
-                ))
-                .intern(interner),
-            ),
+                ty::PredicateAtom::Trait(predicate, _) => chalk_ir::DomainGoal::FromEnv(
+                    chalk_ir::FromEnv::Trait(predicate.trait_ref.lower_into(interner)),
+                ),
+                ty::PredicateAtom::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::PredicateAtom::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::PredicateAtom::Projection(predicate) => chalk_ir::DomainGoal::Holds(
+                    chalk_ir::WhereClause::AliasEq(predicate.lower_into(interner)),
+                ),
+                ty::PredicateAtom::WellFormed(..)
+                | ty::PredicateAtom::ObjectSafe(..)
+                | ty::PredicateAtom::ClosureKind(..)
+                | ty::PredicateAtom::Subtype(..)
+                | ty::PredicateAtom::ConstEvaluatable(..)
+                | ty::PredicateAtom::ConstEquate(..) => 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(&interner, clauses),
+                clauses: chalk_ir::ProgramClauses::from_iter(&interner, clauses),
             },
             goal: goal.intern(&interner),
         }
@@ -185,63 +133,52 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::InEnvironment<chalk_ir::Goal<RustInterner<'
 
 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>> {
-        // FIXME(chalk): forall
-        match self.bound_atom(interner.tcx).skip_binder() {
+        let (predicate, binders, _named_regions) =
+            collect_bound_vars(interner, interner.tcx, &self.bound_atom(interner.tcx));
+
+        let value = match predicate {
             ty::PredicateAtom::Trait(predicate, _) => {
-                ty::Binder::bind(predicate).lower_into(interner)
+                chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(
+                    chalk_ir::WhereClause::Implemented(predicate.trait_ref.lower_into(interner)),
+                ))
             }
             ty::PredicateAtom::RegionOutlives(predicate) => {
-                let predicate = ty::Binder::bind(predicate);
-                let (predicate, binders, _named_regions) =
-                    collect_bound_vars(interner, interner.tcx, &predicate);
-
-                chalk_ir::GoalData::Quantified(
-                    chalk_ir::QuantifierKind::ForAll,
-                    chalk_ir::Binders::new(
-                        binders,
-                        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),
-                            }),
-                        ))
-                        .intern(interner),
-                    ),
-                )
+                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),
+                    }),
+                ))
             }
-            // FIXME(chalk): TypeOutlives
-            ty::PredicateAtom::TypeOutlives(_predicate) => {
-                chalk_ir::GoalData::All(chalk_ir::Goals::new(interner))
+            ty::PredicateAtom::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::PredicateAtom::Projection(predicate) => {
-                ty::Binder::bind(predicate).lower_into(interner)
+                chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(
+                    chalk_ir::WhereClause::AliasEq(predicate.lower_into(interner)),
+                ))
             }
             ty::PredicateAtom::WellFormed(arg) => match arg.unpack() {
-                GenericArgKind::Type(ty) => match ty.kind {
+                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::new(interner)),
-
-                    _ => {
-                        let (ty, binders, _named_regions) =
-                            collect_bound_vars(interner, interner.tcx, &ty::Binder::bind(ty));
-
-                        chalk_ir::GoalData::Quantified(
-                            chalk_ir::QuantifierKind::ForAll,
-                            chalk_ir::Binders::new(
-                                binders,
-                                chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::WellFormed(
-                                    chalk_ir::WellFormed::Ty(ty.lower_into(interner)),
-                                ))
-                                .intern(interner),
-                            ),
-                        )
+                    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::new(interner))
+                    chalk_ir::GoalData::All(chalk_ir::Goals::empty(interner))
                 }
                 GenericArgKind::Lifetime(lt) => bug!("unexpect well formed predicate: {:?}", lt),
             },
@@ -258,9 +195,17 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::GoalData<RustInterner<'tcx>>> for ty::Predi
             | ty::PredicateAtom::Subtype(..)
             | ty::PredicateAtom::ConstEvaluatable(..)
             | ty::PredicateAtom::ConstEquate(..) => {
-                chalk_ir::GoalData::All(chalk_ir::Goals::new(interner))
+                chalk_ir::GoalData::All(chalk_ir::Goals::empty(interner))
             }
-        }
+            ty::PredicateAtom::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)),
+        )
     }
 }
 
@@ -275,25 +220,6 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::TraitRef<RustInterner<'tcx>>>
     }
 }
 
-impl<'tcx> LowerInto<'tcx, chalk_ir::GoalData<RustInterner<'tcx>>>
-    for ty::PolyTraitPredicate<'tcx>
-{
-    fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::GoalData<RustInterner<'tcx>> {
-        let (ty, binders, _named_regions) = collect_bound_vars(interner, interner.tcx, &self);
-
-        chalk_ir::GoalData::Quantified(
-            chalk_ir::QuantifierKind::ForAll,
-            chalk_ir::Binders::new(
-                binders,
-                chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(
-                    chalk_ir::WhereClause::Implemented(ty.trait_ref.lower_into(interner)),
-                ))
-                .intern(interner),
-            ),
-        )
-    }
-}
-
 impl<'tcx> LowerInto<'tcx, chalk_ir::AliasEq<RustInterner<'tcx>>>
     for rustc_middle::ty::ProjectionPredicate<'tcx>
 {
@@ -305,25 +231,6 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::AliasEq<RustInterner<'tcx>>>
     }
 }
 
-impl<'tcx> LowerInto<'tcx, chalk_ir::GoalData<RustInterner<'tcx>>>
-    for ty::PolyProjectionPredicate<'tcx>
-{
-    fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::GoalData<RustInterner<'tcx>> {
-        let (ty, binders, _named_regions) = collect_bound_vars(interner, interner.tcx, &self);
-
-        chalk_ir::GoalData::Quantified(
-            chalk_ir::QuantifierKind::ForAll,
-            chalk_ir::Binders::new(
-                binders,
-                chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(
-                    chalk_ir::WhereClause::AliasEq(ty.lower_into(interner)),
-                ))
-                .intern(interner),
-            ),
-        )
-    }
-}
-
 impl<'tcx> LowerInto<'tcx, chalk_ir::Ty<RustInterner<'tcx>>> for Ty<'tcx> {
     fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::Ty<RustInterner<'tcx>> {
         use chalk_ir::TyData;
@@ -340,7 +247,7 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Ty<RustInterner<'tcx>>> for Ty<'tcx> {
         let uint = |i| apply(chalk_ir::TypeName::Scalar(chalk_ir::Scalar::Uint(i)), empty());
         let float = |f| apply(chalk_ir::TypeName::Scalar(chalk_ir::Scalar::Float(f)), empty());
 
-        match self.kind {
+        match *self.kind() {
             Bool => apply(chalk_ir::TypeName::Scalar(chalk_ir::Scalar::Bool), empty()),
             Char => apply(chalk_ir::TypeName::Scalar(chalk_ir::Scalar::Char), empty()),
             Int(ty) => match ty {
@@ -364,7 +271,7 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Ty<RustInterner<'tcx>>> for Ty<'tcx> {
                 ast::FloatTy::F64 => float(chalk_ir::FloatTy::F64),
             },
             Adt(def, substs) => apply(struct_ty(def.did), substs.lower_into(interner)),
-            Foreign(_def_id) => unimplemented!(),
+            Foreign(def_id) => apply(chalk_ir::TypeName::Foreign(ForeignDefId(def_id)), empty()),
             Str => apply(chalk_ir::TypeName::Str, empty()),
             Array(ty, len) => {
                 let value = match len.val {
@@ -381,7 +288,7 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Ty<RustInterner<'tcx>>> for Ty<'tcx> {
                 };
                 apply(
                     chalk_ir::TypeName::Array,
-                    chalk_ir::Substitution::from(
+                    chalk_ir::Substitution::from_iter(
                         interner,
                         &[
                             chalk_ir::GenericArgData::Ty(ty.lower_into(interner)).intern(interner),
@@ -415,7 +322,7 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Ty<RustInterner<'tcx>>> for Ty<'tcx> {
                 };
                 apply(
                     name,
-                    chalk_ir::Substitution::from(
+                    chalk_ir::Substitution::from_iter(
                         interner,
                         &[
                             chalk_ir::GenericArgData::Lifetime(region.lower_into(interner))
@@ -432,9 +339,10 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Ty<RustInterner<'tcx>>> for Ty<'tcx> {
             FnPtr(sig) => {
                 let (inputs_and_outputs, binders, _named_regions) =
                     collect_bound_vars(interner, interner.tcx, &sig.inputs_and_output());
-                TyData::Function(chalk_ir::Fn {
+                TyData::Function(chalk_ir::FnPointer {
                     num_binders: binders.len(interner),
-                    substitution: chalk_ir::Substitution::from(
+                    sig: sig.lower_into(interner),
+                    substitution: chalk_ir::Substitution::from_iter(
                         interner,
                         inputs_and_outputs.iter().map(|ty| {
                             chalk_ir::GenericArgData::Ty(ty.lower_into(interner)).intern(interner)
@@ -485,6 +393,112 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Ty<RustInterner<'tcx>>> for Ty<'tcx> {
     }
 }
 
+impl<'tcx> LowerInto<'tcx, Ty<'tcx>> for &chalk_ir::Ty<RustInterner<'tcx>> {
+    fn lower_into(self, interner: &RustInterner<'tcx>) -> Ty<'tcx> {
+        use chalk_ir::TyData;
+        use rustc_ast::ast;
+
+        let kind = match self.data(interner) {
+            TyData::Apply(application_ty) => match application_ty.name {
+                chalk_ir::TypeName::Adt(struct_id) => {
+                    ty::Adt(struct_id.0, application_ty.substitution.lower_into(interner))
+                }
+                chalk_ir::TypeName::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(ast::IntTy::Isize),
+                        chalk_ir::IntTy::I8 => ty::Int(ast::IntTy::I8),
+                        chalk_ir::IntTy::I16 => ty::Int(ast::IntTy::I16),
+                        chalk_ir::IntTy::I32 => ty::Int(ast::IntTy::I32),
+                        chalk_ir::IntTy::I64 => ty::Int(ast::IntTy::I64),
+                        chalk_ir::IntTy::I128 => ty::Int(ast::IntTy::I128),
+                    },
+                    chalk_ir::Scalar::Uint(int_ty) => match int_ty {
+                        chalk_ir::UintTy::Usize => ty::Uint(ast::UintTy::Usize),
+                        chalk_ir::UintTy::U8 => ty::Uint(ast::UintTy::U8),
+                        chalk_ir::UintTy::U16 => ty::Uint(ast::UintTy::U16),
+                        chalk_ir::UintTy::U32 => ty::Uint(ast::UintTy::U32),
+                        chalk_ir::UintTy::U64 => ty::Uint(ast::UintTy::U64),
+                        chalk_ir::UintTy::U128 => ty::Uint(ast::UintTy::U128),
+                    },
+                    chalk_ir::Scalar::Float(float_ty) => match float_ty {
+                        chalk_ir::FloatTy::F32 => ty::Float(ast::FloatTy::F32),
+                        chalk_ir::FloatTy::F64 => ty::Float(ast::FloatTy::F64),
+                    },
+                },
+                chalk_ir::TypeName::Array => unimplemented!(),
+                chalk_ir::TypeName::FnDef(id) => {
+                    ty::FnDef(id.0, application_ty.substitution.lower_into(interner))
+                }
+                chalk_ir::TypeName::Closure(closure) => {
+                    ty::Closure(closure.0, application_ty.substitution.lower_into(interner))
+                }
+                chalk_ir::TypeName::Never => ty::Never,
+                chalk_ir::TypeName::Tuple(_size) => {
+                    ty::Tuple(application_ty.substitution.lower_into(interner))
+                }
+                chalk_ir::TypeName::Slice => ty::Slice(
+                    application_ty.substitution.as_slice(interner)[0]
+                        .ty(interner)
+                        .unwrap()
+                        .lower_into(interner),
+                ),
+                chalk_ir::TypeName::Raw(mutbl) => ty::RawPtr(ty::TypeAndMut {
+                    ty: application_ty.substitution.as_slice(interner)[0]
+                        .ty(interner)
+                        .unwrap()
+                        .lower_into(interner),
+                    mutbl: match mutbl {
+                        chalk_ir::Mutability::Mut => ast::Mutability::Mut,
+                        chalk_ir::Mutability::Not => ast::Mutability::Not,
+                    },
+                }),
+                chalk_ir::TypeName::Ref(mutbl) => ty::Ref(
+                    application_ty.substitution.as_slice(interner)[0]
+                        .lifetime(interner)
+                        .unwrap()
+                        .lower_into(interner),
+                    application_ty.substitution.as_slice(interner)[1]
+                        .ty(interner)
+                        .unwrap()
+                        .lower_into(interner),
+                    match mutbl {
+                        chalk_ir::Mutability::Mut => ast::Mutability::Mut,
+                        chalk_ir::Mutability::Not => ast::Mutability::Not,
+                    },
+                ),
+                chalk_ir::TypeName::Str => ty::Str,
+                chalk_ir::TypeName::OpaqueType(opaque_ty) => {
+                    ty::Opaque(opaque_ty.0, application_ty.substitution.lower_into(interner))
+                }
+                chalk_ir::TypeName::AssociatedType(assoc_ty) => ty::Projection(ty::ProjectionTy {
+                    substs: application_ty.substitution.lower_into(interner),
+                    item_def_id: assoc_ty.0,
+                }),
+                chalk_ir::TypeName::Foreign(def_id) => ty::Foreign(def_id.0),
+                chalk_ir::TypeName::Error => unimplemented!(),
+            },
+            TyData::Placeholder(placeholder) => ty::Placeholder(ty::Placeholder {
+                universe: ty::UniverseIndex::from_usize(placeholder.ui.counter),
+                name: ty::BoundVar::from_usize(placeholder.idx),
+            }),
+            TyData::Alias(_alias_ty) => unimplemented!(),
+            TyData::Function(_quantified_ty) => unimplemented!(),
+            TyData::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,
+                },
+            ),
+            TyData::InferenceVar(_, _) => unimplemented!(),
+            TyData::Dyn(_) => unimplemented!(),
+        };
+        interner.tcx.mk_ty(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>> {
         use rustc_middle::ty::RegionKind::*;
@@ -522,6 +536,59 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Lifetime<RustInterner<'tcx>>> for Region<'t
     }
 }
 
+impl<'tcx> LowerInto<'tcx, Region<'tcx>> for &chalk_ir::Lifetime<RustInterner<'tcx>> {
+    fn lower_into(self, interner: &RustInterner<'tcx>) -> Region<'tcx> {
+        let kind = match self.data(interner) {
+            chalk_ir::LifetimeData::BoundVar(var) => ty::RegionKind::ReLateBound(
+                ty::DebruijnIndex::from_u32(var.debruijn.depth()),
+                ty::BoundRegion::BrAnon(var.index as u32),
+            ),
+            chalk_ir::LifetimeData::InferenceVar(_var) => unimplemented!(),
+            chalk_ir::LifetimeData::Placeholder(p) => {
+                ty::RegionKind::RePlaceholder(ty::Placeholder {
+                    universe: ty::UniverseIndex::from_usize(p.ui.counter),
+                    name: ty::BoundRegion::BrAnon(p.idx as u32),
+                })
+            }
+            chalk_ir::LifetimeData::Phantom(_, _) => unimplemented!(),
+        };
+        interner.tcx.mk_region(kind)
+    }
+}
+
+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.val {
+            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 val = 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),
+        };
+        ty::Const { ty, val }
+    }
+}
+
 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() {
@@ -531,18 +598,35 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::GenericArg<RustInterner<'tcx>>> for Generic
             ty::subst::GenericArgKind::Lifetime(lifetime) => {
                 chalk_ir::GenericArgData::Lifetime(lifetime.lower_into(interner))
             }
-            ty::subst::GenericArgKind::Const(_) => chalk_ir::GenericArgData::Ty(
-                chalk_ir::TyData::Apply(chalk_ir::ApplicationTy {
-                    name: chalk_ir::TypeName::Tuple(0),
-                    substitution: chalk_ir::Substitution::empty(interner),
-                })
-                .intern(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);
+                interner.tcx.mk_const(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
@@ -554,41 +638,39 @@ impl<'tcx> LowerInto<'tcx, Option<chalk_ir::QuantifiedWhereClause<RustInterner<'
         self,
         interner: &RustInterner<'tcx>,
     ) -> Option<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>> {
-        // FIXME(chalk): forall
-        match self.bound_atom(interner.tcx).skip_binder() {
+        let (predicate, binders, _named_regions) =
+            collect_bound_vars(interner, interner.tcx, &self.bound_atom(interner.tcx));
+        let value = match predicate {
             ty::PredicateAtom::Trait(predicate, _) => {
-                let predicate = ty::Binder::bind(predicate);
-                let (predicate, binders, _named_regions) =
-                    collect_bound_vars(interner, interner.tcx, &predicate);
-
-                Some(chalk_ir::Binders::new(
-                    binders,
-                    chalk_ir::WhereClause::Implemented(predicate.trait_ref.lower_into(interner)),
-                ))
+                Some(chalk_ir::WhereClause::Implemented(predicate.trait_ref.lower_into(interner)))
             }
             ty::PredicateAtom::RegionOutlives(predicate) => {
-                let predicate = ty::Binder::bind(predicate);
-                let (predicate, binders, _named_regions) =
-                    collect_bound_vars(interner, interner.tcx, &predicate);
-
-                Some(chalk_ir::Binders::new(
-                    binders,
-                    chalk_ir::WhereClause::LifetimeOutlives(chalk_ir::LifetimeOutlives {
-                        a: predicate.0.lower_into(interner),
-                        b: predicate.1.lower_into(interner),
-                    }),
-                ))
+                Some(chalk_ir::WhereClause::LifetimeOutlives(chalk_ir::LifetimeOutlives {
+                    a: predicate.0.lower_into(interner),
+                    b: predicate.1.lower_into(interner),
+                }))
+            }
+            ty::PredicateAtom::TypeOutlives(predicate) => {
+                Some(chalk_ir::WhereClause::TypeOutlives(chalk_ir::TypeOutlives {
+                    ty: predicate.0.lower_into(interner),
+                    lifetime: predicate.1.lower_into(interner),
+                }))
+            }
+            ty::PredicateAtom::Projection(predicate) => {
+                Some(chalk_ir::WhereClause::AliasEq(predicate.lower_into(interner)))
             }
-            ty::PredicateAtom::TypeOutlives(_predicate) => None,
-            ty::PredicateAtom::Projection(_predicate) => None,
             ty::PredicateAtom::WellFormed(_ty) => None,
 
             ty::PredicateAtom::ObjectSafe(..)
             | ty::PredicateAtom::ClosureKind(..)
             | ty::PredicateAtom::Subtype(..)
             | ty::PredicateAtom::ConstEvaluatable(..)
-            | ty::PredicateAtom::ConstEquate(..) => bug!("unexpected predicate {}", &self),
-        }
+            | ty::PredicateAtom::ConstEquate(..)
+            | ty::PredicateAtom::TypeWellFormedFromEnv(..) => {
+                bug!("unexpected predicate {}", &self)
+            }
+        };
+        value.map(|value| chalk_ir::Binders::new(binders, value))
     }
 }
 
@@ -601,30 +683,51 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Binders<chalk_ir::QuantifiedWhereClauses<Ru
     ) -> chalk_ir::Binders<chalk_ir::QuantifiedWhereClauses<RustInterner<'tcx>>> {
         let (predicates, binders, _named_regions) =
             collect_bound_vars(interner, interner.tcx, &self);
+        let self_ty = interner.tcx.mk_ty(ty::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| match predicate {
             ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef { def_id, substs }) => {
                 chalk_ir::Binders::new(
-                    chalk_ir::VariableKinds::new(interner),
+                    chalk_ir::VariableKinds::empty(interner),
                     chalk_ir::WhereClause::Implemented(chalk_ir::TraitRef {
                         trait_id: chalk_ir::TraitId(def_id),
-                        substitution: substs.lower_into(interner),
+                        substitution: interner
+                            .tcx
+                            .mk_substs_trait(self_ty, substs)
+                            .lower_into(interner),
                     }),
                 )
             }
             ty::ExistentialPredicate::Projection(_predicate) => unimplemented!(),
             ty::ExistentialPredicate::AutoTrait(def_id) => chalk_ir::Binders::new(
-                chalk_ir::VariableKinds::new(interner),
+                chalk_ir::VariableKinds::empty(interner),
                 chalk_ir::WhereClause::Implemented(chalk_ir::TraitRef {
                     trait_id: chalk_ir::TraitId(def_id),
-                    substitution: chalk_ir::Substitution::empty(interner),
+                    substitution: interner.tcx.mk_substs_trait(self_ty, &[]).lower_into(interner),
                 }),
             ),
         });
-        let value = chalk_ir::QuantifiedWhereClauses::from(interner, where_clauses);
+        let value = chalk_ir::QuantifiedWhereClauses::from_iter(interner, where_clauses);
         chalk_ir::Binders::new(binders, value)
     }
 }
 
+impl<'tcx> LowerInto<'tcx, chalk_ir::FnSig<RustInterner<'tcx>>> for ty::Binder<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(),
+        }
+    }
+}
+
 /// To collect bound vars, we have to do two passes. In the first pass, we
 /// collect all `BoundRegion`s and `ty::Bound`s. In the second pass, we then
 /// replace `BrNamed` into `BrAnon`. The two separate passes are important,
@@ -662,7 +765,8 @@ crate fn collect_bound_vars<'a, 'tcx, T: TypeFoldable<'tcx>>(
             .or_else(|| bug!("Skipped bound var index: ty={:?}, parameters={:?}", ty, parameters));
     });
 
-    let binders = chalk_ir::VariableKinds::from(interner, parameters.into_iter().map(|(_, v)| v));
+    let binders =
+        chalk_ir::VariableKinds::from_iter(interner, parameters.into_iter().map(|(_, v)| v));
 
     (new_ty, binders, named_parameters)
 }
@@ -692,7 +796,7 @@ impl<'tcx> TypeVisitor<'tcx> for BoundVarsCollector<'tcx> {
     }
 
     fn visit_ty(&mut self, t: Ty<'tcx>) -> bool {
-        match t.kind {
+        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) => {
@@ -773,10 +877,6 @@ impl<'a, 'tcx> TypeFolder<'tcx> for NamedBoundVarSubstitutor<'a, 'tcx> {
         result
     }
 
-    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
-        t.super_fold_with(self)
-    }
-
     fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> {
         match r {
             ty::ReLateBound(index, br) if *index == self.binder_index => match br {
@@ -807,16 +907,18 @@ crate struct ParamsSubstitutor<'tcx> {
     tcx: TyCtxt<'tcx>,
     binder_index: ty::DebruijnIndex,
     list: Vec<rustc_middle::ty::ParamTy>,
+    next_ty_placeholder: usize,
     crate params: rustc_data_structures::fx::FxHashMap<usize, rustc_middle::ty::ParamTy>,
     crate named_regions: BTreeMap<DefId, u32>,
 }
 
 impl<'tcx> ParamsSubstitutor<'tcx> {
-    crate fn new(tcx: TyCtxt<'tcx>) -> Self {
+    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(),
         }
@@ -836,19 +938,19 @@ impl<'tcx> TypeFolder<'tcx> for ParamsSubstitutor<'tcx> {
     }
 
     fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
-        match t.kind {
+        match *t.kind() {
             // FIXME(chalk): currently we convert params to placeholders starting at
             // index `0`. To support placeholders, we'll actually need to do a
             // first pass to collect placeholders. Then we can insert params after.
             ty::Placeholder(_) => unimplemented!(),
             ty::Param(param) => match self.list.iter().position(|r| r == &param) {
-                Some(_idx) => self.tcx.mk_ty(ty::Placeholder(ty::PlaceholderType {
+                Some(idx) => self.tcx.mk_ty(ty::Placeholder(ty::PlaceholderType {
                     universe: ty::UniverseIndex::from_usize(0),
-                    name: ty::BoundVar::from_usize(_idx),
+                    name: ty::BoundVar::from_usize(idx),
                 })),
                 None => {
                     self.list.push(param);
-                    let idx = self.list.len() - 1;
+                    let idx = self.list.len() - 1 + self.next_ty_placeholder;
                     self.params.insert(idx, param);
                     self.tcx.mk_ty(ty::Placeholder(ty::PlaceholderType {
                         universe: ty::UniverseIndex::from_usize(0),
@@ -884,3 +986,83 @@ impl<'tcx> TypeFolder<'tcx> for ParamsSubstitutor<'tcx> {
         }
     }
 }
+
+/// Used to collect `Placeholder`s.
+crate struct PlaceholdersCollector {
+    universe_index: ty::UniverseIndex,
+    crate next_ty_placeholder: usize,
+    crate next_anon_region_placeholder: u32,
+}
+
+impl PlaceholdersCollector {
+    crate fn new() -> Self {
+        PlaceholdersCollector {
+            universe_index: ty::UniverseIndex::ROOT,
+            next_ty_placeholder: 0,
+            next_anon_region_placeholder: 0,
+        }
+    }
+}
+
+impl<'tcx> TypeVisitor<'tcx> for PlaceholdersCollector {
+    fn visit_ty(&mut self, t: Ty<'tcx>) -> bool {
+        match t.kind() {
+            ty::Placeholder(p) if p.universe == self.universe_index => {
+                self.next_ty_placeholder = self.next_ty_placeholder.max(p.name.as_usize() + 1);
+            }
+
+            _ => (),
+        };
+
+        t.super_visit_with(self)
+    }
+
+    fn visit_region(&mut self, r: Region<'tcx>) -> bool {
+        match r {
+            ty::RePlaceholder(p) if p.universe == self.universe_index => {
+                if let ty::BoundRegion::BrAnon(anon) = p.name {
+                    self.next_anon_region_placeholder = self.next_anon_region_placeholder.max(anon);
+                }
+            }
+
+            _ => (),
+        };
+
+        r.super_visit_with(self)
+    }
+}
+
+/// Used to substitute specific `Regions`s with placeholders.
+crate struct RegionsSubstitutor<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    restatic_placeholder: ty::Region<'tcx>,
+    reempty_placeholder: ty::Region<'tcx>,
+}
+
+impl<'tcx> RegionsSubstitutor<'tcx> {
+    crate fn new(
+        tcx: TyCtxt<'tcx>,
+        restatic_placeholder: ty::Region<'tcx>,
+        reempty_placeholder: ty::Region<'tcx>,
+    ) -> Self {
+        RegionsSubstitutor { tcx, restatic_placeholder, reempty_placeholder }
+    }
+}
+
+impl<'tcx> TypeFolder<'tcx> for RegionsSubstitutor<'tcx> {
+    fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
+        self.tcx
+    }
+
+    fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> {
+        match r {
+            ty::ReStatic => self.restatic_placeholder,
+            ty::ReEmpty(ui) => {
+                assert_eq!(ui.as_usize(), 0);
+                self.reempty_placeholder
+            }
+
+            _ => r.super_fold_with(self),
+        }
+    }
+}
diff --git a/compiler/rustc_traits/src/chalk/mod.rs b/compiler/rustc_traits/src/chalk/mod.rs
index f18b4ca65f6..63c5b884357 100644
--- a/compiler/rustc_traits/src/chalk/mod.rs
+++ b/compiler/rustc_traits/src/chalk/mod.rs
@@ -1,8 +1,7 @@
 //! Calls `chalk-solve` to solve a `ty::Predicate`
 //!
-//! In order to call `chalk-solve`, this file must convert a
-//! `ChalkCanonicalGoal` into a Chalk ucanonical goal. It then calls Chalk, and
-//! converts the answer back into rustc solution.
+//! 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.
 
 crate mod db;
 crate mod lowering;
@@ -15,17 +14,17 @@ use rustc_middle::infer::canonical::{CanonicalTyVarKind, CanonicalVarKind};
 use rustc_middle::traits::ChalkRustInterner;
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::subst::GenericArg;
-use rustc_middle::ty::{
-    self, Bound, BoundVar, ParamTy, Region, RegionKind, Ty, TyCtxt, TypeFoldable,
-};
+use rustc_middle::ty::{self, BoundVar, ParamTy, TyCtxt, TypeFoldable};
 
 use rustc_infer::infer::canonical::{
     Canonical, CanonicalVarValues, Certainty, QueryRegionConstraints, QueryResponse,
 };
-use rustc_infer::traits::{self, ChalkCanonicalGoal};
+use rustc_infer::traits::{self, CanonicalChalkEnvironmentAndGoal};
 
 use crate::chalk::db::RustIrDatabase as ChalkRustIrDatabase;
-use crate::chalk::lowering::{LowerInto, ParamsSubstitutor};
+use crate::chalk::lowering::{
+    LowerInto, ParamsSubstitutor, PlaceholdersCollector, RegionsSubstitutor,
+};
 
 use chalk_solve::Solution;
 
@@ -35,21 +34,40 @@ crate fn provide(p: &mut Providers) {
 
 crate fn evaluate_goal<'tcx>(
     tcx: TyCtxt<'tcx>,
-    obligation: ChalkCanonicalGoal<'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 params_substitutor = ParamsSubstitutor::new(tcx);
+    let mut placeholders_collector = PlaceholdersCollector::new();
+    obligation.visit_with(&mut placeholders_collector);
+
+    let restatic_placeholder = tcx.mk_region(ty::RegionKind::RePlaceholder(ty::Placeholder {
+        universe: ty::UniverseIndex::ROOT,
+        name: ty::BoundRegion::BrAnon(placeholders_collector.next_anon_region_placeholder),
+    }));
+    let reempty_placeholder = tcx.mk_region(ty::RegionKind::RePlaceholder(ty::Placeholder {
+        universe: ty::UniverseIndex::ROOT,
+        name: ty::BoundRegion::BrAnon(placeholders_collector.next_anon_region_placeholder + 1),
+    }));
+
+    let mut params_substitutor =
+        ParamsSubstitutor::new(tcx, placeholders_collector.next_ty_placeholder);
     let obligation = obligation.fold_with(&mut params_substitutor);
+    // FIXME(chalk): we really should be substituting these back in the solution
     let _params: FxHashMap<usize, ParamTy> = params_substitutor.params;
+
+    let mut regions_substitutor =
+        RegionsSubstitutor::new(tcx, restatic_placeholder, reempty_placeholder);
+    let obligation = obligation.fold_with(&mut regions_substitutor);
+
     let max_universe = obligation.max_universe.index();
 
-    let _lowered_goal: chalk_ir::UCanonical<
+    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(
+            binders: chalk_ir::CanonicalVarKinds::from_iter(
                 &interner,
                 obligation.variables.iter().map(|v| match v.kind {
                     CanonicalVarKind::PlaceholderTy(_ty) => unimplemented!(),
@@ -81,108 +99,20 @@ crate fn evaluate_goal<'tcx>(
         universes: max_universe + 1,
     };
 
-    let solver_choice = chalk_solve::SolverChoice::SLG { max_size: 32, expected_answers: None };
-    let mut solver = solver_choice.into_solver::<ChalkRustInterner<'tcx>>();
-
-    let db = ChalkRustIrDatabase { tcx, interner };
-    let solution = solver.solve(&db, &_lowered_goal);
+    use chalk_solve::Solver;
+    let mut solver = chalk_engine::solve::SLGSolver::new(32, None);
+    let db = ChalkRustIrDatabase { interner, restatic_placeholder, reempty_placeholder };
+    let solution = chalk_solve::logging::with_tracing_logs(|| solver.solve(&db, &lowered_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<_>| {
+    let make_solution = |subst: chalk_ir::Substitution<_>| {
         let mut var_values: IndexVec<BoundVar, GenericArg<'tcx>> = IndexVec::new();
-        _subst.parameters(&interner).iter().for_each(|p| {
-            // FIXME(chalk): we should move this elsewhere, since this is
-            // essentially inverse of lowering a `GenericArg`.
-            let _data = p.data(&interner);
-            match _data {
-                chalk_ir::GenericArgData::Ty(_t) => {
-                    use chalk_ir::TyData;
-                    use rustc_ast as ast;
-
-                    let _data = _t.data(&interner);
-                    let kind = match _data {
-                        TyData::Apply(_application_ty) => match _application_ty.name {
-                            chalk_ir::TypeName::Adt(_struct_id) => unimplemented!(),
-                            chalk_ir::TypeName::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(ast::IntTy::Isize),
-                                    chalk_ir::IntTy::I8 => ty::Int(ast::IntTy::I8),
-                                    chalk_ir::IntTy::I16 => ty::Int(ast::IntTy::I16),
-                                    chalk_ir::IntTy::I32 => ty::Int(ast::IntTy::I32),
-                                    chalk_ir::IntTy::I64 => ty::Int(ast::IntTy::I64),
-                                    chalk_ir::IntTy::I128 => ty::Int(ast::IntTy::I128),
-                                },
-                                chalk_ir::Scalar::Uint(int_ty) => match int_ty {
-                                    chalk_ir::UintTy::Usize => ty::Uint(ast::UintTy::Usize),
-                                    chalk_ir::UintTy::U8 => ty::Uint(ast::UintTy::U8),
-                                    chalk_ir::UintTy::U16 => ty::Uint(ast::UintTy::U16),
-                                    chalk_ir::UintTy::U32 => ty::Uint(ast::UintTy::U32),
-                                    chalk_ir::UintTy::U64 => ty::Uint(ast::UintTy::U64),
-                                    chalk_ir::UintTy::U128 => ty::Uint(ast::UintTy::U128),
-                                },
-                                chalk_ir::Scalar::Float(float_ty) => match float_ty {
-                                    chalk_ir::FloatTy::F32 => ty::Float(ast::FloatTy::F32),
-                                    chalk_ir::FloatTy::F64 => ty::Float(ast::FloatTy::F64),
-                                },
-                            },
-                            chalk_ir::TypeName::Array => unimplemented!(),
-                            chalk_ir::TypeName::FnDef(_) => unimplemented!(),
-                            chalk_ir::TypeName::Closure(_) => unimplemented!(),
-                            chalk_ir::TypeName::Never => unimplemented!(),
-                            chalk_ir::TypeName::Tuple(_size) => unimplemented!(),
-                            chalk_ir::TypeName::Slice => unimplemented!(),
-                            chalk_ir::TypeName::Raw(_) => unimplemented!(),
-                            chalk_ir::TypeName::Ref(_) => unimplemented!(),
-                            chalk_ir::TypeName::Str => unimplemented!(),
-                            chalk_ir::TypeName::OpaqueType(_ty) => unimplemented!(),
-                            chalk_ir::TypeName::AssociatedType(_assoc_ty) => unimplemented!(),
-                            chalk_ir::TypeName::Error => unimplemented!(),
-                        },
-                        TyData::Placeholder(_placeholder) => {
-                            unimplemented!();
-                        }
-                        TyData::Alias(_alias_ty) => unimplemented!(),
-                        TyData::Function(_quantified_ty) => unimplemented!(),
-                        TyData::BoundVar(_bound) => Bound(
-                            ty::DebruijnIndex::from_usize(_bound.debruijn.depth() as usize),
-                            ty::BoundTy {
-                                var: ty::BoundVar::from_usize(_bound.index),
-                                kind: ty::BoundTyKind::Anon,
-                            },
-                        ),
-                        TyData::InferenceVar(_, _) => unimplemented!(),
-                        TyData::Dyn(_) => unimplemented!(),
-                    };
-                    let _ty: Ty<'_> = tcx.mk_ty(kind);
-                    let _arg: GenericArg<'_> = _ty.into();
-                    var_values.push(_arg);
-                }
-                chalk_ir::GenericArgData::Lifetime(_l) => {
-                    let _data = _l.data(&interner);
-                    let _lifetime: Region<'_> = match _data {
-                        chalk_ir::LifetimeData::BoundVar(_var) => {
-                            tcx.mk_region(RegionKind::ReLateBound(
-                                rustc_middle::ty::DebruijnIndex::from_usize(
-                                    _var.debruijn.depth() as usize
-                                ),
-                                rustc_middle::ty::BoundRegion::BrAnon(_var.index as u32),
-                            ))
-                        }
-                        chalk_ir::LifetimeData::InferenceVar(_var) => unimplemented!(),
-                        chalk_ir::LifetimeData::Placeholder(_index) => unimplemented!(),
-                        chalk_ir::LifetimeData::Phantom(_, _) => unimplemented!(),
-                    };
-                    let _arg: GenericArg<'_> = _lifetime.into();
-                    var_values.push(_arg);
-                }
-                chalk_ir::GenericArgData::Const(_) => unimplemented!(),
-            }
+        subst.as_slice(&interner).iter().for_each(|p| {
+            var_values.push(p.lower_into(&interner));
         });
         let sol = Canonical {
             max_universe: ty::UniverseIndex::from_usize(0),
@@ -194,17 +124,17 @@ crate fn evaluate_goal<'tcx>(
                 value: (),
             },
         };
-        &*tcx.arena.alloc(sol)
+        tcx.arena.alloc(sol)
     };
     solution
         .map(|s| match s {
-            Solution::Unique(_subst) => {
+            Solution::Unique(subst) => {
                 // FIXME(chalk): handle constraints
-                make_solution(_subst.value.subst)
+                make_solution(subst.value.subst)
             }
-            Solution::Ambig(_guidance) => {
-                match _guidance {
-                    chalk_solve::Guidance::Definite(_subst) => make_solution(_subst.value),
+            Solution::Ambig(guidance) => {
+                match guidance {
+                    chalk_solve::Guidance::Definite(subst) => make_solution(subst.value),
                     chalk_solve::Guidance::Suggested(_) => unimplemented!(),
                     chalk_solve::Guidance::Unknown => {
                         // chalk_fulfill doesn't use the var_values here, so
diff --git a/compiler/rustc_traits/src/dropck_outlives.rs b/compiler/rustc_traits/src/dropck_outlives.rs
index ce00060b9b1..3ee391d6dc7 100644
--- a/compiler/rustc_traits/src/dropck_outlives.rs
+++ b/compiler/rustc_traits/src/dropck_outlives.rs
@@ -112,7 +112,7 @@ fn dropck_outlives<'tcx>(
 
                             debug!("dropck_outlives: ty from dtorck_types = {:?}", ty);
 
-                            match ty.kind {
+                            match ty.kind() {
                                 // All parameters live for the duration of the
                                 // function.
                                 ty::Param(..) => {}
@@ -172,7 +172,7 @@ fn dtorck_constraint_for_ty<'tcx>(
         return Ok(());
     }
 
-    match ty.kind {
+    match ty.kind() {
         ty::Bool
         | ty::Char
         | ty::Int(_)
diff --git a/compiler/rustc_traits/src/implied_outlives_bounds.rs b/compiler/rustc_traits/src/implied_outlives_bounds.rs
index de3096eac9b..79308b032ec 100644
--- a/compiler/rustc_traits/src/implied_outlives_bounds.rs
+++ b/compiler/rustc_traits/src/implied_outlives_bounds.rs
@@ -103,7 +103,8 @@ fn compute_implied_outlives_bounds<'tcx>(
                     | ty::PredicateAtom::ClosureKind(..)
                     | ty::PredicateAtom::ObjectSafe(..)
                     | ty::PredicateAtom::ConstEvaluatable(..)
-                    | ty::PredicateAtom::ConstEquate(..) => vec![],
+                    | ty::PredicateAtom::ConstEquate(..)
+                    | ty::PredicateAtom::TypeWellFormedFromEnv(..) => vec![],
                     ty::PredicateAtom::WellFormed(arg) => {
                         wf_args.push(arg);
                         vec![]
diff --git a/compiler/rustc_traits/src/lib.rs b/compiler/rustc_traits/src/lib.rs
index 6fea4732dda..d0b05beb4e6 100644
--- a/compiler/rustc_traits/src/lib.rs
+++ b/compiler/rustc_traits/src/lib.rs
@@ -4,7 +4,6 @@
 #![feature(crate_visibility_modifier)]
 #![feature(in_band_lifetimes)]
 #![feature(nll)]
-#![feature(or_patterns)]
 #![recursion_limit = "256"]
 
 #[macro_use]
diff --git a/compiler/rustc_traits/src/normalize_erasing_regions.rs b/compiler/rustc_traits/src/normalize_erasing_regions.rs
index 83aee31a39f..3e7c9ac62eb 100644
--- a/compiler/rustc_traits/src/normalize_erasing_regions.rs
+++ b/compiler/rustc_traits/src/normalize_erasing_regions.rs
@@ -49,6 +49,7 @@ fn not_outlives_predicate(p: &ty::Predicate<'tcx>) -> bool {
         | ty::PredicateAtom::ClosureKind(..)
         | ty::PredicateAtom::Subtype(..)
         | ty::PredicateAtom::ConstEvaluatable(..)
-        | ty::PredicateAtom::ConstEquate(..) => true,
+        | ty::PredicateAtom::ConstEquate(..)
+        | ty::PredicateAtom::TypeWellFormedFromEnv(..) => true,
     }
 }
diff --git a/compiler/rustc_ty/src/instance.rs b/compiler/rustc_ty/src/instance.rs
index d0bd88af1ff..75bf8ea0bb8 100644
--- a/compiler/rustc_ty/src/instance.rs
+++ b/compiler/rustc_ty/src/instance.rs
@@ -53,7 +53,7 @@ fn inner_resolve_instance<'tcx>(
         let ty = tcx.type_of(def.def_id_for_type_of());
         let item_type = tcx.subst_and_normalize_erasing_regions(substs, param_env, &ty);
 
-        let def = match item_type.kind {
+        let def = match *item_type.kind() {
             ty::FnDef(..)
                 if {
                     let f = item_type.fn_sig(tcx);
@@ -68,7 +68,7 @@ fn inner_resolve_instance<'tcx>(
 
                 if ty.needs_drop(tcx, param_env) {
                     debug!(" => nontrivial drop glue");
-                    match ty.kind {
+                    match *ty.kind() {
                         ty::Closure(..)
                         | ty::Generator(..)
                         | ty::Tuple(..)
@@ -231,7 +231,7 @@ fn resolve_associated_item<'tcx>(
                 trait_closure_kind,
             ))
         }
-        traits::ImplSourceFnPointer(ref data) => match data.fn_ty.kind {
+        traits::ImplSourceFnPointer(ref data) => match data.fn_ty.kind() {
             ty::FnDef(..) | ty::FnPtr(..) => Some(Instance {
                 def: ty::InstanceDef::FnPtrShim(trait_item.def_id, data.fn_ty),
                 substs: rcvr_substs,
@@ -250,7 +250,7 @@ fn resolve_associated_item<'tcx>(
                     let self_ty = trait_ref.self_ty();
 
                     let is_copy = self_ty.is_copy_modulo_regions(tcx.at(DUMMY_SP), param_env);
-                    match self_ty.kind {
+                    match self_ty.kind() {
                         _ if is_copy => (),
                         ty::Array(..) | ty::Closure(..) | ty::Tuple(..) => {}
                         _ => return Ok(None),
diff --git a/compiler/rustc_ty/src/lib.rs b/compiler/rustc_ty/src/lib.rs
index 6e9042d1ba7..904c0062a92 100644
--- a/compiler/rustc_ty/src/lib.rs
+++ b/compiler/rustc_ty/src/lib.rs
@@ -4,8 +4,7 @@
 //!
 //! This API is completely unstable and subject to change.
 
-#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
-#![feature(bool_to_option)]
+#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![feature(nll)]
 #![recursion_limit = "256"]
 
diff --git a/compiler/rustc_ty/src/needs_drop.rs b/compiler/rustc_ty/src/needs_drop.rs
index c4af95205fe..0356bcec549 100644
--- a/compiler/rustc_ty/src/needs_drop.rs
+++ b/compiler/rustc_ty/src/needs_drop.rs
@@ -90,7 +90,7 @@ where
             };
 
             for component in components {
-                match component.kind {
+                match *component.kind() {
                     _ if component.is_copy_modulo_regions(tcx.at(DUMMY_SP), self.param_env) => (),
 
                     ty::Closure(_, substs) => {
@@ -106,7 +106,7 @@ where
                         }
 
                         let witness = substs.witness();
-                        let interior_tys = match &witness.kind {
+                        let interior_tys = match witness.kind() {
                             ty::GeneratorWitness(tys) => tcx.erase_late_bound_regions(tys),
                             _ => {
                                 tcx.sess.delay_span_bug(
diff --git a/compiler/rustc_ty/src/ty.rs b/compiler/rustc_ty/src/ty.rs
index 0f1dee7e2e0..c4b6b64339a 100644
--- a/compiler/rustc_ty/src/ty.rs
+++ b/compiler/rustc_ty/src/ty.rs
@@ -1,3 +1,4 @@
+use rustc_data_structures::fx::FxIndexSet;
 use rustc_data_structures::svh::Svh;
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
@@ -5,7 +6,9 @@ use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE};
 use rustc_infer::traits::util;
 use rustc_middle::hir::map as hir_map;
 use rustc_middle::ty::subst::{InternalSubsts, Subst};
-use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, WithConstness};
+use rustc_middle::ty::{
+    self, Binder, Predicate, PredicateAtom, PredicateKind, ToPredicate, Ty, TyCtxt, WithConstness,
+};
 use rustc_session::CrateDisambiguator;
 use rustc_span::symbol::Symbol;
 use rustc_span::Span;
@@ -18,7 +21,7 @@ fn sized_constraint_for_ty<'tcx>(
 ) -> Vec<Ty<'tcx>> {
     use ty::TyKind::*;
 
-    let result = match ty.kind {
+    let result = match ty.kind() {
         Bool | Char | Int(..) | Uint(..) | Float(..) | RawPtr(..) | Ref(..) | FnDef(..)
         | FnPtr(_) | Array(..) | Closure(..) | Generator(..) | Never => vec![],
 
@@ -245,7 +248,7 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> {
     }
     // Compute the bounds on Self and the type parameters.
 
-    let ty::InstantiatedPredicates { predicates, .. } =
+    let ty::InstantiatedPredicates { mut predicates, .. } =
         tcx.predicates_of(def_id).instantiate_identity(tcx);
 
     // Finally, we have to normalize the bounds in the environment, in
@@ -260,11 +263,13 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> {
     // are any errors at that point, so after type checking you can be
     // sure that this will succeed without errors anyway.
 
-    let unnormalized_env = ty::ParamEnv::new(
-        tcx.intern_predicates(&predicates),
-        traits::Reveal::UserFacing,
-        tcx.sess.opts.debugging_opts.chalk.then_some(def_id),
-    );
+    if tcx.sess.opts.debugging_opts.chalk {
+        let environment = well_formed_types_in_env(tcx, def_id);
+        predicates.extend(environment);
+    }
+
+    let unnormalized_env =
+        ty::ParamEnv::new(tcx.intern_predicates(&predicates), traits::Reveal::UserFacing);
 
     let body_id = def_id
         .as_local()
@@ -276,6 +281,122 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> {
     traits::normalize_param_env_or_error(tcx, def_id, unnormalized_env, cause)
 }
 
+/// 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>(
+    tcx: TyCtxt<'tcx>,
+    def_id: DefId,
+) -> &'tcx ty::List<Predicate<'tcx>> {
+    use rustc_hir::{ForeignItemKind, ImplItemKind, ItemKind, Node, TraitItemKind};
+    use rustc_middle::ty::subst::GenericArgKind;
+
+    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);
+    }
+
+    // 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 hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
+    let node = tcx.hir().get(hir_id);
+
+    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 { of_trait: Some(_), .. } => NodeKind::TraitImpl,
+            ItemKind::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");
+
+            // 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);
+            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);
+            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() {
+            GenericArgKind::Type(ty) => {
+                let binder = Binder::dummy(PredicateAtom::TypeWellFormedFromEnv(ty));
+                Some(tcx.mk_predicate(PredicateKind::ForAll(binder)))
+            }
+
+            // FIXME(eddyb) no WF conditions from lifetimes?
+            GenericArgKind::Lifetime(_) => None,
+
+            // FIXME(eddyb) support const generics in Chalk
+            GenericArgKind::Const(_) => None,
+        }
+    });
+
+    tcx.mk_predicates(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)
 }
@@ -344,7 +465,7 @@ fn issue33140_self_ty(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Ty<'_>> {
     }
 
     let self_ty = trait_ref.self_ty();
-    let self_ty_matches = match self_ty.kind {
+    let self_ty_matches = match self_ty.kind() {
         ty::Dynamic(ref data, ty::ReStatic) => data.principal().is_none(),
         _ => false,
     };
@@ -398,21 +519,21 @@ fn associated_type_projection_predicates(
         let pred = obligation.predicate;
         match pred.skip_binders() {
             ty::PredicateAtom::Trait(tr, _) => {
-                if let ty::Projection(p) = tr.self_ty().kind {
+                if let ty::Projection(p) = *tr.self_ty().kind() {
                     if p == assoc_item_ty {
                         return Some(pred);
                     }
                 }
             }
             ty::PredicateAtom::Projection(proj) => {
-                if let ty::Projection(p) = proj.projection_ty.self_ty().kind {
+                if let ty::Projection(p) = *proj.projection_ty.self_ty().kind() {
                     if p == assoc_item_ty {
                         return Some(pred);
                     }
                 }
             }
             ty::PredicateAtom::TypeOutlives(outlives) => {
-                if let ty::Projection(p) = outlives.0.kind {
+                if let ty::Projection(p) = *outlives.0.kind() {
                     if p == assoc_item_ty {
                         return Some(pred);
                     }
@@ -449,14 +570,15 @@ fn opaque_type_projection_predicates(
         let pred = obligation.predicate;
         match pred.skip_binders() {
             ty::PredicateAtom::Trait(tr, _) => {
-                if let ty::Opaque(opaque_def_id, opaque_substs) = tr.self_ty().kind {
+                if let ty::Opaque(opaque_def_id, opaque_substs) = *tr.self_ty().kind() {
                     if opaque_def_id == def_id && opaque_substs == substs {
                         return Some(pred);
                     }
                 }
             }
             ty::PredicateAtom::Projection(proj) => {
-                if let ty::Opaque(opaque_def_id, opaque_substs) = proj.projection_ty.self_ty().kind
+                if let ty::Opaque(opaque_def_id, opaque_substs) =
+                    *proj.projection_ty.self_ty().kind()
                 {
                     if opaque_def_id == def_id && opaque_substs == substs {
                         return Some(pred);
@@ -464,7 +586,7 @@ fn opaque_type_projection_predicates(
                 }
             }
             ty::PredicateAtom::TypeOutlives(outlives) => {
-                if let ty::Opaque(opaque_def_id, opaque_substs) = outlives.0.kind {
+                if let ty::Opaque(opaque_def_id, opaque_substs) = *outlives.0.kind() {
                     if opaque_def_id == def_id && opaque_substs == substs {
                         return Some(pred);
                     }
diff --git a/compiler/rustc_typeck/Cargo.toml b/compiler/rustc_typeck/Cargo.toml
index 0a6bfaef431..e3ba0bea7e8 100644
--- a/compiler/rustc_typeck/Cargo.toml
+++ b/compiler/rustc_typeck/Cargo.toml
@@ -11,6 +11,7 @@ doctest = false
 [dependencies]
 rustc_arena = { path = "../rustc_arena" }
 tracing = "0.1"
+rustc_macros = { path = "../rustc_macros" }
 rustc_middle = { path = "../rustc_middle" }
 rustc_attr = { path = "../rustc_attr" }
 rustc_data_structures = { path = "../rustc_data_structures" }
diff --git a/compiler/rustc_typeck/src/astconv/generics.rs b/compiler/rustc_typeck/src/astconv/generics.rs
index 84dab6de958..b54de1d0916 100644
--- a/compiler/rustc_typeck/src/astconv/generics.rs
+++ b/compiler/rustc_typeck/src/astconv/generics.rs
@@ -1,8 +1,9 @@
 use crate::astconv::{
     AstConv, ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, GenericArgPosition,
 };
+use crate::errors::AssocTypeBindingNotAllowed;
 use rustc_ast::ast::ParamKindOrd;
-use rustc_errors::{pluralize, struct_span_err, DiagnosticId, ErrorReported};
+use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorReported};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_hir::{GenericArg, GenericArgs};
@@ -367,7 +368,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         }
 
         if position != GenericArgPosition::Type && !args.bindings.is_empty() {
-            Self::prohibit_assoc_ty_binding(tcx, args.bindings[0].span);
+            AstConv::prohibit_assoc_ty_binding(tcx, args.bindings[0].span);
         }
 
         let explicit_late_bound =
@@ -392,7 +393,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             }
 
             if silent {
-                return Err(true);
+                return Err((0i32, None));
             }
 
             // Unfortunately lifetime and type parameter mismatches are typically styled
@@ -441,16 +442,16 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             for span in spans {
                 err.span_label(span, label.as_str());
             }
-            err.emit();
 
-            Err(true)
+            assert_ne!(bound, provided);
+            Err((bound as i32 - provided as i32, Some(err)))
         };
 
-        let mut arg_count_correct = Ok(());
         let mut unexpected_spans = vec![];
 
+        let mut lifetime_count_correct = Ok(());
         if !infer_lifetimes || arg_counts.lifetimes > param_counts.lifetimes {
-            arg_count_correct = check_kind_count(
+            lifetime_count_correct = check_kind_count(
                 "lifetime",
                 param_counts.lifetimes,
                 param_counts.lifetimes,
@@ -458,12 +459,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 0,
                 &mut unexpected_spans,
                 explicit_late_bound == ExplicitLateBound::Yes,
-            )
-            .and(arg_count_correct);
+            );
         }
+
         // FIXME(const_generics:defaults)
+        let mut const_count_correct = Ok(());
         if !infer_args || arg_counts.consts > param_counts.consts {
-            arg_count_correct = check_kind_count(
+            const_count_correct = check_kind_count(
                 "const",
                 param_counts.consts,
                 param_counts.consts,
@@ -471,13 +473,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 arg_counts.lifetimes + arg_counts.types,
                 &mut unexpected_spans,
                 false,
-            )
-            .and(arg_count_correct);
+            );
         }
+
         // Note that type errors are currently be emitted *after* const errors.
+        let mut type_count_correct = Ok(());
         if !infer_args || arg_counts.types > param_counts.types - defaults.types - has_self as usize
         {
-            arg_count_correct = check_kind_count(
+            type_count_correct = check_kind_count(
                 "type",
                 param_counts.types - defaults.types - has_self as usize,
                 param_counts.types - has_self as usize,
@@ -485,14 +488,54 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 arg_counts.lifetimes,
                 &mut unexpected_spans,
                 false,
-            )
-            .and(arg_count_correct);
+            );
+        }
+
+        // Emit a help message if it's possible that a type could be surrounded in braces
+        if let Err((c_mismatch, Some(ref mut _const_err))) = const_count_correct {
+            if let Err((_, Some(ref mut type_err))) = type_count_correct {
+                let possible_matches = args.args[arg_counts.lifetimes..]
+                    .iter()
+                    .filter(|arg| {
+                        matches!(
+                            arg,
+                            GenericArg::Type(hir::Ty { kind: hir::TyKind::Path { .. }, .. })
+                        )
+                    })
+                    .take(c_mismatch.max(0) as usize);
+                for arg in possible_matches {
+                    let suggestions = vec![
+                        (arg.span().shrink_to_lo(), String::from("{ ")),
+                        (arg.span().shrink_to_hi(), String::from(" }")),
+                    ];
+                    type_err.multipart_suggestion(
+                        "If this generic argument was intended as a const parameter, \
+                        try surrounding it with braces:",
+                        suggestions,
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+            }
         }
 
+        let emit_correct =
+            |correct: Result<(), (_, Option<rustc_errors::DiagnosticBuilder<'_>>)>| match correct {
+                Ok(()) => Ok(()),
+                Err((_, None)) => Err(()),
+                Err((_, Some(mut err))) => {
+                    err.emit();
+                    Err(())
+                }
+            };
+
+        let arg_count_correct = emit_correct(lifetime_count_correct)
+            .and(emit_correct(const_count_correct))
+            .and(emit_correct(type_count_correct));
+
         GenericArgCountResult {
             explicit_late_bound,
-            correct: arg_count_correct.map_err(|reported_err| GenericArgCountMismatch {
-                reported: if reported_err { Some(ErrorReported) } else { None },
+            correct: arg_count_correct.map_err(|()| GenericArgCountMismatch {
+                reported: Some(ErrorReported),
                 invalid_args: unexpected_spans,
             }),
         }
@@ -544,13 +587,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
 
     /// Emits an error regarding forbidden type binding associations
     pub fn prohibit_assoc_ty_binding(tcx: TyCtxt<'_>, span: Span) {
-        let mut err = struct_span_err!(
-            tcx.sess,
-            span,
-            E0229,
-            "associated type bindings are not allowed here"
-        );
-        err.span_label(span, "associated type not allowed here").emit();
+        tcx.sess.emit_err(AssocTypeBindingNotAllowed { span });
     }
 
     /// Prohibits explicit lifetime arguments if late-bound lifetime parameters
diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs
index 80dd26e9154..a743dc1cd20 100644
--- a/compiler/rustc_typeck/src/astconv/mod.rs
+++ b/compiler/rustc_typeck/src/astconv/mod.rs
@@ -7,6 +7,10 @@ mod generics;
 
 use crate::bounds::Bounds;
 use crate::collect::PlaceholderHirTyCollector;
+use crate::errors::{
+    AmbiguousLifetimeBound, MultipleRelaxedDefaultBounds, TraitObjectDeclaredWithNoTraits,
+    TypeofReservedKeywordUsed, ValueOfAssociatedStructAlreadySpecified,
+};
 use crate::middle::resolve_lifetime as rl;
 use crate::require_c_abi_if_c_variadic;
 use rustc_ast::util::lev_distance::find_best_match_for_name;
@@ -684,14 +688,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 if unbound.is_none() {
                     unbound = Some(&ptr.trait_ref);
                 } else {
-                    struct_span_err!(
-                        tcx.sess,
-                        span,
-                        E0203,
-                        "type parameter has more than one relaxed default \
-                        bound, only one is supported"
-                    )
-                    .emit();
+                    tcx.sess.emit_err(MultipleRelaxedDefaultBounds { span });
                 }
             }
         }
@@ -927,18 +924,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             dup_bindings
                 .entry(assoc_ty.def_id)
                 .and_modify(|prev_span| {
-                    struct_span_err!(
-                        self.tcx().sess,
-                        binding.span,
-                        E0719,
-                        "the value of the associated type `{}` (from trait `{}`) \
-                         is already specified",
-                        binding.item_name,
-                        tcx.def_path_str(assoc_ty.container.id())
-                    )
-                    .span_label(binding.span, "re-bound here")
-                    .span_label(*prev_span, format!("`{}` bound here first", binding.item_name))
-                    .emit();
+                    self.tcx().sess.emit_err(ValueOfAssociatedStructAlreadySpecified {
+                        span: binding.span,
+                        prev_span: *prev_span,
+                        item_name: binding.item_name,
+                        def_path: tcx.def_path_str(assoc_ty.container.id()),
+                    });
                 })
                 .or_insert(binding.span);
         }
@@ -1051,13 +1042,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         }
 
         if regular_traits.is_empty() && auto_traits.is_empty() {
-            struct_span_err!(
-                tcx.sess,
-                span,
-                E0224,
-                "at least one trait is required for an object type"
-            )
-            .emit();
+            tcx.sess.emit_err(TraitObjectDeclaredWithNoTraits { span });
             return tcx.ty_error();
         }
 
@@ -1454,7 +1439,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
 
         // Check if we have an enum variant.
         let mut variant_resolution = None;
-        if let ty::Adt(adt_def, _) = qself_ty.kind {
+        if let ty::Adt(adt_def, _) = qself_ty.kind() {
             if adt_def.is_enum() {
                 let variant_def = adt_def
                     .variants
@@ -1474,8 +1459,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
 
         // Find the type of the associated item, and the trait where the associated
         // item is declared.
-        let bound = match (&qself_ty.kind, qself_res) {
-            (_, Res::SelfTy(Some(_), Some(impl_def_id))) => {
+        let bound = match (&qself_ty.kind(), qself_res) {
+            (_, Res::SelfTy(Some(_), Some((impl_def_id, _)))) => {
                 // `Self` in an impl of a trait -- we have a concrete self type and a
                 // trait reference.
                 let trait_ref = match tcx.impl_trait_ref(impl_def_id) {
@@ -1932,12 +1917,29 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 self.prohibit_generics(path.segments);
                 tcx.types.self_param
             }
-            Res::SelfTy(_, Some(def_id)) => {
+            Res::SelfTy(_, Some((def_id, forbid_generic))) => {
                 // `Self` in impl (we know the concrete type).
                 assert_eq!(opt_self_ty, None);
                 self.prohibit_generics(path.segments);
                 // Try to evaluate any array length constants.
-                self.normalize_ty(span, tcx.at(span).type_of(def_id))
+                let normalized_ty = self.normalize_ty(span, tcx.at(span).type_of(def_id));
+                if forbid_generic && normalized_ty.needs_subst() {
+                    let mut err = tcx.sess.struct_span_err(
+                        path.span,
+                        "generic `Self` types are currently not permitted in anonymous constants",
+                    );
+                    if let Some(hir::Node::Item(&hir::Item {
+                        kind: hir::ItemKind::Impl { self_ty, .. },
+                        ..
+                    })) = tcx.hir().get_if_local(def_id)
+                    {
+                        err.span_note(self_ty.span, "not a concrete type");
+                    }
+                    err.emit();
+                    tcx.ty_error()
+                } else {
+                    normalized_ty
+                }
             }
             Res::Def(DefKind::AssocTy, def_id) => {
                 debug_assert!(path.segments.len() >= 2);
@@ -2059,15 +2061,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 self.normalize_ty(ast_ty.span, array_ty)
             }
             hir::TyKind::Typeof(ref _e) => {
-                struct_span_err!(
-                    tcx.sess,
-                    ast_ty.span,
-                    E0516,
-                    "`typeof` is a reserved keyword but unimplemented"
-                )
-                .span_label(ast_ty.span, "reserved keyword")
-                .emit();
-
+                tcx.sess.emit_err(TypeofReservedKeywordUsed { span: ast_ty.span });
                 tcx.ty_error()
             }
             hir::TyKind::Infer => {
@@ -2283,13 +2277,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         // error.
         let r = derived_region_bounds[0];
         if derived_region_bounds[1..].iter().any(|r1| r != *r1) {
-            struct_span_err!(
-                tcx.sess,
-                span,
-                E0227,
-                "ambiguous lifetime bound, explicit lifetime bound required"
-            )
-            .emit();
+            tcx.sess.emit_err(AmbiguousLifetimeBound { span });
         }
         Some(r)
     }
diff --git a/compiler/rustc_typeck/src/check/_match.rs b/compiler/rustc_typeck/src/check/_match.rs
index afd4413069e..7cb23dc0537 100644
--- a/compiler/rustc_typeck/src/check/_match.rs
+++ b/compiler/rustc_typeck/src/check/_match.rs
@@ -1,12 +1,15 @@
 use crate::check::coercion::CoerceMany;
 use crate::check::{Diverges, Expectation, FnCtxt, Needs};
-use rustc_hir as hir;
-use rustc_hir::ExprKind;
+use rustc_hir::{self as hir, ExprKind};
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
-use rustc_middle::ty::Ty;
+use rustc_infer::traits::Obligation;
+use rustc_middle::ty::{self, ToPredicate, Ty};
 use rustc_span::Span;
-use rustc_trait_selection::traits::ObligationCauseCode;
-use rustc_trait_selection::traits::{IfExpressionCause, MatchExpressionArmCause, ObligationCause};
+use rustc_trait_selection::opaque_types::InferCtxtExt as _;
+use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
+use rustc_trait_selection::traits::{
+    IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
+};
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     pub fn check_match(
@@ -14,7 +17,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expr: &'tcx hir::Expr<'tcx>,
         scrut: &'tcx hir::Expr<'tcx>,
         arms: &'tcx [hir::Arm<'tcx>],
-        expected: Expectation<'tcx>,
+        orig_expected: Expectation<'tcx>,
         match_src: hir::MatchSource,
     ) -> Ty<'tcx> {
         let tcx = self.tcx;
@@ -22,7 +25,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         use hir::MatchSource::*;
         let (source_if, if_no_else, force_scrutinee_bool) = match match_src {
             IfDesugar { contains_else_clause } => (true, !contains_else_clause, true),
-            IfLetDesugar { contains_else_clause } => (true, !contains_else_clause, false),
+            IfLetDesugar { contains_else_clause, .. } => (true, !contains_else_clause, false),
             WhileDesugar => (false, false, true),
             _ => (false, false, false),
         };
@@ -69,7 +72,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // type in that case)
         let mut all_arms_diverge = Diverges::WarnedAlways;
 
-        let expected = expected.adjust_for_branches(self);
+        let expected = orig_expected.adjust_for_branches(self);
 
         let mut coercion = {
             let coerce_first = match expected {
@@ -112,6 +115,60 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 self.check_expr_with_expectation(&arm.body, expected)
             };
             all_arms_diverge &= self.diverges.get();
+
+            // When we have a `match` as a tail expression in a `fn` with a returned `impl Trait`
+            // we check if the different arms would work with boxed trait objects instead and
+            // provide a structured suggestion in that case.
+            let opt_suggest_box_span = match (
+                orig_expected,
+                self.ret_coercion_impl_trait.map(|ty| (self.body_id.owner, ty)),
+            ) {
+                (Expectation::ExpectHasType(expected), Some((id, ty)))
+                    if self.in_tail_expr && self.can_coerce(arm_ty, expected) =>
+                {
+                    let impl_trait_ret_ty = self.infcx.instantiate_opaque_types(
+                        id,
+                        self.body_id,
+                        self.param_env,
+                        &ty,
+                        arm.body.span,
+                    );
+                    let mut suggest_box = !impl_trait_ret_ty.obligations.is_empty();
+                    for o in impl_trait_ret_ty.obligations {
+                        match o.predicate.skip_binders_unchecked() {
+                            ty::PredicateAtom::Trait(t, constness) => {
+                                let pred = ty::PredicateAtom::Trait(
+                                    ty::TraitPredicate {
+                                        trait_ref: ty::TraitRef {
+                                            def_id: t.def_id(),
+                                            substs: self.infcx.tcx.mk_substs_trait(arm_ty, &[]),
+                                        },
+                                    },
+                                    constness,
+                                );
+                                let obl = Obligation::new(
+                                    o.cause.clone(),
+                                    self.param_env,
+                                    pred.to_predicate(self.infcx.tcx),
+                                );
+                                suggest_box &= self.infcx.predicate_must_hold_modulo_regions(&obl);
+                                if !suggest_box {
+                                    // We've encountered some obligation that didn't hold, so the
+                                    // return expression can't just be boxed. We don't need to
+                                    // evaluate the rest of the obligations.
+                                    break;
+                                }
+                            }
+                            _ => {}
+                        }
+                    }
+                    // If all the obligations hold (or there are no obligations) the tail expression
+                    // we can suggest to return a boxed trait object instead of an opaque type.
+                    if suggest_box { self.ret_type_span } else { None }
+                }
+                _ => None,
+            };
+
             if source_if {
                 let then_expr = &arms[0].body;
                 match (i, if_no_else) {
@@ -119,7 +176,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     (_, true) => {} // Handled above to avoid duplicated type errors (#60254).
                     (_, _) => {
                         let then_ty = prior_arm_ty.unwrap();
-                        let cause = self.if_cause(expr.span, then_expr, &arm.body, then_ty, arm_ty);
+                        let cause = self.if_cause(
+                            expr.span,
+                            then_expr,
+                            &arm.body,
+                            then_ty,
+                            arm_ty,
+                            opt_suggest_box_span,
+                        );
                         coercion.coerce(self, &cause, &arm.body, arm_ty);
                     }
                 }
@@ -142,6 +206,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             prior_arms: other_arms.clone(),
                             last_ty: prior_arm_ty.unwrap(),
                             scrut_hir_id: scrut.hir_id,
+                            opt_suggest_box_span,
                         }),
                     ),
                 };
@@ -266,6 +331,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         else_expr: &'tcx hir::Expr<'tcx>,
         then_ty: Ty<'tcx>,
         else_ty: Ty<'tcx>,
+        opt_suggest_box_span: Option<Span>,
     ) -> ObligationCause<'tcx> {
         let mut outer_sp = if self.tcx.sess.source_map().is_multiline(span) {
             // The `if`/`else` isn't in one line in the output, include some context to make it
@@ -353,8 +419,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             error_sp,
             ObligationCauseCode::IfExpression(box IfExpressionCause {
                 then: then_sp,
+                else_sp: error_sp,
                 outer: outer_sp,
                 semicolon: remove_semicolon,
+                opt_suggest_box_span,
             }),
         )
     }
diff --git a/compiler/rustc_typeck/src/check/autoderef.rs b/compiler/rustc_typeck/src/check/autoderef.rs
index 97d2b3e5a8e..59c366ad7d7 100644
--- a/compiler/rustc_typeck/src/check/autoderef.rs
+++ b/compiler/rustc_typeck/src/check/autoderef.rs
@@ -12,7 +12,18 @@ use std::iter;
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     pub fn autoderef(&'a self, span: Span, base_ty: Ty<'tcx>) -> Autoderef<'a, 'tcx> {
-        Autoderef::new(self, self.param_env, self.body_id, span, base_ty)
+        Autoderef::new(self, self.param_env, self.body_id, span, base_ty, span)
+    }
+
+    /// Like `autoderef`, but provides a custom `Span` to use for calls to
+    /// an overloaded `Deref` operator
+    pub fn autoderef_overloaded_span(
+        &'a self,
+        span: Span,
+        base_ty: Ty<'tcx>,
+        overloaded_span: Span,
+    ) -> Autoderef<'a, 'tcx> {
+        Autoderef::new(self, self.param_env, self.body_id, span, base_ty, overloaded_span)
     }
 
     pub fn try_overloaded_deref(
@@ -43,8 +54,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     self.try_overloaded_deref(autoderef.span(), source).and_then(
                         |InferOk { value: method, obligations: o }| {
                             obligations.extend(o);
-                            if let ty::Ref(region, _, mutbl) = method.sig.output().kind {
-                                Some(OverloadedDeref { region, mutbl })
+                            if let ty::Ref(region, _, mutbl) = *method.sig.output().kind() {
+                                Some(OverloadedDeref {
+                                    region,
+                                    mutbl,
+                                    span: autoderef.overloaded_span(),
+                                })
                             } else {
                                 None
                             }
diff --git a/compiler/rustc_typeck/src/check/callee.rs b/compiler/rustc_typeck/src/check/callee.rs
index 4ba64035ca4..740783aeb9d 100644
--- a/compiler/rustc_typeck/src/check/callee.rs
+++ b/compiler/rustc_typeck/src/check/callee.rs
@@ -114,7 +114,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         );
 
         // If the callee is a bare function or a closure, then we're all set.
-        match adjusted_ty.kind {
+        match *adjusted_ty.kind() {
             ty::FnDef(..) | ty::FnPtr(_) => {
                 let adjustments = self.adjust_steps(autoderef);
                 self.apply_adjustments(callee_expr, adjustments);
@@ -223,12 +223,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 if borrow {
                     // Check for &self vs &mut self in the method signature. Since this is either
                     // the Fn or FnMut trait, it should be one of those.
-                    let (region, mutbl) = if let ty::Ref(r, _, mutbl) = method.sig.inputs()[0].kind
-                    {
-                        (r, mutbl)
-                    } else {
-                        span_bug!(call_expr.span, "input to call/call_mut is not a ref?");
-                    };
+                    let (region, mutbl) =
+                        if let ty::Ref(r, _, mutbl) = method.sig.inputs()[0].kind() {
+                            (r, mutbl)
+                        } else {
+                            span_bug!(call_expr.span, "input to call/call_mut is not a ref?");
+                        };
 
                     let mutbl = match mutbl {
                         hir::Mutability::Not => AutoBorrowMutability::Not,
@@ -285,7 +285,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         arg_exprs: &'tcx [hir::Expr<'tcx>],
         expected: Expectation<'tcx>,
     ) -> Ty<'tcx> {
-        let (fn_sig, def_span) = match callee_ty.kind {
+        let (fn_sig, def_span) = match *callee_ty.kind() {
             ty::FnDef(def_id, _) => {
                 (callee_ty.fn_sig(self.tcx), self.tcx.hir().span_if_local(def_id))
             }
diff --git a/compiler/rustc_typeck/src/check/cast.rs b/compiler/rustc_typeck/src/check/cast.rs
index e41314e8ab0..5c2bdb86f76 100644
--- a/compiler/rustc_typeck/src/check/cast.rs
+++ b/compiler/rustc_typeck/src/check/cast.rs
@@ -97,7 +97,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             return Ok(Some(PointerKind::Thin));
         }
 
-        Ok(match t.kind {
+        Ok(match *t.kind() {
             ty::Slice(_) | ty::Str => Some(PointerKind::Length),
             ty::Dynamic(ref tty, ..) => Some(PointerKind::Vtable(tty.principal_def_id())),
             ty::Adt(def, substs) if def.is_struct() => match def.non_enum_variant().fields.last() {
@@ -203,7 +203,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
         // For better error messages, check for some obviously unsized
         // cases now. We do a more thorough check at the end, once
         // inference is more completely known.
-        match cast_ty.kind {
+        match cast_ty.kind() {
             ty::Dynamic(..) | ty::Slice(..) => {
                 check.report_cast_to_unsized_type(fcx);
                 Err(ErrorReported)
@@ -348,7 +348,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
                     fcx.ty_to_string(self.cast_ty)
                 );
                 let mut sugg = None;
-                if let ty::Ref(reg, _, mutbl) = self.cast_ty.kind {
+                if let ty::Ref(reg, _, mutbl) = *self.cast_ty.kind() {
                     if fcx
                         .try_coerce(
                             self.expr,
@@ -370,7 +370,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
                         Applicability::MachineApplicable,
                     );
                 } else if !matches!(
-                    self.cast_ty.kind,
+                    self.cast_ty.kind(),
                     ty::FnDef(..) | ty::FnPtr(..) | ty::Closure(..)
                 ) {
                     let mut label = true;
@@ -474,7 +474,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
             fcx.resolve_vars_if_possible(&self.expr_ty),
             tstr
         );
-        match self.expr_ty.kind {
+        match self.expr_ty.kind() {
             ty::Ref(_, _, mt) => {
                 let mtstr = mt.prefix_str();
                 if self.cast_ty.is_trait() {
@@ -602,7 +602,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
             (Some(t_from), Some(t_cast)) => (t_from, t_cast),
             // Function item types may need to be reified before casts.
             (None, Some(t_cast)) => {
-                match self.expr_ty.kind {
+                match *self.expr_ty.kind() {
                     ty::FnDef(..) => {
                         // Attempt a coercion to a fn pointer type.
                         let f = fcx.normalize_associated_types_in(
@@ -629,7 +629,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
                     // a cast.
                     ty::Ref(_, inner_ty, mutbl) => {
                         return match t_cast {
-                            Int(_) | Float => match inner_ty.kind {
+                            Int(_) | Float => match *inner_ty.kind() {
                                 ty::Int(_)
                                 | ty::Uint(_)
                                 | ty::Float(_)
@@ -768,7 +768,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
         // array-ptr-cast.
 
         if m_expr.mutbl == hir::Mutability::Not && m_cast.mutbl == hir::Mutability::Not {
-            if let ty::Array(ety, _) = m_expr.ty.kind {
+            if let ty::Array(ety, _) = m_expr.ty.kind() {
                 // Due to the limitations of LLVM global constants,
                 // region pointers end up pointing at copies of
                 // vector elements instead of the original values.
@@ -817,7 +817,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
     }
 
     fn cenum_impl_drop_lint(&self, fcx: &FnCtxt<'a, 'tcx>) {
-        if let ty::Adt(d, _) = self.expr_ty.kind {
+        if let ty::Adt(d, _) = self.expr_ty.kind() {
             if d.has_dtor(fcx.tcx) {
                 fcx.tcx.struct_span_lint_hir(
                     lint::builtin::CENUM_IMPL_DROP_CAST,
diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs
new file mode 100644
index 00000000000..2daa0354acb
--- /dev/null
+++ b/compiler/rustc_typeck/src/check/check.rs
@@ -0,0 +1,1344 @@
+use super::coercion::CoerceMany;
+use super::compare_method::{compare_const_impl, compare_impl_method, compare_ty_impl};
+use super::*;
+
+use rustc_attr as attr;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE};
+use rustc_hir::lang_items::LangItem;
+use rustc_hir::{ItemKind, Node};
+use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+use rustc_infer::infer::RegionVariableOrigin;
+use rustc_middle::ty::fold::TypeFoldable;
+use rustc_middle::ty::subst::GenericArgKind;
+use rustc_middle::ty::util::{Discr, IntTypeExt, Representability};
+use rustc_middle::ty::{self, RegionKind, ToPredicate, Ty, TyCtxt};
+use rustc_session::config::EntryFnType;
+use rustc_span::symbol::sym;
+use rustc_span::{self, MultiSpan, Span};
+use rustc_target::spec::abi::Abi;
+use rustc_trait_selection::traits::{self, ObligationCauseCode};
+
+pub fn check_wf_new(tcx: TyCtxt<'_>) {
+    let visit = wfcheck::CheckTypeWellFormedVisitor::new(tcx);
+    tcx.hir().krate().par_visit_all_item_likes(&visit);
+}
+
+pub(super) fn check_abi(tcx: TyCtxt<'_>, span: Span, abi: Abi) {
+    if !tcx.sess.target.target.is_abi_supported(abi) {
+        struct_span_err!(
+            tcx.sess,
+            span,
+            E0570,
+            "The ABI `{}` is not supported for the current target",
+            abi
+        )
+        .emit()
+    }
+}
+
+/// Helper used for fns and closures. Does the grungy work of checking a function
+/// body and returns the function context used for that purpose, since in the case of a fn item
+/// there is still a bit more to do.
+///
+/// * ...
+/// * inherited: other fields inherited from the enclosing fn (if any)
+pub(super) fn check_fn<'a, 'tcx>(
+    inherited: &'a Inherited<'a, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    fn_sig: ty::FnSig<'tcx>,
+    decl: &'tcx hir::FnDecl<'tcx>,
+    fn_id: hir::HirId,
+    body: &'tcx hir::Body<'tcx>,
+    can_be_generator: Option<hir::Movability>,
+) -> (FnCtxt<'a, 'tcx>, Option<GeneratorTypes<'tcx>>) {
+    let mut fn_sig = fn_sig;
+
+    debug!("check_fn(sig={:?}, fn_id={}, param_env={:?})", fn_sig, fn_id, param_env);
+
+    // Create the function context. This is either derived from scratch or,
+    // in the case of closures, based on the outer context.
+    let mut fcx = FnCtxt::new(inherited, param_env, body.value.hir_id);
+    *fcx.ps.borrow_mut() = UnsafetyState::function(fn_sig.unsafety, fn_id);
+
+    let tcx = fcx.tcx;
+    let sess = tcx.sess;
+    let hir = tcx.hir();
+
+    let declared_ret_ty = fn_sig.output();
+
+    let revealed_ret_ty =
+        fcx.instantiate_opaque_types_from_value(fn_id, &declared_ret_ty, decl.output.span());
+    debug!("check_fn: declared_ret_ty: {}, revealed_ret_ty: {}", declared_ret_ty, revealed_ret_ty);
+    fcx.ret_coercion = Some(RefCell::new(CoerceMany::new(revealed_ret_ty)));
+    fcx.ret_type_span = Some(decl.output.span());
+    if let ty::Opaque(..) = declared_ret_ty.kind() {
+        fcx.ret_coercion_impl_trait = Some(declared_ret_ty);
+    }
+    fn_sig = tcx.mk_fn_sig(
+        fn_sig.inputs().iter().cloned(),
+        revealed_ret_ty,
+        fn_sig.c_variadic,
+        fn_sig.unsafety,
+        fn_sig.abi,
+    );
+
+    let span = body.value.span;
+
+    fn_maybe_err(tcx, span, fn_sig.abi);
+
+    if body.generator_kind.is_some() && can_be_generator.is_some() {
+        let yield_ty = fcx
+            .next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span });
+        fcx.require_type_is_sized(yield_ty, span, traits::SizedYieldType);
+
+        // 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());
+
+        fcx.resume_yield_tys = Some((resume_ty, yield_ty));
+    }
+
+    let outer_def_id = tcx.closure_base_def_id(hir.local_def_id(fn_id).to_def_id()).expect_local();
+    let outer_hir_id = hir.local_def_id_to_hir_id(outer_def_id);
+    GatherLocalsVisitor::new(&fcx, outer_hir_id).visit_body(body);
+
+    // C-variadic fns also have a `VaList` input that's not listed in `fn_sig`
+    // (as it's created inside the body itself, not passed in from outside).
+    let maybe_va_list = if fn_sig.c_variadic {
+        let span = body.params.last().unwrap().span;
+        let va_list_did = tcx.require_lang_item(LangItem::VaList, Some(span));
+        let region = fcx.next_region_var(RegionVariableOrigin::MiscVariable(span));
+
+        Some(tcx.type_of(va_list_did).subst(tcx, &[region.into()]))
+    } else {
+        None
+    };
+
+    // Add formal parameters.
+    let inputs_hir = hir.fn_decl_by_hir_id(fn_id).map(|decl| &decl.inputs);
+    let inputs_fn = fn_sig.inputs().iter().copied();
+    for (idx, (param_ty, param)) in inputs_fn.chain(maybe_va_list).zip(body.params).enumerate() {
+        // Check the pattern.
+        let ty_span = try { inputs_hir?.get(idx)?.span };
+        fcx.check_pat_top(&param.pat, param_ty, ty_span, false);
+
+        // Check that argument is Sized.
+        // The check for a non-trivial pattern is a hack to avoid duplicate warnings
+        // for simple cases like `fn foo(x: Trait)`,
+        // where we would error once on the parameter as a whole, and once on the binding `x`.
+        if param.pat.simple_ident().is_none() && !tcx.features().unsized_locals {
+            fcx.require_type_is_sized(param_ty, param.pat.span, traits::SizedArgumentType(ty_span));
+        }
+
+        fcx.write_ty(param.hir_id, param_ty);
+    }
+
+    inherited.typeck_results.borrow_mut().liberated_fn_sigs_mut().insert(fn_id, fn_sig);
+
+    fcx.in_tail_expr = true;
+    if let ty::Dynamic(..) = declared_ret_ty.kind() {
+        // FIXME: We need to verify that the return type is `Sized` after the return expression has
+        // been evaluated so that we have types available for all the nodes being returned, but that
+        // requires the coerced evaluated type to be stored. Moving `check_return_expr` before this
+        // causes unsized errors caused by the `declared_ret_ty` to point at the return expression,
+        // while keeping the current ordering we will ignore the tail expression's type because we
+        // don't know it yet. We can't do `check_expr_kind` while keeping `check_return_expr`
+        // because we will trigger "unreachable expression" lints unconditionally.
+        // Because of all of this, we perform a crude check to know whether the simplest `!Sized`
+        // case that a newcomer might make, returning a bare trait, and in that case we populate
+        // the tail expression's type so that the suggestion will be correct, but ignore all other
+        // possible cases.
+        fcx.check_expr(&body.value);
+        fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType);
+        tcx.sess.delay_span_bug(decl.output.span(), "`!Sized` return type");
+    } else {
+        fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType);
+        fcx.check_return_expr(&body.value);
+    }
+    fcx.in_tail_expr = false;
+
+    // We insert the deferred_generator_interiors entry after visiting the body.
+    // This ensures that all nested generators appear before the entry of this generator.
+    // resolve_generator_interiors relies on this property.
+    let gen_ty = if let (Some(_), Some(gen_kind)) = (can_be_generator, body.generator_kind) {
+        let interior = fcx
+            .next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span });
+        fcx.deferred_generator_interiors.borrow_mut().push((body.id(), interior, gen_kind));
+
+        let (resume_ty, yield_ty) = fcx.resume_yield_tys.unwrap();
+        Some(GeneratorTypes {
+            resume_ty,
+            yield_ty,
+            interior,
+            movability: can_be_generator.unwrap(),
+        })
+    } else {
+        None
+    };
+
+    // Finalize the return check by taking the LUB of the return types
+    // we saw and assigning it to the expected return type. This isn't
+    // really expected to fail, since the coercions would have failed
+    // earlier when trying to find a LUB.
+    //
+    // However, the behavior around `!` is sort of complex. In the
+    // event that the `actual_return_ty` comes back as `!`, that
+    // indicates that the fn either does not return or "returns" only
+    // values of type `!`. In this case, if there is an expected
+    // return type that is *not* `!`, that should be ok. But if the
+    // return type is being inferred, we want to "fallback" to `!`:
+    //
+    //     let x = move || panic!();
+    //
+    // To allow for that, I am creating a type variable with diverging
+    // fallback. This was deemed ever so slightly better than unifying
+    // the return value with `!` because it allows for the caller to
+    // make more assumptions about the return type (e.g., they could do
+    //
+    //     let y: Option<u32> = Some(x());
+    //
+    // which would then cause this return type to become `u32`, not
+    // `!`).
+    let coercion = fcx.ret_coercion.take().unwrap().into_inner();
+    let mut actual_return_ty = coercion.complete(&fcx);
+    if actual_return_ty.is_never() {
+        actual_return_ty = fcx.next_diverging_ty_var(TypeVariableOrigin {
+            kind: TypeVariableOriginKind::DivergingFn,
+            span,
+        });
+    }
+    fcx.demand_suptype(span, revealed_ret_ty, actual_return_ty);
+
+    // Check that the main return type implements the termination trait.
+    if let Some(term_id) = tcx.lang_items().termination() {
+        if let Some((def_id, EntryFnType::Main)) = tcx.entry_fn(LOCAL_CRATE) {
+            let main_id = hir.local_def_id_to_hir_id(def_id);
+            if main_id == fn_id {
+                let substs = tcx.mk_substs_trait(declared_ret_ty, &[]);
+                let trait_ref = ty::TraitRef::new(term_id, substs);
+                let return_ty_span = decl.output.span();
+                let cause = traits::ObligationCause::new(
+                    return_ty_span,
+                    fn_id,
+                    ObligationCauseCode::MainFunctionType,
+                );
+
+                inherited.register_predicate(traits::Obligation::new(
+                    cause,
+                    param_env,
+                    trait_ref.without_const().to_predicate(tcx),
+                ));
+            }
+        }
+    }
+
+    // Check that a function marked as `#[panic_handler]` has signature `fn(&PanicInfo) -> !`
+    if let Some(panic_impl_did) = tcx.lang_items().panic_impl() {
+        if panic_impl_did == hir.local_def_id(fn_id).to_def_id() {
+            if let Some(panic_info_did) = tcx.lang_items().panic_info() {
+                if *declared_ret_ty.kind() != ty::Never {
+                    sess.span_err(decl.output.span(), "return type should be `!`");
+                }
+
+                let inputs = fn_sig.inputs();
+                let span = hir.span(fn_id);
+                if inputs.len() == 1 {
+                    let arg_is_panic_info = match *inputs[0].kind() {
+                        ty::Ref(region, ty, mutbl) => match *ty.kind() {
+                            ty::Adt(ref adt, _) => {
+                                adt.did == panic_info_did
+                                    && mutbl == hir::Mutability::Not
+                                    && *region != RegionKind::ReStatic
+                            }
+                            _ => false,
+                        },
+                        _ => false,
+                    };
+
+                    if !arg_is_panic_info {
+                        sess.span_err(decl.inputs[0].span, "argument should be `&PanicInfo`");
+                    }
+
+                    if let Node::Item(item) = hir.get(fn_id) {
+                        if let ItemKind::Fn(_, ref generics, _) = item.kind {
+                            if !generics.params.is_empty() {
+                                sess.span_err(span, "should have no type parameters");
+                            }
+                        }
+                    }
+                } else {
+                    let span = sess.source_map().guess_head_span(span);
+                    sess.span_err(span, "function should have one argument");
+                }
+            } else {
+                sess.err("language item required, but not found: `panic_info`");
+            }
+        }
+    }
+
+    // Check that a function marked as `#[alloc_error_handler]` has signature `fn(Layout) -> !`
+    if let Some(alloc_error_handler_did) = tcx.lang_items().oom() {
+        if alloc_error_handler_did == hir.local_def_id(fn_id).to_def_id() {
+            if let Some(alloc_layout_did) = tcx.lang_items().alloc_layout() {
+                if *declared_ret_ty.kind() != ty::Never {
+                    sess.span_err(decl.output.span(), "return type should be `!`");
+                }
+
+                let inputs = fn_sig.inputs();
+                let span = hir.span(fn_id);
+                if inputs.len() == 1 {
+                    let arg_is_alloc_layout = match inputs[0].kind() {
+                        ty::Adt(ref adt, _) => adt.did == alloc_layout_did,
+                        _ => false,
+                    };
+
+                    if !arg_is_alloc_layout {
+                        sess.span_err(decl.inputs[0].span, "argument should be `Layout`");
+                    }
+
+                    if let Node::Item(item) = hir.get(fn_id) {
+                        if let ItemKind::Fn(_, ref generics, _) = item.kind {
+                            if !generics.params.is_empty() {
+                                sess.span_err(
+                                    span,
+                                    "`#[alloc_error_handler]` function should have no type \
+                                     parameters",
+                                );
+                            }
+                        }
+                    }
+                } else {
+                    let span = sess.source_map().guess_head_span(span);
+                    sess.span_err(span, "function should have one argument");
+                }
+            } else {
+                sess.err("language item required, but not found: `alloc_layout`");
+            }
+        }
+    }
+
+    (fcx, gen_ty)
+}
+
+pub(super) fn check_struct(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) {
+    let def_id = tcx.hir().local_def_id(id);
+    let def = tcx.adt_def(def_id);
+    def.destructor(tcx); // force the destructor to be evaluated
+    check_representable(tcx, span, def_id);
+
+    if def.repr.simd() {
+        check_simd(tcx, span, def_id);
+    }
+
+    check_transparent(tcx, span, def);
+    check_packed(tcx, span, def);
+}
+
+pub(super) fn check_union(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) {
+    let def_id = tcx.hir().local_def_id(id);
+    let def = tcx.adt_def(def_id);
+    def.destructor(tcx); // force the destructor to be evaluated
+    check_representable(tcx, span, def_id);
+    check_transparent(tcx, span, def);
+    check_union_fields(tcx, span, def_id);
+    check_packed(tcx, span, def);
+}
+
+/// When the `#![feature(untagged_unions)]` gate is active,
+/// check that the fields of the `union` does not contain fields that need dropping.
+pub(super) fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> bool {
+    let item_type = tcx.type_of(item_def_id);
+    if let ty::Adt(def, substs) = item_type.kind() {
+        assert!(def.is_union());
+        let fields = &def.non_enum_variant().fields;
+        let param_env = tcx.param_env(item_def_id);
+        for field in fields {
+            let field_ty = field.ty(tcx, substs);
+            // We are currently checking the type this field came from, so it must be local.
+            let field_span = tcx.hir().span_if_local(field.did).unwrap();
+            if field_ty.needs_drop(tcx, param_env) {
+                struct_span_err!(
+                    tcx.sess,
+                    field_span,
+                    E0740,
+                    "unions may not contain fields that need dropping"
+                )
+                .span_note(field_span, "`std::mem::ManuallyDrop` can be used to wrap the type")
+                .emit();
+                return false;
+            }
+        }
+    } else {
+        span_bug!(span, "unions must be ty::Adt, but got {:?}", item_type.kind());
+    }
+    true
+}
+
+/// Checks that an opaque type does not contain cycles and does not use `Self` or `T::Foo`
+/// projections that would result in "inheriting lifetimes".
+pub(super) fn check_opaque<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    def_id: LocalDefId,
+    substs: SubstsRef<'tcx>,
+    span: Span,
+    origin: &hir::OpaqueTyOrigin,
+) {
+    check_opaque_for_inheriting_lifetimes(tcx, def_id, span);
+    check_opaque_for_cycles(tcx, def_id, substs, span, origin);
+}
+
+/// Checks that an opaque type does not use `Self` or `T::Foo` projections that would result
+/// in "inheriting lifetimes".
+pub(super) fn check_opaque_for_inheriting_lifetimes(
+    tcx: TyCtxt<'tcx>,
+    def_id: LocalDefId,
+    span: Span,
+) {
+    let item = tcx.hir().expect_item(tcx.hir().local_def_id_to_hir_id(def_id));
+    debug!(
+        "check_opaque_for_inheriting_lifetimes: def_id={:?} span={:?} item={:?}",
+        def_id, span, item
+    );
+
+    #[derive(Debug)]
+    struct ProhibitOpaqueVisitor<'tcx> {
+        opaque_identity_ty: Ty<'tcx>,
+        generics: &'tcx ty::Generics,
+        ty: Option<Ty<'tcx>>,
+    };
+
+    impl<'tcx> ty::fold::TypeVisitor<'tcx> for ProhibitOpaqueVisitor<'tcx> {
+        fn visit_ty(&mut self, t: Ty<'tcx>) -> bool {
+            debug!("check_opaque_for_inheriting_lifetimes: (visit_ty) t={:?}", t);
+            if t != self.opaque_identity_ty && t.super_visit_with(self) {
+                self.ty = Some(t);
+                return true;
+            }
+            false
+        }
+
+        fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool {
+            debug!("check_opaque_for_inheriting_lifetimes: (visit_region) r={:?}", r);
+            if let RegionKind::ReEarlyBound(ty::EarlyBoundRegion { index, .. }) = r {
+                return *index < self.generics.parent_count as u32;
+            }
+
+            r.super_visit_with(self)
+        }
+
+        fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> bool {
+            if let ty::ConstKind::Unevaluated(..) = c.val {
+                // FIXME(#72219) We currenctly don't detect lifetimes within substs
+                // which would violate this check. Even though the particular substitution is not used
+                // within the const, this should still be fixed.
+                return false;
+            }
+            c.super_visit_with(self)
+        }
+    }
+
+    if let ItemKind::OpaqueTy(hir::OpaqueTy {
+        origin: hir::OpaqueTyOrigin::AsyncFn | hir::OpaqueTyOrigin::FnReturn,
+        ..
+    }) = item.kind
+    {
+        let mut visitor = ProhibitOpaqueVisitor {
+            opaque_identity_ty: tcx.mk_opaque(
+                def_id.to_def_id(),
+                InternalSubsts::identity_for_item(tcx, def_id.to_def_id()),
+            ),
+            generics: tcx.generics_of(def_id),
+            ty: None,
+        };
+        let prohibit_opaque = tcx
+            .predicates_of(def_id)
+            .predicates
+            .iter()
+            .any(|(predicate, _)| predicate.visit_with(&mut visitor));
+        debug!(
+            "check_opaque_for_inheriting_lifetimes: prohibit_opaque={:?}, visitor={:?}",
+            prohibit_opaque, visitor
+        );
+
+        if prohibit_opaque {
+            let is_async = match item.kind {
+                ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => match origin {
+                    hir::OpaqueTyOrigin::AsyncFn => true,
+                    _ => false,
+                },
+                _ => unreachable!(),
+            };
+
+            let mut err = struct_span_err!(
+                tcx.sess,
+                span,
+                E0760,
+                "`{}` return type cannot contain a projection or `Self` that references lifetimes from \
+             a parent scope",
+                if is_async { "async fn" } else { "impl Trait" },
+            );
+
+            if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(span) {
+                if snippet == "Self" {
+                    if let Some(ty) = visitor.ty {
+                        err.span_suggestion(
+                            span,
+                            "consider spelling out the type instead",
+                            format!("{:?}", ty),
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                }
+            }
+            err.emit();
+        }
+    }
+}
+
+/// Checks that an opaque type does not contain cycles.
+pub(super) fn check_opaque_for_cycles<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    def_id: LocalDefId,
+    substs: SubstsRef<'tcx>,
+    span: Span,
+    origin: &hir::OpaqueTyOrigin,
+) {
+    if let Err(partially_expanded_type) = tcx.try_expand_impl_trait_type(def_id.to_def_id(), substs)
+    {
+        match origin {
+            hir::OpaqueTyOrigin::AsyncFn => async_opaque_type_cycle_error(tcx, span),
+            hir::OpaqueTyOrigin::Binding => {
+                binding_opaque_type_cycle_error(tcx, def_id, span, partially_expanded_type)
+            }
+            _ => opaque_type_cycle_error(tcx, def_id, span),
+        }
+    }
+}
+
+pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item<'tcx>) {
+    debug!(
+        "check_item_type(it.hir_id={}, it.name={})",
+        it.hir_id,
+        tcx.def_path_str(tcx.hir().local_def_id(it.hir_id).to_def_id())
+    );
+    let _indenter = indenter();
+    match it.kind {
+        // Consts can play a role in type-checking, so they are included here.
+        hir::ItemKind::Static(..) => {
+            let def_id = tcx.hir().local_def_id(it.hir_id);
+            tcx.ensure().typeck(def_id);
+            maybe_check_static_with_link_section(tcx, def_id, it.span);
+        }
+        hir::ItemKind::Const(..) => {
+            tcx.ensure().typeck(tcx.hir().local_def_id(it.hir_id));
+        }
+        hir::ItemKind::Enum(ref enum_definition, _) => {
+            check_enum(tcx, it.span, &enum_definition.variants, it.hir_id);
+        }
+        hir::ItemKind::Fn(..) => {} // entirely within check_item_body
+        hir::ItemKind::Impl { ref items, .. } => {
+            debug!("ItemKind::Impl {} with id {}", it.ident, it.hir_id);
+            let impl_def_id = tcx.hir().local_def_id(it.hir_id);
+            if let Some(impl_trait_ref) = tcx.impl_trait_ref(impl_def_id) {
+                check_impl_items_against_trait(tcx, it.span, impl_def_id, impl_trait_ref, items);
+                let trait_def_id = impl_trait_ref.def_id;
+                check_on_unimplemented(tcx, trait_def_id, it);
+            }
+        }
+        hir::ItemKind::Trait(_, _, _, _, ref items) => {
+            let def_id = tcx.hir().local_def_id(it.hir_id);
+            check_on_unimplemented(tcx, def_id.to_def_id(), it);
+
+            for item in items.iter() {
+                let item = tcx.hir().trait_item(item.id);
+                if let hir::TraitItemKind::Fn(sig, _) = &item.kind {
+                    let abi = sig.header.abi;
+                    fn_maybe_err(tcx, item.ident.span, abi);
+                }
+            }
+        }
+        hir::ItemKind::Struct(..) => {
+            check_struct(tcx, it.hir_id, it.span);
+        }
+        hir::ItemKind::Union(..) => {
+            check_union(tcx, it.hir_id, it.span);
+        }
+        hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => {
+            // HACK(jynelson): trying to infer the type of `impl trait` breaks documenting
+            // `async-std` (and `pub async fn` in general).
+            // Since rustdoc doesn't care about the concrete type behind `impl Trait`, just don't look at it!
+            // See https://github.com/rust-lang/rust/issues/75100
+            if !tcx.sess.opts.actually_rustdoc {
+                let def_id = tcx.hir().local_def_id(it.hir_id);
+
+                let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id());
+                check_opaque(tcx, def_id, substs, it.span, &origin);
+            }
+        }
+        hir::ItemKind::TyAlias(..) => {
+            let def_id = tcx.hir().local_def_id(it.hir_id);
+            let pty_ty = tcx.type_of(def_id);
+            let generics = tcx.generics_of(def_id);
+            check_type_params_are_used(tcx, &generics, pty_ty);
+        }
+        hir::ItemKind::ForeignMod(ref m) => {
+            check_abi(tcx, it.span, m.abi);
+
+            if m.abi == Abi::RustIntrinsic {
+                for item in m.items {
+                    intrinsic::check_intrinsic_type(tcx, item);
+                }
+            } else if m.abi == Abi::PlatformIntrinsic {
+                for item in m.items {
+                    intrinsic::check_platform_intrinsic_type(tcx, item);
+                }
+            } else {
+                for item in m.items {
+                    let generics = tcx.generics_of(tcx.hir().local_def_id(item.hir_id));
+                    let own_counts = generics.own_counts();
+                    if generics.params.len() - own_counts.lifetimes != 0 {
+                        let (kinds, kinds_pl, egs) = match (own_counts.types, own_counts.consts) {
+                            (_, 0) => ("type", "types", Some("u32")),
+                            // We don't specify an example value, because we can't generate
+                            // a valid value for any type.
+                            (0, _) => ("const", "consts", None),
+                            _ => ("type or const", "types or consts", None),
+                        };
+                        struct_span_err!(
+                            tcx.sess,
+                            item.span,
+                            E0044,
+                            "foreign items may not have {} parameters",
+                            kinds,
+                        )
+                        .span_label(item.span, &format!("can't have {} parameters", kinds))
+                        .help(
+                            // FIXME: once we start storing spans for type arguments, turn this
+                            // into a suggestion.
+                            &format!(
+                                "replace the {} parameters with concrete {}{}",
+                                kinds,
+                                kinds_pl,
+                                egs.map(|egs| format!(" like `{}`", egs)).unwrap_or_default(),
+                            ),
+                        )
+                        .emit();
+                    }
+
+                    if let hir::ForeignItemKind::Fn(ref fn_decl, _, _) = item.kind {
+                        require_c_abi_if_c_variadic(tcx, fn_decl, m.abi, item.span);
+                    }
+                }
+            }
+        }
+        _ => { /* nothing to do */ }
+    }
+}
+
+pub(super) fn check_on_unimplemented(tcx: TyCtxt<'_>, trait_def_id: DefId, item: &hir::Item<'_>) {
+    let item_def_id = tcx.hir().local_def_id(item.hir_id);
+    // an error would be reported if this fails.
+    let _ = traits::OnUnimplementedDirective::of_item(tcx, trait_def_id, item_def_id.to_def_id());
+}
+
+pub(super) fn check_specialization_validity<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    trait_def: &ty::TraitDef,
+    trait_item: &ty::AssocItem,
+    impl_id: DefId,
+    impl_item: &hir::ImplItem<'_>,
+) {
+    let kind = match impl_item.kind {
+        hir::ImplItemKind::Const(..) => ty::AssocKind::Const,
+        hir::ImplItemKind::Fn(..) => ty::AssocKind::Fn,
+        hir::ImplItemKind::TyAlias(_) => ty::AssocKind::Type,
+    };
+
+    let ancestors = match trait_def.ancestors(tcx, impl_id) {
+        Ok(ancestors) => ancestors,
+        Err(_) => return,
+    };
+    let mut ancestor_impls = ancestors
+        .skip(1)
+        .filter_map(|parent| {
+            if parent.is_from_trait() {
+                None
+            } else {
+                Some((parent, parent.item(tcx, trait_item.ident, kind, trait_def.def_id)))
+            }
+        })
+        .peekable();
+
+    if ancestor_impls.peek().is_none() {
+        // No parent, nothing to specialize.
+        return;
+    }
+
+    let opt_result = ancestor_impls.find_map(|(parent_impl, parent_item)| {
+        match parent_item {
+            // Parent impl exists, and contains the parent item we're trying to specialize, but
+            // doesn't mark it `default`.
+            Some(parent_item) if traits::impl_item_is_final(tcx, &parent_item) => {
+                Some(Err(parent_impl.def_id()))
+            }
+
+            // Parent impl contains item and makes it specializable.
+            Some(_) => Some(Ok(())),
+
+            // Parent impl doesn't mention the item. This means it's inherited from the
+            // grandparent. In that case, if parent is a `default impl`, inherited items use the
+            // "defaultness" from the grandparent, else they are final.
+            None => {
+                if tcx.impl_defaultness(parent_impl.def_id()).is_default() {
+                    None
+                } else {
+                    Some(Err(parent_impl.def_id()))
+                }
+            }
+        }
+    });
+
+    // If `opt_result` is `None`, we have only encountered `default impl`s that don't contain the
+    // item. This is allowed, the item isn't actually getting specialized here.
+    let result = opt_result.unwrap_or(Ok(()));
+
+    if let Err(parent_impl) = result {
+        report_forbidden_specialization(tcx, impl_item, parent_impl);
+    }
+}
+
+pub(super) fn check_impl_items_against_trait<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    full_impl_span: Span,
+    impl_id: LocalDefId,
+    impl_trait_ref: ty::TraitRef<'tcx>,
+    impl_item_refs: &[hir::ImplItemRef<'_>],
+) {
+    let impl_span = tcx.sess.source_map().guess_head_span(full_impl_span);
+
+    // If the trait reference itself is erroneous (so the compilation is going
+    // to fail), skip checking the items here -- the `impl_item` table in `tcx`
+    // isn't populated for such impls.
+    if impl_trait_ref.references_error() {
+        return;
+    }
+
+    // Negative impls are not expected to have any items
+    match tcx.impl_polarity(impl_id) {
+        ty::ImplPolarity::Reservation | ty::ImplPolarity::Positive => {}
+        ty::ImplPolarity::Negative => {
+            if let [first_item_ref, ..] = impl_item_refs {
+                let first_item_span = tcx.hir().impl_item(first_item_ref.id).span;
+                struct_span_err!(
+                    tcx.sess,
+                    first_item_span,
+                    E0749,
+                    "negative impls cannot have any items"
+                )
+                .emit();
+            }
+            return;
+        }
+    }
+
+    // Locate trait definition and items
+    let trait_def = tcx.trait_def(impl_trait_ref.def_id);
+
+    let impl_items = || impl_item_refs.iter().map(|iiref| tcx.hir().impl_item(iiref.id));
+
+    // Check existing impl methods to see if they are both present in trait
+    // and compatible with trait signature
+    for impl_item in impl_items() {
+        let namespace = impl_item.kind.namespace();
+        let ty_impl_item = tcx.associated_item(tcx.hir().local_def_id(impl_item.hir_id));
+        let ty_trait_item = tcx
+            .associated_items(impl_trait_ref.def_id)
+            .find_by_name_and_namespace(tcx, ty_impl_item.ident, namespace, impl_trait_ref.def_id)
+            .or_else(|| {
+                // Not compatible, but needed for the error message
+                tcx.associated_items(impl_trait_ref.def_id)
+                    .filter_by_name(tcx, ty_impl_item.ident, impl_trait_ref.def_id)
+                    .next()
+            });
+
+        // Check that impl definition matches trait definition
+        if let Some(ty_trait_item) = ty_trait_item {
+            match impl_item.kind {
+                hir::ImplItemKind::Const(..) => {
+                    // Find associated const definition.
+                    if ty_trait_item.kind == ty::AssocKind::Const {
+                        compare_const_impl(
+                            tcx,
+                            &ty_impl_item,
+                            impl_item.span,
+                            &ty_trait_item,
+                            impl_trait_ref,
+                        );
+                    } else {
+                        let mut err = struct_span_err!(
+                            tcx.sess,
+                            impl_item.span,
+                            E0323,
+                            "item `{}` is an associated const, \
+                             which doesn't match its trait `{}`",
+                            ty_impl_item.ident,
+                            impl_trait_ref.print_only_trait_path()
+                        );
+                        err.span_label(impl_item.span, "does not match trait");
+                        // We can only get the spans from local trait definition
+                        // Same for E0324 and E0325
+                        if let Some(trait_span) = tcx.hir().span_if_local(ty_trait_item.def_id) {
+                            err.span_label(trait_span, "item in trait");
+                        }
+                        err.emit()
+                    }
+                }
+                hir::ImplItemKind::Fn(..) => {
+                    let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id);
+                    if ty_trait_item.kind == ty::AssocKind::Fn {
+                        compare_impl_method(
+                            tcx,
+                            &ty_impl_item,
+                            impl_item.span,
+                            &ty_trait_item,
+                            impl_trait_ref,
+                            opt_trait_span,
+                        );
+                    } else {
+                        let mut err = struct_span_err!(
+                            tcx.sess,
+                            impl_item.span,
+                            E0324,
+                            "item `{}` is an associated method, \
+                             which doesn't match its trait `{}`",
+                            ty_impl_item.ident,
+                            impl_trait_ref.print_only_trait_path()
+                        );
+                        err.span_label(impl_item.span, "does not match trait");
+                        if let Some(trait_span) = opt_trait_span {
+                            err.span_label(trait_span, "item in trait");
+                        }
+                        err.emit()
+                    }
+                }
+                hir::ImplItemKind::TyAlias(_) => {
+                    let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id);
+                    if ty_trait_item.kind == ty::AssocKind::Type {
+                        compare_ty_impl(
+                            tcx,
+                            &ty_impl_item,
+                            impl_item.span,
+                            &ty_trait_item,
+                            impl_trait_ref,
+                            opt_trait_span,
+                        );
+                    } else {
+                        let mut err = struct_span_err!(
+                            tcx.sess,
+                            impl_item.span,
+                            E0325,
+                            "item `{}` is an associated type, \
+                             which doesn't match its trait `{}`",
+                            ty_impl_item.ident,
+                            impl_trait_ref.print_only_trait_path()
+                        );
+                        err.span_label(impl_item.span, "does not match trait");
+                        if let Some(trait_span) = opt_trait_span {
+                            err.span_label(trait_span, "item in trait");
+                        }
+                        err.emit()
+                    }
+                }
+            }
+
+            check_specialization_validity(
+                tcx,
+                trait_def,
+                &ty_trait_item,
+                impl_id.to_def_id(),
+                impl_item,
+            );
+        }
+    }
+
+    // Check for missing items from trait
+    let mut missing_items = Vec::new();
+    if let Ok(ancestors) = trait_def.ancestors(tcx, impl_id.to_def_id()) {
+        for trait_item in tcx.associated_items(impl_trait_ref.def_id).in_definition_order() {
+            let is_implemented = ancestors
+                .leaf_def(tcx, trait_item.ident, trait_item.kind)
+                .map(|node_item| !node_item.defining_node.is_from_trait())
+                .unwrap_or(false);
+
+            if !is_implemented && tcx.impl_defaultness(impl_id).is_final() {
+                if !trait_item.defaultness.has_value() {
+                    missing_items.push(*trait_item);
+                }
+            }
+        }
+    }
+
+    if !missing_items.is_empty() {
+        missing_items_err(tcx, impl_span, &missing_items, full_impl_span);
+    }
+}
+
+/// Checks whether a type can be represented in memory. In particular, it
+/// identifies types that contain themselves without indirection through a
+/// pointer, which would mean their size is unbounded.
+pub(super) fn check_representable(tcx: TyCtxt<'_>, sp: Span, item_def_id: LocalDefId) -> bool {
+    let rty = tcx.type_of(item_def_id);
+
+    // Check that it is possible to represent this type. This call identifies
+    // (1) types that contain themselves and (2) types that contain a different
+    // recursive type. It is only necessary to throw an error on those that
+    // contain themselves. For case 2, there must be an inner type that will be
+    // caught by case 1.
+    match rty.is_representable(tcx, sp) {
+        Representability::SelfRecursive(spans) => {
+            recursive_type_with_infinite_size_error(tcx, item_def_id.to_def_id(), spans);
+            return false;
+        }
+        Representability::Representable | Representability::ContainsRecursive => (),
+    }
+    true
+}
+
+pub fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) {
+    let t = tcx.type_of(def_id);
+    if let ty::Adt(def, substs) = t.kind() {
+        if def.is_struct() {
+            let fields = &def.non_enum_variant().fields;
+            if fields.is_empty() {
+                struct_span_err!(tcx.sess, sp, E0075, "SIMD vector cannot be empty").emit();
+                return;
+            }
+            let e = fields[0].ty(tcx, substs);
+            if !fields.iter().all(|f| f.ty(tcx, substs) == e) {
+                struct_span_err!(tcx.sess, sp, E0076, "SIMD vector should be homogeneous")
+                    .span_label(sp, "SIMD elements must have the same type")
+                    .emit();
+                return;
+            }
+            match e.kind() {
+                ty::Param(_) => { /* struct<T>(T, T, T, T) is ok */ }
+                _ if e.is_machine() => { /* struct(u8, u8, u8, u8) is ok */ }
+                _ => {
+                    struct_span_err!(
+                        tcx.sess,
+                        sp,
+                        E0077,
+                        "SIMD vector element type should be machine type"
+                    )
+                    .emit();
+                    return;
+                }
+            }
+        }
+    }
+}
+
+pub(super) fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: &ty::AdtDef) {
+    let repr = def.repr;
+    if repr.packed() {
+        for attr in tcx.get_attrs(def.did).iter() {
+            for r in attr::find_repr_attrs(&tcx.sess, attr) {
+                if let attr::ReprPacked(pack) = r {
+                    if let Some(repr_pack) = repr.pack {
+                        if pack as u64 != repr_pack.bytes() {
+                            struct_span_err!(
+                                tcx.sess,
+                                sp,
+                                E0634,
+                                "type has conflicting packed representation hints"
+                            )
+                            .emit();
+                        }
+                    }
+                }
+            }
+        }
+        if repr.align.is_some() {
+            struct_span_err!(
+                tcx.sess,
+                sp,
+                E0587,
+                "type has conflicting packed and align representation hints"
+            )
+            .emit();
+        } else {
+            if let Some(def_spans) = check_packed_inner(tcx, def.did, &mut vec![]) {
+                let mut err = struct_span_err!(
+                    tcx.sess,
+                    sp,
+                    E0588,
+                    "packed type cannot transitively contain a `#[repr(align)]` type"
+                );
+
+                err.span_note(
+                    tcx.def_span(def_spans[0].0),
+                    &format!(
+                        "`{}` has a `#[repr(align)]` attribute",
+                        tcx.item_name(def_spans[0].0)
+                    ),
+                );
+
+                if def_spans.len() > 2 {
+                    let mut first = true;
+                    for (adt_def, span) in def_spans.iter().skip(1).rev() {
+                        let ident = tcx.item_name(*adt_def);
+                        err.span_note(
+                            *span,
+                            &if first {
+                                format!(
+                                    "`{}` contains a field of type `{}`",
+                                    tcx.type_of(def.did),
+                                    ident
+                                )
+                            } else {
+                                format!("...which contains a field of type `{}`", ident)
+                            },
+                        );
+                        first = false;
+                    }
+                }
+
+                err.emit();
+            }
+        }
+    }
+}
+
+pub(super) fn check_packed_inner(
+    tcx: TyCtxt<'_>,
+    def_id: DefId,
+    stack: &mut Vec<DefId>,
+) -> Option<Vec<(DefId, Span)>> {
+    if let ty::Adt(def, substs) = tcx.type_of(def_id).kind() {
+        if def.is_struct() || def.is_union() {
+            if def.repr.align.is_some() {
+                return Some(vec![(def.did, DUMMY_SP)]);
+            }
+
+            stack.push(def_id);
+            for field in &def.non_enum_variant().fields {
+                if let ty::Adt(def, _) = field.ty(tcx, substs).kind() {
+                    if !stack.contains(&def.did) {
+                        if let Some(mut defs) = check_packed_inner(tcx, def.did, stack) {
+                            defs.push((def.did, field.ident.span));
+                            return Some(defs);
+                        }
+                    }
+                }
+            }
+            stack.pop();
+        }
+    }
+
+    None
+}
+
+pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: &'tcx ty::AdtDef) {
+    if !adt.repr.transparent() {
+        return;
+    }
+    let sp = tcx.sess.source_map().guess_head_span(sp);
+
+    if adt.is_union() && !tcx.features().transparent_unions {
+        feature_err(
+            &tcx.sess.parse_sess,
+            sym::transparent_unions,
+            sp,
+            "transparent unions are unstable",
+        )
+        .emit();
+    }
+
+    if adt.variants.len() != 1 {
+        bad_variant_count(tcx, adt, sp, adt.did);
+        if adt.variants.is_empty() {
+            // Don't bother checking the fields. No variants (and thus no fields) exist.
+            return;
+        }
+    }
+
+    // For each field, figure out if it's known to be a ZST and align(1)
+    let field_infos = adt.all_fields().map(|field| {
+        let ty = field.ty(tcx, InternalSubsts::identity_for_item(tcx, field.did));
+        let param_env = tcx.param_env(field.did);
+        let layout = tcx.layout_of(param_env.and(ty));
+        // We are currently checking the type this field came from, so it must be local
+        let span = tcx.hir().span_if_local(field.did).unwrap();
+        let zst = layout.map(|layout| layout.is_zst()).unwrap_or(false);
+        let align1 = layout.map(|layout| layout.align.abi.bytes() == 1).unwrap_or(false);
+        (span, zst, align1)
+    });
+
+    let non_zst_fields =
+        field_infos.clone().filter_map(|(span, zst, _align1)| if !zst { Some(span) } else { None });
+    let non_zst_count = non_zst_fields.clone().count();
+    if non_zst_count != 1 {
+        bad_non_zero_sized_fields(tcx, adt, non_zst_count, non_zst_fields, sp);
+    }
+    for (span, zst, align1) in field_infos {
+        if zst && !align1 {
+            struct_span_err!(
+                tcx.sess,
+                span,
+                E0691,
+                "zero-sized field in transparent {} has alignment larger than 1",
+                adt.descr(),
+            )
+            .span_label(span, "has alignment larger than 1")
+            .emit();
+        }
+    }
+}
+
+#[allow(trivial_numeric_casts)]
+pub fn check_enum<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    sp: Span,
+    vs: &'tcx [hir::Variant<'tcx>],
+    id: hir::HirId,
+) {
+    let def_id = tcx.hir().local_def_id(id);
+    let def = tcx.adt_def(def_id);
+    def.destructor(tcx); // force the destructor to be evaluated
+
+    if vs.is_empty() {
+        let attributes = tcx.get_attrs(def_id.to_def_id());
+        if let Some(attr) = tcx.sess.find_by_name(&attributes, sym::repr) {
+            struct_span_err!(
+                tcx.sess,
+                attr.span,
+                E0084,
+                "unsupported representation for zero-variant enum"
+            )
+            .span_label(sp, "zero-variant enum")
+            .emit();
+        }
+    }
+
+    let repr_type_ty = def.repr.discr_type().to_ty(tcx);
+    if repr_type_ty == tcx.types.i128 || repr_type_ty == tcx.types.u128 {
+        if !tcx.features().repr128 {
+            feature_err(
+                &tcx.sess.parse_sess,
+                sym::repr128,
+                sp,
+                "repr with 128-bit type is unstable",
+            )
+            .emit();
+        }
+    }
+
+    for v in vs {
+        if let Some(ref e) = v.disr_expr {
+            tcx.ensure().typeck(tcx.hir().local_def_id(e.hir_id));
+        }
+    }
+
+    if tcx.adt_def(def_id).repr.int.is_none() && tcx.features().arbitrary_enum_discriminant {
+        let is_unit = |var: &hir::Variant<'_>| match var.data {
+            hir::VariantData::Unit(..) => true,
+            _ => false,
+        };
+
+        let has_disr = |var: &hir::Variant<'_>| var.disr_expr.is_some();
+        let has_non_units = vs.iter().any(|var| !is_unit(var));
+        let disr_units = vs.iter().any(|var| is_unit(&var) && has_disr(&var));
+        let disr_non_unit = vs.iter().any(|var| !is_unit(&var) && has_disr(&var));
+
+        if disr_non_unit || (disr_units && has_non_units) {
+            let mut err =
+                struct_span_err!(tcx.sess, sp, E0732, "`#[repr(inttype)]` must be specified");
+            err.emit();
+        }
+    }
+
+    let mut disr_vals: Vec<Discr<'tcx>> = Vec::with_capacity(vs.len());
+    for ((_, discr), v) in def.discriminants(tcx).zip(vs) {
+        // Check for duplicate discriminant values
+        if let Some(i) = disr_vals.iter().position(|&x| x.val == discr.val) {
+            let variant_did = def.variants[VariantIdx::new(i)].def_id;
+            let variant_i_hir_id = tcx.hir().local_def_id_to_hir_id(variant_did.expect_local());
+            let variant_i = tcx.hir().expect_variant(variant_i_hir_id);
+            let i_span = match variant_i.disr_expr {
+                Some(ref expr) => tcx.hir().span(expr.hir_id),
+                None => tcx.hir().span(variant_i_hir_id),
+            };
+            let span = match v.disr_expr {
+                Some(ref expr) => tcx.hir().span(expr.hir_id),
+                None => v.span,
+            };
+            struct_span_err!(
+                tcx.sess,
+                span,
+                E0081,
+                "discriminant value `{}` already exists",
+                disr_vals[i]
+            )
+            .span_label(i_span, format!("first use of `{}`", disr_vals[i]))
+            .span_label(span, format!("enum already has `{}`", disr_vals[i]))
+            .emit();
+        }
+        disr_vals.push(discr);
+    }
+
+    check_representable(tcx, sp, def_id);
+    check_transparent(tcx, sp, def);
+}
+
+pub(super) fn check_type_params_are_used<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    generics: &ty::Generics,
+    ty: Ty<'tcx>,
+) {
+    debug!("check_type_params_are_used(generics={:?}, ty={:?})", generics, ty);
+
+    assert_eq!(generics.parent, None);
+
+    if generics.own_counts().types == 0 {
+        return;
+    }
+
+    let mut params_used = BitSet::new_empty(generics.params.len());
+
+    if ty.references_error() {
+        // If there is already another error, do not emit
+        // an error for not using a type parameter.
+        assert!(tcx.sess.has_errors());
+        return;
+    }
+
+    for leaf in ty.walk() {
+        if let GenericArgKind::Type(leaf_ty) = leaf.unpack() {
+            if let ty::Param(param) = leaf_ty.kind() {
+                debug!("found use of ty param {:?}", param);
+                params_used.insert(param.index);
+            }
+        }
+    }
+
+    for param in &generics.params {
+        if !params_used.contains(param.index) {
+            if let ty::GenericParamDefKind::Type { .. } = param.kind {
+                let span = tcx.def_span(param.def_id);
+                struct_span_err!(
+                    tcx.sess,
+                    span,
+                    E0091,
+                    "type parameter `{}` is unused",
+                    param.name,
+                )
+                .span_label(span, "unused type parameter")
+                .emit();
+            }
+        }
+    }
+}
+
+pub(super) fn check_mod_item_types(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
+    tcx.hir().visit_item_likes_in_module(module_def_id, &mut CheckItemTypesVisitor { tcx });
+}
+
+pub(super) fn check_item_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) {
+    wfcheck::check_item_well_formed(tcx, def_id);
+}
+
+pub(super) fn check_trait_item_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) {
+    wfcheck::check_trait_item(tcx, def_id);
+}
+
+pub(super) fn check_impl_item_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) {
+    wfcheck::check_impl_item(tcx, def_id);
+}
+
+fn async_opaque_type_cycle_error(tcx: TyCtxt<'tcx>, span: Span) {
+    struct_span_err!(tcx.sess, span, E0733, "recursion in an `async fn` requires boxing")
+        .span_label(span, "recursive `async fn`")
+        .note("a recursive `async fn` must be rewritten to return a boxed `dyn Future`")
+        .emit();
+}
+
+/// Emit an error for recursive opaque types.
+///
+/// If this is a return `impl Trait`, find the item's return expressions and point at them. For
+/// direct recursion this is enough, but for indirect recursion also point at the last intermediary
+/// `impl Trait`.
+///
+/// If all the return expressions evaluate to `!`, then we explain that the error will go away
+/// after changing it. This can happen when a user uses `panic!()` or similar as a placeholder.
+fn opaque_type_cycle_error(tcx: TyCtxt<'tcx>, def_id: LocalDefId, span: Span) {
+    let mut err = struct_span_err!(tcx.sess, span, E0720, "cannot resolve opaque type");
+
+    let mut label = false;
+    if let Some((hir_id, visitor)) = get_owner_return_paths(tcx, def_id) {
+        let typeck_results = tcx.typeck(tcx.hir().local_def_id(hir_id));
+        if visitor
+            .returns
+            .iter()
+            .filter_map(|expr| typeck_results.node_type_opt(expr.hir_id))
+            .all(|ty| matches!(ty.kind(), ty::Never))
+        {
+            let spans = visitor
+                .returns
+                .iter()
+                .filter(|expr| typeck_results.node_type_opt(expr.hir_id).is_some())
+                .map(|expr| expr.span)
+                .collect::<Vec<Span>>();
+            let span_len = spans.len();
+            if span_len == 1 {
+                err.span_label(spans[0], "this returned value is of `!` type");
+            } else {
+                let mut multispan: MultiSpan = spans.clone().into();
+                for span in spans {
+                    multispan
+                        .push_span_label(span, "this returned value is of `!` type".to_string());
+                }
+                err.span_note(multispan, "these returned values have a concrete \"never\" type");
+            }
+            err.help("this error will resolve once the item's body returns a concrete type");
+        } else {
+            let mut seen = FxHashSet::default();
+            seen.insert(span);
+            err.span_label(span, "recursive opaque type");
+            label = true;
+            for (sp, ty) in visitor
+                .returns
+                .iter()
+                .filter_map(|e| typeck_results.node_type_opt(e.hir_id).map(|t| (e.span, t)))
+                .filter(|(_, ty)| !matches!(ty.kind(), ty::Never))
+            {
+                struct VisitTypes(Vec<DefId>);
+                impl<'tcx> ty::fold::TypeVisitor<'tcx> for VisitTypes {
+                    fn visit_ty(&mut self, t: Ty<'tcx>) -> bool {
+                        match *t.kind() {
+                            ty::Opaque(def, _) => {
+                                self.0.push(def);
+                                false
+                            }
+                            _ => t.super_visit_with(self),
+                        }
+                    }
+                }
+                let mut visitor = VisitTypes(vec![]);
+                ty.visit_with(&mut visitor);
+                for def_id in visitor.0 {
+                    let ty_span = tcx.def_span(def_id);
+                    if !seen.contains(&ty_span) {
+                        err.span_label(ty_span, &format!("returning this opaque type `{}`", ty));
+                        seen.insert(ty_span);
+                    }
+                    err.span_label(sp, &format!("returning here with type `{}`", ty));
+                }
+            }
+        }
+    }
+    if !label {
+        err.span_label(span, "cannot resolve opaque type");
+    }
+    err.emit();
+}
diff --git a/compiler/rustc_typeck/src/check/closure.rs b/compiler/rustc_typeck/src/check/closure.rs
index 97f7e4537ce..8898a545228 100644
--- a/compiler/rustc_typeck/src/check/closure.rs
+++ b/compiler/rustc_typeck/src/check/closure.rs
@@ -170,7 +170,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) {
         debug!("deduce_expectations_from_expected_type(expected_ty={:?})", expected_ty);
 
-        match expected_ty.kind {
+        match *expected_ty.kind() {
             ty::Dynamic(ref object_type, ..) => {
                 let sig = object_type.projection_bounds().find_map(|pb| {
                     let pb = pb.with_self_ty(self.tcx, self.tcx.types.trait_object_dummy_self);
@@ -268,7 +268,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             let arg_param_ty = self.resolve_vars_if_possible(&arg_param_ty);
             debug!("deduce_sig_from_projection: arg_param_ty={:?}", arg_param_ty);
 
-            match arg_param_ty.kind {
+            match arg_param_ty.kind() {
                 ty::Tuple(tys) => tys.into_iter().map(|k| k.expect_ty()).collect::<Vec<_>>(),
                 _ => return None,
             }
@@ -611,7 +611,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // be inferring.
         let ret_ty = ret_coercion.borrow().expected_ty();
         let ret_ty = self.inh.infcx.shallow_resolve(ret_ty);
-        let ret_vid = match ret_ty.kind {
+        let ret_vid = match *ret_ty.kind() {
             ty::Infer(ty::TyVar(ret_vid)) => ret_vid,
             _ => span_bug!(
                 self.tcx.def_span(expr_def_id),
diff --git a/compiler/rustc_typeck/src/check/coercion.rs b/compiler/rustc_typeck/src/check/coercion.rs
index f0802c45ae0..4addee1a4c9 100644
--- a/compiler/rustc_typeck/src/check/coercion.rs
+++ b/compiler/rustc_typeck/src/check/coercion.rs
@@ -37,7 +37,7 @@
 
 use crate::astconv::AstConv;
 use crate::check::FnCtxt;
-use rustc_errors::{struct_span_err, DiagnosticBuilder};
+use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
 use rustc_hir as hir;
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use rustc_infer::infer::{Coercion, InferOk, InferResult};
@@ -51,7 +51,7 @@ use rustc_middle::ty::subst::SubstsRef;
 use rustc_middle::ty::{self, Ty, TypeAndMut};
 use rustc_session::parse::feature_err;
 use rustc_span::symbol::sym;
-use rustc_span::{self, Span};
+use rustc_span::{self, BytePos, Span};
 use rustc_target::spec::abi::Abi;
 use rustc_trait_selection::traits::error_reporting::InferCtxtExt;
 use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode};
@@ -197,7 +197,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
         //
         // Note: does not attempt to resolve type variables we encounter.
         // See above for details.
-        match b.kind {
+        match *b.kind() {
             ty::RawPtr(mt_b) => {
                 return self.coerce_unsafe_ptr(a, b, mt_b.mutbl);
             }
@@ -207,7 +207,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
             _ => {}
         }
 
-        match a.kind {
+        match *a.kind() {
             ty::FnDef(..) => {
                 // Function items are coercible to any closure
                 // type; function pointers are not (that would
@@ -252,7 +252,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
         // to type check, we will construct the type that `&M*expr` would
         // yield.
 
-        let (r_a, mt_a) = match a.kind {
+        let (r_a, mt_a) = match *a.kind() {
             ty::Ref(r_a, ty, mutbl) => {
                 let mt_a = ty::TypeAndMut { ty, mutbl };
                 coerce_mutbls(mt_a.mutbl, mutbl_b)?;
@@ -415,7 +415,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
 
         // Now apply the autoref. We have to extract the region out of
         // the final ref type we got.
-        let r_borrow = match ty.kind {
+        let r_borrow = match ty.kind() {
             ty::Ref(r_borrow, _, _) => r_borrow,
             _ => span_bug!(span, "expected a ref type, got {:?}", ty),
         };
@@ -490,7 +490,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
         // that, at which point we will need extra checks on the target here.
 
         // Handle reborrows before selecting `Source: CoerceUnsized<Target>`.
-        let reborrow = match (&source.kind, &target.kind) {
+        let reborrow = match (source.kind(), target.kind()) {
             (&ty::Ref(_, ty_a, mutbl_a), &ty::Ref(_, _, mutbl_b)) => {
                 coerce_mutbls(mutbl_a, mutbl_b)?;
 
@@ -588,7 +588,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                 {
                     if unsize_did == trait_pred.def_id() {
                         let unsize_ty = trait_pred.trait_ref.substs[1].expect_ty();
-                        if let ty::Tuple(..) = unsize_ty.kind {
+                        if let ty::Tuple(..) = unsize_ty.kind() {
                             debug!("coerce_unsized: found unsized tuple coercion");
                             has_unsized_tuple_coercion = true;
                         }
@@ -608,7 +608,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                         let self_ty = trait_pred.skip_binder().self_ty();
                         let unsize_ty = trait_pred.skip_binder().trait_ref.substs[1].expect_ty();
                         debug!("coerce_unsized: ambiguous unsize case for {:?}", trait_pred);
-                        match (&self_ty.kind, &unsize_ty.kind) {
+                        match (&self_ty.kind(), &unsize_ty.kind()) {
                             (ty::Infer(ty::TyVar(v)), ty::Dynamic(..))
                                 if self.type_var_is_sized(*v) =>
                             {
@@ -672,7 +672,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
         F: FnOnce(Ty<'tcx>) -> Vec<Adjustment<'tcx>>,
         G: FnOnce(Ty<'tcx>) -> Vec<Adjustment<'tcx>>,
     {
-        if let ty::FnPtr(fn_ty_b) = b.kind {
+        if let ty::FnPtr(fn_ty_b) = b.kind() {
             if let (hir::Unsafety::Normal, hir::Unsafety::Unsafe) =
                 (fn_ty_a.unsafety(), fn_ty_b.unsafety())
             {
@@ -712,7 +712,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
         let b = self.shallow_resolve(b);
         debug!("coerce_from_fn_item(a={:?}, b={:?})", a, b);
 
-        match b.kind {
+        match b.kind() {
             ty::FnPtr(b_sig) => {
                 let a_sig = a.fn_sig(self.tcx);
                 // Intrinsics are not coercible to function pointers
@@ -721,7 +721,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                 }
 
                 // Safe `#[target_feature]` functions are not assignable to safe fn pointers (RFC 2396).
-                if let ty::FnDef(def_id, _) = a.kind {
+                if let ty::FnDef(def_id, _) = *a.kind() {
                     if b_sig.unsafety() == hir::Unsafety::Normal
                         && !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty()
                     {
@@ -771,7 +771,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
 
         let b = self.shallow_resolve(b);
 
-        match b.kind {
+        match b.kind() {
             ty::FnPtr(fn_ty) if substs_a.as_closure().upvar_tys().next().is_none() => {
                 // We coerce the closure, which has fn type
                 //     `extern "rust-call" fn((arg0,arg1,...)) -> _`
@@ -802,7 +802,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
     ) -> CoerceResult<'tcx> {
         debug!("coerce_unsafe_ptr(a={:?}, b={:?})", a, b);
 
-        let (is_ref, mt_a) = match a.kind {
+        let (is_ref, mt_a) = match *a.kind() {
             ty::Ref(_, ty, mutbl) => (true, ty::TypeAndMut { ty, mutbl }),
             ty::RawPtr(mt) => (false, mt),
             _ => return self.unify_and(a, b, identity),
@@ -912,10 +912,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     false
                 }
             };
-            if is_capturing_closure(&prev_ty.kind) || is_capturing_closure(&new_ty.kind) {
+            if is_capturing_closure(&prev_ty.kind()) || is_capturing_closure(&new_ty.kind()) {
                 (None, None)
             } else {
-                match (&prev_ty.kind, &new_ty.kind) {
+                match (&prev_ty.kind(), &new_ty.kind()) {
                     (&ty::FnDef(..), &ty::FnDef(..)) => {
                         // Don't reify if the function types have a LUB, i.e., they
                         // are the same function and their parameters have a LUB.
@@ -969,12 +969,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
             // Reify both sides and return the reified fn pointer type.
             let fn_ptr = self.tcx.mk_fn_ptr(sig);
-            let prev_adjustment = match prev_ty.kind {
+            let prev_adjustment = match prev_ty.kind() {
                 ty::Closure(..) => Adjust::Pointer(PointerCast::ClosureFnPointer(a_sig.unsafety())),
                 ty::FnDef(..) => Adjust::Pointer(PointerCast::ReifyFnPointer),
                 _ => unreachable!(),
             };
-            let next_adjustment = match new_ty.kind {
+            let next_adjustment = match new_ty.kind() {
                 ty::Closure(..) => Adjust::Pointer(PointerCast::ClosureFnPointer(b_sig.unsafety())),
                 ty::FnDef(..) => Adjust::Pointer(PointerCast::ReifyFnPointer),
                 _ => unreachable!(),
@@ -1024,7 +1024,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             let noop = match self.typeck_results.borrow().expr_adjustments(expr) {
                 &[Adjustment { kind: Adjust::Deref(_), .. }, Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(_, mutbl_adj)), .. }] =>
                 {
-                    match self.node_ty(expr.hir_id).kind {
+                    match *self.node_ty(expr.hir_id).kind() {
                         ty::Ref(_, _, mt_orig) => {
                             let mutbl_adj: hir::Mutability = mutbl_adj.into();
                             // Reborrow that we can safely ignore, because
@@ -1459,7 +1459,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
             }
         }
         if let (Some(sp), Some(fn_output)) = (fcx.ret_coercion_span.borrow().as_ref(), fn_output) {
-            self.add_impl_trait_explanation(&mut err, fcx, expected, *sp, fn_output);
+            self.add_impl_trait_explanation(&mut err, cause, fcx, expected, *sp, fn_output);
         }
         err
     }
@@ -1467,6 +1467,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
     fn add_impl_trait_explanation<'a>(
         &self,
         err: &mut DiagnosticBuilder<'a>,
+        cause: &ObligationCause<'tcx>,
         fcx: &FnCtxt<'a, 'tcx>,
         expected: Ty<'tcx>,
         sp: Span,
@@ -1501,7 +1502,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
             if let hir::TyKind::OpaqueDef(..) = ty.kind {
                 let ty = AstConv::ast_ty_to_ty(fcx, ty);
                 // Get the `impl Trait`'s `DefId`.
-                if let ty::Opaque(def_id, _) = ty.kind {
+                if let ty::Opaque(def_id, _) = ty.kind() {
                     let hir_id = fcx.tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
                     // Get the `impl Trait`'s `Item` so that we can get its trait bounds and
                     // get the `Trait`'s `DefId`.
@@ -1523,10 +1524,30 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
         };
         if has_impl {
             if is_object_safe {
-                err.help(&format!(
-                    "you can instead return a boxed trait object using `Box<dyn {}>`",
-                    &snippet[5..]
-                ));
+                err.multipart_suggestion(
+                    "you could change the return type to be a boxed trait object",
+                    vec![
+                        (return_sp.with_hi(return_sp.lo() + BytePos(4)), "Box<dyn".to_string()),
+                        (return_sp.shrink_to_hi(), ">".to_string()),
+                    ],
+                    Applicability::MachineApplicable,
+                );
+                let sugg = vec![sp, cause.span]
+                    .into_iter()
+                    .flat_map(|sp| {
+                        vec![
+                            (sp.shrink_to_lo(), "Box::new(".to_string()),
+                            (sp.shrink_to_hi(), ")".to_string()),
+                        ]
+                        .into_iter()
+                    })
+                    .collect::<Vec<_>>();
+                err.multipart_suggestion(
+                    "if you change the return type to expect trait objects, box the returned \
+                     expressions",
+                    sugg,
+                    Applicability::MaybeIncorrect,
+                );
             } else {
                 err.help(&format!(
                     "if the trait `{}` were object safe, you could return a boxed trait object",
@@ -1535,14 +1556,14 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
             }
             err.note(trait_obj_msg);
         }
-        err.help("alternatively, create a new `enum` with a variant for each returned type");
+        err.help("you could instead create a new `enum` with a variant for each returned type");
     }
 
     fn is_return_ty_unsized(&self, fcx: &FnCtxt<'a, 'tcx>, blk_id: hir::HirId) -> bool {
         if let Some((fn_decl, _)) = fcx.get_fn_decl(blk_id) {
             if let hir::FnRetTy::Return(ty) = fn_decl.output {
                 let ty = AstConv::ast_ty_to_ty(fcx, ty);
-                if let ty::Dynamic(..) = ty.kind {
+                if let ty::Dynamic(..) = ty.kind() {
                     return true;
                 }
             }
diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs
index 7adcd7b472e..7aa54e0ebcc 100644
--- a/compiler/rustc_typeck/src/check/compare_method.rs
+++ b/compiler/rustc_typeck/src/check/compare_method.rs
@@ -1,3 +1,4 @@
+use crate::errors::LifetimesOrBoundsMismatchOnTrait;
 use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorReported};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
@@ -201,11 +202,8 @@ fn compare_predicate_entailment<'tcx>(
     // The key step here is to update the caller_bounds's predicates to be
     // the new hybrid bounds we computed.
     let normalize_cause = traits::ObligationCause::misc(impl_m_span, impl_m_hir_id);
-    let param_env = ty::ParamEnv::new(
-        tcx.intern_predicates(&hybrid_preds.predicates),
-        Reveal::UserFacing,
-        None,
-    );
+    let param_env =
+        ty::ParamEnv::new(tcx.intern_predicates(&hybrid_preds.predicates), Reveal::UserFacing);
     let param_env = traits::normalize_param_env_or_error(
         tcx,
         impl_m.def_id,
@@ -366,24 +364,18 @@ fn check_region_bounds_on_impl_item<'tcx>(
         let item_kind = assoc_item_kind_str(impl_m);
         let def_span = tcx.sess.source_map().guess_head_span(span);
         let span = tcx.hir().get_generics(impl_m.def_id).map(|g| g.span).unwrap_or(def_span);
-        let mut err = struct_span_err!(
-            tcx.sess,
+        let generics_span = if let Some(sp) = tcx.hir().span_if_local(trait_m.def_id) {
+            let def_sp = tcx.sess.source_map().guess_head_span(sp);
+            Some(tcx.hir().get_generics(trait_m.def_id).map(|g| g.span).unwrap_or(def_sp))
+        } else {
+            None
+        };
+        tcx.sess.emit_err(LifetimesOrBoundsMismatchOnTrait {
             span,
-            E0195,
-            "lifetime parameters or bounds on {} `{}` do not match the trait declaration",
             item_kind,
-            impl_m.ident,
-        );
-        err.span_label(span, &format!("lifetimes do not match {} in trait", item_kind));
-        if let Some(sp) = tcx.hir().span_if_local(trait_m.def_id) {
-            let def_sp = tcx.sess.source_map().guess_head_span(sp);
-            let sp = tcx.hir().get_generics(trait_m.def_id).map(|g| g.span).unwrap_or(def_sp);
-            err.span_label(
-                sp,
-                &format!("lifetimes in impl do not match this {} in trait", item_kind),
-            );
-        }
-        err.emit();
+            ident: impl_m.ident,
+            generics_span,
+        });
         return Err(ErrorReported);
     }
 
@@ -1125,11 +1117,8 @@ fn compare_type_predicate_entailment<'tcx>(
     debug!("compare_type_predicate_entailment: bounds={:?}", hybrid_preds);
 
     let normalize_cause = traits::ObligationCause::misc(impl_ty_span, impl_ty_hir_id);
-    let param_env = ty::ParamEnv::new(
-        tcx.intern_predicates(&hybrid_preds.predicates),
-        Reveal::UserFacing,
-        None,
-    );
+    let param_env =
+        ty::ParamEnv::new(tcx.intern_predicates(&hybrid_preds.predicates), Reveal::UserFacing);
     let param_env = traits::normalize_param_env_or_error(
         tcx,
         impl_ty.def_id,
@@ -1232,7 +1221,7 @@ fn compare_projection_bounds<'tcx>(
             })
             .to_predicate(tcx),
         );
-        ty::ParamEnv::new(tcx.intern_predicates(&predicates), Reveal::UserFacing, None)
+        ty::ParamEnv::new(tcx.intern_predicates(&predicates), Reveal::UserFacing)
     };
 
     tcx.infer_ctxt().enter(move |infcx| {
diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs
index 5dc5480c335..247bbf637ce 100644
--- a/compiler/rustc_typeck/src/check/demand.rs
+++ b/compiler/rustc_typeck/src/check/demand.rs
@@ -186,7 +186,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expected: Ty<'tcx>,
         expr_ty: Ty<'tcx>,
     ) {
-        if let ty::Adt(expected_adt, substs) = expected.kind {
+        if let ty::Adt(expected_adt, substs) = expected.kind() {
             if !expected_adt.is_enum() {
                 return;
             }
@@ -362,15 +362,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         false
     }
 
-    fn replace_prefix<A, B, C>(&self, s: A, old: B, new: C) -> Option<String>
-    where
-        A: AsRef<str>,
-        B: AsRef<str>,
-        C: AsRef<str>,
-    {
-        let s = s.as_ref();
-        let old = old.as_ref();
-        if s.starts_with(old) { Some(new.as_ref().to_owned() + &s[old.len()..]) } else { None }
+    fn replace_prefix(&self, s: &str, old: &str, new: &str) -> Option<String> {
+        if let Some(stripped) = s.strip_prefix(old) {
+            Some(new.to_string() + stripped)
+        } else {
+            None
+        }
     }
 
     /// This function is used to determine potential "simple" improvements or users' errors and
@@ -413,12 +410,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // `ExprKind::DropTemps` is semantically irrelevant for these suggestions.
         let expr = expr.peel_drop_temps();
 
-        match (&expr.kind, &expected.kind, &checked_ty.kind) {
-            (_, &ty::Ref(_, exp, _), &ty::Ref(_, check, _)) => match (&exp.kind, &check.kind) {
+        match (&expr.kind, expected.kind(), checked_ty.kind()) {
+            (_, &ty::Ref(_, exp, _), &ty::Ref(_, check, _)) => match (exp.kind(), check.kind()) {
                 (&ty::Str, &ty::Array(arr, _) | &ty::Slice(arr)) if arr == self.tcx.types.u8 => {
                     if let hir::ExprKind::Lit(_) = expr.kind {
                         if let Ok(src) = sm.span_to_snippet(sp) {
-                            if let Some(src) = self.replace_prefix(src, "b\"", "\"") {
+                            if let Some(src) = self.replace_prefix(&src, "b\"", "\"") {
                                 return Some((
                                     sp,
                                     "consider removing the leading `b`",
@@ -432,7 +429,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 (&ty::Array(arr, _) | &ty::Slice(arr), &ty::Str) if arr == self.tcx.types.u8 => {
                     if let hir::ExprKind::Lit(_) = expr.kind {
                         if let Ok(src) = sm.span_to_snippet(sp) {
-                            if let Some(src) = self.replace_prefix(src, "\"", "b\"") {
+                            if let Some(src) = self.replace_prefix(&src, "\"", "b\"") {
                                 return Some((
                                     sp,
                                     "consider adding a leading `b`",
@@ -557,7 +554,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // we may want to suggest removing a `&`.
                 if sm.is_imported(expr.span) {
                     if let Ok(src) = sm.span_to_snippet(sp) {
-                        if let Some(src) = self.replace_prefix(src, "&", "") {
+                        if let Some(src) = self.replace_prefix(&src, "&", "") {
                             return Some((
                                 sp,
                                 "consider removing the borrow",
@@ -594,7 +591,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                     match mutbl_a {
                                         hir::Mutability::Mut => {
                                             if let Some(s) =
-                                                self.replace_prefix(src, "&mut ", new_prefix)
+                                                self.replace_prefix(&src, "&mut ", &new_prefix)
                                             {
                                                 Some((s, Applicability::MachineApplicable))
                                             } else {
@@ -603,7 +600,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                         }
                                         hir::Mutability::Not => {
                                             if let Some(s) =
-                                                self.replace_prefix(src, "&", new_prefix)
+                                                self.replace_prefix(&src, "&", &new_prefix)
                                             {
                                                 Some((s, Applicability::Unspecified))
                                             } else {
@@ -617,7 +614,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                     match mutbl_a {
                                         hir::Mutability::Mut => {
                                             if let Some(s) =
-                                                self.replace_prefix(src, "&mut ", new_prefix)
+                                                self.replace_prefix(&src, "&mut ", &new_prefix)
                                             {
                                                 Some((s, Applicability::MachineApplicable))
                                             } else {
@@ -626,7 +623,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                         }
                                         hir::Mutability::Not => {
                                             if let Some(s) =
-                                                self.replace_prefix(src, "&", new_prefix)
+                                                self.replace_prefix(&src, "&", &new_prefix)
                                             {
                                                 Some((s, Applicability::MachineApplicable))
                                             } else {
@@ -774,7 +771,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let suffix_suggestion = with_opt_paren(&format_args!(
             "{}{}",
             if matches!(
-                (&expected_ty.kind, &checked_ty.kind),
+                (&expected_ty.kind(), &checked_ty.kind()),
                 (ty::Int(_) | ty::Uint(_), ty::Float(_))
             ) {
                 // Remove fractional part from literal, for example `42.0f32` into `42`
@@ -790,7 +787,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         };
         let is_negative_int =
             |expr: &hir::Expr<'_>| matches!(expr.kind, hir::ExprKind::Unary(hir::UnOp::UnNeg, ..));
-        let is_uint = |ty: Ty<'_>| matches!(ty.kind, ty::Uint(..));
+        let is_uint = |ty: Ty<'_>| matches!(ty.kind(), ty::Uint(..));
 
         let in_const_context = self.tcx.hir().is_inside_const_context(expr.hir_id);
 
@@ -857,7 +854,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 err.span_suggestion(expr.span, msg, suggestion, Applicability::MachineApplicable);
             };
 
-        match (&expected_ty.kind, &checked_ty.kind) {
+        match (&expected_ty.kind(), &checked_ty.kind()) {
             (&ty::Int(ref exp), &ty::Int(ref found)) => {
                 let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
                 {
diff --git a/compiler/rustc_typeck/src/check/diverges.rs b/compiler/rustc_typeck/src/check/diverges.rs
new file mode 100644
index 00000000000..963a93a95c2
--- /dev/null
+++ b/compiler/rustc_typeck/src/check/diverges.rs
@@ -0,0 +1,78 @@
+use rustc_span::source_map::DUMMY_SP;
+use rustc_span::{self, Span};
+use std::{cmp, ops};
+
+/// Tracks whether executing a node may exit normally (versus
+/// return/break/panic, which "diverge", leaving dead code in their
+/// wake). Tracked semi-automatically (through type variables marked
+/// as diverging), with some manual adjustments for control-flow
+/// primitives (approximating a CFG).
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub enum Diverges {
+    /// Potentially unknown, some cases converge,
+    /// others require a CFG to determine them.
+    Maybe,
+
+    /// Definitely known to diverge and therefore
+    /// not reach the next sibling or its parent.
+    Always {
+        /// The `Span` points to the expression
+        /// that caused us to diverge
+        /// (e.g. `return`, `break`, etc).
+        span: Span,
+        /// In some cases (e.g. a `match` expression
+        /// where all arms diverge), we may be
+        /// able to provide a more informative
+        /// message to the user.
+        /// If this is `None`, a default message
+        /// will be generated, which is suitable
+        /// for most cases.
+        custom_note: Option<&'static str>,
+    },
+
+    /// Same as `Always` but with a reachability
+    /// warning already emitted.
+    WarnedAlways,
+}
+
+// Convenience impls for combining `Diverges`.
+
+impl ops::BitAnd for Diverges {
+    type Output = Self;
+    fn bitand(self, other: Self) -> Self {
+        cmp::min(self, other)
+    }
+}
+
+impl ops::BitOr for Diverges {
+    type Output = Self;
+    fn bitor(self, other: Self) -> Self {
+        cmp::max(self, other)
+    }
+}
+
+impl ops::BitAndAssign for Diverges {
+    fn bitand_assign(&mut self, other: Self) {
+        *self = *self & other;
+    }
+}
+
+impl ops::BitOrAssign for Diverges {
+    fn bitor_assign(&mut self, other: Self) {
+        *self = *self | other;
+    }
+}
+
+impl Diverges {
+    /// Creates a `Diverges::Always` with the provided `span` and the default note message.
+    pub(super) fn always(span: Span) -> Diverges {
+        Diverges::Always { span, custom_note: None }
+    }
+
+    pub(super) fn is_always(self) -> bool {
+        // Enum comparison ignores the
+        // contents of fields, so we just
+        // fill them in with garbage here.
+        self >= Diverges::Always { span: DUMMY_SP, custom_note: None }
+    }
+}
diff --git a/compiler/rustc_typeck/src/check/dropck.rs b/compiler/rustc_typeck/src/check/dropck.rs
index 434886538fb..ae94a6df5fd 100644
--- a/compiler/rustc_typeck/src/check/dropck.rs
+++ b/compiler/rustc_typeck/src/check/dropck.rs
@@ -34,7 +34,7 @@ use rustc_trait_selection::traits::{ObligationCause, TraitEngine, TraitEngineExt
 pub fn check_drop_impl(tcx: TyCtxt<'_>, drop_impl_did: DefId) -> Result<(), ErrorReported> {
     let dtor_self_type = tcx.type_of(drop_impl_did);
     let dtor_predicates = tcx.predicates_of(drop_impl_did);
-    match dtor_self_type.kind {
+    match dtor_self_type.kind() {
         ty::Adt(adt_def, self_to_impl_substs) => {
             ensure_drop_params_and_item_params_correspond(
                 tcx,
diff --git a/compiler/rustc_typeck/src/check/expectation.rs b/compiler/rustc_typeck/src/check/expectation.rs
new file mode 100644
index 00000000000..fd6fe1406c8
--- /dev/null
+++ b/compiler/rustc_typeck/src/check/expectation.rs
@@ -0,0 +1,117 @@
+use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+use rustc_middle::ty::{self, Ty};
+use rustc_span::{self, Span};
+
+use super::Expectation::*;
+use super::FnCtxt;
+
+/// When type-checking an expression, we propagate downward
+/// whatever type hint we are able in the form of an `Expectation`.
+#[derive(Copy, Clone, Debug)]
+pub enum Expectation<'tcx> {
+    /// We know nothing about what type this expression should have.
+    NoExpectation,
+
+    /// This expression should have the type given (or some subtype).
+    ExpectHasType(Ty<'tcx>),
+
+    /// This expression will be cast to the `Ty`.
+    ExpectCastableToType(Ty<'tcx>),
+
+    /// This rvalue expression will be wrapped in `&` or `Box` and coerced
+    /// to `&Ty` or `Box<Ty>`, respectively. `Ty` is `[A]` or `Trait`.
+    ExpectRvalueLikeUnsized(Ty<'tcx>),
+}
+
+impl<'a, 'tcx> Expectation<'tcx> {
+    // Disregard "castable to" expectations because they
+    // can lead us astray. Consider for example `if cond
+    // {22} else {c} as u8` -- if we propagate the
+    // "castable to u8" constraint to 22, it will pick the
+    // type 22u8, which is overly constrained (c might not
+    // be a u8). In effect, the problem is that the
+    // "castable to" expectation is not the tightest thing
+    // we can say, so we want to drop it in this case.
+    // The tightest thing we can say is "must unify with
+    // else branch". Note that in the case of a "has type"
+    // constraint, this limitation does not hold.
+
+    // If the expected type is just a type variable, then don't use
+    // an expected type. Otherwise, we might write parts of the type
+    // when checking the 'then' block which are incompatible with the
+    // 'else' branch.
+    pub(super) fn adjust_for_branches(&self, fcx: &FnCtxt<'a, 'tcx>) -> Expectation<'tcx> {
+        match *self {
+            ExpectHasType(ety) => {
+                let ety = fcx.shallow_resolve(ety);
+                if !ety.is_ty_var() { ExpectHasType(ety) } else { NoExpectation }
+            }
+            ExpectRvalueLikeUnsized(ety) => ExpectRvalueLikeUnsized(ety),
+            _ => NoExpectation,
+        }
+    }
+
+    /// Provides an expectation for an rvalue expression given an *optional*
+    /// hint, which is not required for type safety (the resulting type might
+    /// be checked higher up, as is the case with `&expr` and `box expr`), but
+    /// is useful in determining the concrete type.
+    ///
+    /// The primary use case is where the expected type is a fat pointer,
+    /// like `&[isize]`. For example, consider the following statement:
+    ///
+    ///    let x: &[isize] = &[1, 2, 3];
+    ///
+    /// In this case, the expected type for the `&[1, 2, 3]` expression is
+    /// `&[isize]`. If however we were to say that `[1, 2, 3]` has the
+    /// expectation `ExpectHasType([isize])`, that would be too strong --
+    /// `[1, 2, 3]` does not have the type `[isize]` but rather `[isize; 3]`.
+    /// It is only the `&[1, 2, 3]` expression as a whole that can be coerced
+    /// to the type `&[isize]`. Therefore, we propagate this more limited hint,
+    /// which still is useful, because it informs integer literals and the like.
+    /// See the test case `test/ui/coerce-expect-unsized.rs` and #20169
+    /// for examples of where this comes up,.
+    pub(super) fn rvalue_hint(fcx: &FnCtxt<'a, 'tcx>, ty: Ty<'tcx>) -> Expectation<'tcx> {
+        match fcx.tcx.struct_tail_without_normalization(ty).kind() {
+            ty::Slice(_) | ty::Str | ty::Dynamic(..) => ExpectRvalueLikeUnsized(ty),
+            _ => ExpectHasType(ty),
+        }
+    }
+
+    // Resolves `expected` by a single level if it is a variable. If
+    // there is no expected type or resolution is not possible (e.g.,
+    // no constraints yet present), just returns `None`.
+    fn resolve(self, fcx: &FnCtxt<'a, 'tcx>) -> Expectation<'tcx> {
+        match self {
+            NoExpectation => NoExpectation,
+            ExpectCastableToType(t) => ExpectCastableToType(fcx.resolve_vars_if_possible(&t)),
+            ExpectHasType(t) => ExpectHasType(fcx.resolve_vars_if_possible(&t)),
+            ExpectRvalueLikeUnsized(t) => ExpectRvalueLikeUnsized(fcx.resolve_vars_if_possible(&t)),
+        }
+    }
+
+    pub(super) fn to_option(self, fcx: &FnCtxt<'a, 'tcx>) -> Option<Ty<'tcx>> {
+        match self.resolve(fcx) {
+            NoExpectation => None,
+            ExpectCastableToType(ty) | ExpectHasType(ty) | ExpectRvalueLikeUnsized(ty) => Some(ty),
+        }
+    }
+
+    /// It sometimes happens that we want to turn an expectation into
+    /// a **hard constraint** (i.e., something that must be satisfied
+    /// for the program to type-check). `only_has_type` will return
+    /// such a constraint, if it exists.
+    pub(super) fn only_has_type(self, fcx: &FnCtxt<'a, 'tcx>) -> Option<Ty<'tcx>> {
+        match self.resolve(fcx) {
+            ExpectHasType(ty) => Some(ty),
+            NoExpectation | ExpectCastableToType(_) | ExpectRvalueLikeUnsized(_) => None,
+        }
+    }
+
+    /// Like `only_has_type`, but instead of returning `None` if no
+    /// hard constraint exists, creates a fresh type variable.
+    pub(super) fn coercion_target_type(self, fcx: &FnCtxt<'a, 'tcx>, span: Span) -> Ty<'tcx> {
+        self.only_has_type(fcx).unwrap_or_else(|| {
+            fcx.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span })
+        })
+    }
+}
diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs
index 1573fb96791..af800eab67a 100644
--- a/compiler/rustc_typeck/src/check/expr.rs
+++ b/compiler/rustc_typeck/src/check/expr.rs
@@ -14,8 +14,13 @@ use crate::check::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExp
 use crate::check::FnCtxt;
 use crate::check::Needs;
 use crate::check::TupleArgumentsFlag::DontTupleArguments;
+use crate::errors::{
+    FieldMultiplySpecifiedInInitializer, FunctionalRecordUpdateOnNonStruct,
+    YieldExprOutsideOfGenerator,
+};
 use crate::type_error_struct;
 
+use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive};
 use rustc_ast as ast;
 use rustc_ast::util::lev_distance::find_best_match_for_name;
 use rustc_data_structures::fx::FxHashMap;
@@ -296,7 +301,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     }
 
     fn check_expr_box(&self, expr: &'tcx hir::Expr<'tcx>, expected: Expectation<'tcx>) -> Ty<'tcx> {
-        let expected_inner = expected.to_option(self).map_or(NoExpectation, |ty| match ty.kind {
+        let expected_inner = expected.to_option(self).map_or(NoExpectation, |ty| match ty.kind() {
             ty::Adt(def, _) if def.is_box() => Expectation::rvalue_hint(self, ty.boxed_ty()),
             _ => NoExpectation,
         });
@@ -346,7 +351,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 hir::UnOp::UnNot => {
                     let result = self.check_user_unop(expr, oprnd_t, unop);
                     // If it's builtin, we can reuse the type, this helps inference.
-                    if !(oprnd_t.is_integral() || oprnd_t.kind == ty::Bool) {
+                    if !(oprnd_t.is_integral() || *oprnd_t.kind() == ty::Bool) {
                         oprnd_t = result;
                     }
                 }
@@ -371,7 +376,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expr: &'tcx hir::Expr<'tcx>,
     ) -> Ty<'tcx> {
         let hint = expected.only_has_type(self).map_or(NoExpectation, |ty| {
-            match ty.kind {
+            match ty.kind() {
                 ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => {
                     if oprnd.is_syntactic_place_expr() {
                         // Places may legitimately have unsized types.
@@ -434,19 +439,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // This is maybe too permissive, since it allows
             // `let u = &raw const Box::new((1,)).0`, which creates an
             // immediately dangling raw pointer.
-            self.typeck_results.borrow().adjustments().get(base.hir_id).map_or(false, |x| {
-                x.iter().any(|adj| if let Adjust::Deref(_) = adj.kind { true } else { false })
-            })
+            self.typeck_results
+                .borrow()
+                .adjustments()
+                .get(base.hir_id)
+                .map_or(false, |x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_))))
         });
         if !is_named {
-            struct_span_err!(
-                self.tcx.sess,
-                oprnd.span,
-                E0745,
-                "cannot take address of a temporary"
-            )
-            .span_label(oprnd.span, "temporary value")
-            .emit();
+            self.tcx.sess.emit_err(AddressOfTemporaryTaken { span: oprnd.span })
         }
     }
 
@@ -473,7 +473,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             _ => self.instantiate_value_path(segs, opt_ty, res, expr.span, expr.hir_id).0,
         };
 
-        if let ty::FnDef(..) = ty.kind {
+        if let ty::FnDef(..) = ty.kind() {
             let fn_sig = ty.fn_sig(tcx);
             if !tcx.features().unsized_locals {
                 // We want to remove some Sized bounds from std functions,
@@ -665,13 +665,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expr: &'tcx hir::Expr<'tcx>,
     ) -> Ty<'tcx> {
         if self.ret_coercion.is_none() {
-            struct_span_err!(
-                self.tcx.sess,
-                expr.span,
-                E0572,
-                "return statement outside of function body",
-            )
-            .emit();
+            self.tcx.sess.emit_err(ReturnStmtOutsideOfFnBody { span: expr.span });
         } else if let Some(ref e) = expr_opt {
             if self.ret_coercion_span.borrow().is_none() {
                 *self.ret_coercion_span.borrow_mut() = Some(e.span);
@@ -740,6 +734,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expr_span: &Span,
     ) {
         if !lhs.is_syntactic_place_expr() {
+            // FIXME: Make this use SessionDiagnostic once error codes can be dynamically set.
             let mut err = self.tcx.sess.struct_span_err_with_code(
                 *expr_span,
                 "invalid left-hand side of assignment",
@@ -764,9 +759,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         rhs: &'tcx hir::Expr<'tcx>,
         span: &Span,
     ) -> Ty<'tcx> {
-        let lhs_ty = self.check_expr_with_needs(&lhs, Needs::MutPlace);
-        let rhs_ty = self.check_expr_coercable_to_type(&rhs, lhs_ty, Some(lhs));
-
         let expected_ty = expected.coercion_target_type(self, expr.span);
         if expected_ty == self.tcx.types.bool {
             // The expected type is `bool` but this will result in `()` so we can reasonably
@@ -774,20 +766,52 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // The likely cause of this is `if foo = bar { .. }`.
             let actual_ty = self.tcx.mk_unit();
             let mut err = self.demand_suptype_diag(expr.span, expected_ty, actual_ty).unwrap();
-            let msg = "try comparing for equality";
-            let left = self.tcx.sess.source_map().span_to_snippet(lhs.span);
-            let right = self.tcx.sess.source_map().span_to_snippet(rhs.span);
-            if let (Ok(left), Ok(right)) = (left, right) {
-                let help = format!("{} == {}", left, right);
-                err.span_suggestion(expr.span, msg, help, Applicability::MaybeIncorrect);
+            let lhs_ty = self.check_expr(&lhs);
+            let rhs_ty = self.check_expr(&rhs);
+            if self.can_coerce(lhs_ty, rhs_ty) {
+                if !lhs.is_syntactic_place_expr() {
+                    // Do not suggest `if let x = y` as `==` is way more likely to be the intention.
+                    if let hir::Node::Expr(hir::Expr {
+                        kind: ExprKind::Match(_, _, hir::MatchSource::IfDesugar { .. }),
+                        ..
+                    }) = self.tcx.hir().get(
+                        self.tcx.hir().get_parent_node(self.tcx.hir().get_parent_node(expr.hir_id)),
+                    ) {
+                        // Likely `if let` intended.
+                        err.span_suggestion_verbose(
+                            expr.span.shrink_to_lo(),
+                            "you might have meant to use pattern matching",
+                            "let ".to_string(),
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                }
+                err.span_suggestion_verbose(
+                    *span,
+                    "you might have meant to compare for equality",
+                    "==".to_string(),
+                    Applicability::MaybeIncorrect,
+                );
             } else {
-                err.help(msg);
+                // Do this to cause extra errors about the assignment.
+                let lhs_ty = self.check_expr_with_needs(&lhs, Needs::MutPlace);
+                let _ = self.check_expr_coercable_to_type(&rhs, lhs_ty, Some(lhs));
             }
-            err.emit();
-        } else {
-            self.check_lhs_assignable(lhs, "E0070", span);
+
+            if self.sess().if_let_suggestions.borrow().get(&expr.span).is_some() {
+                // We already emitted an `if let` suggestion due to an identifier not found.
+                err.delay_as_bug();
+            } else {
+                err.emit();
+            }
+            return self.tcx.ty_error();
         }
 
+        self.check_lhs_assignable(lhs, "E0070", span);
+
+        let lhs_ty = self.check_expr_with_needs(&lhs, Needs::MutPlace);
+        let rhs_ty = self.check_expr_coercable_to_type(&rhs, lhs_ty, Some(lhs));
+
         self.require_type_is_sized(lhs_ty, lhs.span, traits::AssignmentLhsSized);
 
         if lhs_ty.references_error() || rhs_ty.references_error() {
@@ -922,7 +946,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             error,
             Some(args),
         ) {
-            if let ty::Adt(..) = rcvr_t.kind {
+            if let ty::Adt(..) = rcvr_t.kind() {
                 // Try alternative arbitrary self types that could fulfill this call.
                 // FIXME: probe for all types that *could* be arbitrary self-types, not
                 // just this list.
@@ -973,7 +997,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let element_ty = if !args.is_empty() {
             let coerce_to = expected
                 .to_option(self)
-                .and_then(|uty| match uty.kind {
+                .and_then(|uty| match *uty.kind() {
                     ty::Array(ty, _) | ty::Slice(ty) => Some(ty),
                     _ => None,
                 })
@@ -1011,7 +1035,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let count = self.to_const(count);
 
         let uty = match expected {
-            ExpectHasType(uty) => match uty.kind {
+            ExpectHasType(uty) => match *uty.kind() {
                 ty::Array(ty, _) | ty::Slice(ty) => Some(ty),
                 _ => None,
             },
@@ -1048,7 +1072,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ) -> Ty<'tcx> {
         let flds = expected.only_has_type(self).and_then(|ty| {
             let ty = self.resolve_vars_with_obligations(ty);
-            match ty.kind {
+            match ty.kind() {
                 ty::Tuple(ref flds) => Some(&flds[..]),
                 _ => None,
             }
@@ -1091,14 +1115,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // Prohibit struct expressions when non-exhaustive flag is set.
         let adt = adt_ty.ty_adt_def().expect("`check_struct_path` returned non-ADT type");
         if !adt.did.is_local() && variant.is_field_list_non_exhaustive() {
-            struct_span_err!(
-                self.tcx.sess,
-                expr.span,
-                E0639,
-                "cannot create non-exhaustive {} using struct expression",
-                adt.variant_descr()
-            )
-            .emit();
+            self.tcx
+                .sess
+                .emit_err(StructExprNonExhaustive { span: expr.span, what: adt.variant_descr() });
         }
 
         let error_happened = self.check_expr_struct_fields(
@@ -1116,7 +1135,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // when certain fields are assumed to exist that in fact do not.
             if !error_happened {
                 self.check_expr_has_type_or_error(base_expr, adt_ty, |_| {});
-                match adt_ty.kind {
+                match adt_ty.kind() {
                     ty::Adt(adt, substs) if adt.is_struct() => {
                         let fru_field_types = adt
                             .non_enum_variant()
@@ -1136,13 +1155,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             .insert(expr.hir_id, fru_field_types);
                     }
                     _ => {
-                        struct_span_err!(
-                            self.tcx.sess,
-                            base_expr.span,
-                            E0436,
-                            "functional record update syntax requires a struct"
-                        )
-                        .emit();
+                        self.tcx
+                            .sess
+                            .emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span });
                     }
                 }
             }
@@ -1171,7 +1186,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // re-link the regions that EIfEO can erase.
         self.demand_eqtype(span, adt_ty_hint, adt_ty);
 
-        let (substs, adt_kind, kind_name) = match &adt_ty.kind {
+        let (substs, adt_kind, kind_name) = match &adt_ty.kind() {
             &ty::Adt(adt, substs) => (substs, adt.adt_kind(), adt.variant_descr()),
             _ => span_bug!(span, "non-ADT passed to check_expr_struct_fields"),
         };
@@ -1205,18 +1220,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             } else {
                 error_happened = true;
                 if let Some(prev_span) = seen_fields.get(&ident) {
-                    let mut err = struct_span_err!(
-                        self.tcx.sess,
-                        field.ident.span,
-                        E0062,
-                        "field `{}` specified more than once",
-                        ident
-                    );
-
-                    err.span_label(field.ident.span, "used more than once");
-                    err.span_label(*prev_span, format!("first use of `{}`", ident));
-
-                    err.emit();
+                    tcx.sess.emit_err(FieldMultiplySpecifiedInInitializer {
+                        span: field.ident.span,
+                        prev_span: *prev_span,
+                        ident,
+                    });
                 } else {
                     self.report_unknown_field(adt_ty, variant, field, ast_fields, kind_name, span);
                 }
@@ -1235,42 +1243,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 tcx.sess.span_err(span, "union expressions should have exactly one field");
             }
         } else if check_completeness && !error_happened && !remaining_fields.is_empty() {
-            let len = remaining_fields.len();
-
-            let mut displayable_field_names =
-                remaining_fields.keys().map(|ident| ident.as_str()).collect::<Vec<_>>();
-
-            displayable_field_names.sort();
+            let no_accessible_remaining_fields = remaining_fields
+                .iter()
+                .find(|(_, (_, field))| {
+                    field.vis.is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx)
+                })
+                .is_none();
 
-            let truncated_fields_error = if len <= 3 {
-                String::new()
+            if no_accessible_remaining_fields {
+                self.report_no_accessible_fields(adt_ty, span);
             } else {
-                format!(" and {} other field{}", (len - 3), if len - 3 == 1 { "" } else { "s" })
-            };
-
-            let remaining_fields_names = displayable_field_names
-                .iter()
-                .take(3)
-                .map(|n| format!("`{}`", n))
-                .collect::<Vec<_>>()
-                .join(", ");
-
-            struct_span_err!(
-                tcx.sess,
-                span,
-                E0063,
-                "missing field{} {}{} in initializer of `{}`",
-                pluralize!(remaining_fields.len()),
-                remaining_fields_names,
-                truncated_fields_error,
-                adt_ty
-            )
-            .span_label(
-                span,
-                format!("missing {}{}", remaining_fields_names, truncated_fields_error),
-            )
-            .emit();
+                self.report_missing_field(adt_ty, span, remaining_fields);
+            }
         }
+
         error_happened
     }
 
@@ -1287,6 +1273,79 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
+    /// Report an error for a struct field expression when there are fields which aren't provided.
+    ///
+    /// ```ignore (diagnostic)
+    /// error: missing field `you_can_use_this_field` in initializer of `foo::Foo`
+    ///  --> src/main.rs:8:5
+    ///   |
+    /// 8 |     foo::Foo {};
+    ///   |     ^^^^^^^^ missing `you_can_use_this_field`
+    ///
+    /// error: aborting due to previous error
+    /// ```
+    fn report_missing_field(
+        &self,
+        adt_ty: Ty<'tcx>,
+        span: Span,
+        remaining_fields: FxHashMap<Ident, (usize, &ty::FieldDef)>,
+    ) {
+        let tcx = self.tcx;
+        let len = remaining_fields.len();
+
+        let mut displayable_field_names =
+            remaining_fields.keys().map(|ident| ident.as_str()).collect::<Vec<_>>();
+
+        displayable_field_names.sort();
+
+        let truncated_fields_error = if len <= 3 {
+            String::new()
+        } else {
+            format!(" and {} other field{}", (len - 3), if len - 3 == 1 { "" } else { "s" })
+        };
+
+        let remaining_fields_names = displayable_field_names
+            .iter()
+            .take(3)
+            .map(|n| format!("`{}`", n))
+            .collect::<Vec<_>>()
+            .join(", ");
+
+        struct_span_err!(
+            tcx.sess,
+            span,
+            E0063,
+            "missing field{} {}{} in initializer of `{}`",
+            pluralize!(remaining_fields.len()),
+            remaining_fields_names,
+            truncated_fields_error,
+            adt_ty
+        )
+        .span_label(span, format!("missing {}{}", remaining_fields_names, truncated_fields_error))
+        .emit();
+    }
+
+    /// Report an error for a struct field expression when there are no visible fields.
+    ///
+    /// ```ignore (diagnostic)
+    /// error: cannot construct `Foo` with struct literal syntax due to inaccessible fields
+    ///  --> src/main.rs:8:5
+    ///   |
+    /// 8 |     foo::Foo {};
+    ///   |     ^^^^^^^^
+    ///
+    /// error: aborting due to previous error
+    /// ```
+    fn report_no_accessible_fields(&self, adt_ty: Ty<'tcx>, span: Span) {
+        self.tcx.sess.span_err(
+            span,
+            &format!(
+                "cannot construct `{}` with struct literal syntax due to inaccessible fields",
+                adt_ty,
+            ),
+        );
+    }
+
     fn report_unknown_field(
         &self,
         ty: Ty<'tcx>,
@@ -1302,7 +1361,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
         let mut err = self.type_error_struct_with_diag(
             field.ident.span,
-            |actual| match ty.kind {
+            |actual| match ty.kind() {
                 ty::Adt(adt, ..) if adt.is_enum() => struct_span_err!(
                     self.tcx.sess,
                     field.ident.span,
@@ -1352,7 +1411,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         Applicability::MaybeIncorrect,
                     );
                 } else {
-                    match ty.kind {
+                    match ty.kind() {
                         ty::Adt(adt, ..) => {
                             if adt.is_enum() {
                                 err.span_label(
@@ -1439,7 +1498,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let mut private_candidate = None;
         let mut autoderef = self.autoderef(expr.span, expr_t);
         while let Some((base_t, _)) = autoderef.next() {
-            match base_t.kind {
+            match base_t.kind() {
                 ty::Adt(base_def, substs) if !base_def.is_enum() => {
                     debug!("struct named {:?}", base_t);
                     let (ident, def_scope) =
@@ -1542,9 +1601,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             debug!(
                 "suggest_await_on_field_access: normalized_ty={:?}, ty_kind={:?}",
                 self.resolve_vars_if_possible(&normalized_ty),
-                normalized_ty.kind,
+                normalized_ty.kind(),
             );
-            if let ty::Adt(def, _) = normalized_ty.kind {
+            if let ty::Adt(def, _) = normalized_ty.kind() {
                 // no field access on enum type
                 if !def.is_enum() {
                     if def.non_enum_variant().fields.iter().any(|field| field.ident == field_ident)
@@ -1574,7 +1633,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         );
         let mut err = self.no_such_field_err(field.span, field, expr_t);
 
-        match expr_t.peel_refs().kind {
+        match *expr_t.peel_refs().kind() {
             ty::Array(_, len) => {
                 self.maybe_suggest_array_indexing(&mut err, expr, base, field, len);
             }
@@ -1794,7 +1853,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         base_t
                     );
                     // Try to give some advice about indexing tuples.
-                    if let ty::Tuple(..) = base_t.kind {
+                    if let ty::Tuple(..) = base_t.kind() {
                         let mut needs_note = true;
                         // If the index is an integer, we can show the actual
                         // fixed expression:
@@ -1847,13 +1906,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 self.tcx.mk_unit()
             }
             _ => {
-                struct_span_err!(
-                    self.tcx.sess,
-                    expr.span,
-                    E0627,
-                    "yield expression outside of generator literal"
-                )
-                .emit();
+                self.tcx.sess.emit_err(YieldExprOutsideOfGenerator { span: expr.span });
                 self.tcx.mk_unit()
             }
         }
@@ -1879,7 +1932,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // function.
         if is_input {
             let ty = self.structurally_resolved_type(expr.span, &ty);
-            match ty.kind {
+            match *ty.kind() {
                 ty::FnDef(..) => {
                     let fnptr_ty = self.tcx.mk_fn_ptr(ty.fn_sig(self.tcx));
                     self.demand_coerce(expr, ty, fnptr_ty, None, AllowTwoPhase::No);
@@ -1927,7 +1980,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 }
 
 pub(super) fn ty_kind_suggestion(ty: Ty<'_>) -> Option<&'static str> {
-    Some(match ty.kind {
+    Some(match ty.kind() {
         ty::Bool => "true",
         ty::Char => "'a'",
         ty::Int(_) | ty::Uint(_) => "42",
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt.rs b/compiler/rustc_typeck/src/check/fn_ctxt.rs
new file mode 100644
index 00000000000..a03b8064b59
--- /dev/null
+++ b/compiler/rustc_typeck/src/check/fn_ctxt.rs
@@ -0,0 +1,3200 @@
+// ignore-tidy-filelength
+// FIXME: This file seems to have too much functionality wrapped into it,
+// leading to it being too long.
+// Splitting this file may involve abstracting functionality into other files.
+
+use super::callee::{self, DeferredCallResolution};
+use super::coercion::{CoerceMany, DynamicCoerceMany};
+use super::method::{self, MethodCallee, SelfSource};
+use super::Expectation::*;
+use super::TupleArgumentsFlag::*;
+use super::{
+    potentially_plural_count, struct_span_err, BreakableCtxt, Diverges, EnclosingBreakables,
+    Expectation, FallbackMode, Inherited, LocalTy, Needs, TupleArgumentsFlag, UnsafetyState,
+};
+use crate::astconv::{
+    AstConv, ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, PathSeg,
+};
+
+use rustc_ast as ast;
+use rustc_ast::util::parser::ExprPrecedence;
+use rustc_data_structures::captures::Captures;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_errors::ErrorReported;
+use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticId};
+use rustc_hir as hir;
+use rustc_hir::def::{CtorOf, DefKind, Res};
+use rustc_hir::def_id::DefId;
+use rustc_hir::lang_items::LangItem;
+use rustc_hir::{ExprKind, GenericArg, ItemKind, Node, QPath};
+use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse};
+use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282;
+use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+use rustc_infer::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
+use rustc_infer::infer::{self, InferOk, InferResult};
+use rustc_middle::hir::map::blocks::FnLikeNode;
+use rustc_middle::ty::adjustment::{
+    Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
+};
+use rustc_middle::ty::fold::TypeFoldable;
+use rustc_middle::ty::subst::{
+    self, GenericArgKind, InternalSubsts, Subst, SubstsRef, UserSelfTy, UserSubsts,
+};
+use rustc_middle::ty::{
+    self, AdtKind, CanonicalUserType, Const, DefIdTree, GenericParamDefKind, ToPolyTraitRef,
+    ToPredicate, Ty, TyCtxt, UserType,
+};
+use rustc_session::{lint, Session};
+use rustc_span::hygiene::DesugaringKind;
+use rustc_span::source_map::{original_sp, DUMMY_SP};
+use rustc_span::symbol::{kw, sym, Ident};
+use rustc_span::{self, BytePos, MultiSpan, Span};
+use rustc_trait_selection::infer::InferCtxtExt as _;
+use rustc_trait_selection::opaque_types::InferCtxtExt as _;
+use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
+use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
+use rustc_trait_selection::traits::{
+    self, ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt,
+};
+
+use std::cell::{Cell, RefCell};
+use std::collections::hash_map::Entry;
+use std::iter;
+use std::mem::replace;
+use std::ops::Deref;
+use std::slice;
+
+pub struct FnCtxt<'a, 'tcx> {
+    pub(super) body_id: hir::HirId,
+
+    /// The parameter environment used for proving trait obligations
+    /// in this function. This can change when we descend into
+    /// closures (as they bring new things into scope), hence it is
+    /// not part of `Inherited` (as of the time of this writing,
+    /// closures do not yet change the environment, but they will
+    /// eventually).
+    pub(super) param_env: ty::ParamEnv<'tcx>,
+
+    /// Number of errors that had been reported when we started
+    /// checking this function. On exit, if we find that *more* errors
+    /// have been reported, we will skip regionck and other work that
+    /// expects the types within the function to be consistent.
+    // FIXME(matthewjasper) This should not exist, and it's not correct
+    // if type checking is run in parallel.
+    err_count_on_creation: usize,
+
+    /// If `Some`, this stores coercion information for returned
+    /// expressions. If `None`, this is in a context where return is
+    /// inappropriate, such as a const expression.
+    ///
+    /// This is a `RefCell<DynamicCoerceMany>`, which means that we
+    /// can track all the return expressions and then use them to
+    /// compute a useful coercion from the set, similar to a match
+    /// expression or other branching context. You can use methods
+    /// like `expected_ty` to access the declared return type (if
+    /// any).
+    pub(super) ret_coercion: Option<RefCell<DynamicCoerceMany<'tcx>>>,
+
+    pub(super) ret_coercion_impl_trait: Option<Ty<'tcx>>,
+
+    pub(super) ret_type_span: Option<Span>,
+
+    /// Used exclusively to reduce cost of advanced evaluation used for
+    /// more helpful diagnostics.
+    pub(super) in_tail_expr: bool,
+
+    /// First span of a return site that we find. Used in error messages.
+    pub(super) ret_coercion_span: RefCell<Option<Span>>,
+
+    pub(super) resume_yield_tys: Option<(Ty<'tcx>, Ty<'tcx>)>,
+
+    pub(super) ps: RefCell<UnsafetyState>,
+
+    /// Whether the last checked node generates a divergence (e.g.,
+    /// `return` will set this to `Always`). In general, when entering
+    /// an expression or other node in the tree, the initial value
+    /// indicates whether prior parts of the containing expression may
+    /// have diverged. It is then typically set to `Maybe` (and the
+    /// old value remembered) for processing the subparts of the
+    /// current expression. As each subpart is processed, they may set
+    /// the flag to `Always`, etc. Finally, at the end, we take the
+    /// result and "union" it with the original value, so that when we
+    /// return the flag indicates if any subpart of the parent
+    /// expression (up to and including this part) has diverged. So,
+    /// if you read it after evaluating a subexpression `X`, the value
+    /// you get indicates whether any subexpression that was
+    /// evaluating up to and including `X` diverged.
+    ///
+    /// We currently use this flag only for diagnostic purposes:
+    ///
+    /// - To warn about unreachable code: if, after processing a
+    ///   sub-expression but before we have applied the effects of the
+    ///   current node, we see that the flag is set to `Always`, we
+    ///   can issue a warning. This corresponds to something like
+    ///   `foo(return)`; we warn on the `foo()` expression. (We then
+    ///   update the flag to `WarnedAlways` to suppress duplicate
+    ///   reports.) Similarly, if we traverse to a fresh statement (or
+    ///   tail expression) from a `Always` setting, we will issue a
+    ///   warning. This corresponds to something like `{return;
+    ///   foo();}` or `{return; 22}`, where we would warn on the
+    ///   `foo()` or `22`.
+    ///
+    /// An expression represents dead code if, after checking it,
+    /// the diverges flag is set to something other than `Maybe`.
+    pub(super) diverges: Cell<Diverges>,
+
+    /// Whether any child nodes have any type errors.
+    pub(super) has_errors: Cell<bool>,
+
+    pub(super) enclosing_breakables: RefCell<EnclosingBreakables<'tcx>>,
+
+    pub(super) inh: &'a Inherited<'a, 'tcx>,
+}
+
+impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
+    pub fn new(
+        inh: &'a Inherited<'a, 'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        body_id: hir::HirId,
+    ) -> FnCtxt<'a, 'tcx> {
+        FnCtxt {
+            body_id,
+            param_env,
+            err_count_on_creation: inh.tcx.sess.err_count(),
+            ret_coercion: None,
+            ret_coercion_impl_trait: None,
+            ret_type_span: None,
+            in_tail_expr: false,
+            ret_coercion_span: RefCell::new(None),
+            resume_yield_tys: None,
+            ps: RefCell::new(UnsafetyState::function(hir::Unsafety::Normal, hir::CRATE_HIR_ID)),
+            diverges: Cell::new(Diverges::Maybe),
+            has_errors: Cell::new(false),
+            enclosing_breakables: RefCell::new(EnclosingBreakables {
+                stack: Vec::new(),
+                by_id: Default::default(),
+            }),
+            inh,
+        }
+    }
+
+    pub fn sess(&self) -> &Session {
+        &self.tcx.sess
+    }
+
+    pub fn errors_reported_since_creation(&self) -> bool {
+        self.tcx.sess.err_count() > self.err_count_on_creation
+    }
+
+    /// Produces warning on the given node, if the current point in the
+    /// function is unreachable, and there hasn't been another warning.
+    pub(super) fn warn_if_unreachable(&self, id: hir::HirId, span: Span, kind: &str) {
+        // FIXME: Combine these two 'if' expressions into one once
+        // let chains are implemented
+        if let Diverges::Always { span: orig_span, custom_note } = self.diverges.get() {
+            // If span arose from a desugaring of `if` or `while`, then it is the condition itself,
+            // which diverges, that we are about to lint on. This gives suboptimal diagnostics.
+            // Instead, stop here so that the `if`- or `while`-expression's block is linted instead.
+            if !span.is_desugaring(DesugaringKind::CondTemporary)
+                && !span.is_desugaring(DesugaringKind::Async)
+                && !orig_span.is_desugaring(DesugaringKind::Await)
+            {
+                self.diverges.set(Diverges::WarnedAlways);
+
+                debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind);
+
+                self.tcx().struct_span_lint_hir(lint::builtin::UNREACHABLE_CODE, id, span, |lint| {
+                    let msg = format!("unreachable {}", kind);
+                    lint.build(&msg)
+                        .span_label(span, &msg)
+                        .span_label(
+                            orig_span,
+                            custom_note
+                                .unwrap_or("any code following this expression is unreachable"),
+                        )
+                        .emit();
+                })
+            }
+        }
+    }
+
+    pub fn cause(&self, span: Span, code: ObligationCauseCode<'tcx>) -> ObligationCause<'tcx> {
+        ObligationCause::new(span, self.body_id, code)
+    }
+
+    pub fn misc(&self, span: Span) -> ObligationCause<'tcx> {
+        self.cause(span, ObligationCauseCode::MiscObligation)
+    }
+
+    /// Resolves type and const variables in `ty` if possible. Unlike the infcx
+    /// version (resolve_vars_if_possible), this version will
+    /// also select obligations if it seems useful, in an effort
+    /// to get more type information.
+    pub(super) fn resolve_vars_with_obligations(&self, mut ty: Ty<'tcx>) -> Ty<'tcx> {
+        debug!("resolve_vars_with_obligations(ty={:?})", ty);
+
+        // No Infer()? Nothing needs doing.
+        if !ty.has_infer_types_or_consts() {
+            debug!("resolve_vars_with_obligations: ty={:?}", ty);
+            return ty;
+        }
+
+        // If `ty` is a type variable, see whether we already know what it is.
+        ty = self.resolve_vars_if_possible(&ty);
+        if !ty.has_infer_types_or_consts() {
+            debug!("resolve_vars_with_obligations: ty={:?}", ty);
+            return ty;
+        }
+
+        // If not, try resolving pending obligations as much as
+        // possible. This can help substantially when there are
+        // indirect dependencies that don't seem worth tracking
+        // precisely.
+        self.select_obligations_where_possible(false, |_| {});
+        ty = self.resolve_vars_if_possible(&ty);
+
+        debug!("resolve_vars_with_obligations: ty={:?}", ty);
+        ty
+    }
+
+    pub(super) fn record_deferred_call_resolution(
+        &self,
+        closure_def_id: DefId,
+        r: DeferredCallResolution<'tcx>,
+    ) {
+        let mut deferred_call_resolutions = self.deferred_call_resolutions.borrow_mut();
+        deferred_call_resolutions.entry(closure_def_id).or_default().push(r);
+    }
+
+    pub(super) fn remove_deferred_call_resolutions(
+        &self,
+        closure_def_id: DefId,
+    ) -> Vec<DeferredCallResolution<'tcx>> {
+        let mut deferred_call_resolutions = self.deferred_call_resolutions.borrow_mut();
+        deferred_call_resolutions.remove(&closure_def_id).unwrap_or(vec![])
+    }
+
+    pub fn tag(&self) -> String {
+        format!("{:p}", self)
+    }
+
+    pub fn local_ty(&self, span: Span, nid: hir::HirId) -> LocalTy<'tcx> {
+        self.locals.borrow().get(&nid).cloned().unwrap_or_else(|| {
+            span_bug!(span, "no type for local variable {}", self.tcx.hir().node_to_string(nid))
+        })
+    }
+
+    #[inline]
+    pub fn write_ty(&self, id: hir::HirId, ty: Ty<'tcx>) {
+        debug!(
+            "write_ty({:?}, {:?}) in fcx {}",
+            id,
+            self.resolve_vars_if_possible(&ty),
+            self.tag()
+        );
+        self.typeck_results.borrow_mut().node_types_mut().insert(id, ty);
+
+        if ty.references_error() {
+            self.has_errors.set(true);
+            self.set_tainted_by_errors();
+        }
+    }
+
+    pub fn write_field_index(&self, hir_id: hir::HirId, index: usize) {
+        self.typeck_results.borrow_mut().field_indices_mut().insert(hir_id, index);
+    }
+
+    fn write_resolution(&self, hir_id: hir::HirId, r: Result<(DefKind, DefId), ErrorReported>) {
+        self.typeck_results.borrow_mut().type_dependent_defs_mut().insert(hir_id, r);
+    }
+
+    pub fn write_method_call(&self, hir_id: hir::HirId, method: MethodCallee<'tcx>) {
+        debug!("write_method_call(hir_id={:?}, method={:?})", hir_id, method);
+        self.write_resolution(hir_id, Ok((DefKind::AssocFn, method.def_id)));
+        self.write_substs(hir_id, method.substs);
+
+        // When the method is confirmed, the `method.substs` includes
+        // parameters from not just the method, but also the impl of
+        // the method -- in particular, the `Self` type will be fully
+        // resolved. However, those are not something that the "user
+        // specified" -- i.e., those types come from the inferred type
+        // of the receiver, not something the user wrote. So when we
+        // create the user-substs, we want to replace those earlier
+        // types with just the types that the user actually wrote --
+        // that is, those that appear on the *method itself*.
+        //
+        // As an example, if the user wrote something like
+        // `foo.bar::<u32>(...)` -- the `Self` type here will be the
+        // type of `foo` (possibly adjusted), but we don't want to
+        // include that. We want just the `[_, u32]` part.
+        if !method.substs.is_noop() {
+            let method_generics = self.tcx.generics_of(method.def_id);
+            if !method_generics.params.is_empty() {
+                let user_type_annotation = self.infcx.probe(|_| {
+                    let user_substs = UserSubsts {
+                        substs: InternalSubsts::for_item(self.tcx, method.def_id, |param, _| {
+                            let i = param.index as usize;
+                            if i < method_generics.parent_count {
+                                self.infcx.var_for_def(DUMMY_SP, param)
+                            } else {
+                                method.substs[i]
+                            }
+                        }),
+                        user_self_ty: None, // not relevant here
+                    };
+
+                    self.infcx.canonicalize_user_type_annotation(&UserType::TypeOf(
+                        method.def_id,
+                        user_substs,
+                    ))
+                });
+
+                debug!("write_method_call: user_type_annotation={:?}", user_type_annotation);
+                self.write_user_type_annotation(hir_id, user_type_annotation);
+            }
+        }
+    }
+
+    pub fn write_substs(&self, node_id: hir::HirId, substs: SubstsRef<'tcx>) {
+        if !substs.is_noop() {
+            debug!("write_substs({:?}, {:?}) in fcx {}", node_id, substs, self.tag());
+
+            self.typeck_results.borrow_mut().node_substs_mut().insert(node_id, substs);
+        }
+    }
+
+    /// Given the substs that we just converted from the HIR, try to
+    /// canonicalize them and store them as user-given substitutions
+    /// (i.e., substitutions that must be respected by the NLL check).
+    ///
+    /// This should be invoked **before any unifications have
+    /// occurred**, so that annotations like `Vec<_>` are preserved
+    /// properly.
+    pub fn write_user_type_annotation_from_substs(
+        &self,
+        hir_id: hir::HirId,
+        def_id: DefId,
+        substs: SubstsRef<'tcx>,
+        user_self_ty: Option<UserSelfTy<'tcx>>,
+    ) {
+        debug!(
+            "write_user_type_annotation_from_substs: hir_id={:?} def_id={:?} substs={:?} \
+             user_self_ty={:?} in fcx {}",
+            hir_id,
+            def_id,
+            substs,
+            user_self_ty,
+            self.tag(),
+        );
+
+        if Self::can_contain_user_lifetime_bounds((substs, user_self_ty)) {
+            let canonicalized = self.infcx.canonicalize_user_type_annotation(&UserType::TypeOf(
+                def_id,
+                UserSubsts { substs, user_self_ty },
+            ));
+            debug!("write_user_type_annotation_from_substs: canonicalized={:?}", canonicalized);
+            self.write_user_type_annotation(hir_id, canonicalized);
+        }
+    }
+
+    pub fn write_user_type_annotation(
+        &self,
+        hir_id: hir::HirId,
+        canonical_user_type_annotation: CanonicalUserType<'tcx>,
+    ) {
+        debug!(
+            "write_user_type_annotation: hir_id={:?} canonical_user_type_annotation={:?} tag={}",
+            hir_id,
+            canonical_user_type_annotation,
+            self.tag(),
+        );
+
+        if !canonical_user_type_annotation.is_identity() {
+            self.typeck_results
+                .borrow_mut()
+                .user_provided_types_mut()
+                .insert(hir_id, canonical_user_type_annotation);
+        } else {
+            debug!("write_user_type_annotation: skipping identity substs");
+        }
+    }
+
+    pub fn apply_adjustments(&self, expr: &hir::Expr<'_>, adj: Vec<Adjustment<'tcx>>) {
+        debug!("apply_adjustments(expr={:?}, adj={:?})", expr, adj);
+
+        if adj.is_empty() {
+            return;
+        }
+
+        let autoborrow_mut = adj.iter().any(|adj| {
+            matches!(adj, &Adjustment {
+                kind: Adjust::Borrow(AutoBorrow::Ref(_, AutoBorrowMutability::Mut { .. })),
+                ..
+            })
+        });
+
+        match self.typeck_results.borrow_mut().adjustments_mut().entry(expr.hir_id) {
+            Entry::Vacant(entry) => {
+                entry.insert(adj);
+            }
+            Entry::Occupied(mut entry) => {
+                debug!(" - composing on top of {:?}", entry.get());
+                match (&entry.get()[..], &adj[..]) {
+                    // Applying any adjustment on top of a NeverToAny
+                    // is a valid NeverToAny adjustment, because it can't
+                    // be reached.
+                    (&[Adjustment { kind: Adjust::NeverToAny, .. }], _) => return,
+                    (&[
+                        Adjustment { kind: Adjust::Deref(_), .. },
+                        Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. },
+                    ], &[
+                        Adjustment { kind: Adjust::Deref(_), .. },
+                        .. // Any following adjustments are allowed.
+                    ]) => {
+                        // A reborrow has no effect before a dereference.
+                    }
+                    // FIXME: currently we never try to compose autoderefs
+                    // and ReifyFnPointer/UnsafeFnPointer, but we could.
+                    _ =>
+                        bug!("while adjusting {:?}, can't compose {:?} and {:?}",
+                             expr, entry.get(), adj)
+                };
+                *entry.get_mut() = adj;
+            }
+        }
+
+        // If there is an mutable auto-borrow, it is equivalent to `&mut <expr>`.
+        // In this case implicit use of `Deref` and `Index` within `<expr>` should
+        // instead be `DerefMut` and `IndexMut`, so fix those up.
+        if autoborrow_mut {
+            self.convert_place_derefs_to_mutable(expr);
+        }
+    }
+
+    /// Basically whenever we are converting from a type scheme into
+    /// the fn body space, we always want to normalize associated
+    /// types as well. This function combines the two.
+    fn instantiate_type_scheme<T>(&self, span: Span, substs: SubstsRef<'tcx>, value: &T) -> T
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        let value = value.subst(self.tcx, substs);
+        let result = self.normalize_associated_types_in(span, &value);
+        debug!("instantiate_type_scheme(value={:?}, substs={:?}) = {:?}", value, substs, result);
+        result
+    }
+
+    /// As `instantiate_type_scheme`, but for the bounds found in a
+    /// generic type scheme.
+    fn instantiate_bounds(
+        &self,
+        span: Span,
+        def_id: DefId,
+        substs: SubstsRef<'tcx>,
+    ) -> (ty::InstantiatedPredicates<'tcx>, Vec<Span>) {
+        let bounds = self.tcx.predicates_of(def_id);
+        let spans: Vec<Span> = bounds.predicates.iter().map(|(_, span)| *span).collect();
+        let result = bounds.instantiate(self.tcx, substs);
+        let result = self.normalize_associated_types_in(span, &result);
+        debug!(
+            "instantiate_bounds(bounds={:?}, substs={:?}) = {:?}, {:?}",
+            bounds, substs, result, spans,
+        );
+        (result, spans)
+    }
+
+    /// Replaces the opaque types from the given value with type variables,
+    /// and records the `OpaqueTypeMap` for later use during writeback. See
+    /// `InferCtxt::instantiate_opaque_types` for more details.
+    pub(super) fn instantiate_opaque_types_from_value<T: TypeFoldable<'tcx>>(
+        &self,
+        parent_id: hir::HirId,
+        value: &T,
+        value_span: Span,
+    ) -> T {
+        let parent_def_id = self.tcx.hir().local_def_id(parent_id);
+        debug!(
+            "instantiate_opaque_types_from_value(parent_def_id={:?}, value={:?})",
+            parent_def_id, value
+        );
+
+        let (value, opaque_type_map) =
+            self.register_infer_ok_obligations(self.instantiate_opaque_types(
+                parent_def_id,
+                self.body_id,
+                self.param_env,
+                value,
+                value_span,
+            ));
+
+        let mut opaque_types = self.opaque_types.borrow_mut();
+        let mut opaque_types_vars = self.opaque_types_vars.borrow_mut();
+        for (ty, decl) in opaque_type_map {
+            let _ = opaque_types.insert(ty, decl);
+            let _ = opaque_types_vars.insert(decl.concrete_ty, decl.opaque_type);
+        }
+
+        value
+    }
+
+    pub(super) fn normalize_associated_types_in<T>(&self, span: Span, value: &T) -> T
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        self.inh.normalize_associated_types_in(span, self.body_id, self.param_env, value)
+    }
+
+    pub(super) fn normalize_associated_types_in_as_infer_ok<T>(
+        &self,
+        span: Span,
+        value: &T,
+    ) -> InferOk<'tcx, T>
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        self.inh.partially_normalize_associated_types_in(span, self.body_id, self.param_env, value)
+    }
+
+    pub fn require_type_meets(
+        &self,
+        ty: Ty<'tcx>,
+        span: Span,
+        code: traits::ObligationCauseCode<'tcx>,
+        def_id: DefId,
+    ) {
+        self.register_bound(ty, def_id, traits::ObligationCause::new(span, self.body_id, code));
+    }
+
+    pub fn require_type_is_sized(
+        &self,
+        ty: Ty<'tcx>,
+        span: Span,
+        code: traits::ObligationCauseCode<'tcx>,
+    ) {
+        if !ty.references_error() {
+            let lang_item = self.tcx.require_lang_item(LangItem::Sized, None);
+            self.require_type_meets(ty, span, code, lang_item);
+        }
+    }
+
+    pub fn require_type_is_sized_deferred(
+        &self,
+        ty: Ty<'tcx>,
+        span: Span,
+        code: traits::ObligationCauseCode<'tcx>,
+    ) {
+        if !ty.references_error() {
+            self.deferred_sized_obligations.borrow_mut().push((ty, span, code));
+        }
+    }
+
+    pub fn register_bound(
+        &self,
+        ty: Ty<'tcx>,
+        def_id: DefId,
+        cause: traits::ObligationCause<'tcx>,
+    ) {
+        if !ty.references_error() {
+            self.fulfillment_cx.borrow_mut().register_bound(
+                self,
+                self.param_env,
+                ty,
+                def_id,
+                cause,
+            );
+        }
+    }
+
+    pub fn to_ty(&self, ast_t: &hir::Ty<'_>) -> Ty<'tcx> {
+        let t = AstConv::ast_ty_to_ty(self, ast_t);
+        self.register_wf_obligation(t.into(), ast_t.span, traits::MiscObligation);
+        t
+    }
+
+    pub fn to_ty_saving_user_provided_ty(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> {
+        let ty = self.to_ty(ast_ty);
+        debug!("to_ty_saving_user_provided_ty: ty={:?}", ty);
+
+        if Self::can_contain_user_lifetime_bounds(ty) {
+            let c_ty = self.infcx.canonicalize_response(&UserType::Ty(ty));
+            debug!("to_ty_saving_user_provided_ty: c_ty={:?}", c_ty);
+            self.typeck_results.borrow_mut().user_provided_types_mut().insert(ast_ty.hir_id, c_ty);
+        }
+
+        ty
+    }
+
+    pub fn to_const(&self, ast_c: &hir::AnonConst) -> &'tcx ty::Const<'tcx> {
+        let const_def_id = self.tcx.hir().local_def_id(ast_c.hir_id);
+        let c = ty::Const::from_anon_const(self.tcx, const_def_id);
+        self.register_wf_obligation(
+            c.into(),
+            self.tcx.hir().span(ast_c.hir_id),
+            ObligationCauseCode::MiscObligation,
+        );
+        c
+    }
+
+    pub fn const_arg_to_const(
+        &self,
+        ast_c: &hir::AnonConst,
+        param_def_id: DefId,
+    ) -> &'tcx ty::Const<'tcx> {
+        let const_def = ty::WithOptConstParam {
+            did: self.tcx.hir().local_def_id(ast_c.hir_id),
+            const_param_did: Some(param_def_id),
+        };
+        let c = ty::Const::from_opt_const_arg_anon_const(self.tcx, const_def);
+        self.register_wf_obligation(
+            c.into(),
+            self.tcx.hir().span(ast_c.hir_id),
+            ObligationCauseCode::MiscObligation,
+        );
+        c
+    }
+
+    // If the type given by the user has free regions, save it for later, since
+    // NLL would like to enforce those. Also pass in types that involve
+    // projections, since those can resolve to `'static` bounds (modulo #54940,
+    // which hopefully will be fixed by the time you see this comment, dear
+    // reader, although I have my doubts). Also pass in types with inference
+    // types, because they may be repeated. Other sorts of things are already
+    // sufficiently enforced with erased regions. =)
+    fn can_contain_user_lifetime_bounds<T>(t: T) -> bool
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        t.has_free_regions() || t.has_projections() || t.has_infer_types()
+    }
+
+    pub fn node_ty(&self, id: hir::HirId) -> Ty<'tcx> {
+        match self.typeck_results.borrow().node_types().get(id) {
+            Some(&t) => t,
+            None if self.is_tainted_by_errors() => self.tcx.ty_error(),
+            None => {
+                bug!(
+                    "no type for node {}: {} in fcx {}",
+                    id,
+                    self.tcx.hir().node_to_string(id),
+                    self.tag()
+                );
+            }
+        }
+    }
+
+    /// Registers an obligation for checking later, during regionck, that `arg` is well-formed.
+    pub fn register_wf_obligation(
+        &self,
+        arg: subst::GenericArg<'tcx>,
+        span: Span,
+        code: traits::ObligationCauseCode<'tcx>,
+    ) {
+        // WF obligations never themselves fail, so no real need to give a detailed cause:
+        let cause = traits::ObligationCause::new(span, self.body_id, code);
+        self.register_predicate(traits::Obligation::new(
+            cause,
+            self.param_env,
+            ty::PredicateAtom::WellFormed(arg).to_predicate(self.tcx),
+        ));
+    }
+
+    /// Registers obligations that all `substs` are well-formed.
+    pub fn add_wf_bounds(&self, substs: SubstsRef<'tcx>, expr: &hir::Expr<'_>) {
+        for arg in substs.iter().filter(|arg| {
+            matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..))
+        }) {
+            self.register_wf_obligation(arg, expr.span, traits::MiscObligation);
+        }
+    }
+
+    /// Given a fully substituted set of bounds (`generic_bounds`), and the values with which each
+    /// type/region parameter was instantiated (`substs`), creates and registers suitable
+    /// trait/region obligations.
+    ///
+    /// For example, if there is a function:
+    ///
+    /// ```
+    /// fn foo<'a,T:'a>(...)
+    /// ```
+    ///
+    /// and a reference:
+    ///
+    /// ```
+    /// let f = foo;
+    /// ```
+    ///
+    /// Then we will create a fresh region variable `'$0` and a fresh type variable `$1` for `'a`
+    /// and `T`. This routine will add a region obligation `$1:'$0` and register it locally.
+    pub fn add_obligations_for_parameters(
+        &self,
+        cause: traits::ObligationCause<'tcx>,
+        predicates: ty::InstantiatedPredicates<'tcx>,
+    ) {
+        assert!(!predicates.has_escaping_bound_vars());
+
+        debug!("add_obligations_for_parameters(predicates={:?})", predicates);
+
+        for obligation in traits::predicates_for_generics(cause, self.param_env, predicates) {
+            self.register_predicate(obligation);
+        }
+    }
+
+    // FIXME(arielb1): use this instead of field.ty everywhere
+    // Only for fields! Returns <none> for methods>
+    // Indifferent to privacy flags
+    pub fn field_ty(
+        &self,
+        span: Span,
+        field: &'tcx ty::FieldDef,
+        substs: SubstsRef<'tcx>,
+    ) -> Ty<'tcx> {
+        self.normalize_associated_types_in(span, &field.ty(self.tcx, substs))
+    }
+
+    pub(super) fn check_casts(&self) {
+        let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut();
+        for cast in deferred_cast_checks.drain(..) {
+            cast.check(self);
+        }
+    }
+
+    pub(super) fn resolve_generator_interiors(&self, def_id: DefId) {
+        let mut generators = self.deferred_generator_interiors.borrow_mut();
+        for (body_id, interior, kind) in generators.drain(..) {
+            self.select_obligations_where_possible(false, |_| {});
+            super::generator_interior::resolve_interior(self, def_id, body_id, interior, kind);
+        }
+    }
+
+    // Tries to apply a fallback to `ty` if it is an unsolved variable.
+    //
+    // - Unconstrained ints are replaced with `i32`.
+    //
+    // - Unconstrained floats are replaced with with `f64`.
+    //
+    // - Non-numerics get replaced with `!` when `#![feature(never_type_fallback)]`
+    //   is enabled. Otherwise, they are replaced with `()`.
+    //
+    // Fallback becomes very dubious if we have encountered type-checking errors.
+    // In that case, fallback to Error.
+    // The return value indicates whether fallback has occurred.
+    pub(super) fn fallback_if_possible(&self, ty: Ty<'tcx>, mode: FallbackMode) -> bool {
+        use rustc_middle::ty::error::UnconstrainedNumeric::Neither;
+        use rustc_middle::ty::error::UnconstrainedNumeric::{UnconstrainedFloat, UnconstrainedInt};
+
+        assert!(ty.is_ty_infer());
+        let fallback = match self.type_is_unconstrained_numeric(ty) {
+            _ if self.is_tainted_by_errors() => self.tcx().ty_error(),
+            UnconstrainedInt => self.tcx.types.i32,
+            UnconstrainedFloat => self.tcx.types.f64,
+            Neither if self.type_var_diverges(ty) => self.tcx.mk_diverging_default(),
+            Neither => {
+                // This type variable was created from the instantiation of an opaque
+                // type. The fact that we're attempting to perform fallback for it
+                // means that the function neither constrained it to a concrete
+                // type, nor to the opaque type itself.
+                //
+                // For example, in this code:
+                //
+                //```
+                // type MyType = impl Copy;
+                // fn defining_use() -> MyType { true }
+                // fn other_use() -> MyType { defining_use() }
+                // ```
+                //
+                // `defining_use` will constrain the instantiated inference
+                // variable to `bool`, while `other_use` will constrain
+                // the instantiated inference variable to `MyType`.
+                //
+                // When we process opaque types during writeback, we
+                // will handle cases like `other_use`, and not count
+                // them as defining usages
+                //
+                // However, we also need to handle cases like this:
+                //
+                // ```rust
+                // pub type Foo = impl Copy;
+                // fn produce() -> Option<Foo> {
+                //     None
+                //  }
+                //  ```
+                //
+                // In the above snippet, the inference variable created by
+                // instantiating `Option<Foo>` will be completely unconstrained.
+                // We treat this as a non-defining use by making the inference
+                // variable fall back to the opaque type itself.
+                if let FallbackMode::All = mode {
+                    if let Some(opaque_ty) = self.opaque_types_vars.borrow().get(ty) {
+                        debug!(
+                            "fallback_if_possible: falling back opaque type var {:?} to {:?}",
+                            ty, opaque_ty
+                        );
+                        *opaque_ty
+                    } else {
+                        return false;
+                    }
+                } else {
+                    return false;
+                }
+            }
+        };
+        debug!("fallback_if_possible: defaulting `{:?}` to `{:?}`", ty, fallback);
+        self.demand_eqtype(rustc_span::DUMMY_SP, ty, fallback);
+        true
+    }
+
+    pub(super) fn select_all_obligations_or_error(&self) {
+        debug!("select_all_obligations_or_error");
+        if let Err(errors) = self.fulfillment_cx.borrow_mut().select_all_or_error(&self) {
+            self.report_fulfillment_errors(&errors, self.inh.body_id, false);
+        }
+    }
+
+    /// Select as many obligations as we can at present.
+    pub(super) fn select_obligations_where_possible(
+        &self,
+        fallback_has_occurred: bool,
+        mutate_fullfillment_errors: impl Fn(&mut Vec<traits::FulfillmentError<'tcx>>),
+    ) {
+        let result = self.fulfillment_cx.borrow_mut().select_where_possible(self);
+        if let Err(mut errors) = result {
+            mutate_fullfillment_errors(&mut errors);
+            self.report_fulfillment_errors(&errors, self.inh.body_id, fallback_has_occurred);
+        }
+    }
+
+    /// For the overloaded place expressions (`*x`, `x[3]`), the trait
+    /// returns a type of `&T`, but the actual type we assign to the
+    /// *expression* is `T`. So this function just peels off the return
+    /// type by one layer to yield `T`.
+    pub(super) fn make_overloaded_place_return_type(
+        &self,
+        method: MethodCallee<'tcx>,
+    ) -> ty::TypeAndMut<'tcx> {
+        // extract method return type, which will be &T;
+        let ret_ty = method.sig.output();
+
+        // method returns &T, but the type as visible to user is T, so deref
+        ret_ty.builtin_deref(true).unwrap()
+    }
+
+    pub(super) fn check_method_argument_types(
+        &self,
+        sp: Span,
+        expr: &'tcx hir::Expr<'tcx>,
+        method: Result<MethodCallee<'tcx>, ()>,
+        args_no_rcvr: &'tcx [hir::Expr<'tcx>],
+        tuple_arguments: TupleArgumentsFlag,
+        expected: Expectation<'tcx>,
+    ) -> Ty<'tcx> {
+        let has_error = match method {
+            Ok(method) => method.substs.references_error() || method.sig.references_error(),
+            Err(_) => true,
+        };
+        if has_error {
+            let err_inputs = self.err_args(args_no_rcvr.len());
+
+            let err_inputs = match tuple_arguments {
+                DontTupleArguments => err_inputs,
+                TupleArguments => vec![self.tcx.intern_tup(&err_inputs[..])],
+            };
+
+            self.check_argument_types(
+                sp,
+                expr,
+                &err_inputs[..],
+                &[],
+                args_no_rcvr,
+                false,
+                tuple_arguments,
+                None,
+            );
+            return self.tcx.ty_error();
+        }
+
+        let method = method.unwrap();
+        // HACK(eddyb) ignore self in the definition (see above).
+        let expected_arg_tys = self.expected_inputs_for_expected_output(
+            sp,
+            expected,
+            method.sig.output(),
+            &method.sig.inputs()[1..],
+        );
+        self.check_argument_types(
+            sp,
+            expr,
+            &method.sig.inputs()[1..],
+            &expected_arg_tys[..],
+            args_no_rcvr,
+            method.sig.c_variadic,
+            tuple_arguments,
+            self.tcx.hir().span_if_local(method.def_id),
+        );
+        method.sig.output()
+    }
+
+    fn self_type_matches_expected_vid(
+        &self,
+        trait_ref: ty::PolyTraitRef<'tcx>,
+        expected_vid: ty::TyVid,
+    ) -> bool {
+        let self_ty = self.shallow_resolve(trait_ref.skip_binder().self_ty());
+        debug!(
+            "self_type_matches_expected_vid(trait_ref={:?}, self_ty={:?}, expected_vid={:?})",
+            trait_ref, self_ty, expected_vid
+        );
+        match *self_ty.kind() {
+            ty::Infer(ty::TyVar(found_vid)) => {
+                // FIXME: consider using `sub_root_var` here so we
+                // can see through subtyping.
+                let found_vid = self.root_var(found_vid);
+                debug!("self_type_matches_expected_vid - found_vid={:?}", found_vid);
+                expected_vid == found_vid
+            }
+            _ => false,
+        }
+    }
+
+    pub(super) fn obligations_for_self_ty<'b>(
+        &'b self,
+        self_ty: ty::TyVid,
+    ) -> impl Iterator<Item = (ty::PolyTraitRef<'tcx>, traits::PredicateObligation<'tcx>)>
+    + Captures<'tcx>
+    + 'b {
+        // FIXME: consider using `sub_root_var` here so we
+        // can see through subtyping.
+        let ty_var_root = self.root_var(self_ty);
+        debug!(
+            "obligations_for_self_ty: self_ty={:?} ty_var_root={:?} pending_obligations={:?}",
+            self_ty,
+            ty_var_root,
+            self.fulfillment_cx.borrow().pending_obligations()
+        );
+
+        self.fulfillment_cx
+            .borrow()
+            .pending_obligations()
+            .into_iter()
+            .filter_map(move |obligation| {
+                match obligation.predicate.skip_binders() {
+                    ty::PredicateAtom::Projection(data) => {
+                        Some((ty::Binder::bind(data).to_poly_trait_ref(self.tcx), obligation))
+                    }
+                    ty::PredicateAtom::Trait(data, _) => {
+                        Some((ty::Binder::bind(data).to_poly_trait_ref(), obligation))
+                    }
+                    ty::PredicateAtom::Subtype(..) => None,
+                    ty::PredicateAtom::RegionOutlives(..) => None,
+                    ty::PredicateAtom::TypeOutlives(..) => None,
+                    ty::PredicateAtom::WellFormed(..) => None,
+                    ty::PredicateAtom::ObjectSafe(..) => None,
+                    ty::PredicateAtom::ConstEvaluatable(..) => None,
+                    ty::PredicateAtom::ConstEquate(..) => None,
+                    // N.B., this predicate is created by breaking down a
+                    // `ClosureType: FnFoo()` predicate, where
+                    // `ClosureType` represents some `Closure`. It can't
+                    // possibly be referring to the current closure,
+                    // because we haven't produced the `Closure` for
+                    // this closure yet; this is exactly why the other
+                    // code is looking for a self type of a unresolved
+                    // inference variable.
+                    ty::PredicateAtom::ClosureKind(..) => None,
+                    ty::PredicateAtom::TypeWellFormedFromEnv(..) => None,
+                }
+            })
+            .filter(move |(tr, _)| self.self_type_matches_expected_vid(*tr, ty_var_root))
+    }
+
+    pub(super) fn type_var_is_sized(&self, self_ty: ty::TyVid) -> bool {
+        self.obligations_for_self_ty(self_ty)
+            .any(|(tr, _)| Some(tr.def_id()) == self.tcx.lang_items().sized_trait())
+    }
+
+    /// Generic function that factors out common logic from function calls,
+    /// method calls and overloaded operators.
+    pub(super) fn check_argument_types(
+        &self,
+        sp: Span,
+        expr: &'tcx hir::Expr<'tcx>,
+        fn_inputs: &[Ty<'tcx>],
+        expected_arg_tys: &[Ty<'tcx>],
+        args: &'tcx [hir::Expr<'tcx>],
+        c_variadic: bool,
+        tuple_arguments: TupleArgumentsFlag,
+        def_span: Option<Span>,
+    ) {
+        let tcx = self.tcx;
+        // Grab the argument types, supplying fresh type variables
+        // if the wrong number of arguments were supplied
+        let supplied_arg_count = if tuple_arguments == DontTupleArguments { args.len() } else { 1 };
+
+        // All the input types from the fn signature must outlive the call
+        // so as to validate implied bounds.
+        for (&fn_input_ty, arg_expr) in fn_inputs.iter().zip(args.iter()) {
+            self.register_wf_obligation(fn_input_ty.into(), arg_expr.span, traits::MiscObligation);
+        }
+
+        let expected_arg_count = fn_inputs.len();
+
+        let param_count_error = |expected_count: usize,
+                                 arg_count: usize,
+                                 error_code: &str,
+                                 c_variadic: bool,
+                                 sugg_unit: bool| {
+            let (span, start_span, args) = match &expr.kind {
+                hir::ExprKind::Call(hir::Expr { span, .. }, args) => (*span, *span, &args[..]),
+                hir::ExprKind::MethodCall(path_segment, span, args, _) => (
+                    *span,
+                    // `sp` doesn't point at the whole `foo.bar()`, only at `bar`.
+                    path_segment
+                        .args
+                        .and_then(|args| args.args.iter().last())
+                        // Account for `foo.bar::<T>()`.
+                        .map(|arg| {
+                            // Skip the closing `>`.
+                            tcx.sess
+                                .source_map()
+                                .next_point(tcx.sess.source_map().next_point(arg.span()))
+                        })
+                        .unwrap_or(*span),
+                    &args[1..], // Skip the receiver.
+                ),
+                k => span_bug!(sp, "checking argument types on a non-call: `{:?}`", k),
+            };
+            let arg_spans = if args.is_empty() {
+                // foo()
+                // ^^^-- supplied 0 arguments
+                // |
+                // expected 2 arguments
+                vec![tcx.sess.source_map().next_point(start_span).with_hi(sp.hi())]
+            } else {
+                // foo(1, 2, 3)
+                // ^^^ -  -  - supplied 3 arguments
+                // |
+                // expected 2 arguments
+                args.iter().map(|arg| arg.span).collect::<Vec<Span>>()
+            };
+
+            let mut err = tcx.sess.struct_span_err_with_code(
+                span,
+                &format!(
+                    "this function takes {}{} but {} {} supplied",
+                    if c_variadic { "at least " } else { "" },
+                    potentially_plural_count(expected_count, "argument"),
+                    potentially_plural_count(arg_count, "argument"),
+                    if arg_count == 1 { "was" } else { "were" }
+                ),
+                DiagnosticId::Error(error_code.to_owned()),
+            );
+            let label = format!("supplied {}", potentially_plural_count(arg_count, "argument"));
+            for (i, span) in arg_spans.into_iter().enumerate() {
+                err.span_label(
+                    span,
+                    if arg_count == 0 || i + 1 == arg_count { &label } else { "" },
+                );
+            }
+
+            if let Some(def_s) = def_span.map(|sp| tcx.sess.source_map().guess_head_span(sp)) {
+                err.span_label(def_s, "defined here");
+            }
+            if sugg_unit {
+                let sugg_span = tcx.sess.source_map().end_point(expr.span);
+                // remove closing `)` from the span
+                let sugg_span = sugg_span.shrink_to_lo();
+                err.span_suggestion(
+                    sugg_span,
+                    "expected the unit value `()`; create it with empty parentheses",
+                    String::from("()"),
+                    Applicability::MachineApplicable,
+                );
+            } else {
+                err.span_label(
+                    span,
+                    format!(
+                        "expected {}{}",
+                        if c_variadic { "at least " } else { "" },
+                        potentially_plural_count(expected_count, "argument")
+                    ),
+                );
+            }
+            err.emit();
+        };
+
+        let mut expected_arg_tys = expected_arg_tys.to_vec();
+
+        let formal_tys = if tuple_arguments == TupleArguments {
+            let tuple_type = self.structurally_resolved_type(sp, fn_inputs[0]);
+            match tuple_type.kind() {
+                ty::Tuple(arg_types) if arg_types.len() != args.len() => {
+                    param_count_error(arg_types.len(), args.len(), "E0057", false, false);
+                    expected_arg_tys = vec![];
+                    self.err_args(args.len())
+                }
+                ty::Tuple(arg_types) => {
+                    expected_arg_tys = match expected_arg_tys.get(0) {
+                        Some(&ty) => match ty.kind() {
+                            ty::Tuple(ref tys) => tys.iter().map(|k| k.expect_ty()).collect(),
+                            _ => vec![],
+                        },
+                        None => vec![],
+                    };
+                    arg_types.iter().map(|k| k.expect_ty()).collect()
+                }
+                _ => {
+                    struct_span_err!(
+                        tcx.sess,
+                        sp,
+                        E0059,
+                        "cannot use call notation; the first type parameter \
+                         for the function trait is neither a tuple nor unit"
+                    )
+                    .emit();
+                    expected_arg_tys = vec![];
+                    self.err_args(args.len())
+                }
+            }
+        } else if expected_arg_count == supplied_arg_count {
+            fn_inputs.to_vec()
+        } else if c_variadic {
+            if supplied_arg_count >= expected_arg_count {
+                fn_inputs.to_vec()
+            } else {
+                param_count_error(expected_arg_count, supplied_arg_count, "E0060", true, false);
+                expected_arg_tys = vec![];
+                self.err_args(supplied_arg_count)
+            }
+        } else {
+            // is the missing argument of type `()`?
+            let sugg_unit = if expected_arg_tys.len() == 1 && supplied_arg_count == 0 {
+                self.resolve_vars_if_possible(&expected_arg_tys[0]).is_unit()
+            } else if fn_inputs.len() == 1 && supplied_arg_count == 0 {
+                self.resolve_vars_if_possible(&fn_inputs[0]).is_unit()
+            } else {
+                false
+            };
+            param_count_error(expected_arg_count, supplied_arg_count, "E0061", false, sugg_unit);
+
+            expected_arg_tys = vec![];
+            self.err_args(supplied_arg_count)
+        };
+
+        debug!(
+            "check_argument_types: formal_tys={:?}",
+            formal_tys.iter().map(|t| self.ty_to_string(*t)).collect::<Vec<String>>()
+        );
+
+        // If there is no expectation, expect formal_tys.
+        let expected_arg_tys =
+            if !expected_arg_tys.is_empty() { expected_arg_tys } else { formal_tys.clone() };
+
+        let mut final_arg_types: Vec<(usize, Ty<'_>, Ty<'_>)> = vec![];
+
+        // Check the arguments.
+        // We do this in a pretty awful way: first we type-check any arguments
+        // that are not closures, then we type-check the closures. This is so
+        // that we have more information about the types of arguments when we
+        // type-check the functions. This isn't really the right way to do this.
+        for &check_closures in &[false, true] {
+            debug!("check_closures={}", check_closures);
+
+            // More awful hacks: before we check argument types, try to do
+            // an "opportunistic" trait resolution of any trait bounds on
+            // the call. This helps coercions.
+            if check_closures {
+                self.select_obligations_where_possible(false, |errors| {
+                    self.point_at_type_arg_instead_of_call_if_possible(errors, expr);
+                    self.point_at_arg_instead_of_call_if_possible(
+                        errors,
+                        &final_arg_types[..],
+                        sp,
+                        &args,
+                    );
+                })
+            }
+
+            // For C-variadic functions, we don't have a declared type for all of
+            // the arguments hence we only do our usual type checking with
+            // the arguments who's types we do know.
+            let t = if c_variadic {
+                expected_arg_count
+            } else if tuple_arguments == TupleArguments {
+                args.len()
+            } else {
+                supplied_arg_count
+            };
+            for (i, arg) in args.iter().take(t).enumerate() {
+                // Warn only for the first loop (the "no closures" one).
+                // Closure arguments themselves can't be diverging, but
+                // a previous argument can, e.g., `foo(panic!(), || {})`.
+                if !check_closures {
+                    self.warn_if_unreachable(arg.hir_id, arg.span, "expression");
+                }
+
+                let is_closure = match arg.kind {
+                    ExprKind::Closure(..) => true,
+                    _ => false,
+                };
+
+                if is_closure != check_closures {
+                    continue;
+                }
+
+                debug!("checking the argument");
+                let formal_ty = formal_tys[i];
+
+                // The special-cased logic below has three functions:
+                // 1. Provide as good of an expected type as possible.
+                let expected = Expectation::rvalue_hint(self, expected_arg_tys[i]);
+
+                let checked_ty = self.check_expr_with_expectation(&arg, expected);
+
+                // 2. Coerce to the most detailed type that could be coerced
+                //    to, which is `expected_ty` if `rvalue_hint` returns an
+                //    `ExpectHasType(expected_ty)`, or the `formal_ty` otherwise.
+                let coerce_ty = expected.only_has_type(self).unwrap_or(formal_ty);
+                // We're processing function arguments so we definitely want to use
+                // two-phase borrows.
+                self.demand_coerce(&arg, checked_ty, coerce_ty, None, AllowTwoPhase::Yes);
+                final_arg_types.push((i, checked_ty, coerce_ty));
+
+                // 3. Relate the expected type and the formal one,
+                //    if the expected type was used for the coercion.
+                self.demand_suptype(arg.span, formal_ty, coerce_ty);
+            }
+        }
+
+        // We also need to make sure we at least write the ty of the other
+        // arguments which we skipped above.
+        if c_variadic {
+            fn variadic_error<'tcx>(s: &Session, span: Span, t: Ty<'tcx>, cast_ty: &str) {
+                use crate::structured_errors::{StructuredDiagnostic, VariadicError};
+                VariadicError::new(s, span, t, cast_ty).diagnostic().emit();
+            }
+
+            for arg in args.iter().skip(expected_arg_count) {
+                let arg_ty = self.check_expr(&arg);
+
+                // 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);
+                match arg_ty.kind() {
+                    ty::Float(ast::FloatTy::F32) => {
+                        variadic_error(tcx.sess, arg.span, arg_ty, "c_double");
+                    }
+                    ty::Int(ast::IntTy::I8 | ast::IntTy::I16) | ty::Bool => {
+                        variadic_error(tcx.sess, arg.span, arg_ty, "c_int");
+                    }
+                    ty::Uint(ast::UintTy::U8 | ast::UintTy::U16) => {
+                        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 = self.resolve_vars_if_possible(&ptr_ty);
+                        variadic_error(tcx.sess, arg.span, arg_ty, &ptr_ty.to_string());
+                    }
+                    _ => {}
+                }
+            }
+        }
+    }
+
+    pub(super) fn err_args(&self, len: usize) -> Vec<Ty<'tcx>> {
+        vec![self.tcx.ty_error(); len]
+    }
+
+    /// Given a vec of evaluated `FulfillmentError`s and an `fn` call argument expressions, we walk
+    /// the checked and coerced types for each argument to see if any of the `FulfillmentError`s
+    /// reference a type argument. The reason to walk also the checked type is that the coerced type
+    /// can be not easily comparable with predicate type (because of coercion). If the types match
+    /// for either checked or coerced type, and there's only *one* argument that does, we point at
+    /// the corresponding argument's expression span instead of the `fn` call path span.
+    fn point_at_arg_instead_of_call_if_possible(
+        &self,
+        errors: &mut Vec<traits::FulfillmentError<'tcx>>,
+        final_arg_types: &[(usize, Ty<'tcx>, Ty<'tcx>)],
+        call_sp: Span,
+        args: &'tcx [hir::Expr<'tcx>],
+    ) {
+        // We *do not* do this for desugared call spans to keep good diagnostics when involving
+        // the `?` operator.
+        if call_sp.desugaring_kind().is_some() {
+            return;
+        }
+
+        for error in errors {
+            // Only if the cause is somewhere inside the expression we want try to point at arg.
+            // Otherwise, it means that the cause is somewhere else and we should not change
+            // anything because we can break the correct span.
+            if !call_sp.contains(error.obligation.cause.span) {
+                continue;
+            }
+
+            if let ty::PredicateAtom::Trait(predicate, _) =
+                error.obligation.predicate.skip_binders()
+            {
+                // Collect the argument position for all arguments that could have caused this
+                // `FulfillmentError`.
+                let mut referenced_in = final_arg_types
+                    .iter()
+                    .map(|&(i, checked_ty, _)| (i, checked_ty))
+                    .chain(final_arg_types.iter().map(|&(i, _, coerced_ty)| (i, coerced_ty)))
+                    .flat_map(|(i, ty)| {
+                        let ty = self.resolve_vars_if_possible(&ty);
+                        // We walk the argument type because the argument's type could have
+                        // been `Option<T>`, but the `FulfillmentError` references `T`.
+                        if ty.walk().any(|arg| arg == predicate.self_ty().into()) {
+                            Some(i)
+                        } else {
+                            None
+                        }
+                    })
+                    .collect::<Vec<usize>>();
+
+                // Both checked and coerced types could have matched, thus we need to remove
+                // duplicates.
+
+                // We sort primitive type usize here and can use unstable sort
+                referenced_in.sort_unstable();
+                referenced_in.dedup();
+
+                if let (Some(ref_in), None) = (referenced_in.pop(), referenced_in.pop()) {
+                    // We make sure that only *one* argument matches the obligation failure
+                    // and we assign the obligation's span to its expression's.
+                    error.obligation.cause.make_mut().span = args[ref_in].span;
+                    error.points_at_arg_span = true;
+                }
+            }
+        }
+    }
+
+    /// Given a vec of evaluated `FulfillmentError`s and an `fn` call expression, we walk the
+    /// `PathSegment`s and resolve their type parameters to see if any of the `FulfillmentError`s
+    /// were caused by them. If they were, we point at the corresponding type argument's span
+    /// instead of the `fn` call path span.
+    fn point_at_type_arg_instead_of_call_if_possible(
+        &self,
+        errors: &mut Vec<traits::FulfillmentError<'tcx>>,
+        call_expr: &'tcx hir::Expr<'tcx>,
+    ) {
+        if let hir::ExprKind::Call(path, _) = &call_expr.kind {
+            if let hir::ExprKind::Path(qpath) = &path.kind {
+                if let hir::QPath::Resolved(_, path) = &qpath {
+                    for error in errors {
+                        if let ty::PredicateAtom::Trait(predicate, _) =
+                            error.obligation.predicate.skip_binders()
+                        {
+                            // If any of the type arguments in this path segment caused the
+                            // `FullfillmentError`, point at its span (#61860).
+                            for arg in path
+                                .segments
+                                .iter()
+                                .filter_map(|seg| seg.args.as_ref())
+                                .flat_map(|a| a.args.iter())
+                            {
+                                if let hir::GenericArg::Type(hir_ty) = &arg {
+                                    if let hir::TyKind::Path(hir::QPath::TypeRelative(..)) =
+                                        &hir_ty.kind
+                                    {
+                                        // Avoid ICE with associated types. As this is best
+                                        // effort only, it's ok to ignore the case. It
+                                        // would trigger in `is_send::<T::AssocType>();`
+                                        // from `typeck-default-trait-impl-assoc-type.rs`.
+                                    } else {
+                                        let ty = AstConv::ast_ty_to_ty(self, hir_ty);
+                                        let ty = self.resolve_vars_if_possible(&ty);
+                                        if ty == predicate.self_ty() {
+                                            error.obligation.cause.make_mut().span = hir_ty.span;
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    // AST fragment checking
+    pub(super) fn check_lit(&self, lit: &hir::Lit, expected: Expectation<'tcx>) -> Ty<'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::Byte(_) => tcx.types.u8,
+            ast::LitKind::Char(_) => tcx.types.char,
+            ast::LitKind::Int(_, ast::LitIntType::Signed(t)) => tcx.mk_mach_int(t),
+            ast::LitKind::Int(_, ast::LitIntType::Unsigned(t)) => tcx.mk_mach_uint(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),
+                    ty::Char => Some(tcx.types.u8),
+                    ty::RawPtr(..) => Some(tcx.types.usize),
+                    ty::FnDef(..) | ty::FnPtr(_) => Some(tcx.types.usize),
+                    _ => None,
+                });
+                opt_ty.unwrap_or_else(|| self.next_int_var())
+            }
+            ast::LitKind::Float(_, ast::LitFloatType::Suffixed(t)) => tcx.mk_mach_float(t),
+            ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) => {
+                let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() {
+                    ty::Float(_) => Some(ty),
+                    _ => None,
+                });
+                opt_ty.unwrap_or_else(|| self.next_float_var())
+            }
+            ast::LitKind::Bool(_) => tcx.types.bool,
+            ast::LitKind::Err(_) => tcx.ty_error(),
+        }
+    }
+
+    /// Unifies the output type with the expected type early, for more coercions
+    /// and forward type information on the input expressions.
+    pub(super) fn expected_inputs_for_expected_output(
+        &self,
+        call_span: Span,
+        expected_ret: Expectation<'tcx>,
+        formal_ret: Ty<'tcx>,
+        formal_args: &[Ty<'tcx>],
+    ) -> Vec<Ty<'tcx>> {
+        let formal_ret = self.resolve_vars_with_obligations(formal_ret);
+        let ret_ty = match expected_ret.only_has_type(self) {
+            Some(ret) => ret,
+            None => return Vec::new(),
+        };
+        let expect_args = self
+            .fudge_inference_if_ok(|| {
+                // Attempt to apply a subtyping relationship between the formal
+                // return type (likely containing type variables if the function
+                // is polymorphic) and the expected return type.
+                // No argument expectations are produced if unification fails.
+                let origin = self.misc(call_span);
+                let ures = self.at(&origin, self.param_env).sup(ret_ty, &formal_ret);
+
+                // FIXME(#27336) can't use ? here, Try::from_error doesn't default
+                // to identity so the resulting type is not constrained.
+                match ures {
+                    Ok(ok) => {
+                        // Process any obligations locally as much as
+                        // we can.  We don't care if some things turn
+                        // out unconstrained or ambiguous, as we're
+                        // just trying to get hints here.
+                        self.save_and_restore_in_snapshot_flag(|_| {
+                            let mut fulfill = TraitEngine::new(self.tcx);
+                            for obligation in ok.obligations {
+                                fulfill.register_predicate_obligation(self, obligation);
+                            }
+                            fulfill.select_where_possible(self)
+                        })
+                        .map_err(|_| ())?;
+                    }
+                    Err(_) => return Err(()),
+                }
+
+                // Record all the argument types, with the substitutions
+                // produced from the above subtyping unification.
+                Ok(formal_args.iter().map(|ty| self.resolve_vars_if_possible(ty)).collect())
+            })
+            .unwrap_or_default();
+        debug!(
+            "expected_inputs_for_expected_output(formal={:?} -> {:?}, expected={:?} -> {:?})",
+            formal_args, formal_ret, expect_args, expected_ret
+        );
+        expect_args
+    }
+
+    pub fn check_struct_path(
+        &self,
+        qpath: &QPath<'_>,
+        hir_id: hir::HirId,
+    ) -> Option<(&'tcx ty::VariantDef, Ty<'tcx>)> {
+        let path_span = qpath.qself_span();
+        let (def, ty) = self.finish_resolving_struct_path(qpath, path_span, hir_id);
+        let variant = match def {
+            Res::Err => {
+                self.set_tainted_by_errors();
+                return None;
+            }
+            Res::Def(DefKind::Variant, _) => match ty.kind() {
+                ty::Adt(adt, substs) => Some((adt.variant_of_res(def), adt.did, substs)),
+                _ => bug!("unexpected type: {:?}", ty),
+            },
+            Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _)
+            | Res::SelfTy(..) => match ty.kind() {
+                ty::Adt(adt, substs) if !adt.is_enum() => {
+                    Some((adt.non_enum_variant(), adt.did, substs))
+                }
+                _ => None,
+            },
+            _ => bug!("unexpected definition: {:?}", def),
+        };
+
+        if let Some((variant, did, substs)) = variant {
+            debug!("check_struct_path: did={:?} substs={:?}", did, substs);
+            self.write_user_type_annotation_from_substs(hir_id, did, substs, None);
+
+            // Check bounds on type arguments used in the path.
+            let (bounds, _) = self.instantiate_bounds(path_span, did, substs);
+            let cause =
+                traits::ObligationCause::new(path_span, self.body_id, traits::ItemObligation(did));
+            self.add_obligations_for_parameters(cause, bounds);
+
+            Some((variant, ty))
+        } else {
+            struct_span_err!(
+                self.tcx.sess,
+                path_span,
+                E0071,
+                "expected struct, variant or union type, found {}",
+                ty.sort_string(self.tcx)
+            )
+            .span_label(path_span, "not a struct")
+            .emit();
+            None
+        }
+    }
+
+    // Finish resolving a path in a struct expression or pattern `S::A { .. }` if necessary.
+    // The newly resolved definition is written into `type_dependent_defs`.
+    fn finish_resolving_struct_path(
+        &self,
+        qpath: &QPath<'_>,
+        path_span: Span,
+        hir_id: hir::HirId,
+    ) -> (Res, Ty<'tcx>) {
+        match *qpath {
+            QPath::Resolved(ref maybe_qself, ref path) => {
+                let self_ty = maybe_qself.as_ref().map(|qself| self.to_ty(qself));
+                let ty = AstConv::res_to_ty(self, self_ty, path, true);
+                (path.res, ty)
+            }
+            QPath::TypeRelative(ref qself, ref segment) => {
+                let ty = self.to_ty(qself);
+
+                let res = if let hir::TyKind::Path(QPath::Resolved(_, ref path)) = qself.kind {
+                    path.res
+                } else {
+                    Res::Err
+                };
+                let result =
+                    AstConv::associated_path_to_ty(self, hir_id, path_span, ty, res, segment, true);
+                let ty = result.map(|(ty, _, _)| ty).unwrap_or_else(|_| self.tcx().ty_error());
+                let result = result.map(|(_, kind, def_id)| (kind, def_id));
+
+                // Write back the new resolution.
+                self.write_resolution(hir_id, result);
+
+                (result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err), ty)
+            }
+            QPath::LangItem(lang_item, span) => {
+                self.resolve_lang_item_path(lang_item, span, hir_id)
+            }
+        }
+    }
+
+    pub(super) fn resolve_lang_item_path(
+        &self,
+        lang_item: hir::LangItem,
+        span: Span,
+        hir_id: hir::HirId,
+    ) -> (Res, Ty<'tcx>) {
+        let def_id = self.tcx.require_lang_item(lang_item, Some(span));
+        let def_kind = self.tcx.def_kind(def_id);
+
+        let item_ty = if let DefKind::Variant = def_kind {
+            self.tcx.type_of(self.tcx.parent(def_id).expect("variant w/out parent"))
+        } else {
+            self.tcx.type_of(def_id)
+        };
+        let substs = self.infcx.fresh_substs_for_item(span, def_id);
+        let ty = item_ty.subst(self.tcx, substs);
+
+        self.write_resolution(hir_id, Ok((def_kind, def_id)));
+        self.add_required_obligations(span, def_id, &substs);
+        (Res::Def(def_kind, def_id), ty)
+    }
+
+    /// Resolves an associated value path into a base type and associated constant, or method
+    /// resolution. The newly resolved definition is written into `type_dependent_defs`.
+    pub fn resolve_ty_and_res_ufcs<'b>(
+        &self,
+        qpath: &'b QPath<'b>,
+        hir_id: hir::HirId,
+        span: Span,
+    ) -> (Res, Option<Ty<'tcx>>, &'b [hir::PathSegment<'b>]) {
+        debug!("resolve_ty_and_res_ufcs: qpath={:?} hir_id={:?} span={:?}", qpath, hir_id, span);
+        let (ty, qself, item_segment) = match *qpath {
+            QPath::Resolved(ref opt_qself, ref path) => {
+                return (
+                    path.res,
+                    opt_qself.as_ref().map(|qself| self.to_ty(qself)),
+                    &path.segments[..],
+                );
+            }
+            QPath::TypeRelative(ref qself, ref segment) => (self.to_ty(qself), qself, segment),
+            QPath::LangItem(..) => bug!("`resolve_ty_and_res_ufcs` called on `LangItem`"),
+        };
+        if let Some(&cached_result) = self.typeck_results.borrow().type_dependent_defs().get(hir_id)
+        {
+            // Return directly on cache hit. This is useful to avoid doubly reporting
+            // errors with default match binding modes. See #44614.
+            let def =
+                cached_result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err);
+            return (def, Some(ty), slice::from_ref(&**item_segment));
+        }
+        let item_name = item_segment.ident;
+        let result = self.resolve_ufcs(span, item_name, ty, hir_id).or_else(|error| {
+            let result = match error {
+                method::MethodError::PrivateMatch(kind, def_id, _) => Ok((kind, def_id)),
+                _ => Err(ErrorReported),
+            };
+            if item_name.name != kw::Invalid {
+                if let Some(mut e) = self.report_method_error(
+                    span,
+                    ty,
+                    item_name,
+                    SelfSource::QPath(qself),
+                    error,
+                    None,
+                ) {
+                    e.emit();
+                }
+            }
+            result
+        });
+
+        // Write back the new resolution.
+        self.write_resolution(hir_id, result);
+        (
+            result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err),
+            Some(ty),
+            slice::from_ref(&**item_segment),
+        )
+    }
+
+    pub fn check_decl_initializer(
+        &self,
+        local: &'tcx hir::Local<'tcx>,
+        init: &'tcx hir::Expr<'tcx>,
+    ) -> Ty<'tcx> {
+        // FIXME(tschottdorf): `contains_explicit_ref_binding()` must be removed
+        // for #42640 (default match binding modes).
+        //
+        // See #44848.
+        let ref_bindings = local.pat.contains_explicit_ref_binding();
+
+        let local_ty = self.local_ty(init.span, local.hir_id).revealed_ty;
+        if let Some(m) = ref_bindings {
+            // Somewhat subtle: if we have a `ref` binding in the pattern,
+            // we want to avoid introducing coercions for the RHS. This is
+            // both because it helps preserve sanity and, in the case of
+            // ref mut, for soundness (issue #23116). In particular, in
+            // the latter case, we need to be clear that the type of the
+            // referent for the reference that results is *equal to* the
+            // type of the place it is referencing, and not some
+            // supertype thereof.
+            let init_ty = self.check_expr_with_needs(init, Needs::maybe_mut_place(m));
+            self.demand_eqtype(init.span, local_ty, init_ty);
+            init_ty
+        } else {
+            self.check_expr_coercable_to_type(init, local_ty, None)
+        }
+    }
+
+    /// Type check a `let` statement.
+    pub fn check_decl_local(&self, local: &'tcx hir::Local<'tcx>) {
+        // Determine and write the type which we'll check the pattern against.
+        let ty = self.local_ty(local.span, local.hir_id).decl_ty;
+        self.write_ty(local.hir_id, ty);
+
+        // Type check the initializer.
+        if let Some(ref init) = local.init {
+            let init_ty = self.check_decl_initializer(local, &init);
+            self.overwrite_local_ty_if_err(local, ty, init_ty);
+        }
+
+        // Does the expected pattern type originate from an expression and what is the span?
+        let (origin_expr, ty_span) = match (local.ty, local.init) {
+            (Some(ty), _) => (false, Some(ty.span)), // Bias towards the explicit user type.
+            (_, Some(init)) => (true, Some(init.span)), // No explicit type; so use the scrutinee.
+            _ => (false, None), // We have `let $pat;`, so the expected type is unconstrained.
+        };
+
+        // Type check the pattern. Override if necessary to avoid knock-on errors.
+        self.check_pat_top(&local.pat, ty, ty_span, origin_expr);
+        let pat_ty = self.node_ty(local.pat.hir_id);
+        self.overwrite_local_ty_if_err(local, ty, pat_ty);
+    }
+
+    fn overwrite_local_ty_if_err(
+        &self,
+        local: &'tcx hir::Local<'tcx>,
+        decl_ty: Ty<'tcx>,
+        ty: Ty<'tcx>,
+    ) {
+        if ty.references_error() {
+            // Override the types everywhere with `err()` to avoid knock on errors.
+            self.write_ty(local.hir_id, ty);
+            self.write_ty(local.pat.hir_id, ty);
+            let local_ty = LocalTy { decl_ty, revealed_ty: ty };
+            self.locals.borrow_mut().insert(local.hir_id, local_ty);
+            self.locals.borrow_mut().insert(local.pat.hir_id, local_ty);
+        }
+    }
+
+    pub(super) fn suggest_semicolon_at_end(&self, span: Span, err: &mut DiagnosticBuilder<'_>) {
+        err.span_suggestion_short(
+            span.shrink_to_hi(),
+            "consider using a semicolon here",
+            ";".to_string(),
+            Applicability::MachineApplicable,
+        );
+    }
+
+    pub fn check_stmt(&self, stmt: &'tcx hir::Stmt<'tcx>) {
+        // Don't do all the complex logic below for `DeclItem`.
+        match stmt.kind {
+            hir::StmtKind::Item(..) => return,
+            hir::StmtKind::Local(..) | hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => {}
+        }
+
+        self.warn_if_unreachable(stmt.hir_id, stmt.span, "statement");
+
+        // Hide the outer diverging and `has_errors` flags.
+        let old_diverges = self.diverges.replace(Diverges::Maybe);
+        let old_has_errors = self.has_errors.replace(false);
+
+        match stmt.kind {
+            hir::StmtKind::Local(ref l) => {
+                self.check_decl_local(&l);
+            }
+            // Ignore for now.
+            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.suggest_semicolon_at_end(expr.span, err);
+                });
+            }
+            hir::StmtKind::Semi(ref expr) => {
+                self.check_expr(&expr);
+            }
+        }
+
+        // Combine the diverging and `has_error` flags.
+        self.diverges.set(self.diverges.get() | old_diverges);
+        self.has_errors.set(self.has_errors.get() | old_has_errors);
+    }
+
+    pub fn check_block_no_value(&self, blk: &'tcx hir::Block<'tcx>) {
+        let unit = self.tcx.mk_unit();
+        let ty = self.check_block_with_expected(blk, ExpectHasType(unit));
+
+        // if the block produces a `!` value, that can always be
+        // (effectively) coerced to unit.
+        if !ty.is_never() {
+            self.demand_suptype(blk.span, unit, ty);
+        }
+    }
+
+    /// If `expr` is a `match` expression that has only one non-`!` arm, use that arm's tail
+    /// expression's `Span`, otherwise return `expr.span`. This is done to give better errors
+    /// when given code like the following:
+    /// ```text
+    /// if false { return 0i32; } else { 1u32 }
+    /// //                               ^^^^ point at this instead of the whole `if` expression
+    /// ```
+    fn get_expr_coercion_span(&self, expr: &hir::Expr<'_>) -> rustc_span::Span {
+        if let hir::ExprKind::Match(_, arms, _) = &expr.kind {
+            let arm_spans: Vec<Span> = arms
+                .iter()
+                .filter_map(|arm| {
+                    self.in_progress_typeck_results
+                        .and_then(|typeck_results| {
+                            typeck_results.borrow().node_type_opt(arm.body.hir_id)
+                        })
+                        .and_then(|arm_ty| {
+                            if arm_ty.is_never() {
+                                None
+                            } else {
+                                Some(match &arm.body.kind {
+                                    // Point at the tail expression when possible.
+                                    hir::ExprKind::Block(block, _) => {
+                                        block.expr.as_ref().map(|e| e.span).unwrap_or(block.span)
+                                    }
+                                    _ => arm.body.span,
+                                })
+                            }
+                        })
+                })
+                .collect();
+            if arm_spans.len() == 1 {
+                return arm_spans[0];
+            }
+        }
+        expr.span
+    }
+
+    pub(super) fn check_block_with_expected(
+        &self,
+        blk: &'tcx hir::Block<'tcx>,
+        expected: Expectation<'tcx>,
+    ) -> Ty<'tcx> {
+        let prev = {
+            let mut fcx_ps = self.ps.borrow_mut();
+            let unsafety_state = fcx_ps.recurse(blk);
+            replace(&mut *fcx_ps, unsafety_state)
+        };
+
+        // In some cases, blocks have just one exit, but other blocks
+        // can be targeted by multiple breaks. This can happen both
+        // with labeled blocks as well as when we desugar
+        // a `try { ... }` expression.
+        //
+        // Example 1:
+        //
+        //    'a: { if true { break 'a Err(()); } Ok(()) }
+        //
+        // Here we would wind up with two coercions, one from
+        // `Err(())` and the other from the tail expression
+        // `Ok(())`. If the tail expression is omitted, that's a
+        // "forced unit" -- unless the block diverges, in which
+        // case we can ignore the tail expression (e.g., `'a: {
+        // break 'a 22; }` would not force the type of the block
+        // to be `()`).
+        let tail_expr = blk.expr.as_ref();
+        let coerce_to_ty = expected.coercion_target_type(self, blk.span);
+        let coerce = if blk.targeted_by_break {
+            CoerceMany::new(coerce_to_ty)
+        } else {
+            let tail_expr: &[&hir::Expr<'_>] = match tail_expr {
+                Some(e) => slice::from_ref(e),
+                None => &[],
+            };
+            CoerceMany::with_coercion_sites(coerce_to_ty, tail_expr)
+        };
+
+        let prev_diverges = self.diverges.get();
+        let ctxt = BreakableCtxt { coerce: Some(coerce), may_break: false };
+
+        let (ctxt, ()) = self.with_breakable_ctxt(blk.hir_id, ctxt, || {
+            for s in blk.stmts {
+                self.check_stmt(s);
+            }
+
+            // check the tail expression **without** holding the
+            // `enclosing_breakables` lock below.
+            let tail_expr_ty = tail_expr.map(|t| self.check_expr_with_expectation(t, expected));
+
+            let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
+            let ctxt = enclosing_breakables.find_breakable(blk.hir_id);
+            let coerce = ctxt.coerce.as_mut().unwrap();
+            if let Some(tail_expr_ty) = tail_expr_ty {
+                let tail_expr = tail_expr.unwrap();
+                let span = self.get_expr_coercion_span(tail_expr);
+                let cause = self.cause(span, ObligationCauseCode::BlockTailExpression(blk.hir_id));
+                coerce.coerce(self, &cause, tail_expr, tail_expr_ty);
+            } else {
+                // Subtle: if there is no explicit tail expression,
+                // that is typically equivalent to a tail expression
+                // of `()` -- except if the block diverges. In that
+                // case, there is no value supplied from the tail
+                // expression (assuming there are no other breaks,
+                // this implies that the type of the block will be
+                // `!`).
+                //
+                // #41425 -- label the implicit `()` as being the
+                // "found type" here, rather than the "expected type".
+                if !self.diverges.get().is_always() {
+                    // #50009 -- Do not point at the entire fn block span, point at the return type
+                    // span, as it is the cause of the requirement, and
+                    // `consider_hint_about_removing_semicolon` will point at the last expression
+                    // if it were a relevant part of the error. This improves usability in editors
+                    // that highlight errors inline.
+                    let mut sp = blk.span;
+                    let mut fn_span = None;
+                    if let Some((decl, ident)) = self.get_parent_fn_decl(blk.hir_id) {
+                        let ret_sp = decl.output.span();
+                        if let Some(block_sp) = self.parent_item_span(blk.hir_id) {
+                            // HACK: on some cases (`ui/liveness/liveness-issue-2163.rs`) the
+                            // output would otherwise be incorrect and even misleading. Make sure
+                            // the span we're aiming at correspond to a `fn` body.
+                            if block_sp == blk.span {
+                                sp = ret_sp;
+                                fn_span = Some(ident.span);
+                            }
+                        }
+                    }
+                    coerce.coerce_forced_unit(
+                        self,
+                        &self.misc(sp),
+                        &mut |err| {
+                            if let Some(expected_ty) = expected.only_has_type(self) {
+                                self.consider_hint_about_removing_semicolon(blk, expected_ty, err);
+                            }
+                            if let Some(fn_span) = fn_span {
+                                err.span_label(
+                                    fn_span,
+                                    "implicitly returns `()` as its body has no tail or `return` \
+                                     expression",
+                                );
+                            }
+                        },
+                        false,
+                    );
+                }
+            }
+        });
+
+        if ctxt.may_break {
+            // If we can break from the block, then the block's exit is always reachable
+            // (... as long as the entry is reachable) - regardless of the tail of the block.
+            self.diverges.set(prev_diverges);
+        }
+
+        let mut ty = ctxt.coerce.unwrap().complete(self);
+
+        if self.has_errors.get() || ty.references_error() {
+            ty = self.tcx.ty_error()
+        }
+
+        self.write_ty(blk.hir_id, ty);
+
+        *self.ps.borrow_mut() = prev;
+        ty
+    }
+
+    fn parent_item_span(&self, id: hir::HirId) -> Option<Span> {
+        let node = self.tcx.hir().get(self.tcx.hir().get_parent_item(id));
+        match node {
+            Node::Item(&hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. })
+            | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(_, body_id), .. }) => {
+                let body = self.tcx.hir().body(body_id);
+                if let ExprKind::Block(block, _) = &body.value.kind {
+                    return Some(block.span);
+                }
+            }
+            _ => {}
+        }
+        None
+    }
+
+    /// Given a function block's `HirId`, returns its `FnDecl` if it exists, or `None` otherwise.
+    fn get_parent_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident)> {
+        let parent = self.tcx.hir().get(self.tcx.hir().get_parent_item(blk_id));
+        self.get_node_fn_decl(parent).map(|(fn_decl, ident, _)| (fn_decl, ident))
+    }
+
+    /// Given a function `Node`, return its `FnDecl` if it exists, or `None` otherwise.
+    pub(super) fn get_node_fn_decl(
+        &self,
+        node: Node<'tcx>,
+    ) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident, bool)> {
+        match node {
+            Node::Item(&hir::Item { ident, kind: hir::ItemKind::Fn(ref sig, ..), .. }) => {
+                // This is less than ideal, it will not suggest a return type span on any
+                // method called `main`, regardless of whether it is actually the entry point,
+                // but it will still present it as the reason for the expected type.
+                Some((&sig.decl, ident, ident.name != sym::main))
+            }
+            Node::TraitItem(&hir::TraitItem {
+                ident,
+                kind: hir::TraitItemKind::Fn(ref sig, ..),
+                ..
+            }) => Some((&sig.decl, ident, true)),
+            Node::ImplItem(&hir::ImplItem {
+                ident,
+                kind: hir::ImplItemKind::Fn(ref sig, ..),
+                ..
+            }) => Some((&sig.decl, ident, false)),
+            _ => None,
+        }
+    }
+
+    /// Given a `HirId`, return the `FnDecl` of the method it is enclosed by and whether a
+    /// suggestion can be made, `None` otherwise.
+    pub fn get_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, bool)> {
+        // Get enclosing Fn, if it is a function or a trait method, unless there's a `loop` or
+        // `while` before reaching it, as block tail returns are not available in them.
+        self.tcx.hir().get_return_block(blk_id).and_then(|blk_id| {
+            let parent = self.tcx.hir().get(blk_id);
+            self.get_node_fn_decl(parent).map(|(fn_decl, _, is_main)| (fn_decl, is_main))
+        })
+    }
+
+    /// On implicit return expressions with mismatched types, provides the following suggestions:
+    ///
+    /// - Points out the method's return type as the reason for the expected type.
+    /// - Possible missing semicolon.
+    /// - Possible missing return type if the return type is the default, and not `fn main()`.
+    pub fn suggest_mismatched_types_on_tail(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        expr: &'tcx hir::Expr<'tcx>,
+        expected: Ty<'tcx>,
+        found: Ty<'tcx>,
+        cause_span: Span,
+        blk_id: hir::HirId,
+    ) -> bool {
+        let expr = expr.peel_drop_temps();
+        self.suggest_missing_semicolon(err, expr, expected, cause_span);
+        let mut pointing_at_return_type = false;
+        if let Some((fn_decl, can_suggest)) = self.get_fn_decl(blk_id) {
+            pointing_at_return_type =
+                self.suggest_missing_return_type(err, &fn_decl, expected, found, can_suggest);
+        }
+        pointing_at_return_type
+    }
+
+    /// When encountering an fn-like ctor that needs to unify with a value, check whether calling
+    /// the ctor would successfully solve the type mismatch and if so, suggest it:
+    /// ```
+    /// fn foo(x: usize) -> usize { x }
+    /// let x: usize = foo;  // suggest calling the `foo` function: `foo(42)`
+    /// ```
+    fn suggest_fn_call(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        expr: &hir::Expr<'_>,
+        expected: Ty<'tcx>,
+        found: Ty<'tcx>,
+    ) -> bool {
+        let hir = self.tcx.hir();
+        let (def_id, sig) = match *found.kind() {
+            ty::FnDef(def_id, _) => (def_id, found.fn_sig(self.tcx)),
+            ty::Closure(def_id, substs) => (def_id, substs.as_closure().sig()),
+            _ => return false,
+        };
+
+        let sig = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, &sig).0;
+        let sig = self.normalize_associated_types_in(expr.span, &sig);
+        if self.can_coerce(sig.output(), expected) {
+            let (mut sugg_call, applicability) = if sig.inputs().is_empty() {
+                (String::new(), Applicability::MachineApplicable)
+            } else {
+                ("...".to_string(), Applicability::HasPlaceholders)
+            };
+            let mut msg = "call this function";
+            match hir.get_if_local(def_id) {
+                Some(
+                    Node::Item(hir::Item { kind: ItemKind::Fn(.., body_id), .. })
+                    | Node::ImplItem(hir::ImplItem {
+                        kind: hir::ImplItemKind::Fn(_, body_id), ..
+                    })
+                    | Node::TraitItem(hir::TraitItem {
+                        kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Provided(body_id)),
+                        ..
+                    }),
+                ) => {
+                    let body = hir.body(*body_id);
+                    sugg_call = body
+                        .params
+                        .iter()
+                        .map(|param| match &param.pat.kind {
+                            hir::PatKind::Binding(_, _, ident, None)
+                                if ident.name != kw::SelfLower =>
+                            {
+                                ident.to_string()
+                            }
+                            _ => "_".to_string(),
+                        })
+                        .collect::<Vec<_>>()
+                        .join(", ");
+                }
+                Some(Node::Expr(hir::Expr {
+                    kind: ExprKind::Closure(_, _, body_id, _, _),
+                    span: full_closure_span,
+                    ..
+                })) => {
+                    if *full_closure_span == expr.span {
+                        return false;
+                    }
+                    msg = "call this closure";
+                    let body = hir.body(*body_id);
+                    sugg_call = body
+                        .params
+                        .iter()
+                        .map(|param| match &param.pat.kind {
+                            hir::PatKind::Binding(_, _, ident, None)
+                                if ident.name != kw::SelfLower =>
+                            {
+                                ident.to_string()
+                            }
+                            _ => "_".to_string(),
+                        })
+                        .collect::<Vec<_>>()
+                        .join(", ");
+                }
+                Some(Node::Ctor(hir::VariantData::Tuple(fields, _))) => {
+                    sugg_call = fields.iter().map(|_| "_").collect::<Vec<_>>().join(", ");
+                    match def_id.as_local().map(|def_id| hir.def_kind(def_id)) {
+                        Some(DefKind::Ctor(hir::def::CtorOf::Variant, _)) => {
+                            msg = "instantiate this tuple variant";
+                        }
+                        Some(DefKind::Ctor(CtorOf::Struct, _)) => {
+                            msg = "instantiate this tuple struct";
+                        }
+                        _ => {}
+                    }
+                }
+                Some(Node::ForeignItem(hir::ForeignItem {
+                    kind: hir::ForeignItemKind::Fn(_, idents, _),
+                    ..
+                })) => {
+                    sugg_call = idents
+                        .iter()
+                        .map(|ident| {
+                            if ident.name != kw::SelfLower {
+                                ident.to_string()
+                            } else {
+                                "_".to_string()
+                            }
+                        })
+                        .collect::<Vec<_>>()
+                        .join(", ")
+                }
+                Some(Node::TraitItem(hir::TraitItem {
+                    kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Required(idents)),
+                    ..
+                })) => {
+                    sugg_call = idents
+                        .iter()
+                        .map(|ident| {
+                            if ident.name != kw::SelfLower {
+                                ident.to_string()
+                            } else {
+                                "_".to_string()
+                            }
+                        })
+                        .collect::<Vec<_>>()
+                        .join(", ")
+                }
+                _ => {}
+            }
+            err.span_suggestion_verbose(
+                expr.span.shrink_to_hi(),
+                &format!("use parentheses to {}", msg),
+                format!("({})", sugg_call),
+                applicability,
+            );
+            return true;
+        }
+        false
+    }
+
+    pub fn suggest_deref_ref_or_into(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        expr: &hir::Expr<'_>,
+        expected: Ty<'tcx>,
+        found: Ty<'tcx>,
+        expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
+    ) {
+        if let Some((sp, msg, suggestion, applicability)) = self.check_ref(expr, found, expected) {
+            err.span_suggestion(sp, msg, suggestion, applicability);
+        } else if let (ty::FnDef(def_id, ..), true) =
+            (&found.kind(), self.suggest_fn_call(err, expr, expected, found))
+        {
+            if let Some(sp) = self.tcx.hir().span_if_local(*def_id) {
+                let sp = self.sess().source_map().guess_head_span(sp);
+                err.span_label(sp, &format!("{} defined here", found));
+            }
+        } else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) {
+            let is_struct_pat_shorthand_field =
+                self.is_hir_id_from_struct_pattern_shorthand_field(expr.hir_id, expr.span);
+            let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id);
+            if let Ok(expr_text) = self.sess().source_map().span_to_snippet(expr.span) {
+                let mut suggestions = iter::repeat(&expr_text)
+                    .zip(methods.iter())
+                    .filter_map(|(receiver, method)| {
+                        let method_call = format!(".{}()", method.ident);
+                        if receiver.ends_with(&method_call) {
+                            None // do not suggest code that is already there (#53348)
+                        } else {
+                            let method_call_list = [".to_vec()", ".to_string()"];
+                            let sugg = if receiver.ends_with(".clone()")
+                                && method_call_list.contains(&method_call.as_str())
+                            {
+                                let max_len = receiver.rfind('.').unwrap();
+                                format!("{}{}", &receiver[..max_len], method_call)
+                            } else {
+                                if expr.precedence().order() < ExprPrecedence::MethodCall.order() {
+                                    format!("({}){}", receiver, method_call)
+                                } else {
+                                    format!("{}{}", receiver, method_call)
+                                }
+                            };
+                            Some(if is_struct_pat_shorthand_field {
+                                format!("{}: {}", receiver, sugg)
+                            } else {
+                                sugg
+                            })
+                        }
+                    })
+                    .peekable();
+                if suggestions.peek().is_some() {
+                    err.span_suggestions(
+                        expr.span,
+                        "try using a conversion method",
+                        suggestions,
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+            }
+        }
+    }
+
+    /// When encountering the expected boxed value allocated in the stack, suggest allocating it
+    /// in the heap by calling `Box::new()`.
+    pub(super) fn suggest_boxing_when_appropriate(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        expr: &hir::Expr<'_>,
+        expected: Ty<'tcx>,
+        found: Ty<'tcx>,
+    ) {
+        if self.tcx.hir().is_inside_const_context(expr.hir_id) {
+            // Do not suggest `Box::new` in const context.
+            return;
+        }
+        if !expected.is_box() || found.is_box() {
+            return;
+        }
+        let boxed_found = self.tcx.mk_box(found);
+        if let (true, Ok(snippet)) = (
+            self.can_coerce(boxed_found, expected),
+            self.sess().source_map().span_to_snippet(expr.span),
+        ) {
+            err.span_suggestion(
+                expr.span,
+                "store this in the heap by calling `Box::new`",
+                format!("Box::new({})", snippet),
+                Applicability::MachineApplicable,
+            );
+            err.note(
+                "for more on the distinction between the stack and the heap, read \
+                 https://doc.rust-lang.org/book/ch15-01-box.html, \
+                 https://doc.rust-lang.org/rust-by-example/std/box.html, and \
+                 https://doc.rust-lang.org/std/boxed/index.html",
+            );
+        }
+    }
+
+    pub(super) fn note_internal_mutation_in_method(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        expr: &hir::Expr<'_>,
+        expected: Ty<'tcx>,
+        found: Ty<'tcx>,
+    ) {
+        if found != self.tcx.types.unit {
+            return;
+        }
+        if let ExprKind::MethodCall(path_segment, _, [rcvr, ..], _) = expr.kind {
+            if self
+                .typeck_results
+                .borrow()
+                .expr_ty_adjusted_opt(rcvr)
+                .map_or(true, |ty| expected.peel_refs() != ty.peel_refs())
+            {
+                return;
+            }
+            let mut sp = MultiSpan::from_span(path_segment.ident.span);
+            sp.push_span_label(
+                path_segment.ident.span,
+                format!(
+                    "this call modifies {} in-place",
+                    match rcvr.kind {
+                        ExprKind::Path(QPath::Resolved(
+                            None,
+                            hir::Path { segments: [segment], .. },
+                        )) => format!("`{}`", segment.ident),
+                        _ => "its receiver".to_string(),
+                    }
+                ),
+            );
+            sp.push_span_label(
+                rcvr.span,
+                "you probably want to use this value after calling the method...".to_string(),
+            );
+            err.span_note(
+                sp,
+                &format!("method `{}` modifies its receiver in-place", path_segment.ident),
+            );
+            err.note(&format!("...instead of the `()` output of method `{}`", path_segment.ident));
+        }
+    }
+
+    /// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`.
+    pub(super) fn suggest_calling_boxed_future_when_appropriate(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        expr: &hir::Expr<'_>,
+        expected: Ty<'tcx>,
+        found: Ty<'tcx>,
+    ) -> bool {
+        // Handle #68197.
+
+        if self.tcx.hir().is_inside_const_context(expr.hir_id) {
+            // Do not suggest `Box::new` in const context.
+            return false;
+        }
+        let pin_did = self.tcx.lang_items().pin_type();
+        match expected.kind() {
+            ty::Adt(def, _) if Some(def.did) != pin_did => return false,
+            // This guards the `unwrap` and `mk_box` below.
+            _ if pin_did.is_none() || self.tcx.lang_items().owned_box().is_none() => return false,
+            _ => {}
+        }
+        let boxed_found = self.tcx.mk_box(found);
+        let new_found = self.tcx.mk_lang_item(boxed_found, LangItem::Pin).unwrap();
+        if let (true, Ok(snippet)) = (
+            self.can_coerce(new_found, expected),
+            self.sess().source_map().span_to_snippet(expr.span),
+        ) {
+            match found.kind() {
+                ty::Adt(def, _) if def.is_box() => {
+                    err.help("use `Box::pin`");
+                }
+                _ => {
+                    err.span_suggestion(
+                        expr.span,
+                        "you need to pin and box this expression",
+                        format!("Box::pin({})", snippet),
+                        Applicability::MachineApplicable,
+                    );
+                }
+            }
+            true
+        } else {
+            false
+        }
+    }
+
+    /// A common error is to forget to add a semicolon at the end of a block, e.g.,
+    ///
+    /// ```
+    /// fn foo() {
+    ///     bar_that_returns_u32()
+    /// }
+    /// ```
+    ///
+    /// This routine checks if the return expression in a block would make sense on its own as a
+    /// statement and the return type has been left as default or has been specified as `()`. If so,
+    /// it suggests adding a semicolon.
+    fn suggest_missing_semicolon(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        expression: &'tcx hir::Expr<'tcx>,
+        expected: Ty<'tcx>,
+        cause_span: Span,
+    ) {
+        if expected.is_unit() {
+            // `BlockTailExpression` only relevant if the tail expr would be
+            // useful on its own.
+            match expression.kind {
+                ExprKind::Call(..)
+                | ExprKind::MethodCall(..)
+                | ExprKind::Loop(..)
+                | ExprKind::Match(..)
+                | ExprKind::Block(..) => {
+                    err.span_suggestion(
+                        cause_span.shrink_to_hi(),
+                        "try adding a semicolon",
+                        ";".to_string(),
+                        Applicability::MachineApplicable,
+                    );
+                }
+                _ => (),
+            }
+        }
+    }
+
+    /// A possible error is to forget to add a return type that is needed:
+    ///
+    /// ```
+    /// fn foo() {
+    ///     bar_that_returns_u32()
+    /// }
+    /// ```
+    ///
+    /// This routine checks if the return type is left as default, the method is not part of an
+    /// `impl` block and that it isn't the `main` method. If so, it suggests setting the return
+    /// type.
+    pub(super) fn suggest_missing_return_type(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        fn_decl: &hir::FnDecl<'_>,
+        expected: Ty<'tcx>,
+        found: Ty<'tcx>,
+        can_suggest: bool,
+    ) -> bool {
+        // Only suggest changing the return type for methods that
+        // haven't set a return type at all (and aren't `fn main()` or an impl).
+        match (&fn_decl.output, found.is_suggestable(), can_suggest, expected.is_unit()) {
+            (&hir::FnRetTy::DefaultReturn(span), true, true, true) => {
+                err.span_suggestion(
+                    span,
+                    "try adding a return type",
+                    format!("-> {} ", self.resolve_vars_with_obligations(found)),
+                    Applicability::MachineApplicable,
+                );
+                true
+            }
+            (&hir::FnRetTy::DefaultReturn(span), false, true, true) => {
+                err.span_label(span, "possibly return type missing here?");
+                true
+            }
+            (&hir::FnRetTy::DefaultReturn(span), _, false, true) => {
+                // `fn main()` must return `()`, do not suggest changing return type
+                err.span_label(span, "expected `()` because of default return type");
+                true
+            }
+            // expectation was caused by something else, not the default return
+            (&hir::FnRetTy::DefaultReturn(_), _, _, false) => false,
+            (&hir::FnRetTy::Return(ref ty), _, _, _) => {
+                // Only point to return type if the expected type is the return type, as if they
+                // are not, the expectation must have been caused by something else.
+                debug!("suggest_missing_return_type: return type {:?} node {:?}", ty, ty.kind);
+                let sp = ty.span;
+                let ty = AstConv::ast_ty_to_ty(self, ty);
+                debug!("suggest_missing_return_type: return type {:?}", ty);
+                debug!("suggest_missing_return_type: expected type {:?}", ty);
+                if ty.kind() == expected.kind() {
+                    err.span_label(sp, format!("expected `{}` because of return type", expected));
+                    return true;
+                }
+                false
+            }
+        }
+    }
+
+    /// A possible error is to forget to add `.await` when using futures:
+    ///
+    /// ```
+    /// async fn make_u32() -> u32 {
+    ///     22
+    /// }
+    ///
+    /// fn take_u32(x: u32) {}
+    ///
+    /// async fn foo() {
+    ///     let x = make_u32();
+    ///     take_u32(x);
+    /// }
+    /// ```
+    ///
+    /// This routine checks if the found type `T` implements `Future<Output=U>` where `U` is the
+    /// expected type. If this is the case, and we are inside of an async body, it suggests adding
+    /// `.await` to the tail of the expression.
+    pub(super) fn suggest_missing_await(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        expr: &hir::Expr<'_>,
+        expected: Ty<'tcx>,
+        found: Ty<'tcx>,
+    ) {
+        debug!("suggest_missing_await: expr={:?} expected={:?}, found={:?}", expr, expected, found);
+        // `.await` is not permitted outside of `async` bodies, so don't bother to suggest if the
+        // body isn't `async`.
+        let item_id = self.tcx().hir().get_parent_node(self.body_id);
+        if let Some(body_id) = self.tcx().hir().maybe_body_owned_by(item_id) {
+            let body = self.tcx().hir().body(body_id);
+            if let Some(hir::GeneratorKind::Async(_)) = body.generator_kind {
+                let sp = expr.span;
+                // Check for `Future` implementations by constructing a predicate to
+                // prove: `<T as Future>::Output == U`
+                let future_trait = self.tcx.require_lang_item(LangItem::Future, Some(sp));
+                let item_def_id = self
+                    .tcx
+                    .associated_items(future_trait)
+                    .in_definition_order()
+                    .next()
+                    .unwrap()
+                    .def_id;
+                // `<T as Future>::Output`
+                let projection_ty = ty::ProjectionTy {
+                    // `T`
+                    substs: self
+                        .tcx
+                        .mk_substs_trait(found, self.fresh_substs_for_item(sp, item_def_id)),
+                    // `Future::Output`
+                    item_def_id,
+                };
+
+                let predicate = ty::PredicateAtom::Projection(ty::ProjectionPredicate {
+                    projection_ty,
+                    ty: expected,
+                })
+                .potentially_quantified(self.tcx, ty::PredicateKind::ForAll);
+                let obligation = traits::Obligation::new(self.misc(sp), self.param_env, predicate);
+
+                debug!("suggest_missing_await: trying obligation {:?}", obligation);
+
+                if self.infcx.predicate_may_hold(&obligation) {
+                    debug!("suggest_missing_await: obligation held: {:?}", obligation);
+                    if let Ok(code) = self.sess().source_map().span_to_snippet(sp) {
+                        err.span_suggestion(
+                            sp,
+                            "consider using `.await` here",
+                            format!("{}.await", code),
+                            Applicability::MaybeIncorrect,
+                        );
+                    } else {
+                        debug!("suggest_missing_await: no snippet for {:?}", sp);
+                    }
+                } else {
+                    debug!("suggest_missing_await: obligation did not hold: {:?}", obligation)
+                }
+            }
+        }
+    }
+
+    pub(super) fn suggest_missing_parentheses(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        expr: &hir::Expr<'_>,
+    ) {
+        let sp = self.tcx.sess.source_map().start_point(expr.span);
+        if let Some(sp) = self.tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) {
+            // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }`
+            self.tcx.sess.parse_sess.expr_parentheses_needed(err, *sp, None);
+        }
+    }
+
+    pub(super) fn note_need_for_fn_pointer(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        expected: Ty<'tcx>,
+        found: Ty<'tcx>,
+    ) {
+        let (sig, did, substs) = match (&expected.kind(), &found.kind()) {
+            (ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => {
+                let sig1 = self.tcx.fn_sig(*did1).subst(self.tcx, substs1);
+                let sig2 = self.tcx.fn_sig(*did2).subst(self.tcx, substs2);
+                if sig1 != sig2 {
+                    return;
+                }
+                err.note(
+                    "different `fn` items always have unique types, even if their signatures are \
+                     the same",
+                );
+                (sig1, *did1, substs1)
+            }
+            (ty::FnDef(did, substs), ty::FnPtr(sig2)) => {
+                let sig1 = self.tcx.fn_sig(*did).subst(self.tcx, substs);
+                if sig1 != *sig2 {
+                    return;
+                }
+                (sig1, *did, substs)
+            }
+            _ => return,
+        };
+        err.help(&format!("change the expected type to be function pointer `{}`", sig));
+        err.help(&format!(
+            "if the expected type is due to type inference, cast the expected `fn` to a function \
+             pointer: `{} as {}`",
+            self.tcx.def_path_str_with_substs(did, substs),
+            sig
+        ));
+    }
+
+    /// A common error is to add an extra semicolon:
+    ///
+    /// ```
+    /// fn foo() -> usize {
+    ///     22;
+    /// }
+    /// ```
+    ///
+    /// This routine checks if the final statement in a block is an
+    /// expression with an explicit semicolon whose type is compatible
+    /// with `expected_ty`. If so, it suggests removing the semicolon.
+    fn consider_hint_about_removing_semicolon(
+        &self,
+        blk: &'tcx hir::Block<'tcx>,
+        expected_ty: Ty<'tcx>,
+        err: &mut DiagnosticBuilder<'_>,
+    ) {
+        if let Some(span_semi) = self.could_remove_semicolon(blk, expected_ty) {
+            err.span_suggestion(
+                span_semi,
+                "consider removing this semicolon",
+                String::new(),
+                Applicability::MachineApplicable,
+            );
+        }
+    }
+
+    pub(super) fn could_remove_semicolon(
+        &self,
+        blk: &'tcx hir::Block<'tcx>,
+        expected_ty: Ty<'tcx>,
+    ) -> Option<Span> {
+        // Be helpful when the user wrote `{... expr;}` and
+        // taking the `;` off is enough to fix the error.
+        let last_stmt = blk.stmts.last()?;
+        let last_expr = match last_stmt.kind {
+            hir::StmtKind::Semi(ref e) => e,
+            _ => return None,
+        };
+        let last_expr_ty = self.node_ty(last_expr.hir_id);
+        if matches!(last_expr_ty.kind(), ty::Error(_))
+            || self.can_sub(self.param_env, last_expr_ty, expected_ty).is_err()
+        {
+            return None;
+        }
+        let original_span = original_sp(last_stmt.span, blk.span);
+        Some(original_span.with_lo(original_span.hi() - BytePos(1)))
+    }
+
+    // Instantiates the given path, which must refer to an item with the given
+    // number of type parameters and type.
+    pub fn instantiate_value_path(
+        &self,
+        segments: &[hir::PathSegment<'_>],
+        self_ty: Option<Ty<'tcx>>,
+        res: Res,
+        span: Span,
+        hir_id: hir::HirId,
+    ) -> (Ty<'tcx>, Res) {
+        debug!(
+            "instantiate_value_path(segments={:?}, self_ty={:?}, res={:?}, hir_id={})",
+            segments, self_ty, res, hir_id,
+        );
+
+        let tcx = self.tcx;
+
+        let path_segs = match res {
+            Res::Local(_) | Res::SelfCtor(_) => vec![],
+            Res::Def(kind, def_id) => {
+                AstConv::def_ids_for_value_path_segments(self, segments, self_ty, kind, def_id)
+            }
+            _ => bug!("instantiate_value_path on {:?}", res),
+        };
+
+        let mut user_self_ty = None;
+        let mut is_alias_variant_ctor = false;
+        match res {
+            Res::Def(DefKind::Ctor(CtorOf::Variant, _), _) => {
+                if let Some(self_ty) = self_ty {
+                    let adt_def = self_ty.ty_adt_def().unwrap();
+                    user_self_ty = Some(UserSelfTy { impl_def_id: adt_def.did, self_ty });
+                    is_alias_variant_ctor = true;
+                }
+            }
+            Res::Def(DefKind::AssocFn | DefKind::AssocConst, def_id) => {
+                let container = tcx.associated_item(def_id).container;
+                debug!("instantiate_value_path: def_id={:?} container={:?}", def_id, container);
+                match container {
+                    ty::TraitContainer(trait_did) => {
+                        callee::check_legal_trait_for_method_call(tcx, span, None, trait_did)
+                    }
+                    ty::ImplContainer(impl_def_id) => {
+                        if segments.len() == 1 {
+                            // `<T>::assoc` will end up here, and so
+                            // can `T::assoc`. It this came from an
+                            // inherent impl, we need to record the
+                            // `T` for posterity (see `UserSelfTy` for
+                            // details).
+                            let self_ty = self_ty.expect("UFCS sugared assoc missing Self");
+                            user_self_ty = Some(UserSelfTy { impl_def_id, self_ty });
+                        }
+                    }
+                }
+            }
+            _ => {}
+        }
+
+        // Now that we have categorized what space the parameters for each
+        // segment belong to, let's sort out the parameters that the user
+        // provided (if any) into their appropriate spaces. We'll also report
+        // errors if type parameters are provided in an inappropriate place.
+
+        let generic_segs: FxHashSet<_> = path_segs.iter().map(|PathSeg(_, index)| index).collect();
+        let generics_has_err = AstConv::prohibit_generics(
+            self,
+            segments.iter().enumerate().filter_map(|(index, seg)| {
+                if !generic_segs.contains(&index) || is_alias_variant_ctor {
+                    Some(seg)
+                } else {
+                    None
+                }
+            }),
+        );
+
+        if let Res::Local(hid) = res {
+            let ty = self.local_ty(span, hid).decl_ty;
+            let ty = self.normalize_associated_types_in(span, &ty);
+            self.write_ty(hir_id, ty);
+            return (ty, res);
+        }
+
+        if generics_has_err {
+            // Don't try to infer type parameters when prohibited generic arguments were given.
+            user_self_ty = None;
+        }
+
+        // Now we have to compare the types that the user *actually*
+        // provided against the types that were *expected*. If the user
+        // did not provide any types, then we want to substitute inference
+        // variables. If the user provided some types, we may still need
+        // to add defaults. If the user provided *too many* types, that's
+        // a problem.
+
+        let mut infer_args_for_err = FxHashSet::default();
+        for &PathSeg(def_id, index) in &path_segs {
+            let seg = &segments[index];
+            let generics = tcx.generics_of(def_id);
+            // Argument-position `impl Trait` is treated as a normal generic
+            // parameter internally, but we don't allow users to specify the
+            // parameter's value explicitly, so we have to do some error-
+            // checking here.
+            if let GenericArgCountResult {
+                correct: Err(GenericArgCountMismatch { reported: Some(ErrorReported), .. }),
+                ..
+            } = AstConv::check_generic_arg_count_for_call(
+                tcx, span, &generics, &seg, false, // `is_method_call`
+            ) {
+                infer_args_for_err.insert(index);
+                self.set_tainted_by_errors(); // See issue #53251.
+            }
+        }
+
+        let has_self = path_segs
+            .last()
+            .map(|PathSeg(def_id, _)| tcx.generics_of(*def_id).has_self)
+            .unwrap_or(false);
+
+        let (res, self_ctor_substs) = if let Res::SelfCtor(impl_def_id) = res {
+            let ty = self.normalize_ty(span, tcx.at(span).type_of(impl_def_id));
+            match *ty.kind() {
+                ty::Adt(adt_def, substs) if adt_def.has_ctor() => {
+                    let variant = adt_def.non_enum_variant();
+                    let ctor_def_id = variant.ctor_def_id.unwrap();
+                    (
+                        Res::Def(DefKind::Ctor(CtorOf::Struct, variant.ctor_kind), ctor_def_id),
+                        Some(substs),
+                    )
+                }
+                _ => {
+                    let mut err = tcx.sess.struct_span_err(
+                        span,
+                        "the `Self` constructor can only be used with tuple or unit structs",
+                    );
+                    if let Some(adt_def) = ty.ty_adt_def() {
+                        match adt_def.adt_kind() {
+                            AdtKind::Enum => {
+                                err.help("did you mean to use one of the enum's variants?");
+                            }
+                            AdtKind::Struct | AdtKind::Union => {
+                                err.span_suggestion(
+                                    span,
+                                    "use curly brackets",
+                                    String::from("Self { /* fields */ }"),
+                                    Applicability::HasPlaceholders,
+                                );
+                            }
+                        }
+                    }
+                    err.emit();
+
+                    return (tcx.ty_error(), res);
+                }
+            }
+        } else {
+            (res, None)
+        };
+        let def_id = res.def_id();
+
+        // The things we are substituting into the type should not contain
+        // escaping late-bound regions, and nor should the base type scheme.
+        let ty = tcx.type_of(def_id);
+
+        let arg_count = GenericArgCountResult {
+            explicit_late_bound: ExplicitLateBound::No,
+            correct: if infer_args_for_err.is_empty() {
+                Ok(())
+            } else {
+                Err(GenericArgCountMismatch::default())
+            },
+        };
+
+        let substs = self_ctor_substs.unwrap_or_else(|| {
+            AstConv::create_substs_for_generic_args(
+                tcx,
+                def_id,
+                &[][..],
+                has_self,
+                self_ty,
+                arg_count,
+                // Provide the generic args, and whether types should be inferred.
+                |def_id| {
+                    if let Some(&PathSeg(_, index)) =
+                        path_segs.iter().find(|&PathSeg(did, _)| *did == def_id)
+                    {
+                        // If we've encountered an `impl Trait`-related error, we're just
+                        // going to infer the arguments for better error messages.
+                        if !infer_args_for_err.contains(&index) {
+                            // Check whether the user has provided generic arguments.
+                            if let Some(ref data) = segments[index].args {
+                                return (Some(data), segments[index].infer_args);
+                            }
+                        }
+                        return (None, segments[index].infer_args);
+                    }
+
+                    (None, true)
+                },
+                // Provide substitutions for parameters for which (valid) arguments have been provided.
+                |param, arg| match (&param.kind, arg) {
+                    (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => {
+                        AstConv::ast_region_to_region(self, lt, Some(param)).into()
+                    }
+                    (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => {
+                        self.to_ty(ty).into()
+                    }
+                    (GenericParamDefKind::Const, GenericArg::Const(ct)) => {
+                        self.const_arg_to_const(&ct.value, param.def_id).into()
+                    }
+                    _ => unreachable!(),
+                },
+                // Provide substitutions for parameters for which arguments are inferred.
+                |substs, param, infer_args| {
+                    match param.kind {
+                        GenericParamDefKind::Lifetime => {
+                            self.re_infer(Some(param), span).unwrap().into()
+                        }
+                        GenericParamDefKind::Type { has_default, .. } => {
+                            if !infer_args && has_default {
+                                // If we have a default, then we it doesn't matter that we're not
+                                // inferring the type arguments: we provide the default where any
+                                // is missing.
+                                let default = tcx.type_of(param.def_id);
+                                self.normalize_ty(
+                                    span,
+                                    default.subst_spanned(tcx, substs.unwrap(), Some(span)),
+                                )
+                                .into()
+                            } else {
+                                // If no type arguments were provided, we have to infer them.
+                                // This case also occurs as a result of some malformed input, e.g.
+                                // a lifetime argument being given instead of a type parameter.
+                                // Using inference instead of `Error` gives better error messages.
+                                self.var_for_def(span, param)
+                            }
+                        }
+                        GenericParamDefKind::Const => {
+                            // FIXME(const_generics:defaults)
+                            // No const parameters were provided, we have to infer them.
+                            self.var_for_def(span, param)
+                        }
+                    }
+                },
+            )
+        });
+        assert!(!substs.has_escaping_bound_vars());
+        assert!(!ty.has_escaping_bound_vars());
+
+        // First, store the "user substs" for later.
+        self.write_user_type_annotation_from_substs(hir_id, def_id, substs, user_self_ty);
+
+        self.add_required_obligations(span, def_id, &substs);
+
+        // Substitute the values for the type parameters into the type of
+        // the referenced item.
+        let ty_substituted = self.instantiate_type_scheme(span, &substs, &ty);
+
+        if let Some(UserSelfTy { impl_def_id, self_ty }) = user_self_ty {
+            // In the case of `Foo<T>::method` and `<Foo<T>>::method`, if `method`
+            // is inherent, there is no `Self` parameter; instead, the impl needs
+            // type parameters, which we can infer by unifying the provided `Self`
+            // with the substituted impl type.
+            // This also occurs for an enum variant on a type alias.
+            let ty = tcx.type_of(impl_def_id);
+
+            let impl_ty = self.instantiate_type_scheme(span, &substs, &ty);
+            match self.at(&self.misc(span), self.param_env).sup(impl_ty, self_ty) {
+                Ok(ok) => self.register_infer_ok_obligations(ok),
+                Err(_) => {
+                    self.tcx.sess.delay_span_bug(
+                        span,
+                        &format!(
+                        "instantiate_value_path: (UFCS) {:?} was a subtype of {:?} but now is not?",
+                        self_ty,
+                        impl_ty,
+                    ),
+                    );
+                }
+            }
+        }
+
+        self.check_rustc_args_require_const(def_id, hir_id, span);
+
+        debug!("instantiate_value_path: type of {:?} is {:?}", hir_id, ty_substituted);
+        self.write_substs(hir_id, substs);
+
+        (ty_substituted, res)
+    }
+
+    /// Add all the obligations that are required, substituting and normalized appropriately.
+    fn add_required_obligations(&self, span: Span, def_id: DefId, substs: &SubstsRef<'tcx>) {
+        let (bounds, spans) = self.instantiate_bounds(span, def_id, &substs);
+
+        for (i, mut obligation) in traits::predicates_for_generics(
+            traits::ObligationCause::new(span, self.body_id, traits::ItemObligation(def_id)),
+            self.param_env,
+            bounds,
+        )
+        .enumerate()
+        {
+            // This makes the error point at the bound, but we want to point at the argument
+            if let Some(span) = spans.get(i) {
+                obligation.cause.make_mut().code = traits::BindingObligation(def_id, *span);
+            }
+            self.register_predicate(obligation);
+        }
+    }
+
+    fn check_rustc_args_require_const(&self, def_id: DefId, hir_id: hir::HirId, span: Span) {
+        // We're only interested in functions tagged with
+        // #[rustc_args_required_const], so ignore anything that's not.
+        if !self.tcx.has_attr(def_id, sym::rustc_args_required_const) {
+            return;
+        }
+
+        // If our calling expression is indeed the function itself, we're good!
+        // If not, generate an error that this can only be called directly.
+        if let Node::Expr(expr) = self.tcx.hir().get(self.tcx.hir().get_parent_node(hir_id)) {
+            if let ExprKind::Call(ref callee, ..) = expr.kind {
+                if callee.hir_id == hir_id {
+                    return;
+                }
+            }
+        }
+
+        self.tcx.sess.span_err(
+            span,
+            "this function can only be invoked directly, not through a function pointer",
+        );
+    }
+
+    /// Resolves `typ` by a single level if `typ` is a type variable.
+    /// 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 ty = self.resolve_vars_with_obligations(ty);
+        if !ty.is_ty_var() {
+            ty
+        } else {
+            if !self.is_tainted_by_errors() {
+                self.need_type_info_err((**self).body_id, sp, ty, E0282)
+                    .note("type must be known at this point")
+                    .emit();
+            }
+            let err = self.tcx.ty_error();
+            self.demand_suptype(sp, err, ty);
+            err
+        }
+    }
+
+    pub(super) fn with_breakable_ctxt<F: FnOnce() -> R, R>(
+        &self,
+        id: hir::HirId,
+        ctxt: BreakableCtxt<'tcx>,
+        f: F,
+    ) -> (BreakableCtxt<'tcx>, R) {
+        let index;
+        {
+            let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
+            index = enclosing_breakables.stack.len();
+            enclosing_breakables.by_id.insert(id, index);
+            enclosing_breakables.stack.push(ctxt);
+        }
+        let result = f();
+        let ctxt = {
+            let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
+            debug_assert!(enclosing_breakables.stack.len() == index + 1);
+            enclosing_breakables.by_id.remove(&id).expect("missing breakable context");
+            enclosing_breakables.stack.pop().expect("missing breakable context")
+        };
+        (ctxt, result)
+    }
+
+    /// Instantiate a QueryResponse in a probe context, without a
+    /// good ObligationCause.
+    pub(super) fn probe_instantiate_query_response(
+        &self,
+        span: Span,
+        original_values: &OriginalQueryValues<'tcx>,
+        query_result: &Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>,
+    ) -> InferResult<'tcx, Ty<'tcx>> {
+        self.instantiate_query_response_and_region_obligations(
+            &traits::ObligationCause::misc(span, self.body_id),
+            self.param_env,
+            original_values,
+            query_result,
+        )
+    }
+
+    /// Returns `true` if an expression is contained inside the LHS of an assignment expression.
+    pub(super) fn expr_in_place(&self, mut expr_id: hir::HirId) -> bool {
+        let mut contained_in_place = false;
+
+        while let hir::Node::Expr(parent_expr) =
+            self.tcx.hir().get(self.tcx.hir().get_parent_node(expr_id))
+        {
+            match &parent_expr.kind {
+                hir::ExprKind::Assign(lhs, ..) | hir::ExprKind::AssignOp(_, lhs, ..) => {
+                    if lhs.hir_id == expr_id {
+                        contained_in_place = true;
+                        break;
+                    }
+                }
+                _ => (),
+            }
+            expr_id = parent_expr.hir_id;
+        }
+
+        contained_in_place
+    }
+}
+impl<'a, 'tcx> Deref for FnCtxt<'a, 'tcx> {
+    type Target = Inherited<'a, 'tcx>;
+    fn deref(&self) -> &Self::Target {
+        &self.inh
+    }
+}
+
+impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
+    fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
+        self.tcx
+    }
+
+    fn item_def_id(&self) -> Option<DefId> {
+        None
+    }
+
+    fn default_constness_for_trait_bounds(&self) -> hir::Constness {
+        // FIXME: refactor this into a method
+        let node = self.tcx.hir().get(self.body_id);
+        if let Some(fn_like) = FnLikeNode::from_node(node) {
+            fn_like.constness()
+        } else {
+            hir::Constness::NotConst
+        }
+    }
+
+    fn get_type_parameter_bounds(&self, _: Span, def_id: DefId) -> ty::GenericPredicates<'tcx> {
+        let tcx = self.tcx;
+        let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
+        let item_id = tcx.hir().ty_param_owner(hir_id);
+        let item_def_id = tcx.hir().local_def_id(item_id);
+        let generics = tcx.generics_of(item_def_id);
+        let index = generics.param_def_id_to_index[&def_id];
+        ty::GenericPredicates {
+            parent: None,
+            predicates: tcx.arena.alloc_from_iter(
+                self.param_env.caller_bounds().iter().filter_map(|predicate| {
+                    match predicate.skip_binders() {
+                        ty::PredicateAtom::Trait(data, _) if data.self_ty().is_param(index) => {
+                            // HACK(eddyb) should get the original `Span`.
+                            let span = tcx.def_span(def_id);
+                            Some((predicate, span))
+                        }
+                        _ => None,
+                    }
+                }),
+            ),
+        }
+    }
+
+    fn re_infer(&self, def: Option<&ty::GenericParamDef>, span: Span) -> Option<ty::Region<'tcx>> {
+        let v = match def {
+            Some(def) => infer::EarlyBoundRegion(span, def.name),
+            None => infer::MiscVariable(span),
+        };
+        Some(self.next_region_var(v))
+    }
+
+    fn allow_ty_infer(&self) -> bool {
+        true
+    }
+
+    fn ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> {
+        if let Some(param) = param {
+            if let GenericArgKind::Type(ty) = self.var_for_def(span, param).unpack() {
+                return ty;
+            }
+            unreachable!()
+        } else {
+            self.next_ty_var(TypeVariableOrigin {
+                kind: TypeVariableOriginKind::TypeInference,
+                span,
+            })
+        }
+    }
+
+    fn ct_infer(
+        &self,
+        ty: Ty<'tcx>,
+        param: Option<&ty::GenericParamDef>,
+        span: Span,
+    ) -> &'tcx Const<'tcx> {
+        if let Some(param) = param {
+            if let GenericArgKind::Const(ct) = self.var_for_def(span, param).unpack() {
+                return ct;
+            }
+            unreachable!()
+        } else {
+            self.next_const_var(
+                ty,
+                ConstVariableOrigin { kind: ConstVariableOriginKind::ConstInference, span },
+            )
+        }
+    }
+
+    fn projected_ty_from_poly_trait_ref(
+        &self,
+        span: Span,
+        item_def_id: DefId,
+        item_segment: &hir::PathSegment<'_>,
+        poly_trait_ref: ty::PolyTraitRef<'tcx>,
+    ) -> Ty<'tcx> {
+        let (trait_ref, _) = self.replace_bound_vars_with_fresh_vars(
+            span,
+            infer::LateBoundRegionConversionTime::AssocTypeProjection(item_def_id),
+            &poly_trait_ref,
+        );
+
+        let item_substs = <dyn AstConv<'tcx>>::create_substs_for_associated_item(
+            self,
+            self.tcx,
+            span,
+            item_def_id,
+            item_segment,
+            trait_ref.substs,
+        );
+
+        self.tcx().mk_projection(item_def_id, item_substs)
+    }
+
+    fn normalize_ty(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
+        if ty.has_escaping_bound_vars() {
+            ty // FIXME: normalization and escaping regions
+        } else {
+            self.normalize_associated_types_in(span, &ty)
+        }
+    }
+
+    fn set_tainted_by_errors(&self) {
+        self.infcx.set_tainted_by_errors()
+    }
+
+    fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, _span: Span) {
+        self.write_ty(hir_id, ty)
+    }
+}
diff --git a/compiler/rustc_typeck/src/check/gather_locals.rs b/compiler/rustc_typeck/src/check/gather_locals.rs
new file mode 100644
index 00000000000..1d505cfa698
--- /dev/null
+++ b/compiler/rustc_typeck/src/check/gather_locals.rs
@@ -0,0 +1,120 @@
+use crate::check::{FnCtxt, LocalTy, UserType};
+use rustc_hir as hir;
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
+use rustc_hir::PatKind;
+use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+use rustc_middle::ty::Ty;
+use rustc_span::Span;
+use rustc_trait_selection::traits;
+
+pub(super) struct GatherLocalsVisitor<'a, 'tcx> {
+    fcx: &'a FnCtxt<'a, 'tcx>,
+    parent_id: hir::HirId,
+}
+
+impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
+    pub(super) fn new(fcx: &'a FnCtxt<'a, 'tcx>, parent_id: hir::HirId) -> Self {
+        Self { fcx, parent_id }
+    }
+
+    fn assign(&mut self, span: Span, nid: hir::HirId, ty_opt: Option<LocalTy<'tcx>>) -> Ty<'tcx> {
+        match ty_opt {
+            None => {
+                // Infer the variable's type.
+                let var_ty = self.fcx.next_ty_var(TypeVariableOrigin {
+                    kind: TypeVariableOriginKind::TypeInference,
+                    span,
+                });
+                self.fcx
+                    .locals
+                    .borrow_mut()
+                    .insert(nid, LocalTy { decl_ty: var_ty, revealed_ty: var_ty });
+                var_ty
+            }
+            Some(typ) => {
+                // Take type that the user specified.
+                self.fcx.locals.borrow_mut().insert(nid, typ);
+                typ.revealed_ty
+            }
+        }
+    }
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
+    type Map = intravisit::ErasedMap<'tcx>;
+
+    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+        NestedVisitorMap::None
+    }
+
+    // Add explicitly-declared locals.
+    fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) {
+        let local_ty = match local.ty {
+            Some(ref ty) => {
+                let o_ty = self.fcx.to_ty(&ty);
+
+                let revealed_ty = if self.fcx.tcx.features().impl_trait_in_bindings {
+                    self.fcx.instantiate_opaque_types_from_value(self.parent_id, &o_ty, ty.span)
+                } else {
+                    o_ty
+                };
+
+                let c_ty = self
+                    .fcx
+                    .inh
+                    .infcx
+                    .canonicalize_user_type_annotation(&UserType::Ty(revealed_ty));
+                debug!(
+                    "visit_local: ty.hir_id={:?} o_ty={:?} revealed_ty={:?} c_ty={:?}",
+                    ty.hir_id, o_ty, revealed_ty, c_ty
+                );
+                self.fcx
+                    .typeck_results
+                    .borrow_mut()
+                    .user_provided_types_mut()
+                    .insert(ty.hir_id, c_ty);
+
+                Some(LocalTy { decl_ty: o_ty, revealed_ty })
+            }
+            None => None,
+        };
+        self.assign(local.span, local.hir_id, local_ty);
+
+        debug!(
+            "local variable {:?} is assigned type {}",
+            local.pat,
+            self.fcx.ty_to_string(&*self.fcx.locals.borrow().get(&local.hir_id).unwrap().decl_ty)
+        );
+        intravisit::walk_local(self, local);
+    }
+
+    // Add pattern bindings.
+    fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) {
+        if let PatKind::Binding(_, _, ident, _) = p.kind {
+            let var_ty = self.assign(p.span, p.hir_id, None);
+
+            if !self.fcx.tcx.features().unsized_locals {
+                self.fcx.require_type_is_sized(var_ty, p.span, traits::VariableType(p.hir_id));
+            }
+
+            debug!(
+                "pattern binding {} is assigned to {} with type {:?}",
+                ident,
+                self.fcx.ty_to_string(&*self.fcx.locals.borrow().get(&p.hir_id).unwrap().decl_ty),
+                var_ty
+            );
+        }
+        intravisit::walk_pat(self, p);
+    }
+
+    // Don't descend into the bodies of nested closures.
+    fn visit_fn(
+        &mut self,
+        _: intravisit::FnKind<'tcx>,
+        _: &'tcx hir::FnDecl<'tcx>,
+        _: hir::BodyId,
+        _: Span,
+        _: hir::HirId,
+    ) {
+    }
+}
diff --git a/compiler/rustc_typeck/src/check/inherited.rs b/compiler/rustc_typeck/src/check/inherited.rs
new file mode 100644
index 00000000000..7e580485c3d
--- /dev/null
+++ b/compiler/rustc_typeck/src/check/inherited.rs
@@ -0,0 +1,167 @@
+use super::callee::DeferredCallResolution;
+use super::MaybeInProgressTables;
+
+use rustc_data_structures::fx::FxHashMap;
+use rustc_hir as hir;
+use rustc_hir::def_id::{DefIdMap, LocalDefId};
+use rustc_hir::HirIdMap;
+use rustc_infer::infer;
+use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt};
+use rustc_middle::ty::fold::TypeFoldable;
+use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_span::{self, Span};
+use rustc_trait_selection::infer::InferCtxtExt as _;
+use rustc_trait_selection::opaque_types::OpaqueTypeDecl;
+use rustc_trait_selection::traits::{self, TraitEngine, TraitEngineExt};
+
+use std::cell::RefCell;
+use std::ops::Deref;
+
+/// Closures defined within the function. For example:
+///
+///     fn foo() {
+///         bar(move|| { ... })
+///     }
+///
+/// Here, the function `foo()` and the closure passed to
+/// `bar()` will each have their own `FnCtxt`, but they will
+/// share the inherited fields.
+pub struct Inherited<'a, 'tcx> {
+    pub(super) infcx: InferCtxt<'a, 'tcx>,
+
+    pub(super) typeck_results: super::MaybeInProgressTables<'a, 'tcx>,
+
+    pub(super) locals: RefCell<HirIdMap<super::LocalTy<'tcx>>>,
+
+    pub(super) fulfillment_cx: RefCell<Box<dyn TraitEngine<'tcx>>>,
+
+    // Some additional `Sized` obligations badly affect type inference.
+    // These obligations are added in a later stage of typeck.
+    pub(super) deferred_sized_obligations:
+        RefCell<Vec<(Ty<'tcx>, Span, traits::ObligationCauseCode<'tcx>)>>,
+
+    // When we process a call like `c()` where `c` is a closure type,
+    // we may not have decided yet whether `c` is a `Fn`, `FnMut`, or
+    // `FnOnce` closure. In that case, we defer full resolution of the
+    // call until upvar inference can kick in and make the
+    // decision. We keep these deferred resolutions grouped by the
+    // def-id of the closure, so that once we decide, we can easily go
+    // back and process them.
+    pub(super) deferred_call_resolutions: RefCell<DefIdMap<Vec<DeferredCallResolution<'tcx>>>>,
+
+    pub(super) deferred_cast_checks: RefCell<Vec<super::cast::CastCheck<'tcx>>>,
+
+    pub(super) deferred_generator_interiors:
+        RefCell<Vec<(hir::BodyId, Ty<'tcx>, hir::GeneratorKind)>>,
+
+    // Opaque types found in explicit return types and their
+    // associated fresh inference variable. Writeback resolves these
+    // variables to get the concrete type, which can be used to
+    // 'de-opaque' OpaqueTypeDecl, after typeck is done with all functions.
+    pub(super) opaque_types: RefCell<DefIdMap<OpaqueTypeDecl<'tcx>>>,
+
+    /// A map from inference variables created from opaque
+    /// type instantiations (`ty::Infer`) to the actual opaque
+    /// type (`ty::Opaque`). Used during fallback to map unconstrained
+    /// opaque type inference variables to their corresponding
+    /// opaque type.
+    pub(super) opaque_types_vars: RefCell<FxHashMap<Ty<'tcx>, Ty<'tcx>>>,
+
+    pub(super) body_id: Option<hir::BodyId>,
+}
+
+impl<'a, 'tcx> Deref for Inherited<'a, 'tcx> {
+    type Target = InferCtxt<'a, 'tcx>;
+    fn deref(&self) -> &Self::Target {
+        &self.infcx
+    }
+}
+
+/// Helper type of a temporary returned by `Inherited::build(...)`.
+/// Necessary because we can't write the following bound:
+/// `F: for<'b, 'tcx> where 'tcx FnOnce(Inherited<'b, 'tcx>)`.
+pub struct InheritedBuilder<'tcx> {
+    infcx: infer::InferCtxtBuilder<'tcx>,
+    def_id: LocalDefId,
+}
+
+impl Inherited<'_, 'tcx> {
+    pub fn build(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> InheritedBuilder<'tcx> {
+        let hir_owner = tcx.hir().local_def_id_to_hir_id(def_id).owner;
+
+        InheritedBuilder {
+            infcx: tcx.infer_ctxt().with_fresh_in_progress_typeck_results(hir_owner),
+            def_id,
+        }
+    }
+}
+
+impl<'tcx> InheritedBuilder<'tcx> {
+    pub fn enter<F, R>(&mut self, f: F) -> R
+    where
+        F: for<'a> FnOnce(Inherited<'a, 'tcx>) -> R,
+    {
+        let def_id = self.def_id;
+        self.infcx.enter(|infcx| f(Inherited::new(infcx, def_id)))
+    }
+}
+
+impl Inherited<'a, 'tcx> {
+    pub(super) fn new(infcx: InferCtxt<'a, 'tcx>, def_id: LocalDefId) -> Self {
+        let tcx = infcx.tcx;
+        let item_id = tcx.hir().local_def_id_to_hir_id(def_id);
+        let body_id = tcx.hir().maybe_body_owned_by(item_id);
+
+        Inherited {
+            typeck_results: MaybeInProgressTables {
+                maybe_typeck_results: infcx.in_progress_typeck_results,
+            },
+            infcx,
+            fulfillment_cx: RefCell::new(TraitEngine::new(tcx)),
+            locals: RefCell::new(Default::default()),
+            deferred_sized_obligations: RefCell::new(Vec::new()),
+            deferred_call_resolutions: RefCell::new(Default::default()),
+            deferred_cast_checks: RefCell::new(Vec::new()),
+            deferred_generator_interiors: RefCell::new(Vec::new()),
+            opaque_types: RefCell::new(Default::default()),
+            opaque_types_vars: RefCell::new(Default::default()),
+            body_id,
+        }
+    }
+
+    pub(super) fn register_predicate(&self, obligation: traits::PredicateObligation<'tcx>) {
+        debug!("register_predicate({:?})", obligation);
+        if obligation.has_escaping_bound_vars() {
+            span_bug!(obligation.cause.span, "escaping bound vars in predicate {:?}", obligation);
+        }
+        self.fulfillment_cx.borrow_mut().register_predicate_obligation(self, obligation);
+    }
+
+    pub(super) fn register_predicates<I>(&self, obligations: I)
+    where
+        I: IntoIterator<Item = traits::PredicateObligation<'tcx>>,
+    {
+        for obligation in obligations {
+            self.register_predicate(obligation);
+        }
+    }
+
+    pub(super) fn register_infer_ok_obligations<T>(&self, infer_ok: InferOk<'tcx, T>) -> T {
+        self.register_predicates(infer_ok.obligations);
+        infer_ok.value
+    }
+
+    pub(super) fn normalize_associated_types_in<T>(
+        &self,
+        span: Span,
+        body_id: hir::HirId,
+        param_env: ty::ParamEnv<'tcx>,
+        value: &T,
+    ) -> T
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        let ok = self.partially_normalize_associated_types_in(span, body_id, param_env, value);
+        self.register_infer_ok_obligations(ok)
+    }
+}
diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_typeck/src/check/intrinsic.rs
index 47cea8649ef..b8230f52444 100644
--- a/compiler/rustc_typeck/src/check/intrinsic.rs
+++ b/compiler/rustc_typeck/src/check/intrinsic.rs
@@ -1,6 +1,10 @@
 //! Type-checking for the rust-intrinsic and platform-intrinsic
 //! intrinsics that the compiler exposes.
 
+use crate::errors::{
+    SimdShuffleMissingLength, UnrecognizedAtomicOperation, UnrecognizedIntrinsicFunction,
+    WrongNumberOfTypeArgumentsToInstrinsic,
+};
 use crate::require_same_types;
 
 use rustc_errors::struct_span_err;
@@ -41,17 +45,11 @@ fn equate_intrinsic_type<'tcx>(
             _ => bug!(),
         };
 
-        struct_span_err!(
-            tcx.sess,
+        tcx.sess.emit_err(WrongNumberOfTypeArgumentsToInstrinsic {
             span,
-            E0094,
-            "intrinsic has wrong number of type \
-                         parameters: found {}, expected {}",
-            i_n_tps,
-            n_tps
-        )
-        .span_label(span, format!("expected {} type parameter", n_tps))
-        .emit();
+            found: i_n_tps,
+            expected: n_tps,
+        });
         return;
     }
 
@@ -146,15 +144,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
             | "umin" => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], param(0)),
             "fence" | "singlethreadfence" => (0, Vec::new(), tcx.mk_unit()),
             op => {
-                struct_span_err!(
-                    tcx.sess,
-                    it.span,
-                    E0092,
-                    "unrecognized atomic operation function: `{}`",
-                    op
-                )
-                .span_label(it.span, "unrecognized atomic operation")
-                .emit();
+                tcx.sess.emit_err(UnrecognizedAtomicOperation { span: it.span, op });
                 return;
             }
         };
@@ -380,15 +370,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
             sym::nontemporal_store => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit()),
 
             other => {
-                struct_span_err!(
-                    tcx.sess,
-                    it.span,
-                    E0093,
-                    "unrecognized intrinsic function: `{}`",
-                    other,
-                )
-                .span_label(it.span, "unrecognized intrinsic")
-                .emit();
+                tcx.sess.emit_err(UnrecognizedIntrinsicFunction { span: it.span, name: other });
                 return;
             }
         };
@@ -468,14 +450,7 @@ pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>)
                     (2, params, param(1))
                 }
                 Err(_) => {
-                    struct_span_err!(
-                        tcx.sess,
-                        it.span,
-                        E0439,
-                        "invalid `simd_shuffle`, needs length: `{}`",
-                        name
-                    )
-                    .emit();
+                    tcx.sess.emit_err(SimdShuffleMissingLength { span: it.span, name });
                     return;
                 }
             }
diff --git a/compiler/rustc_typeck/src/check/method/confirm.rs b/compiler/rustc_typeck/src/check/method/confirm.rs
index 41e37ee9752..fd2700b85e2 100644
--- a/compiler/rustc_typeck/src/check/method/confirm.rs
+++ b/compiler/rustc_typeck/src/check/method/confirm.rs
@@ -137,7 +137,8 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
     ) -> Ty<'tcx> {
         // Commit the autoderefs by calling `autoderef` again, but this
         // time writing the results into the various typeck results.
-        let mut autoderef = self.autoderef(self.span, unadjusted_self_ty);
+        let mut autoderef =
+            self.autoderef_overloaded_span(self.span, unadjusted_self_ty, self.call_expr.span);
         let (_, n) = match autoderef.nth(pick.autoderefs) {
             Some(n) => n,
             None => {
@@ -269,7 +270,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
         self.fcx
             .autoderef(self.span, self_ty)
             .include_raw_pointers()
-            .find_map(|(ty, _)| match ty.kind {
+            .find_map(|(ty, _)| match ty.kind() {
                 ty::Dynamic(ref data, ..) => Some(closure(
                     self,
                     ty,
@@ -464,7 +465,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
                 }
                 _ => None,
             })
-            .find_map(|(trait_pred, span)| match trait_pred.self_ty().kind {
+            .find_map(|(trait_pred, span)| match trait_pred.self_ty().kind() {
                 ty::Dynamic(..) => Some(span),
                 _ => None,
             })
diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs
index c9a4df0317a..84bc3979e12 100644
--- a/compiler/rustc_typeck/src/check/method/mod.rs
+++ b/compiler/rustc_typeck/src/check/method/mod.rs
@@ -207,7 +207,7 @@ 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 {
+            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() });
@@ -424,7 +424,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let tcx = self.tcx;
 
         // Check if we have an enum variant.
-        if let ty::Adt(adt_def, _) = self_ty.kind {
+        if let ty::Adt(adt_def, _) = self_ty.kind() {
             if adt_def.is_enum() {
                 let variant_def = adt_def
                     .variants
diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_typeck/src/check/method/probe.rs
index 7ac6681be1a..8a62031ec88 100644
--- a/compiler/rustc_typeck/src/check/method/probe.rs
+++ b/compiler/rustc_typeck/src/check/method/probe.rs
@@ -4,6 +4,7 @@ use super::NoMatchData;
 use super::{CandidateSource, ImplSource, TraitSource};
 
 use crate::check::FnCtxt;
+use crate::errors::MethodCallOnUnknownType;
 use crate::hir::def::DefKind;
 use crate::hir::def_id::DefId;
 
@@ -11,7 +12,6 @@ use rustc_ast as ast;
 use rustc_ast::util::lev_distance::{find_best_match_for_name, lev_distance};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::sync::Lrc;
-use rustc_errors::struct_span_err;
 use rustc_hir as hir;
 use rustc_hir::def::Namespace;
 use rustc_infer::infer::canonical::OriginalQueryValues;
@@ -376,14 +376,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // so we do a future-compat lint here for the 2015 edition
                 // (see https://github.com/rust-lang/rust/issues/46906)
                 if self.tcx.sess.rust_2018() {
-                    struct_span_err!(
-                        self.tcx.sess,
-                        span,
-                        E0699,
-                        "the type of this value must be known to call a method on a raw pointer on \
-                         it"
-                    )
-                    .emit();
+                    self.tcx.sess.emit_err(MethodCallOnUnknownType { span });
                 } else {
                     self.tcx.struct_span_lint_hir(
                         lint::builtin::TYVAR_BEHIND_RAW_POINTER,
@@ -401,7 +394,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     .probe_instantiate_query_response(span, &orig_values, ty)
                     .unwrap_or_else(|_| span_bug!(span, "instantiating {:?} failed?", ty));
                 let ty = self.structurally_resolved_type(span, ty.value);
-                assert!(matches!(ty.kind, ty::Error(_)));
+                assert!(matches!(ty.kind(), ty::Error(_)));
                 return Err(MethodError::NoMatch(NoMatchData::new(
                     Vec::new(),
                     Vec::new(),
@@ -453,9 +446,10 @@ fn method_autoderef_steps<'tcx>(
     tcx.infer_ctxt().enter_with_canonical(DUMMY_SP, &goal, |ref infcx, goal, inference_vars| {
         let ParamEnvAnd { param_env, value: self_ty } = goal;
 
-        let mut autoderef = Autoderef::new(infcx, param_env, hir::CRATE_HIR_ID, DUMMY_SP, self_ty)
-            .include_raw_pointers()
-            .silence_errors();
+        let mut autoderef =
+            Autoderef::new(infcx, param_env, hir::CRATE_HIR_ID, DUMMY_SP, self_ty, DUMMY_SP)
+                .include_raw_pointers()
+                .silence_errors();
         let mut reached_raw_pointer = false;
         let mut steps: Vec<_> = autoderef
             .by_ref()
@@ -469,7 +463,7 @@ fn method_autoderef_steps<'tcx>(
                     from_unsafe_deref: reached_raw_pointer,
                     unsize: false,
                 };
-                if let ty::RawPtr(_) = ty.kind {
+                if let ty::RawPtr(_) = ty.kind() {
                     // all the subsequent steps will be from_unsafe_deref
                     reached_raw_pointer = true;
                 }
@@ -478,7 +472,7 @@ fn method_autoderef_steps<'tcx>(
             .collect();
 
         let final_ty = autoderef.final_ty(true);
-        let opt_bad_ty = match final_ty.kind {
+        let opt_bad_ty = match final_ty.kind() {
             ty::Infer(ty::TyVar(_)) | ty::Error(_) => Some(MethodAutoderefBadTy {
                 reached_raw_pointer,
                 ty: infcx
@@ -587,7 +581,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
         debug!("assemble_probe: self_ty={:?}", self_ty);
         let lang_items = self.tcx.lang_items();
 
-        match self_ty.value.value.kind {
+        match *self_ty.value.value.kind() {
             ty::Dynamic(ref data, ..) => {
                 if let Some(p) = data.principal() {
                     // Subtle: we can't use `instantiate_query_response` here: using it will
@@ -759,7 +753,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
     fn assemble_inherent_candidates_from_object(&mut self, self_ty: Ty<'tcx>) {
         debug!("assemble_inherent_candidates_from_object(self_ty={:?})", self_ty);
 
-        let principal = match self_ty.kind {
+        let principal = match self_ty.kind() {
             ty::Dynamic(ref data, ..) => Some(data),
             _ => None,
         }
@@ -806,7 +800,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
             self.param_env.caller_bounds().iter().map(ty::Predicate::skip_binders).filter_map(
                 |predicate| match predicate {
                     ty::PredicateAtom::Trait(trait_predicate, _) => {
-                        match trait_predicate.trait_ref.self_ty().kind {
+                        match trait_predicate.trait_ref.self_ty().kind() {
                             ty::Param(ref p) if *p == param_ty => {
                                 Some(ty::Binder::bind(trait_predicate.trait_ref))
                             }
@@ -821,7 +815,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
                     | ty::PredicateAtom::ClosureKind(..)
                     | ty::PredicateAtom::TypeOutlives(..)
                     | ty::PredicateAtom::ConstEvaluatable(..)
-                    | ty::PredicateAtom::ConstEquate(..) => None,
+                    | ty::PredicateAtom::ConstEquate(..)
+                    | ty::PredicateAtom::TypeWellFormedFromEnv(..) => None,
                 },
             );
 
@@ -1125,7 +1120,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
                 pick.autoderefs = step.autoderefs;
 
                 // Insert a `&*` or `&mut *` if this is a reference type:
-                if let ty::Ref(_, _, mutbl) = step.self_ty.value.value.kind {
+                if let ty::Ref(_, _, mutbl) = *step.self_ty.value.value.kind() {
                     pick.autoderefs += 1;
                     pick.autoref = Some(mutbl);
                 }
diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs
index 5cae66bc5da..e33a4e98c59 100644
--- a/compiler/rustc_typeck/src/check/method/suggest.rs
+++ b/compiler/rustc_typeck/src/check/method/suggest.rs
@@ -31,7 +31,7 @@ use super::{CandidateSource, MethodError, NoMatchData};
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool {
         let tcx = self.tcx;
-        match ty.kind {
+        match ty.kind() {
             // Not all of these (e.g., unsafe fns) implement `FnOnce`,
             // so we look for these beforehand.
             ty::Closure(..) | ty::FnDef(..) | ty::FnPtr(_) => true,
@@ -413,7 +413,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                 );
                             }
                         }
-                        if let ty::RawPtr(_) = &actual.kind {
+                        if let ty::RawPtr(_) = &actual.kind() {
                             err.note(
                                 "try using `<*const T>::as_ref()` to get a reference to the \
                                       type behind the pointer: https://doc.rust-lang.org/std/\
@@ -450,7 +450,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // give a helping note that it has to be called as `(x.f)(...)`.
                 if let SelfSource::MethodCall(expr) = source {
                     let field_receiver =
-                        self.autoderef(span, rcvr_ty).find_map(|(ty, _)| match ty.kind {
+                        self.autoderef(span, rcvr_ty).find_map(|(ty, _)| match ty.kind() {
                             ty::Adt(def, substs) if !def.is_enum() => {
                                 let variant = &def.non_enum_variant();
                                 self.tcx.find_field_index(item_name, variant).map(|index| {
@@ -545,7 +545,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         // original type that has the associated function for accurate suggestions.
                         // (#61411)
                         let ty = tcx.at(span).type_of(*impl_did);
-                        match (&ty.peel_refs().kind, &actual.peel_refs().kind) {
+                        match (&ty.peel_refs().kind(), &actual.peel_refs().kind()) {
                             (ty::Adt(def, _), ty::Adt(def_actual, _)) if def == def_actual => {
                                 // Use `actual` as it will have more `substs` filled in.
                                 self.ty_to_value_string(actual.peel_refs())
@@ -583,9 +583,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         |self_ty: Ty<'tcx>, parent_pred: &ty::Predicate<'tcx>, obligation: &str| {
                             // We don't care about regions here, so it's fine to skip the binder here.
                             if let (ty::Param(_), ty::PredicateAtom::Trait(p, _)) =
-                                (&self_ty.kind, parent_pred.skip_binders())
+                                (self_ty.kind(), parent_pred.skip_binders())
                             {
-                                if let ty::Adt(def, _) = p.trait_ref.self_ty().kind {
+                                if let ty::Adt(def, _) = p.trait_ref.self_ty().kind() {
                                     let node = def.did.as_local().map(|def_id| {
                                         self.tcx
                                             .hir()
@@ -615,7 +615,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             "doesn't satisfy `{}`",
                             if obligation.len() > 50 { quiet } else { obligation }
                         );
-                        match &self_ty.kind {
+                        match &self_ty.kind() {
                             // Point at the type that couldn't satisfy the bound.
                             ty::Adt(def, _) => bound_spans.push((def_span(def.did), msg)),
                             // Point at the trait object that couldn't satisfy the bound.
@@ -837,7 +837,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     );
                     self.suggest_use_candidates(&mut err, help, candidates);
                 }
-                if let ty::Ref(region, t_type, mutability) = rcvr_ty.kind {
+                if let ty::Ref(region, t_type, mutability) = rcvr_ty.kind() {
                     if needs_mut {
                         let trait_type = self.tcx.mk_ref(
                             region,
@@ -856,7 +856,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     /// Print out the type for use in value namespace.
     fn ty_to_value_string(&self, ty: Ty<'tcx>) -> String {
-        match ty.kind {
+        match ty.kind() {
             ty::Adt(def, substs) => format!("{}", ty::Instance::new(def.did, substs)),
             _ => self.ty_to_string(ty),
         }
@@ -870,7 +870,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         call: &hir::Expr<'_>,
         span: Span,
     ) {
-        if let ty::Opaque(def_id, _) = ty.kind {
+        if let ty::Opaque(def_id, _) = *ty.kind() {
             let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
             // Future::Output
             let item_def_id = self
@@ -897,7 +897,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 debug!(
                     "suggest_await_before_method: normalized_ty={:?}, ty_kind={:?}",
                     self.resolve_vars_if_possible(&normalized_ty),
-                    normalized_ty.kind,
+                    normalized_ty.kind(),
                 );
                 let method_exists = self.method_exists(item_name, normalized_ty, call.hir_id, true);
                 debug!("suggest_await_before_method: is_method_exist={}", method_exists);
@@ -1089,9 +1089,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             candidates.sort_by(|a, b| a.cmp(b).reverse());
             candidates.dedup();
 
-            let param_type = match rcvr_ty.kind {
+            let param_type = match rcvr_ty.kind() {
                 ty::Param(param) => Some(param),
-                ty::Ref(_, ty, _) => match ty.kind {
+                ty::Ref(_, ty, _) => match ty.kind() {
                     ty::Param(param) => Some(param),
                     _ => None,
                 },
@@ -1243,7 +1243,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// autoderefs of `rcvr_ty`.
     fn type_derefs_to_local(&self, span: Span, rcvr_ty: Ty<'tcx>, source: SelfSource<'_>) -> bool {
         fn is_local(ty: Ty<'_>) -> bool {
-            match ty.kind {
+            match ty.kind() {
                 ty::Adt(def, _) => def.did.is_local(),
                 ty::Foreign(did) => did.is_local(),
 
diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs
index 031d48f8a60..97172d391ba 100644
--- a/compiler/rustc_typeck/src/check/mod.rs
+++ b/compiler/rustc_typeck/src/check/mod.rs
@@ -1,5 +1,3 @@
-// ignore-tidy-filelength
-
 /*!
 
 # typeck: check phase
@@ -68,13 +66,19 @@ pub mod _match;
 mod autoderef;
 mod callee;
 pub mod cast;
+mod check;
 mod closure;
 pub mod coercion;
 mod compare_method;
 pub mod demand;
+mod diverges;
 pub mod dropck;
+mod expectation;
 mod expr;
+mod fn_ctxt;
+mod gather_locals;
 mod generator_interior;
+mod inherited;
 pub mod intrinsic;
 pub mod method;
 mod op;
@@ -85,81 +89,53 @@ mod upvar;
 mod wfcheck;
 pub mod writeback;
 
-use crate::astconv::{
-    AstConv, ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, PathSeg,
+use check::{
+    check_abi, check_fn, check_impl_item_well_formed, check_item_well_formed, check_mod_item_types,
+    check_trait_item_well_formed,
 };
-use rustc_ast as ast;
-use rustc_ast::util::parser::ExprPrecedence;
-use rustc_attr as attr;
-use rustc_data_structures::captures::Captures;
+pub use check::{check_item_type, check_wf_new};
+pub use diverges::Diverges;
+pub use expectation::Expectation;
+pub use fn_ctxt::FnCtxt;
+pub use inherited::{Inherited, InheritedBuilder};
+
+use crate::astconv::AstConv;
+use crate::check::gather_locals::GatherLocalsVisitor;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_errors::ErrorReported;
-use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, DiagnosticId};
+use rustc_errors::{pluralize, struct_span_err, Applicability};
 use rustc_hir as hir;
-use rustc_hir::def::{CtorOf, DefKind, Res};
-use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LOCAL_CRATE};
-use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
+use rustc_hir::def::Res;
+use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE};
+use rustc_hir::intravisit::Visitor;
 use rustc_hir::itemlikevisit::ItemLikeVisitor;
-use rustc_hir::lang_items::LangItem;
-use rustc_hir::{ExprKind, GenericArg, HirIdMap, ItemKind, Node, PatKind, QPath};
+use rustc_hir::{HirIdMap, Node};
 use rustc_index::bit_set::BitSet;
 use rustc_index::vec::Idx;
-use rustc_infer::infer;
-use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse};
-use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282;
-use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
-use rustc_infer::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
-use rustc_infer::infer::{InferCtxt, InferOk, InferResult, RegionVariableOrigin, TyCtxtInferExt};
-use rustc_middle::hir::map::blocks::FnLikeNode;
-use rustc_middle::mir::interpret::ConstValue;
-use rustc_middle::ty::adjustment::{
-    Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
-};
 use rustc_middle::ty::fold::{TypeFoldable, TypeFolder};
 use rustc_middle::ty::query::Providers;
-use rustc_middle::ty::subst::{self, InternalSubsts, Subst, SubstsRef};
-use rustc_middle::ty::subst::{GenericArgKind, UserSelfTy, UserSubsts};
-use rustc_middle::ty::util::{Discr, IntTypeExt, Representability};
+use rustc_middle::ty::subst::GenericArgKind;
+use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef};
 use rustc_middle::ty::WithConstness;
-use rustc_middle::ty::{self, AdtKind, CanonicalUserType, Const, DefIdTree, GenericParamDefKind};
-use rustc_middle::ty::{RegionKind, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, UserType};
-use rustc_session::config::{self, EntryFnType};
-use rustc_session::lint;
+use rustc_middle::ty::{self, RegionKind, Ty, TyCtxt, UserType};
+use rustc_session::config;
 use rustc_session::parse::feature_err;
 use rustc_session::Session;
-use rustc_span::hygiene::DesugaringKind;
-use rustc_span::source_map::{original_sp, DUMMY_SP};
-use rustc_span::symbol::{kw, sym, Ident};
+use rustc_span::source_map::DUMMY_SP;
+use rustc_span::symbol::{kw, Ident};
 use rustc_span::{self, BytePos, MultiSpan, Span};
 use rustc_target::abi::VariantIdx;
 use rustc_target::spec::abi::Abi;
-use rustc_trait_selection::infer::InferCtxtExt as _;
-use rustc_trait_selection::opaque_types::{InferCtxtExt as _, OpaqueTypeDecl};
+use rustc_trait_selection::traits;
 use rustc_trait_selection::traits::error_reporting::recursive_type_with_infinite_size_error;
 use rustc_trait_selection::traits::error_reporting::suggestions::ReturnsVisitor;
-use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
-use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
-use rustc_trait_selection::traits::{
-    self, ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt,
-};
 
-use std::cell::{Cell, Ref, RefCell, RefMut};
-use std::cmp;
-use std::collections::hash_map::Entry;
-use std::iter;
-use std::mem::replace;
-use std::ops::{self, Deref};
-use std::slice;
+use std::cell::{Ref, RefCell, RefMut};
 
 use crate::require_c_abi_if_c_variadic;
 use crate::util::common::indenter;
 
-use self::callee::DeferredCallResolution;
-use self::coercion::{CoerceMany, DynamicCoerceMany};
-use self::compare_method::{compare_const_impl, compare_impl_method, compare_ty_impl};
-use self::method::{MethodCallee, SelfSource};
+use self::coercion::DynamicCoerceMany;
 pub use self::Expectation::*;
-use self::TupleArgumentsFlag::*;
 
 #[macro_export]
 macro_rules! type_error_struct {
@@ -179,201 +155,6 @@ pub struct LocalTy<'tcx> {
     revealed_ty: Ty<'tcx>,
 }
 
-/// A wrapper for `InferCtxt`'s `in_progress_typeck_results` field.
-#[derive(Copy, Clone)]
-struct MaybeInProgressTables<'a, 'tcx> {
-    maybe_typeck_results: Option<&'a RefCell<ty::TypeckResults<'tcx>>>,
-}
-
-impl<'a, 'tcx> MaybeInProgressTables<'a, 'tcx> {
-    fn borrow(self) -> Ref<'a, ty::TypeckResults<'tcx>> {
-        match self.maybe_typeck_results {
-            Some(typeck_results) => typeck_results.borrow(),
-            None => bug!(
-                "MaybeInProgressTables: inh/fcx.typeck_results.borrow() with no typeck results"
-            ),
-        }
-    }
-
-    fn borrow_mut(self) -> RefMut<'a, ty::TypeckResults<'tcx>> {
-        match self.maybe_typeck_results {
-            Some(typeck_results) => typeck_results.borrow_mut(),
-            None => bug!(
-                "MaybeInProgressTables: inh/fcx.typeck_results.borrow_mut() with no typeck results"
-            ),
-        }
-    }
-}
-
-/// Closures defined within the function. For example:
-///
-///     fn foo() {
-///         bar(move|| { ... })
-///     }
-///
-/// Here, the function `foo()` and the closure passed to
-/// `bar()` will each have their own `FnCtxt`, but they will
-/// share the inherited fields.
-pub struct Inherited<'a, 'tcx> {
-    infcx: InferCtxt<'a, 'tcx>,
-
-    typeck_results: MaybeInProgressTables<'a, 'tcx>,
-
-    locals: RefCell<HirIdMap<LocalTy<'tcx>>>,
-
-    fulfillment_cx: RefCell<Box<dyn TraitEngine<'tcx>>>,
-
-    // Some additional `Sized` obligations badly affect type inference.
-    // These obligations are added in a later stage of typeck.
-    deferred_sized_obligations: RefCell<Vec<(Ty<'tcx>, Span, traits::ObligationCauseCode<'tcx>)>>,
-
-    // When we process a call like `c()` where `c` is a closure type,
-    // we may not have decided yet whether `c` is a `Fn`, `FnMut`, or
-    // `FnOnce` closure. In that case, we defer full resolution of the
-    // call until upvar inference can kick in and make the
-    // decision. We keep these deferred resolutions grouped by the
-    // def-id of the closure, so that once we decide, we can easily go
-    // back and process them.
-    deferred_call_resolutions: RefCell<DefIdMap<Vec<DeferredCallResolution<'tcx>>>>,
-
-    deferred_cast_checks: RefCell<Vec<cast::CastCheck<'tcx>>>,
-
-    deferred_generator_interiors: RefCell<Vec<(hir::BodyId, Ty<'tcx>, hir::GeneratorKind)>>,
-
-    // Opaque types found in explicit return types and their
-    // associated fresh inference variable. Writeback resolves these
-    // variables to get the concrete type, which can be used to
-    // 'de-opaque' OpaqueTypeDecl, after typeck is done with all functions.
-    opaque_types: RefCell<DefIdMap<OpaqueTypeDecl<'tcx>>>,
-
-    /// A map from inference variables created from opaque
-    /// type instantiations (`ty::Infer`) to the actual opaque
-    /// type (`ty::Opaque`). Used during fallback to map unconstrained
-    /// opaque type inference variables to their corresponding
-    /// opaque type.
-    opaque_types_vars: RefCell<FxHashMap<Ty<'tcx>, Ty<'tcx>>>,
-
-    body_id: Option<hir::BodyId>,
-}
-
-impl<'a, 'tcx> Deref for Inherited<'a, 'tcx> {
-    type Target = InferCtxt<'a, 'tcx>;
-    fn deref(&self) -> &Self::Target {
-        &self.infcx
-    }
-}
-
-/// When type-checking an expression, we propagate downward
-/// whatever type hint we are able in the form of an `Expectation`.
-#[derive(Copy, Clone, Debug)]
-pub enum Expectation<'tcx> {
-    /// We know nothing about what type this expression should have.
-    NoExpectation,
-
-    /// This expression should have the type given (or some subtype).
-    ExpectHasType(Ty<'tcx>),
-
-    /// This expression will be cast to the `Ty`.
-    ExpectCastableToType(Ty<'tcx>),
-
-    /// This rvalue expression will be wrapped in `&` or `Box` and coerced
-    /// to `&Ty` or `Box<Ty>`, respectively. `Ty` is `[A]` or `Trait`.
-    ExpectRvalueLikeUnsized(Ty<'tcx>),
-}
-
-impl<'a, 'tcx> Expectation<'tcx> {
-    // Disregard "castable to" expectations because they
-    // can lead us astray. Consider for example `if cond
-    // {22} else {c} as u8` -- if we propagate the
-    // "castable to u8" constraint to 22, it will pick the
-    // type 22u8, which is overly constrained (c might not
-    // be a u8). In effect, the problem is that the
-    // "castable to" expectation is not the tightest thing
-    // we can say, so we want to drop it in this case.
-    // The tightest thing we can say is "must unify with
-    // else branch". Note that in the case of a "has type"
-    // constraint, this limitation does not hold.
-
-    // If the expected type is just a type variable, then don't use
-    // an expected type. Otherwise, we might write parts of the type
-    // when checking the 'then' block which are incompatible with the
-    // 'else' branch.
-    fn adjust_for_branches(&self, fcx: &FnCtxt<'a, 'tcx>) -> Expectation<'tcx> {
-        match *self {
-            ExpectHasType(ety) => {
-                let ety = fcx.shallow_resolve(ety);
-                if !ety.is_ty_var() { ExpectHasType(ety) } else { NoExpectation }
-            }
-            ExpectRvalueLikeUnsized(ety) => ExpectRvalueLikeUnsized(ety),
-            _ => NoExpectation,
-        }
-    }
-
-    /// Provides an expectation for an rvalue expression given an *optional*
-    /// hint, which is not required for type safety (the resulting type might
-    /// be checked higher up, as is the case with `&expr` and `box expr`), but
-    /// is useful in determining the concrete type.
-    ///
-    /// The primary use case is where the expected type is a fat pointer,
-    /// like `&[isize]`. For example, consider the following statement:
-    ///
-    ///    let x: &[isize] = &[1, 2, 3];
-    ///
-    /// In this case, the expected type for the `&[1, 2, 3]` expression is
-    /// `&[isize]`. If however we were to say that `[1, 2, 3]` has the
-    /// expectation `ExpectHasType([isize])`, that would be too strong --
-    /// `[1, 2, 3]` does not have the type `[isize]` but rather `[isize; 3]`.
-    /// It is only the `&[1, 2, 3]` expression as a whole that can be coerced
-    /// to the type `&[isize]`. Therefore, we propagate this more limited hint,
-    /// which still is useful, because it informs integer literals and the like.
-    /// See the test case `test/ui/coerce-expect-unsized.rs` and #20169
-    /// for examples of where this comes up,.
-    fn rvalue_hint(fcx: &FnCtxt<'a, 'tcx>, ty: Ty<'tcx>) -> Expectation<'tcx> {
-        match fcx.tcx.struct_tail_without_normalization(ty).kind {
-            ty::Slice(_) | ty::Str | ty::Dynamic(..) => ExpectRvalueLikeUnsized(ty),
-            _ => ExpectHasType(ty),
-        }
-    }
-
-    // Resolves `expected` by a single level if it is a variable. If
-    // there is no expected type or resolution is not possible (e.g.,
-    // no constraints yet present), just returns `None`.
-    fn resolve(self, fcx: &FnCtxt<'a, 'tcx>) -> Expectation<'tcx> {
-        match self {
-            NoExpectation => NoExpectation,
-            ExpectCastableToType(t) => ExpectCastableToType(fcx.resolve_vars_if_possible(&t)),
-            ExpectHasType(t) => ExpectHasType(fcx.resolve_vars_if_possible(&t)),
-            ExpectRvalueLikeUnsized(t) => ExpectRvalueLikeUnsized(fcx.resolve_vars_if_possible(&t)),
-        }
-    }
-
-    fn to_option(self, fcx: &FnCtxt<'a, 'tcx>) -> Option<Ty<'tcx>> {
-        match self.resolve(fcx) {
-            NoExpectation => None,
-            ExpectCastableToType(ty) | ExpectHasType(ty) | ExpectRvalueLikeUnsized(ty) => Some(ty),
-        }
-    }
-
-    /// It sometimes happens that we want to turn an expectation into
-    /// a **hard constraint** (i.e., something that must be satisfied
-    /// for the program to type-check). `only_has_type` will return
-    /// such a constraint, if it exists.
-    fn only_has_type(self, fcx: &FnCtxt<'a, 'tcx>) -> Option<Ty<'tcx>> {
-        match self.resolve(fcx) {
-            ExpectHasType(ty) => Some(ty),
-            NoExpectation | ExpectCastableToType(_) | ExpectRvalueLikeUnsized(_) => None,
-        }
-    }
-
-    /// Like `only_has_type`, but instead of returning `None` if no
-    /// hard constraint exists, creates a fresh type variable.
-    fn coercion_target_type(self, fcx: &FnCtxt<'a, 'tcx>, span: Span) -> Ty<'tcx> {
-        self.only_has_type(fcx).unwrap_or_else(|| {
-            fcx.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span })
-        })
-    }
-}
-
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
 pub enum Needs {
     MutPlace,
@@ -436,81 +217,6 @@ pub enum PlaceOp {
     Index,
 }
 
-/// Tracks whether executing a node may exit normally (versus
-/// return/break/panic, which "diverge", leaving dead code in their
-/// wake). Tracked semi-automatically (through type variables marked
-/// as diverging), with some manual adjustments for control-flow
-/// primitives (approximating a CFG).
-#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
-pub enum Diverges {
-    /// Potentially unknown, some cases converge,
-    /// others require a CFG to determine them.
-    Maybe,
-
-    /// Definitely known to diverge and therefore
-    /// not reach the next sibling or its parent.
-    Always {
-        /// The `Span` points to the expression
-        /// that caused us to diverge
-        /// (e.g. `return`, `break`, etc).
-        span: Span,
-        /// In some cases (e.g. a `match` expression
-        /// where all arms diverge), we may be
-        /// able to provide a more informative
-        /// message to the user.
-        /// If this is `None`, a default message
-        /// will be generated, which is suitable
-        /// for most cases.
-        custom_note: Option<&'static str>,
-    },
-
-    /// Same as `Always` but with a reachability
-    /// warning already emitted.
-    WarnedAlways,
-}
-
-// Convenience impls for combining `Diverges`.
-
-impl ops::BitAnd for Diverges {
-    type Output = Self;
-    fn bitand(self, other: Self) -> Self {
-        cmp::min(self, other)
-    }
-}
-
-impl ops::BitOr for Diverges {
-    type Output = Self;
-    fn bitor(self, other: Self) -> Self {
-        cmp::max(self, other)
-    }
-}
-
-impl ops::BitAndAssign for Diverges {
-    fn bitand_assign(&mut self, other: Self) {
-        *self = *self & other;
-    }
-}
-
-impl ops::BitOrAssign for Diverges {
-    fn bitor_assign(&mut self, other: Self) {
-        *self = *self | other;
-    }
-}
-
-impl Diverges {
-    /// Creates a `Diverges::Always` with the provided `span` and the default note message.
-    fn always(span: Span) -> Diverges {
-        Diverges::Always { span, custom_note: None }
-    }
-
-    fn is_always(self) -> bool {
-        // Enum comparison ignores the
-        // contents of fields, so we just
-        // fill them in with garbage here.
-        self >= Diverges::Always { span: DUMMY_SP, custom_note: None }
-    }
-}
-
 pub struct BreakableCtxt<'tcx> {
     may_break: bool,
 
@@ -539,221 +245,6 @@ impl<'tcx> EnclosingBreakables<'tcx> {
     }
 }
 
-pub struct FnCtxt<'a, 'tcx> {
-    body_id: hir::HirId,
-
-    /// The parameter environment used for proving trait obligations
-    /// in this function. This can change when we descend into
-    /// closures (as they bring new things into scope), hence it is
-    /// not part of `Inherited` (as of the time of this writing,
-    /// closures do not yet change the environment, but they will
-    /// eventually).
-    param_env: ty::ParamEnv<'tcx>,
-
-    /// Number of errors that had been reported when we started
-    /// checking this function. On exit, if we find that *more* errors
-    /// have been reported, we will skip regionck and other work that
-    /// expects the types within the function to be consistent.
-    // FIXME(matthewjasper) This should not exist, and it's not correct
-    // if type checking is run in parallel.
-    err_count_on_creation: usize,
-
-    /// If `Some`, this stores coercion information for returned
-    /// expressions. If `None`, this is in a context where return is
-    /// inappropriate, such as a const expression.
-    ///
-    /// This is a `RefCell<DynamicCoerceMany>`, which means that we
-    /// can track all the return expressions and then use them to
-    /// compute a useful coercion from the set, similar to a match
-    /// expression or other branching context. You can use methods
-    /// like `expected_ty` to access the declared return type (if
-    /// any).
-    ret_coercion: Option<RefCell<DynamicCoerceMany<'tcx>>>,
-
-    /// First span of a return site that we find. Used in error messages.
-    ret_coercion_span: RefCell<Option<Span>>,
-
-    resume_yield_tys: Option<(Ty<'tcx>, Ty<'tcx>)>,
-
-    ps: RefCell<UnsafetyState>,
-
-    /// Whether the last checked node generates a divergence (e.g.,
-    /// `return` will set this to `Always`). In general, when entering
-    /// an expression or other node in the tree, the initial value
-    /// indicates whether prior parts of the containing expression may
-    /// have diverged. It is then typically set to `Maybe` (and the
-    /// old value remembered) for processing the subparts of the
-    /// current expression. As each subpart is processed, they may set
-    /// the flag to `Always`, etc. Finally, at the end, we take the
-    /// result and "union" it with the original value, so that when we
-    /// return the flag indicates if any subpart of the parent
-    /// expression (up to and including this part) has diverged. So,
-    /// if you read it after evaluating a subexpression `X`, the value
-    /// you get indicates whether any subexpression that was
-    /// evaluating up to and including `X` diverged.
-    ///
-    /// We currently use this flag only for diagnostic purposes:
-    ///
-    /// - To warn about unreachable code: if, after processing a
-    ///   sub-expression but before we have applied the effects of the
-    ///   current node, we see that the flag is set to `Always`, we
-    ///   can issue a warning. This corresponds to something like
-    ///   `foo(return)`; we warn on the `foo()` expression. (We then
-    ///   update the flag to `WarnedAlways` to suppress duplicate
-    ///   reports.) Similarly, if we traverse to a fresh statement (or
-    ///   tail expression) from a `Always` setting, we will issue a
-    ///   warning. This corresponds to something like `{return;
-    ///   foo();}` or `{return; 22}`, where we would warn on the
-    ///   `foo()` or `22`.
-    ///
-    /// An expression represents dead code if, after checking it,
-    /// the diverges flag is set to something other than `Maybe`.
-    diverges: Cell<Diverges>,
-
-    /// Whether any child nodes have any type errors.
-    has_errors: Cell<bool>,
-
-    enclosing_breakables: RefCell<EnclosingBreakables<'tcx>>,
-
-    inh: &'a Inherited<'a, 'tcx>,
-}
-
-impl<'a, 'tcx> Deref for FnCtxt<'a, 'tcx> {
-    type Target = Inherited<'a, 'tcx>;
-    fn deref(&self) -> &Self::Target {
-        &self.inh
-    }
-}
-
-/// Helper type of a temporary returned by `Inherited::build(...)`.
-/// Necessary because we can't write the following bound:
-/// `F: for<'b, 'tcx> where 'tcx FnOnce(Inherited<'b, 'tcx>)`.
-pub struct InheritedBuilder<'tcx> {
-    infcx: infer::InferCtxtBuilder<'tcx>,
-    def_id: LocalDefId,
-}
-
-impl Inherited<'_, 'tcx> {
-    pub fn build(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> InheritedBuilder<'tcx> {
-        let hir_owner = tcx.hir().local_def_id_to_hir_id(def_id).owner;
-
-        InheritedBuilder {
-            infcx: tcx.infer_ctxt().with_fresh_in_progress_typeck_results(hir_owner),
-            def_id,
-        }
-    }
-}
-
-impl<'tcx> InheritedBuilder<'tcx> {
-    pub fn enter<F, R>(&mut self, f: F) -> R
-    where
-        F: for<'a> FnOnce(Inherited<'a, 'tcx>) -> R,
-    {
-        let def_id = self.def_id;
-        self.infcx.enter(|infcx| f(Inherited::new(infcx, def_id)))
-    }
-}
-
-impl Inherited<'a, 'tcx> {
-    fn new(infcx: InferCtxt<'a, 'tcx>, def_id: LocalDefId) -> Self {
-        let tcx = infcx.tcx;
-        let item_id = tcx.hir().local_def_id_to_hir_id(def_id);
-        let body_id = tcx.hir().maybe_body_owned_by(item_id);
-
-        Inherited {
-            typeck_results: MaybeInProgressTables {
-                maybe_typeck_results: infcx.in_progress_typeck_results,
-            },
-            infcx,
-            fulfillment_cx: RefCell::new(TraitEngine::new(tcx)),
-            locals: RefCell::new(Default::default()),
-            deferred_sized_obligations: RefCell::new(Vec::new()),
-            deferred_call_resolutions: RefCell::new(Default::default()),
-            deferred_cast_checks: RefCell::new(Vec::new()),
-            deferred_generator_interiors: RefCell::new(Vec::new()),
-            opaque_types: RefCell::new(Default::default()),
-            opaque_types_vars: RefCell::new(Default::default()),
-            body_id,
-        }
-    }
-
-    fn register_predicate(&self, obligation: traits::PredicateObligation<'tcx>) {
-        debug!("register_predicate({:?})", obligation);
-        if obligation.has_escaping_bound_vars() {
-            span_bug!(obligation.cause.span, "escaping bound vars in predicate {:?}", obligation);
-        }
-        self.fulfillment_cx.borrow_mut().register_predicate_obligation(self, obligation);
-    }
-
-    fn register_predicates<I>(&self, obligations: I)
-    where
-        I: IntoIterator<Item = traits::PredicateObligation<'tcx>>,
-    {
-        for obligation in obligations {
-            self.register_predicate(obligation);
-        }
-    }
-
-    fn register_infer_ok_obligations<T>(&self, infer_ok: InferOk<'tcx, T>) -> T {
-        self.register_predicates(infer_ok.obligations);
-        infer_ok.value
-    }
-
-    fn normalize_associated_types_in<T>(
-        &self,
-        span: Span,
-        body_id: hir::HirId,
-        param_env: ty::ParamEnv<'tcx>,
-        value: &T,
-    ) -> T
-    where
-        T: TypeFoldable<'tcx>,
-    {
-        let ok = self.partially_normalize_associated_types_in(span, body_id, param_env, value);
-        self.register_infer_ok_obligations(ok)
-    }
-}
-
-struct CheckItemTypesVisitor<'tcx> {
-    tcx: TyCtxt<'tcx>,
-}
-
-impl ItemLikeVisitor<'tcx> for CheckItemTypesVisitor<'tcx> {
-    fn visit_item(&mut self, i: &'tcx hir::Item<'tcx>) {
-        check_item_type(self.tcx, i);
-    }
-    fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem<'tcx>) {}
-    fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem<'tcx>) {}
-}
-
-pub fn check_wf_new(tcx: TyCtxt<'_>) {
-    let visit = wfcheck::CheckTypeWellFormedVisitor::new(tcx);
-    tcx.hir().krate().par_visit_all_item_likes(&visit);
-}
-
-fn check_mod_item_types(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
-    tcx.hir().visit_item_likes_in_module(module_def_id, &mut CheckItemTypesVisitor { tcx });
-}
-
-fn typeck_item_bodies(tcx: TyCtxt<'_>, crate_num: CrateNum) {
-    debug_assert!(crate_num == LOCAL_CRATE);
-    tcx.par_body_owners(|body_owner_def_id| {
-        tcx.ensure().typeck(body_owner_def_id);
-    });
-}
-
-fn check_item_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) {
-    wfcheck::check_item_well_formed(tcx, def_id);
-}
-
-fn check_trait_item_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) {
-    wfcheck::check_trait_item(tcx, def_id);
-}
-
-fn check_impl_item_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) {
-    wfcheck::check_impl_item(tcx, def_id);
-}
-
 pub fn provide(providers: &mut Providers) {
     method::provide(providers);
     *providers = Providers {
@@ -884,7 +375,7 @@ where
         }
 
         fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
-            match ty.kind {
+            match *ty.kind() {
                 ty::Opaque(def_id, substs) => {
                     debug!("fixup_opaque_types: found type {:?}", ty);
                     // Here, we replace any inference variables that occur within
@@ -900,7 +391,7 @@ where
                             let old_param = substs[param.index as usize];
                             match old_param.unpack() {
                                 GenericArgKind::Type(old_ty) => {
-                                    if let ty::Infer(_) = old_ty.kind {
+                                    if let ty::Infer(_) = old_ty.kind() {
                                         // Replace inference type with a generic parameter
                                         self.tcx.mk_param_from_def(param)
                                     } else {
@@ -1048,7 +539,7 @@ fn typeck_with_fallback<'tcx>(
             };
 
             // Gather locals in statics (because of block expressions).
-            GatherLocalsVisitor { fcx: &fcx, parent_id: id }.visit_body(body);
+            GatherLocalsVisitor::new(&fcx, id).visit_body(body);
 
             fcx.check_expr_coercable_to_type(&body.value, revealed_ty, None);
 
@@ -1135,127 +626,6 @@ fn typeck_with_fallback<'tcx>(
     typeck_results
 }
 
-fn check_abi(tcx: TyCtxt<'_>, span: Span, abi: Abi) {
-    if !tcx.sess.target.target.is_abi_supported(abi) {
-        struct_span_err!(
-            tcx.sess,
-            span,
-            E0570,
-            "The ABI `{}` is not supported for the current target",
-            abi
-        )
-        .emit()
-    }
-}
-
-struct GatherLocalsVisitor<'a, 'tcx> {
-    fcx: &'a FnCtxt<'a, 'tcx>,
-    parent_id: hir::HirId,
-}
-
-impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
-    fn assign(&mut self, span: Span, nid: hir::HirId, ty_opt: Option<LocalTy<'tcx>>) -> Ty<'tcx> {
-        match ty_opt {
-            None => {
-                // Infer the variable's type.
-                let var_ty = self.fcx.next_ty_var(TypeVariableOrigin {
-                    kind: TypeVariableOriginKind::TypeInference,
-                    span,
-                });
-                self.fcx
-                    .locals
-                    .borrow_mut()
-                    .insert(nid, LocalTy { decl_ty: var_ty, revealed_ty: var_ty });
-                var_ty
-            }
-            Some(typ) => {
-                // Take type that the user specified.
-                self.fcx.locals.borrow_mut().insert(nid, typ);
-                typ.revealed_ty
-            }
-        }
-    }
-}
-
-impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
-    type Map = intravisit::ErasedMap<'tcx>;
-
-    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
-        NestedVisitorMap::None
-    }
-
-    // Add explicitly-declared locals.
-    fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) {
-        let local_ty = match local.ty {
-            Some(ref ty) => {
-                let o_ty = self.fcx.to_ty(&ty);
-
-                let revealed_ty = if self.fcx.tcx.features().impl_trait_in_bindings {
-                    self.fcx.instantiate_opaque_types_from_value(self.parent_id, &o_ty, ty.span)
-                } else {
-                    o_ty
-                };
-
-                let c_ty = self
-                    .fcx
-                    .inh
-                    .infcx
-                    .canonicalize_user_type_annotation(&UserType::Ty(revealed_ty));
-                debug!(
-                    "visit_local: ty.hir_id={:?} o_ty={:?} revealed_ty={:?} c_ty={:?}",
-                    ty.hir_id, o_ty, revealed_ty, c_ty
-                );
-                self.fcx
-                    .typeck_results
-                    .borrow_mut()
-                    .user_provided_types_mut()
-                    .insert(ty.hir_id, c_ty);
-
-                Some(LocalTy { decl_ty: o_ty, revealed_ty })
-            }
-            None => None,
-        };
-        self.assign(local.span, local.hir_id, local_ty);
-
-        debug!(
-            "local variable {:?} is assigned type {}",
-            local.pat,
-            self.fcx.ty_to_string(&*self.fcx.locals.borrow().get(&local.hir_id).unwrap().decl_ty)
-        );
-        intravisit::walk_local(self, local);
-    }
-
-    // Add pattern bindings.
-    fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) {
-        if let PatKind::Binding(_, _, ident, _) = p.kind {
-            let var_ty = self.assign(p.span, p.hir_id, None);
-
-            if !self.fcx.tcx.features().unsized_locals {
-                self.fcx.require_type_is_sized(var_ty, p.span, traits::VariableType(p.hir_id));
-            }
-
-            debug!(
-                "pattern binding {} is assigned to {} with type {:?}",
-                ident,
-                self.fcx.ty_to_string(&*self.fcx.locals.borrow().get(&p.hir_id).unwrap().decl_ty),
-                var_ty
-            );
-        }
-        intravisit::walk_pat(self, p);
-    }
-
-    // Don't descend into the bodies of nested closures.
-    fn visit_fn(
-        &mut self,
-        _: intravisit::FnKind<'tcx>,
-        _: &'tcx hir::FnDecl<'tcx>,
-        _: hir::BodyId,
-        _: Span,
-        _: hir::HirId,
-    ) {
-    }
-}
-
 /// When `check_fn` is invoked on a generator (i.e., a body that
 /// includes yield), it returns back some information about the yield
 /// points.
@@ -1273,453 +643,6 @@ struct GeneratorTypes<'tcx> {
     movability: hir::Movability,
 }
 
-/// Helper used for fns and closures. Does the grungy work of checking a function
-/// body and returns the function context used for that purpose, since in the case of a fn item
-/// there is still a bit more to do.
-///
-/// * ...
-/// * inherited: other fields inherited from the enclosing fn (if any)
-fn check_fn<'a, 'tcx>(
-    inherited: &'a Inherited<'a, 'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
-    fn_sig: ty::FnSig<'tcx>,
-    decl: &'tcx hir::FnDecl<'tcx>,
-    fn_id: hir::HirId,
-    body: &'tcx hir::Body<'tcx>,
-    can_be_generator: Option<hir::Movability>,
-) -> (FnCtxt<'a, 'tcx>, Option<GeneratorTypes<'tcx>>) {
-    let mut fn_sig = fn_sig;
-
-    debug!("check_fn(sig={:?}, fn_id={}, param_env={:?})", fn_sig, fn_id, param_env);
-
-    // Create the function context. This is either derived from scratch or,
-    // in the case of closures, based on the outer context.
-    let mut fcx = FnCtxt::new(inherited, param_env, body.value.hir_id);
-    *fcx.ps.borrow_mut() = UnsafetyState::function(fn_sig.unsafety, fn_id);
-
-    let tcx = fcx.tcx;
-    let sess = tcx.sess;
-    let hir = tcx.hir();
-
-    let declared_ret_ty = fn_sig.output();
-    let revealed_ret_ty =
-        fcx.instantiate_opaque_types_from_value(fn_id, &declared_ret_ty, decl.output.span());
-    debug!("check_fn: declared_ret_ty: {}, revealed_ret_ty: {}", declared_ret_ty, revealed_ret_ty);
-    fcx.ret_coercion = Some(RefCell::new(CoerceMany::new(revealed_ret_ty)));
-    fn_sig = tcx.mk_fn_sig(
-        fn_sig.inputs().iter().cloned(),
-        revealed_ret_ty,
-        fn_sig.c_variadic,
-        fn_sig.unsafety,
-        fn_sig.abi,
-    );
-
-    let span = body.value.span;
-
-    fn_maybe_err(tcx, span, fn_sig.abi);
-
-    if body.generator_kind.is_some() && can_be_generator.is_some() {
-        let yield_ty = fcx
-            .next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span });
-        fcx.require_type_is_sized(yield_ty, span, traits::SizedYieldType);
-
-        // 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());
-
-        fcx.resume_yield_tys = Some((resume_ty, yield_ty));
-    }
-
-    let outer_def_id = tcx.closure_base_def_id(hir.local_def_id(fn_id).to_def_id()).expect_local();
-    let outer_hir_id = hir.local_def_id_to_hir_id(outer_def_id);
-    GatherLocalsVisitor { fcx: &fcx, parent_id: outer_hir_id }.visit_body(body);
-
-    // C-variadic fns also have a `VaList` input that's not listed in `fn_sig`
-    // (as it's created inside the body itself, not passed in from outside).
-    let maybe_va_list = if fn_sig.c_variadic {
-        let span = body.params.last().unwrap().span;
-        let va_list_did = tcx.require_lang_item(LangItem::VaList, Some(span));
-        let region = fcx.next_region_var(RegionVariableOrigin::MiscVariable(span));
-
-        Some(tcx.type_of(va_list_did).subst(tcx, &[region.into()]))
-    } else {
-        None
-    };
-
-    // Add formal parameters.
-    let inputs_hir = hir.fn_decl_by_hir_id(fn_id).map(|decl| &decl.inputs);
-    let inputs_fn = fn_sig.inputs().iter().copied();
-    for (idx, (param_ty, param)) in inputs_fn.chain(maybe_va_list).zip(body.params).enumerate() {
-        // Check the pattern.
-        let ty_span = try { inputs_hir?.get(idx)?.span };
-        fcx.check_pat_top(&param.pat, param_ty, ty_span, false);
-
-        // Check that argument is Sized.
-        // The check for a non-trivial pattern is a hack to avoid duplicate warnings
-        // for simple cases like `fn foo(x: Trait)`,
-        // where we would error once on the parameter as a whole, and once on the binding `x`.
-        if param.pat.simple_ident().is_none() && !tcx.features().unsized_locals {
-            fcx.require_type_is_sized(param_ty, param.pat.span, traits::SizedArgumentType(ty_span));
-        }
-
-        fcx.write_ty(param.hir_id, param_ty);
-    }
-
-    inherited.typeck_results.borrow_mut().liberated_fn_sigs_mut().insert(fn_id, fn_sig);
-
-    if let ty::Dynamic(..) = declared_ret_ty.kind {
-        // FIXME: We need to verify that the return type is `Sized` after the return expression has
-        // been evaluated so that we have types available for all the nodes being returned, but that
-        // requires the coerced evaluated type to be stored. Moving `check_return_expr` before this
-        // causes unsized errors caused by the `declared_ret_ty` to point at the return expression,
-        // while keeping the current ordering we will ignore the tail expression's type because we
-        // don't know it yet. We can't do `check_expr_kind` while keeping `check_return_expr`
-        // because we will trigger "unreachable expression" lints unconditionally.
-        // Because of all of this, we perform a crude check to know whether the simplest `!Sized`
-        // case that a newcomer might make, returning a bare trait, and in that case we populate
-        // the tail expression's type so that the suggestion will be correct, but ignore all other
-        // possible cases.
-        fcx.check_expr(&body.value);
-        fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType);
-        tcx.sess.delay_span_bug(decl.output.span(), "`!Sized` return type");
-    } else {
-        fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType);
-        fcx.check_return_expr(&body.value);
-    }
-
-    // We insert the deferred_generator_interiors entry after visiting the body.
-    // This ensures that all nested generators appear before the entry of this generator.
-    // resolve_generator_interiors relies on this property.
-    let gen_ty = if let (Some(_), Some(gen_kind)) = (can_be_generator, body.generator_kind) {
-        let interior = fcx
-            .next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span });
-        fcx.deferred_generator_interiors.borrow_mut().push((body.id(), interior, gen_kind));
-
-        let (resume_ty, yield_ty) = fcx.resume_yield_tys.unwrap();
-        Some(GeneratorTypes {
-            resume_ty,
-            yield_ty,
-            interior,
-            movability: can_be_generator.unwrap(),
-        })
-    } else {
-        None
-    };
-
-    // Finalize the return check by taking the LUB of the return types
-    // we saw and assigning it to the expected return type. This isn't
-    // really expected to fail, since the coercions would have failed
-    // earlier when trying to find a LUB.
-    //
-    // However, the behavior around `!` is sort of complex. In the
-    // event that the `actual_return_ty` comes back as `!`, that
-    // indicates that the fn either does not return or "returns" only
-    // values of type `!`. In this case, if there is an expected
-    // return type that is *not* `!`, that should be ok. But if the
-    // return type is being inferred, we want to "fallback" to `!`:
-    //
-    //     let x = move || panic!();
-    //
-    // To allow for that, I am creating a type variable with diverging
-    // fallback. This was deemed ever so slightly better than unifying
-    // the return value with `!` because it allows for the caller to
-    // make more assumptions about the return type (e.g., they could do
-    //
-    //     let y: Option<u32> = Some(x());
-    //
-    // which would then cause this return type to become `u32`, not
-    // `!`).
-    let coercion = fcx.ret_coercion.take().unwrap().into_inner();
-    let mut actual_return_ty = coercion.complete(&fcx);
-    if actual_return_ty.is_never() {
-        actual_return_ty = fcx.next_diverging_ty_var(TypeVariableOrigin {
-            kind: TypeVariableOriginKind::DivergingFn,
-            span,
-        });
-    }
-    fcx.demand_suptype(span, revealed_ret_ty, actual_return_ty);
-
-    // Check that the main return type implements the termination trait.
-    if let Some(term_id) = tcx.lang_items().termination() {
-        if let Some((def_id, EntryFnType::Main)) = tcx.entry_fn(LOCAL_CRATE) {
-            let main_id = hir.local_def_id_to_hir_id(def_id);
-            if main_id == fn_id {
-                let substs = tcx.mk_substs_trait(declared_ret_ty, &[]);
-                let trait_ref = ty::TraitRef::new(term_id, substs);
-                let return_ty_span = decl.output.span();
-                let cause = traits::ObligationCause::new(
-                    return_ty_span,
-                    fn_id,
-                    ObligationCauseCode::MainFunctionType,
-                );
-
-                inherited.register_predicate(traits::Obligation::new(
-                    cause,
-                    param_env,
-                    trait_ref.without_const().to_predicate(tcx),
-                ));
-            }
-        }
-    }
-
-    // Check that a function marked as `#[panic_handler]` has signature `fn(&PanicInfo) -> !`
-    if let Some(panic_impl_did) = tcx.lang_items().panic_impl() {
-        if panic_impl_did == hir.local_def_id(fn_id).to_def_id() {
-            if let Some(panic_info_did) = tcx.lang_items().panic_info() {
-                if declared_ret_ty.kind != ty::Never {
-                    sess.span_err(decl.output.span(), "return type should be `!`");
-                }
-
-                let inputs = fn_sig.inputs();
-                let span = hir.span(fn_id);
-                if inputs.len() == 1 {
-                    let arg_is_panic_info = match inputs[0].kind {
-                        ty::Ref(region, ty, mutbl) => match ty.kind {
-                            ty::Adt(ref adt, _) => {
-                                adt.did == panic_info_did
-                                    && mutbl == hir::Mutability::Not
-                                    && *region != RegionKind::ReStatic
-                            }
-                            _ => false,
-                        },
-                        _ => false,
-                    };
-
-                    if !arg_is_panic_info {
-                        sess.span_err(decl.inputs[0].span, "argument should be `&PanicInfo`");
-                    }
-
-                    if let Node::Item(item) = hir.get(fn_id) {
-                        if let ItemKind::Fn(_, ref generics, _) = item.kind {
-                            if !generics.params.is_empty() {
-                                sess.span_err(span, "should have no type parameters");
-                            }
-                        }
-                    }
-                } else {
-                    let span = sess.source_map().guess_head_span(span);
-                    sess.span_err(span, "function should have one argument");
-                }
-            } else {
-                sess.err("language item required, but not found: `panic_info`");
-            }
-        }
-    }
-
-    // Check that a function marked as `#[alloc_error_handler]` has signature `fn(Layout) -> !`
-    if let Some(alloc_error_handler_did) = tcx.lang_items().oom() {
-        if alloc_error_handler_did == hir.local_def_id(fn_id).to_def_id() {
-            if let Some(alloc_layout_did) = tcx.lang_items().alloc_layout() {
-                if declared_ret_ty.kind != ty::Never {
-                    sess.span_err(decl.output.span(), "return type should be `!`");
-                }
-
-                let inputs = fn_sig.inputs();
-                let span = hir.span(fn_id);
-                if inputs.len() == 1 {
-                    let arg_is_alloc_layout = match inputs[0].kind {
-                        ty::Adt(ref adt, _) => adt.did == alloc_layout_did,
-                        _ => false,
-                    };
-
-                    if !arg_is_alloc_layout {
-                        sess.span_err(decl.inputs[0].span, "argument should be `Layout`");
-                    }
-
-                    if let Node::Item(item) = hir.get(fn_id) {
-                        if let ItemKind::Fn(_, ref generics, _) = item.kind {
-                            if !generics.params.is_empty() {
-                                sess.span_err(
-                                    span,
-                                    "`#[alloc_error_handler]` function should have no type \
-                                     parameters",
-                                );
-                            }
-                        }
-                    }
-                } else {
-                    let span = sess.source_map().guess_head_span(span);
-                    sess.span_err(span, "function should have one argument");
-                }
-            } else {
-                sess.err("language item required, but not found: `alloc_layout`");
-            }
-        }
-    }
-
-    (fcx, gen_ty)
-}
-
-fn check_struct(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) {
-    let def_id = tcx.hir().local_def_id(id);
-    let def = tcx.adt_def(def_id);
-    def.destructor(tcx); // force the destructor to be evaluated
-    check_representable(tcx, span, def_id);
-
-    if def.repr.simd() {
-        check_simd(tcx, span, def_id);
-    }
-
-    check_transparent(tcx, span, def);
-    check_packed(tcx, span, def);
-}
-
-fn check_union(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) {
-    let def_id = tcx.hir().local_def_id(id);
-    let def = tcx.adt_def(def_id);
-    def.destructor(tcx); // force the destructor to be evaluated
-    check_representable(tcx, span, def_id);
-    check_transparent(tcx, span, def);
-    check_union_fields(tcx, span, def_id);
-    check_packed(tcx, span, def);
-}
-
-/// When the `#![feature(untagged_unions)]` gate is active,
-/// check that the fields of the `union` does not contain fields that need dropping.
-fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> bool {
-    let item_type = tcx.type_of(item_def_id);
-    if let ty::Adt(def, substs) = item_type.kind {
-        assert!(def.is_union());
-        let fields = &def.non_enum_variant().fields;
-        let param_env = tcx.param_env(item_def_id);
-        for field in fields {
-            let field_ty = field.ty(tcx, substs);
-            // We are currently checking the type this field came from, so it must be local.
-            let field_span = tcx.hir().span_if_local(field.did).unwrap();
-            if field_ty.needs_drop(tcx, param_env) {
-                struct_span_err!(
-                    tcx.sess,
-                    field_span,
-                    E0740,
-                    "unions may not contain fields that need dropping"
-                )
-                .span_note(field_span, "`std::mem::ManuallyDrop` can be used to wrap the type")
-                .emit();
-                return false;
-            }
-        }
-    } else {
-        span_bug!(span, "unions must be ty::Adt, but got {:?}", item_type.kind);
-    }
-    true
-}
-
-/// Checks that an opaque type does not contain cycles and does not use `Self` or `T::Foo`
-/// projections that would result in "inheriting lifetimes".
-fn check_opaque<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    def_id: LocalDefId,
-    substs: SubstsRef<'tcx>,
-    span: Span,
-    origin: &hir::OpaqueTyOrigin,
-) {
-    check_opaque_for_inheriting_lifetimes(tcx, def_id, span);
-    check_opaque_for_cycles(tcx, def_id, substs, span, origin);
-}
-
-/// Checks that an opaque type does not use `Self` or `T::Foo` projections that would result
-/// in "inheriting lifetimes".
-fn check_opaque_for_inheriting_lifetimes(tcx: TyCtxt<'tcx>, def_id: LocalDefId, span: Span) {
-    let item = tcx.hir().expect_item(tcx.hir().local_def_id_to_hir_id(def_id));
-    debug!(
-        "check_opaque_for_inheriting_lifetimes: def_id={:?} span={:?} item={:?}",
-        def_id, span, item
-    );
-
-    #[derive(Debug)]
-    struct ProhibitOpaqueVisitor<'tcx> {
-        opaque_identity_ty: Ty<'tcx>,
-        generics: &'tcx ty::Generics,
-        ty: Option<Ty<'tcx>>,
-    };
-
-    impl<'tcx> ty::fold::TypeVisitor<'tcx> for ProhibitOpaqueVisitor<'tcx> {
-        fn visit_ty(&mut self, t: Ty<'tcx>) -> bool {
-            debug!("check_opaque_for_inheriting_lifetimes: (visit_ty) t={:?}", t);
-            if t != self.opaque_identity_ty && t.super_visit_with(self) {
-                self.ty = Some(t);
-                return true;
-            }
-            false
-        }
-
-        fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool {
-            debug!("check_opaque_for_inheriting_lifetimes: (visit_region) r={:?}", r);
-            if let RegionKind::ReEarlyBound(ty::EarlyBoundRegion { index, .. }) = r {
-                return *index < self.generics.parent_count as u32;
-            }
-
-            r.super_visit_with(self)
-        }
-
-        fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> bool {
-            if let ty::ConstKind::Unevaluated(..) = c.val {
-                // FIXME(#72219) We currenctly don't detect lifetimes within substs
-                // which would violate this check. Even though the particular substitution is not used
-                // within the const, this should still be fixed.
-                return false;
-            }
-            c.super_visit_with(self)
-        }
-    }
-
-    if let ItemKind::OpaqueTy(hir::OpaqueTy {
-        origin: hir::OpaqueTyOrigin::AsyncFn | hir::OpaqueTyOrigin::FnReturn,
-        ..
-    }) = item.kind
-    {
-        let mut visitor = ProhibitOpaqueVisitor {
-            opaque_identity_ty: tcx.mk_opaque(
-                def_id.to_def_id(),
-                InternalSubsts::identity_for_item(tcx, def_id.to_def_id()),
-            ),
-            generics: tcx.generics_of(def_id),
-            ty: None,
-        };
-        let prohibit_opaque = tcx
-            .predicates_of(def_id)
-            .predicates
-            .iter()
-            .any(|(predicate, _)| predicate.visit_with(&mut visitor));
-        debug!(
-            "check_opaque_for_inheriting_lifetimes: prohibit_opaque={:?}, visitor={:?}",
-            prohibit_opaque, visitor
-        );
-
-        if prohibit_opaque {
-            let is_async = match item.kind {
-                ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => match origin {
-                    hir::OpaqueTyOrigin::AsyncFn => true,
-                    _ => false,
-                },
-                _ => unreachable!(),
-            };
-
-            let mut err = struct_span_err!(
-                tcx.sess,
-                span,
-                E0760,
-                "`{}` return type cannot contain a projection or `Self` that references lifetimes from \
-             a parent scope",
-                if is_async { "async fn" } else { "impl Trait" },
-            );
-
-            if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(span) {
-                if snippet == "Self" {
-                    if let Some(ty) = visitor.ty {
-                        err.span_suggestion(
-                            span,
-                            "consider spelling out the type instead",
-                            format!("{:?}", ty),
-                            Applicability::MaybeIncorrect,
-                        );
-                    }
-                }
-            }
-            err.emit();
-        }
-    }
-}
-
 /// Given a `DefId` for an opaque type in return position, find its parent item's return
 /// expressions.
 fn get_owner_return_paths(
@@ -1740,86 +663,6 @@ fn get_owner_return_paths(
         })
 }
 
-/// Emit an error for recursive opaque types.
-///
-/// If this is a return `impl Trait`, find the item's return expressions and point at them. For
-/// direct recursion this is enough, but for indirect recursion also point at the last intermediary
-/// `impl Trait`.
-///
-/// If all the return expressions evaluate to `!`, then we explain that the error will go away
-/// after changing it. This can happen when a user uses `panic!()` or similar as a placeholder.
-fn opaque_type_cycle_error(tcx: TyCtxt<'tcx>, def_id: LocalDefId, span: Span) {
-    let mut err = struct_span_err!(tcx.sess, span, E0720, "cannot resolve opaque type");
-
-    let mut label = false;
-    if let Some((hir_id, visitor)) = get_owner_return_paths(tcx, def_id) {
-        let typeck_results = tcx.typeck(tcx.hir().local_def_id(hir_id));
-        if visitor
-            .returns
-            .iter()
-            .filter_map(|expr| typeck_results.node_type_opt(expr.hir_id))
-            .all(|ty| matches!(ty.kind, ty::Never))
-        {
-            let spans = visitor
-                .returns
-                .iter()
-                .filter(|expr| typeck_results.node_type_opt(expr.hir_id).is_some())
-                .map(|expr| expr.span)
-                .collect::<Vec<Span>>();
-            let span_len = spans.len();
-            if span_len == 1 {
-                err.span_label(spans[0], "this returned value is of `!` type");
-            } else {
-                let mut multispan: MultiSpan = spans.clone().into();
-                for span in spans {
-                    multispan
-                        .push_span_label(span, "this returned value is of `!` type".to_string());
-                }
-                err.span_note(multispan, "these returned values have a concrete \"never\" type");
-            }
-            err.help("this error will resolve once the item's body returns a concrete type");
-        } else {
-            let mut seen = FxHashSet::default();
-            seen.insert(span);
-            err.span_label(span, "recursive opaque type");
-            label = true;
-            for (sp, ty) in visitor
-                .returns
-                .iter()
-                .filter_map(|e| typeck_results.node_type_opt(e.hir_id).map(|t| (e.span, t)))
-                .filter(|(_, ty)| !matches!(ty.kind, ty::Never))
-            {
-                struct VisitTypes(Vec<DefId>);
-                impl<'tcx> ty::fold::TypeVisitor<'tcx> for VisitTypes {
-                    fn visit_ty(&mut self, t: Ty<'tcx>) -> bool {
-                        match t.kind {
-                            ty::Opaque(def, _) => {
-                                self.0.push(def);
-                                false
-                            }
-                            _ => t.super_visit_with(self),
-                        }
-                    }
-                }
-                let mut visitor = VisitTypes(vec![]);
-                ty.visit_with(&mut visitor);
-                for def_id in visitor.0 {
-                    let ty_span = tcx.def_span(def_id);
-                    if !seen.contains(&ty_span) {
-                        err.span_label(ty_span, &format!("returning this opaque type `{}`", ty));
-                        seen.insert(ty_span);
-                    }
-                    err.span_label(sp, &format!("returning here with type `{}`", ty));
-                }
-            }
-        }
-    }
-    if !label {
-        err.span_label(span, "cannot resolve opaque type");
-    }
-    err.emit();
-}
-
 /// Emit an error for recursive opaque types in a `let` binding.
 fn binding_opaque_type_cycle_error(
     tcx: TyCtxt<'tcx>,
@@ -1829,7 +672,7 @@ fn binding_opaque_type_cycle_error(
 ) {
     let mut err = struct_span_err!(tcx.sess, span, E0720, "cannot resolve opaque type");
     err.span_label(span, "cannot resolve opaque type");
-    // Find the the owner that declared this `impl Trait` type.
+    // Find the owner that declared this `impl Trait` type.
     let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
     let mut prev_hir_id = hir_id;
     let mut hir_id = tcx.hir().get_parent_node(hir_id);
@@ -1880,33 +723,6 @@ fn binding_opaque_type_cycle_error(
     err.emit();
 }
 
-fn async_opaque_type_cycle_error(tcx: TyCtxt<'tcx>, span: Span) {
-    struct_span_err!(tcx.sess, span, E0733, "recursion in an `async fn` requires boxing")
-        .span_label(span, "recursive `async fn`")
-        .note("a recursive `async fn` must be rewritten to return a boxed `dyn Future`")
-        .emit();
-}
-
-/// Checks that an opaque type does not contain cycles.
-fn check_opaque_for_cycles<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    def_id: LocalDefId,
-    substs: SubstsRef<'tcx>,
-    span: Span,
-    origin: &hir::OpaqueTyOrigin,
-) {
-    if let Err(partially_expanded_type) = tcx.try_expand_impl_trait_type(def_id.to_def_id(), substs)
-    {
-        match origin {
-            hir::OpaqueTyOrigin::AsyncFn => async_opaque_type_cycle_error(tcx, span),
-            hir::OpaqueTyOrigin::Binding => {
-                binding_opaque_type_cycle_error(tcx, def_id, span, partially_expanded_type)
-            }
-            _ => opaque_type_cycle_error(tcx, def_id, span),
-        }
-    }
-}
-
 // Forbid defining intrinsics in Rust code,
 // as they must always be defined by the compiler.
 fn fn_maybe_err(tcx: TyCtxt<'_>, sp: Span, abi: Abi) {
@@ -1915,126 +731,6 @@ fn fn_maybe_err(tcx: TyCtxt<'_>, sp: Span, abi: Abi) {
     }
 }
 
-pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item<'tcx>) {
-    debug!(
-        "check_item_type(it.hir_id={}, it.name={})",
-        it.hir_id,
-        tcx.def_path_str(tcx.hir().local_def_id(it.hir_id).to_def_id())
-    );
-    let _indenter = indenter();
-    match it.kind {
-        // Consts can play a role in type-checking, so they are included here.
-        hir::ItemKind::Static(..) => {
-            let def_id = tcx.hir().local_def_id(it.hir_id);
-            tcx.ensure().typeck(def_id);
-            maybe_check_static_with_link_section(tcx, def_id, it.span);
-        }
-        hir::ItemKind::Const(..) => {
-            tcx.ensure().typeck(tcx.hir().local_def_id(it.hir_id));
-        }
-        hir::ItemKind::Enum(ref enum_definition, _) => {
-            check_enum(tcx, it.span, &enum_definition.variants, it.hir_id);
-        }
-        hir::ItemKind::Fn(..) => {} // entirely within check_item_body
-        hir::ItemKind::Impl { ref items, .. } => {
-            debug!("ItemKind::Impl {} with id {}", it.ident, it.hir_id);
-            let impl_def_id = tcx.hir().local_def_id(it.hir_id);
-            if let Some(impl_trait_ref) = tcx.impl_trait_ref(impl_def_id) {
-                check_impl_items_against_trait(tcx, it.span, impl_def_id, impl_trait_ref, items);
-                let trait_def_id = impl_trait_ref.def_id;
-                check_on_unimplemented(tcx, trait_def_id, it);
-            }
-        }
-        hir::ItemKind::Trait(_, _, _, _, ref items) => {
-            let def_id = tcx.hir().local_def_id(it.hir_id);
-            check_on_unimplemented(tcx, def_id.to_def_id(), it);
-
-            for item in items.iter() {
-                let item = tcx.hir().trait_item(item.id);
-                if let hir::TraitItemKind::Fn(sig, _) = &item.kind {
-                    let abi = sig.header.abi;
-                    fn_maybe_err(tcx, item.ident.span, abi);
-                }
-            }
-        }
-        hir::ItemKind::Struct(..) => {
-            check_struct(tcx, it.hir_id, it.span);
-        }
-        hir::ItemKind::Union(..) => {
-            check_union(tcx, it.hir_id, it.span);
-        }
-        hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => {
-            // HACK(jynelson): trying to infer the type of `impl trait` breaks documenting
-            // `async-std` (and `pub async fn` in general).
-            // Since rustdoc doesn't care about the concrete type behind `impl Trait`, just don't look at it!
-            // See https://github.com/rust-lang/rust/issues/75100
-            if !tcx.sess.opts.actually_rustdoc {
-                let def_id = tcx.hir().local_def_id(it.hir_id);
-
-                let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id());
-                check_opaque(tcx, def_id, substs, it.span, &origin);
-            }
-        }
-        hir::ItemKind::TyAlias(..) => {
-            let def_id = tcx.hir().local_def_id(it.hir_id);
-            let pty_ty = tcx.type_of(def_id);
-            let generics = tcx.generics_of(def_id);
-            check_type_params_are_used(tcx, &generics, pty_ty);
-        }
-        hir::ItemKind::ForeignMod(ref m) => {
-            check_abi(tcx, it.span, m.abi);
-
-            if m.abi == Abi::RustIntrinsic {
-                for item in m.items {
-                    intrinsic::check_intrinsic_type(tcx, item);
-                }
-            } else if m.abi == Abi::PlatformIntrinsic {
-                for item in m.items {
-                    intrinsic::check_platform_intrinsic_type(tcx, item);
-                }
-            } else {
-                for item in m.items {
-                    let generics = tcx.generics_of(tcx.hir().local_def_id(item.hir_id));
-                    let own_counts = generics.own_counts();
-                    if generics.params.len() - own_counts.lifetimes != 0 {
-                        let (kinds, kinds_pl, egs) = match (own_counts.types, own_counts.consts) {
-                            (_, 0) => ("type", "types", Some("u32")),
-                            // We don't specify an example value, because we can't generate
-                            // a valid value for any type.
-                            (0, _) => ("const", "consts", None),
-                            _ => ("type or const", "types or consts", None),
-                        };
-                        struct_span_err!(
-                            tcx.sess,
-                            item.span,
-                            E0044,
-                            "foreign items may not have {} parameters",
-                            kinds,
-                        )
-                        .span_label(item.span, &format!("can't have {} parameters", kinds))
-                        .help(
-                            // FIXME: once we start storing spans for type arguments, turn this
-                            // into a suggestion.
-                            &format!(
-                                "replace the {} parameters with concrete {}{}",
-                                kinds,
-                                kinds_pl,
-                                egs.map(|egs| format!(" like `{}`", egs)).unwrap_or_default(),
-                            ),
-                        )
-                        .emit();
-                    }
-
-                    if let hir::ForeignItemKind::Fn(ref fn_decl, _, _) = item.kind {
-                        require_c_abi_if_c_variadic(tcx, fn_decl, m.abi, item.span);
-                    }
-                }
-            }
-        }
-        _ => { /* nothing to do */ }
-    }
-}
-
 fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId, span: Span) {
     // Only restricted on wasm32 target for now
     if !tcx.sess.opts.target_triple.triple().starts_with("wasm32") {
@@ -2055,8 +751,8 @@ fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId, span: S
     // `#[link_section]` may contain arbitrary, or even undefined bytes, but it is
     // the consumer's responsibility to ensure all bytes that have been read
     // have defined values.
-    match tcx.const_eval_poly(id.to_def_id()) {
-        Ok(ConstValue::ByRef { alloc, .. }) => {
+    match tcx.eval_static_initializer(id.to_def_id()) {
+        Ok(alloc) => {
             if alloc.relocations().len() != 0 {
                 let msg = "statics with a custom `#[link_section]` must be a \
                            simple list of bytes on the wasm target with no \
@@ -2064,17 +760,10 @@ fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId, span: S
                 tcx.sess.span_err(span, msg);
             }
         }
-        Ok(_) => bug!("Matching on non-ByRef static"),
         Err(_) => {}
     }
 }
 
-fn check_on_unimplemented(tcx: TyCtxt<'_>, trait_def_id: DefId, item: &hir::Item<'_>) {
-    let item_def_id = tcx.hir().local_def_id(item.hir_id);
-    // an error would be reported if this fails.
-    let _ = traits::OnUnimplementedDirective::of_item(tcx, trait_def_id, item_def_id.to_def_id());
-}
-
 fn report_forbidden_specialization(
     tcx: TyCtxt<'_>,
     impl_item: &hir::ImplItem<'_>,
@@ -2106,248 +795,6 @@ fn report_forbidden_specialization(
     err.emit();
 }
 
-fn check_specialization_validity<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    trait_def: &ty::TraitDef,
-    trait_item: &ty::AssocItem,
-    impl_id: DefId,
-    impl_item: &hir::ImplItem<'_>,
-) {
-    let kind = match impl_item.kind {
-        hir::ImplItemKind::Const(..) => ty::AssocKind::Const,
-        hir::ImplItemKind::Fn(..) => ty::AssocKind::Fn,
-        hir::ImplItemKind::TyAlias(_) => ty::AssocKind::Type,
-    };
-
-    let ancestors = match trait_def.ancestors(tcx, impl_id) {
-        Ok(ancestors) => ancestors,
-        Err(_) => return,
-    };
-    let mut ancestor_impls = ancestors
-        .skip(1)
-        .filter_map(|parent| {
-            if parent.is_from_trait() {
-                None
-            } else {
-                Some((parent, parent.item(tcx, trait_item.ident, kind, trait_def.def_id)))
-            }
-        })
-        .peekable();
-
-    if ancestor_impls.peek().is_none() {
-        // No parent, nothing to specialize.
-        return;
-    }
-
-    let opt_result = ancestor_impls.find_map(|(parent_impl, parent_item)| {
-        match parent_item {
-            // Parent impl exists, and contains the parent item we're trying to specialize, but
-            // doesn't mark it `default`.
-            Some(parent_item) if traits::impl_item_is_final(tcx, &parent_item) => {
-                Some(Err(parent_impl.def_id()))
-            }
-
-            // Parent impl contains item and makes it specializable.
-            Some(_) => Some(Ok(())),
-
-            // Parent impl doesn't mention the item. This means it's inherited from the
-            // grandparent. In that case, if parent is a `default impl`, inherited items use the
-            // "defaultness" from the grandparent, else they are final.
-            None => {
-                if tcx.impl_defaultness(parent_impl.def_id()).is_default() {
-                    None
-                } else {
-                    Some(Err(parent_impl.def_id()))
-                }
-            }
-        }
-    });
-
-    // If `opt_result` is `None`, we have only encountered `default impl`s that don't contain the
-    // item. This is allowed, the item isn't actually getting specialized here.
-    let result = opt_result.unwrap_or(Ok(()));
-
-    if let Err(parent_impl) = result {
-        report_forbidden_specialization(tcx, impl_item, parent_impl);
-    }
-}
-
-fn check_impl_items_against_trait<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    full_impl_span: Span,
-    impl_id: LocalDefId,
-    impl_trait_ref: ty::TraitRef<'tcx>,
-    impl_item_refs: &[hir::ImplItemRef<'_>],
-) {
-    let impl_span = tcx.sess.source_map().guess_head_span(full_impl_span);
-
-    // If the trait reference itself is erroneous (so the compilation is going
-    // to fail), skip checking the items here -- the `impl_item` table in `tcx`
-    // isn't populated for such impls.
-    if impl_trait_ref.references_error() {
-        return;
-    }
-
-    // Negative impls are not expected to have any items
-    match tcx.impl_polarity(impl_id) {
-        ty::ImplPolarity::Reservation | ty::ImplPolarity::Positive => {}
-        ty::ImplPolarity::Negative => {
-            if let [first_item_ref, ..] = impl_item_refs {
-                let first_item_span = tcx.hir().impl_item(first_item_ref.id).span;
-                struct_span_err!(
-                    tcx.sess,
-                    first_item_span,
-                    E0749,
-                    "negative impls cannot have any items"
-                )
-                .emit();
-            }
-            return;
-        }
-    }
-
-    // Locate trait definition and items
-    let trait_def = tcx.trait_def(impl_trait_ref.def_id);
-
-    let impl_items = || impl_item_refs.iter().map(|iiref| tcx.hir().impl_item(iiref.id));
-
-    // Check existing impl methods to see if they are both present in trait
-    // and compatible with trait signature
-    for impl_item in impl_items() {
-        let namespace = impl_item.kind.namespace();
-        let ty_impl_item = tcx.associated_item(tcx.hir().local_def_id(impl_item.hir_id));
-        let ty_trait_item = tcx
-            .associated_items(impl_trait_ref.def_id)
-            .find_by_name_and_namespace(tcx, ty_impl_item.ident, namespace, impl_trait_ref.def_id)
-            .or_else(|| {
-                // Not compatible, but needed for the error message
-                tcx.associated_items(impl_trait_ref.def_id)
-                    .filter_by_name(tcx, ty_impl_item.ident, impl_trait_ref.def_id)
-                    .next()
-            });
-
-        // Check that impl definition matches trait definition
-        if let Some(ty_trait_item) = ty_trait_item {
-            match impl_item.kind {
-                hir::ImplItemKind::Const(..) => {
-                    // Find associated const definition.
-                    if ty_trait_item.kind == ty::AssocKind::Const {
-                        compare_const_impl(
-                            tcx,
-                            &ty_impl_item,
-                            impl_item.span,
-                            &ty_trait_item,
-                            impl_trait_ref,
-                        );
-                    } else {
-                        let mut err = struct_span_err!(
-                            tcx.sess,
-                            impl_item.span,
-                            E0323,
-                            "item `{}` is an associated const, \
-                             which doesn't match its trait `{}`",
-                            ty_impl_item.ident,
-                            impl_trait_ref.print_only_trait_path()
-                        );
-                        err.span_label(impl_item.span, "does not match trait");
-                        // We can only get the spans from local trait definition
-                        // Same for E0324 and E0325
-                        if let Some(trait_span) = tcx.hir().span_if_local(ty_trait_item.def_id) {
-                            err.span_label(trait_span, "item in trait");
-                        }
-                        err.emit()
-                    }
-                }
-                hir::ImplItemKind::Fn(..) => {
-                    let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id);
-                    if ty_trait_item.kind == ty::AssocKind::Fn {
-                        compare_impl_method(
-                            tcx,
-                            &ty_impl_item,
-                            impl_item.span,
-                            &ty_trait_item,
-                            impl_trait_ref,
-                            opt_trait_span,
-                        );
-                    } else {
-                        let mut err = struct_span_err!(
-                            tcx.sess,
-                            impl_item.span,
-                            E0324,
-                            "item `{}` is an associated method, \
-                             which doesn't match its trait `{}`",
-                            ty_impl_item.ident,
-                            impl_trait_ref.print_only_trait_path()
-                        );
-                        err.span_label(impl_item.span, "does not match trait");
-                        if let Some(trait_span) = opt_trait_span {
-                            err.span_label(trait_span, "item in trait");
-                        }
-                        err.emit()
-                    }
-                }
-                hir::ImplItemKind::TyAlias(_) => {
-                    let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id);
-                    if ty_trait_item.kind == ty::AssocKind::Type {
-                        compare_ty_impl(
-                            tcx,
-                            &ty_impl_item,
-                            impl_item.span,
-                            &ty_trait_item,
-                            impl_trait_ref,
-                            opt_trait_span,
-                        );
-                    } else {
-                        let mut err = struct_span_err!(
-                            tcx.sess,
-                            impl_item.span,
-                            E0325,
-                            "item `{}` is an associated type, \
-                             which doesn't match its trait `{}`",
-                            ty_impl_item.ident,
-                            impl_trait_ref.print_only_trait_path()
-                        );
-                        err.span_label(impl_item.span, "does not match trait");
-                        if let Some(trait_span) = opt_trait_span {
-                            err.span_label(trait_span, "item in trait");
-                        }
-                        err.emit()
-                    }
-                }
-            }
-
-            check_specialization_validity(
-                tcx,
-                trait_def,
-                &ty_trait_item,
-                impl_id.to_def_id(),
-                impl_item,
-            );
-        }
-    }
-
-    // Check for missing items from trait
-    let mut missing_items = Vec::new();
-    if let Ok(ancestors) = trait_def.ancestors(tcx, impl_id.to_def_id()) {
-        for trait_item in tcx.associated_items(impl_trait_ref.def_id).in_definition_order() {
-            let is_implemented = ancestors
-                .leaf_def(tcx, trait_item.ident, trait_item.kind)
-                .map(|node_item| !node_item.defining_node.is_from_trait())
-                .unwrap_or(false);
-
-            if !is_implemented && tcx.impl_defaultness(impl_id).is_final() {
-                if !trait_item.defaultness.has_value() {
-                    missing_items.push(*trait_item);
-                }
-            }
-        }
-    }
-
-    if !missing_items.is_empty() {
-        missing_items_err(tcx, impl_span, &missing_items, full_impl_span);
-    }
-}
-
 fn missing_items_err(
     tcx: TyCtxt<'_>,
     impl_span: Span,
@@ -2426,7 +873,7 @@ fn bounds_from_generic_predicates<'tcx>(
             "<{}>",
             types
                 .keys()
-                .filter_map(|t| match t.kind {
+                .filter_map(|t| match t.kind() {
                     ty::Param(_) => Some(t.to_string()),
                     // Avoid suggesting the following:
                     // fn foo<T, <T as Trait>::Bar>(_: T) where T: Trait, <T as Trait>::Bar: Other {}
@@ -2471,7 +918,7 @@ fn fn_sig_suggestion<'tcx>(
         .iter()
         .enumerate()
         .map(|(i, ty)| {
-            Some(match ty.kind {
+            Some(match ty.kind() {
                 ty::Param(_) if assoc.fn_has_self_parameter && i == 0 => "self".to_string(),
                 ty::Ref(reg, ref_ty, mutability) if i == 0 => {
                     let reg = match &format!("{}", reg)[..] {
@@ -2479,7 +926,7 @@ fn fn_sig_suggestion<'tcx>(
                         reg => format!("{} ", reg),
                     };
                     if assoc.fn_has_self_parameter {
-                        match ref_ty.kind {
+                        match ref_ty.kind() {
                             ty::Param(param) if param.name == kw::SelfUpper => {
                                 format!("&{}{}self", reg, mutability.prefix_str())
                             }
@@ -2487,14 +934,14 @@ fn fn_sig_suggestion<'tcx>(
                             _ => format!("self: {}", ty),
                         }
                     } else {
-                        format!("_: {:?}", ty)
+                        format!("_: {}", ty)
                     }
                 }
                 _ => {
                     if assoc.fn_has_self_parameter && i == 0 {
-                        format!("self: {:?}", ty)
+                        format!("self: {}", ty)
                     } else {
-                        format!("_: {:?}", ty)
+                        format!("_: {}", ty)
                     }
                 }
             })
@@ -2504,7 +951,7 @@ fn fn_sig_suggestion<'tcx>(
         .collect::<Vec<String>>()
         .join(", ");
     let output = sig.output();
-    let output = if !output.is_unit() { format!(" -> {:?}", output) } else { String::new() };
+    let output = if !output.is_unit() { format!(" -> {}", output) } else { String::new() };
 
     let unsafety = sig.unsafety.prefix_str();
     let (generics, where_clauses) = bounds_from_generic_predicates(tcx, predicates);
@@ -2542,164 +989,9 @@ fn suggestion_signature(assoc: &ty::AssocItem, tcx: TyCtxt<'_>) -> String {
         ty::AssocKind::Const => {
             let ty = tcx.type_of(assoc.def_id);
             let val = expr::ty_kind_suggestion(ty).unwrap_or("value");
-            format!("const {}: {:?} = {};", assoc.ident, ty, val)
-        }
-    }
-}
-
-/// Checks whether a type can be represented in memory. In particular, it
-/// identifies types that contain themselves without indirection through a
-/// pointer, which would mean their size is unbounded.
-fn check_representable(tcx: TyCtxt<'_>, sp: Span, item_def_id: LocalDefId) -> bool {
-    let rty = tcx.type_of(item_def_id);
-
-    // Check that it is possible to represent this type. This call identifies
-    // (1) types that contain themselves and (2) types that contain a different
-    // recursive type. It is only necessary to throw an error on those that
-    // contain themselves. For case 2, there must be an inner type that will be
-    // caught by case 1.
-    match rty.is_representable(tcx, sp) {
-        Representability::SelfRecursive(spans) => {
-            recursive_type_with_infinite_size_error(tcx, item_def_id.to_def_id(), spans);
-            return false;
+            format!("const {}: {} = {};", assoc.ident, ty, val)
         }
-        Representability::Representable | Representability::ContainsRecursive => (),
     }
-    true
-}
-
-pub fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) {
-    let t = tcx.type_of(def_id);
-    if let ty::Adt(def, substs) = t.kind {
-        if def.is_struct() {
-            let fields = &def.non_enum_variant().fields;
-            if fields.is_empty() {
-                struct_span_err!(tcx.sess, sp, E0075, "SIMD vector cannot be empty").emit();
-                return;
-            }
-            let e = fields[0].ty(tcx, substs);
-            if !fields.iter().all(|f| f.ty(tcx, substs) == e) {
-                struct_span_err!(tcx.sess, sp, E0076, "SIMD vector should be homogeneous")
-                    .span_label(sp, "SIMD elements must have the same type")
-                    .emit();
-                return;
-            }
-            match e.kind {
-                ty::Param(_) => { /* struct<T>(T, T, T, T) is ok */ }
-                _ if e.is_machine() => { /* struct(u8, u8, u8, u8) is ok */ }
-                _ => {
-                    struct_span_err!(
-                        tcx.sess,
-                        sp,
-                        E0077,
-                        "SIMD vector element type should be machine type"
-                    )
-                    .emit();
-                    return;
-                }
-            }
-        }
-    }
-}
-
-fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: &ty::AdtDef) {
-    let repr = def.repr;
-    if repr.packed() {
-        for attr in tcx.get_attrs(def.did).iter() {
-            for r in attr::find_repr_attrs(&tcx.sess, attr) {
-                if let attr::ReprPacked(pack) = r {
-                    if let Some(repr_pack) = repr.pack {
-                        if pack as u64 != repr_pack.bytes() {
-                            struct_span_err!(
-                                tcx.sess,
-                                sp,
-                                E0634,
-                                "type has conflicting packed representation hints"
-                            )
-                            .emit();
-                        }
-                    }
-                }
-            }
-        }
-        if repr.align.is_some() {
-            struct_span_err!(
-                tcx.sess,
-                sp,
-                E0587,
-                "type has conflicting packed and align representation hints"
-            )
-            .emit();
-        } else {
-            if let Some(def_spans) = check_packed_inner(tcx, def.did, &mut vec![]) {
-                let mut err = struct_span_err!(
-                    tcx.sess,
-                    sp,
-                    E0588,
-                    "packed type cannot transitively contain a `#[repr(align)]` type"
-                );
-
-                err.span_note(
-                    tcx.def_span(def_spans[0].0),
-                    &format!(
-                        "`{}` has a `#[repr(align)]` attribute",
-                        tcx.item_name(def_spans[0].0)
-                    ),
-                );
-
-                if def_spans.len() > 2 {
-                    let mut first = true;
-                    for (adt_def, span) in def_spans.iter().skip(1).rev() {
-                        let ident = tcx.item_name(*adt_def);
-                        err.span_note(
-                            *span,
-                            &if first {
-                                format!(
-                                    "`{}` contains a field of type `{}`",
-                                    tcx.type_of(def.did),
-                                    ident
-                                )
-                            } else {
-                                format!("...which contains a field of type `{}`", ident)
-                            },
-                        );
-                        first = false;
-                    }
-                }
-
-                err.emit();
-            }
-        }
-    }
-}
-
-fn check_packed_inner(
-    tcx: TyCtxt<'_>,
-    def_id: DefId,
-    stack: &mut Vec<DefId>,
-) -> Option<Vec<(DefId, Span)>> {
-    if let ty::Adt(def, substs) = tcx.type_of(def_id).kind {
-        if def.is_struct() || def.is_union() {
-            if def.repr.align.is_some() {
-                return Some(vec![(def.did, DUMMY_SP)]);
-            }
-
-            stack.push(def_id);
-            for field in &def.non_enum_variant().fields {
-                if let ty::Adt(def, _) = field.ty(tcx, substs).kind {
-                    if !stack.contains(&def.did) {
-                        if let Some(mut defs) = check_packed_inner(tcx, def.did, stack) {
-                            defs.push((def.did, field.ident.span));
-                            return Some(defs);
-                        }
-                    }
-                }
-            }
-            stack.pop();
-        }
-    }
-
-    None
 }
 
 /// Emit an error when encountering more or less than one variant in a transparent enum.
@@ -2747,158 +1039,6 @@ fn bad_non_zero_sized_fields<'tcx>(
     err.emit();
 }
 
-fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: &'tcx ty::AdtDef) {
-    if !adt.repr.transparent() {
-        return;
-    }
-    let sp = tcx.sess.source_map().guess_head_span(sp);
-
-    if adt.is_union() && !tcx.features().transparent_unions {
-        feature_err(
-            &tcx.sess.parse_sess,
-            sym::transparent_unions,
-            sp,
-            "transparent unions are unstable",
-        )
-        .emit();
-    }
-
-    if adt.variants.len() != 1 {
-        bad_variant_count(tcx, adt, sp, adt.did);
-        if adt.variants.is_empty() {
-            // Don't bother checking the fields. No variants (and thus no fields) exist.
-            return;
-        }
-    }
-
-    // For each field, figure out if it's known to be a ZST and align(1)
-    let field_infos = adt.all_fields().map(|field| {
-        let ty = field.ty(tcx, InternalSubsts::identity_for_item(tcx, field.did));
-        let param_env = tcx.param_env(field.did);
-        let layout = tcx.layout_of(param_env.and(ty));
-        // We are currently checking the type this field came from, so it must be local
-        let span = tcx.hir().span_if_local(field.did).unwrap();
-        let zst = layout.map(|layout| layout.is_zst()).unwrap_or(false);
-        let align1 = layout.map(|layout| layout.align.abi.bytes() == 1).unwrap_or(false);
-        (span, zst, align1)
-    });
-
-    let non_zst_fields =
-        field_infos.clone().filter_map(|(span, zst, _align1)| if !zst { Some(span) } else { None });
-    let non_zst_count = non_zst_fields.clone().count();
-    if non_zst_count != 1 {
-        bad_non_zero_sized_fields(tcx, adt, non_zst_count, non_zst_fields, sp);
-    }
-    for (span, zst, align1) in field_infos {
-        if zst && !align1 {
-            struct_span_err!(
-                tcx.sess,
-                span,
-                E0691,
-                "zero-sized field in transparent {} has alignment larger than 1",
-                adt.descr(),
-            )
-            .span_label(span, "has alignment larger than 1")
-            .emit();
-        }
-    }
-}
-
-#[allow(trivial_numeric_casts)]
-pub fn check_enum<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    sp: Span,
-    vs: &'tcx [hir::Variant<'tcx>],
-    id: hir::HirId,
-) {
-    let def_id = tcx.hir().local_def_id(id);
-    let def = tcx.adt_def(def_id);
-    def.destructor(tcx); // force the destructor to be evaluated
-
-    if vs.is_empty() {
-        let attributes = tcx.get_attrs(def_id.to_def_id());
-        if let Some(attr) = tcx.sess.find_by_name(&attributes, sym::repr) {
-            struct_span_err!(
-                tcx.sess,
-                attr.span,
-                E0084,
-                "unsupported representation for zero-variant enum"
-            )
-            .span_label(sp, "zero-variant enum")
-            .emit();
-        }
-    }
-
-    let repr_type_ty = def.repr.discr_type().to_ty(tcx);
-    if repr_type_ty == tcx.types.i128 || repr_type_ty == tcx.types.u128 {
-        if !tcx.features().repr128 {
-            feature_err(
-                &tcx.sess.parse_sess,
-                sym::repr128,
-                sp,
-                "repr with 128-bit type is unstable",
-            )
-            .emit();
-        }
-    }
-
-    for v in vs {
-        if let Some(ref e) = v.disr_expr {
-            tcx.ensure().typeck(tcx.hir().local_def_id(e.hir_id));
-        }
-    }
-
-    if tcx.adt_def(def_id).repr.int.is_none() && tcx.features().arbitrary_enum_discriminant {
-        let is_unit = |var: &hir::Variant<'_>| match var.data {
-            hir::VariantData::Unit(..) => true,
-            _ => false,
-        };
-
-        let has_disr = |var: &hir::Variant<'_>| var.disr_expr.is_some();
-        let has_non_units = vs.iter().any(|var| !is_unit(var));
-        let disr_units = vs.iter().any(|var| is_unit(&var) && has_disr(&var));
-        let disr_non_unit = vs.iter().any(|var| !is_unit(&var) && has_disr(&var));
-
-        if disr_non_unit || (disr_units && has_non_units) {
-            let mut err =
-                struct_span_err!(tcx.sess, sp, E0732, "`#[repr(inttype)]` must be specified");
-            err.emit();
-        }
-    }
-
-    let mut disr_vals: Vec<Discr<'tcx>> = Vec::with_capacity(vs.len());
-    for ((_, discr), v) in def.discriminants(tcx).zip(vs) {
-        // Check for duplicate discriminant values
-        if let Some(i) = disr_vals.iter().position(|&x| x.val == discr.val) {
-            let variant_did = def.variants[VariantIdx::new(i)].def_id;
-            let variant_i_hir_id = tcx.hir().local_def_id_to_hir_id(variant_did.expect_local());
-            let variant_i = tcx.hir().expect_variant(variant_i_hir_id);
-            let i_span = match variant_i.disr_expr {
-                Some(ref expr) => tcx.hir().span(expr.hir_id),
-                None => tcx.hir().span(variant_i_hir_id),
-            };
-            let span = match v.disr_expr {
-                Some(ref expr) => tcx.hir().span(expr.hir_id),
-                None => v.span,
-            };
-            struct_span_err!(
-                tcx.sess,
-                span,
-                E0081,
-                "discriminant value `{}` already exists",
-                disr_vals[i]
-            )
-            .span_label(i_span, format!("first use of `{}`", disr_vals[i]))
-            .span_label(span, format!("enum already has `{}`", disr_vals[i]))
-            .emit();
-        }
-        disr_vals.push(discr);
-    }
-
-    check_representable(tcx, sp, def_id);
-    check_transparent(tcx, sp, def);
-}
-
 fn report_unexpected_variant_res(tcx: TyCtxt<'_>, res: Res, span: Span) {
     struct_span_err!(
         tcx.sess,
@@ -2911,136 +1051,6 @@ fn report_unexpected_variant_res(tcx: TyCtxt<'_>, res: Res, span: Span) {
     .emit();
 }
 
-impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
-    fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
-        self.tcx
-    }
-
-    fn item_def_id(&self) -> Option<DefId> {
-        None
-    }
-
-    fn default_constness_for_trait_bounds(&self) -> hir::Constness {
-        // FIXME: refactor this into a method
-        let node = self.tcx.hir().get(self.body_id);
-        if let Some(fn_like) = FnLikeNode::from_node(node) {
-            fn_like.constness()
-        } else {
-            hir::Constness::NotConst
-        }
-    }
-
-    fn get_type_parameter_bounds(&self, _: Span, def_id: DefId) -> ty::GenericPredicates<'tcx> {
-        let tcx = self.tcx;
-        let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
-        let item_id = tcx.hir().ty_param_owner(hir_id);
-        let item_def_id = tcx.hir().local_def_id(item_id);
-        let generics = tcx.generics_of(item_def_id);
-        let index = generics.param_def_id_to_index[&def_id];
-        ty::GenericPredicates {
-            parent: None,
-            predicates: tcx.arena.alloc_from_iter(
-                self.param_env.caller_bounds().iter().filter_map(|predicate| {
-                    match predicate.skip_binders() {
-                        ty::PredicateAtom::Trait(data, _) if data.self_ty().is_param(index) => {
-                            // HACK(eddyb) should get the original `Span`.
-                            let span = tcx.def_span(def_id);
-                            Some((predicate, span))
-                        }
-                        _ => None,
-                    }
-                }),
-            ),
-        }
-    }
-
-    fn re_infer(&self, def: Option<&ty::GenericParamDef>, span: Span) -> Option<ty::Region<'tcx>> {
-        let v = match def {
-            Some(def) => infer::EarlyBoundRegion(span, def.name),
-            None => infer::MiscVariable(span),
-        };
-        Some(self.next_region_var(v))
-    }
-
-    fn allow_ty_infer(&self) -> bool {
-        true
-    }
-
-    fn ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> {
-        if let Some(param) = param {
-            if let GenericArgKind::Type(ty) = self.var_for_def(span, param).unpack() {
-                return ty;
-            }
-            unreachable!()
-        } else {
-            self.next_ty_var(TypeVariableOrigin {
-                kind: TypeVariableOriginKind::TypeInference,
-                span,
-            })
-        }
-    }
-
-    fn ct_infer(
-        &self,
-        ty: Ty<'tcx>,
-        param: Option<&ty::GenericParamDef>,
-        span: Span,
-    ) -> &'tcx Const<'tcx> {
-        if let Some(param) = param {
-            if let GenericArgKind::Const(ct) = self.var_for_def(span, param).unpack() {
-                return ct;
-            }
-            unreachable!()
-        } else {
-            self.next_const_var(
-                ty,
-                ConstVariableOrigin { kind: ConstVariableOriginKind::ConstInference, span },
-            )
-        }
-    }
-
-    fn projected_ty_from_poly_trait_ref(
-        &self,
-        span: Span,
-        item_def_id: DefId,
-        item_segment: &hir::PathSegment<'_>,
-        poly_trait_ref: ty::PolyTraitRef<'tcx>,
-    ) -> Ty<'tcx> {
-        let (trait_ref, _) = self.replace_bound_vars_with_fresh_vars(
-            span,
-            infer::LateBoundRegionConversionTime::AssocTypeProjection(item_def_id),
-            &poly_trait_ref,
-        );
-
-        let item_substs = <dyn AstConv<'tcx>>::create_substs_for_associated_item(
-            self,
-            self.tcx,
-            span,
-            item_def_id,
-            item_segment,
-            trait_ref.substs,
-        );
-
-        self.tcx().mk_projection(item_def_id, item_substs)
-    }
-
-    fn normalize_ty(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
-        if ty.has_escaping_bound_vars() {
-            ty // FIXME: normalization and escaping regions
-        } else {
-            self.normalize_associated_types_in(span, &ty)
-        }
-    }
-
-    fn set_tainted_by_errors(&self) {
-        self.infcx.set_tainted_by_errors()
-    }
-
-    fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, _span: Span) {
-        self.write_ty(hir_id, ty)
-    }
-}
-
 /// Controls whether the arguments are tupled. This is used for the call
 /// operator.
 ///
@@ -3073,2948 +1083,49 @@ enum FallbackMode {
     All,
 }
 
-impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
-    pub fn new(
-        inh: &'a Inherited<'a, 'tcx>,
-        param_env: ty::ParamEnv<'tcx>,
-        body_id: hir::HirId,
-    ) -> FnCtxt<'a, 'tcx> {
-        FnCtxt {
-            body_id,
-            param_env,
-            err_count_on_creation: inh.tcx.sess.err_count(),
-            ret_coercion: None,
-            ret_coercion_span: RefCell::new(None),
-            resume_yield_tys: None,
-            ps: RefCell::new(UnsafetyState::function(hir::Unsafety::Normal, hir::CRATE_HIR_ID)),
-            diverges: Cell::new(Diverges::Maybe),
-            has_errors: Cell::new(false),
-            enclosing_breakables: RefCell::new(EnclosingBreakables {
-                stack: Vec::new(),
-                by_id: Default::default(),
-            }),
-            inh,
-        }
-    }
-
-    pub fn sess(&self) -> &Session {
-        &self.tcx.sess
-    }
-
-    pub fn errors_reported_since_creation(&self) -> bool {
-        self.tcx.sess.err_count() > self.err_count_on_creation
-    }
-
-    /// Produces warning on the given node, if the current point in the
-    /// function is unreachable, and there hasn't been another warning.
-    fn warn_if_unreachable(&self, id: hir::HirId, span: Span, kind: &str) {
-        // FIXME: Combine these two 'if' expressions into one once
-        // let chains are implemented
-        if let Diverges::Always { span: orig_span, custom_note } = self.diverges.get() {
-            // If span arose from a desugaring of `if` or `while`, then it is the condition itself,
-            // which diverges, that we are about to lint on. This gives suboptimal diagnostics.
-            // Instead, stop here so that the `if`- or `while`-expression's block is linted instead.
-            if !span.is_desugaring(DesugaringKind::CondTemporary)
-                && !span.is_desugaring(DesugaringKind::Async)
-                && !orig_span.is_desugaring(DesugaringKind::Await)
-            {
-                self.diverges.set(Diverges::WarnedAlways);
-
-                debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind);
-
-                self.tcx().struct_span_lint_hir(lint::builtin::UNREACHABLE_CODE, id, span, |lint| {
-                    let msg = format!("unreachable {}", kind);
-                    lint.build(&msg)
-                        .span_label(span, &msg)
-                        .span_label(
-                            orig_span,
-                            custom_note
-                                .unwrap_or("any code following this expression is unreachable"),
-                        )
-                        .emit();
-                })
-            }
-        }
-    }
-
-    pub fn cause(&self, span: Span, code: ObligationCauseCode<'tcx>) -> ObligationCause<'tcx> {
-        ObligationCause::new(span, self.body_id, code)
-    }
-
-    pub fn misc(&self, span: Span) -> ObligationCause<'tcx> {
-        self.cause(span, ObligationCauseCode::MiscObligation)
-    }
-
-    /// Resolves type and const variables in `ty` if possible. Unlike the infcx
-    /// version (resolve_vars_if_possible), this version will
-    /// also select obligations if it seems useful, in an effort
-    /// to get more type information.
-    fn resolve_vars_with_obligations(&self, mut ty: Ty<'tcx>) -> Ty<'tcx> {
-        debug!("resolve_vars_with_obligations(ty={:?})", ty);
-
-        // No Infer()? Nothing needs doing.
-        if !ty.has_infer_types_or_consts() {
-            debug!("resolve_vars_with_obligations: ty={:?}", ty);
-            return ty;
-        }
-
-        // If `ty` is a type variable, see whether we already know what it is.
-        ty = self.resolve_vars_if_possible(&ty);
-        if !ty.has_infer_types_or_consts() {
-            debug!("resolve_vars_with_obligations: ty={:?}", ty);
-            return ty;
-        }
-
-        // If not, try resolving pending obligations as much as
-        // possible. This can help substantially when there are
-        // indirect dependencies that don't seem worth tracking
-        // precisely.
-        self.select_obligations_where_possible(false, |_| {});
-        ty = self.resolve_vars_if_possible(&ty);
-
-        debug!("resolve_vars_with_obligations: ty={:?}", ty);
-        ty
-    }
-
-    fn record_deferred_call_resolution(
-        &self,
-        closure_def_id: DefId,
-        r: DeferredCallResolution<'tcx>,
-    ) {
-        let mut deferred_call_resolutions = self.deferred_call_resolutions.borrow_mut();
-        deferred_call_resolutions.entry(closure_def_id).or_default().push(r);
-    }
-
-    fn remove_deferred_call_resolutions(
-        &self,
-        closure_def_id: DefId,
-    ) -> Vec<DeferredCallResolution<'tcx>> {
-        let mut deferred_call_resolutions = self.deferred_call_resolutions.borrow_mut();
-        deferred_call_resolutions.remove(&closure_def_id).unwrap_or(vec![])
-    }
-
-    pub fn tag(&self) -> String {
-        format!("{:p}", self)
-    }
-
-    pub fn local_ty(&self, span: Span, nid: hir::HirId) -> LocalTy<'tcx> {
-        self.locals.borrow().get(&nid).cloned().unwrap_or_else(|| {
-            span_bug!(span, "no type for local variable {}", self.tcx.hir().node_to_string(nid))
-        })
-    }
-
-    #[inline]
-    pub fn write_ty(&self, id: hir::HirId, ty: Ty<'tcx>) {
-        debug!(
-            "write_ty({:?}, {:?}) in fcx {}",
-            id,
-            self.resolve_vars_if_possible(&ty),
-            self.tag()
-        );
-        self.typeck_results.borrow_mut().node_types_mut().insert(id, ty);
-
-        if ty.references_error() {
-            self.has_errors.set(true);
-            self.set_tainted_by_errors();
-        }
-    }
-
-    pub fn write_field_index(&self, hir_id: hir::HirId, index: usize) {
-        self.typeck_results.borrow_mut().field_indices_mut().insert(hir_id, index);
-    }
-
-    fn write_resolution(&self, hir_id: hir::HirId, r: Result<(DefKind, DefId), ErrorReported>) {
-        self.typeck_results.borrow_mut().type_dependent_defs_mut().insert(hir_id, r);
-    }
-
-    pub fn write_method_call(&self, hir_id: hir::HirId, method: MethodCallee<'tcx>) {
-        debug!("write_method_call(hir_id={:?}, method={:?})", hir_id, method);
-        self.write_resolution(hir_id, Ok((DefKind::AssocFn, method.def_id)));
-        self.write_substs(hir_id, method.substs);
-
-        // When the method is confirmed, the `method.substs` includes
-        // parameters from not just the method, but also the impl of
-        // the method -- in particular, the `Self` type will be fully
-        // resolved. However, those are not something that the "user
-        // specified" -- i.e., those types come from the inferred type
-        // of the receiver, not something the user wrote. So when we
-        // create the user-substs, we want to replace those earlier
-        // types with just the types that the user actually wrote --
-        // that is, those that appear on the *method itself*.
-        //
-        // As an example, if the user wrote something like
-        // `foo.bar::<u32>(...)` -- the `Self` type here will be the
-        // type of `foo` (possibly adjusted), but we don't want to
-        // include that. We want just the `[_, u32]` part.
-        if !method.substs.is_noop() {
-            let method_generics = self.tcx.generics_of(method.def_id);
-            if !method_generics.params.is_empty() {
-                let user_type_annotation = self.infcx.probe(|_| {
-                    let user_substs = UserSubsts {
-                        substs: InternalSubsts::for_item(self.tcx, method.def_id, |param, _| {
-                            let i = param.index as usize;
-                            if i < method_generics.parent_count {
-                                self.infcx.var_for_def(DUMMY_SP, param)
-                            } else {
-                                method.substs[i]
-                            }
-                        }),
-                        user_self_ty: None, // not relevant here
-                    };
-
-                    self.infcx.canonicalize_user_type_annotation(&UserType::TypeOf(
-                        method.def_id,
-                        user_substs,
-                    ))
-                });
-
-                debug!("write_method_call: user_type_annotation={:?}", user_type_annotation);
-                self.write_user_type_annotation(hir_id, user_type_annotation);
-            }
-        }
-    }
-
-    pub fn write_substs(&self, node_id: hir::HirId, substs: SubstsRef<'tcx>) {
-        if !substs.is_noop() {
-            debug!("write_substs({:?}, {:?}) in fcx {}", node_id, substs, self.tag());
-
-            self.typeck_results.borrow_mut().node_substs_mut().insert(node_id, substs);
-        }
-    }
-
-    /// Given the substs that we just converted from the HIR, try to
-    /// canonicalize them and store them as user-given substitutions
-    /// (i.e., substitutions that must be respected by the NLL check).
-    ///
-    /// This should be invoked **before any unifications have
-    /// occurred**, so that annotations like `Vec<_>` are preserved
-    /// properly.
-    pub fn write_user_type_annotation_from_substs(
-        &self,
-        hir_id: hir::HirId,
-        def_id: DefId,
-        substs: SubstsRef<'tcx>,
-        user_self_ty: Option<UserSelfTy<'tcx>>,
-    ) {
-        debug!(
-            "write_user_type_annotation_from_substs: hir_id={:?} def_id={:?} substs={:?} \
-             user_self_ty={:?} in fcx {}",
-            hir_id,
-            def_id,
-            substs,
-            user_self_ty,
-            self.tag(),
-        );
-
-        if Self::can_contain_user_lifetime_bounds((substs, user_self_ty)) {
-            let canonicalized = self.infcx.canonicalize_user_type_annotation(&UserType::TypeOf(
-                def_id,
-                UserSubsts { substs, user_self_ty },
-            ));
-            debug!("write_user_type_annotation_from_substs: canonicalized={:?}", canonicalized);
-            self.write_user_type_annotation(hir_id, canonicalized);
-        }
-    }
-
-    pub fn write_user_type_annotation(
-        &self,
-        hir_id: hir::HirId,
-        canonical_user_type_annotation: CanonicalUserType<'tcx>,
-    ) {
-        debug!(
-            "write_user_type_annotation: hir_id={:?} canonical_user_type_annotation={:?} tag={}",
-            hir_id,
-            canonical_user_type_annotation,
-            self.tag(),
-        );
-
-        if !canonical_user_type_annotation.is_identity() {
-            self.typeck_results
-                .borrow_mut()
-                .user_provided_types_mut()
-                .insert(hir_id, canonical_user_type_annotation);
-        } else {
-            debug!("write_user_type_annotation: skipping identity substs");
-        }
-    }
-
-    pub fn apply_adjustments(&self, expr: &hir::Expr<'_>, adj: Vec<Adjustment<'tcx>>) {
-        debug!("apply_adjustments(expr={:?}, adj={:?})", expr, adj);
-
-        if adj.is_empty() {
-            return;
-        }
-
-        let autoborrow_mut = adj.iter().any(|adj| {
-            matches!(adj, &Adjustment {
-                kind: Adjust::Borrow(AutoBorrow::Ref(_, AutoBorrowMutability::Mut { .. })),
-                ..
-            })
-        });
-
-        match self.typeck_results.borrow_mut().adjustments_mut().entry(expr.hir_id) {
-            Entry::Vacant(entry) => {
-                entry.insert(adj);
-            }
-            Entry::Occupied(mut entry) => {
-                debug!(" - composing on top of {:?}", entry.get());
-                match (&entry.get()[..], &adj[..]) {
-                    // Applying any adjustment on top of a NeverToAny
-                    // is a valid NeverToAny adjustment, because it can't
-                    // be reached.
-                    (&[Adjustment { kind: Adjust::NeverToAny, .. }], _) => return,
-                    (&[
-                        Adjustment { kind: Adjust::Deref(_), .. },
-                        Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. },
-                    ], &[
-                        Adjustment { kind: Adjust::Deref(_), .. },
-                        .. // Any following adjustments are allowed.
-                    ]) => {
-                        // A reborrow has no effect before a dereference.
-                    }
-                    // FIXME: currently we never try to compose autoderefs
-                    // and ReifyFnPointer/UnsafeFnPointer, but we could.
-                    _ =>
-                        bug!("while adjusting {:?}, can't compose {:?} and {:?}",
-                             expr, entry.get(), adj)
-                };
-                *entry.get_mut() = adj;
-            }
-        }
-
-        // If there is an mutable auto-borrow, it is equivalent to `&mut <expr>`.
-        // In this case implicit use of `Deref` and `Index` within `<expr>` should
-        // instead be `DerefMut` and `IndexMut`, so fix those up.
-        if autoborrow_mut {
-            self.convert_place_derefs_to_mutable(expr);
-        }
-    }
-
-    /// Basically whenever we are converting from a type scheme into
-    /// the fn body space, we always want to normalize associated
-    /// types as well. This function combines the two.
-    fn instantiate_type_scheme<T>(&self, span: Span, substs: SubstsRef<'tcx>, value: &T) -> T
-    where
-        T: TypeFoldable<'tcx>,
-    {
-        let value = value.subst(self.tcx, substs);
-        let result = self.normalize_associated_types_in(span, &value);
-        debug!("instantiate_type_scheme(value={:?}, substs={:?}) = {:?}", value, substs, result);
-        result
-    }
-
-    /// As `instantiate_type_scheme`, but for the bounds found in a
-    /// generic type scheme.
-    fn instantiate_bounds(
-        &self,
-        span: Span,
-        def_id: DefId,
-        substs: SubstsRef<'tcx>,
-    ) -> (ty::InstantiatedPredicates<'tcx>, Vec<Span>) {
-        let bounds = self.tcx.predicates_of(def_id);
-        let spans: Vec<Span> = bounds.predicates.iter().map(|(_, span)| *span).collect();
-        let result = bounds.instantiate(self.tcx, substs);
-        let result = self.normalize_associated_types_in(span, &result);
-        debug!(
-            "instantiate_bounds(bounds={:?}, substs={:?}) = {:?}, {:?}",
-            bounds, substs, result, spans,
-        );
-        (result, spans)
-    }
-
-    /// Replaces the opaque types from the given value with type variables,
-    /// and records the `OpaqueTypeMap` for later use during writeback. See
-    /// `InferCtxt::instantiate_opaque_types` for more details.
-    fn instantiate_opaque_types_from_value<T: TypeFoldable<'tcx>>(
-        &self,
-        parent_id: hir::HirId,
-        value: &T,
-        value_span: Span,
-    ) -> T {
-        let parent_def_id = self.tcx.hir().local_def_id(parent_id);
-        debug!(
-            "instantiate_opaque_types_from_value(parent_def_id={:?}, value={:?})",
-            parent_def_id, value
-        );
-
-        let (value, opaque_type_map) =
-            self.register_infer_ok_obligations(self.instantiate_opaque_types(
-                parent_def_id,
-                self.body_id,
-                self.param_env,
-                value,
-                value_span,
-            ));
-
-        let mut opaque_types = self.opaque_types.borrow_mut();
-        let mut opaque_types_vars = self.opaque_types_vars.borrow_mut();
-        for (ty, decl) in opaque_type_map {
-            let _ = opaque_types.insert(ty, decl);
-            let _ = opaque_types_vars.insert(decl.concrete_ty, decl.opaque_type);
-        }
-
-        value
-    }
-
-    fn normalize_associated_types_in<T>(&self, span: Span, value: &T) -> T
-    where
-        T: TypeFoldable<'tcx>,
-    {
-        self.inh.normalize_associated_types_in(span, self.body_id, self.param_env, value)
-    }
-
-    fn normalize_associated_types_in_as_infer_ok<T>(
-        &self,
-        span: Span,
-        value: &T,
-    ) -> InferOk<'tcx, T>
-    where
-        T: TypeFoldable<'tcx>,
-    {
-        self.inh.partially_normalize_associated_types_in(span, self.body_id, self.param_env, value)
-    }
-
-    pub fn require_type_meets(
-        &self,
-        ty: Ty<'tcx>,
-        span: Span,
-        code: traits::ObligationCauseCode<'tcx>,
-        def_id: DefId,
-    ) {
-        self.register_bound(ty, def_id, traits::ObligationCause::new(span, self.body_id, code));
-    }
-
-    pub fn require_type_is_sized(
-        &self,
-        ty: Ty<'tcx>,
-        span: Span,
-        code: traits::ObligationCauseCode<'tcx>,
-    ) {
-        if !ty.references_error() {
-            let lang_item = self.tcx.require_lang_item(LangItem::Sized, None);
-            self.require_type_meets(ty, span, code, lang_item);
-        }
-    }
-
-    pub fn require_type_is_sized_deferred(
-        &self,
-        ty: Ty<'tcx>,
-        span: Span,
-        code: traits::ObligationCauseCode<'tcx>,
-    ) {
-        if !ty.references_error() {
-            self.deferred_sized_obligations.borrow_mut().push((ty, span, code));
-        }
-    }
-
-    pub fn register_bound(
-        &self,
-        ty: Ty<'tcx>,
-        def_id: DefId,
-        cause: traits::ObligationCause<'tcx>,
-    ) {
-        if !ty.references_error() {
-            self.fulfillment_cx.borrow_mut().register_bound(
-                self,
-                self.param_env,
-                ty,
-                def_id,
-                cause,
-            );
-        }
-    }
-
-    pub fn to_ty(&self, ast_t: &hir::Ty<'_>) -> Ty<'tcx> {
-        let t = AstConv::ast_ty_to_ty(self, ast_t);
-        self.register_wf_obligation(t.into(), ast_t.span, traits::MiscObligation);
-        t
-    }
-
-    pub fn to_ty_saving_user_provided_ty(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> {
-        let ty = self.to_ty(ast_ty);
-        debug!("to_ty_saving_user_provided_ty: ty={:?}", ty);
-
-        if Self::can_contain_user_lifetime_bounds(ty) {
-            let c_ty = self.infcx.canonicalize_response(&UserType::Ty(ty));
-            debug!("to_ty_saving_user_provided_ty: c_ty={:?}", c_ty);
-            self.typeck_results.borrow_mut().user_provided_types_mut().insert(ast_ty.hir_id, c_ty);
-        }
-
-        ty
-    }
-
-    pub fn to_const(&self, ast_c: &hir::AnonConst) -> &'tcx ty::Const<'tcx> {
-        let const_def_id = self.tcx.hir().local_def_id(ast_c.hir_id);
-        let c = ty::Const::from_anon_const(self.tcx, const_def_id);
-        self.register_wf_obligation(
-            c.into(),
-            self.tcx.hir().span(ast_c.hir_id),
-            ObligationCauseCode::MiscObligation,
-        );
-        c
-    }
-
-    pub fn const_arg_to_const(
-        &self,
-        ast_c: &hir::AnonConst,
-        param_def_id: DefId,
-    ) -> &'tcx ty::Const<'tcx> {
-        let const_def = ty::WithOptConstParam {
-            did: self.tcx.hir().local_def_id(ast_c.hir_id),
-            const_param_did: Some(param_def_id),
-        };
-        let c = ty::Const::from_opt_const_arg_anon_const(self.tcx, const_def);
-        self.register_wf_obligation(
-            c.into(),
-            self.tcx.hir().span(ast_c.hir_id),
-            ObligationCauseCode::MiscObligation,
-        );
-        c
-    }
-
-    // If the type given by the user has free regions, save it for later, since
-    // NLL would like to enforce those. Also pass in types that involve
-    // projections, since those can resolve to `'static` bounds (modulo #54940,
-    // which hopefully will be fixed by the time you see this comment, dear
-    // reader, although I have my doubts). Also pass in types with inference
-    // types, because they may be repeated. Other sorts of things are already
-    // sufficiently enforced with erased regions. =)
-    fn can_contain_user_lifetime_bounds<T>(t: T) -> bool
-    where
-        T: TypeFoldable<'tcx>,
-    {
-        t.has_free_regions() || t.has_projections() || t.has_infer_types()
-    }
-
-    pub fn node_ty(&self, id: hir::HirId) -> Ty<'tcx> {
-        match self.typeck_results.borrow().node_types().get(id) {
-            Some(&t) => t,
-            None if self.is_tainted_by_errors() => self.tcx.ty_error(),
-            None => {
-                bug!(
-                    "no type for node {}: {} in fcx {}",
-                    id,
-                    self.tcx.hir().node_to_string(id),
-                    self.tag()
-                );
-            }
-        }
-    }
-
-    /// Registers an obligation for checking later, during regionck, that `arg` is well-formed.
-    pub fn register_wf_obligation(
-        &self,
-        arg: subst::GenericArg<'tcx>,
-        span: Span,
-        code: traits::ObligationCauseCode<'tcx>,
-    ) {
-        // WF obligations never themselves fail, so no real need to give a detailed cause:
-        let cause = traits::ObligationCause::new(span, self.body_id, code);
-        self.register_predicate(traits::Obligation::new(
-            cause,
-            self.param_env,
-            ty::PredicateAtom::WellFormed(arg).to_predicate(self.tcx),
-        ));
-    }
-
-    /// Registers obligations that all `substs` are well-formed.
-    pub fn add_wf_bounds(&self, substs: SubstsRef<'tcx>, expr: &hir::Expr<'_>) {
-        for arg in substs.iter().filter(|arg| {
-            matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..))
-        }) {
-            self.register_wf_obligation(arg, expr.span, traits::MiscObligation);
-        }
-    }
-
-    /// Given a fully substituted set of bounds (`generic_bounds`), and the values with which each
-    /// type/region parameter was instantiated (`substs`), creates and registers suitable
-    /// trait/region obligations.
-    ///
-    /// For example, if there is a function:
-    ///
-    /// ```
-    /// fn foo<'a,T:'a>(...)
-    /// ```
-    ///
-    /// and a reference:
-    ///
-    /// ```
-    /// let f = foo;
-    /// ```
-    ///
-    /// Then we will create a fresh region variable `'$0` and a fresh type variable `$1` for `'a`
-    /// and `T`. This routine will add a region obligation `$1:'$0` and register it locally.
-    pub fn add_obligations_for_parameters(
-        &self,
-        cause: traits::ObligationCause<'tcx>,
-        predicates: ty::InstantiatedPredicates<'tcx>,
-    ) {
-        assert!(!predicates.has_escaping_bound_vars());
-
-        debug!("add_obligations_for_parameters(predicates={:?})", predicates);
-
-        for obligation in traits::predicates_for_generics(cause, self.param_env, predicates) {
-            self.register_predicate(obligation);
-        }
-    }
-
-    // FIXME(arielb1): use this instead of field.ty everywhere
-    // Only for fields! Returns <none> for methods>
-    // Indifferent to privacy flags
-    pub fn field_ty(
-        &self,
-        span: Span,
-        field: &'tcx ty::FieldDef,
-        substs: SubstsRef<'tcx>,
-    ) -> Ty<'tcx> {
-        self.normalize_associated_types_in(span, &field.ty(self.tcx, substs))
-    }
-
-    fn check_casts(&self) {
-        let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut();
-        for cast in deferred_cast_checks.drain(..) {
-            cast.check(self);
-        }
-    }
-
-    fn resolve_generator_interiors(&self, def_id: DefId) {
-        let mut generators = self.deferred_generator_interiors.borrow_mut();
-        for (body_id, interior, kind) in generators.drain(..) {
-            self.select_obligations_where_possible(false, |_| {});
-            generator_interior::resolve_interior(self, def_id, body_id, interior, kind);
-        }
-    }
-
-    // Tries to apply a fallback to `ty` if it is an unsolved variable.
-    //
-    // - Unconstrained ints are replaced with `i32`.
-    //
-    // - Unconstrained floats are replaced with with `f64`.
-    //
-    // - Non-numerics get replaced with `!` when `#![feature(never_type_fallback)]`
-    //   is enabled. Otherwise, they are replaced with `()`.
-    //
-    // Fallback becomes very dubious if we have encountered type-checking errors.
-    // In that case, fallback to Error.
-    // The return value indicates whether fallback has occurred.
-    fn fallback_if_possible(&self, ty: Ty<'tcx>, mode: FallbackMode) -> bool {
-        use rustc_middle::ty::error::UnconstrainedNumeric::Neither;
-        use rustc_middle::ty::error::UnconstrainedNumeric::{UnconstrainedFloat, UnconstrainedInt};
-
-        assert!(ty.is_ty_infer());
-        let fallback = match self.type_is_unconstrained_numeric(ty) {
-            _ if self.is_tainted_by_errors() => self.tcx().ty_error(),
-            UnconstrainedInt => self.tcx.types.i32,
-            UnconstrainedFloat => self.tcx.types.f64,
-            Neither if self.type_var_diverges(ty) => self.tcx.mk_diverging_default(),
-            Neither => {
-                // This type variable was created from the instantiation of an opaque
-                // type. The fact that we're attempting to perform fallback for it
-                // means that the function neither constrained it to a concrete
-                // type, nor to the opaque type itself.
-                //
-                // For example, in this code:
-                //
-                //```
-                // type MyType = impl Copy;
-                // fn defining_use() -> MyType { true }
-                // fn other_use() -> MyType { defining_use() }
-                // ```
-                //
-                // `defining_use` will constrain the instantiated inference
-                // variable to `bool`, while `other_use` will constrain
-                // the instantiated inference variable to `MyType`.
-                //
-                // When we process opaque types during writeback, we
-                // will handle cases like `other_use`, and not count
-                // them as defining usages
-                //
-                // However, we also need to handle cases like this:
-                //
-                // ```rust
-                // pub type Foo = impl Copy;
-                // fn produce() -> Option<Foo> {
-                //     None
-                //  }
-                //  ```
-                //
-                // In the above snippet, the inference variable created by
-                // instantiating `Option<Foo>` will be completely unconstrained.
-                // We treat this as a non-defining use by making the inference
-                // variable fall back to the opaque type itself.
-                if let FallbackMode::All = mode {
-                    if let Some(opaque_ty) = self.opaque_types_vars.borrow().get(ty) {
-                        debug!(
-                            "fallback_if_possible: falling back opaque type var {:?} to {:?}",
-                            ty, opaque_ty
-                        );
-                        *opaque_ty
-                    } else {
-                        return false;
-                    }
-                } else {
-                    return false;
-                }
-            }
-        };
-        debug!("fallback_if_possible: defaulting `{:?}` to `{:?}`", ty, fallback);
-        self.demand_eqtype(rustc_span::DUMMY_SP, ty, fallback);
-        true
-    }
-
-    fn select_all_obligations_or_error(&self) {
-        debug!("select_all_obligations_or_error");
-        if let Err(errors) = self.fulfillment_cx.borrow_mut().select_all_or_error(&self) {
-            self.report_fulfillment_errors(&errors, self.inh.body_id, false);
-        }
-    }
-
-    /// Select as many obligations as we can at present.
-    fn select_obligations_where_possible(
-        &self,
-        fallback_has_occurred: bool,
-        mutate_fullfillment_errors: impl Fn(&mut Vec<traits::FulfillmentError<'tcx>>),
-    ) {
-        let result = self.fulfillment_cx.borrow_mut().select_where_possible(self);
-        if let Err(mut errors) = result {
-            mutate_fullfillment_errors(&mut errors);
-            self.report_fulfillment_errors(&errors, self.inh.body_id, fallback_has_occurred);
-        }
-    }
-
-    /// For the overloaded place expressions (`*x`, `x[3]`), the trait
-    /// returns a type of `&T`, but the actual type we assign to the
-    /// *expression* is `T`. So this function just peels off the return
-    /// type by one layer to yield `T`.
-    fn make_overloaded_place_return_type(
-        &self,
-        method: MethodCallee<'tcx>,
-    ) -> ty::TypeAndMut<'tcx> {
-        // extract method return type, which will be &T;
-        let ret_ty = method.sig.output();
-
-        // method returns &T, but the type as visible to user is T, so deref
-        ret_ty.builtin_deref(true).unwrap()
-    }
-
-    fn check_method_argument_types(
-        &self,
-        sp: Span,
-        expr: &'tcx hir::Expr<'tcx>,
-        method: Result<MethodCallee<'tcx>, ()>,
-        args_no_rcvr: &'tcx [hir::Expr<'tcx>],
-        tuple_arguments: TupleArgumentsFlag,
-        expected: Expectation<'tcx>,
-    ) -> Ty<'tcx> {
-        let has_error = match method {
-            Ok(method) => method.substs.references_error() || method.sig.references_error(),
-            Err(_) => true,
-        };
-        if has_error {
-            let err_inputs = self.err_args(args_no_rcvr.len());
-
-            let err_inputs = match tuple_arguments {
-                DontTupleArguments => err_inputs,
-                TupleArguments => vec![self.tcx.intern_tup(&err_inputs[..])],
-            };
-
-            self.check_argument_types(
-                sp,
-                expr,
-                &err_inputs[..],
-                &[],
-                args_no_rcvr,
-                false,
-                tuple_arguments,
-                None,
-            );
-            return self.tcx.ty_error();
-        }
-
-        let method = method.unwrap();
-        // HACK(eddyb) ignore self in the definition (see above).
-        let expected_arg_tys = self.expected_inputs_for_expected_output(
-            sp,
-            expected,
-            method.sig.output(),
-            &method.sig.inputs()[1..],
-        );
-        self.check_argument_types(
-            sp,
-            expr,
-            &method.sig.inputs()[1..],
-            &expected_arg_tys[..],
-            args_no_rcvr,
-            method.sig.c_variadic,
-            tuple_arguments,
-            self.tcx.hir().span_if_local(method.def_id),
-        );
-        method.sig.output()
-    }
-
-    fn self_type_matches_expected_vid(
-        &self,
-        trait_ref: ty::PolyTraitRef<'tcx>,
-        expected_vid: ty::TyVid,
-    ) -> bool {
-        let self_ty = self.shallow_resolve(trait_ref.skip_binder().self_ty());
-        debug!(
-            "self_type_matches_expected_vid(trait_ref={:?}, self_ty={:?}, expected_vid={:?})",
-            trait_ref, self_ty, expected_vid
-        );
-        match self_ty.kind {
-            ty::Infer(ty::TyVar(found_vid)) => {
-                // FIXME: consider using `sub_root_var` here so we
-                // can see through subtyping.
-                let found_vid = self.root_var(found_vid);
-                debug!("self_type_matches_expected_vid - found_vid={:?}", found_vid);
-                expected_vid == found_vid
-            }
-            _ => false,
-        }
-    }
-
-    fn obligations_for_self_ty<'b>(
-        &'b self,
-        self_ty: ty::TyVid,
-    ) -> impl Iterator<Item = (ty::PolyTraitRef<'tcx>, traits::PredicateObligation<'tcx>)>
-    + Captures<'tcx>
-    + 'b {
-        // FIXME: consider using `sub_root_var` here so we
-        // can see through subtyping.
-        let ty_var_root = self.root_var(self_ty);
-        debug!(
-            "obligations_for_self_ty: self_ty={:?} ty_var_root={:?} pending_obligations={:?}",
-            self_ty,
-            ty_var_root,
-            self.fulfillment_cx.borrow().pending_obligations()
-        );
-
-        self.fulfillment_cx
-            .borrow()
-            .pending_obligations()
-            .into_iter()
-            .filter_map(move |obligation| {
-                match obligation.predicate.skip_binders() {
-                    ty::PredicateAtom::Projection(data) => {
-                        Some((ty::Binder::bind(data).to_poly_trait_ref(self.tcx), obligation))
-                    }
-                    ty::PredicateAtom::Trait(data, _) => {
-                        Some((ty::Binder::bind(data).to_poly_trait_ref(), obligation))
-                    }
-                    ty::PredicateAtom::Subtype(..) => None,
-                    ty::PredicateAtom::RegionOutlives(..) => None,
-                    ty::PredicateAtom::TypeOutlives(..) => None,
-                    ty::PredicateAtom::WellFormed(..) => None,
-                    ty::PredicateAtom::ObjectSafe(..) => None,
-                    ty::PredicateAtom::ConstEvaluatable(..) => None,
-                    ty::PredicateAtom::ConstEquate(..) => None,
-                    // N.B., this predicate is created by breaking down a
-                    // `ClosureType: FnFoo()` predicate, where
-                    // `ClosureType` represents some `Closure`. It can't
-                    // possibly be referring to the current closure,
-                    // because we haven't produced the `Closure` for
-                    // this closure yet; this is exactly why the other
-                    // code is looking for a self type of a unresolved
-                    // inference variable.
-                    ty::PredicateAtom::ClosureKind(..) => None,
-                }
-            })
-            .filter(move |(tr, _)| self.self_type_matches_expected_vid(*tr, ty_var_root))
-    }
-
-    fn type_var_is_sized(&self, self_ty: ty::TyVid) -> bool {
-        self.obligations_for_self_ty(self_ty)
-            .any(|(tr, _)| Some(tr.def_id()) == self.tcx.lang_items().sized_trait())
-    }
-
-    /// Generic function that factors out common logic from function calls,
-    /// method calls and overloaded operators.
-    fn check_argument_types(
-        &self,
-        sp: Span,
-        expr: &'tcx hir::Expr<'tcx>,
-        fn_inputs: &[Ty<'tcx>],
-        expected_arg_tys: &[Ty<'tcx>],
-        args: &'tcx [hir::Expr<'tcx>],
-        c_variadic: bool,
-        tuple_arguments: TupleArgumentsFlag,
-        def_span: Option<Span>,
-    ) {
-        let tcx = self.tcx;
-        // Grab the argument types, supplying fresh type variables
-        // if the wrong number of arguments were supplied
-        let supplied_arg_count = if tuple_arguments == DontTupleArguments { args.len() } else { 1 };
-
-        // All the input types from the fn signature must outlive the call
-        // so as to validate implied bounds.
-        for (&fn_input_ty, arg_expr) in fn_inputs.iter().zip(args.iter()) {
-            self.register_wf_obligation(fn_input_ty.into(), arg_expr.span, traits::MiscObligation);
-        }
-
-        let expected_arg_count = fn_inputs.len();
-
-        let param_count_error = |expected_count: usize,
-                                 arg_count: usize,
-                                 error_code: &str,
-                                 c_variadic: bool,
-                                 sugg_unit: bool| {
-            let (span, start_span, args) = match &expr.kind {
-                hir::ExprKind::Call(hir::Expr { span, .. }, args) => (*span, *span, &args[..]),
-                hir::ExprKind::MethodCall(path_segment, span, args, _) => (
-                    *span,
-                    // `sp` doesn't point at the whole `foo.bar()`, only at `bar`.
-                    path_segment
-                        .args
-                        .and_then(|args| args.args.iter().last())
-                        // Account for `foo.bar::<T>()`.
-                        .map(|arg| {
-                            // Skip the closing `>`.
-                            tcx.sess
-                                .source_map()
-                                .next_point(tcx.sess.source_map().next_point(arg.span()))
-                        })
-                        .unwrap_or(*span),
-                    &args[1..], // Skip the receiver.
-                ),
-                k => span_bug!(sp, "checking argument types on a non-call: `{:?}`", k),
-            };
-            let arg_spans = if args.is_empty() {
-                // foo()
-                // ^^^-- supplied 0 arguments
-                // |
-                // expected 2 arguments
-                vec![tcx.sess.source_map().next_point(start_span).with_hi(sp.hi())]
-            } else {
-                // foo(1, 2, 3)
-                // ^^^ -  -  - supplied 3 arguments
-                // |
-                // expected 2 arguments
-                args.iter().map(|arg| arg.span).collect::<Vec<Span>>()
-            };
-
-            let mut err = tcx.sess.struct_span_err_with_code(
-                span,
-                &format!(
-                    "this function takes {}{} but {} {} supplied",
-                    if c_variadic { "at least " } else { "" },
-                    potentially_plural_count(expected_count, "argument"),
-                    potentially_plural_count(arg_count, "argument"),
-                    if arg_count == 1 { "was" } else { "were" }
-                ),
-                DiagnosticId::Error(error_code.to_owned()),
-            );
-            let label = format!("supplied {}", potentially_plural_count(arg_count, "argument"));
-            for (i, span) in arg_spans.into_iter().enumerate() {
-                err.span_label(
-                    span,
-                    if arg_count == 0 || i + 1 == arg_count { &label } else { "" },
-                );
-            }
-
-            if let Some(def_s) = def_span.map(|sp| tcx.sess.source_map().guess_head_span(sp)) {
-                err.span_label(def_s, "defined here");
-            }
-            if sugg_unit {
-                let sugg_span = tcx.sess.source_map().end_point(expr.span);
-                // remove closing `)` from the span
-                let sugg_span = sugg_span.shrink_to_lo();
-                err.span_suggestion(
-                    sugg_span,
-                    "expected the unit value `()`; create it with empty parentheses",
-                    String::from("()"),
-                    Applicability::MachineApplicable,
-                );
-            } else {
-                err.span_label(
-                    span,
-                    format!(
-                        "expected {}{}",
-                        if c_variadic { "at least " } else { "" },
-                        potentially_plural_count(expected_count, "argument")
-                    ),
-                );
-            }
-            err.emit();
-        };
-
-        let mut expected_arg_tys = expected_arg_tys.to_vec();
-
-        let formal_tys = if tuple_arguments == TupleArguments {
-            let tuple_type = self.structurally_resolved_type(sp, fn_inputs[0]);
-            match tuple_type.kind {
-                ty::Tuple(arg_types) if arg_types.len() != args.len() => {
-                    param_count_error(arg_types.len(), args.len(), "E0057", false, false);
-                    expected_arg_tys = vec![];
-                    self.err_args(args.len())
-                }
-                ty::Tuple(arg_types) => {
-                    expected_arg_tys = match expected_arg_tys.get(0) {
-                        Some(&ty) => match ty.kind {
-                            ty::Tuple(ref tys) => tys.iter().map(|k| k.expect_ty()).collect(),
-                            _ => vec![],
-                        },
-                        None => vec![],
-                    };
-                    arg_types.iter().map(|k| k.expect_ty()).collect()
-                }
-                _ => {
-                    struct_span_err!(
-                        tcx.sess,
-                        sp,
-                        E0059,
-                        "cannot use call notation; the first type parameter \
-                         for the function trait is neither a tuple nor unit"
-                    )
-                    .emit();
-                    expected_arg_tys = vec![];
-                    self.err_args(args.len())
-                }
-            }
-        } else if expected_arg_count == supplied_arg_count {
-            fn_inputs.to_vec()
-        } else if c_variadic {
-            if supplied_arg_count >= expected_arg_count {
-                fn_inputs.to_vec()
-            } else {
-                param_count_error(expected_arg_count, supplied_arg_count, "E0060", true, false);
-                expected_arg_tys = vec![];
-                self.err_args(supplied_arg_count)
-            }
-        } else {
-            // is the missing argument of type `()`?
-            let sugg_unit = if expected_arg_tys.len() == 1 && supplied_arg_count == 0 {
-                self.resolve_vars_if_possible(&expected_arg_tys[0]).is_unit()
-            } else if fn_inputs.len() == 1 && supplied_arg_count == 0 {
-                self.resolve_vars_if_possible(&fn_inputs[0]).is_unit()
-            } else {
-                false
-            };
-            param_count_error(expected_arg_count, supplied_arg_count, "E0061", false, sugg_unit);
-
-            expected_arg_tys = vec![];
-            self.err_args(supplied_arg_count)
-        };
-
-        debug!(
-            "check_argument_types: formal_tys={:?}",
-            formal_tys.iter().map(|t| self.ty_to_string(*t)).collect::<Vec<String>>()
-        );
-
-        // If there is no expectation, expect formal_tys.
-        let expected_arg_tys =
-            if !expected_arg_tys.is_empty() { expected_arg_tys } else { formal_tys.clone() };
-
-        let mut final_arg_types: Vec<(usize, Ty<'_>, Ty<'_>)> = vec![];
-
-        // Check the arguments.
-        // We do this in a pretty awful way: first we type-check any arguments
-        // that are not closures, then we type-check the closures. This is so
-        // that we have more information about the types of arguments when we
-        // type-check the functions. This isn't really the right way to do this.
-        for &check_closures in &[false, true] {
-            debug!("check_closures={}", check_closures);
-
-            // More awful hacks: before we check argument types, try to do
-            // an "opportunistic" trait resolution of any trait bounds on
-            // the call. This helps coercions.
-            if check_closures {
-                self.select_obligations_where_possible(false, |errors| {
-                    self.point_at_type_arg_instead_of_call_if_possible(errors, expr);
-                    self.point_at_arg_instead_of_call_if_possible(
-                        errors,
-                        &final_arg_types[..],
-                        sp,
-                        &args,
-                    );
-                })
-            }
-
-            // For C-variadic functions, we don't have a declared type for all of
-            // the arguments hence we only do our usual type checking with
-            // the arguments who's types we do know.
-            let t = if c_variadic {
-                expected_arg_count
-            } else if tuple_arguments == TupleArguments {
-                args.len()
-            } else {
-                supplied_arg_count
-            };
-            for (i, arg) in args.iter().take(t).enumerate() {
-                // Warn only for the first loop (the "no closures" one).
-                // Closure arguments themselves can't be diverging, but
-                // a previous argument can, e.g., `foo(panic!(), || {})`.
-                if !check_closures {
-                    self.warn_if_unreachable(arg.hir_id, arg.span, "expression");
-                }
-
-                let is_closure = match arg.kind {
-                    ExprKind::Closure(..) => true,
-                    _ => false,
-                };
-
-                if is_closure != check_closures {
-                    continue;
-                }
-
-                debug!("checking the argument");
-                let formal_ty = formal_tys[i];
-
-                // The special-cased logic below has three functions:
-                // 1. Provide as good of an expected type as possible.
-                let expected = Expectation::rvalue_hint(self, expected_arg_tys[i]);
-
-                let checked_ty = self.check_expr_with_expectation(&arg, expected);
-
-                // 2. Coerce to the most detailed type that could be coerced
-                //    to, which is `expected_ty` if `rvalue_hint` returns an
-                //    `ExpectHasType(expected_ty)`, or the `formal_ty` otherwise.
-                let coerce_ty = expected.only_has_type(self).unwrap_or(formal_ty);
-                // We're processing function arguments so we definitely want to use
-                // two-phase borrows.
-                self.demand_coerce(&arg, checked_ty, coerce_ty, None, AllowTwoPhase::Yes);
-                final_arg_types.push((i, checked_ty, coerce_ty));
-
-                // 3. Relate the expected type and the formal one,
-                //    if the expected type was used for the coercion.
-                self.demand_suptype(arg.span, formal_ty, coerce_ty);
-            }
-        }
-
-        // We also need to make sure we at least write the ty of the other
-        // arguments which we skipped above.
-        if c_variadic {
-            fn variadic_error<'tcx>(s: &Session, span: Span, t: Ty<'tcx>, cast_ty: &str) {
-                use crate::structured_errors::{StructuredDiagnostic, VariadicError};
-                VariadicError::new(s, span, t, cast_ty).diagnostic().emit();
-            }
-
-            for arg in args.iter().skip(expected_arg_count) {
-                let arg_ty = self.check_expr(&arg);
-
-                // 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);
-                match arg_ty.kind {
-                    ty::Float(ast::FloatTy::F32) => {
-                        variadic_error(tcx.sess, arg.span, arg_ty, "c_double");
-                    }
-                    ty::Int(ast::IntTy::I8 | ast::IntTy::I16) | ty::Bool => {
-                        variadic_error(tcx.sess, arg.span, arg_ty, "c_int");
-                    }
-                    ty::Uint(ast::UintTy::U8 | ast::UintTy::U16) => {
-                        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 = self.resolve_vars_if_possible(&ptr_ty);
-                        variadic_error(tcx.sess, arg.span, arg_ty, &ptr_ty.to_string());
-                    }
-                    _ => {}
-                }
-            }
-        }
-    }
-
-    fn err_args(&self, len: usize) -> Vec<Ty<'tcx>> {
-        vec![self.tcx.ty_error(); len]
-    }
-
-    /// Given a vec of evaluated `FulfillmentError`s and an `fn` call argument expressions, we walk
-    /// the checked and coerced types for each argument to see if any of the `FulfillmentError`s
-    /// reference a type argument. The reason to walk also the checked type is that the coerced type
-    /// can be not easily comparable with predicate type (because of coercion). If the types match
-    /// for either checked or coerced type, and there's only *one* argument that does, we point at
-    /// the corresponding argument's expression span instead of the `fn` call path span.
-    fn point_at_arg_instead_of_call_if_possible(
-        &self,
-        errors: &mut Vec<traits::FulfillmentError<'tcx>>,
-        final_arg_types: &[(usize, Ty<'tcx>, Ty<'tcx>)],
-        call_sp: Span,
-        args: &'tcx [hir::Expr<'tcx>],
-    ) {
-        // We *do not* do this for desugared call spans to keep good diagnostics when involving
-        // the `?` operator.
-        if call_sp.desugaring_kind().is_some() {
-            return;
-        }
-
-        for error in errors {
-            // Only if the cause is somewhere inside the expression we want try to point at arg.
-            // Otherwise, it means that the cause is somewhere else and we should not change
-            // anything because we can break the correct span.
-            if !call_sp.contains(error.obligation.cause.span) {
-                continue;
-            }
-
-            if let ty::PredicateAtom::Trait(predicate, _) =
-                error.obligation.predicate.skip_binders()
-            {
-                // Collect the argument position for all arguments that could have caused this
-                // `FulfillmentError`.
-                let mut referenced_in = final_arg_types
-                    .iter()
-                    .map(|&(i, checked_ty, _)| (i, checked_ty))
-                    .chain(final_arg_types.iter().map(|&(i, _, coerced_ty)| (i, coerced_ty)))
-                    .flat_map(|(i, ty)| {
-                        let ty = self.resolve_vars_if_possible(&ty);
-                        // We walk the argument type because the argument's type could have
-                        // been `Option<T>`, but the `FulfillmentError` references `T`.
-                        if ty.walk().any(|arg| arg == predicate.self_ty().into()) {
-                            Some(i)
-                        } else {
-                            None
-                        }
-                    })
-                    .collect::<Vec<_>>();
-
-                // Both checked and coerced types could have matched, thus we need to remove
-                // duplicates.
-                referenced_in.sort();
-                referenced_in.dedup();
-
-                if let (Some(ref_in), None) = (referenced_in.pop(), referenced_in.pop()) {
-                    // We make sure that only *one* argument matches the obligation failure
-                    // and we assign the obligation's span to its expression's.
-                    error.obligation.cause.make_mut().span = args[ref_in].span;
-                    error.points_at_arg_span = true;
-                }
-            }
-        }
-    }
-
-    /// Given a vec of evaluated `FulfillmentError`s and an `fn` call expression, we walk the
-    /// `PathSegment`s and resolve their type parameters to see if any of the `FulfillmentError`s
-    /// were caused by them. If they were, we point at the corresponding type argument's span
-    /// instead of the `fn` call path span.
-    fn point_at_type_arg_instead_of_call_if_possible(
-        &self,
-        errors: &mut Vec<traits::FulfillmentError<'tcx>>,
-        call_expr: &'tcx hir::Expr<'tcx>,
-    ) {
-        if let hir::ExprKind::Call(path, _) = &call_expr.kind {
-            if let hir::ExprKind::Path(qpath) = &path.kind {
-                if let hir::QPath::Resolved(_, path) = &qpath {
-                    for error in errors {
-                        if let ty::PredicateAtom::Trait(predicate, _) =
-                            error.obligation.predicate.skip_binders()
-                        {
-                            // If any of the type arguments in this path segment caused the
-                            // `FullfillmentError`, point at its span (#61860).
-                            for arg in path
-                                .segments
-                                .iter()
-                                .filter_map(|seg| seg.args.as_ref())
-                                .flat_map(|a| a.args.iter())
-                            {
-                                if let hir::GenericArg::Type(hir_ty) = &arg {
-                                    if let hir::TyKind::Path(hir::QPath::TypeRelative(..)) =
-                                        &hir_ty.kind
-                                    {
-                                        // Avoid ICE with associated types. As this is best
-                                        // effort only, it's ok to ignore the case. It
-                                        // would trigger in `is_send::<T::AssocType>();`
-                                        // from `typeck-default-trait-impl-assoc-type.rs`.
-                                    } else {
-                                        let ty = AstConv::ast_ty_to_ty(self, hir_ty);
-                                        let ty = self.resolve_vars_if_possible(&ty);
-                                        if ty == predicate.self_ty() {
-                                            error.obligation.cause.make_mut().span = hir_ty.span;
-                                        }
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    // AST fragment checking
-    fn check_lit(&self, lit: &hir::Lit, expected: Expectation<'tcx>) -> Ty<'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::Byte(_) => tcx.types.u8,
-            ast::LitKind::Char(_) => tcx.types.char,
-            ast::LitKind::Int(_, ast::LitIntType::Signed(t)) => tcx.mk_mach_int(t),
-            ast::LitKind::Int(_, ast::LitIntType::Unsigned(t)) => tcx.mk_mach_uint(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),
-                    ty::Char => Some(tcx.types.u8),
-                    ty::RawPtr(..) => Some(tcx.types.usize),
-                    ty::FnDef(..) | ty::FnPtr(_) => Some(tcx.types.usize),
-                    _ => None,
-                });
-                opt_ty.unwrap_or_else(|| self.next_int_var())
-            }
-            ast::LitKind::Float(_, ast::LitFloatType::Suffixed(t)) => tcx.mk_mach_float(t),
-            ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) => {
-                let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind {
-                    ty::Float(_) => Some(ty),
-                    _ => None,
-                });
-                opt_ty.unwrap_or_else(|| self.next_float_var())
-            }
-            ast::LitKind::Bool(_) => tcx.types.bool,
-            ast::LitKind::Err(_) => tcx.ty_error(),
-        }
-    }
-
-    /// Unifies the output type with the expected type early, for more coercions
-    /// and forward type information on the input expressions.
-    fn expected_inputs_for_expected_output(
-        &self,
-        call_span: Span,
-        expected_ret: Expectation<'tcx>,
-        formal_ret: Ty<'tcx>,
-        formal_args: &[Ty<'tcx>],
-    ) -> Vec<Ty<'tcx>> {
-        let formal_ret = self.resolve_vars_with_obligations(formal_ret);
-        let ret_ty = match expected_ret.only_has_type(self) {
-            Some(ret) => ret,
-            None => return Vec::new(),
-        };
-        let expect_args = self
-            .fudge_inference_if_ok(|| {
-                // Attempt to apply a subtyping relationship between the formal
-                // return type (likely containing type variables if the function
-                // is polymorphic) and the expected return type.
-                // No argument expectations are produced if unification fails.
-                let origin = self.misc(call_span);
-                let ures = self.at(&origin, self.param_env).sup(ret_ty, &formal_ret);
-
-                // FIXME(#27336) can't use ? here, Try::from_error doesn't default
-                // to identity so the resulting type is not constrained.
-                match ures {
-                    Ok(ok) => {
-                        // Process any obligations locally as much as
-                        // we can.  We don't care if some things turn
-                        // out unconstrained or ambiguous, as we're
-                        // just trying to get hints here.
-                        self.save_and_restore_in_snapshot_flag(|_| {
-                            let mut fulfill = TraitEngine::new(self.tcx);
-                            for obligation in ok.obligations {
-                                fulfill.register_predicate_obligation(self, obligation);
-                            }
-                            fulfill.select_where_possible(self)
-                        })
-                        .map_err(|_| ())?;
-                    }
-                    Err(_) => return Err(()),
-                }
-
-                // Record all the argument types, with the substitutions
-                // produced from the above subtyping unification.
-                Ok(formal_args.iter().map(|ty| self.resolve_vars_if_possible(ty)).collect())
-            })
-            .unwrap_or_default();
-        debug!(
-            "expected_inputs_for_expected_output(formal={:?} -> {:?}, expected={:?} -> {:?})",
-            formal_args, formal_ret, expect_args, expected_ret
-        );
-        expect_args
-    }
-
-    pub fn check_struct_path(
-        &self,
-        qpath: &QPath<'_>,
-        hir_id: hir::HirId,
-    ) -> Option<(&'tcx ty::VariantDef, Ty<'tcx>)> {
-        let path_span = qpath.qself_span();
-        let (def, ty) = self.finish_resolving_struct_path(qpath, path_span, hir_id);
-        let variant = match def {
-            Res::Err => {
-                self.set_tainted_by_errors();
-                return None;
-            }
-            Res::Def(DefKind::Variant, _) => match ty.kind {
-                ty::Adt(adt, substs) => Some((adt.variant_of_res(def), adt.did, substs)),
-                _ => bug!("unexpected type: {:?}", ty),
-            },
-            Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _)
-            | Res::SelfTy(..) => match ty.kind {
-                ty::Adt(adt, substs) if !adt.is_enum() => {
-                    Some((adt.non_enum_variant(), adt.did, substs))
-                }
-                _ => None,
-            },
-            _ => bug!("unexpected definition: {:?}", def),
-        };
-
-        if let Some((variant, did, substs)) = variant {
-            debug!("check_struct_path: did={:?} substs={:?}", did, substs);
-            self.write_user_type_annotation_from_substs(hir_id, did, substs, None);
-
-            // Check bounds on type arguments used in the path.
-            let (bounds, _) = self.instantiate_bounds(path_span, did, substs);
-            let cause =
-                traits::ObligationCause::new(path_span, self.body_id, traits::ItemObligation(did));
-            self.add_obligations_for_parameters(cause, bounds);
-
-            Some((variant, ty))
-        } else {
-            struct_span_err!(
-                self.tcx.sess,
-                path_span,
-                E0071,
-                "expected struct, variant or union type, found {}",
-                ty.sort_string(self.tcx)
-            )
-            .span_label(path_span, "not a struct")
-            .emit();
-            None
-        }
-    }
-
-    // Finish resolving a path in a struct expression or pattern `S::A { .. }` if necessary.
-    // The newly resolved definition is written into `type_dependent_defs`.
-    fn finish_resolving_struct_path(
-        &self,
-        qpath: &QPath<'_>,
-        path_span: Span,
-        hir_id: hir::HirId,
-    ) -> (Res, Ty<'tcx>) {
-        match *qpath {
-            QPath::Resolved(ref maybe_qself, ref path) => {
-                let self_ty = maybe_qself.as_ref().map(|qself| self.to_ty(qself));
-                let ty = AstConv::res_to_ty(self, self_ty, path, true);
-                (path.res, ty)
-            }
-            QPath::TypeRelative(ref qself, ref segment) => {
-                let ty = self.to_ty(qself);
-
-                let res = if let hir::TyKind::Path(QPath::Resolved(_, ref path)) = qself.kind {
-                    path.res
-                } else {
-                    Res::Err
-                };
-                let result =
-                    AstConv::associated_path_to_ty(self, hir_id, path_span, ty, res, segment, true);
-                let ty = result.map(|(ty, _, _)| ty).unwrap_or_else(|_| self.tcx().ty_error());
-                let result = result.map(|(_, kind, def_id)| (kind, def_id));
-
-                // Write back the new resolution.
-                self.write_resolution(hir_id, result);
-
-                (result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err), ty)
-            }
-            QPath::LangItem(lang_item, span) => {
-                self.resolve_lang_item_path(lang_item, span, hir_id)
-            }
-        }
-    }
-
-    fn resolve_lang_item_path(
-        &self,
-        lang_item: hir::LangItem,
-        span: Span,
-        hir_id: hir::HirId,
-    ) -> (Res, Ty<'tcx>) {
-        let def_id = self.tcx.require_lang_item(lang_item, Some(span));
-        let def_kind = self.tcx.def_kind(def_id);
-
-        let item_ty = if let DefKind::Variant = def_kind {
-            self.tcx.type_of(self.tcx.parent(def_id).expect("variant w/out parent"))
-        } else {
-            self.tcx.type_of(def_id)
-        };
-        let substs = self.infcx.fresh_substs_for_item(span, def_id);
-        let ty = item_ty.subst(self.tcx, substs);
-
-        self.write_resolution(hir_id, Ok((def_kind, def_id)));
-        self.add_required_obligations(span, def_id, &substs);
-        (Res::Def(def_kind, def_id), ty)
-    }
-
-    /// Resolves an associated value path into a base type and associated constant, or method
-    /// resolution. The newly resolved definition is written into `type_dependent_defs`.
-    pub fn resolve_ty_and_res_ufcs<'b>(
-        &self,
-        qpath: &'b QPath<'b>,
-        hir_id: hir::HirId,
-        span: Span,
-    ) -> (Res, Option<Ty<'tcx>>, &'b [hir::PathSegment<'b>]) {
-        debug!("resolve_ty_and_res_ufcs: qpath={:?} hir_id={:?} span={:?}", qpath, hir_id, span);
-        let (ty, qself, item_segment) = match *qpath {
-            QPath::Resolved(ref opt_qself, ref path) => {
-                return (
-                    path.res,
-                    opt_qself.as_ref().map(|qself| self.to_ty(qself)),
-                    &path.segments[..],
-                );
-            }
-            QPath::TypeRelative(ref qself, ref segment) => (self.to_ty(qself), qself, segment),
-            QPath::LangItem(..) => bug!("`resolve_ty_and_res_ufcs` called on `LangItem`"),
-        };
-        if let Some(&cached_result) = self.typeck_results.borrow().type_dependent_defs().get(hir_id)
-        {
-            // Return directly on cache hit. This is useful to avoid doubly reporting
-            // errors with default match binding modes. See #44614.
-            let def =
-                cached_result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err);
-            return (def, Some(ty), slice::from_ref(&**item_segment));
-        }
-        let item_name = item_segment.ident;
-        let result = self.resolve_ufcs(span, item_name, ty, hir_id).or_else(|error| {
-            let result = match error {
-                method::MethodError::PrivateMatch(kind, def_id, _) => Ok((kind, def_id)),
-                _ => Err(ErrorReported),
-            };
-            if item_name.name != kw::Invalid {
-                if let Some(mut e) = self.report_method_error(
-                    span,
-                    ty,
-                    item_name,
-                    SelfSource::QPath(qself),
-                    error,
-                    None,
-                ) {
-                    e.emit();
-                }
-            }
-            result
-        });
-
-        // Write back the new resolution.
-        self.write_resolution(hir_id, result);
-        (
-            result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err),
-            Some(ty),
-            slice::from_ref(&**item_segment),
-        )
-    }
-
-    pub fn check_decl_initializer(
-        &self,
-        local: &'tcx hir::Local<'tcx>,
-        init: &'tcx hir::Expr<'tcx>,
-    ) -> Ty<'tcx> {
-        // FIXME(tschottdorf): `contains_explicit_ref_binding()` must be removed
-        // for #42640 (default match binding modes).
-        //
-        // See #44848.
-        let ref_bindings = local.pat.contains_explicit_ref_binding();
-
-        let local_ty = self.local_ty(init.span, local.hir_id).revealed_ty;
-        if let Some(m) = ref_bindings {
-            // Somewhat subtle: if we have a `ref` binding in the pattern,
-            // we want to avoid introducing coercions for the RHS. This is
-            // both because it helps preserve sanity and, in the case of
-            // ref mut, for soundness (issue #23116). In particular, in
-            // the latter case, we need to be clear that the type of the
-            // referent for the reference that results is *equal to* the
-            // type of the place it is referencing, and not some
-            // supertype thereof.
-            let init_ty = self.check_expr_with_needs(init, Needs::maybe_mut_place(m));
-            self.demand_eqtype(init.span, local_ty, init_ty);
-            init_ty
-        } else {
-            self.check_expr_coercable_to_type(init, local_ty, None)
-        }
-    }
-
-    /// Type check a `let` statement.
-    pub fn check_decl_local(&self, local: &'tcx hir::Local<'tcx>) {
-        // Determine and write the type which we'll check the pattern against.
-        let ty = self.local_ty(local.span, local.hir_id).decl_ty;
-        self.write_ty(local.hir_id, ty);
-
-        // Type check the initializer.
-        if let Some(ref init) = local.init {
-            let init_ty = self.check_decl_initializer(local, &init);
-            self.overwrite_local_ty_if_err(local, ty, init_ty);
-        }
-
-        // Does the expected pattern type originate from an expression and what is the span?
-        let (origin_expr, ty_span) = match (local.ty, local.init) {
-            (Some(ty), _) => (false, Some(ty.span)), // Bias towards the explicit user type.
-            (_, Some(init)) => (true, Some(init.span)), // No explicit type; so use the scrutinee.
-            _ => (false, None), // We have `let $pat;`, so the expected type is unconstrained.
-        };
-
-        // Type check the pattern. Override if necessary to avoid knock-on errors.
-        self.check_pat_top(&local.pat, ty, ty_span, origin_expr);
-        let pat_ty = self.node_ty(local.pat.hir_id);
-        self.overwrite_local_ty_if_err(local, ty, pat_ty);
-    }
-
-    fn overwrite_local_ty_if_err(
-        &self,
-        local: &'tcx hir::Local<'tcx>,
-        decl_ty: Ty<'tcx>,
-        ty: Ty<'tcx>,
-    ) {
-        if ty.references_error() {
-            // Override the types everywhere with `err()` to avoid knock on errors.
-            self.write_ty(local.hir_id, ty);
-            self.write_ty(local.pat.hir_id, ty);
-            let local_ty = LocalTy { decl_ty, revealed_ty: ty };
-            self.locals.borrow_mut().insert(local.hir_id, local_ty);
-            self.locals.borrow_mut().insert(local.pat.hir_id, local_ty);
-        }
-    }
-
-    fn suggest_semicolon_at_end(&self, span: Span, err: &mut DiagnosticBuilder<'_>) {
-        err.span_suggestion_short(
-            span.shrink_to_hi(),
-            "consider using a semicolon here",
-            ";".to_string(),
-            Applicability::MachineApplicable,
-        );
-    }
-
-    pub fn check_stmt(&self, stmt: &'tcx hir::Stmt<'tcx>) {
-        // Don't do all the complex logic below for `DeclItem`.
-        match stmt.kind {
-            hir::StmtKind::Item(..) => return,
-            hir::StmtKind::Local(..) | hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => {}
-        }
-
-        self.warn_if_unreachable(stmt.hir_id, stmt.span, "statement");
-
-        // Hide the outer diverging and `has_errors` flags.
-        let old_diverges = self.diverges.replace(Diverges::Maybe);
-        let old_has_errors = self.has_errors.replace(false);
-
-        match stmt.kind {
-            hir::StmtKind::Local(ref l) => {
-                self.check_decl_local(&l);
-            }
-            // Ignore for now.
-            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.suggest_semicolon_at_end(expr.span, err);
-                });
-            }
-            hir::StmtKind::Semi(ref expr) => {
-                self.check_expr(&expr);
-            }
-        }
-
-        // Combine the diverging and `has_error` flags.
-        self.diverges.set(self.diverges.get() | old_diverges);
-        self.has_errors.set(self.has_errors.get() | old_has_errors);
-    }
-
-    pub fn check_block_no_value(&self, blk: &'tcx hir::Block<'tcx>) {
-        let unit = self.tcx.mk_unit();
-        let ty = self.check_block_with_expected(blk, ExpectHasType(unit));
-
-        // if the block produces a `!` value, that can always be
-        // (effectively) coerced to unit.
-        if !ty.is_never() {
-            self.demand_suptype(blk.span, unit, ty);
-        }
-    }
-
-    /// If `expr` is a `match` expression that has only one non-`!` arm, use that arm's tail
-    /// expression's `Span`, otherwise return `expr.span`. This is done to give better errors
-    /// when given code like the following:
-    /// ```text
-    /// if false { return 0i32; } else { 1u32 }
-    /// //                               ^^^^ point at this instead of the whole `if` expression
-    /// ```
-    fn get_expr_coercion_span(&self, expr: &hir::Expr<'_>) -> rustc_span::Span {
-        if let hir::ExprKind::Match(_, arms, _) = &expr.kind {
-            let arm_spans: Vec<Span> = arms
-                .iter()
-                .filter_map(|arm| {
-                    self.in_progress_typeck_results
-                        .and_then(|typeck_results| {
-                            typeck_results.borrow().node_type_opt(arm.body.hir_id)
-                        })
-                        .and_then(|arm_ty| {
-                            if arm_ty.is_never() {
-                                None
-                            } else {
-                                Some(match &arm.body.kind {
-                                    // Point at the tail expression when possible.
-                                    hir::ExprKind::Block(block, _) => {
-                                        block.expr.as_ref().map(|e| e.span).unwrap_or(block.span)
-                                    }
-                                    _ => arm.body.span,
-                                })
-                            }
-                        })
-                })
-                .collect();
-            if arm_spans.len() == 1 {
-                return arm_spans[0];
-            }
-        }
-        expr.span
-    }
-
-    fn check_block_with_expected(
-        &self,
-        blk: &'tcx hir::Block<'tcx>,
-        expected: Expectation<'tcx>,
-    ) -> Ty<'tcx> {
-        let prev = {
-            let mut fcx_ps = self.ps.borrow_mut();
-            let unsafety_state = fcx_ps.recurse(blk);
-            replace(&mut *fcx_ps, unsafety_state)
-        };
-
-        // In some cases, blocks have just one exit, but other blocks
-        // can be targeted by multiple breaks. This can happen both
-        // with labeled blocks as well as when we desugar
-        // a `try { ... }` expression.
-        //
-        // Example 1:
-        //
-        //    'a: { if true { break 'a Err(()); } Ok(()) }
-        //
-        // Here we would wind up with two coercions, one from
-        // `Err(())` and the other from the tail expression
-        // `Ok(())`. If the tail expression is omitted, that's a
-        // "forced unit" -- unless the block diverges, in which
-        // case we can ignore the tail expression (e.g., `'a: {
-        // break 'a 22; }` would not force the type of the block
-        // to be `()`).
-        let tail_expr = blk.expr.as_ref();
-        let coerce_to_ty = expected.coercion_target_type(self, blk.span);
-        let coerce = if blk.targeted_by_break {
-            CoerceMany::new(coerce_to_ty)
-        } else {
-            let tail_expr: &[&hir::Expr<'_>] = match tail_expr {
-                Some(e) => slice::from_ref(e),
-                None => &[],
-            };
-            CoerceMany::with_coercion_sites(coerce_to_ty, tail_expr)
-        };
-
-        let prev_diverges = self.diverges.get();
-        let ctxt = BreakableCtxt { coerce: Some(coerce), may_break: false };
-
-        let (ctxt, ()) = self.with_breakable_ctxt(blk.hir_id, ctxt, || {
-            for s in blk.stmts {
-                self.check_stmt(s);
-            }
-
-            // check the tail expression **without** holding the
-            // `enclosing_breakables` lock below.
-            let tail_expr_ty = tail_expr.map(|t| self.check_expr_with_expectation(t, expected));
-
-            let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
-            let ctxt = enclosing_breakables.find_breakable(blk.hir_id);
-            let coerce = ctxt.coerce.as_mut().unwrap();
-            if let Some(tail_expr_ty) = tail_expr_ty {
-                let tail_expr = tail_expr.unwrap();
-                let span = self.get_expr_coercion_span(tail_expr);
-                let cause = self.cause(span, ObligationCauseCode::BlockTailExpression(blk.hir_id));
-                coerce.coerce(self, &cause, tail_expr, tail_expr_ty);
-            } else {
-                // Subtle: if there is no explicit tail expression,
-                // that is typically equivalent to a tail expression
-                // of `()` -- except if the block diverges. In that
-                // case, there is no value supplied from the tail
-                // expression (assuming there are no other breaks,
-                // this implies that the type of the block will be
-                // `!`).
-                //
-                // #41425 -- label the implicit `()` as being the
-                // "found type" here, rather than the "expected type".
-                if !self.diverges.get().is_always() {
-                    // #50009 -- Do not point at the entire fn block span, point at the return type
-                    // span, as it is the cause of the requirement, and
-                    // `consider_hint_about_removing_semicolon` will point at the last expression
-                    // if it were a relevant part of the error. This improves usability in editors
-                    // that highlight errors inline.
-                    let mut sp = blk.span;
-                    let mut fn_span = None;
-                    if let Some((decl, ident)) = self.get_parent_fn_decl(blk.hir_id) {
-                        let ret_sp = decl.output.span();
-                        if let Some(block_sp) = self.parent_item_span(blk.hir_id) {
-                            // HACK: on some cases (`ui/liveness/liveness-issue-2163.rs`) the
-                            // output would otherwise be incorrect and even misleading. Make sure
-                            // the span we're aiming at correspond to a `fn` body.
-                            if block_sp == blk.span {
-                                sp = ret_sp;
-                                fn_span = Some(ident.span);
-                            }
-                        }
-                    }
-                    coerce.coerce_forced_unit(
-                        self,
-                        &self.misc(sp),
-                        &mut |err| {
-                            if let Some(expected_ty) = expected.only_has_type(self) {
-                                self.consider_hint_about_removing_semicolon(blk, expected_ty, err);
-                            }
-                            if let Some(fn_span) = fn_span {
-                                err.span_label(
-                                    fn_span,
-                                    "implicitly returns `()` as its body has no tail or `return` \
-                                     expression",
-                                );
-                            }
-                        },
-                        false,
-                    );
-                }
-            }
-        });
-
-        if ctxt.may_break {
-            // If we can break from the block, then the block's exit is always reachable
-            // (... as long as the entry is reachable) - regardless of the tail of the block.
-            self.diverges.set(prev_diverges);
-        }
-
-        let mut ty = ctxt.coerce.unwrap().complete(self);
-
-        if self.has_errors.get() || ty.references_error() {
-            ty = self.tcx.ty_error()
-        }
-
-        self.write_ty(blk.hir_id, ty);
-
-        *self.ps.borrow_mut() = prev;
-        ty
-    }
-
-    fn parent_item_span(&self, id: hir::HirId) -> Option<Span> {
-        let node = self.tcx.hir().get(self.tcx.hir().get_parent_item(id));
-        match node {
-            Node::Item(&hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. })
-            | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(_, body_id), .. }) => {
-                let body = self.tcx.hir().body(body_id);
-                if let ExprKind::Block(block, _) = &body.value.kind {
-                    return Some(block.span);
-                }
-            }
-            _ => {}
-        }
-        None
-    }
-
-    /// Given a function block's `HirId`, returns its `FnDecl` if it exists, or `None` otherwise.
-    fn get_parent_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident)> {
-        let parent = self.tcx.hir().get(self.tcx.hir().get_parent_item(blk_id));
-        self.get_node_fn_decl(parent).map(|(fn_decl, ident, _)| (fn_decl, ident))
-    }
-
-    /// Given a function `Node`, return its `FnDecl` if it exists, or `None` otherwise.
-    fn get_node_fn_decl(&self, node: Node<'tcx>) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident, bool)> {
-        match node {
-            Node::Item(&hir::Item { ident, kind: hir::ItemKind::Fn(ref sig, ..), .. }) => {
-                // This is less than ideal, it will not suggest a return type span on any
-                // method called `main`, regardless of whether it is actually the entry point,
-                // but it will still present it as the reason for the expected type.
-                Some((&sig.decl, ident, ident.name != sym::main))
-            }
-            Node::TraitItem(&hir::TraitItem {
-                ident,
-                kind: hir::TraitItemKind::Fn(ref sig, ..),
-                ..
-            }) => Some((&sig.decl, ident, true)),
-            Node::ImplItem(&hir::ImplItem {
-                ident,
-                kind: hir::ImplItemKind::Fn(ref sig, ..),
-                ..
-            }) => Some((&sig.decl, ident, false)),
-            _ => None,
-        }
-    }
-
-    /// Given a `HirId`, return the `FnDecl` of the method it is enclosed by and whether a
-    /// suggestion can be made, `None` otherwise.
-    pub fn get_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, bool)> {
-        // Get enclosing Fn, if it is a function or a trait method, unless there's a `loop` or
-        // `while` before reaching it, as block tail returns are not available in them.
-        self.tcx.hir().get_return_block(blk_id).and_then(|blk_id| {
-            let parent = self.tcx.hir().get(blk_id);
-            self.get_node_fn_decl(parent).map(|(fn_decl, _, is_main)| (fn_decl, is_main))
-        })
-    }
-
-    /// On implicit return expressions with mismatched types, provides the following suggestions:
-    ///
-    /// - Points out the method's return type as the reason for the expected type.
-    /// - Possible missing semicolon.
-    /// - Possible missing return type if the return type is the default, and not `fn main()`.
-    pub fn suggest_mismatched_types_on_tail(
-        &self,
-        err: &mut DiagnosticBuilder<'_>,
-        expr: &'tcx hir::Expr<'tcx>,
-        expected: Ty<'tcx>,
-        found: Ty<'tcx>,
-        cause_span: Span,
-        blk_id: hir::HirId,
-    ) -> bool {
-        let expr = expr.peel_drop_temps();
-        self.suggest_missing_semicolon(err, expr, expected, cause_span);
-        let mut pointing_at_return_type = false;
-        if let Some((fn_decl, can_suggest)) = self.get_fn_decl(blk_id) {
-            pointing_at_return_type =
-                self.suggest_missing_return_type(err, &fn_decl, expected, found, can_suggest);
-        }
-        pointing_at_return_type
-    }
-
-    /// When encountering an fn-like ctor that needs to unify with a value, check whether calling
-    /// the ctor would successfully solve the type mismatch and if so, suggest it:
-    /// ```
-    /// fn foo(x: usize) -> usize { x }
-    /// let x: usize = foo;  // suggest calling the `foo` function: `foo(42)`
-    /// ```
-    fn suggest_fn_call(
-        &self,
-        err: &mut DiagnosticBuilder<'_>,
-        expr: &hir::Expr<'_>,
-        expected: Ty<'tcx>,
-        found: Ty<'tcx>,
-    ) -> bool {
-        let hir = self.tcx.hir();
-        let (def_id, sig) = match found.kind {
-            ty::FnDef(def_id, _) => (def_id, found.fn_sig(self.tcx)),
-            ty::Closure(def_id, substs) => (def_id, substs.as_closure().sig()),
-            _ => return false,
-        };
-
-        let sig = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, &sig).0;
-        let sig = self.normalize_associated_types_in(expr.span, &sig);
-        if self.can_coerce(sig.output(), expected) {
-            let (mut sugg_call, applicability) = if sig.inputs().is_empty() {
-                (String::new(), Applicability::MachineApplicable)
-            } else {
-                ("...".to_string(), Applicability::HasPlaceholders)
-            };
-            let mut msg = "call this function";
-            match hir.get_if_local(def_id) {
-                Some(
-                    Node::Item(hir::Item { kind: ItemKind::Fn(.., body_id), .. })
-                    | Node::ImplItem(hir::ImplItem {
-                        kind: hir::ImplItemKind::Fn(_, body_id), ..
-                    })
-                    | Node::TraitItem(hir::TraitItem {
-                        kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Provided(body_id)),
-                        ..
-                    }),
-                ) => {
-                    let body = hir.body(*body_id);
-                    sugg_call = body
-                        .params
-                        .iter()
-                        .map(|param| match &param.pat.kind {
-                            hir::PatKind::Binding(_, _, ident, None)
-                                if ident.name != kw::SelfLower =>
-                            {
-                                ident.to_string()
-                            }
-                            _ => "_".to_string(),
-                        })
-                        .collect::<Vec<_>>()
-                        .join(", ");
-                }
-                Some(Node::Expr(hir::Expr {
-                    kind: ExprKind::Closure(_, _, body_id, _, _),
-                    span: full_closure_span,
-                    ..
-                })) => {
-                    if *full_closure_span == expr.span {
-                        return false;
-                    }
-                    msg = "call this closure";
-                    let body = hir.body(*body_id);
-                    sugg_call = body
-                        .params
-                        .iter()
-                        .map(|param| match &param.pat.kind {
-                            hir::PatKind::Binding(_, _, ident, None)
-                                if ident.name != kw::SelfLower =>
-                            {
-                                ident.to_string()
-                            }
-                            _ => "_".to_string(),
-                        })
-                        .collect::<Vec<_>>()
-                        .join(", ");
-                }
-                Some(Node::Ctor(hir::VariantData::Tuple(fields, _))) => {
-                    sugg_call = fields.iter().map(|_| "_").collect::<Vec<_>>().join(", ");
-                    match def_id.as_local().map(|def_id| hir.def_kind(def_id)) {
-                        Some(DefKind::Ctor(hir::def::CtorOf::Variant, _)) => {
-                            msg = "instantiate this tuple variant";
-                        }
-                        Some(DefKind::Ctor(CtorOf::Struct, _)) => {
-                            msg = "instantiate this tuple struct";
-                        }
-                        _ => {}
-                    }
-                }
-                Some(Node::ForeignItem(hir::ForeignItem {
-                    kind: hir::ForeignItemKind::Fn(_, idents, _),
-                    ..
-                })) => {
-                    sugg_call = idents
-                        .iter()
-                        .map(|ident| {
-                            if ident.name != kw::SelfLower {
-                                ident.to_string()
-                            } else {
-                                "_".to_string()
-                            }
-                        })
-                        .collect::<Vec<_>>()
-                        .join(", ")
-                }
-                Some(Node::TraitItem(hir::TraitItem {
-                    kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Required(idents)),
-                    ..
-                })) => {
-                    sugg_call = idents
-                        .iter()
-                        .map(|ident| {
-                            if ident.name != kw::SelfLower {
-                                ident.to_string()
-                            } else {
-                                "_".to_string()
-                            }
-                        })
-                        .collect::<Vec<_>>()
-                        .join(", ")
-                }
-                _ => {}
-            }
-            err.span_suggestion_verbose(
-                expr.span.shrink_to_hi(),
-                &format!("use parentheses to {}", msg),
-                format!("({})", sugg_call),
-                applicability,
-            );
-            return true;
-        }
-        false
-    }
-
-    pub fn suggest_deref_ref_or_into(
-        &self,
-        err: &mut DiagnosticBuilder<'_>,
-        expr: &hir::Expr<'_>,
-        expected: Ty<'tcx>,
-        found: Ty<'tcx>,
-        expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
-    ) {
-        if let Some((sp, msg, suggestion, applicability)) = self.check_ref(expr, found, expected) {
-            err.span_suggestion(sp, msg, suggestion, applicability);
-        } else if let (ty::FnDef(def_id, ..), true) =
-            (&found.kind, self.suggest_fn_call(err, expr, expected, found))
-        {
-            if let Some(sp) = self.tcx.hir().span_if_local(*def_id) {
-                let sp = self.sess().source_map().guess_head_span(sp);
-                err.span_label(sp, &format!("{} defined here", found));
-            }
-        } else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) {
-            let is_struct_pat_shorthand_field =
-                self.is_hir_id_from_struct_pattern_shorthand_field(expr.hir_id, expr.span);
-            let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id);
-            if let Ok(expr_text) = self.sess().source_map().span_to_snippet(expr.span) {
-                let mut suggestions = iter::repeat(&expr_text)
-                    .zip(methods.iter())
-                    .filter_map(|(receiver, method)| {
-                        let method_call = format!(".{}()", method.ident);
-                        if receiver.ends_with(&method_call) {
-                            None // do not suggest code that is already there (#53348)
-                        } else {
-                            let method_call_list = [".to_vec()", ".to_string()"];
-                            let sugg = if receiver.ends_with(".clone()")
-                                && method_call_list.contains(&method_call.as_str())
-                            {
-                                let max_len = receiver.rfind('.').unwrap();
-                                format!("{}{}", &receiver[..max_len], method_call)
-                            } else {
-                                if expr.precedence().order() < ExprPrecedence::MethodCall.order() {
-                                    format!("({}){}", receiver, method_call)
-                                } else {
-                                    format!("{}{}", receiver, method_call)
-                                }
-                            };
-                            Some(if is_struct_pat_shorthand_field {
-                                format!("{}: {}", receiver, sugg)
-                            } else {
-                                sugg
-                            })
-                        }
-                    })
-                    .peekable();
-                if suggestions.peek().is_some() {
-                    err.span_suggestions(
-                        expr.span,
-                        "try using a conversion method",
-                        suggestions,
-                        Applicability::MaybeIncorrect,
-                    );
-                }
-            }
-        }
-    }
-
-    /// When encountering the expected boxed value allocated in the stack, suggest allocating it
-    /// in the heap by calling `Box::new()`.
-    fn suggest_boxing_when_appropriate(
-        &self,
-        err: &mut DiagnosticBuilder<'_>,
-        expr: &hir::Expr<'_>,
-        expected: Ty<'tcx>,
-        found: Ty<'tcx>,
-    ) {
-        if self.tcx.hir().is_inside_const_context(expr.hir_id) {
-            // Do not suggest `Box::new` in const context.
-            return;
-        }
-        if !expected.is_box() || found.is_box() {
-            return;
-        }
-        let boxed_found = self.tcx.mk_box(found);
-        if let (true, Ok(snippet)) = (
-            self.can_coerce(boxed_found, expected),
-            self.sess().source_map().span_to_snippet(expr.span),
-        ) {
-            err.span_suggestion(
-                expr.span,
-                "store this in the heap by calling `Box::new`",
-                format!("Box::new({})", snippet),
-                Applicability::MachineApplicable,
-            );
-            err.note(
-                "for more on the distinction between the stack and the heap, read \
-                 https://doc.rust-lang.org/book/ch15-01-box.html, \
-                 https://doc.rust-lang.org/rust-by-example/std/box.html, and \
-                 https://doc.rust-lang.org/std/boxed/index.html",
-            );
-        }
-    }
-
-    fn note_internal_mutation_in_method(
-        &self,
-        err: &mut DiagnosticBuilder<'_>,
-        expr: &hir::Expr<'_>,
-        expected: Ty<'tcx>,
-        found: Ty<'tcx>,
-    ) {
-        if found != self.tcx.types.unit {
-            return;
-        }
-        if let ExprKind::MethodCall(path_segment, _, [rcvr, ..], _) = expr.kind {
-            if self
-                .typeck_results
-                .borrow()
-                .expr_ty_adjusted_opt(rcvr)
-                .map_or(true, |ty| expected.peel_refs() != ty.peel_refs())
-            {
-                return;
-            }
-            let mut sp = MultiSpan::from_span(path_segment.ident.span);
-            sp.push_span_label(
-                path_segment.ident.span,
-                format!(
-                    "this call modifies {} in-place",
-                    match rcvr.kind {
-                        ExprKind::Path(QPath::Resolved(
-                            None,
-                            hir::Path { segments: [segment], .. },
-                        )) => format!("`{}`", segment.ident),
-                        _ => "its receiver".to_string(),
-                    }
-                ),
-            );
-            sp.push_span_label(
-                rcvr.span,
-                "you probably want to use this value after calling the method...".to_string(),
-            );
-            err.span_note(
-                sp,
-                &format!("method `{}` modifies its receiver in-place", path_segment.ident),
-            );
-            err.note(&format!("...instead of the `()` output of method `{}`", path_segment.ident));
-        }
-    }
-
-    /// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`.
-    fn suggest_calling_boxed_future_when_appropriate(
-        &self,
-        err: &mut DiagnosticBuilder<'_>,
-        expr: &hir::Expr<'_>,
-        expected: Ty<'tcx>,
-        found: Ty<'tcx>,
-    ) -> bool {
-        // Handle #68197.
-
-        if self.tcx.hir().is_inside_const_context(expr.hir_id) {
-            // Do not suggest `Box::new` in const context.
-            return false;
-        }
-        let pin_did = self.tcx.lang_items().pin_type();
-        match expected.kind {
-            ty::Adt(def, _) if Some(def.did) != pin_did => return false,
-            // This guards the `unwrap` and `mk_box` below.
-            _ if pin_did.is_none() || self.tcx.lang_items().owned_box().is_none() => return false,
-            _ => {}
-        }
-        let boxed_found = self.tcx.mk_box(found);
-        let new_found = self.tcx.mk_lang_item(boxed_found, LangItem::Pin).unwrap();
-        if let (true, Ok(snippet)) = (
-            self.can_coerce(new_found, expected),
-            self.sess().source_map().span_to_snippet(expr.span),
-        ) {
-            match found.kind {
-                ty::Adt(def, _) if def.is_box() => {
-                    err.help("use `Box::pin`");
-                }
-                _ => {
-                    err.span_suggestion(
-                        expr.span,
-                        "you need to pin and box this expression",
-                        format!("Box::pin({})", snippet),
-                        Applicability::MachineApplicable,
-                    );
-                }
-            }
-            true
-        } else {
-            false
-        }
-    }
-
-    /// A common error is to forget to add a semicolon at the end of a block, e.g.,
-    ///
-    /// ```
-    /// fn foo() {
-    ///     bar_that_returns_u32()
-    /// }
-    /// ```
-    ///
-    /// This routine checks if the return expression in a block would make sense on its own as a
-    /// statement and the return type has been left as default or has been specified as `()`. If so,
-    /// it suggests adding a semicolon.
-    fn suggest_missing_semicolon(
-        &self,
-        err: &mut DiagnosticBuilder<'_>,
-        expression: &'tcx hir::Expr<'tcx>,
-        expected: Ty<'tcx>,
-        cause_span: Span,
-    ) {
-        if expected.is_unit() {
-            // `BlockTailExpression` only relevant if the tail expr would be
-            // useful on its own.
-            match expression.kind {
-                ExprKind::Call(..)
-                | ExprKind::MethodCall(..)
-                | ExprKind::Loop(..)
-                | ExprKind::Match(..)
-                | ExprKind::Block(..) => {
-                    err.span_suggestion(
-                        cause_span.shrink_to_hi(),
-                        "try adding a semicolon",
-                        ";".to_string(),
-                        Applicability::MachineApplicable,
-                    );
-                }
-                _ => (),
-            }
-        }
-    }
-
-    /// A possible error is to forget to add a return type that is needed:
-    ///
-    /// ```
-    /// fn foo() {
-    ///     bar_that_returns_u32()
-    /// }
-    /// ```
-    ///
-    /// This routine checks if the return type is left as default, the method is not part of an
-    /// `impl` block and that it isn't the `main` method. If so, it suggests setting the return
-    /// type.
-    fn suggest_missing_return_type(
-        &self,
-        err: &mut DiagnosticBuilder<'_>,
-        fn_decl: &hir::FnDecl<'_>,
-        expected: Ty<'tcx>,
-        found: Ty<'tcx>,
-        can_suggest: bool,
-    ) -> bool {
-        // Only suggest changing the return type for methods that
-        // haven't set a return type at all (and aren't `fn main()` or an impl).
-        match (&fn_decl.output, found.is_suggestable(), can_suggest, expected.is_unit()) {
-            (&hir::FnRetTy::DefaultReturn(span), true, true, true) => {
-                err.span_suggestion(
-                    span,
-                    "try adding a return type",
-                    format!("-> {} ", self.resolve_vars_with_obligations(found)),
-                    Applicability::MachineApplicable,
-                );
-                true
-            }
-            (&hir::FnRetTy::DefaultReturn(span), false, true, true) => {
-                err.span_label(span, "possibly return type missing here?");
-                true
-            }
-            (&hir::FnRetTy::DefaultReturn(span), _, false, true) => {
-                // `fn main()` must return `()`, do not suggest changing return type
-                err.span_label(span, "expected `()` because of default return type");
-                true
-            }
-            // expectation was caused by something else, not the default return
-            (&hir::FnRetTy::DefaultReturn(_), _, _, false) => false,
-            (&hir::FnRetTy::Return(ref ty), _, _, _) => {
-                // Only point to return type if the expected type is the return type, as if they
-                // are not, the expectation must have been caused by something else.
-                debug!("suggest_missing_return_type: return type {:?} node {:?}", ty, ty.kind);
-                let sp = ty.span;
-                let ty = AstConv::ast_ty_to_ty(self, ty);
-                debug!("suggest_missing_return_type: return type {:?}", ty);
-                debug!("suggest_missing_return_type: expected type {:?}", ty);
-                if ty.kind == expected.kind {
-                    err.span_label(sp, format!("expected `{}` because of return type", expected));
-                    return true;
-                }
-                false
-            }
-        }
-    }
-
-    /// A possible error is to forget to add `.await` when using futures:
-    ///
-    /// ```
-    /// async fn make_u32() -> u32 {
-    ///     22
-    /// }
-    ///
-    /// fn take_u32(x: u32) {}
-    ///
-    /// async fn foo() {
-    ///     let x = make_u32();
-    ///     take_u32(x);
-    /// }
-    /// ```
-    ///
-    /// This routine checks if the found type `T` implements `Future<Output=U>` where `U` is the
-    /// expected type. If this is the case, and we are inside of an async body, it suggests adding
-    /// `.await` to the tail of the expression.
-    fn suggest_missing_await(
-        &self,
-        err: &mut DiagnosticBuilder<'_>,
-        expr: &hir::Expr<'_>,
-        expected: Ty<'tcx>,
-        found: Ty<'tcx>,
-    ) {
-        debug!("suggest_missing_await: expr={:?} expected={:?}, found={:?}", expr, expected, found);
-        // `.await` is not permitted outside of `async` bodies, so don't bother to suggest if the
-        // body isn't `async`.
-        let item_id = self.tcx().hir().get_parent_node(self.body_id);
-        if let Some(body_id) = self.tcx().hir().maybe_body_owned_by(item_id) {
-            let body = self.tcx().hir().body(body_id);
-            if let Some(hir::GeneratorKind::Async(_)) = body.generator_kind {
-                let sp = expr.span;
-                // Check for `Future` implementations by constructing a predicate to
-                // prove: `<T as Future>::Output == U`
-                let future_trait = self.tcx.require_lang_item(LangItem::Future, Some(sp));
-                let item_def_id = self
-                    .tcx
-                    .associated_items(future_trait)
-                    .in_definition_order()
-                    .next()
-                    .unwrap()
-                    .def_id;
-                // `<T as Future>::Output`
-                let projection_ty = ty::ProjectionTy {
-                    // `T`
-                    substs: self
-                        .tcx
-                        .mk_substs_trait(found, self.fresh_substs_for_item(sp, item_def_id)),
-                    // `Future::Output`
-                    item_def_id,
-                };
-
-                let predicate = ty::PredicateAtom::Projection(ty::ProjectionPredicate {
-                    projection_ty,
-                    ty: expected,
-                })
-                .potentially_quantified(self.tcx, ty::PredicateKind::ForAll);
-                let obligation = traits::Obligation::new(self.misc(sp), self.param_env, predicate);
-
-                debug!("suggest_missing_await: trying obligation {:?}", obligation);
-
-                if self.infcx.predicate_may_hold(&obligation) {
-                    debug!("suggest_missing_await: obligation held: {:?}", obligation);
-                    if let Ok(code) = self.sess().source_map().span_to_snippet(sp) {
-                        err.span_suggestion(
-                            sp,
-                            "consider using `.await` here",
-                            format!("{}.await", code),
-                            Applicability::MaybeIncorrect,
-                        );
-                    } else {
-                        debug!("suggest_missing_await: no snippet for {:?}", sp);
-                    }
-                } else {
-                    debug!("suggest_missing_await: obligation did not hold: {:?}", obligation)
-                }
-            }
-        }
-    }
-
-    fn suggest_missing_parentheses(&self, err: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'_>) {
-        let sp = self.tcx.sess.source_map().start_point(expr.span);
-        if let Some(sp) = self.tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) {
-            // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }`
-            self.tcx.sess.parse_sess.expr_parentheses_needed(err, *sp, None);
-        }
-    }
-
-    fn note_need_for_fn_pointer(
-        &self,
-        err: &mut DiagnosticBuilder<'_>,
-        expected: Ty<'tcx>,
-        found: Ty<'tcx>,
-    ) {
-        let (sig, did, substs) = match (&expected.kind, &found.kind) {
-            (ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => {
-                let sig1 = self.tcx.fn_sig(*did1).subst(self.tcx, substs1);
-                let sig2 = self.tcx.fn_sig(*did2).subst(self.tcx, substs2);
-                if sig1 != sig2 {
-                    return;
-                }
-                err.note(
-                    "different `fn` items always have unique types, even if their signatures are \
-                     the same",
-                );
-                (sig1, *did1, substs1)
-            }
-            (ty::FnDef(did, substs), ty::FnPtr(sig2)) => {
-                let sig1 = self.tcx.fn_sig(*did).subst(self.tcx, substs);
-                if sig1 != *sig2 {
-                    return;
-                }
-                (sig1, *did, substs)
-            }
-            _ => return,
-        };
-        err.help(&format!("change the expected type to be function pointer `{}`", sig));
-        err.help(&format!(
-            "if the expected type is due to type inference, cast the expected `fn` to a function \
-             pointer: `{} as {}`",
-            self.tcx.def_path_str_with_substs(did, substs),
-            sig
-        ));
-    }
-
-    /// A common error is to add an extra semicolon:
-    ///
-    /// ```
-    /// fn foo() -> usize {
-    ///     22;
-    /// }
-    /// ```
-    ///
-    /// This routine checks if the final statement in a block is an
-    /// expression with an explicit semicolon whose type is compatible
-    /// with `expected_ty`. If so, it suggests removing the semicolon.
-    fn consider_hint_about_removing_semicolon(
-        &self,
-        blk: &'tcx hir::Block<'tcx>,
-        expected_ty: Ty<'tcx>,
-        err: &mut DiagnosticBuilder<'_>,
-    ) {
-        if let Some(span_semi) = self.could_remove_semicolon(blk, expected_ty) {
-            err.span_suggestion(
-                span_semi,
-                "consider removing this semicolon",
-                String::new(),
-                Applicability::MachineApplicable,
-            );
-        }
-    }
-
-    fn could_remove_semicolon(
-        &self,
-        blk: &'tcx hir::Block<'tcx>,
-        expected_ty: Ty<'tcx>,
-    ) -> Option<Span> {
-        // Be helpful when the user wrote `{... expr;}` and
-        // taking the `;` off is enough to fix the error.
-        let last_stmt = blk.stmts.last()?;
-        let last_expr = match last_stmt.kind {
-            hir::StmtKind::Semi(ref e) => e,
-            _ => return None,
-        };
-        let last_expr_ty = self.node_ty(last_expr.hir_id);
-        if matches!(last_expr_ty.kind, ty::Error(_))
-            || self.can_sub(self.param_env, last_expr_ty, expected_ty).is_err()
-        {
-            return None;
-        }
-        let original_span = original_sp(last_stmt.span, blk.span);
-        Some(original_span.with_lo(original_span.hi() - BytePos(1)))
-    }
-
-    // Instantiates the given path, which must refer to an item with the given
-    // number of type parameters and type.
-    pub fn instantiate_value_path(
-        &self,
-        segments: &[hir::PathSegment<'_>],
-        self_ty: Option<Ty<'tcx>>,
-        res: Res,
-        span: Span,
-        hir_id: hir::HirId,
-    ) -> (Ty<'tcx>, Res) {
-        debug!(
-            "instantiate_value_path(segments={:?}, self_ty={:?}, res={:?}, hir_id={})",
-            segments, self_ty, res, hir_id,
-        );
-
-        let tcx = self.tcx;
-
-        let path_segs = match res {
-            Res::Local(_) | Res::SelfCtor(_) => vec![],
-            Res::Def(kind, def_id) => {
-                AstConv::def_ids_for_value_path_segments(self, segments, self_ty, kind, def_id)
-            }
-            _ => bug!("instantiate_value_path on {:?}", res),
-        };
-
-        let mut user_self_ty = None;
-        let mut is_alias_variant_ctor = false;
-        match res {
-            Res::Def(DefKind::Ctor(CtorOf::Variant, _), _) => {
-                if let Some(self_ty) = self_ty {
-                    let adt_def = self_ty.ty_adt_def().unwrap();
-                    user_self_ty = Some(UserSelfTy { impl_def_id: adt_def.did, self_ty });
-                    is_alias_variant_ctor = true;
-                }
-            }
-            Res::Def(DefKind::AssocFn | DefKind::AssocConst, def_id) => {
-                let container = tcx.associated_item(def_id).container;
-                debug!("instantiate_value_path: def_id={:?} container={:?}", def_id, container);
-                match container {
-                    ty::TraitContainer(trait_did) => {
-                        callee::check_legal_trait_for_method_call(tcx, span, None, trait_did)
-                    }
-                    ty::ImplContainer(impl_def_id) => {
-                        if segments.len() == 1 {
-                            // `<T>::assoc` will end up here, and so
-                            // can `T::assoc`. It this came from an
-                            // inherent impl, we need to record the
-                            // `T` for posterity (see `UserSelfTy` for
-                            // details).
-                            let self_ty = self_ty.expect("UFCS sugared assoc missing Self");
-                            user_self_ty = Some(UserSelfTy { impl_def_id, self_ty });
-                        }
-                    }
-                }
-            }
-            _ => {}
-        }
-
-        // Now that we have categorized what space the parameters for each
-        // segment belong to, let's sort out the parameters that the user
-        // provided (if any) into their appropriate spaces. We'll also report
-        // errors if type parameters are provided in an inappropriate place.
-
-        let generic_segs: FxHashSet<_> = path_segs.iter().map(|PathSeg(_, index)| index).collect();
-        let generics_has_err = AstConv::prohibit_generics(
-            self,
-            segments.iter().enumerate().filter_map(|(index, seg)| {
-                if !generic_segs.contains(&index) || is_alias_variant_ctor {
-                    Some(seg)
-                } else {
-                    None
-                }
-            }),
-        );
-
-        if let Res::Local(hid) = res {
-            let ty = self.local_ty(span, hid).decl_ty;
-            let ty = self.normalize_associated_types_in(span, &ty);
-            self.write_ty(hir_id, ty);
-            return (ty, res);
-        }
-
-        if generics_has_err {
-            // Don't try to infer type parameters when prohibited generic arguments were given.
-            user_self_ty = None;
-        }
-
-        // Now we have to compare the types that the user *actually*
-        // provided against the types that were *expected*. If the user
-        // did not provide any types, then we want to substitute inference
-        // variables. If the user provided some types, we may still need
-        // to add defaults. If the user provided *too many* types, that's
-        // a problem.
-
-        let mut infer_args_for_err = FxHashSet::default();
-        for &PathSeg(def_id, index) in &path_segs {
-            let seg = &segments[index];
-            let generics = tcx.generics_of(def_id);
-            // Argument-position `impl Trait` is treated as a normal generic
-            // parameter internally, but we don't allow users to specify the
-            // parameter's value explicitly, so we have to do some error-
-            // checking here.
-            if let GenericArgCountResult {
-                correct: Err(GenericArgCountMismatch { reported: Some(ErrorReported), .. }),
-                ..
-            } = AstConv::check_generic_arg_count_for_call(
-                tcx, span, &generics, &seg, false, // `is_method_call`
-            ) {
-                infer_args_for_err.insert(index);
-                self.set_tainted_by_errors(); // See issue #53251.
-            }
-        }
-
-        let has_self = path_segs
-            .last()
-            .map(|PathSeg(def_id, _)| tcx.generics_of(*def_id).has_self)
-            .unwrap_or(false);
-
-        let (res, self_ctor_substs) = if let Res::SelfCtor(impl_def_id) = res {
-            let ty = self.normalize_ty(span, tcx.at(span).type_of(impl_def_id));
-            match ty.kind {
-                ty::Adt(adt_def, substs) if adt_def.has_ctor() => {
-                    let variant = adt_def.non_enum_variant();
-                    let ctor_def_id = variant.ctor_def_id.unwrap();
-                    (
-                        Res::Def(DefKind::Ctor(CtorOf::Struct, variant.ctor_kind), ctor_def_id),
-                        Some(substs),
-                    )
-                }
-                _ => {
-                    let mut err = tcx.sess.struct_span_err(
-                        span,
-                        "the `Self` constructor can only be used with tuple or unit structs",
-                    );
-                    if let Some(adt_def) = ty.ty_adt_def() {
-                        match adt_def.adt_kind() {
-                            AdtKind::Enum => {
-                                err.help("did you mean to use one of the enum's variants?");
-                            }
-                            AdtKind::Struct | AdtKind::Union => {
-                                err.span_suggestion(
-                                    span,
-                                    "use curly brackets",
-                                    String::from("Self { /* fields */ }"),
-                                    Applicability::HasPlaceholders,
-                                );
-                            }
-                        }
-                    }
-                    err.emit();
-
-                    return (tcx.ty_error(), res);
-                }
-            }
-        } else {
-            (res, None)
-        };
-        let def_id = res.def_id();
-
-        // The things we are substituting into the type should not contain
-        // escaping late-bound regions, and nor should the base type scheme.
-        let ty = tcx.type_of(def_id);
-
-        let arg_count = GenericArgCountResult {
-            explicit_late_bound: ExplicitLateBound::No,
-            correct: if infer_args_for_err.is_empty() {
-                Ok(())
-            } else {
-                Err(GenericArgCountMismatch::default())
-            },
-        };
-
-        let substs = self_ctor_substs.unwrap_or_else(|| {
-            AstConv::create_substs_for_generic_args(
-                tcx,
-                def_id,
-                &[][..],
-                has_self,
-                self_ty,
-                arg_count,
-                // Provide the generic args, and whether types should be inferred.
-                |def_id| {
-                    if let Some(&PathSeg(_, index)) =
-                        path_segs.iter().find(|&PathSeg(did, _)| *did == def_id)
-                    {
-                        // If we've encountered an `impl Trait`-related error, we're just
-                        // going to infer the arguments for better error messages.
-                        if !infer_args_for_err.contains(&index) {
-                            // Check whether the user has provided generic arguments.
-                            if let Some(ref data) = segments[index].args {
-                                return (Some(data), segments[index].infer_args);
-                            }
-                        }
-                        return (None, segments[index].infer_args);
-                    }
-
-                    (None, true)
-                },
-                // Provide substitutions for parameters for which (valid) arguments have been provided.
-                |param, arg| match (&param.kind, arg) {
-                    (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => {
-                        AstConv::ast_region_to_region(self, lt, Some(param)).into()
-                    }
-                    (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => {
-                        self.to_ty(ty).into()
-                    }
-                    (GenericParamDefKind::Const, GenericArg::Const(ct)) => {
-                        self.const_arg_to_const(&ct.value, param.def_id).into()
-                    }
-                    _ => unreachable!(),
-                },
-                // Provide substitutions for parameters for which arguments are inferred.
-                |substs, param, infer_args| {
-                    match param.kind {
-                        GenericParamDefKind::Lifetime => {
-                            self.re_infer(Some(param), span).unwrap().into()
-                        }
-                        GenericParamDefKind::Type { has_default, .. } => {
-                            if !infer_args && has_default {
-                                // If we have a default, then we it doesn't matter that we're not
-                                // inferring the type arguments: we provide the default where any
-                                // is missing.
-                                let default = tcx.type_of(param.def_id);
-                                self.normalize_ty(
-                                    span,
-                                    default.subst_spanned(tcx, substs.unwrap(), Some(span)),
-                                )
-                                .into()
-                            } else {
-                                // If no type arguments were provided, we have to infer them.
-                                // This case also occurs as a result of some malformed input, e.g.
-                                // a lifetime argument being given instead of a type parameter.
-                                // Using inference instead of `Error` gives better error messages.
-                                self.var_for_def(span, param)
-                            }
-                        }
-                        GenericParamDefKind::Const => {
-                            // FIXME(const_generics:defaults)
-                            // No const parameters were provided, we have to infer them.
-                            self.var_for_def(span, param)
-                        }
-                    }
-                },
-            )
-        });
-        assert!(!substs.has_escaping_bound_vars());
-        assert!(!ty.has_escaping_bound_vars());
-
-        // First, store the "user substs" for later.
-        self.write_user_type_annotation_from_substs(hir_id, def_id, substs, user_self_ty);
-
-        self.add_required_obligations(span, def_id, &substs);
-
-        // Substitute the values for the type parameters into the type of
-        // the referenced item.
-        let ty_substituted = self.instantiate_type_scheme(span, &substs, &ty);
-
-        if let Some(UserSelfTy { impl_def_id, self_ty }) = user_self_ty {
-            // In the case of `Foo<T>::method` and `<Foo<T>>::method`, if `method`
-            // is inherent, there is no `Self` parameter; instead, the impl needs
-            // type parameters, which we can infer by unifying the provided `Self`
-            // with the substituted impl type.
-            // This also occurs for an enum variant on a type alias.
-            let ty = tcx.type_of(impl_def_id);
-
-            let impl_ty = self.instantiate_type_scheme(span, &substs, &ty);
-            match self.at(&self.misc(span), self.param_env).sup(impl_ty, self_ty) {
-                Ok(ok) => self.register_infer_ok_obligations(ok),
-                Err(_) => {
-                    self.tcx.sess.delay_span_bug(
-                        span,
-                        &format!(
-                        "instantiate_value_path: (UFCS) {:?} was a subtype of {:?} but now is not?",
-                        self_ty,
-                        impl_ty,
-                    ),
-                    );
-                }
-            }
-        }
-
-        self.check_rustc_args_require_const(def_id, hir_id, span);
-
-        debug!("instantiate_value_path: type of {:?} is {:?}", hir_id, ty_substituted);
-        self.write_substs(hir_id, substs);
-
-        (ty_substituted, res)
-    }
-
-    /// Add all the obligations that are required, substituting and normalized appropriately.
-    fn add_required_obligations(&self, span: Span, def_id: DefId, substs: &SubstsRef<'tcx>) {
-        let (bounds, spans) = self.instantiate_bounds(span, def_id, &substs);
-
-        for (i, mut obligation) in traits::predicates_for_generics(
-            traits::ObligationCause::new(span, self.body_id, traits::ItemObligation(def_id)),
-            self.param_env,
-            bounds,
-        )
-        .enumerate()
-        {
-            // This makes the error point at the bound, but we want to point at the argument
-            if let Some(span) = spans.get(i) {
-                obligation.cause.make_mut().code = traits::BindingObligation(def_id, *span);
-            }
-            self.register_predicate(obligation);
-        }
-    }
-
-    fn check_rustc_args_require_const(&self, def_id: DefId, hir_id: hir::HirId, span: Span) {
-        // We're only interested in functions tagged with
-        // #[rustc_args_required_const], so ignore anything that's not.
-        if !self.tcx.has_attr(def_id, sym::rustc_args_required_const) {
-            return;
-        }
-
-        // If our calling expression is indeed the function itself, we're good!
-        // If not, generate an error that this can only be called directly.
-        if let Node::Expr(expr) = self.tcx.hir().get(self.tcx.hir().get_parent_node(hir_id)) {
-            if let ExprKind::Call(ref callee, ..) = expr.kind {
-                if callee.hir_id == hir_id {
-                    return;
-                }
-            }
-        }
-
-        self.tcx.sess.span_err(
-            span,
-            "this function can only be invoked directly, not through a function pointer",
-        );
-    }
-
-    /// Resolves `typ` by a single level if `typ` is a type variable.
-    /// 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 ty = self.resolve_vars_with_obligations(ty);
-        if !ty.is_ty_var() {
-            ty
-        } else {
-            if !self.is_tainted_by_errors() {
-                self.need_type_info_err((**self).body_id, sp, ty, E0282)
-                    .note("type must be known at this point")
-                    .emit();
-            }
-            let err = self.tcx.ty_error();
-            self.demand_suptype(sp, err, ty);
-            err
-        }
-    }
+/// A wrapper for `InferCtxt`'s `in_progress_typeck_results` field.
+#[derive(Copy, Clone)]
+struct MaybeInProgressTables<'a, 'tcx> {
+    maybe_typeck_results: Option<&'a RefCell<ty::TypeckResults<'tcx>>>,
+}
 
-    fn with_breakable_ctxt<F: FnOnce() -> R, R>(
-        &self,
-        id: hir::HirId,
-        ctxt: BreakableCtxt<'tcx>,
-        f: F,
-    ) -> (BreakableCtxt<'tcx>, R) {
-        let index;
-        {
-            let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
-            index = enclosing_breakables.stack.len();
-            enclosing_breakables.by_id.insert(id, index);
-            enclosing_breakables.stack.push(ctxt);
+impl<'a, 'tcx> MaybeInProgressTables<'a, 'tcx> {
+    fn borrow(self) -> Ref<'a, ty::TypeckResults<'tcx>> {
+        match self.maybe_typeck_results {
+            Some(typeck_results) => typeck_results.borrow(),
+            None => bug!(
+                "MaybeInProgressTables: inh/fcx.typeck_results.borrow() with no typeck results"
+            ),
         }
-        let result = f();
-        let ctxt = {
-            let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
-            debug_assert!(enclosing_breakables.stack.len() == index + 1);
-            enclosing_breakables.by_id.remove(&id).expect("missing breakable context");
-            enclosing_breakables.stack.pop().expect("missing breakable context")
-        };
-        (ctxt, result)
-    }
-
-    /// Instantiate a QueryResponse in a probe context, without a
-    /// good ObligationCause.
-    fn probe_instantiate_query_response(
-        &self,
-        span: Span,
-        original_values: &OriginalQueryValues<'tcx>,
-        query_result: &Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>,
-    ) -> InferResult<'tcx, Ty<'tcx>> {
-        self.instantiate_query_response_and_region_obligations(
-            &traits::ObligationCause::misc(span, self.body_id),
-            self.param_env,
-            original_values,
-            query_result,
-        )
     }
 
-    /// Returns `true` if an expression is contained inside the LHS of an assignment expression.
-    fn expr_in_place(&self, mut expr_id: hir::HirId) -> bool {
-        let mut contained_in_place = false;
-
-        while let hir::Node::Expr(parent_expr) =
-            self.tcx.hir().get(self.tcx.hir().get_parent_node(expr_id))
-        {
-            match &parent_expr.kind {
-                hir::ExprKind::Assign(lhs, ..) | hir::ExprKind::AssignOp(_, lhs, ..) => {
-                    if lhs.hir_id == expr_id {
-                        contained_in_place = true;
-                        break;
-                    }
-                }
-                _ => (),
-            }
-            expr_id = parent_expr.hir_id;
+    fn borrow_mut(self) -> RefMut<'a, ty::TypeckResults<'tcx>> {
+        match self.maybe_typeck_results {
+            Some(typeck_results) => typeck_results.borrow_mut(),
+            None => bug!(
+                "MaybeInProgressTables: inh/fcx.typeck_results.borrow_mut() with no typeck results"
+            ),
         }
-
-        contained_in_place
     }
 }
 
-fn check_type_params_are_used<'tcx>(tcx: TyCtxt<'tcx>, generics: &ty::Generics, ty: Ty<'tcx>) {
-    debug!("check_type_params_are_used(generics={:?}, ty={:?})", generics, ty);
-
-    assert_eq!(generics.parent, None);
-
-    if generics.own_counts().types == 0 {
-        return;
-    }
-
-    let mut params_used = BitSet::new_empty(generics.params.len());
-
-    if ty.references_error() {
-        // If there is already another error, do not emit
-        // an error for not using a type parameter.
-        assert!(tcx.sess.has_errors());
-        return;
-    }
+struct CheckItemTypesVisitor<'tcx> {
+    tcx: TyCtxt<'tcx>,
+}
 
-    for leaf in ty.walk() {
-        if let GenericArgKind::Type(leaf_ty) = leaf.unpack() {
-            if let ty::Param(param) = leaf_ty.kind {
-                debug!("found use of ty param {:?}", param);
-                params_used.insert(param.index);
-            }
-        }
+impl ItemLikeVisitor<'tcx> for CheckItemTypesVisitor<'tcx> {
+    fn visit_item(&mut self, i: &'tcx hir::Item<'tcx>) {
+        check_item_type(self.tcx, i);
     }
+    fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem<'tcx>) {}
+    fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem<'tcx>) {}
+}
 
-    for param in &generics.params {
-        if !params_used.contains(param.index) {
-            if let ty::GenericParamDefKind::Type { .. } = param.kind {
-                let span = tcx.def_span(param.def_id);
-                struct_span_err!(
-                    tcx.sess,
-                    span,
-                    E0091,
-                    "type parameter `{}` is unused",
-                    param.name,
-                )
-                .span_label(span, "unused type parameter")
-                .emit();
-            }
-        }
-    }
+fn typeck_item_bodies(tcx: TyCtxt<'_>, crate_num: CrateNum) {
+    debug_assert!(crate_num == LOCAL_CRATE);
+    tcx.par_body_owners(|body_owner_def_id| {
+        tcx.ensure().typeck(body_owner_def_id);
+    });
 }
 
 fn fatally_break_rust(sess: &Session) {
diff --git a/compiler/rustc_typeck/src/check/op.rs b/compiler/rustc_typeck/src/check/op.rs
index 66fb01a54f5..66975f32a1f 100644
--- a/compiler/rustc_typeck/src/check/op.rs
+++ b/compiler/rustc_typeck/src/check/op.rs
@@ -206,7 +206,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             Ok(method) => {
                 let by_ref_binop = !op.node.is_by_value();
                 if is_assign == IsAssign::Yes || by_ref_binop {
-                    if let ty::Ref(region, _, mutbl) = method.sig.inputs()[0].kind {
+                    if let ty::Ref(region, _, mutbl) = method.sig.inputs()[0].kind() {
                         let mutbl = match mutbl {
                             hir::Mutability::Not => AutoBorrowMutability::Not,
                             hir::Mutability::Mut => AutoBorrowMutability::Mut {
@@ -223,7 +223,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     }
                 }
                 if by_ref_binop {
-                    if let ty::Ref(region, _, mutbl) = method.sig.inputs()[1].kind {
+                    if let ty::Ref(region, _, mutbl) = method.sig.inputs()[1].kind() {
                         let mutbl = match mutbl {
                             hir::Mutability::Not => AutoBorrowMutability::Not,
                             hir::Mutability::Mut => AutoBorrowMutability::Mut {
@@ -395,7 +395,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     }
                 };
                 let mut suggested_deref = false;
-                if let Ref(_, rty, _) = lhs_ty.kind {
+                if let Ref(_, rty, _) = lhs_ty.kind() {
                     if {
                         self.infcx.type_is_copy_modulo_regions(self.param_env, rty, lhs_expr.span)
                             && self
@@ -436,7 +436,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         // concatenation (e.g., "Hello " + "World!"). This means
                         // we don't want the note in the else clause to be emitted
                     } else if let [ty] = &visitor.0[..] {
-                        if let ty::Param(p) = ty.kind {
+                        if let ty::Param(p) = *ty.kind() {
                             // Check if the method would be found if the type param wasn't
                             // involved. If so, it means that adding a trait bound to the param is
                             // enough. Otherwise we do not give the suggestion.
@@ -468,7 +468,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                 ));
                             }
                         } else {
-                            bug!("type param visitor stored a non type param: {:?}", ty.kind);
+                            bug!("type param visitor stored a non type param: {:?}", ty.kind());
                         }
                     } else if !suggested_deref && !involves_fn {
                         suggest_impl_missing(&mut err, lhs_ty, &missing_trait);
@@ -494,7 +494,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         is_assign: IsAssign,
     ) -> bool /* did we suggest to call a function because of missing parenthesis? */ {
         err.span_label(span, ty.to_string());
-        if let FnDef(def_id, _) = ty.kind {
+        if let FnDef(def_id, _) = *ty.kind() {
             let source_map = self.tcx.sess.source_map();
             if !self.tcx.has_typeck_results(def_id) {
                 return false;
@@ -502,7 +502,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // We're emitting a suggestion, so we can just ignore regions
             let fn_sig = self.tcx.fn_sig(def_id).skip_binder();
 
-            let other_ty = if let FnDef(def_id, _) = other_ty.kind {
+            let other_ty = if let FnDef(def_id, _) = *other_ty.kind() {
                 if !self.tcx.has_typeck_results(def_id) {
                     return false;
                 }
@@ -568,10 +568,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             None => false,
         };
 
-        match (&lhs_ty.kind, &rhs_ty.kind) {
+        match (lhs_ty.kind(), rhs_ty.kind()) {
             (&Ref(_, l_ty, _), &Ref(_, r_ty, _)) // &str or &String + &str, &String or &&str
-                if (l_ty.kind == Str || is_std_string(l_ty)) && (
-                        r_ty.kind == Str || is_std_string(r_ty) ||
+                if (*l_ty.kind() == Str || is_std_string(l_ty)) && (
+                        *r_ty.kind() == Str || is_std_string(r_ty) ||
                         &format!("{:?}", rhs_ty) == "&&str"
                     ) =>
             {
@@ -589,10 +589,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                 } else {
                                     msg
                                 },
-                                if lstring.starts_with('&') {
+                                if let Some(stripped) = lstring.strip_prefix('&') {
                                     // let a = String::new();
                                     // let _ = &a + "bar";
-                                    lstring[1..].to_string()
+                                    stripped.to_string()
                                 } else {
                                     format!("{}.to_owned()", lstring)
                                 },
@@ -605,7 +605,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 true
             }
             (&Ref(_, l_ty, _), &Adt(..)) // Handle `&str` & `&String` + `String`
-                if (l_ty.kind == Str || is_std_string(l_ty)) && is_std_string(rhs_ty) =>
+                if (*l_ty.kind() == Str || is_std_string(l_ty)) && is_std_string(rhs_ty) =>
             {
                 err.span_label(
                     op.span,
@@ -617,10 +617,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     is_assign,
                 ) {
                     (Ok(l), Ok(r), IsAssign::No) => {
-                        let to_string = if l.starts_with('&') {
+                        let to_string = if let Some(stripped) = l.strip_prefix('&') {
                             // let a = String::new(); let b = String::new();
                             // let _ = &a + b;
-                            l[1..].to_string()
+                            stripped.to_string()
                         } else {
                             format!("{}.to_owned()", l)
                         };
@@ -670,12 +670,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         ex.span,
                         format!("cannot apply unary operator `{}`", op.as_str()),
                     );
-                    match actual.kind {
+                    match actual.kind() {
                         Uint(_) if op == hir::UnOp::UnNeg => {
                             err.note("unsigned values cannot be negated");
                         }
                         Str | Never | Char | Tuple(_) | Array(_, _) => {}
-                        Ref(_, ref lty, _) if lty.kind == Str => {}
+                        Ref(_, ref lty, _) if *lty.kind() == Str => {}
                         _ => {
                             let missing_trait = match op {
                                 hir::UnOp::UnNeg => "std::ops::Neg",
@@ -844,7 +844,7 @@ enum Op {
 
 /// Dereferences a single level of immutable referencing.
 fn deref_ty_if_possible(ty: Ty<'tcx>) -> Ty<'tcx> {
-    match ty.kind {
+    match ty.kind() {
         ty::Ref(_, ty, hir::Mutability::Not) => ty,
         _ => ty,
     }
@@ -903,7 +903,7 @@ fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, op: hir::BinOp) -> bool
 
 /// If applicable, note that an implementation of `trait` for `ty` may fix the error.
 fn suggest_impl_missing(err: &mut DiagnosticBuilder<'_>, ty: Ty<'_>, missing_trait: &str) {
-    if let Adt(def, _) = ty.peel_refs().kind {
+    if let Adt(def, _) = ty.peel_refs().kind() {
         if def.did.is_local() {
             err.note(&format!(
                 "an implementation of `{}` might be missing for `{}`",
@@ -957,7 +957,7 @@ struct TypeParamVisitor<'tcx>(Vec<Ty<'tcx>>);
 
 impl<'tcx> TypeVisitor<'tcx> for TypeParamVisitor<'tcx> {
     fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool {
-        if let ty::Param(_) = ty.kind {
+        if let ty::Param(_) = ty.kind() {
             self.0.push(ty);
         }
         ty.super_visit_with(self)
@@ -972,7 +972,7 @@ impl TypeFolder<'tcx> for TypeParamEraser<'_, 'tcx> {
     }
 
     fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
-        match ty.kind {
+        match ty.kind() {
             ty::Param(_) => self.0.next_ty_var(TypeVariableOrigin {
                 kind: TypeVariableOriginKind::MiscVariable,
                 span: self.1,
diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs
index d1864ee2b35..321472b8fe8 100644
--- a/compiler/rustc_typeck/src/check/pat.rs
+++ b/compiler/rustc_typeck/src/check/pat.rs
@@ -1,5 +1,6 @@
 use crate::check::FnCtxt;
 use rustc_ast as ast;
+
 use rustc_ast::util::lev_distance::find_best_match_for_name;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder};
@@ -10,7 +11,7 @@ use rustc_hir::{HirId, Pat, PatKind};
 use rustc_infer::infer;
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use rustc_middle::ty::subst::GenericArg;
-use rustc_middle::ty::{self, BindingMode, Ty, TypeFoldable};
+use rustc_middle::ty::{self, Adt, BindingMode, Ty, TypeFoldable};
 use rustc_span::hygiene::DesugaringKind;
 use rustc_span::source_map::{Span, Spanned};
 use rustc_span::symbol::Ident;
@@ -281,7 +282,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // String and byte-string literals result in types `&str` and `&[u8]` respectively.
             // All other literals result in non-reference types.
             // As a result, we allow `if let 0 = &&0 {}` but not `if let "foo" = &&"foo {}`.
-            PatKind::Lit(lt) => match self.check_expr(lt).kind {
+            PatKind::Lit(lt) => match self.check_expr(lt).kind() {
                 ty::Ref(..) => AdjustMode::Pass,
                 _ => AdjustMode::Peel,
             },
@@ -341,7 +342,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         //
         // See the examples in `ui/match-defbm*.rs`.
         let mut pat_adjustments = vec![];
-        while let ty::Ref(_, inner_ty, inner_mutability) = expected.kind {
+        while let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() {
             debug!("inspecting {:?}", expected);
 
             debug!("current discriminant is Ref, inserting implicit deref");
@@ -389,9 +390,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         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);
-            if let ty::Ref(_, ty::TyS { kind: ty::Slice(_), .. }, _) = expected.kind {
-                let tcx = self.tcx;
-                pat_ty = tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_slice(tcx.types.u8));
+            if let ty::Ref(_, inner_ty, _) = expected.kind() {
+                if matches!(inner_ty.kind(), ty::Slice(_)) {
+                    let tcx = self.tcx;
+                    pat_ty = tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_slice(tcx.types.u8));
+                }
             }
         }
 
@@ -639,7 +642,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     pub fn check_dereferenceable(&self, span: Span, expected: Ty<'tcx>, inner: &Pat<'_>) -> bool {
         if let PatKind::Binding(..) = inner.kind {
             if let Some(mt) = self.shallow_resolve(expected).builtin_deref(true) {
-                if let ty::Dynamic(..) = mt.ty.kind {
+                if let ty::Dynamic(..) = mt.ty.kind() {
                     // This is "x = SomeTrait" being reduced from
                     // "let &x = &SomeTrait" or "let box x = Box<SomeTrait>", an error.
                     let type_str = self.ty_to_string(expected);
@@ -733,17 +736,52 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         if let Some(err) =
             self.demand_suptype_with_origin(&self.pattern_cause(ti, pat.span), expected, pat_ty)
         {
-            self.emit_bad_pat_path(err, pat.span, res, pat_res, segments, ti.parent_pat);
+            self.emit_bad_pat_path(err, pat.span, res, pat_res, pat_ty, segments, ti.parent_pat);
         }
         pat_ty
     }
 
+    fn maybe_suggest_range_literal(
+        &self,
+        e: &mut DiagnosticBuilder<'_>,
+        opt_def_id: Option<hir::def_id::DefId>,
+        ident: Ident,
+    ) -> bool {
+        match opt_def_id {
+            Some(def_id) => match self.tcx.hir().get_if_local(def_id) {
+                Some(hir::Node::Item(hir::Item {
+                    kind: hir::ItemKind::Const(_, body_id), ..
+                })) => match self.tcx.hir().get(body_id.hir_id) {
+                    hir::Node::Expr(expr) => {
+                        if hir::is_range_literal(expr) {
+                            let span = self.tcx.hir().span(body_id.hir_id);
+                            if let Ok(snip) = self.tcx.sess.source_map().span_to_snippet(span) {
+                                e.span_suggestion_verbose(
+                                    ident.span,
+                                    "you may want to move the range into the match block",
+                                    snip,
+                                    Applicability::MachineApplicable,
+                                );
+                                return true;
+                            }
+                        }
+                    }
+                    _ => (),
+                },
+                _ => (),
+            },
+            _ => (),
+        }
+        false
+    }
+
     fn emit_bad_pat_path(
         &self,
         mut e: DiagnosticBuilder<'_>,
         pat_span: Span,
         res: Res,
         pat_res: Res,
+        pat_ty: Ty<'tcx>,
         segments: &'b [hir::PathSegment<'b>],
         parent_pat: Option<&Pat<'_>>,
     ) {
@@ -769,9 +807,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         );
                     }
                     _ => {
-                        let msg = "introduce a new binding instead";
-                        let sugg = format!("other_{}", ident.as_str().to_lowercase());
-                        e.span_suggestion(ident.span, msg, sugg, Applicability::HasPlaceholders);
+                        let (type_def_id, item_def_id) = match pat_ty.kind() {
+                            Adt(def, _) => match res {
+                                Res::Def(DefKind::Const, def_id) => (Some(def.did), Some(def_id)),
+                                _ => (None, None),
+                            },
+                            _ => (None, None),
+                        };
+
+                        let ranges = &[
+                            self.tcx.lang_items().range_struct(),
+                            self.tcx.lang_items().range_from_struct(),
+                            self.tcx.lang_items().range_to_struct(),
+                            self.tcx.lang_items().range_full_struct(),
+                            self.tcx.lang_items().range_inclusive_struct(),
+                            self.tcx.lang_items().range_to_inclusive_struct(),
+                        ];
+                        if type_def_id != None && ranges.contains(&type_def_id) {
+                            if !self.maybe_suggest_range_literal(&mut e, item_def_id, *ident) {
+                                let msg = "constants only support matching by type, \
+                                    if you meant to match against a range of values, \
+                                    consider using a range pattern like `min ..= max` in the match block";
+                                e.note(msg);
+                            }
+                        } else {
+                            let msg = "introduce a new binding instead";
+                            let sugg = format!("other_{}", ident.as_str().to_lowercase());
+                            e.span_suggestion(
+                                ident.span,
+                                msg,
+                                sugg,
+                                Applicability::HasPlaceholders,
+                            );
+                        }
                     }
                 };
             }
@@ -871,7 +939,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         if subpats.len() == variant.fields.len()
             || subpats.len() < variant.fields.len() && ddpos.is_some()
         {
-            let substs = match pat_ty.kind {
+            let substs = match pat_ty.kind() {
                 ty::Adt(_, substs) => substs,
                 _ => bug!("unexpected pattern type {:?}", pat_ty),
             };
@@ -924,13 +992,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // More generally, the expected type wants a tuple variant with one field of an
         // N-arity-tuple, e.g., `V_i((p_0, .., p_N))`. Meanwhile, the user supplied a pattern
         // with the subpatterns directly in the tuple variant pattern, e.g., `V_i(p_0, .., p_N)`.
-        let missing_parenthesis = match (&expected.kind, fields, had_err) {
+        let missing_parenthesis = match (&expected.kind(), fields, had_err) {
             // #67037: only do this if we could successfully type-check the expected type against
             // the tuple struct pattern. Otherwise the substs could get out of range on e.g.,
             // `let P() = U;` where `P != U` with `struct P<T>(T);`.
             (ty::Adt(_, substs), [field], false) => {
                 let field_ty = self.field_ty(pat_span, field, substs);
-                match field_ty.kind {
+                match field_ty.kind() {
                     ty::Tuple(_) => field_ty.tuple_fields().count() == subpats.len(),
                     _ => false,
                 }
@@ -981,7 +1049,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let mut expected_len = elements.len();
         if ddpos.is_some() {
             // Require known type only when `..` is present.
-            if let ty::Tuple(ref tys) = self.structurally_resolved_type(span, expected).kind {
+            if let ty::Tuple(ref tys) = self.structurally_resolved_type(span, expected).kind() {
                 expected_len = tys.len();
             }
         }
@@ -1025,7 +1093,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ) -> bool {
         let tcx = self.tcx;
 
-        let (substs, adt) = match adt_ty.kind {
+        let (substs, adt) = match adt_ty.kind() {
             ty::Adt(adt, substs) => (substs, adt),
             _ => span_bug!(pat.span, "struct pattern is not an ADT"),
         };
@@ -1076,8 +1144,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let mut unmentioned_fields = variant
             .fields
             .iter()
-            .map(|field| field.ident.normalize_to_macros_2_0())
-            .filter(|ident| !used_fields.contains_key(&ident))
+            .map(|field| (field, field.ident.normalize_to_macros_2_0()))
+            .filter(|(_, ident)| !used_fields.contains_key(&ident))
             .collect::<Vec<_>>();
 
         let inexistent_fields_err = if !(inexistent_fields.is_empty() || variant.is_recovered()) {
@@ -1108,7 +1176,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 tcx.sess.struct_span_err(pat.span, "`..` cannot be used in union patterns").emit();
             }
         } else if !etc && !unmentioned_fields.is_empty() {
-            unmentioned_err = Some(self.error_unmentioned_fields(pat, &unmentioned_fields));
+            let no_accessible_unmentioned_fields = unmentioned_fields
+                .iter()
+                .find(|(field, _)| {
+                    field.vis.is_accessible_from(tcx.parent_module(pat.hir_id).to_def_id(), tcx)
+                })
+                .is_none();
+
+            if no_accessible_unmentioned_fields {
+                unmentioned_err = Some(self.error_no_accessible_fields(pat, &fields));
+            } else {
+                unmentioned_err =
+                    Some(self.error_unmentioned_fields(pat, &unmentioned_fields, &fields));
+            }
         }
         match (inexistent_fields_err, unmentioned_err) {
             (Some(mut i), Some(mut u)) => {
@@ -1171,7 +1251,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self,
         kind_name: &str,
         inexistent_fields: &[Ident],
-        unmentioned_fields: &mut Vec<Ident>,
+        unmentioned_fields: &mut Vec<(&ty::FieldDef, Ident)>,
         variant: &ty::VariantDef,
     ) -> DiagnosticBuilder<'tcx> {
         let tcx = self.tcx;
@@ -1213,7 +1293,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 ),
             );
             if plural == "" {
-                let input = unmentioned_fields.iter().map(|field| &field.name);
+                let input = unmentioned_fields.iter().map(|(_, field)| &field.name);
                 let suggested_name = find_best_match_for_name(input, ident.name, None);
                 if let Some(suggested_name) = suggested_name {
                     err.span_suggestion(
@@ -1230,7 +1310,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     // `smart_resolve_context_dependent_help`.
                     if suggested_name.to_ident_string().parse::<usize>().is_err() {
                         // We don't want to throw `E0027` in case we have thrown `E0026` for them.
-                        unmentioned_fields.retain(|&x| x.name != suggested_name);
+                        unmentioned_fields.retain(|&(_, x)| x.name != suggested_name);
                     }
                 }
             }
@@ -1298,17 +1378,78 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         None
     }
 
+    /// Returns a diagnostic reporting a struct pattern which is missing an `..` due to
+    /// inaccessible fields.
+    ///
+    /// ```ignore (diagnostic)
+    /// error: pattern requires `..` due to inaccessible fields
+    ///   --> src/main.rs:10:9
+    ///    |
+    /// LL |     let foo::Foo {} = foo::Foo::default();
+    ///    |         ^^^^^^^^^^^
+    ///    |
+    /// help: add a `..`
+    ///    |
+    /// LL |     let foo::Foo { .. } = foo::Foo::default();
+    ///    |                  ^^^^^^
+    /// ```
+    fn error_no_accessible_fields(
+        &self,
+        pat: &Pat<'_>,
+        fields: &'tcx [hir::FieldPat<'tcx>],
+    ) -> DiagnosticBuilder<'tcx> {
+        let mut err = self
+            .tcx
+            .sess
+            .struct_span_err(pat.span, "pattern requires `..` due to inaccessible fields");
+
+        if let Some(field) = fields.last() {
+            err.span_suggestion_verbose(
+                field.span.shrink_to_hi(),
+                "ignore the inaccessible and unused fields",
+                ", ..".to_string(),
+                Applicability::MachineApplicable,
+            );
+        } else {
+            let qpath_span = if let PatKind::Struct(qpath, ..) = &pat.kind {
+                qpath.span()
+            } else {
+                bug!("`error_no_accessible_fields` called on non-struct pattern");
+            };
+
+            // Shrink the span to exclude the `foo:Foo` in `foo::Foo { }`.
+            let span = pat.span.with_lo(qpath_span.shrink_to_hi().hi());
+            err.span_suggestion_verbose(
+                span,
+                "ignore the inaccessible and unused fields",
+                " { .. }".to_string(),
+                Applicability::MachineApplicable,
+            );
+        }
+        err
+    }
+
+    /// Returns a diagnostic reporting a struct pattern which does not mention some fields.
+    ///
+    /// ```ignore (diagnostic)
+    /// error[E0027]: pattern does not mention field `you_cant_use_this_field`
+    ///   --> src/main.rs:15:9
+    ///    |
+    /// LL |     let foo::Foo {} = foo::Foo::new();
+    ///    |         ^^^^^^^^^^^ missing field `you_cant_use_this_field`
+    /// ```
     fn error_unmentioned_fields(
         &self,
         pat: &Pat<'_>,
-        unmentioned_fields: &[Ident],
+        unmentioned_fields: &[(&ty::FieldDef, Ident)],
+        fields: &'tcx [hir::FieldPat<'tcx>],
     ) -> DiagnosticBuilder<'tcx> {
         let field_names = if unmentioned_fields.len() == 1 {
-            format!("field `{}`", unmentioned_fields[0])
+            format!("field `{}`", unmentioned_fields[0].1)
         } else {
             let fields = unmentioned_fields
                 .iter()
-                .map(|name| format!("`{}`", name))
+                .map(|(_, name)| format!("`{}`", name))
                 .collect::<Vec<String>>()
                 .join(", ");
             format!("fields {}", fields)
@@ -1321,14 +1462,52 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             field_names
         );
         err.span_label(pat.span, format!("missing {}", field_names));
-        if self.tcx.sess.teach(&err.get_code().unwrap()) {
-            err.note(
-                "This error indicates that a pattern for a struct fails to specify a \
-                 sub-pattern for every one of the struct's fields. Ensure that each field \
-                 from the struct's definition is mentioned in the pattern, or use `..` to \
-                 ignore unwanted fields.",
-            );
-        }
+        let len = unmentioned_fields.len();
+        let (prefix, postfix, sp) = match fields {
+            [] => match &pat.kind {
+                PatKind::Struct(path, [], false) => {
+                    (" { ", " }", path.span().shrink_to_hi().until(pat.span.shrink_to_hi()))
+                }
+                _ => return err,
+            },
+            [.., field] => (
+                match pat.kind {
+                    PatKind::Struct(_, [_, ..], _) => ", ",
+                    _ => "",
+                },
+                "",
+                field.span.shrink_to_hi(),
+            ),
+        };
+        err.span_suggestion(
+            sp,
+            &format!(
+                "include the missing field{} in the pattern",
+                if len == 1 { "" } else { "s" },
+            ),
+            format!(
+                "{}{}{}",
+                prefix,
+                unmentioned_fields
+                    .iter()
+                    .map(|(_, name)| name.to_string())
+                    .collect::<Vec<_>>()
+                    .join(", "),
+                postfix,
+            ),
+            Applicability::MachineApplicable,
+        );
+        err.span_suggestion(
+            sp,
+            &format!(
+                "if you don't care about {} missing field{}, you can explicitely ignore {}",
+                if len == 1 { "this" } else { "these" },
+                if len == 1 { "" } else { "s" },
+                if len == 1 { "it" } else { "them" },
+            ),
+            format!("{}..{}", prefix, postfix),
+            Applicability::MachineApplicable,
+        );
         err
     }
 
@@ -1378,7 +1557,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // to avoid creating needless variables. This also helps with
             // the bad  interactions of the given hack detailed in (note_1).
             debug!("check_pat_ref: expected={:?}", expected);
-            match expected.kind {
+            match *expected.kind() {
                 ty::Ref(_, r_ty, r_mutbl) if r_mutbl == mutbl => (expected, r_ty),
                 _ => {
                     let inner_ty = self.next_ty_var(TypeVariableOrigin {
@@ -1434,7 +1613,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         ti: TopInfo<'tcx>,
     ) -> Ty<'tcx> {
         let expected = self.structurally_resolved_type(span, expected);
-        let (element_ty, opt_slice_ty, inferred) = match expected.kind {
+        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) => {
                 let min = before.len() as u64 + after.len() as u64;
@@ -1570,8 +1749,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             "expected an array or slice, found `{}`",
             expected_ty
         );
-        if let ty::Ref(_, ty, _) = expected_ty.kind {
-            if let ty::Array(..) | ty::Slice(..) = ty.kind {
+        if let ty::Ref(_, ty, _) = expected_ty.kind() {
+            if let ty::Array(..) | ty::Slice(..) = ty.kind() {
                 err.help("the semantics of slice patterns changed recently; see issue #62254");
             }
         }
diff --git a/compiler/rustc_typeck/src/check/place_op.rs b/compiler/rustc_typeck/src/check/place_op.rs
index 4bef9aecd2e..502cb562385 100644
--- a/compiler/rustc_typeck/src/check/place_op.rs
+++ b/compiler/rustc_typeck/src/check/place_op.rs
@@ -25,7 +25,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         let ok = self.try_overloaded_deref(expr.span, oprnd_ty)?;
         let method = self.register_infer_ok_obligations(ok);
-        if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind {
+        if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind() {
             self.apply_adjustments(
                 oprnd_expr,
                 vec![Adjustment {
@@ -86,7 +86,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             let mut self_ty = adjusted_ty;
             if unsize {
                 // We only unsize arrays here.
-                if let ty::Array(element_ty, _) = adjusted_ty.kind {
+                if let ty::Array(element_ty, _) = adjusted_ty.kind() {
                     self_ty = self.tcx.mk_slice(element_ty);
                 } else {
                     continue;
@@ -108,7 +108,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let method = self.register_infer_ok_obligations(ok);
 
                 let mut adjustments = self.adjust_steps(autoderef);
-                if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind {
+                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(
@@ -193,7 +193,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// Convert auto-derefs, indices, etc of an expression from `Deref` and `Index`
     /// into `DerefMut` and `IndexMut` respectively.
     ///
-    /// This is a second pass of typechecking derefs/indices. We need this we do not
+    /// This is a second pass of typechecking derefs/indices. We need this because we do not
     /// always know whether a place needs to be mutable or not in the first pass.
     /// This happens whether there is an implicit mutable reborrow, e.g. when the type
     /// is used as the receiver of a method call.
@@ -211,13 +211,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         debug!("convert_place_derefs_to_mutable: exprs={:?}", exprs);
 
         // Fix up autoderefs and derefs.
+        let mut inside_union = false;
         for (i, &expr) in exprs.iter().rev().enumerate() {
             debug!("convert_place_derefs_to_mutable: i={} expr={:?}", i, expr);
 
+            let mut source = self.node_ty(expr.hir_id);
+            if matches!(expr.kind, hir::ExprKind::Unary(hir::UnOp::UnDeref, _)) {
+                // Clear previous flag; after a pointer indirection it does not apply any more.
+                inside_union = false;
+            }
+            if source.ty_adt_def().map_or(false, |adt| adt.is_union()) {
+                inside_union = true;
+            }
             // Fix up the autoderefs. Autorefs can only occur immediately preceding
             // overloaded place ops, and will be fixed by them in order to get
             // the correct region.
-            let mut source = self.node_ty(expr.hir_id);
             // Do not mutate adjustments in place, but rather take them,
             // and replace them after mutating them, to avoid having the
             // typeck results borrowed during (`deref_mut`) method resolution.
@@ -233,8 +241,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             PlaceOp::Deref,
                         ) {
                             let method = self.register_infer_ok_obligations(ok);
-                            if let ty::Ref(region, _, mutbl) = method.sig.output().kind {
-                                *deref = OverloadedDeref { region, mutbl };
+                            if let ty::Ref(region, _, mutbl) = *method.sig.output().kind() {
+                                *deref = OverloadedDeref { region, mutbl, span: deref.span };
+                            }
+                            // If this is a union field, also throw an error for `DerefMut` of `ManuallyDrop` (see RFC 2514).
+                            // This helps avoid accidental drops.
+                            if inside_union
+                                && source.ty_adt_def().map_or(false, |adt| adt.is_manually_drop())
+                            {
+                                let mut err = self.tcx.sess.struct_span_err(
+                                    expr.span,
+                                    "not automatically applying `DerefMut` on `ManuallyDrop` union field",
+                                );
+                                err.help(
+                                    "writing to this reference calls the destructor for the old value",
+                                );
+                                err.help("add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor");
+                                err.emit();
                             }
                         }
                     }
@@ -305,7 +328,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         debug!("convert_place_op_to_mutable: method={:?}", method);
         self.write_method_call(expr.hir_id, method);
 
-        let region = if let ty::Ref(r, _, hir::Mutability::Mut) = method.sig.inputs()[0].kind {
+        let region = if let ty::Ref(r, _, hir::Mutability::Mut) = method.sig.inputs()[0].kind() {
             r
         } else {
             span_bug!(expr.span, "input to mutable place op is not a mut ref?");
diff --git a/compiler/rustc_typeck/src/check/regionck.rs b/compiler/rustc_typeck/src/check/regionck.rs
index 484961dbdb8..ba0f22513a1 100644
--- a/compiler/rustc_typeck/src/check/regionck.rs
+++ b/compiler/rustc_typeck/src/check/regionck.rs
@@ -624,7 +624,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> {
         );
 
         let rptr_ty = self.resolve_node_type(id);
-        if let ty::Ref(r, _, _) = rptr_ty.kind {
+        if let ty::Ref(r, _, _) = rptr_ty.kind() {
             debug!("rptr_ty={}", rptr_ty);
             self.link_region(span, r, ty::BorrowKind::from_mutbl(mutbl), cmt_borrowed);
         }
@@ -649,7 +649,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> {
                 "link_region(borrow_region={:?}, borrow_kind={:?}, pointer_ty={:?})",
                 borrow_region, borrow_kind, borrow_place
             );
-            match pointer_ty.kind {
+            match *pointer_ty.kind() {
                 ty::RawPtr(_) => return,
                 ty::Ref(ref_region, _, ref_mutability) => {
                     if self.link_reborrowed_region(span, borrow_region, ref_region, ref_mutability)
@@ -794,7 +794,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> {
 
         // A closure capture can't be borrowed for longer than the
         // reference to the closure.
-        if let ty::Closure(_, substs) = ty.kind {
+        if let ty::Closure(_, substs) = ty.kind() {
             match self.infcx.closure_kind(substs) {
                 Some(ty::ClosureKind::Fn | ty::ClosureKind::FnMut) => {
                     // Region of environment pointer
diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs
index 9bb84c07868..2c3be0da5dd 100644
--- a/compiler/rustc_typeck/src/check/upvar.rs
+++ b/compiler/rustc_typeck/src/check/upvar.rs
@@ -88,7 +88,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         // Extract the type of the closure.
         let ty = self.node_ty(closure_hir_id);
-        let (closure_def_id, substs) = match ty.kind {
+        let (closure_def_id, substs) = match *ty.kind() {
             ty::Closure(def_id, substs) => (def_id, UpvarSubsts::Closure(substs)),
             ty::Generator(def_id, substs, _) => (def_id, UpvarSubsts::Generator(substs)),
             ty::Error(_) => {
@@ -349,7 +349,7 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
         if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base {
             let mut borrow_kind = ty::MutBorrow;
             for pointer_ty in place_with_id.place.deref_tys() {
-                match pointer_ty.kind {
+                match pointer_ty.kind() {
                     // Raw pointers don't inherit mutability.
                     ty::RawPtr(_) => return,
                     // assignment to deref of an `&mut`
diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs
index 9c692edaa7f..5203f3fa8f1 100644
--- a/compiler/rustc_typeck/src/check/wfcheck.rs
+++ b/compiler/rustc_typeck/src/check/wfcheck.rs
@@ -291,7 +291,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) {
             let err_ty_str;
             let mut is_ptr = true;
             let err = if tcx.features().min_const_generics {
-                match ty.kind {
+                match ty.kind() {
                     ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Error(_) => None,
                     ty::FnPtr(_) => Some("function pointers"),
                     ty::RawPtr(_) => Some("raw pointers"),
@@ -302,7 +302,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) {
                     }
                 }
             } else {
-                match ty.peel_refs().kind {
+                match ty.peel_refs().kind() {
                     ty::FnPtr(_) => Some("function pointers"),
                     ty::RawPtr(_) => Some("raw pointers"),
                     _ => None,
@@ -338,7 +338,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) {
                 // We use the same error code in both branches, because this is really the same
                 // issue: we just special-case the message for type parameters to make it
                 // clearer.
-                if let ty::Param(_) = ty.peel_refs().kind {
+                if let ty::Param(_) = ty.peel_refs().kind() {
                     // Const parameters may not have type parameters as their types,
                     // because we cannot be sure that the type parameter derives `PartialEq`
                     // and `Eq` (just implementing them is not enough for `structural_match`).
@@ -638,7 +638,7 @@ fn check_associated_type_defaults(fcx: &FnCtxt<'_, '_>, trait_def_id: DefId) {
         }
 
         fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
-            match t.kind {
+            match t.kind() {
                 ty::Projection(proj_ty) => {
                     if let Some(default) = self.map.get(&proj_ty) {
                         default
@@ -709,7 +709,7 @@ fn check_item_type(tcx: TyCtxt<'_>, item_id: hir::HirId, ty_span: Span, allow_fo
         let mut forbid_unsized = true;
         if allow_foreign_ty {
             let tail = fcx.tcx.struct_tail_erasing_lifetimes(item_ty, fcx.param_env);
-            if let ty::Foreign(_) = tail.kind {
+            if let ty::Foreign(_) = tail.kind() {
                 forbid_unsized = false;
             }
         }
@@ -867,7 +867,7 @@ fn check_where_clauses<'tcx, 'fcx>(
             }
             impl<'tcx> ty::fold::TypeVisitor<'tcx> for CountParams {
                 fn visit_ty(&mut self, t: Ty<'tcx>) -> bool {
-                    if let ty::Param(param) = t.kind {
+                    if let ty::Param(param) = t.kind() {
                         self.params.insert(param.index);
                     }
                     t.super_visit_with(self)
@@ -1001,7 +1001,7 @@ fn check_opaque_types<'fcx, 'tcx>(
     ty.fold_with(&mut ty::fold::BottomUpFolder {
         tcx: fcx.tcx,
         ty_op: |ty| {
-            if let ty::Opaque(def_id, substs) = ty.kind {
+            if let ty::Opaque(def_id, substs) = *ty.kind() {
                 trace!("check_opaque_types: opaque_ty, {:?}, {:?}", def_id, substs);
                 let generics = tcx.generics_of(def_id);
 
@@ -1044,7 +1044,7 @@ fn check_opaque_types<'fcx, 'tcx>(
                 let mut seen_params: FxHashMap<_, Vec<_>> = FxHashMap::default();
                 for (i, arg) in substs.iter().enumerate() {
                     let arg_is_param = match arg.unpack() {
-                        GenericArgKind::Type(ty) => matches!(ty.kind, ty::Param(_)),
+                        GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)),
 
                         GenericArgKind::Lifetime(region) => {
                             if let ty::ReStatic = region {
@@ -1177,7 +1177,7 @@ fn e0307(fcx: &FnCtxt<'fcx, 'tcx>, span: Span, receiver_ty: Ty<'_>) {
         fcx.tcx.sess.diagnostic(),
         span,
         E0307,
-        "invalid `self` parameter type: {:?}",
+        "invalid `self` parameter type: {}",
         receiver_ty,
     )
     .note("type of `self` must be `Self` or a type that dereferences to it")
diff --git a/compiler/rustc_typeck/src/check/writeback.rs b/compiler/rustc_typeck/src/check/writeback.rs
index 67f67e64dd4..b55f62ee436 100644
--- a/compiler/rustc_typeck/src/check/writeback.rs
+++ b/compiler/rustc_typeck/src/check/writeback.rs
@@ -193,7 +193,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
             let mut typeck_results = self.fcx.typeck_results.borrow_mut();
 
             // All valid indexing looks like this; might encounter non-valid indexes at this point.
-            let base_ty = typeck_results.expr_ty_adjusted_opt(&base).map(|t| &t.kind);
+            let base_ty = typeck_results.expr_ty_adjusted_opt(&base).map(|t| t.kind());
             if base_ty.is_none() {
                 // When encountering `return [0][0]` outside of a `fn` body we can encounter a base
                 // that isn't in the type table. We assume more relevant errors have already been
@@ -459,7 +459,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
 
             let mut skip_add = false;
 
-            if let ty::Opaque(defin_ty_def_id, _substs) = definition_ty.kind {
+            if let ty::Opaque(defin_ty_def_id, _substs) = *definition_ty.kind() {
                 if let hir::OpaqueTyOrigin::Misc = opaque_defn.origin {
                     if def_id == defin_ty_def_id {
                         debug!(
diff --git a/compiler/rustc_typeck/src/coherence/builtin.rs b/compiler/rustc_typeck/src/coherence/builtin.rs
index 0d3cac7f7f3..89270fb6c77 100644
--- a/compiler/rustc_typeck/src/coherence/builtin.rs
+++ b/compiler/rustc_typeck/src/coherence/builtin.rs
@@ -1,6 +1,7 @@
 //! Check properties that are required by built-in traits and set
 //! up data structures required by type-checking/codegen.
 
+use crate::errors::{CopyImplOnNonAdt, CopyImplOnTypeWithDtor, DropImplOnWrongItem};
 use rustc_errors::struct_span_err;
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
@@ -48,7 +49,7 @@ impl<'tcx> Checker<'tcx> {
 
 fn visit_implementation_of_drop(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
     // Destructors only work on nominal types.
-    if let ty::Adt(..) | ty::Error(_) = tcx.type_of(impl_did).kind {
+    if let ty::Adt(..) | ty::Error(_) = tcx.type_of(impl_did).kind() {
         return;
     }
 
@@ -58,14 +59,7 @@ fn visit_implementation_of_drop(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
         _ => bug!("expected Drop impl item"),
     };
 
-    struct_span_err!(
-        tcx.sess,
-        sp,
-        E0120,
-        "the `Drop` trait may only be implemented for structs, enums, and unions",
-    )
-    .span_label(sp, "must be a struct, enum, or union")
-    .emit();
+    tcx.sess.emit_err(DropImplOnWrongItem { span: sp });
 }
 
 fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
@@ -108,25 +102,10 @@ fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
             let span =
                 if let ItemKind::Impl { self_ty, .. } = item.kind { self_ty.span } else { span };
 
-            struct_span_err!(
-                tcx.sess,
-                span,
-                E0206,
-                "the trait `Copy` may not be implemented for this type"
-            )
-            .span_label(span, "type is not a structure or enumeration")
-            .emit();
+            tcx.sess.emit_err(CopyImplOnNonAdt { span });
         }
         Err(CopyImplementationError::HasDestructor) => {
-            struct_span_err!(
-                tcx.sess,
-                span,
-                E0184,
-                "the trait `Copy` may not be implemented for this type; the \
-                              type has a destructor"
-            )
-            .span_label(span, "Copy not allowed on types with destructors")
-            .emit();
+            tcx.sess.emit_err(CopyImplOnTypeWithDtor { span });
         }
     }
 }
@@ -168,7 +147,7 @@ fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: LocalDef
         let cause = ObligationCause::misc(span, impl_hir_id);
 
         use ty::TyKind::*;
-        match (&source.kind, &target.kind) {
+        match (source.kind(), target.kind()) {
             (&Ref(r_a, _, mutbl_a), Ref(r_b, _, mutbl_b))
                 if infcx.at(&cause, param_env).eq(r_a, r_b).is_ok() && mutbl_a == *mutbl_b => {}
             (&RawPtr(tm_a), &RawPtr(tm_b)) if tm_a.mutbl == tm_b.mutbl => (),
@@ -352,7 +331,7 @@ pub fn coerce_unsized_info(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUnsizedI
             }
             (mt_a.ty, mt_b.ty, unsize_trait, None)
         };
-        let (source, target, trait_def_id, kind) = match (&source.kind, &target.kind) {
+        let (source, target, trait_def_id, kind) = match (source.kind(), target.kind()) {
             (&ty::Ref(r_a, ty_a, mutbl_a), &ty::Ref(r_b, ty_b, mutbl_b)) => {
                 infcx.sub_regions(infer::RelateObjectBound(span), r_b, r_a);
                 let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a };
diff --git a/compiler/rustc_typeck/src/coherence/inherent_impls.rs b/compiler/rustc_typeck/src/coherence/inherent_impls.rs
index 859d510dcbe..373acb95c9e 100644
--- a/compiler/rustc_typeck/src/coherence/inherent_impls.rs
+++ b/compiler/rustc_typeck/src/coherence/inherent_impls.rs
@@ -52,7 +52,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> {
         let def_id = self.tcx.hir().local_def_id(item.hir_id);
         let self_ty = self.tcx.type_of(def_id);
         let lang_items = self.tcx.lang_items();
-        match self_ty.kind {
+        match *self_ty.kind() {
             ty::Adt(def, _) => {
                 self.check_def_id(item, def.did);
             }
@@ -123,7 +123,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> {
                 );
             }
             ty::RawPtr(ty::TypeAndMut { ty: inner, mutbl: hir::Mutability::Not })
-                if matches!(inner.kind, ty::Slice(_)) =>
+                if matches!(inner.kind(), ty::Slice(_)) =>
             {
                 self.check_primitive_impl(
                     def_id,
@@ -135,7 +135,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> {
                 );
             }
             ty::RawPtr(ty::TypeAndMut { ty: inner, mutbl: hir::Mutability::Mut })
-                if matches!(inner.kind, ty::Slice(_)) =>
+                if matches!(inner.kind(), ty::Slice(_)) =>
             {
                 self.check_primitive_impl(
                     def_id,
@@ -308,18 +308,25 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> {
             }
             ty::Error(_) => {}
             _ => {
-                struct_span_err!(
+                let mut err = struct_span_err!(
                     self.tcx.sess,
                     ty.span,
                     E0118,
-                    "no base type found for inherent implementation"
-                )
-                .span_label(ty.span, "impl requires a base type")
-                .note(
-                    "either implement a trait on it or create a newtype \
-                       to wrap it instead",
-                )
-                .emit();
+                    "no nominal type found for inherent implementation"
+                );
+
+                err.span_label(ty.span, "impl requires a nominal type")
+                    .note("either implement a trait on it or create a newtype to wrap it instead");
+
+                if let ty::Ref(_, subty, _) = self_ty.kind() {
+                    err.note(&format!(
+                        "you could also try moving the reference to \
+                            uses of `{}` (such as `self`) within the implementation",
+                        subty
+                    ));
+                }
+
+                err.emit();
             }
         }
     }
diff --git a/compiler/rustc_typeck/src/coherence/mod.rs b/compiler/rustc_typeck/src/coherence/mod.rs
index 1483244717b..4294450333c 100644
--- a/compiler/rustc_typeck/src/coherence/mod.rs
+++ b/compiler/rustc_typeck/src/coherence/mod.rs
@@ -209,7 +209,7 @@ fn check_object_overlap<'tcx>(
     }
 
     // check for overlap with the automatic `impl Trait for dyn Trait`
-    if let ty::Dynamic(ref data, ..) = trait_ref.self_ty().kind {
+    if let ty::Dynamic(ref data, ..) = trait_ref.self_ty().kind() {
         // This is something like impl Trait1 for Trait2. Illegal
         // if Trait1 is a supertrait of Trait2 or Trait2 is not object safe.
 
diff --git a/compiler/rustc_typeck/src/coherence/orphan.rs b/compiler/rustc_typeck/src/coherence/orphan.rs
index 71469770f2a..917fc5631c4 100644
--- a/compiler/rustc_typeck/src/coherence/orphan.rs
+++ b/compiler/rustc_typeck/src/coherence/orphan.rs
@@ -52,7 +52,7 @@ impl ItemLikeVisitor<'v> for OrphanChecker<'tcx> {
                             // Remove the lifetimes unnecessary for this error.
                             ty = infcx.freshen(ty);
                         });
-                        ty = match ty.kind {
+                        ty = match ty.kind() {
                             // Remove the type arguments from the output, as they are not relevant.
                             // You can think of this as the reverse of `resolve_vars_if_possible`.
                             // That way if we had `Vec<MyType>`, we will properly attribute the
@@ -62,7 +62,7 @@ impl ItemLikeVisitor<'v> for OrphanChecker<'tcx> {
                             _ => ty,
                         };
                         let this = "this".to_string();
-                        let (ty, postfix) = match &ty.kind {
+                        let (ty, postfix) = match &ty.kind() {
                             ty::Slice(_) => (this, " because slices are always foreign"),
                             ty::Array(..) => (this, " because arrays are always foreign"),
                             ty::Tuple(..) => (this, " because tuples are always foreign"),
@@ -185,7 +185,7 @@ impl ItemLikeVisitor<'v> for OrphanChecker<'tcx> {
             );
             if self.tcx.trait_is_auto(trait_def_id) && !trait_def_id.is_local() {
                 let self_ty = trait_ref.self_ty();
-                let opt_self_def_id = match self_ty.kind {
+                let opt_self_def_id = match *self_ty.kind() {
                     ty::Adt(self_def, _) => Some(self_def.did),
                     ty::Foreign(did) => Some(did),
                     _ => None,
@@ -230,6 +230,14 @@ impl ItemLikeVisitor<'v> for OrphanChecker<'tcx> {
                     return;
                 }
             }
+
+            if let ty::Opaque(def_id, _) = *trait_ref.self_ty().kind() {
+                self.tcx
+                    .sess
+                    .struct_span_err(sp, "cannot implement trait on type alias impl trait")
+                    .span_note(self.tcx.def_span(def_id), "type alias impl trait defined here")
+                    .emit();
+            }
         }
     }
 
diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs
index 7a3f7ec56a2..a571bd58abc 100644
--- a/compiler/rustc_typeck/src/collect.rs
+++ b/compiler/rustc_typeck/src/collect.rs
@@ -18,6 +18,7 @@ use crate::astconv::{AstConv, SizedByDefault};
 use crate::bounds::Bounds;
 use crate::check::intrinsic::intrinsic_operation_unsafety;
 use crate::constrained_generic_params as cgp;
+use crate::errors;
 use crate::middle::resolve_lifetime as rl;
 use rustc_ast as ast;
 use rustc_ast::MetaItemKind;
@@ -834,16 +835,11 @@ fn convert_variant(
             let fid = tcx.hir().local_def_id(f.hir_id);
             let dup_span = seen_fields.get(&f.ident.normalize_to_macros_2_0()).cloned();
             if let Some(prev_span) = dup_span {
-                struct_span_err!(
-                    tcx.sess,
-                    f.span,
-                    E0124,
-                    "field `{}` is already declared",
-                    f.ident
-                )
-                .span_label(f.span, "field already declared")
-                .span_label(prev_span, format!("`{}` first declared here", f.ident))
-                .emit();
+                tcx.sess.emit_err(errors::FieldAlreadyDeclared {
+                    field_name: f.ident,
+                    span: f.span,
+                    prev_span,
+                });
             } else {
                 seen_fields.insert(f.ident.normalize_to_macros_2_0(), f.span);
             }
@@ -1676,6 +1672,7 @@ fn predicates_defined_on(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicate
                 .alloc_from_iter(result.predicates.iter().chain(inferred_outlives).copied());
         }
     }
+
     debug!("predicates_defined_on({:?}) = {:?}", def_id, result);
     result
 }
@@ -1717,29 +1714,6 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat
 
     debug!("explicit_predicates_of(def_id={:?})", def_id);
 
-    /// A data structure with unique elements, which preserves order of insertion.
-    /// Preserving the order of insertion is important here so as not to break
-    /// compile-fail UI tests.
-    struct UniquePredicates<'tcx> {
-        predicates: FxIndexSet<(ty::Predicate<'tcx>, Span)>,
-    }
-
-    impl<'tcx> UniquePredicates<'tcx> {
-        fn new() -> Self {
-            UniquePredicates { predicates: FxIndexSet::default() }
-        }
-
-        fn push(&mut self, value: (ty::Predicate<'tcx>, Span)) {
-            self.predicates.insert(value);
-        }
-
-        fn extend<I: IntoIterator<Item = (ty::Predicate<'tcx>, Span)>>(&mut self, iter: I) {
-            for value in iter {
-                self.push(value);
-            }
-        }
-    }
-
     let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
     let node = tcx.hir().get(hir_id);
 
@@ -1752,7 +1726,10 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat
 
     const NO_GENERICS: &hir::Generics<'_> = &hir::Generics::empty();
 
-    let mut predicates = UniquePredicates::new();
+    // We use an `IndexSet` to preserves order of insertion.
+    // Preserving the order of insertion is important here so as not to break
+    // compile-fail UI tests.
+    let mut predicates: FxIndexSet<(ty::Predicate<'_>, Span)> = FxIndexSet::default();
 
     let ast_generics = match node {
         Node::TraitItem(item) => {
@@ -1854,7 +1831,7 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat
     // (see below). Recall that a default impl is not itself an impl, but rather a
     // set of defaults that can be incorporated into another impl.
     if let Some(trait_ref) = is_default_impl_trait {
-        predicates.push((
+        predicates.insert((
             trait_ref.to_poly_trait_ref().without_const().to_predicate(tcx),
             tcx.def_span(def_id),
         ));
@@ -1878,7 +1855,7 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat
                     hir::GenericBound::Outlives(lt) => {
                         let bound = AstConv::ast_region_to_region(&icx, &lt, None);
                         let outlives = ty::Binder::bind(ty::OutlivesPredicate(region, bound));
-                        predicates.push((outlives.to_predicate(tcx), lt.span));
+                        predicates.insert((outlives.to_predicate(tcx), lt.span));
                     }
                     _ => bug!(),
                 });
@@ -1922,7 +1899,7 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat
                 // That way, `where Ty:` is not a complete noop (see #53696) and `Ty`
                 // is still checked for WF.
                 if bound_pred.bounds.is_empty() {
-                    if let ty::Param(_) = ty.kind {
+                    if let ty::Param(_) = ty.kind() {
                         // This is a `where T:`, which can be in the HIR from the
                         // transformation that moves `?Sized` to `T`'s declaration.
                         // We can skip the predicate because type parameters are
@@ -1933,7 +1910,7 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat
                         let span = bound_pred.bounded_ty.span;
                         let re_root_empty = tcx.lifetimes.re_root_empty;
                         let predicate = ty::OutlivesPredicate(ty, re_root_empty);
-                        predicates.push((
+                        predicates.insert((
                             ty::PredicateAtom::TypeOutlives(predicate)
                                 .potentially_quantified(tcx, ty::PredicateKind::ForAll),
                             span,
@@ -1977,11 +1954,11 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat
 
                         &hir::GenericBound::Outlives(ref lifetime) => {
                             let region = AstConv::ast_region_to_region(&icx, lifetime, None);
-                            predicates.push((
+                            predicates.insert((
                                 ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(ty, region))
                                     .potentially_quantified(tcx, ty::PredicateKind::ForAll),
                                 lifetime.span,
-                            ))
+                            ));
                         }
                     }
                 }
@@ -2026,7 +2003,11 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat
         }))
     }
 
-    let mut predicates: Vec<_> = predicates.predicates.into_iter().collect();
+    if tcx.features().const_evaluatable_checked {
+        predicates.extend(const_evaluatable_predicates_of(tcx, def_id.expect_local()));
+    }
+
+    let mut predicates: Vec<_> = predicates.into_iter().collect();
 
     // Subtle: before we store the predicates into the tcx, we
     // sort them so that predicates like `T: Foo<Item=U>` come
@@ -2052,6 +2033,97 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat
     result
 }
 
+fn const_evaluatable_predicates_of<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    def_id: LocalDefId,
+) -> FxIndexSet<(ty::Predicate<'tcx>, Span)> {
+    struct ConstCollector<'tcx> {
+        tcx: TyCtxt<'tcx>,
+        preds: FxIndexSet<(ty::Predicate<'tcx>, Span)>,
+    }
+
+    impl<'tcx> intravisit::Visitor<'tcx> for ConstCollector<'tcx> {
+        type Map = Map<'tcx>;
+
+        fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
+            intravisit::NestedVisitorMap::None
+        }
+
+        fn visit_anon_const(&mut self, c: &'tcx hir::AnonConst) {
+            let def_id = self.tcx.hir().local_def_id(c.hir_id);
+            let ct = ty::Const::from_anon_const(self.tcx, def_id);
+            if let ty::ConstKind::Unevaluated(def, substs, None) = ct.val {
+                let span = self.tcx.hir().span(c.hir_id);
+                self.preds.insert((
+                    ty::PredicateAtom::ConstEvaluatable(def, substs).to_predicate(self.tcx),
+                    span,
+                ));
+            }
+        }
+
+        // Look into `TyAlias`.
+        fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
+            use ty::fold::{TypeFoldable, TypeVisitor};
+            struct TyAliasVisitor<'a, 'tcx> {
+                tcx: TyCtxt<'tcx>,
+                preds: &'a mut FxIndexSet<(ty::Predicate<'tcx>, Span)>,
+                span: Span,
+            }
+
+            impl<'a, 'tcx> TypeVisitor<'tcx> for TyAliasVisitor<'a, 'tcx> {
+                fn visit_const(&mut self, ct: &'tcx Const<'tcx>) -> bool {
+                    if let ty::ConstKind::Unevaluated(def, substs, None) = ct.val {
+                        self.preds.insert((
+                            ty::PredicateAtom::ConstEvaluatable(def, substs).to_predicate(self.tcx),
+                            self.span,
+                        ));
+                    }
+                    false
+                }
+            }
+
+            if let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = ty.kind {
+                if let Res::Def(DefKind::TyAlias, def_id) = path.res {
+                    let mut visitor =
+                        TyAliasVisitor { tcx: self.tcx, preds: &mut self.preds, span: path.span };
+                    self.tcx.type_of(def_id).visit_with(&mut visitor);
+                }
+            }
+
+            intravisit::walk_ty(self, ty)
+        }
+    }
+
+    let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
+    let node = tcx.hir().get(hir_id);
+
+    let mut collector = ConstCollector { tcx, preds: FxIndexSet::default() };
+    if let hir::Node::Item(item) = node {
+        if let hir::ItemKind::Impl { ref of_trait, ref self_ty, .. } = item.kind {
+            if let Some(of_trait) = of_trait {
+                warn!("const_evaluatable_predicates_of({:?}): visit impl trait_ref", def_id);
+                collector.visit_trait_ref(of_trait);
+            }
+
+            warn!("const_evaluatable_predicates_of({:?}): visit_self_ty", def_id);
+            collector.visit_ty(self_ty);
+        }
+    }
+
+    if let Some(generics) = node.generics() {
+        warn!("const_evaluatable_predicates_of({:?}): visit_generics", def_id);
+        collector.visit_generics(generics);
+    }
+
+    if let Some(fn_sig) = tcx.hir().fn_sig_by_hir_id(hir_id) {
+        warn!("const_evaluatable_predicates_of({:?}): visit_fn_decl", def_id);
+        collector.visit_fn_decl(fn_sig.decl);
+    }
+    warn!("const_evaluatable_predicates_of({:?}) = {:?}", def_id, collector.preds);
+
+    collector.preds
+}
+
 fn projection_ty_from_predicates(
     tcx: TyCtxt<'tcx>,
     key: (
@@ -2306,8 +2378,8 @@ fn from_target_feature(
                         item.span(),
                         format!("`{}` is not valid for this target", feature),
                     );
-                    if feature.starts_with('+') {
-                        let valid = supported_target_features.contains_key(&feature[1..]);
+                    if let Some(stripped) = feature.strip_prefix('+') {
+                        let valid = supported_target_features.contains_key(stripped);
                         if valid {
                             err.help("consider removing the leading `+` in the feature name");
                         }
@@ -2326,7 +2398,6 @@ fn from_target_feature(
                 Some(sym::mips_target_feature) => rust_features.mips_target_feature,
                 Some(sym::riscv_target_feature) => rust_features.riscv_target_feature,
                 Some(sym::avx512_target_feature) => rust_features.avx512_target_feature,
-                Some(sym::mmx_target_feature) => rust_features.mmx_target_feature,
                 Some(sym::sse4a_target_feature) => rust_features.sse4a_target_feature,
                 Some(sym::tbm_target_feature) => rust_features.tbm_target_feature,
                 Some(sym::wasm_target_feature) => rust_features.wasm_target_feature,
@@ -2490,10 +2561,17 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
                 codegen_fn_attrs.export_name = Some(s);
             }
         } else if tcx.sess.check_name(attr, sym::target_feature) {
-            if !tcx.features().target_feature_11 {
-                check_target_feature_safe_fn(tcx, id, attr.span);
-            } else if let Some(local_id) = id.as_local() {
-                if tcx.fn_sig(id).unsafety() == hir::Unsafety::Normal {
+            if !tcx.is_closure(id) && tcx.fn_sig(id).unsafety() == hir::Unsafety::Normal {
+                if !tcx.features().target_feature_11 {
+                    let mut err = feature_err(
+                        &tcx.sess.parse_sess,
+                        sym::target_feature_11,
+                        attr.span,
+                        "`#[target_feature(..)]` can only be applied to `unsafe` functions",
+                    );
+                    err.span_label(tcx.def_span(id), "not an `unsafe` function");
+                    err.emit();
+                } else if let Some(local_id) = id.as_local() {
                     check_target_feature_trait_unsafe(tcx, local_id, attr.span);
                 }
             }
@@ -2750,21 +2828,6 @@ fn check_link_name_xor_ordinal(
     }
 }
 
-/// Checks the function annotated with `#[target_feature]` is unsafe,
-/// reporting an error if it isn't.
-fn check_target_feature_safe_fn(tcx: TyCtxt<'_>, id: DefId, attr_span: Span) {
-    if tcx.is_closure(id) || tcx.fn_sig(id).unsafety() == hir::Unsafety::Normal {
-        let mut err = feature_err(
-            &tcx.sess.parse_sess,
-            sym::target_feature_11,
-            attr_span,
-            "`#[target_feature(..)]` can only be applied to `unsafe` functions",
-        );
-        err.span_label(tcx.def_span(id), "not an `unsafe` function");
-        err.emit();
-    }
-}
-
 /// Checks the function annotated with `#[target_feature]` is not a safe
 /// trait method implementation, reporting an error if it is.
 fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, attr_span: Span) {
diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs
index 70ed92c5614..4b3250a1d44 100644
--- a/compiler/rustc_typeck/src/collect/type_of.rs
+++ b/compiler/rustc_typeck/src/collect/type_of.rs
@@ -1,5 +1,6 @@
+use crate::errors::AssocTypeOnInherentImpl;
 use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::{struct_span_err, Applicability, ErrorReported, StashKey};
+use rustc_errors::{Applicability, ErrorReported, StashKey};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, LocalDefId};
@@ -382,7 +383,7 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> {
                 let mut used_params: FxHashSet<_> = FxHashSet::default();
                 for (i, arg) in substs.iter().enumerate() {
                     let arg_is_param = match arg.unpack() {
-                        GenericArgKind::Type(ty) => matches!(ty.kind, ty::Param(_)),
+                        GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)),
                         GenericArgKind::Lifetime(lt) => {
                             matches!(lt, ty::ReEarlyBound(_) | ty::ReFree(_))
                         }
@@ -607,7 +608,7 @@ fn infer_placeholder_type(
         }
         None => {
             let mut diag = bad_placeholder_type(tcx, vec![span]);
-            if !matches!(ty.kind, ty::Error(_)) {
+            if !matches!(ty.kind(), ty::Error(_)) {
                 diag.span_suggestion(
                     span,
                     "replace `_` with the correct type",
@@ -627,11 +628,5 @@ fn infer_placeholder_type(
 }
 
 fn report_assoc_ty_on_inherent_impl(tcx: TyCtxt<'_>, span: Span) {
-    struct_span_err!(
-        tcx.sess,
-        span,
-        E0202,
-        "associated types are not yet supported in inherent impls (see #8995)"
-    )
-    .emit();
+    tcx.sess.emit_err(AssocTypeOnInherentImpl { span });
 }
diff --git a/compiler/rustc_typeck/src/constrained_generic_params.rs b/compiler/rustc_typeck/src/constrained_generic_params.rs
index 7c80315ee19..09b5a9b0a65 100644
--- a/compiler/rustc_typeck/src/constrained_generic_params.rs
+++ b/compiler/rustc_typeck/src/constrained_generic_params.rs
@@ -57,7 +57,7 @@ struct ParameterCollector {
 
 impl<'tcx> TypeVisitor<'tcx> for ParameterCollector {
     fn visit_ty(&mut self, t: Ty<'tcx>) -> bool {
-        match t.kind {
+        match *t.kind() {
             ty::Projection(..) | ty::Opaque(..) if !self.include_nonconstraining => {
                 // projections are not injective
                 return false;
diff --git a/compiler/rustc_typeck/src/errors.rs b/compiler/rustc_typeck/src/errors.rs
new file mode 100644
index 00000000000..a769e48d2ca
--- /dev/null
+++ b/compiler/rustc_typeck/src/errors.rs
@@ -0,0 +1,199 @@
+//! Errors emitted by typeck.
+use rustc_macros::SessionDiagnostic;
+use rustc_span::{symbol::Ident, Span, Symbol};
+
+#[derive(SessionDiagnostic)]
+#[error = "E0062"]
+pub struct FieldMultiplySpecifiedInInitializer {
+    #[message = "field `{ident}` specified more than once"]
+    #[label = "used more than once"]
+    pub span: Span,
+    #[label = "first use of `{ident}`"]
+    pub prev_span: Span,
+    pub ident: Ident,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0092"]
+pub struct UnrecognizedAtomicOperation<'a> {
+    #[message = "unrecognized atomic operation function: `{op}`"]
+    #[label = "unrecognized atomic operation"]
+    pub span: Span,
+    pub op: &'a str,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0094"]
+pub struct WrongNumberOfTypeArgumentsToInstrinsic {
+    #[message = "intrinsic has wrong number of type \
+                         parameters: found {found}, expected {expected}"]
+    #[label = "expected {expected} type parameter"]
+    pub span: Span,
+    pub found: usize,
+    pub expected: usize,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0093"]
+pub struct UnrecognizedIntrinsicFunction {
+    #[message = "unrecognized intrinsic function: `{name}`"]
+    #[label = "unrecognized intrinsic"]
+    pub span: Span,
+    pub name: Symbol,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0195"]
+pub struct LifetimesOrBoundsMismatchOnTrait {
+    #[message = "lifetime parameters or bounds on {item_kind} `{ident}` do not match the trait declaration"]
+    #[label = "lifetimes do not match {item_kind} in trait"]
+    pub span: Span,
+    #[label = "lifetimes in impl do not match this {item_kind} in trait"]
+    pub generics_span: Option<Span>,
+    pub item_kind: &'static str,
+    pub ident: Ident,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0120"]
+pub struct DropImplOnWrongItem {
+    #[message = "the `Drop` trait may only be implemented for structs, enums, and unions"]
+    #[label = "must be a struct, enum, or union"]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0124"]
+pub struct FieldAlreadyDeclared {
+    pub field_name: Ident,
+    #[message = "field `{field_name}` is already declared"]
+    #[label = "field already declared"]
+    pub span: Span,
+    #[label = "`{field_name}` first declared here"]
+    pub prev_span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0184"]
+pub struct CopyImplOnTypeWithDtor {
+    #[message = "the trait `Copy` may not be implemented for this type; the \
+                              type has a destructor"]
+    #[label = "Copy not allowed on types with destructors"]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0202"]
+pub struct AssocTypeOnInherentImpl {
+    #[message = "associated types are not yet supported in inherent impls (see #8995)"]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0203"]
+pub struct MultipleRelaxedDefaultBounds {
+    #[message = "type parameter has more than one relaxed default bound, only one is supported"]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0206"]
+pub struct CopyImplOnNonAdt {
+    #[message = "the trait `Copy` may not be implemented for this type"]
+    #[label = "type is not a structure or enumeration"]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0224"]
+pub struct TraitObjectDeclaredWithNoTraits {
+    #[message = "at least one trait is required for an object type"]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0227"]
+pub struct AmbiguousLifetimeBound {
+    #[message = "ambiguous lifetime bound, explicit lifetime bound required"]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0229"]
+pub struct AssocTypeBindingNotAllowed {
+    #[message = "associated type bindings are not allowed here"]
+    #[label = "associated type not allowed here"]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0439"]
+pub struct SimdShuffleMissingLength {
+    #[message = "invalid `simd_shuffle`, needs length: `{name}`"]
+    pub span: Span,
+    pub name: Symbol,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0436"]
+pub struct FunctionalRecordUpdateOnNonStruct {
+    #[message = "functional record update syntax requires a struct"]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0516"]
+pub struct TypeofReservedKeywordUsed {
+    #[message = "`typeof` is a reserved keyword but unimplemented"]
+    #[label = "reserved keyword"]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0572"]
+pub struct ReturnStmtOutsideOfFnBody {
+    #[message = "return statement outside of function body"]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0627"]
+pub struct YieldExprOutsideOfGenerator {
+    #[message = "yield expression outside of generator literal"]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0639"]
+pub struct StructExprNonExhaustive {
+    #[message = "cannot create non-exhaustive {what} using struct expression"]
+    pub span: Span,
+    pub what: &'static str,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0699"]
+pub struct MethodCallOnUnknownType {
+    #[message = "the type of this value must be known to call a method on a raw pointer on it"]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0719"]
+pub struct ValueOfAssociatedStructAlreadySpecified {
+    #[message = "the value of the associated type `{item_name}` (from trait `{def_path}`) is already specified"]
+    #[label = "re-bound here"]
+    pub span: Span,
+    #[label = "`{item_name}` bound here first"]
+    pub prev_span: Span,
+    pub item_name: Ident,
+    pub def_path: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0745"]
+pub struct AddressOfTemporaryTaken {
+    #[message = "cannot take address of a temporary"]
+    #[label = "temporary value"]
+    pub span: Span,
+}
diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_typeck/src/expr_use_visitor.rs
index e774f2d095d..e16f26c3304 100644
--- a/compiler/rustc_typeck/src/expr_use_visitor.rs
+++ b/compiler/rustc_typeck/src/expr_use_visitor.rs
@@ -387,7 +387,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
 
         // Select just those fields of the `with`
         // expression that will actually be used
-        match with_place.place.ty().kind {
+        match with_place.place.ty().kind() {
             ty::Adt(adt, substs) if adt.is_struct() => {
                 // Consume those fields of the with expression that are needed.
                 for (f_index, with_field) in adt.non_enum_variant().fields.iter().enumerate() {
diff --git a/compiler/rustc_typeck/src/impl_wf_check.rs b/compiler/rustc_typeck/src/impl_wf_check.rs
index 891e482b431..4901d6041d6 100644
--- a/compiler/rustc_typeck/src/impl_wf_check.rs
+++ b/compiler/rustc_typeck/src/impl_wf_check.rs
@@ -187,7 +187,7 @@ fn enforce_impl_params_are_constrained(
     }
 
     // (*) This is a horrible concession to reality. I think it'd be
-    // better to just ban unconstrianed lifetimes outright, but in
+    // better to just ban unconstrained lifetimes outright, but in
     // practice people do non-hygenic macros like:
     //
     // ```
@@ -207,7 +207,7 @@ fn enforce_impl_params_are_constrained(
 }
 
 fn report_unused_parameter(tcx: TyCtxt<'_>, span: Span, kind: &str, name: &str) {
-    struct_span_err!(
+    let mut err = struct_span_err!(
         tcx.sess,
         span,
         E0207,
@@ -215,9 +215,17 @@ fn report_unused_parameter(tcx: TyCtxt<'_>, span: Span, kind: &str, name: &str)
         impl trait, self type, or predicates",
         kind,
         name
-    )
-    .span_label(span, format!("unconstrained {} parameter", kind))
-    .emit();
+    );
+    err.span_label(span, format!("unconstrained {} parameter", kind));
+    if kind == "const" {
+        err.note(
+            "expressions using a const parameter must map each value to a distinct output value",
+        );
+        err.note(
+            "proving the result of expressions other than the parameter are unique is not supported",
+        );
+    }
+    err.emit();
 }
 
 /// Enforce that we do not have two items in an impl with the same name.
diff --git a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs
index 3746e5778aa..60b9467fca8 100644
--- a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs
+++ b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs
@@ -405,6 +405,7 @@ fn trait_predicate_kind<'tcx>(
         | ty::PredicateAtom::ObjectSafe(_)
         | ty::PredicateAtom::ClosureKind(..)
         | ty::PredicateAtom::ConstEvaluatable(..)
-        | ty::PredicateAtom::ConstEquate(..) => None,
+        | ty::PredicateAtom::ConstEquate(..)
+        | ty::PredicateAtom::TypeWellFormedFromEnv(..) => None,
     }
 }
diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs
index 62f92fe7ffa..84efb92582e 100644
--- a/compiler/rustc_typeck/src/lib.rs
+++ b/compiler/rustc_typeck/src/lib.rs
@@ -55,7 +55,7 @@ This API is completely unstable and subject to change.
 
 */
 
-#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
+#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![allow(non_camel_case_types)]
 #![feature(bool_to_option)]
 #![feature(box_syntax)]
@@ -84,6 +84,7 @@ mod check_unused;
 mod coherence;
 mod collect;
 mod constrained_generic_params;
+mod errors;
 mod impl_wf_check;
 mod mem_categorization;
 mod outlives;
@@ -158,7 +159,7 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: LocalDefId) {
     let main_id = tcx.hir().local_def_id_to_hir_id(main_def_id);
     let main_span = tcx.def_span(main_def_id);
     let main_t = tcx.type_of(main_def_id);
-    match main_t.kind {
+    match main_t.kind() {
         ty::FnDef(..) => {
             if let Some(Node::Item(it)) = tcx.hir().find(main_id) {
                 if let hir::ItemKind::Fn(ref sig, ref generics, _) = it.kind {
@@ -254,7 +255,7 @@ fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: LocalDefId) {
     let start_id = tcx.hir().local_def_id_to_hir_id(start_def_id);
     let start_span = tcx.def_span(start_def_id);
     let start_t = tcx.type_of(start_def_id);
-    match start_t.kind {
+    match start_t.kind() {
         ty::FnDef(..) => {
             if let Some(Node::Item(it)) = tcx.hir().find(start_id) {
                 if let hir::ItemKind::Fn(ref sig, ref generics, _) = it.kind {
diff --git a/compiler/rustc_typeck/src/mem_categorization.rs b/compiler/rustc_typeck/src/mem_categorization.rs
index 8a6fe620af7..04ead74936f 100644
--- a/compiler/rustc_typeck/src/mem_categorization.rs
+++ b/compiler/rustc_typeck/src/mem_categorization.rs
@@ -482,7 +482,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
         let place_ty = self.expr_ty(expr)?;
         let base_ty = self.expr_ty_adjusted(base)?;
 
-        let (region, mutbl) = match base_ty.kind {
+        let (region, mutbl) = match *base_ty.kind() {
             ty::Ref(region, _, mutbl) => (region, mutbl),
             _ => span_bug!(expr.span, "cat_overloaded_place: base is not a reference"),
         };
@@ -542,7 +542,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
     ) -> McResult<VariantIdx> {
         let res = self.typeck_results.qpath_res(qpath, pat_hir_id);
         let ty = self.typeck_results.node_type(pat_hir_id);
-        let adt_def = match ty.kind {
+        let adt_def = match ty.kind() {
             ty::Adt(adt_def, _) => adt_def,
             _ => {
                 self.tcx()
@@ -577,7 +577,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
         span: Span,
     ) -> McResult<usize> {
         let ty = self.typeck_results.node_type(pat_hir_id);
-        match ty.kind {
+        match ty.kind() {
             ty::Adt(adt_def, _) => Ok(adt_def.variants[variant_index].fields.len()),
             _ => {
                 self.tcx()
@@ -592,7 +592,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
     /// Here `pat_hir_id` is the HirId of the pattern itself.
     fn total_fields_in_tuple(&self, pat_hir_id: hir::HirId, span: Span) -> McResult<usize> {
         let ty = self.typeck_results.node_type(pat_hir_id);
-        match ty.kind {
+        match ty.kind() {
             ty::Tuple(substs) => Ok(substs.len()),
             _ => {
                 self.tcx().sess.delay_span_bug(span, "tuple pattern not applied to a tuple");
diff --git a/compiler/rustc_typeck/src/outlives/explicit.rs b/compiler/rustc_typeck/src/outlives/explicit.rs
index 135960a4c11..ae336ccca45 100644
--- a/compiler/rustc_typeck/src/outlives/explicit.rs
+++ b/compiler/rustc_typeck/src/outlives/explicit.rs
@@ -57,7 +57,8 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> {
                     | ty::PredicateAtom::ClosureKind(..)
                     | ty::PredicateAtom::Subtype(..)
                     | ty::PredicateAtom::ConstEvaluatable(..)
-                    | ty::PredicateAtom::ConstEquate(..) => (),
+                    | ty::PredicateAtom::ConstEquate(..)
+                    | ty::PredicateAtom::TypeWellFormedFromEnv(..) => (),
                 }
             }
 
diff --git a/compiler/rustc_typeck/src/outlives/implicit_infer.rs b/compiler/rustc_typeck/src/outlives/implicit_infer.rs
index 762d4216f70..e7a9e078a73 100644
--- a/compiler/rustc_typeck/src/outlives/implicit_infer.rs
+++ b/compiler/rustc_typeck/src/outlives/implicit_infer.rs
@@ -128,7 +128,7 @@ fn insert_required_predicates_to_be_wf<'tcx>(
             GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => continue,
         };
 
-        match ty.kind {
+        match *ty.kind() {
             // The field is of type &'a T which means that we will have
             // a predicate requirement of T: 'a (T outlives 'a).
             //
diff --git a/compiler/rustc_typeck/src/variance/constraints.rs b/compiler/rustc_typeck/src/variance/constraints.rs
index 535530a2ed4..b2b062e4095 100644
--- a/compiler/rustc_typeck/src/variance/constraints.rs
+++ b/compiler/rustc_typeck/src/variance/constraints.rs
@@ -140,7 +140,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
         let id = tcx.hir().local_def_id_to_hir_id(def_id);
         let inferred_start = self.terms_cx.inferred_starts[&id];
         let current_item = &CurrentItem { inferred_start };
-        match tcx.type_of(def_id).kind {
+        match tcx.type_of(def_id).kind() {
             ty::Adt(def, _) => {
                 // Not entirely obvious: constraints on structs/enums do not
                 // affect the variance of their type parameters. See discussion
@@ -257,7 +257,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
     ) {
         debug!("add_constraints_from_ty(ty={:?}, variance={:?})", ty, variance);
 
-        match ty.kind {
+        match *ty.kind() {
             ty::Bool
             | ty::Char
             | ty::Int(_)
diff --git a/compiler/rustc_typeck/src/variance/solve.rs b/compiler/rustc_typeck/src/variance/solve.rs
index 7402117a7eb..2d3369cba7a 100644
--- a/compiler/rustc_typeck/src/variance/solve.rs
+++ b/compiler/rustc_typeck/src/variance/solve.rs
@@ -107,7 +107,7 @@ impl<'a, 'tcx> SolveContext<'a, 'tcx> {
                 self.enforce_const_invariance(generics, variances);
 
                 // Functions are permitted to have unused generic parameters: make those invariant.
-                if let ty::FnDef(..) = tcx.type_of(def_id).kind {
+                if let ty::FnDef(..) = tcx.type_of(def_id).kind() {
                     for variance in variances.iter_mut() {
                         if *variance == ty::Bivariant {
                             *variance = ty::Invariant;